Hi

I've been working on a test and improvements for CloneMesh as part of
GSoC 2011. I would be grateful if anyone could comment on my work.
There are five test cases in the attached patch:

  0. Basic mesh cloning. Declaration has position and normal, and the
new declaration is the same as the original.
  1. Same as test 0, but with 16-bit indices.
  2. Shows that the vertex buffer can be widened so that it has room
for a new vertex component. The declaration has position and normal,
and the new declaration has texture coordinates after position and
normal.
  3. Same as test 2, but the new declaration has the texture
coordinates in between position and normal.
  4. Shows that a vertex component can be converted into another type
if the new declaration specifies another type. The declaration has
position and normal. The normal is a FLOAT2 and in the new declaration
it is instead FLOAT16_2.

Test 0 and 1 tests what is already known to work in the current
implementation. Test 2, 3 and 4 do not work in the current
implementation because the original and new declarations are not the
same.

I've also improved the current CloneMesh implementation so that it
also passes test 2, 3, and 4. The patch is not complete because it
does not support converting all the possible types. I expect to
implement conversion for the remaining types during this week.

Again any comments or ideas are welcome.

Thanks,
Michael Mc Donnell
From caac5a9e2f6bbba3d05e52cf952f65db86b15099 Mon Sep 17 00:00:00 2001
From: Michael Mc Donnell <mich...@mcdonnell.dk>
Date: Sun, 24 Jul 2011 17:18:05 +0200
Subject: d3dx9: Implemented CloneMesh conversion and test.

---
 dlls/d3dx9_36/mesh.c       |  165 ++++++++++---
 dlls/d3dx9_36/tests/mesh.c |  614 ++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 749 insertions(+), 30 deletions(-)

diff --git a/dlls/d3dx9_36/mesh.c b/dlls/d3dx9_36/mesh.c
index 9a0fd03..b6c3792 100644
--- a/dlls/d3dx9_36/mesh.c
+++ b/dlls/d3dx9_36/mesh.c
@@ -65,6 +65,27 @@ typedef struct ID3DXMeshImpl
     D3DXATTRIBUTERANGE *attrib_table;
 } ID3DXMeshImpl;
 
+const UINT d3dx_decltype_size[D3DDECLTYPE_UNUSED] =
+{
+   /* D3DDECLTYPE_FLOAT1    */ 1 * 4,
+   /* D3DDECLTYPE_FLOAT2    */ 2 * 4,
+   /* D3DDECLTYPE_FLOAT3    */ 3 * 4,
+   /* D3DDECLTYPE_FLOAT4    */ 4 * 4,
+   /* D3DDECLTYPE_D3DCOLOR  */ 4 * 1,
+   /* D3DDECLTYPE_UBYTE4    */ 4 * 1,
+   /* D3DDECLTYPE_SHORT2    */ 2 * 2,
+   /* D3DDECLTYPE_SHORT4    */ 4 * 2,
+   /* D3DDECLTYPE_UBYTE4N   */ 4 * 1,
+   /* D3DDECLTYPE_SHORT2N   */ 2 * 2,
+   /* D3DDECLTYPE_SHORT4N   */ 4 * 2,
+   /* D3DDECLTYPE_USHORT2N  */ 2 * 2,
+   /* D3DDECLTYPE_USHORT4N  */ 4 * 2,
+   /* D3DDECLTYPE_UDEC3     */ 4, /* 3 * 10 bits + 2 padding */
+   /* D3DDECLTYPE_DEC3N     */ 4,
+   /* D3DDECLTYPE_FLOAT16_2 */ 2 * 2,
+   /* D3DDECLTYPE_FLOAT16_4 */ 4 * 2,
+};
+
 static inline ID3DXMeshImpl *impl_from_ID3DXMesh(ID3DXMesh *iface)
 {
     return CONTAINING_RECORD(iface, ID3DXMeshImpl, ID3DXMesh_iface);
@@ -260,6 +281,102 @@ static HRESULT WINAPI ID3DXMeshImpl_CloneMeshFVF(ID3DXMesh *iface, DWORD options
     return iface->lpVtbl->CloneMesh(iface, options, declaration, device, clone_mesh);
 }
 
+static INT get_equivalent_declaration_index(D3DVERTEXELEMENT9 orig_declaration, D3DVERTEXELEMENT9 *declaration)
+{
+    INT i;
+
+    for (i = 0; declaration[i].Stream != 0xff; i++)
+    {
+        if (orig_declaration.Method == declaration[i].Method
+            && orig_declaration.Usage == declaration[i].Usage
+            && orig_declaration.UsageIndex == declaration[i].UsageIndex)
+        {
+            return i;
+        }
+    }
+
+    return -1;
+}
+
+static void convert_component(BYTE *dst, BYTE *src, D3DDECLTYPE type_dst, D3DDECLTYPE type_src)
+{
+    BOOL fixme_once = FALSE;
+
+    switch (type_src)
+    {
+        case D3DDECLTYPE_FLOAT2:
+        {
+            switch (type_dst)
+            {
+                case D3DDECLTYPE_FLOAT16_2:
+                    D3DXFloat32To16Array((D3DXFLOAT16*)dst, (FLOAT*)src, 2);
+                    break;
+                default:
+                    break;
+            }
+            break;
+        }
+        default:
+            if (!fixme_once++)
+                FIXME("Conversion of D3DDECLTYPE %d to %d not implemented.\n", type_src, type_dst);
+            break;
+    }
+}
+
+static HRESULT convert_vertex_buffer(ID3DXMesh *mesh_dst, ID3DXMesh *mesh_src)
+{
+    HRESULT hr;
+    D3DVERTEXELEMENT9 orig_declaration[MAX_FVF_DECL_SIZE] = {D3DDECL_END()};
+    D3DVERTEXELEMENT9 declaration[MAX_FVF_DECL_SIZE] = {D3DDECL_END()};
+    BYTE *vb_dst = NULL;
+    BYTE *vb_src = NULL;
+    UINT i;
+    UINT num_vertices = mesh_src->lpVtbl->GetNumVertices(mesh_src);
+    UINT dst_vertex_size = mesh_dst->lpVtbl->GetNumBytesPerVertex(mesh_dst);
+    UINT src_vertex_size = mesh_src->lpVtbl->GetNumBytesPerVertex(mesh_src);
+
+    hr = mesh_src->lpVtbl->GetDeclaration(mesh_src, orig_declaration);
+    if (FAILED(hr)) return hr;
+    hr = mesh_dst->lpVtbl->GetDeclaration(mesh_dst, declaration);
+    if (FAILED(hr)) return hr;
+
+    hr = mesh_src->lpVtbl->LockVertexBuffer(mesh_src, D3DLOCK_READONLY, (void**)&vb_src);
+    if (FAILED(hr)) goto cleanup;
+    hr = mesh_dst->lpVtbl->LockVertexBuffer(mesh_dst, D3DLOCK_DISCARD, (void**)&vb_dst);
+    if (FAILED(hr)) goto cleanup;
+
+    /* Clear all new fields by clearing the entire vertex buffer. */
+    memset(vb_dst, 0, num_vertices * dst_vertex_size);
+
+    for (i = 0; orig_declaration[i].Stream != 0xff; i++)
+    {
+        INT eq_idx = get_equivalent_declaration_index(orig_declaration[i], declaration);
+
+        if (eq_idx >= 0)
+        {
+            UINT j;
+            for (j = 0; j < num_vertices; j++)
+            {
+                UINT idx_dst = dst_vertex_size * j + declaration[eq_idx].Offset;
+                UINT idx_src = src_vertex_size * j + orig_declaration[i].Offset;
+                UINT type_size = d3dx_decltype_size[orig_declaration[i].Type];
+
+                if (orig_declaration[i].Type == declaration[eq_idx].Type)
+                    memcpy(&vb_dst[idx_dst], &vb_src[idx_src], type_size);
+                else
+                   convert_component(&vb_dst[idx_dst], &vb_src[idx_src], declaration[eq_idx].Type, orig_declaration[i].Type);
+            }
+        }
+    }
+
+    hr = D3D_OK;
+cleanup:
+    if (vb_dst) mesh_dst->lpVtbl->UnlockVertexBuffer(mesh_dst);
+    if (vb_src) mesh_src->lpVtbl->UnlockVertexBuffer(mesh_src);
+
+    return hr;
+}
+
 static HRESULT WINAPI ID3DXMeshImpl_CloneMesh(ID3DXMesh *iface, DWORD options, CONST D3DVERTEXELEMENT9 *declaration, LPDIRECT3DDEVICE9 device,
                                               LPD3DXMESH *clone_mesh_out)
 {
@@ -269,8 +386,10 @@ static HRESULT WINAPI ID3DXMeshImpl_CloneMesh(ID3DXMesh *iface, DWORD options, C
     D3DVERTEXELEMENT9 orig_declaration[MAX_FVF_DECL_SIZE] = { D3DDECL_END() };
     void *data_in, *data_out;
     DWORD vertex_size;
+    DWORD orig_vertex_size;
     HRESULT hr;
     int i;
+    BOOL same_declaration = TRUE;
 
     TRACE("(%p)->(%x,%p,%p,%p)\n", This, options, declaration, device, clone_mesh_out);
 
@@ -280,27 +399,31 @@ static HRESULT WINAPI ID3DXMeshImpl_CloneMesh(ID3DXMesh *iface, DWORD options, C
     hr = iface->lpVtbl->GetDeclaration(iface, orig_declaration);
     if (FAILED(hr)) return hr;
 
-    for (i = 0; orig_declaration[i].Stream != 0xff; i++) {
-        if (memcmp(&orig_declaration[i], &declaration[i], sizeof(*declaration)))
-        {
-            FIXME("Vertex buffer conversion not implemented.\n");
-            return E_NOTIMPL;
-        }
-    }
-
     hr = D3DXCreateMesh(This->numfaces, This->numvertices, options & ~D3DXMESH_VB_SHARE,
                         declaration, device, &clone_mesh);
     if (FAILED(hr)) return hr;
 
     cloned_this = impl_from_ID3DXMesh(clone_mesh);
     vertex_size = clone_mesh->lpVtbl->GetNumBytesPerVertex(clone_mesh);
+    orig_vertex_size = iface->lpVtbl->GetNumBytesPerVertex(iface);
+
+    for (i = 0; orig_declaration[i].Stream != 0xff; i++) {
+        if (memcmp(&orig_declaration[i], &declaration[i], sizeof(*declaration))) {
+            same_declaration = FALSE;
+            break;
+        }
+    }
+
+    if (vertex_size != orig_vertex_size)
+        same_declaration = FALSE;
 
     if (options & D3DXMESH_VB_SHARE) {
+        if (!same_declaration) goto error;
         IDirect3DVertexBuffer9_AddRef(This->vertex_buffer);
         /* FIXME: refactor to avoid creating a new vertex buffer */
         IDirect3DVertexBuffer9_Release(cloned_this->vertex_buffer);
         cloned_this->vertex_buffer = This->vertex_buffer;
-    } else {
+    } else if (same_declaration) {
         hr = iface->lpVtbl->LockVertexBuffer(iface, D3DLOCK_READONLY, &data_in);
         if (FAILED(hr)) goto error;
         hr = clone_mesh->lpVtbl->LockVertexBuffer(clone_mesh, D3DLOCK_DISCARD, &data_out);
@@ -311,6 +434,9 @@ static HRESULT WINAPI ID3DXMeshImpl_CloneMesh(ID3DXMesh *iface, DWORD options, C
         memcpy(data_out, data_in, This->numvertices * vertex_size);
         clone_mesh->lpVtbl->UnlockVertexBuffer(clone_mesh);
         iface->lpVtbl->UnlockVertexBuffer(iface);
+    } else {
+        hr = convert_vertex_buffer(clone_mesh, iface);
+        if (FAILED(hr)) goto error;
     }
 
     hr = iface->lpVtbl->LockIndexBuffer(iface, D3DLOCK_READONLY, &data_in);
@@ -1601,27 +1727,6 @@ HRESULT WINAPI D3DXComputeBoundingSphere(CONST D3DXVECTOR3* pfirstposition, DWOR
     return D3D_OK;
 }
 
-static const UINT d3dx_decltype_size[D3DDECLTYPE_UNUSED] =
-{
-   /* D3DDECLTYPE_FLOAT1    */ 1 * 4,
-   /* D3DDECLTYPE_FLOAT2    */ 2 * 4,
-   /* D3DDECLTYPE_FLOAT3    */ 3 * 4,
-   /* D3DDECLTYPE_FLOAT4    */ 4 * 4,
-   /* D3DDECLTYPE_D3DCOLOR  */ 4 * 1,
-   /* D3DDECLTYPE_UBYTE4    */ 4 * 1,
-   /* D3DDECLTYPE_SHORT2    */ 2 * 2,
-   /* D3DDECLTYPE_SHORT4    */ 4 * 2,
-   /* D3DDECLTYPE_UBYTE4N   */ 4 * 1,
-   /* D3DDECLTYPE_SHORT2N   */ 2 * 2,
-   /* D3DDECLTYPE_SHORT4N   */ 4 * 2,
-   /* D3DDECLTYPE_USHORT2N  */ 2 * 2,
-   /* D3DDECLTYPE_USHORT4N  */ 4 * 2,
-   /* D3DDECLTYPE_UDEC3     */ 4, /* 3 * 10 bits + 2 padding */
-   /* D3DDECLTYPE_DEC3N     */ 4,
-   /* D3DDECLTYPE_FLOAT16_2 */ 2 * 2,
-   /* D3DDECLTYPE_FLOAT16_4 */ 4 * 2,
-};
-
 static void append_decl_element(D3DVERTEXELEMENT9 *declaration, UINT *idx, UINT *offset,
         D3DDECLTYPE type, D3DDECLUSAGE usage, UINT usage_idx)
 {
diff --git a/dlls/d3dx9_36/tests/mesh.c b/dlls/d3dx9_36/tests/mesh.c
index 39c78e6..7c1e19a 100644
--- a/dlls/d3dx9_36/tests/mesh.c
+++ b/dlls/d3dx9_36/tests/mesh.c
@@ -5967,6 +5967,619 @@ cleanup:
     free_test_context(test_context);
 }
 
+static HRESULT init_test_mesh(const DWORD num_faces, const DWORD num_vertices,
+                              const DWORD options,
+                              const D3DVERTEXELEMENT9 *declaration,
+                              IDirect3DDevice9 *device, ID3DXMesh **mesh_ptr,
+                              const void *vertices, const DWORD vertex_size,
+                              const DWORD *indices, const DWORD *attributes)
+{
+    HRESULT hr;
+    void *vertex_buffer;
+    void *index_buffer;
+    DWORD *attributes_buffer;
+    ID3DXMesh *mesh = NULL;
+
+    hr = D3DXCreateMesh(num_faces, num_vertices, options, declaration, device, mesh_ptr);
+    if (FAILED(hr))
+    {
+        skip("Couldn't create mesh. Got %x expected D3D_OK\n", hr);
+        goto cleanup;
+    }
+    mesh = *mesh_ptr;
+
+    hr = mesh->lpVtbl->LockVertexBuffer(mesh, 0, &vertex_buffer);
+    if (FAILED(hr))
+    {
+        skip("Couldn't lock vertex buffer.\n");
+        goto cleanup;
+    }
+    memcpy(vertex_buffer, vertices, num_vertices * vertex_size);
+    hr = mesh->lpVtbl->UnlockVertexBuffer(mesh);
+    if (FAILED(hr))
+    {
+        skip("Couldn't unlock vertex buffer.\n");
+        goto cleanup;
+    }
+
+    hr = mesh->lpVtbl->LockIndexBuffer(mesh, 0, &index_buffer);
+    if (FAILED(hr))
+    {
+        skip("Couldn't lock index buffer.\n");
+        goto cleanup;
+    }
+    if (options & D3DXMESH_32BIT)
+    {
+        if (indices)
+            memcpy(index_buffer, indices, 3 * num_faces * sizeof(DWORD));
+        else
+        {
+            /* Fill index buffer with 0, 1, 2, ...*/
+            DWORD *indices_32bit = (DWORD*)index_buffer;
+            UINT i;
+            for (i = 0; i < 3 * num_faces; i++)
+                indices_32bit[i] = i;
+        }
+    }
+    else
+    {
+        if (indices)
+            memcpy(index_buffer, indices, 3 * num_faces * sizeof(WORD));
+        else
+        {
+            /* Fill index buffer with 0, 1, 2, ...*/
+            WORD *indices_16bit = (WORD*)index_buffer;
+            UINT i;
+            for (i = 0; i < 3 * num_faces; i++)
+                indices_16bit[i] = i;
+        }
+    }
+    hr = mesh->lpVtbl->UnlockIndexBuffer(mesh);
+    if (FAILED(hr)) {
+        skip("Couldn't unlock index buffer.\n");
+        goto cleanup;
+    }
+
+    hr = mesh->lpVtbl->LockAttributeBuffer(mesh, 0, &attributes_buffer);
+    if (FAILED(hr))
+    {
+        skip("Couldn't lock attributes buffer.\n");
+        goto cleanup;
+    }
+
+    if (attributes)
+        memcpy(attributes_buffer, attributes, num_faces * sizeof(*attributes));
+    else
+        memset(attributes_buffer, 0, num_faces * sizeof(*attributes));
+
+    hr = mesh->lpVtbl->UnlockAttributeBuffer(mesh);
+    if (FAILED(hr))
+    {
+        skip("Couldn't unlock attributes buffer.\n");
+        goto cleanup;
+    }
+
+    hr = D3D_OK;
+cleanup:
+    return hr;
+}
+
+/* Using structs instead of bit-fields in order to avoid compiler issues. */
+struct udec3
+{
+    UINT x;
+    UINT y;
+    UINT z;
+    UINT w;
+};
+
+struct dec3n
+{
+    INT x;
+    INT y;
+    INT z;
+    INT w;
+};
+
+static struct udec3 dword_to_udec3(DWORD d)
+{
+    struct udec3 v;
+
+    v.x = d & 0x3ff;
+    v.y = (d & 0xffc00) >> 10;
+    v.z = (d & 0x3ff00000) >> 20;
+    v.w = (d & 0xc0000000) >> 30;
+
+    return v;
+}
+
+static struct dec3n dword_to_dec3n(DWORD d)
+{
+    struct dec3n v;
+
+    v.x = d & 0x3ff;
+    v.y = (d & 0xffc00) >> 10;
+    v.z = (d & 0x3ff00000) >> 20;
+    v.w = (d & 0xc0000000) >> 30;
+
+    return v;
+}
+
+static void check_vertex_components(int line, int mesh_number, int vertex_number, BYTE *got_ptr, const BYTE *exp_ptr, const D3DVERTEXELEMENT9 *declaration)
+{
+    const char *usage_strings[] =
+    {
+        "position",
+        "blend weight",
+        "blend indices",
+        "normal",
+        "point size",
+        "texture coordinates",
+        "tangent",
+        "binormal",
+        "tessellation factor",
+        "position transformed",
+        "color",
+        "fog",
+        "depth",
+        "sample"
+    };
+    D3DVERTEXELEMENT9 *decl_ptr;
+
+    for (decl_ptr = (D3DVERTEXELEMENT9*)declaration; decl_ptr->Stream != 0xFF; decl_ptr++)
+    {
+        switch (decl_ptr->Type)
+        {
+            case D3DDECLTYPE_FLOAT1:
+            {
+                FLOAT *got = (FLOAT*)(got_ptr + decl_ptr->Offset);
+                FLOAT *exp = (FLOAT*)(exp_ptr + decl_ptr->Offset);
+                FLOAT diff = fabsf(*got - *exp);
+                ok (diff <= FLT_EPSILON, "Mesh %d: Got %f for vertex %d %s, expected %f.\n",
+                    mesh_number, *got, vertex_number, usage_strings[decl_ptr->Usage], *exp);
+                break;
+            }
+            case D3DDECLTYPE_FLOAT2:
+            {
+                D3DXVECTOR2 *got = (D3DXVECTOR2*)(got_ptr + decl_ptr->Offset);
+                D3DXVECTOR2 *exp = (D3DXVECTOR2*)(exp_ptr + decl_ptr->Offset);
+                FLOAT diff = fmaxf(fabsf(got->x - exp->x), fabsf(got->y - exp->y));
+                ok_(__FILE__,line)(diff <= FLT_EPSILON, "Mesh %d: Got (%f, %f) for vertex %d %s, expected (%f, %f).\n",
+                    mesh_number, got->x, got->y, vertex_number, usage_strings[decl_ptr->Usage], exp->x, exp->y);
+                break;
+            }
+            case D3DDECLTYPE_FLOAT3:
+            {
+                D3DXVECTOR3 *got = (D3DXVECTOR3*)(got_ptr + decl_ptr->Offset);
+                D3DXVECTOR3 *exp = (D3DXVECTOR3*)(exp_ptr + decl_ptr->Offset);
+                FLOAT diff = fmaxf(fabsf(got->x - exp->x), fabsf(got->y - exp->y));
+                diff = fmaxf(diff, fabsf(got->z - exp->z));
+                ok_(__FILE__,line)(diff <= FLT_EPSILON, "Mesh %d: Got (%f, %f, %f) for vertex %d %s, expected (%f, %f, %f).\n",
+                    mesh_number, got->x, got->y, got->z, vertex_number, usage_strings[decl_ptr->Usage], exp->x, exp->y, exp->z);
+                break;
+            }
+            case D3DDECLTYPE_FLOAT4:
+            {
+                D3DXVECTOR4 *got = (D3DXVECTOR4*)(got_ptr + decl_ptr->Offset);
+                D3DXVECTOR4 *exp = (D3DXVECTOR4*)(exp_ptr + decl_ptr->Offset);
+                FLOAT diff = fmaxf(fabsf(got->x - exp->x), fabsf(got->y - exp->y));
+                diff = fmaxf(diff, fabsf(got->z - exp->z));
+                diff = fmaxf(diff, fabsf(got->w - exp->w));
+                ok_(__FILE__,line)(diff <= FLT_EPSILON, "Mesh %d: Got (%f, %f, %f, %f) for vertex %d %s, expected (%f, %f, %f, %f).\n",
+                    mesh_number, got->x, got->y, got->z, got->w, vertex_number, usage_strings[decl_ptr->Usage], exp->x, exp->y, exp->z, got->w);
+                break;
+            }
+            case D3DDECLTYPE_D3DCOLOR:
+            {
+                BYTE *got = got_ptr + decl_ptr->Offset;
+                const BYTE *exp = exp_ptr + decl_ptr->Offset;
+                BOOL same_color = got[0] == exp[0] && got[1] == exp[1]
+                                  && got[2] == exp[2] && got[3] == exp[3];
+                const char *color_types[] = {"diffuse", "specular", "undefined color"};
+                BYTE usage_index = decl_ptr->UsageIndex;
+                if (usage_index > 1) usage_index = 2;
+                ok_(__FILE__,line)(same_color, "Mesh %d: Got (%u, %u, %u, %u) for vertex %d %s, expected (%u, %u, %u, %u).\n",
+                    mesh_number, got[0], got[1], got[2], got[3], vertex_number, color_types[usage_index], exp[0], exp[1], exp[2], exp[3]);
+                break;
+            }
+            case D3DDECLTYPE_UBYTE4:
+            case D3DDECLTYPE_UBYTE4N:
+            {
+                BYTE *got = got_ptr + decl_ptr->Offset;
+                const BYTE *exp = exp_ptr + decl_ptr->Offset;
+                BOOL same = got[0] == exp[0] && got[1] == exp[1]
+                            && got[2] == exp[2] && got[3] == exp[3];
+                ok_(__FILE__,line)(same, "Mesh %d: Got (%u, %u, %u, %u) for vertex %d %s, expected (%u, %u, %u, %u).\n",
+                    mesh_number, got[0], got[1], got[2], got[3], vertex_number, usage_strings[decl_ptr->Usage], exp[0], exp[1], exp[2], exp[3]);
+                break;
+            }
+            case D3DDECLTYPE_SHORT2:
+            case D3DDECLTYPE_SHORT2N:
+            {
+                SHORT *got = (SHORT*)(got_ptr + decl_ptr->Offset);
+                SHORT *exp = (SHORT*)(exp_ptr + decl_ptr->Offset);
+                BOOL same = got[0] == exp[0] && got[1] == exp[1];
+                ok_(__FILE__,line)(same, "Mesh %d: Got (%hd, %hd) for vertex %d %s, expected (%hd, %hd).\n",
+                    mesh_number, got[0], got[1], vertex_number, usage_strings[decl_ptr->Usage], exp[0], exp[1]);
+                break;
+            }
+            case D3DDECLTYPE_SHORT4:
+            case D3DDECLTYPE_SHORT4N:
+            {
+                SHORT *got = (SHORT*)(got_ptr + decl_ptr->Offset);
+                SHORT *exp = (SHORT*)(exp_ptr + decl_ptr->Offset);
+                BOOL same = got[0] == exp[0] && got[1] == exp[1]
+                            && got[2] == exp[2] && got[3] == exp[3];
+                ok_(__FILE__,line)(same, "Mesh %d: Got (%hd, %hd, %hd, %hd) for vertex %d %s, expected (%hd, %hd, %hd, %hd).\n",
+                    mesh_number, got[0], got[1], got[2], got[3], vertex_number, usage_strings[decl_ptr->Usage], exp[0], exp[1], exp[2], exp[3]);
+                break;
+            }
+            case D3DDECLTYPE_UDEC3:
+            {
+                DWORD *got = (DWORD*)(got_ptr + decl_ptr->Offset);
+                DWORD *exp = (DWORD*)(exp_ptr + decl_ptr->Offset);
+                BOOL same = memcmp(got, exp, sizeof(*got)) == 0;
+                struct udec3 got_udec3 = dword_to_udec3(*got);
+                struct udec3 exp_udec3 = dword_to_udec3(*exp);
+                ok_(__FILE__,line)(same, "Mesh %d: Got (%u, %u, %u, %u) for vertex %d %s, expected (%u, %u, %u, %u).\n",
+                    mesh_number, got_udec3.x, got_udec3.y, got_udec3.z, got_udec3.w, vertex_number, usage_strings[decl_ptr->Usage], exp_udec3.x, exp_udec3.y, exp_udec3.z, exp_udec3.w);
+
+                break;
+            }
+            case D3DDECLTYPE_DEC3N:
+            {
+                DWORD *got = (DWORD*)(got_ptr + decl_ptr->Offset);
+                DWORD *exp = (DWORD*)(exp_ptr + decl_ptr->Offset);
+                BOOL same = memcmp(got, exp, sizeof(*got)) == 0;
+                struct dec3n got_dec3n = dword_to_dec3n(*got);
+                struct dec3n exp_dec3n = dword_to_dec3n(*exp);
+                ok_(__FILE__,line)(same, "Mesh %d: Got (%d, %d, %d, %d) for vertex %d %s, expected (%d, %d, %d, %d).\n",
+                    mesh_number, got_dec3n.x, got_dec3n.y, got_dec3n.z, got_dec3n.w, vertex_number, usage_strings[decl_ptr->Usage], exp_dec3n.x, exp_dec3n.y, exp_dec3n.z, exp_dec3n.w);
+                break;
+            }
+            case D3DDECLTYPE_FLOAT16_2:
+            {
+                WORD *got = (WORD*)(got_ptr + decl_ptr->Offset);
+                WORD *exp = (WORD*)(exp_ptr + decl_ptr->Offset);
+                BOOL same = got[0] == exp[0] && got[1] == exp[1];
+                ok_(__FILE__,line)(same, "Mesh %d: Got (%hx, %hx) for vertex %d %s, expected (%hx, %hx).\n",
+                    mesh_number, got[0], got[1], vertex_number, usage_strings[decl_ptr->Usage], exp[0], exp[1]);
+                break;
+            }
+            case D3DDECLTYPE_FLOAT16_4:
+            {
+                WORD *got = (WORD*)(got_ptr + decl_ptr->Offset);
+                WORD *exp = (WORD*)(exp_ptr + decl_ptr->Offset);
+                BOOL same = got[0] == exp[0] && got[1] == exp[1]
+                            && got[2] == exp[2] && got[3] == exp[3];
+                ok_(__FILE__,line)(same, "Mesh %d: Got (%hx, %hx, %hx, %hx) for vertex %d %s, expected (%hx, %hx, %hx, %hx).\n",
+                    mesh_number, got[0], got[1], got[2], got[3], vertex_number, usage_strings[decl_ptr->Usage], exp[0], exp[1], exp[3], exp[4]);
+                break;
+            }
+            default:
+                break;
+        }
+    }
+}
+
+static void test_clone_mesh(void)
+{
+    HRESULT hr;
+    struct test_context *test_context = NULL;
+    const DWORD options = D3DXMESH_32BIT | D3DXMESH_SYSTEMMEM;
+    const D3DVERTEXELEMENT9 declaration_pn[] =
+    {
+        {0, 0, D3DDECLTYPE_FLOAT3, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION, 0},
+        {0, 12, D3DDECLTYPE_FLOAT3, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_NORMAL, 0},
+        D3DDECL_END()
+    };
+    const D3DVERTEXELEMENT9 declaration_pntc[] =
+    {
+        {0, 0, D3DDECLTYPE_FLOAT3, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION, 0},
+        {0, 12, D3DDECLTYPE_FLOAT3, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_NORMAL, 0},
+        {0, 24, D3DDECLTYPE_FLOAT2, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_TEXCOORD, 0},
+        D3DDECL_END()
+    };
+    const D3DVERTEXELEMENT9 declaration_ptcn[] =
+    {
+        {0, 0, D3DDECLTYPE_FLOAT3, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION, 0},
+        {0, 12, D3DDECLTYPE_FLOAT2, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_TEXCOORD, 0},
+        {0, 20, D3DDECLTYPE_FLOAT3, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_NORMAL, 0},
+        D3DDECL_END()
+    };
+    const D3DVERTEXELEMENT9 declaration_ptc[] =
+    {
+        {0, 0, D3DDECLTYPE_FLOAT3, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION, 0},
+        {0, 12, D3DDECLTYPE_FLOAT2, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_TEXCOORD, 0},
+        D3DDECL_END()
+    };
+    const D3DVERTEXELEMENT9 declaration_ptc_float16_2[] =
+    {
+        {0, 0, D3DDECLTYPE_FLOAT3, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION, 0},
+        {0, 12, D3DDECLTYPE_FLOAT16_2, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_TEXCOORD, 0},
+        D3DDECL_END()
+    };
+    const unsigned int VERTS_PER_FACE = 3;
+    BYTE *vertices = NULL;
+    INT i;
+    struct vertex_pn
+    {
+        D3DXVECTOR3 position;
+        D3DXVECTOR3 normal;
+    };
+    struct vertex_pntc
+    {
+        D3DXVECTOR3 position;
+        D3DXVECTOR3 normal;
+        D3DXVECTOR2 texcoords;
+    };
+    struct vertex_ptcn
+    {
+        D3DXVECTOR3 position;
+        D3DXVECTOR2 texcoords;
+        D3DXVECTOR3 normal;
+    };
+    struct vertex_ptc
+    {
+        D3DXVECTOR3 position;
+        D3DXVECTOR2 texcoords;
+    };
+    struct vertex_ptc_float16_2
+    {
+        D3DXVECTOR3 position;
+        WORD texcoords[2]; /* float16_2 */
+    };
+    D3DXVECTOR3 up = {0.0f, 0.0f, 1.0f};
+    D3DXVECTOR2 zero_vec2 = {0.0f, 0.0f};
+    /* Test 0. Check that a mesh can be cloned if the new declaration is the
+     * same as the one used to create the mesh.
+     *
+     * 0--1 3
+     * | / /|
+     * |/ / |
+     * 2 5--4
+     */
+    const struct vertex_pn vertices0[] =
+    {
+        {{ 0.0f,  3.0f,  0.f}, up},
+        {{ 2.0f,  3.0f,  0.f}, up},
+        {{ 0.0f,  0.0f,  0.f}, up},
+
+        {{ 3.0f,  3.0f,  0.f}, up},
+        {{ 3.0f,  0.0f,  0.f}, up},
+        {{ 1.0f,  0.0f,  0.f}, up},
+    };
+    const UINT num_vertices0 = ARRAY_SIZE(vertices0);
+    const UINT num_faces0 = ARRAY_SIZE(vertices0) / VERTS_PER_FACE;
+    const UINT vertex_size0 = sizeof(*vertices0);
+    /* Test 1. Check that 16-bit indices are handled. */
+    const DWORD options_16bit = D3DXMESH_SYSTEMMEM;
+    /* Test 2. Check that the size of each vertex is increased and the data
+     * moved if the new declaration adds an element after the original elements.
+     */
+    const struct vertex_pntc exp_vertices2[] =
+    {
+        {{ 0.0f,  3.0f,  0.f}, up, zero_vec2},
+        {{ 2.0f,  3.0f,  0.f}, up, zero_vec2},
+        {{ 0.0f,  0.0f,  0.f}, up, zero_vec2},
+
+        {{ 3.0f,  3.0f,  0.f}, up, zero_vec2},
+        {{ 3.0f,  0.0f,  0.f}, up, zero_vec2},
+        {{ 1.0f,  0.0f,  0.f}, up, zero_vec2},
+    };
+    const UINT exp_vertex_size2 = sizeof(*exp_vertices2);
+    /* Test 3. Check that the size of each vertex is increased and the data
+     * moved if the new declaration adds an element between the original
+     * elements.
+     */
+    const struct vertex_ptcn exp_vertices3[] =
+    {
+        {{ 0.0f,  3.0f,  0.f}, zero_vec2, up},
+        {{ 2.0f,  3.0f,  0.f}, zero_vec2, up},
+        {{ 0.0f,  0.0f,  0.f}, zero_vec2, up},
+
+        {{ 3.0f,  3.0f,  0.f}, zero_vec2, up},
+        {{ 3.0f,  0.0f,  0.f}, zero_vec2, up},
+        {{ 1.0f,  0.0f,  0.f}, zero_vec2, up},
+    };
+    const UINT exp_vertex_size3 = sizeof(*exp_vertices3);
+    /* Test 4. Test that data types can be converted, e.g. D3DXVECTOR2 to
+     * FLOAT16_2. */
+    const struct vertex_ptc vertices4[] =
+    {
+        {{ 0.0f,  3.0f,  0.f}, { 1.0f,  1.0f}},
+        {{ 2.0f,  3.0f,  0.f}, { 0.5f,  0.7f}},
+        {{ 0.0f,  0.0f,  0.f}, {-0.2f, -0.3f}},
+
+        {{ 3.0f,  3.0f,  0.f}, { 0.2f,  0.3f}},
+        {{ 3.0f,  0.0f,  0.f}, { 1.0f,  1.0f}},
+        {{ 1.0f,  0.0f,  0.f}, { 0.1f,  0.2f}},
+    };
+    const UINT num_vertices4 = ARRAY_SIZE(vertices4);
+    const UINT num_faces4 = ARRAY_SIZE(vertices4) / VERTS_PER_FACE;
+    const UINT vertex_size4 = sizeof(*vertices4);
+    const struct vertex_ptc_float16_2 exp_vertices4[] =
+    {
+        {{ 0.0f,  3.0f,  0.f}, {0x3c00, 0x3c00}}, /* {1.0f, 1.0f} */
+        {{ 2.0f,  3.0f,  0.f}, {0x3800, 0x399a}}, /* {0.5f, 0.7f} */
+        {{ 0.0f,  0.0f,  0.f}, {0xb266, 0xb4cd}}, /* {-0.2f, -0.3f} */
+
+        {{ 3.0f,  3.0f,  0.f}, {0x3266, 0x34cd}}, /* {0.2f, 0.3f} */
+        {{ 3.0f,  0.0f,  0.f}, {0x3c00, 0x3c00}}, /* {1.0f, 1.0f} */
+        {{ 1.0f,  0.0f,  0.f}, {0x2e66, 0x3266}}, /* {0.1f, 0.2f} */
+    };
+    const UINT exp_vertex_size4 = sizeof(*exp_vertices4);
+    /* Common mesh data */
+    ID3DXMesh *mesh = NULL;
+    ID3DXMesh *mesh_clone = NULL;
+    struct
+    {
+        const BYTE *vertices;
+        const DWORD *indices;
+        const DWORD *attributes;
+        const UINT num_vertices;
+        const UINT num_faces;
+        const UINT vertex_size;
+        const DWORD options;
+        const D3DVERTEXELEMENT9 *declaration;
+        const D3DVERTEXELEMENT9 *new_declaration;
+        const BYTE *exp_vertices;
+        const UINT exp_vertex_size;
+    }
+    tc[] =
+    {
+        {
+            (BYTE*)vertices0,
+            NULL,
+            NULL,
+            num_vertices0,
+            num_faces0,
+            vertex_size0,
+            options,
+            declaration_pn,
+            declaration_pn,
+            (BYTE*)vertices0,
+            vertex_size0
+        },
+        {
+            (BYTE*)vertices0,
+            NULL,
+            NULL,
+            num_vertices0,
+            num_faces0,
+            vertex_size0,
+            options_16bit,
+            declaration_pn,
+            declaration_pn,
+            (BYTE*)vertices0,
+            vertex_size0
+        },
+        {
+            (BYTE*)vertices0,
+            NULL,
+            NULL,
+            num_vertices0,
+            num_faces0,
+            vertex_size0,
+            options,
+            declaration_pn,
+            declaration_pntc,
+            (BYTE*)exp_vertices2,
+            exp_vertex_size2
+        },
+        {
+            (BYTE*)vertices0,
+            NULL,
+            NULL,
+            num_vertices0,
+            num_faces0,
+            vertex_size0,
+            options,
+            declaration_pn,
+            declaration_ptcn,
+            (BYTE*)exp_vertices3,
+            exp_vertex_size3
+        },
+        {
+            (BYTE*)vertices4,
+            NULL,
+            NULL,
+            num_vertices4,
+            num_faces4,
+            vertex_size4,
+            options,
+            declaration_ptc,
+            declaration_ptc_float16_2,
+            (BYTE*)exp_vertices4,
+            exp_vertex_size4
+        },
+    };
+
+    test_context = new_test_context();
+    if (!test_context)
+    {
+        skip("Couldn't create test context\n");
+        goto cleanup;
+    }
+
+    for (i = 0; i < ARRAY_SIZE(tc); i++)
+    {
+        UINT j;
+        D3DVERTEXELEMENT9 new_declaration[MAX_FVF_DECL_SIZE];
+        UINT exp_new_decl_length, new_decl_length;
+        UINT exp_new_decl_size, new_decl_size;
+
+        hr = init_test_mesh(tc[i].num_faces, tc[i].num_vertices,
+                              tc[i].options,
+                              tc[i].declaration,
+                              test_context->device, &mesh,
+                              tc[i].vertices, tc[i].vertex_size,
+                              tc[i].indices, tc[i].attributes);
+        if (FAILED(hr))
+        {
+            skip("Couldn't initialize test mesh %d. Got %x expected D3D_OK\n", i, hr);
+            goto cleanup;
+        }
+
+        hr = mesh->lpVtbl->CloneMesh(mesh, tc[i].options, tc[i].new_declaration,
+                                     test_context->device, &mesh_clone);
+        ok(hr == D3D_OK, "CloneMesh test case %d failed. Got %x\n, expected D3D_OK\n", i, hr);
+
+        hr = mesh_clone->lpVtbl->GetDeclaration(mesh_clone, new_declaration);
+        ok(hr == D3D_OK, "GetDeclaration test case %d failed. Got %x\n, expected D3D_OK\n", i, hr);
+        /* Check declaration elements */
+        for (j = 0; tc[i].new_declaration[j].Stream != 0xFF; j++)
+        {
+            ok(memcmp(&tc[i].new_declaration[j], &new_declaration[j], sizeof(*new_declaration)) == 0,
+               "Test case %d failed. Declaration element %d did not match.", i, j);
+        }
+
+        /* Check declaration length */
+        exp_new_decl_length = D3DXGetDeclLength(tc[i].new_declaration);
+        new_decl_length = D3DXGetDeclLength(new_declaration);
+        ok(new_decl_length == exp_new_decl_length,
+           "Test case %d failed. Got new declaration length %d, expected %d\n",
+           i, new_decl_length, exp_new_decl_length);
+
+        /* Check declaration size */
+        exp_new_decl_size = D3DXGetDeclVertexSize(tc[i].new_declaration, 0);
+        new_decl_size = D3DXGetDeclVertexSize(new_declaration, 0);
+        ok(new_decl_size == exp_new_decl_size,
+           "Test case %d failed. Got new declaration size %d, expected %d\n",
+           i, new_decl_size, exp_new_decl_size);
+
+        /* Check vertex data in cloned mesh */
+        hr = mesh_clone->lpVtbl->LockVertexBuffer(mesh_clone, 0, (void**)&vertices);
+        if (FAILED(hr))
+        {
+            skip("Couldn't lock cloned vertex buffer.\n");
+            goto cleanup;
+        }
+        for (j = 0; j < tc[i].num_vertices; j++)
+        {
+            UINT index = tc[i].exp_vertex_size * j;
+            check_vertex_components(__LINE__, i, j, &vertices[index], &tc[i].exp_vertices[index], tc[i].new_declaration);
+        }
+        hr = mesh_clone->lpVtbl->UnlockVertexBuffer(mesh_clone);
+        if (FAILED(hr))
+        {
+            skip("Couldn't unlock vertex buffer.\n");
+            goto cleanup;
+        }
+        vertices = NULL;
+        mesh->lpVtbl->Release(mesh);
+        mesh = NULL;
+        mesh_clone->lpVtbl->Release(mesh_clone);
+        mesh_clone = NULL;
+    }
+
+cleanup:
+    if (vertices) mesh->lpVtbl->UnlockVertexBuffer(mesh);
+    if (mesh) mesh->lpVtbl->Release(mesh);
+    if (mesh_clone) mesh_clone->lpVtbl->Release(mesh_clone);
+    free_test_context(test_context);
+}
+
 START_TEST(mesh)
 {
     D3DXBoundProbeTest();
@@ -5989,4 +6602,5 @@ START_TEST(mesh)
     test_create_skin_info();
     test_convert_adjacency_to_point_reps();
     test_convert_point_reps_to_adjacency();
+    test_clone_mesh();
 }
-- 
1.7.6



Reply via email to