From 33e4e3219056f09ed8b406d154b87bafedb1ebe0 Mon Sep 17 00:00:00 2001
From: Christian Costa <titan.costa@gmail.com>
Date: Mon, 22 Jul 2013 21:51:20 +0200
Subject: d3dx9_36: Implement ID3DXSkinInfoImpl_UpdateSkinnedMesh.

This patch fixes last problem of bug 32572.
---
 dlls/d3dx9_36/skin.c       | 86 ++++++++++++++++++++++++++++++++++++++++++++--
 dlls/d3dx9_36/tests/mesh.c | 83 ++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 166 insertions(+), 3 deletions(-)

diff --git a/dlls/d3dx9_36/skin.c b/dlls/d3dx9_36/skin.c
index aa5acf2..bd7e581 100644
--- a/dlls/d3dx9_36/skin.c
+++ b/dlls/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
@@ -375,10 +376,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/dlls/d3dx9_36/tests/mesh.c b/dlls/d3dx9_36/tests/mesh.c
index 17c5d9c..e255eac 100644
--- a/dlls/d3dx9_36/tests/mesh.c
+++ b/dlls/d3dx9_36/tests/mesh.c
@@ -5152,6 +5152,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;
@@ -10468,6 +10550,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();
-- 
2.1.2

