d3dx9_36: Implement D3DXCreateMesh and initial ID3DXMesh methods.
authorMisha Koshelev <misha680@gmail.com>
Thu, 16 Sep 2010 22:02:48 +0000 (17:02 -0500)
committerAlexandre Julliard <julliard@winehq.org>
Tue, 21 Sep 2010 18:16:16 +0000 (20:16 +0200)
dlls/d3dx9_36/d3dx9_36_private.h
dlls/d3dx9_36/mesh.c
dlls/d3dx9_36/tests/mesh.c

index b910139cee5fc72bd8aaeafac21b59826aebfe64..95980901fea53f289521788e7d4afdbcd9cdb7fc 100644 (file)
@@ -93,6 +93,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;
+    IDirect3DVertexDeclaration9 *vertex_declaration;
+    IDirect3DVertexBuffer9 *vertex_buffer;
+    IDirect3DIndexBuffer9 *index_buffer;
+} ID3DXMeshImpl;
+
 /*ID3DXSprite */
 typedef struct _SPRITE {
     LPDIRECT3DTEXTURE9 texture;
index a6146fe8e9d6a942b8762da345b4769000871799..844d521f8d1bdf60c1f420031bfee8fbacd3057b 100644 (file)
 #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)
+    {
+        IDirect3DIndexBuffer9_Release(This->index_buffer);
+        IDirect3DVertexBuffer9_Release(This->vertex_buffer);
+        IDirect3DVertexDeclaration9_Release(This->vertex_declaration);
+        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;
+    UINT numelements;
+
+    TRACE("(%p)\n", This);
+
+    if (declaration == NULL) return D3DERR_INVALIDCALL;
+
+    return IDirect3DVertexDeclaration9_GetDeclaration(This->vertex_declaration,
+                                                      declaration,
+                                                      &numelements);
+}
+
+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)\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;
+    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;
+    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
  */
@@ -599,12 +941,99 @@ BOOL WINAPI D3DXSphereBoundProbe(CONST D3DXVECTOR3 *pcenter, FLOAT radius, CONST
     return TRUE;
 }
 
+/*************************************************************************
+ * D3DXCreateMesh
+ */
 HRESULT WINAPI D3DXCreateMesh(DWORD numfaces, DWORD numvertices, DWORD options, CONST D3DVERTEXELEMENT9 *declaration,
                               LPDIRECT3DDEVICE9 device, LPD3DXMESH *mesh)
 {
-    FIXME("(%d, %d, %d, %p, %p, %p): stub\n", numfaces, numvertices, options, declaration, device, mesh);
+    HRESULT hr;
+    DWORD fvf;
+    IDirect3DVertexDeclaration9 *vertex_declaration;
+    IDirect3DVertexBuffer9 *vertex_buffer;
+    IDirect3DIndexBuffer9 *index_buffer;
+    ID3DXMeshImpl *object;
 
-    return E_NOTIMPL;
+    TRACE("(%d, %d, %d, %p, %p, %p)\n", numfaces, numvertices, options, declaration, device, mesh);
+
+    if (numfaces == 0 || numvertices == 0 || declaration == NULL || device == NULL || mesh == NULL)
+    {
+        return D3DERR_INVALIDCALL;
+    }
+
+    hr = D3DXFVFFromDeclarator(declaration, &fvf);
+    if (hr != D3D_OK)
+    {
+        fvf = 0;
+    }
+
+    /* Create vertex declaration */
+    hr = IDirect3DDevice9_CreateVertexDeclaration(device,
+                                                  declaration,
+                                                  &vertex_declaration);
+    if (FAILED(hr))
+    {
+        WARN("Unexpected return value %x from IDirect3DDevice9_CreateVertexDeclaration.\n",hr);
+        return hr;
+    }
+
+    /* Create vertex buffer */
+    hr = IDirect3DDevice9_CreateVertexBuffer(device,
+                                             numvertices * D3DXGetDeclVertexSize(declaration, declaration[0].Stream),
+                                             0,
+                                             fvf,
+                                             D3DPOOL_MANAGED,
+                                             &vertex_buffer,
+                                             NULL);
+    if (FAILED(hr))
+    {
+        WARN("Unexpected return value %x from IDirect3DDevice9_CreateVertexBuffer.\n",hr);
+        IDirect3DVertexDeclaration9_Release(vertex_declaration);
+        return hr;
+    }
+
+    /* Create index buffer */
+    hr = IDirect3DDevice9_CreateIndexBuffer(device,
+                                            numfaces * 6, /* 3 vertices per triangle, 2 triangles per face */
+                                            0,
+                                            D3DFMT_INDEX16,
+                                            D3DPOOL_MANAGED,
+                                            &index_buffer,
+                                            NULL);
+    if (FAILED(hr))
+    {
+        WARN("Unexpected return value %x from IDirect3DDevice9_CreateVertexBuffer.\n",hr);
+        IDirect3DVertexBuffer9_Release(vertex_buffer);
+        IDirect3DVertexDeclaration9_Release(vertex_declaration);
+        return hr;
+    }
+
+    object = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(ID3DXMeshImpl));
+    if (object == NULL)
+    {
+        IDirect3DIndexBuffer9_Release(index_buffer);
+        IDirect3DVertexBuffer9_Release(vertex_buffer);
+        IDirect3DVertexDeclaration9_Release(vertex_declaration);
+        *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_declaration = vertex_declaration;
+    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,
index e5a49bfe2488d36aa4423d724f04c7c53d2f4fe8..05df51ec82f92df38dab9ed5e50076a18c8a6875 100644 (file)
@@ -58,6 +58,9 @@ struct mesh
 
     DWORD number_of_faces;
     face *faces;
+
+    DWORD fvf;
+    UINT vertex_size;
 };
 
 static void free_mesh(struct mesh *mesh)
@@ -132,11 +135,18 @@ static void compare_mesh(const char *name, ID3DXMesh *d3dxmesh, struct mesh *mes
             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.FVF == mesh->fvf, "Test %s, result %x, expected %x\n",
+               name, vertex_buffer_description.FVF, mesh->fvf);
+            if (mesh->fvf == 0)
+            {
+                expected = number_of_vertices * mesh->vertex_size;
+            }
+            else
+            {
+                expected = number_of_vertices * D3DXGetFVFVertexSize(mesh->fvf);
+            }
             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 */
@@ -191,7 +201,7 @@ static void compare_mesh(const char *name, ID3DXMesh *d3dxmesh, struct mesh *mes
                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);
             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;
@@ -995,19 +1005,30 @@ static void D3DXCreateMeshTest(void)
     ID3DXMesh *d3dxmesh;
     int i, size;
     D3DVERTEXELEMENT9 test_decl[MAX_FVF_DECL_SIZE];
-    DWORD fvf, options;
+    DWORD options;
     struct mesh mesh;
 
-    static const D3DVERTEXELEMENT9 decl[3] = {
+    static const D3DVERTEXELEMENT9 decl1[3] = {
         {0, 0, D3DDECLTYPE_FLOAT3, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION, 0},
-        {0, 0xC, D3DDECLTYPE_FLOAT3, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_NORMAL, 0},
+        {0, 12, D3DDECLTYPE_FLOAT3, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_NORMAL, 0},
         D3DDECL_END(), };
 
+    static const D3DVERTEXELEMENT9 decl2[] = {
+        {0, 0, D3DDECLTYPE_FLOAT3, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION, 0},
+        {0, 12, D3DDECLTYPE_FLOAT3, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_NORMAL, 0},
+        {0, 24, D3DDECLTYPE_FLOAT1, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_PSIZE, 0},
+        {0, 28, D3DDECLTYPE_D3DCOLOR, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_COLOR, 1},
+        {0, 32, D3DDECLTYPE_FLOAT1, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_TEXCOORD, 0},
+        /* 8 bytes padding */
+        {0, 44, D3DDECLTYPE_FLOAT4, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_TEXCOORD, 1},
+        D3DDECL_END(),
+    };
+
     hr = D3DXCreateMesh(0, 0, 0, NULL, 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 = D3DXCreateMesh(1, 3, D3DXMESH_MANAGED, decl, NULL, &d3dxmesh);
-    todo_wine ok(hr == D3DERR_INVALIDCALL, "Got result %x, expected %x (D3DERR_INVALIDCALL)\n", hr, D3DERR_INVALIDCALL);
+    hr = D3DXCreateMesh(1, 3, D3DXMESH_MANAGED, decl1, NULL, &d3dxmesh);
+    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);
     if (!wnd)
@@ -1035,14 +1056,14 @@ static void D3DXCreateMeshTest(void)
         return;
     }
 
-    hr = D3DXCreateMesh(0, 3, D3DXMESH_MANAGED, decl, device, &d3dxmesh);
-    todo_wine ok(hr == D3DERR_INVALIDCALL, "Got result %x, expected %x (D3DERR_INVALIDCALL)\n", hr, D3DERR_INVALIDCALL);
+    hr = D3DXCreateMesh(0, 3, D3DXMESH_MANAGED, decl1, device, &d3dxmesh);
+    ok(hr == D3DERR_INVALIDCALL, "Got result %x, expected %x (D3DERR_INVALIDCALL)\n", hr, D3DERR_INVALIDCALL);
 
-    hr = D3DXCreateMesh(1, 0, D3DXMESH_MANAGED, decl, device, &d3dxmesh);
-    todo_wine ok(hr == D3DERR_INVALIDCALL, "Got result %x, expected %x (D3DERR_INVALIDCALL)\n", hr, D3DERR_INVALIDCALL);
+    hr = D3DXCreateMesh(1, 0, D3DXMESH_MANAGED, decl1, device, &d3dxmesh);
+    ok(hr == D3DERR_INVALIDCALL, "Got result %x, expected %x (D3DERR_INVALIDCALL)\n", hr, D3DERR_INVALIDCALL);
 
-    hr = D3DXCreateMesh(1, 3, 0, decl, device, &d3dxmesh);
-    todo_wine ok(hr == D3D_OK, "Got result %x, expected %x (D3D_OK)\n", hr, D3D_OK);
+    hr = D3DXCreateMesh(1, 3, 0, decl1, device, &d3dxmesh);
+    ok(hr == D3D_OK, "Got result %x, expected %x (D3D_OK)\n", hr, D3D_OK);
 
     if (hr == D3D_OK)
     {
@@ -1050,13 +1071,13 @@ static void D3DXCreateMeshTest(void)
     }
 
     hr = D3DXCreateMesh(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 = D3DXCreateMesh(1, 3, D3DXMESH_MANAGED, decl, device, NULL);
-    todo_wine ok(hr == D3DERR_INVALIDCALL, "Got result %x, expected %x (D3DERR_INVALIDCALL)\n", hr, D3DERR_INVALIDCALL);
+    hr = D3DXCreateMesh(1, 3, D3DXMESH_MANAGED, decl1, device, NULL);
+    ok(hr == D3DERR_INVALIDCALL, "Got result %x, expected %x (D3DERR_INVALIDCALL)\n", hr, D3DERR_INVALIDCALL);
 
-    hr = D3DXCreateMesh(1, 3, D3DXMESH_MANAGED, decl, device, &d3dxmesh);
-    todo_wine ok(hr == D3D_OK, "Got result %x, expected 0 (D3D_OK)\n", hr);
+    hr = D3DXCreateMesh(1, 3, D3DXMESH_MANAGED, decl1, device, &d3dxmesh);
+    ok(hr == D3D_OK, "Got result %x, expected 0 (D3D_OK)\n", hr);
 
     if (hr == D3D_OK)
     {
@@ -1074,27 +1095,87 @@ static void D3DXCreateMeshTest(void)
         }
 
         /* declaration */
+        hr = d3dxmesh->lpVtbl->GetDeclaration(d3dxmesh, NULL);
+        ok(hr == D3DERR_INVALIDCALL, "Got result %x, expected %x (D3DERR_INVALIDCALL)\n", hr, D3DERR_INVALIDCALL);
+
         hr = d3dxmesh->lpVtbl->GetDeclaration(d3dxmesh, test_decl);
         ok(hr == D3D_OK, "Got result %x, expected 0 (D3D_OK)\n", hr);
 
         if (hr == D3D_OK)
         {
-            size = sizeof(decl) / sizeof(decl[0]);
+            size = sizeof(decl1) / sizeof(decl1[0]);
             for (i = 0; i < size - 1; i++)
             {
-                ok(test_decl[i].Stream == decl[i].Stream, "Returned stream %d, expected %d\n", test_decl[i].Stream, decl[i].Stream);
-                ok(test_decl[i].Type == decl[i].Type, "Returned type %d, expected %d\n", test_decl[i].Type, decl[i].Type);
-                ok(test_decl[i].Method == decl[i].Method, "Returned method %d, expected %d\n", test_decl[i].Method, decl[i].Method);
-                ok(test_decl[i].Usage == decl[i].Usage, "Returned usage %d, expected %d\n", test_decl[i].Usage, decl[i].Usage);
-                ok(test_decl[i].UsageIndex == decl[i].UsageIndex, "Returned usage index %d, expected %d\n", test_decl[i].UsageIndex, decl[i].UsageIndex);
-                ok(test_decl[i].Offset == decl[i].Offset, "Returned offset %d, expected %d\n", decl[1].Offset, decl[i].Offset);
+                ok(test_decl[i].Stream == decl1[i].Stream, "Returned stream %d, expected %d\n", test_decl[i].Stream, decl1[i].Stream);
+                ok(test_decl[i].Type == decl1[i].Type, "Returned type %d, expected %d\n", test_decl[i].Type, decl1[i].Type);
+                ok(test_decl[i].Method == decl1[i].Method, "Returned method %d, expected %d\n", test_decl[i].Method, decl1[i].Method);
+                ok(test_decl[i].Usage == decl1[i].Usage, "Returned usage %d, expected %d\n", test_decl[i].Usage, decl1[i].Usage);
+                ok(test_decl[i].UsageIndex == decl1[i].UsageIndex, "Returned usage index %d, expected %d\n", test_decl[i].UsageIndex, decl1[i].UsageIndex);
+                ok(test_decl[i].Offset == decl1[i].Offset, "Returned offset %d, expected %d\n", test_decl[i].Offset, decl1[i].Offset);
             }
-            ok(decl[size-1].Stream == 0xFF, "Returned too long vertex declaration\n"); /* end element */
+            ok(decl1[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));
+            mesh.fvf = D3DFVF_XYZ | D3DFVF_NORMAL;
+
+            compare_mesh("createmesh1", d3dxmesh, &mesh);
+
+            free_mesh(&mesh);
+        }
+
+        d3dxmesh->lpVtbl->Release(d3dxmesh);
+    }
+
+    /* Test a declaration that can't be converted to an FVF. */
+    hr = D3DXCreateMesh(1, 3, D3DXMESH_MANAGED, decl2, device, &d3dxmesh);
+    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, test_decl);
+        ok(hr == D3D_OK, "Got result %x, expected 0 (D3D_OK)\n", hr);
+
+        if (hr == D3D_OK)
+        {
+            size = sizeof(decl2) / sizeof(decl2[0]);
+            for (i = 0; i < size - 1; i++)
+            {
+                ok(test_decl[i].Stream == decl2[i].Stream, "Returned stream %d, expected %d\n", test_decl[i].Stream, decl2[i].Stream);
+                ok(test_decl[i].Type == decl2[i].Type, "Returned type %d, expected %d\n", test_decl[i].Type, decl2[i].Type);
+                ok(test_decl[i].Method == decl2[i].Method, "Returned method %d, expected %d\n", test_decl[i].Method, decl2[i].Method);
+                ok(test_decl[i].Usage == decl2[i].Usage, "Returned usage %d, expected %d\n", test_decl[i].Usage, decl2[i].Usage);
+                ok(test_decl[i].UsageIndex == decl2[i].UsageIndex, "Returned usage index %d, expected %d\n", test_decl[i].UsageIndex, decl2[i].UsageIndex);
+                ok(test_decl[i].Offset == decl2[i].Offset, "Returned offset %d, expected %d\n", test_decl[i].Offset, decl2[i].Offset);
+            }
+            ok(decl2[size-1].Stream == 0xFF, "Returned too long vertex declaration\n"); /* end element */
+        }
 
         /* options */
         options = d3dxmesh->lpVtbl->GetOptions(d3dxmesh);
@@ -1109,8 +1190,10 @@ static void D3DXCreateMeshTest(void)
         {
             memset(mesh.vertices, 0, mesh.number_of_vertices * sizeof(*mesh.vertices));
             memset(mesh.faces, 0, mesh.number_of_faces * sizeof(*mesh.faces));
+            mesh.fvf = 0;
+            mesh.vertex_size = 60;
 
-            compare_mesh("createmeshfvf", d3dxmesh, &mesh);
+            compare_mesh("createmesh2", d3dxmesh, &mesh);
 
             free_mesh(&mesh);
         }
@@ -1327,6 +1410,8 @@ static void test_sphere(IDirect3DDevice9 *device, FLOAT radius, UINT slices, UIN
         return;
     }
 
+    mesh.fvf = D3DFVF_XYZ | D3DFVF_NORMAL;
+
     sprintf(name, "sphere (%g, %u, %u)", radius, slices, stacks);
     compare_mesh(name, sphere, &mesh);