Changeset View
Changeset View
Standalone View
Standalone View
source/blender/blenkernel/intern/armature.c
| Show First 20 Lines • Show All 1,408 Lines • ▼ Show 20 Lines | |||||
| * | * | ||||
| * pose_mat(b)= pose_mat(b-1) * yoffs(b-1) * d_root(b) * bone_mat(b) * chan_mat(b) | * pose_mat(b)= pose_mat(b-1) * yoffs(b-1) * d_root(b) * bone_mat(b) * chan_mat(b) | ||||
| * | * | ||||
| * we then - in init deform - store the deform in chan_mat, such that: | * we then - in init deform - store the deform in chan_mat, such that: | ||||
| * | * | ||||
| * pose_mat(b)= arm_mat(b) * chan_mat(b) | * pose_mat(b)= arm_mat(b) * chan_mat(b) | ||||
| * | * | ||||
| * *************************************************************************** */ | * *************************************************************************** */ | ||||
| /* Computes vector and roll based on a rotation. | /* Computes vector and roll based on a rotation. | ||||
| * "mat" must contain only a rotation, and no scaling. */ | * "mat" must contain only a rotation, and no scaling. */ | ||||
| void mat3_to_vec_roll(float mat[3][3], float r_vec[3], float *r_roll) | void mat3_to_vec_roll(float mat[3][3], float r_vec[3], float *r_roll) | ||||
| { | { | ||||
| if (r_vec) { | if (r_vec) { | ||||
| copy_v3_v3(r_vec, mat[1]); | copy_v3_v3(r_vec, mat[1]); | ||||
| } | } | ||||
| if (r_roll) { | if (r_roll) { | ||||
| float vecmat[3][3], vecmatinv[3][3], rollmat[3][3]; | float vecmat[3][3], vecmatinv[3][3], rollmat[3][3]; | ||||
| vec_roll_to_mat3(mat[1], 0.0f, vecmat); | vec_roll_to_mat3(mat[1], 0.0f, vecmat); | ||||
| invert_m3_m3(vecmatinv, vecmat); | invert_m3_m3(vecmatinv, vecmat); | ||||
| mul_m3_m3m3(rollmat, vecmatinv, mat); | mul_m3_m3m3(rollmat, vecmatinv, mat); | ||||
| *r_roll = atan2f(rollmat[2][0], rollmat[2][2]); | *r_roll = atan2f(rollmat[2][0], rollmat[2][2]); | ||||
| } | } | ||||
| } | } | ||||
| /* Calculates the rest matrix of a bone based | /* Calculates the rest matrix of a bone based on its vector and a roll around that vector. */ | ||||
| * On its vector and a roll around that vector */ | /* Given v = (v.x, v.y, v.z) our (normalized) bone vector, we want the rotation matrix M | ||||
| * from the Y axis (so that M * (0, 1, 0) = v). | |||||
| * -> The rotation axis a lays on XZ plane, and it is orthonormal to v, hence to the projection of v onto XZ plane. | |||||
| * -> a = (v.z, 0, -v.x) | |||||
| * We know a is eigenvector of M (so M * a = a). | |||||
| * Finally, we have w, such that M * w = (0, 1, 0) (i.e. the vector that will be aligned with Y axis once transformed). | |||||
| * We know w is symmetric to v by the Y axis. | |||||
| * -> w = (-v.x, v.y, -v.z) | |||||
| * | |||||
| * Solving this, we get (x, y and z being the components of v): | |||||
| * ┌ (x^2 * y + z^2) / (x^2 + z^2), x, x * z * (y - 1) / (x^2 + z^2) ┐ | |||||
| * M = │ x * (y^2 - 1) / (x^2 + z^2), y, z * (y^2 - 1) / (x^2 + z^2) │ | |||||
| * └ x * z * (y - 1) / (x^2 + z^2), z, (x^2 + z^2 * y) / (x^2 + z^2) ┘ | |||||
| * | |||||
| * This is stable as long as v (the bone) is not too much aligned with +/-Y (i.e. x and z components | |||||
| * are not too close to 0). | |||||
| * | |||||
| * Since v is normalized, we have x^2 + y^2 + z^2 = 1, hence x^2 + z^2 = 1 - y^2 = (1 - y)(1 + y). | |||||
| * This allows to simplifies M like this: | |||||
| * ┌ 1 - x^2 / (1 + y), x, -x * z / (1 + y) ┐ | |||||
| * M = │ -x, y, -z │ | |||||
| * └ -x * z / (1 + y), z, 1 - z^2 / (1 + y) ┘ | |||||
| * | |||||
| * Written this way, we see the case v = +Y is no more a singularity. The only one remaining is the bone being | |||||
| * aligned with -Y. | |||||
| * | |||||
| * Let's handle the asymptotic behavior when bone vector is reaching the limit of y = -1. Each of the four corner | |||||
| * elements can vary from -1 to 1, depending on the axis a chosen for doing the rotation. And the "rotation" here | |||||
| * is in fact established by mirroring XZ plane by that given axis, then inversing the Y-axis. | |||||
| * For sufficiently small x and z, and with y approaching -1, all elements but the four corner ones of M | |||||
| * will degenerate. So let's now focus on these corner elements. | |||||
| * | |||||
| * We rewrite M so that it only contains its four corner elements, and combine the 1 / (1 + y) factor: | |||||
| * ┌ 1 + y - x^2, -x * z ┐ | |||||
| * M* = 1 / (1 + y) * │ │ | |||||
| * └ -x * z, 1 + y - z^2 ┘ | |||||
| * | |||||
| * When y is close to -1, computing 1 / (1 + y) will cause severe numerical instability, so we ignore it and | |||||
| * normalize M instead. We know y^2 = 1 - (x^2 + z^2), and y < 0, hence y = -sqrt(1 - (x^2 + z^2)). | |||||
| * Since x and z are both close to 0, we apply the binomial expansion to the first order: | |||||
| * y = -sqrt(1 - (x^2 + z^2)) = -1 + (x^2 + z^2) / 2. Which gives: | |||||
| * ┌ z^2 - x^2, -2 * x * z ┐ | |||||
| * M* = 1 / (x^2 + z^2) * │ │ | |||||
| * └ -2 * x * z, x^2 - z^2 ┘ | |||||
| */ | |||||
| void vec_roll_to_mat3(const float vec[3], const float roll, float mat[3][3]) | void vec_roll_to_mat3(const float vec[3], const float roll, float mat[3][3]) | ||||
| { | { | ||||
| float nor[3], axis[3], target[3] = {0, 1, 0}; | float nor[3]; | ||||
| float theta; | float theta; | ||||
| float rMatrix[3][3], bMatrix[3][3]; | float rMatrix[3][3], bMatrix[3][3]; | ||||
| normalize_v3_v3(nor, vec); | normalize_v3_v3(nor, vec); | ||||
| /* Find Axis & Amount for bone matrix */ | theta = 1 + nor[1]; | ||||
| cross_v3_v3v3(axis, target, nor); | |||||
| /* was 0.0000000000001, caused bug [#23954], smaller values give unstable | /* With old algo, 1.0e-13f caused T23954 and T31333, 1.0e-6f caused T27675 and T30438, | ||||
| * roll when toggling editmode. | * so using 1.0e-9f as best compromise. | ||||
| * | |||||
| * was 0.00001, causes bug [#27675], with 0.00000495, | |||||
| * so a value inbetween these is needed. | |||||
| * | * | ||||
| * was 0.000001, causes bug [#30438] (which is same as [#27675, imho). | * New algo is supposed much more precise, since less complex computations are performed, | ||||
| * Resetting it to org value seems to cause no more [#23954]... | * but it uses two different threshold values... | ||||
| * | |||||
| * was 0.0000000000001, caused bug [#31333], smaller values give unstable | |||||
| * roll when toggling editmode again... | |||||
| * No good value here, trying 0.000000001 as best compromise. :/ | |||||
| */ | */ | ||||
| if (len_squared_v3(axis) > 1.0e-9f) { | if (theta > 1.0e-9f) { | ||||
| /* if nor is *not* a multiple of target ... */ | /* nor is *not* -Y. | ||||
| normalize_v3(axis); | * We got these values for free... so be happy with it... ;) | ||||
| */ | |||||
| theta = angle_normalized_v3v3(target, nor); | bMatrix[0][1] = -nor[0]; | ||||
| bMatrix[1][0] = nor[0]; | |||||
| /* Make Bone matrix*/ | bMatrix[1][1] = nor[1]; | ||||
| axis_angle_normalized_to_mat3(bMatrix, axis, theta); | bMatrix[1][2] = nor[2]; | ||||
| bMatrix[2][1] = -nor[2]; | |||||
| if (theta > 1.0e-5f) { | |||||
| /* If nor is far enough from -Y, apply the general case. */ | |||||
| bMatrix[0][0] = 1 - nor[0] * nor[0] / theta; | |||||
| bMatrix[2][2] = 1 - nor[2] * nor[2] / theta; | |||||
| bMatrix[2][0] = bMatrix[0][2] = -nor[0] * nor[2] / theta; | |||||
| } | |||||
| else { | |||||
| /* If nor is too close to -Y, apply the special case. */ | |||||
| theta = nor[0] * nor[0] + nor[2] * nor[2]; | |||||
| bMatrix[0][0] = (nor[0] + nor[2]) * (nor[0] - nor[2]) / theta; | |||||
| bMatrix[2][2] = -bMatrix[0][0]; | |||||
| bMatrix[2][0] = bMatrix[0][2] = 2.0f * nor[0] * nor[2] / theta; | |||||
| } | |||||
| } | } | ||||
| else { | else { | ||||
| /* if nor is a multiple of target ... */ | /* If nor is -Y, simple symmetry by Z axis. */ | ||||
| float updown; | unit_m3(bMatrix); | ||||
| bMatrix[0][0] = bMatrix[1][1] = -1.0; | |||||
| /* point same direction, or opposite? */ | |||||
| updown = (dot_v3v3(target, nor) > 0) ? 1.0f : -1.0f; | |||||
| /* I think this should work... */ | |||||
| bMatrix[0][0] = updown; bMatrix[0][1] = 0.0; bMatrix[0][2] = 0.0; | |||||
| bMatrix[1][0] = 0.0; bMatrix[1][1] = updown; bMatrix[1][2] = 0.0; | |||||
| bMatrix[2][0] = 0.0; bMatrix[2][1] = 0.0; bMatrix[2][2] = 1.0; | |||||
| } | } | ||||
| /* Make Roll matrix */ | /* Make Roll matrix */ | ||||
| axis_angle_normalized_to_mat3(rMatrix, nor, roll); | axis_angle_normalized_to_mat3(rMatrix, nor, roll); | ||||
| /* Combine and output result */ | /* Combine and output result */ | ||||
| mul_m3_m3m3(mat, rMatrix, bMatrix); | mul_m3_m3m3(mat, rMatrix, bMatrix); | ||||
| } | } | ||||
| ▲ Show 20 Lines • Show All 1,089 Lines • Show Last 20 Lines | |||||