Bug fix for avatar neck cauterization

The basic dual-quaternion skinning algorithm does not handle non-rigid transformations like scale well.
Because we only use scaling for head cauterization, we special case this by passing in a cauterization factor,
as well as a cauterization position to the vertex shader.  If a vertex is flagged as cauterized, we slam it to equal the cauterization position.
Although, not as smooth as the previous method, it seems to work well enough on the avatar's I've tested.

(cherry picked from commit faf8350369)
This commit is contained in:
Anthony J. Thibault 2018-01-25 15:57:48 -08:00
parent e081808ace
commit 21c1e32490
3 changed files with 52 additions and 10 deletions

View file

@ -115,6 +115,7 @@ void CauterizedModel::updateClusterMatrices() {
Transform clusterTransform;
Transform::mult(clusterTransform, jointTransform, cluster.inverseBindTransform);
state.clusterTransforms[j] = Model::TransformDualQuaternion(clusterTransform);
state.clusterTransforms[j].setCauterizationParameters(0.0f, jointPose.trans());
#else
auto jointMatrix = _rig.getJointTransform(cluster.jointIndex);
glm_mat4u_mul(jointMatrix, cluster.inverseBindMatrix, state.clusterTransforms[j]);
@ -151,6 +152,7 @@ void CauterizedModel::updateClusterMatrices() {
Transform clusterTransform;
Transform::mult(clusterTransform, jointTransform, cluster.inverseBindTransform);
state.clusterTransforms[j] = Model::TransformDualQuaternion(clusterTransform);
state.clusterTransforms[j].setCauterizationParameters(1.0f, cauterizePose.trans());
#else
glm_mat4u_mul(cauterizeMatrix, cluster.inverseBindMatrix, state.clusterTransforms[j]);
#endif

View file

@ -263,26 +263,34 @@ public:
_scale.x = p.scale().x;
_scale.y = p.scale().y;
_scale.z = p.scale().z;
_scale.w = 0.0f;
_dq = DualQuaternion(p.rot(), p.trans());
}
TransformDualQuaternion(const glm::vec3& scale, const glm::quat& rot, const glm::vec3& trans) {
_scale.x = scale.x;
_scale.y = scale.y;
_scale.z = scale.z;
_scale.w = 0.0f;
_dq = DualQuaternion(rot, trans);
}
TransformDualQuaternion(const Transform& transform) {
_scale = glm::vec4(transform.getScale(), 0.0f);
_scale.w = 0.0f;
_dq = DualQuaternion(transform.getRotation(), transform.getTranslation());
}
glm::vec3 getScale() const { return glm::vec3(_scale); }
glm::quat getRotation() const { return _dq.getRotation(); }
glm::vec3 getTranslation() const { return _dq.getTranslation(); }
glm::mat4 getMatrix() const { return createMatFromScaleQuatAndPos(getScale(), getRotation(), getTranslation()); };
void setCauterizationParameters(float cauterizationAmount, const glm::vec3& cauterizedPosition) {
_scale.w = cauterizationAmount;
_cauterizedPosition = glm::vec4(cauterizedPosition, 1.0f);
}
protected:
glm::vec4 _scale { 1.0f, 1.0f, 1.0f, 0.0f };
DualQuaternion _dq;
glm::vec4 _padding;
glm::vec4 _cauterizedPosition { 0.0f, 0.0f, 0.0f, 1.0f };
};
#endif

View file

@ -58,17 +58,19 @@ mat4 dualQuatToMat4(vec4 real, vec4 dual) {
void skinPosition(ivec4 skinClusterIndex, vec4 skinClusterWeight, vec4 inPosition, out vec4 skinnedPosition) {
// linearly blend scale and dual quaternion components
vec3 sAccum = vec3(0.0, 0.0, 0.0);
vec4 sAccum = vec4(0.0, 0.0, 0.0, 0.0);
vec4 rAccum = vec4(0.0, 0.0, 0.0, 0.0);
vec4 dAccum = vec4(0.0, 0.0, 0.0, 0.0);
vec4 cAccum = vec4(0.0, 0.0, 0.0, 0.0);
vec4 polarityReference = clusterMatrices[skinClusterIndex[0]][1];
for (int i = 0; i < INDICES_PER_VERTEX; i++) {
mat4 clusterMatrix = clusterMatrices[(skinClusterIndex[i])];
float clusterWeight = skinClusterWeight[i];
vec3 scale = vec3(clusterMatrix[0]);
vec4 scale = clusterMatrix[0];
vec4 real = clusterMatrix[1];
vec4 dual = clusterMatrix[2];
vec4 cauterizedPos = clusterMatrix[3];
// to ensure that we rotate along the shortest arc, reverse dual quaternions with negative polarity.
float dqClusterWeight = clusterWeight;
@ -79,6 +81,7 @@ void skinPosition(ivec4 skinClusterIndex, vec4 skinClusterWeight, vec4 inPositio
sAccum += scale * clusterWeight;
rAccum += real * dqClusterWeight;
dAccum += dual * dqClusterWeight;
cAccum += cauterizedPos * clusterWeight;
}
// normalize dual quaternion
@ -88,25 +91,34 @@ void skinPosition(ivec4 skinClusterIndex, vec4 skinClusterWeight, vec4 inPositio
// conversion from dual quaternion to 4x4 matrix.
mat4 m = dualQuatToMat4(rAccum, dAccum);
skinnedPosition = m * (vec4(sAccum, 1) * inPosition);
// an sAccum.w of > 0 indicates that this joint is cauterized.
if (sAccum.w > 0.1) {
skinnedPosition = cAccum;
} else {
sAccum.w = 1.0;
skinnedPosition = m * (sAccum * inPosition);
}
}
void skinPositionNormal(ivec4 skinClusterIndex, vec4 skinClusterWeight, vec4 inPosition, vec3 inNormal,
out vec4 skinnedPosition, out vec3 skinnedNormal) {
// linearly blend scale and dual quaternion components
vec3 sAccum = vec3(0.0, 0.0, 0.0);
vec4 sAccum = vec4(0.0, 0.0, 0.0, 0.0);
vec4 rAccum = vec4(0.0, 0.0, 0.0, 0.0);
vec4 dAccum = vec4(0.0, 0.0, 0.0, 0.0);
vec4 cAccum = vec4(0.0, 0.0, 0.0, 0.0);
vec4 polarityReference = clusterMatrices[skinClusterIndex[0]][1];
for (int i = 0; i < INDICES_PER_VERTEX; i++) {
mat4 clusterMatrix = clusterMatrices[(skinClusterIndex[i])];
float clusterWeight = skinClusterWeight[i];
vec3 scale = vec3(clusterMatrix[0]);
vec4 scale = clusterMatrix[0];
vec4 real = clusterMatrix[1];
vec4 dual = clusterMatrix[2];
vec4 cauterizedPos = clusterMatrix[3];
// to ensure that we rotate along the shortest arc, reverse dual quaternions with negative polarity.
float dqClusterWeight = clusterWeight;
@ -117,6 +129,7 @@ void skinPositionNormal(ivec4 skinClusterIndex, vec4 skinClusterWeight, vec4 inP
sAccum += scale * clusterWeight;
rAccum += real * dqClusterWeight;
dAccum += dual * dqClusterWeight;
cAccum += cauterizedPos * clusterWeight;
}
// normalize dual quaternion
@ -126,7 +139,15 @@ void skinPositionNormal(ivec4 skinClusterIndex, vec4 skinClusterWeight, vec4 inP
// conversion from dual quaternion to 4x4 matrix.
mat4 m = dualQuatToMat4(rAccum, dAccum);
skinnedPosition = m * (vec4(sAccum, 1) * inPosition);
// an sAccum.w of > 0 indicates that this joint is cauterized.
if (sAccum.w > 0.1) {
skinnedPosition = cAccum;
} else {
sAccum.w = 1.0;
skinnedPosition = m * (sAccum * inPosition);
}
skinnedNormal = vec3(m * vec4(inNormal, 0));
}
@ -134,18 +155,20 @@ void skinPositionNormalTangent(ivec4 skinClusterIndex, vec4 skinClusterWeight, v
out vec4 skinnedPosition, out vec3 skinnedNormal, out vec3 skinnedTangent) {
// linearly blend scale and dual quaternion components
vec3 sAccum = vec3(0.0, 0.0, 0.0);
vec4 sAccum = vec4(0.0, 0.0, 0.0, 0.0);
vec4 rAccum = vec4(0.0, 0.0, 0.0, 0.0);
vec4 dAccum = vec4(0.0, 0.0, 0.0, 0.0);
vec4 cAccum = vec4(0.0, 0.0, 0.0, 0.0);
vec4 polarityReference = clusterMatrices[skinClusterIndex[0]][1];
for (int i = 0; i < INDICES_PER_VERTEX; i++) {
mat4 clusterMatrix = clusterMatrices[(skinClusterIndex[i])];
float clusterWeight = skinClusterWeight[i];
vec3 scale = vec3(clusterMatrix[0]);
vec4 scale = clusterMatrix[0];
vec4 real = clusterMatrix[1];
vec4 dual = clusterMatrix[2];
vec4 cauterizedPos = clusterMatrix[3];
// to ensure that we rotate along the shortest arc, reverse dual quaternions with negative polarity.
float dqClusterWeight = clusterWeight;
@ -156,6 +179,7 @@ void skinPositionNormalTangent(ivec4 skinClusterIndex, vec4 skinClusterWeight, v
sAccum += scale * clusterWeight;
rAccum += real * dqClusterWeight;
dAccum += dual * dqClusterWeight;
cAccum += cauterizedPos * clusterWeight;
}
// normalize dual quaternion
@ -165,7 +189,15 @@ void skinPositionNormalTangent(ivec4 skinClusterIndex, vec4 skinClusterWeight, v
// conversion from dual quaternion to 4x4 matrix.
mat4 m = dualQuatToMat4(rAccum, dAccum);
skinnedPosition = m * (vec4(sAccum, 1) * inPosition);
// an sAccum.w of > 0 indicates that this vertex is cauterized.
if (sAccum.w > 0.1) {
skinnedPosition = cAccum;
} else {
sAccum.w = 1.0;
skinnedPosition = m * (sAccum * inPosition);
}
skinnedNormal = vec3(m * vec4(inNormal, 0));
skinnedTangent = vec3(m * vec4(inTangent, 0));
}