https://git.reactos.org/?p=reactos.git;a=commitdiff;h=692992f2bf0d703c02800…
commit 692992f2bf0d703c0280094df166fe4506cfa519
Author: winesync <ros-dev(a)reactos.org>
AuthorDate: Sat Jan 4 02:11:30 2020 +0100
Commit: Jérôme Gardou <zefklop(a)users.noreply.github.com>
CommitDate: Wed Feb 26 18:19:18 2020 +0100
[WINESYNC]d3dx9_36: Implement ID3DXSkinInfoImpl_UpdateSkinnedMesh.
This patch fixes last problem of bug 32572.
wine-staging patch by Christian Costa <titan.costa(a)gmail.com>
---
dll/directx/wine/d3dx9_36/skin.c | 86 ++++++++-
modules/rostests/winetests/d3dx9_36/mesh.c | 83 +++++++++
...lement_ID3DXSkinInfoImpl_UpdateSkinnedMesh.diff | 206 +++++++++++++++++++++
3 files changed, 372 insertions(+), 3 deletions(-)
diff --git a/dll/directx/wine/d3dx9_36/skin.c b/dll/directx/wine/d3dx9_36/skin.c
index bed4b9f49d2..0a305a99236 100644
--- a/dll/directx/wine/d3dx9_36/skin.c
+++ b/dll/directx/wine/d3dx9_36/skin.c
@@ -2,6 +2,7 @@
* Skin Info operations specific to D3DX9.
*
* Copyright (C) 2011 Dylan Smith
+ * Copyright (C) 2013 Christian Costa
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
@@ -377,10 +378,89 @@ static HRESULT WINAPI d3dx9_skin_info_GetDeclaration(ID3DXSkinInfo
*iface,
static HRESULT WINAPI d3dx9_skin_info_UpdateSkinnedMesh(ID3DXSkinInfo *iface, const
D3DXMATRIX *bone_transforms,
const D3DXMATRIX *bone_inv_transpose_transforms, const void *src_vertices, void
*dst_vertices)
{
- FIXME("iface %p, bone_transforms %p, bone_inv_transpose_transforms %p,
src_vertices %p, dst_vertices %p stub!\n",
- iface, bone_transforms, bone_inv_transpose_transforms, src_vertices,
dst_vertices);
+ struct d3dx9_skin_info *skin = impl_from_ID3DXSkinInfo(iface);
+ DWORD size = D3DXGetFVFVertexSize(skin->fvf);
+ DWORD i, j;
- return E_NOTIMPL;
+ TRACE("iface %p, bone_transforms %p, bone_inv_transpose_transforms %p,
src_vertices %p, dst_vertices %p\n",
+ skin, bone_transforms, bone_inv_transpose_transforms, src_vertices,
dst_vertices);
+
+ if (bone_inv_transpose_transforms)
+ FIXME("Skinning vertices with two position elements not supported\n");
+
+ if ((skin->fvf & D3DFVF_POSITION_MASK) != D3DFVF_XYZ) {
+ FIXME("Vertex type %#x not supported\n", skin->fvf &
D3DFVF_POSITION_MASK);
+ return E_FAIL;
+ }
+
+ /* Reset all positions */
+ for (i = 0; i < skin->num_vertices; i++) {
+ D3DXVECTOR3 *position = (D3DXVECTOR3*)((BYTE*)dst_vertices + size * i);
+ position->x = 0.0f;
+ position->y = 0.0f;
+ position->z = 0.0f;
+ }
+
+ /* Update positions that are influenced by bones */
+ for (i = 0; i < skin->num_bones; i++) {
+ D3DXMATRIX bone_inverse, matrix;
+
+ D3DXMatrixInverse(&bone_inverse, NULL, &skin->bones[i].transform);
+ D3DXMatrixMultiply(&matrix, &bone_transforms[i], &bone_inverse);
+ D3DXMatrixMultiply(&matrix, &matrix, &skin->bones[i].transform);
+
+ for (j = 0; j < skin->bones[i].num_influences; j++) {
+ D3DXVECTOR3 position;
+ D3DXVECTOR3 *position_src = (D3DXVECTOR3*)((BYTE*)src_vertices + size *
skin->bones[i].vertices[j]);
+ D3DXVECTOR3 *position_dest = (D3DXVECTOR3*)((BYTE*)dst_vertices + size *
skin->bones[i].vertices[j]);
+ FLOAT weight = skin->bones[i].weights[j];
+
+ D3DXVec3TransformCoord(&position, position_src, &matrix);
+ position_dest->x += weight * position.x;
+ position_dest->y += weight * position.y;
+ position_dest->z += weight * position.z;
+ }
+ }
+
+ if (skin->fvf & D3DFVF_NORMAL) {
+ /* Reset all normals */
+ for (i = 0; i < skin->num_vertices; i++) {
+ D3DXVECTOR3 *normal = (D3DXVECTOR3*)((BYTE*)dst_vertices + size * i +
sizeof(D3DXVECTOR3));
+ normal->x = 0.0f;
+ normal->y = 0.0f;
+ normal->z = 0.0f;
+ }
+
+ /* Update normals that are influenced by bones */
+ for (i = 0; i < skin->num_bones; i++) {
+ D3DXMATRIX bone_inverse, matrix;
+
+ D3DXMatrixInverse(&bone_inverse, NULL,
&skin->bones[i].transform);
+ D3DXMatrixMultiply(&matrix, &skin->bones[i].transform,
&bone_transforms[i]);
+
+ for (j = 0; j < skin->bones[i].num_influences; j++) {
+ D3DXVECTOR3 normal;
+ D3DXVECTOR3 *normal_src = (D3DXVECTOR3*)((BYTE*)src_vertices + size *
skin->bones[i].vertices[j] + sizeof(D3DXVECTOR3));
+ D3DXVECTOR3 *normal_dest = (D3DXVECTOR3*)((BYTE*)dst_vertices + size *
skin->bones[i].vertices[j] + sizeof(D3DXVECTOR3));
+ FLOAT weight = skin->bones[i].weights[j];
+
+ D3DXVec3TransformNormal(&normal, normal_src, &bone_inverse);
+ D3DXVec3TransformNormal(&normal, &normal, &matrix);
+ normal_dest->x += weight * normal.x;
+ normal_dest->y += weight * normal.y;
+ normal_dest->z += weight * normal.z;
+ }
+ }
+
+ /* Normalize all normals that are influenced by bones*/
+ for (i = 0; i < skin->num_vertices; i++) {
+ D3DXVECTOR3 *normal_dest = (D3DXVECTOR3*)((BYTE*)dst_vertices + (i * size) +
sizeof(D3DXVECTOR3));
+ if ((normal_dest->x != 0.0f) && (normal_dest->y != 0.0f)
&& (normal_dest->z != 0.0f))
+ D3DXVec3Normalize(normal_dest, normal_dest);
+ }
+ }
+
+ return D3D_OK;
}
static HRESULT WINAPI d3dx9_skin_info_ConvertToBlendedMesh(ID3DXSkinInfo *iface,
ID3DXMesh *mesh_in,
diff --git a/modules/rostests/winetests/d3dx9_36/mesh.c
b/modules/rostests/winetests/d3dx9_36/mesh.c
index 35dd6c4c3b0..77f513f78dc 100644
--- a/modules/rostests/winetests/d3dx9_36/mesh.c
+++ b/modules/rostests/winetests/d3dx9_36/mesh.c
@@ -5316,6 +5316,88 @@ static void test_create_skin_info(void)
ok(hr == D3DERR_INVALIDCALL, "Expected D3DERR_INVALIDCALL, got %#x\n",
hr);
}
+static void test_update_skinned_mesh(void)
+{
+ static DWORD bone0_vertices[2] = { 1, 3 };
+ static FLOAT bone0_weights[2] = { 1.0f, 0.5f };
+ static DWORD bone1_vertices[2] = { 2, 3 };
+ static FLOAT bone1_weights[2] = { 1.0f, 0.5f };
+ static D3DMATRIX bones_matrix[2] =
+ { { { {
+ 1.0f, 0.0f, 0.0f, 0.0f,
+ 0.0f, 1.0f, 0.0f, 0.0f,
+ 0.0f, 0.0f, 1.0f, 0.0f,
+ 2.0f, 2.0f, 4.0f, 1.0f
+ } } },
+ { { {
+ 1.0f, 0.0f, 0.0f, 0.0f,
+ 0.0f, 1.0f, 0.0f, 0.0f,
+ 0.0f, 0.0f, 1.0f, 0.0f,
+ -4.0f, -4.0f, 4.0f, 1.0f
+ } } } };
+ static D3DVECTOR vertices_src[] = {{ 1.0f, 1.0f, 1.0f },
+ { 1.0f, 0.0f, 0.0f },
+ { 1.0f, 1.0f, -1.0f },
+ { 0.0f, 1.0f, 0.0f },
+ { -1.0f, -1.0f, 1.0f },
+ { 0.0f, 0.0f, 1.0f },
+ { -1.0f, -1.0f, -1.0f },
+ { -1.0f, 0.0f, 0.0f },
+ };
+ static D3DVECTOR vertices_ref[] = {{ 0.0f, 0.0f, 0.0f },
+ { 0.0f, 0.0f, 0.0f },
+ { 3.0f, 3.0f, 3.0f },
+ { 0.0f, 1.0f, 0.0f },
+ { -5.0f, -5.0f, 5.0f },
+ { 0.0f, 0.0f, 1.0f },
+ { -2.0f, -2.0f, 3.0f },
+ { -1.0f, 0.0f, 0.0f },
+ };
+ D3DVECTOR vertices_dest[8];
+ HRESULT hr;
+ ID3DXSkinInfo *skin_info;
+ D3DXMATRIX matrix;
+ int i;
+
+ D3DXMatrixIdentity(&matrix);
+ for (i = 0; i < 8; i++)
+ {
+ vertices_dest[i].x = 10000.0f;
+ vertices_dest[i].y = 10000.0f;
+ vertices_dest[i].z = 10000.0f;
+ }
+
+ hr = D3DXCreateSkinInfoFVF(4, D3DFVF_XYZ | D3DFVF_NORMAL, 2, &skin_info);
+ ok(hr == D3D_OK, "Expected D3D_OK, got %#x\n", hr);
+
+ skin_info->lpVtbl->SetBoneInfluence(skin_info, 0, 2, bone0_vertices,
bone0_weights);
+ ok(hr == D3D_OK, "Expected D3D_OK, got %#x\n", hr);
+ skin_info->lpVtbl->SetBoneOffsetMatrix(skin_info, 0, &matrix);
+ ok(hr == D3D_OK, "Expected D3D_OK, got %#x\n", hr);
+ skin_info->lpVtbl->SetBoneInfluence(skin_info, 1, 2, bone1_vertices,
bone1_weights);
+ ok(hr == D3D_OK, "Expected D3D_OK, got %#x\n", hr);
+ skin_info->lpVtbl->SetBoneOffsetMatrix(skin_info, 1, &matrix);
+ ok(hr == D3D_OK, "Expected D3D_OK, got %#x\n", hr);
+ skin_info->lpVtbl->UpdateSkinnedMesh(skin_info, bones_matrix, NULL,
vertices_src, vertices_dest);
+ ok(hr == D3D_OK, "Expected D3D_OK, got %#x\n", hr);
+ for (i = 0; i < 4; i++)
+ {
+ ok(compare(vertices_dest[i*2].x, vertices_ref[i*2].x),
"Vertex[%d].position.x: got %g, expected %g\n",
+ i, vertices_dest[i*2].x, vertices_ref[i*2].x);
+ ok(compare(vertices_dest[i*2].y, vertices_ref[i*2].y),
"Vertex[%d].position.y: got %g, expected %g\n",
+ i, vertices_dest[i*2].y, vertices_ref[i*2].y);
+ ok(compare(vertices_dest[i*2].z, vertices_ref[i*2].z),
"Vertex[%d].position.z: got %g, expected %g\n",
+ i, vertices_dest[i*2].z, vertices_ref[i*2].z);
+ ok(compare(vertices_dest[i*2+1].x, vertices_ref[i*2+1].x),
"Vertex[%d].normal.x: got %g, expected %g\n",
+ i, vertices_dest[i*2+1].x, vertices_ref[i*2+1].x);
+ ok(compare(vertices_dest[i*2+1].y, vertices_ref[i*2+1].y),
"Vertex[%d].normal.y: got %g, expected %g\n",
+ i, vertices_dest[i*2+1].y, vertices_ref[i*2+1].y);
+ ok(compare(vertices_dest[i*2+1].z, vertices_ref[i*2+1].z),
"Vertex[%d].normal.z: got %g, expected %g\n",
+ i, vertices_dest[i*2+1].z, vertices_ref[i*2+1].z);
+ }
+ skin_info->lpVtbl->Release(skin_info);
+}
+
static void test_convert_adjacency_to_point_reps(void)
{
HRESULT hr;
@@ -11393,6 +11475,7 @@ START_TEST(mesh)
D3DXGenerateAdjacencyTest();
test_update_semantics();
test_create_skin_info();
+ test_update_skinned_mesh();
test_convert_adjacency_to_point_reps();
test_convert_point_reps_to_adjacency();
test_weld_vertices();
diff --git
a/sdk/tools/winesync/d3dx9_staging/0024-d3dx9_36__Implement_ID3DXSkinInfoImpl_UpdateSkinnedMesh.diff
b/sdk/tools/winesync/d3dx9_staging/0024-d3dx9_36__Implement_ID3DXSkinInfoImpl_UpdateSkinnedMesh.diff
new file mode 100644
index 00000000000..9ef27a4688c
--- /dev/null
+++
b/sdk/tools/winesync/d3dx9_staging/0024-d3dx9_36__Implement_ID3DXSkinInfoImpl_UpdateSkinnedMesh.diff
@@ -0,0 +1,206 @@
+diff --git a/dll/directx/wine/d3dx9_36/skin.c b/dll/directx/wine/d3dx9_36/skin.c
+index bed4b9f..0a305a9 100644
+--- a/dll/directx/wine/d3dx9_36/skin.c
++++ b/dll/directx/wine/d3dx9_36/skin.c
+@@ -2,6 +2,7 @@
+ * Skin Info operations specific to D3DX9.
+ *
+ * Copyright (C) 2011 Dylan Smith
++ * Copyright (C) 2013 Christian Costa
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+@@ -377,10 +378,89 @@ static HRESULT WINAPI d3dx9_skin_info_GetDeclaration(ID3DXSkinInfo
*iface,
+ static HRESULT WINAPI d3dx9_skin_info_UpdateSkinnedMesh(ID3DXSkinInfo *iface, const
D3DXMATRIX *bone_transforms,
+ const D3DXMATRIX *bone_inv_transpose_transforms, const void *src_vertices, void
*dst_vertices)
+ {
+- FIXME("iface %p, bone_transforms %p, bone_inv_transpose_transforms %p,
src_vertices %p, dst_vertices %p stub!\n",
+- iface, bone_transforms, bone_inv_transpose_transforms, src_vertices,
dst_vertices);
++ struct d3dx9_skin_info *skin = impl_from_ID3DXSkinInfo(iface);
++ DWORD size = D3DXGetFVFVertexSize(skin->fvf);
++ DWORD i, j;
+
+- return E_NOTIMPL;
++ TRACE("iface %p, bone_transforms %p, bone_inv_transpose_transforms %p,
src_vertices %p, dst_vertices %p\n",
++ skin, bone_transforms, bone_inv_transpose_transforms, src_vertices,
dst_vertices);
++
++ if (bone_inv_transpose_transforms)
++ FIXME("Skinning vertices with two position elements not
supported\n");
++
++ if ((skin->fvf & D3DFVF_POSITION_MASK) != D3DFVF_XYZ) {
++ FIXME("Vertex type %#x not supported\n", skin->fvf &
D3DFVF_POSITION_MASK);
++ return E_FAIL;
++ }
++
++ /* Reset all positions */
++ for (i = 0; i < skin->num_vertices; i++) {
++ D3DXVECTOR3 *position = (D3DXVECTOR3*)((BYTE*)dst_vertices + size * i);
++ position->x = 0.0f;
++ position->y = 0.0f;
++ position->z = 0.0f;
++ }
++
++ /* Update positions that are influenced by bones */
++ for (i = 0; i < skin->num_bones; i++) {
++ D3DXMATRIX bone_inverse, matrix;
++
++ D3DXMatrixInverse(&bone_inverse, NULL, &skin->bones[i].transform);
++ D3DXMatrixMultiply(&matrix, &bone_transforms[i], &bone_inverse);
++ D3DXMatrixMultiply(&matrix, &matrix, &skin->bones[i].transform);
++
++ for (j = 0; j < skin->bones[i].num_influences; j++) {
++ D3DXVECTOR3 position;
++ D3DXVECTOR3 *position_src = (D3DXVECTOR3*)((BYTE*)src_vertices + size *
skin->bones[i].vertices[j]);
++ D3DXVECTOR3 *position_dest = (D3DXVECTOR3*)((BYTE*)dst_vertices + size *
skin->bones[i].vertices[j]);
++ FLOAT weight = skin->bones[i].weights[j];
++
++ D3DXVec3TransformCoord(&position, position_src, &matrix);
++ position_dest->x += weight * position.x;
++ position_dest->y += weight * position.y;
++ position_dest->z += weight * position.z;
++ }
++ }
++
++ if (skin->fvf & D3DFVF_NORMAL) {
++ /* Reset all normals */
++ for (i = 0; i < skin->num_vertices; i++) {
++ D3DXVECTOR3 *normal = (D3DXVECTOR3*)((BYTE*)dst_vertices + size * i +
sizeof(D3DXVECTOR3));
++ normal->x = 0.0f;
++ normal->y = 0.0f;
++ normal->z = 0.0f;
++ }
++
++ /* Update normals that are influenced by bones */
++ for (i = 0; i < skin->num_bones; i++) {
++ D3DXMATRIX bone_inverse, matrix;
++
++ D3DXMatrixInverse(&bone_inverse, NULL,
&skin->bones[i].transform);
++ D3DXMatrixMultiply(&matrix, &skin->bones[i].transform,
&bone_transforms[i]);
++
++ for (j = 0; j < skin->bones[i].num_influences; j++) {
++ D3DXVECTOR3 normal;
++ D3DXVECTOR3 *normal_src = (D3DXVECTOR3*)((BYTE*)src_vertices + size *
skin->bones[i].vertices[j] + sizeof(D3DXVECTOR3));
++ D3DXVECTOR3 *normal_dest = (D3DXVECTOR3*)((BYTE*)dst_vertices + size *
skin->bones[i].vertices[j] + sizeof(D3DXVECTOR3));
++ FLOAT weight = skin->bones[i].weights[j];
++
++ D3DXVec3TransformNormal(&normal, normal_src, &bone_inverse);
++ D3DXVec3TransformNormal(&normal, &normal, &matrix);
++ normal_dest->x += weight * normal.x;
++ normal_dest->y += weight * normal.y;
++ normal_dest->z += weight * normal.z;
++ }
++ }
++
++ /* Normalize all normals that are influenced by bones*/
++ for (i = 0; i < skin->num_vertices; i++) {
++ D3DXVECTOR3 *normal_dest = (D3DXVECTOR3*)((BYTE*)dst_vertices + (i * size) +
sizeof(D3DXVECTOR3));
++ if ((normal_dest->x != 0.0f) && (normal_dest->y != 0.0f)
&& (normal_dest->z != 0.0f))
++ D3DXVec3Normalize(normal_dest, normal_dest);
++ }
++ }
++
++ return D3D_OK;
+ }
+
+ static HRESULT WINAPI d3dx9_skin_info_ConvertToBlendedMesh(ID3DXSkinInfo *iface,
ID3DXMesh *mesh_in,
+diff --git a/modules/rostests/winetests/d3dx9_36/mesh.c
b/modules/rostests/winetests/d3dx9_36/mesh.c
+index ea0b867..c12bf60 100644
+--- a/modules/rostests/winetests/d3dx9_36/mesh.c
++++ b/modules/rostests/winetests/d3dx9_36/mesh.c
+@@ -5316,6 +5316,88 @@ static void test_create_skin_info(void)
+ ok(hr == D3DERR_INVALIDCALL, "Expected D3DERR_INVALIDCALL, got %#x\n",
hr);
+ }
+
++static void test_update_skinned_mesh(void)
++{
++ static DWORD bone0_vertices[2] = { 1, 3 };
++ static FLOAT bone0_weights[2] = { 1.0f, 0.5f };
++ static DWORD bone1_vertices[2] = { 2, 3 };
++ static FLOAT bone1_weights[2] = { 1.0f, 0.5f };
++ static D3DMATRIX bones_matrix[2] =
++ { { { {
++ 1.0f, 0.0f, 0.0f, 0.0f,
++ 0.0f, 1.0f, 0.0f, 0.0f,
++ 0.0f, 0.0f, 1.0f, 0.0f,
++ 2.0f, 2.0f, 4.0f, 1.0f
++ } } },
++ { { {
++ 1.0f, 0.0f, 0.0f, 0.0f,
++ 0.0f, 1.0f, 0.0f, 0.0f,
++ 0.0f, 0.0f, 1.0f, 0.0f,
++ -4.0f, -4.0f, 4.0f, 1.0f
++ } } } };
++ static D3DVECTOR vertices_src[] = {{ 1.0f, 1.0f, 1.0f },
++ { 1.0f, 0.0f, 0.0f },
++ { 1.0f, 1.0f, -1.0f },
++ { 0.0f, 1.0f, 0.0f },
++ { -1.0f, -1.0f, 1.0f },
++ { 0.0f, 0.0f, 1.0f },
++ { -1.0f, -1.0f, -1.0f },
++ { -1.0f, 0.0f, 0.0f },
++ };
++ static D3DVECTOR vertices_ref[] = {{ 0.0f, 0.0f, 0.0f },
++ { 0.0f, 0.0f, 0.0f },
++ { 3.0f, 3.0f, 3.0f },
++ { 0.0f, 1.0f, 0.0f },
++ { -5.0f, -5.0f, 5.0f },
++ { 0.0f, 0.0f, 1.0f },
++ { -2.0f, -2.0f, 3.0f },
++ { -1.0f, 0.0f, 0.0f },
++ };
++ D3DVECTOR vertices_dest[8];
++ HRESULT hr;
++ ID3DXSkinInfo *skin_info;
++ D3DXMATRIX matrix;
++ int i;
++
++ D3DXMatrixIdentity(&matrix);
++ for (i = 0; i < 8; i++)
++ {
++ vertices_dest[i].x = 10000.0f;
++ vertices_dest[i].y = 10000.0f;
++ vertices_dest[i].z = 10000.0f;
++ }
++
++ hr = D3DXCreateSkinInfoFVF(4, D3DFVF_XYZ | D3DFVF_NORMAL, 2, &skin_info);
++ ok(hr == D3D_OK, "Expected D3D_OK, got %#x\n", hr);
++
++ skin_info->lpVtbl->SetBoneInfluence(skin_info, 0, 2, bone0_vertices,
bone0_weights);
++ ok(hr == D3D_OK, "Expected D3D_OK, got %#x\n", hr);
++ skin_info->lpVtbl->SetBoneOffsetMatrix(skin_info, 0, &matrix);
++ ok(hr == D3D_OK, "Expected D3D_OK, got %#x\n", hr);
++ skin_info->lpVtbl->SetBoneInfluence(skin_info, 1, 2, bone1_vertices,
bone1_weights);
++ ok(hr == D3D_OK, "Expected D3D_OK, got %#x\n", hr);
++ skin_info->lpVtbl->SetBoneOffsetMatrix(skin_info, 1, &matrix);
++ ok(hr == D3D_OK, "Expected D3D_OK, got %#x\n", hr);
++ skin_info->lpVtbl->UpdateSkinnedMesh(skin_info, bones_matrix, NULL,
vertices_src, vertices_dest);
++ ok(hr == D3D_OK, "Expected D3D_OK, got %#x\n", hr);
++ for (i = 0; i < 4; i++)
++ {
++ ok(compare(vertices_dest[i*2].x, vertices_ref[i*2].x),
"Vertex[%d].position.x: got %g, expected %g\n",
++ i, vertices_dest[i*2].x, vertices_ref[i*2].x);
++ ok(compare(vertices_dest[i*2].y, vertices_ref[i*2].y),
"Vertex[%d].position.y: got %g, expected %g\n",
++ i, vertices_dest[i*2].y, vertices_ref[i*2].y);
++ ok(compare(vertices_dest[i*2].z, vertices_ref[i*2].z),
"Vertex[%d].position.z: got %g, expected %g\n",
++ i, vertices_dest[i*2].z, vertices_ref[i*2].z);
++ ok(compare(vertices_dest[i*2+1].x, vertices_ref[i*2+1].x),
"Vertex[%d].normal.x: got %g, expected %g\n",
++ i, vertices_dest[i*2+1].x, vertices_ref[i*2+1].x);
++ ok(compare(vertices_dest[i*2+1].y, vertices_ref[i*2+1].y),
"Vertex[%d].normal.y: got %g, expected %g\n",
++ i, vertices_dest[i*2+1].y, vertices_ref[i*2+1].y);
++ ok(compare(vertices_dest[i*2+1].z, vertices_ref[i*2+1].z),
"Vertex[%d].normal.z: got %g, expected %g\n",
++ i, vertices_dest[i*2+1].z, vertices_ref[i*2+1].z);
++ }
++ skin_info->lpVtbl->Release(skin_info);
++}
++
+ static void test_convert_adjacency_to_point_reps(void)
+ {
+ HRESULT hr;
+@@ -11362,6 +11444,7 @@ START_TEST(mesh)
+ D3DXGenerateAdjacencyTest();
+ test_update_semantics();
+ test_create_skin_info();
++ test_update_skinned_mesh();
+ test_convert_adjacency_to_point_reps();
+ test_convert_point_reps_to_adjacency();
+ test_weld_vertices();