From 565875e823893528184f37649deeaac27b28abd6 Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Fri, 15 Dec 2017 15:23:10 -0800 Subject: [PATCH 01/32] WIP first attempt at dual quat skinning --- libraries/animation/src/Rig.cpp | 8 + libraries/animation/src/Rig.h | 1 + .../render-utils/src/CauterizedModel.cpp | 49 +++++++ .../render-utils/src/MeshPartPayload.cpp | 6 +- libraries/render-utils/src/Model.cpp | 25 ++++ libraries/render-utils/src/Skinning.slh | 112 ++++++++++---- libraries/shared/src/DualQuaternion.cpp | 81 ++++++++++ libraries/shared/src/DualQuaternion.h | 58 ++++++++ tests/shared/src/DualQuaternionTests.cpp | 138 ++++++++++++++++++ tests/shared/src/DualQuaternionTests.h | 26 ++++ 10 files changed, 475 insertions(+), 29 deletions(-) create mode 100644 libraries/shared/src/DualQuaternion.cpp create mode 100644 libraries/shared/src/DualQuaternion.h create mode 100644 tests/shared/src/DualQuaternionTests.cpp create mode 100644 tests/shared/src/DualQuaternionTests.h diff --git a/libraries/animation/src/Rig.cpp b/libraries/animation/src/Rig.cpp index 44745c5c2d..34a3207f47 100644 --- a/libraries/animation/src/Rig.cpp +++ b/libraries/animation/src/Rig.cpp @@ -1731,6 +1731,14 @@ glm::mat4 Rig::getJointTransform(int jointIndex) const { } } +AnimPose Rig::getJointPose(int jointIndex) const { + if (isIndexValid(jointIndex)) { + return _internalPoseSet._absolutePoses[jointIndex]; + } else { + return AnimPose::identity; + } +} + void Rig::copyJointsIntoJointData(QVector& jointDataVec) const { const AnimPose geometryToRigPose(_geometryToRigTransform); diff --git a/libraries/animation/src/Rig.h b/libraries/animation/src/Rig.h index 1ec4d9527f..7d7b55f4a8 100644 --- a/libraries/animation/src/Rig.h +++ b/libraries/animation/src/Rig.h @@ -159,6 +159,7 @@ public: // rig space glm::mat4 getJointTransform(int jointIndex) const; + AnimPose getJointPose(int jointIndex) const; // Start or stop animations as needed. void computeMotionAnimationState(float deltaTime, const glm::vec3& worldPosition, const glm::vec3& worldVelocity, const glm::quat& worldRotation, CharacterControllerState ccState); diff --git a/libraries/render-utils/src/CauterizedModel.cpp b/libraries/render-utils/src/CauterizedModel.cpp index dbb82ab638..36b06b1035 100644 --- a/libraries/render-utils/src/CauterizedModel.cpp +++ b/libraries/render-utils/src/CauterizedModel.cpp @@ -9,6 +9,7 @@ #include "CauterizedModel.h" #include +#include #include "AbstractViewStateInterface.h" #include "MeshPartPayload.h" @@ -109,30 +110,78 @@ void CauterizedModel::updateClusterMatrices() { const FBXMesh& mesh = geometry.meshes.at(i); for (int j = 0; j < mesh.clusters.size(); j++) { const FBXCluster& cluster = mesh.clusters.at(j); + + /* AJT: TODO REMOVE auto jointMatrix = _rig.getJointTransform(cluster.jointIndex); glm_mat4u_mul(jointMatrix, cluster.inverseBindMatrix, state.clusterMatrices[j]); + */ + // AJT: TODO OPTOMIZE + AnimPose jointPose = _rig.getJointPose(cluster.jointIndex); + AnimPose result = jointPose * AnimPose(cluster.inverseBindMatrix); + + // pack scale rotation and translation into a mat4. + state.clusterMatrices[j][0].x = result.scale().x; + state.clusterMatrices[j][0].y = result.scale().y; + state.clusterMatrices[j][0].z = result.scale().z; + + DualQuaternion dq(result.rot(), result.trans()); + state.clusterMatrices[j][1].x = dq.real().x; + state.clusterMatrices[j][1].y = dq.real().y; + state.clusterMatrices[j][1].z = dq.real().z; + state.clusterMatrices[j][1].w = dq.real().w; + state.clusterMatrices[j][2].x = dq.imag().x; + state.clusterMatrices[j][2].y = dq.imag().y; + state.clusterMatrices[j][2].z = dq.imag().z; + state.clusterMatrices[j][2].w = dq.imag().w; } } // as an optimization, don't build cautrizedClusterMatrices if the boneSet is empty. if (!_cauterizeBoneSet.empty()) { + static const glm::mat4 zeroScale( glm::vec4(0.0f, 0.0f, 0.0f, 0.0f), glm::vec4(0.0f, 0.0f, 0.0f, 0.0f), glm::vec4(0.0f, 0.0f, 0.0f, 0.0f), glm::vec4(0.0f, 0.0f, 0.0f, 1.0f)); auto cauterizeMatrix = _rig.getJointTransform(geometry.neckJointIndex) * zeroScale; + auto cauterizePose = AnimPose(cauterizeMatrix); for (int i = 0; i < _cauterizeMeshStates.size(); i++) { Model::MeshState& state = _cauterizeMeshStates[i]; const FBXMesh& mesh = geometry.meshes.at(i); for (int j = 0; j < mesh.clusters.size(); j++) { const FBXCluster& cluster = mesh.clusters.at(j); + + // AJT: TODO REMOVE: + /* auto jointMatrix = _rig.getJointTransform(cluster.jointIndex); if (_cauterizeBoneSet.find(cluster.jointIndex) != _cauterizeBoneSet.end()) { jointMatrix = cauterizeMatrix; } glm_mat4u_mul(jointMatrix, cluster.inverseBindMatrix, state.clusterMatrices[j]); + */ + + auto jointPose = _rig.getJointPose(cluster.jointIndex); + if (_cauterizeBoneSet.find(cluster.jointIndex) != _cauterizeBoneSet.end()) { + jointPose = cauterizePose; + } + AnimPose result = jointPose * AnimPose(cluster.inverseBindMatrix); + + // pack scale rotation and translation into a mat4. + state.clusterMatrices[j][0].x = result.scale().x; + state.clusterMatrices[j][0].y = result.scale().y; + state.clusterMatrices[j][0].z = result.scale().z; + + DualQuaternion dq(result.rot(), result.trans()); + state.clusterMatrices[j][1].x = dq.real().x; + state.clusterMatrices[j][1].y = dq.real().y; + state.clusterMatrices[j][1].z = dq.real().z; + state.clusterMatrices[j][1].w = dq.real().w; + state.clusterMatrices[j][2].x = dq.imag().x; + state.clusterMatrices[j][2].y = dq.imag().y; + state.clusterMatrices[j][2].z = dq.imag().z; + state.clusterMatrices[j][2].w = dq.imag().w; } } } diff --git a/libraries/render-utils/src/MeshPartPayload.cpp b/libraries/render-utils/src/MeshPartPayload.cpp index 2a59c7d3c5..eb3866df21 100644 --- a/libraries/render-utils/src/MeshPartPayload.cpp +++ b/libraries/render-utils/src/MeshPartPayload.cpp @@ -12,6 +12,7 @@ #include "MeshPartPayload.h" #include +#include #include "DeferredLightingEffect.h" @@ -330,7 +331,10 @@ ModelMeshPartPayload::ModelMeshPartPayload(ModelPointer model, int meshIndex, in updateTransform(transform, offsetTransform); Transform renderTransform = transform; if (state.clusterMatrices.size() == 1) { - renderTransform = transform.worldTransform(Transform(state.clusterMatrices[0])); + // convert form dual quaternion representation to a Transform. + glm::vec3 scale = glm::vec3(state.clusterMatrices[0][0]); + DualQuaternion dq(state.clusterMatrices[0][1], state.clusterMatrices[0][2]); + renderTransform = transform.worldTransform(Transform(dq.getRotation(), scale, dq.getTranslation())); } updateTransformForSkinnedMesh(renderTransform, transform); diff --git a/libraries/render-utils/src/Model.cpp b/libraries/render-utils/src/Model.cpp index b91f4dd405..6ff86516fc 100644 --- a/libraries/render-utils/src/Model.cpp +++ b/libraries/render-utils/src/Model.cpp @@ -25,6 +25,7 @@ #include #include #include +#include #include "AbstractViewStateInterface.h" #include "MeshPartPayload.h" @@ -1176,8 +1177,32 @@ void Model::updateClusterMatrices() { const FBXMesh& mesh = geometry.meshes.at(i); for (int j = 0; j < mesh.clusters.size(); j++) { const FBXCluster& cluster = mesh.clusters.at(j); + + // AJT: TODO REMOVE + /* auto jointMatrix = _rig.getJointTransform(cluster.jointIndex); glm_mat4u_mul(jointMatrix, cluster.inverseBindMatrix, state.clusterMatrices[j]); + */ + AnimPose jointPose = _rig.getJointPose(cluster.jointIndex); + + // AJT: TODO: store inverseBindMatrix as an animpose + // AJT: TODO: optimize AnimPose::operator* + AnimPose result = jointPose * AnimPose(cluster.inverseBindMatrix); + + // pack scale rotation and translation into a mat4. + state.clusterMatrices[j][0].x = result.scale().x; + state.clusterMatrices[j][0].y = result.scale().y; + state.clusterMatrices[j][0].z = result.scale().z; + + DualQuaternion dq(result.rot(), result.trans()); + state.clusterMatrices[j][1].x = dq.real().x; + state.clusterMatrices[j][1].y = dq.real().y; + state.clusterMatrices[j][1].z = dq.real().z; + state.clusterMatrices[j][1].w = dq.real().w; + state.clusterMatrices[j][2].x = dq.imag().x; + state.clusterMatrices[j][2].y = dq.imag().y; + state.clusterMatrices[j][2].z = dq.imag().z; + state.clusterMatrices[j][2].w = dq.imag().w; } } diff --git a/libraries/render-utils/src/Skinning.slh b/libraries/render-utils/src/Skinning.slh index 2d1f010029..10dc4042b2 100644 --- a/libraries/render-utils/src/Skinning.slh +++ b/libraries/render-utils/src/Skinning.slh @@ -18,52 +18,108 @@ layout(std140) uniform skinClusterBuffer { mat4 clusterMatrices[MAX_CLUSTERS]; }; -void skinPosition(ivec4 skinClusterIndex, vec4 skinClusterWeight, vec4 inPosition, out vec4 skinnedPosition) { - vec4 newPosition = vec4(0.0, 0.0, 0.0, 0.0); +vec4 quatConj(vec4 v) { + return vec4(-v.x, -v.y, -v.z, v.w); +} +vec4 quatMul(vec4 q1, vec4 q2) { + return vec4( q1.x * q2.w + q1.y * q2.z - q1.z * q2.y + q1.w * q2.x, + -q1.x * q2.z + q1.y * q2.w + q1.z * q2.x + q1.w * q2.y, + q1.x * q2.y - q1.y * q2.x + q1.z * q2.w + q1.w * q2.z, + -q1.x * q2.x - q1.y * q2.y - q1.z * q2.z + q1.w * q2.w); +} + +vec3 rotateByQuat(vec4 q, vec3 p) { + return vec3(quatMul(quatMul(q, vec4(p.x, p.y, p.z, 0.0)), quatConj(q))); +} + +void dqMul(vec4 lhsReal, vec4 lhsImag, vec4 rhsReal, vec4 rhsImag, out vec4 realOut, out vec4 imagOut) { + realOut = quatMul(lhsReal, rhsReal); + imagOut = quatMul(lhsReal, rhsImag) + quatMul(lhsImag, rhsReal); +} + +void blendClusters(ivec4 skinClusterIndex, vec4 skinClusterWeight, out vec3 scaleOut, out vec4 rotOut, out vec3 posOut) { + vec3 scale = vec3(0.0, 0.0, 0.0); + vec4 dqReal = vec4(0.0, 0.0, 0.0, 0.0); + vec4 dqImag = vec4(0.0, 0.0, 0.0, 0.0); for (int i = 0; i < INDICES_PER_VERTEX; i++) { mat4 clusterMatrix = clusterMatrices[(skinClusterIndex[i])]; float clusterWeight = skinClusterWeight[i]; - newPosition += clusterMatrix * inPosition * clusterWeight; + + vec3 s = vec3(clusterMatrix[0][0], clusterMatrix[0][1], clusterMatrix[0][2]); + vec4 dqr = vec4(clusterMatrix[1][0], clusterMatrix[1][1], clusterMatrix[1][2], clusterMatrix[1][3]); + vec4 dqi = vec4(clusterMatrix[2][0], clusterMatrix[2][1], clusterMatrix[2][2], clusterMatrix[2][3]); + + scale += s * clusterWeight; + dqReal += dqr * clusterWeight; + dqImag += dqi * clusterWeight; } - skinnedPosition = newPosition; + scaleOut = scale; + + float dqLen = length(dqReal); + dqReal *= 1.0 / dqLen; + dqImag *= 1.0 / dqLen; + + rotOut = dqReal; + + vec4 invReal = quatConj(dqReal); + posOut.xyz = 2.0 * quatMul(dqImag, invReal).xyz; +} + +void blendClusters_rigid(ivec4 skinClusterIndex, vec4 skinClusterWeight, out vec3 scaleOut, out vec4 rotOut, out vec3 posOut) { + float maxWeight = 0.0; + for (int i = 0; i < INDICES_PER_VERTEX; i++) { + mat4 clusterMatrix = clusterMatrices[(skinClusterIndex[i])]; + float clusterWeight = skinClusterWeight[i]; + + vec3 s = vec3(clusterMatrix[0][0], clusterMatrix[0][1], clusterMatrix[0][2]); + vec4 dqr = vec4(clusterMatrix[1][0], clusterMatrix[1][1], clusterMatrix[1][2], clusterMatrix[1][3]); + vec4 dqi = vec4(clusterMatrix[2][0], clusterMatrix[2][1], clusterMatrix[2][2], clusterMatrix[2][3]); + + if (clusterWeight > maxWeight) { + maxWeight = clusterWeight; + scaleOut = s; + rotOut = dqr; + vec4 invReal = quatConj(dqr); + posOut = 2.0 * quatMul(dqi, invReal).xyz; + } + } +} + +void skinPosition(ivec4 skinClusterIndex, vec4 skinClusterWeight, vec4 inPosition, out vec4 skinnedPosition) { + + vec3 scale, pos; + vec4 rot; + blendClusters(skinClusterIndex, skinClusterWeight, scale, rot, pos); + + skinnedPosition.xyz = rotateByQuat(rot, (vec3(inPosition) * scale)) + pos; + skinnedPosition.w = 1; } void skinPositionNormal(ivec4 skinClusterIndex, vec4 skinClusterWeight, vec4 inPosition, vec3 inNormal, out vec4 skinnedPosition, out vec3 skinnedNormal) { - vec4 newPosition = vec4(0.0, 0.0, 0.0, 0.0); - vec4 newNormal = vec4(0.0, 0.0, 0.0, 0.0); - for (int i = 0; i < INDICES_PER_VERTEX; i++) { - mat4 clusterMatrix = clusterMatrices[(skinClusterIndex[i])]; - float clusterWeight = skinClusterWeight[i]; - newPosition += clusterMatrix * inPosition * clusterWeight; - newNormal += clusterMatrix * vec4(inNormal.xyz, 0.0) * clusterWeight; - } + vec3 scale, pos; + vec4 rot; + blendClusters(skinClusterIndex, skinClusterWeight, scale, rot, pos); - skinnedPosition = newPosition; - skinnedNormal = newNormal.xyz; + skinnedNormal.xyz = rotateByQuat(rot, inNormal * scale); } void skinPositionNormalTangent(ivec4 skinClusterIndex, vec4 skinClusterWeight, vec4 inPosition, vec3 inNormal, vec3 inTangent, out vec4 skinnedPosition, out vec3 skinnedNormal, out vec3 skinnedTangent) { - vec4 newPosition = vec4(0.0, 0.0, 0.0, 0.0); - vec4 newNormal = vec4(0.0, 0.0, 0.0, 0.0); - vec4 newTangent = vec4(0.0, 0.0, 0.0, 0.0); - for (int i = 0; i < INDICES_PER_VERTEX; i++) { - mat4 clusterMatrix = clusterMatrices[(skinClusterIndex[i])]; - float clusterWeight = skinClusterWeight[i]; - newPosition += clusterMatrix * inPosition * clusterWeight; - newNormal += clusterMatrix * vec4(inNormal.xyz, 0.0) * clusterWeight; - newTangent += clusterMatrix * vec4(inTangent.xyz, 0.0) * clusterWeight; - } + vec3 scale, pos; + vec4 rot; + blendClusters(skinClusterIndex, skinClusterWeight, scale, rot, pos); - skinnedPosition = newPosition; - skinnedNormal = newNormal.xyz; - skinnedTangent = newTangent.xyz; + skinnedPosition.xyz = rotateByQuat(rot, (vec3(inPosition) * scale)) + pos; + skinnedPosition.w = 1; + + skinnedNormal = rotateByQuat(rot, inNormal * scale); + skinnedTangent = rotateByQuat(rot, inTangent * scale); } -<@endif@> \ No newline at end of file +<@endif@> diff --git a/libraries/shared/src/DualQuaternion.cpp b/libraries/shared/src/DualQuaternion.cpp new file mode 100644 index 0000000000..3b5aaf6d6d --- /dev/null +++ b/libraries/shared/src/DualQuaternion.cpp @@ -0,0 +1,81 @@ +// +// DualQuaternion.cpp +// +// Created by Anthony J. Thibault on Dec 13th 2017. +// Copyright (c) 2017 High Fidelity, Inc. All rights reserved. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#include "DualQuaternion.h" +#include "GLMHelpers.h" + +// delegating constructor +DualQuaternion::DualQuaternion(const glm::mat4& m) : DualQuaternion(glmExtractRotation(m), extractTranslation(m)) { +} + +DualQuaternion::DualQuaternion(const glm::quat& real, const glm::quat& imag) : _real(real), _imag(imag) { +} + +DualQuaternion::DualQuaternion(const glm::vec4& real, const glm::vec4& imag) : + _real(real.w, real.x, real.y, real.z), + _imag(imag.w, imag.x, imag.y, imag.z) { +} + +DualQuaternion::DualQuaternion(const glm::quat& rotation, const glm::vec3& translation) { + _real = rotation; + _imag = glm::quat(0, 0.5f * translation.x, 0.5f * translation.y, 0.5f * translation.z) * rotation; +} + +DualQuaternion DualQuaternion::operator*(const DualQuaternion& rhs) const { + return DualQuaternion(_real * rhs._real, _real * rhs._imag + _imag * rhs._real); +} + +DualQuaternion DualQuaternion::operator*(float scalar) const { + return DualQuaternion(_real * scalar, _imag * scalar); +} + +DualQuaternion DualQuaternion::operator+(const DualQuaternion& rhs) const { + return DualQuaternion(_real + rhs._real, _imag + rhs._imag); +} + +glm::vec3 DualQuaternion::xformPoint(const glm::vec3& rhs) const { + DualQuaternion v(glm::quat(), glm::quat(0.0f, rhs.x, rhs.y, rhs.z)); + DualQuaternion dualConj(glm::conjugate(_real), -glm::conjugate(_imag)); + DualQuaternion result = *this * v * dualConj; + return vec3(result._imag.x, result._imag.y, result._imag.z); +} + +glm::quat DualQuaternion::getRotation() const { + return _real; +} + +glm::vec3 DualQuaternion::getTranslation() const { + glm::quat result = 2.0f * (_imag * glm::inverse(_real)); + return glm::vec3(result.x, result.y, result.z); +} + + +glm::vec3 DualQuaternion::xformVector(const glm::vec3& rhs) const { + return _real * rhs; +} + +DualQuaternion DualQuaternion::inverse() const { + glm::quat invReal = glm::inverse(_real); + return DualQuaternion(invReal, - invReal * _imag * invReal); +} + +DualQuaternion DualQuaternion::conjugate() const { + return DualQuaternion(glm::conjugate(_real), glm::conjugate(_imag)); +} + +float DualQuaternion::length() const { + DualQuaternion result = *this * conjugate(); + return sqrtf(result._real.w); +} + +DualQuaternion DualQuaternion::normalize() const { + float invLen = 1.0f / length(); + return *this * invLen; +} diff --git a/libraries/shared/src/DualQuaternion.h b/libraries/shared/src/DualQuaternion.h new file mode 100644 index 0000000000..ed8cdf54ff --- /dev/null +++ b/libraries/shared/src/DualQuaternion.h @@ -0,0 +1,58 @@ +// +// DualQuaternion.h +// +// Created by Anthony J. Thibault on Dec 13th 2017. +// Copyright (c) 2017 High Fidelity, Inc. All rights reserved. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + + +#ifndef hifi_DualQuaternion +#define hifi_DualQuaternion + +#include +#include +#include +#include + +class DualQuaternion { +public: + explicit DualQuaternion(const glm::mat4& m); + DualQuaternion(const glm::quat& real, const glm::quat& imag); + DualQuaternion(const glm::quat& rotation, const glm::vec3& translation); + DualQuaternion(const glm::vec4& real, const glm::vec4& imag); + DualQuaternion operator*(const DualQuaternion& rhs) const; + DualQuaternion operator*(float scalar) const; + DualQuaternion operator+(const DualQuaternion& rhs) const; + + const glm::quat& real() const { return _real; } + glm::quat& real() { return _real; } + + const glm::quat& imag() const { return _imag; } + glm::quat& imag() { return _imag; } + + glm::quat getRotation() const; + glm::vec3 getTranslation() const; + + glm::vec3 xformPoint(const glm::vec3& rhs) const; + glm::vec3 xformVector(const glm::vec3& rhs) const; + + DualQuaternion inverse() const; + DualQuaternion conjugate() const; + float length() const; + DualQuaternion normalize() const; + +protected: + friend QDebug operator<<(QDebug debug, const DualQuaternion& pose); + glm::quat _real; + glm::quat _imag; +}; + +inline QDebug operator<<(QDebug debug, const DualQuaternion& dq) { + debug << "AnimPose, real = (" << dq._real.x << dq._real.y << dq._real.z << dq._real.w << "), imag = (" << dq._imag.x << dq._imag.y << dq._imag.z << dq._imag.w << ")"; + return debug; +} + +#endif diff --git a/tests/shared/src/DualQuaternionTests.cpp b/tests/shared/src/DualQuaternionTests.cpp new file mode 100644 index 0000000000..8187ed78bd --- /dev/null +++ b/tests/shared/src/DualQuaternionTests.cpp @@ -0,0 +1,138 @@ +// +// DualQuaternionTests.cpp +// tests/shared/src +// +// Copyright 2017 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#include + +#include "DualQuaternionTests.h" + +#include +#include +#include +#include + +#include <../GLMTestUtils.h> +#include <../QTestExtensions.h> + +QTEST_MAIN(DualQuaternionTests) + +static void quatComp(const glm::quat& q1, const glm::quat& q2) { + QCOMPARE_WITH_ABS_ERROR(q1.x, q2.x, EPSILON); + QCOMPARE_WITH_ABS_ERROR(q1.y, q2.y, EPSILON); + QCOMPARE_WITH_ABS_ERROR(q1.z, q2.z, EPSILON); + QCOMPARE_WITH_ABS_ERROR(q1.w, q2.w, EPSILON); +} + +void DualQuaternionTests::ctor() { + glm::quat real = angleAxis(PI / 2.0f, Vectors::UNIT_Y); + glm::quat imag(0.0f, 1.0f, 2.0f, 3.0f); + + DualQuaternion dq(real, imag); + quatComp(real, dq.real()); + quatComp(imag, dq.imag()); + + glm::quat rotation = angleAxis(PI / 3.0f, Vectors::UNIT_X); + glm::vec3 translation(1.0, 2.0f, 3.0f); + dq = DualQuaternion(rotation, translation); + quatComp(rotation, dq.getRotation()); + QCOMPARE_WITH_ABS_ERROR(translation, dq.getTranslation(), EPSILON); + + rotation = angleAxis(-2.0f * PI / 7.0f, Vectors::UNIT_Z); + translation = glm::vec3(-1.0, 12.0f, 2.0f); + glm::mat4 m = createMatFromQuatAndPos(rotation, translation); + dq = DualQuaternion(m); + quatComp(rotation, dq.getRotation()); + QCOMPARE_WITH_ABS_ERROR(translation, dq.getTranslation(), EPSILON); +} + +void DualQuaternionTests::mult() { + + glm::quat rotation = angleAxis(PI / 3.0f, Vectors::UNIT_X); + glm::vec3 translation(1.0, 2.0f, 3.0f); + glm::mat4 m1 = createMatFromQuatAndPos(rotation, translation); + DualQuaternion dq1(m1); + + rotation = angleAxis(-2.0f * PI / 7.0f, Vectors::UNIT_Z); + translation = glm::vec3(-1.0, 12.0f, 2.0f); + glm::mat4 m2 = createMatFromQuatAndPos(rotation, translation); + DualQuaternion dq2(m2); + + DualQuaternion dq3 = dq1 * dq2; + glm::mat4 m3 = m1 * m2; + + rotation = glmExtractRotation(m3); + translation = extractTranslation(m3); + + quatComp(rotation, dq3.getRotation()); + QCOMPARE_WITH_ABS_ERROR(translation, dq3.getTranslation(), EPSILON); +} + +void DualQuaternionTests::xform() { + + glm::quat rotation = angleAxis(PI / 3.0f, Vectors::UNIT_X); + glm::vec3 translation(1.0, 2.0f, 3.0f); + glm::mat4 m1 = createMatFromQuatAndPos(rotation, translation); + DualQuaternion dq1(m1); + + rotation = angleAxis(-2.0f * PI / 7.0f, Vectors::UNIT_Z); + translation = glm::vec3(-1.0, 12.0f, 2.0f); + glm::mat4 m2 = createMatFromQuatAndPos(rotation, translation); + DualQuaternion dq2(m2); + + DualQuaternion dq3 = dq1 * dq2; + glm::mat4 m3 = m1 * m2; + + glm::vec3 p(1.0f, 2.0f, 3.0f); + + glm::vec3 p1 = transformPoint(m3, p); + glm::vec3 p2 = dq3.xformPoint(p); + + QCOMPARE_WITH_ABS_ERROR(p1, p2, 0.001f); + + p1 = transformVectorFast(m3, p); + p2 = dq3.xformVector(p); + + QCOMPARE_WITH_ABS_ERROR(p1, p2, 0.001f); +} + +void DualQuaternionTests::trans() { + glm::vec3 t1 = glm::vec3(); + DualQuaternion dq1(Quaternions::IDENTITY, t1); + glm::vec3 t2 = glm::vec3(1.0f, 2.0f, 3.0f); + DualQuaternion dq2(angleAxis(PI / 3.0f, Vectors::UNIT_X), t2); + glm::vec3 t3 = glm::vec3(3.0f, 2.0f, 1.0f); + DualQuaternion dq3(angleAxis(PI / 5.0f, Vectors::UNIT_Y), t3); + + QCOMPARE_WITH_ABS_ERROR(t1, dq1.getTranslation(), 0.001f); + QCOMPARE_WITH_ABS_ERROR(t2, dq2.getTranslation(), 0.001f); + QCOMPARE_WITH_ABS_ERROR(t3, dq3.getTranslation(), 0.001f); +} + +// Dual Quaternion Linear Blending test +void DualQuaternionTests::dlb() { + DualQuaternion dq1(Quaternions::IDENTITY, glm::vec3()); + DualQuaternion dq2(angleAxis(PI / 2.0f, Vectors::UNIT_X), glm::vec3(0.0f, 1.0f, 0.0f)); + + qDebug() << "dq1 =" << dq1; + qDebug() << "dq1.length = " << dq1.length(); + qDebug() << "dq2 =" << dq2; + qDebug() << "dq2.length = " << dq2.length(); + + // linear blend between dq1 and dq2 + DualQuaternion dq3 = dq1 * 0.5f + dq2 * 0.5f; + DualQuaternion dq4 = dq3.normalize(); + + qDebug() << "dq3 =" << dq3; + qDebug() << "dq3.length = " << dq3.length(); + qDebug() << "dq3.trans =" << dq3.getTranslation(); + + qDebug() << "dq4 =" << dq4; + qDebug() << "dq4.length = " << dq4.length(); + qDebug() << "dq4.trans =" << dq4.getTranslation(); +} diff --git a/tests/shared/src/DualQuaternionTests.h b/tests/shared/src/DualQuaternionTests.h new file mode 100644 index 0000000000..988973b689 --- /dev/null +++ b/tests/shared/src/DualQuaternionTests.h @@ -0,0 +1,26 @@ +// +// DualQuaternionTests.h +// tests/shared/src +// +// Copyright 2017 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#ifndef hifi_DualQuaternionTests_h +#define hifi_DualQuaternionTests_h + +#include + +class DualQuaternionTests : public QObject { + Q_OBJECT +private slots: + void ctor(); + void mult(); + void xform(); + void trans(); + void dlb(); +}; + +#endif // hifi_DualQuaternionTests_h From 532c8a23f98f8704ecc32f8769c47d508acfe036 Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Fri, 15 Dec 2017 15:50:22 -0800 Subject: [PATCH 02/32] tests --- tests/shared/src/DualQuaternionTests.cpp | 25 ++++++++++++++++-------- 1 file changed, 17 insertions(+), 8 deletions(-) diff --git a/tests/shared/src/DualQuaternionTests.cpp b/tests/shared/src/DualQuaternionTests.cpp index 8187ed78bd..1c34bfbee7 100644 --- a/tests/shared/src/DualQuaternionTests.cpp +++ b/tests/shared/src/DualQuaternionTests.cpp @@ -118,21 +118,30 @@ void DualQuaternionTests::trans() { void DualQuaternionTests::dlb() { DualQuaternion dq1(Quaternions::IDENTITY, glm::vec3()); DualQuaternion dq2(angleAxis(PI / 2.0f, Vectors::UNIT_X), glm::vec3(0.0f, 1.0f, 0.0f)); + DualQuaternion dq2Alt(-angleAxis(PI / 2.0f, Vectors::UNIT_X), glm::vec3(0.0f, 1.0f, 0.0f)); qDebug() << "dq1 =" << dq1; - qDebug() << "dq1.length = " << dq1.length(); qDebug() << "dq2 =" << dq2; - qDebug() << "dq2.length = " << dq2.length(); // linear blend between dq1 and dq2 DualQuaternion dq3 = dq1 * 0.5f + dq2 * 0.5f; - DualQuaternion dq4 = dq3.normalize(); + + // alternate linear blend between dq1 and dq2 + DualQuaternion dq4 = dq1 * 0.5 + dq2Alt * 0.5f; qDebug() << "dq3 =" << dq3; - qDebug() << "dq3.length = " << dq3.length(); - qDebug() << "dq3.trans =" << dq3.getTranslation(); - qDebug() << "dq4 =" << dq4; - qDebug() << "dq4.length = " << dq4.length(); - qDebug() << "dq4.trans =" << dq4.getTranslation(); + + glm::vec3 p1(0.0f, 0.5f, -0.5f); + glm::vec3 p2(0.0f, 0.5f, 0.5f); + + glm::vec3 p3 = dq3.xformPoint(p1); + glm::vec3 p4 = dq3.xformPoint(p2); + glm::vec3 p5 = dq4.xformPoint(p1); + glm::vec3 p6 = dq4.xformPoint(p2); + + qDebug() << "p3 =" << p3; + qDebug() << "p4 =" << p4; + qDebug() << "p5 =" << p5; + qDebug() << "p6 =" << p6; } From 23affb570b1be336a2404ea08fc2df3a78cdbf7c Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Mon, 18 Dec 2017 15:26:02 -0800 Subject: [PATCH 03/32] WIP checkpoint, root transforms for shapes and non-animated fbx files are working. --- .../render-utils/src/CauterizedModel.cpp | 72 ++++++++++++------ .../render-utils/src/MeshPartPayload.cpp | 26 ++++++- libraries/render-utils/src/Model.cpp | 39 ++++++---- libraries/render-utils/src/Model.h | 11 +++ libraries/render-utils/src/Skinning.slh | 76 ++++++++++++++++++- .../render-utils/src/SoftAttachmentModel.cpp | 29 +++++++ 6 files changed, 207 insertions(+), 46 deletions(-) diff --git a/libraries/render-utils/src/CauterizedModel.cpp b/libraries/render-utils/src/CauterizedModel.cpp index 36b06b1035..cba716312a 100644 --- a/libraries/render-utils/src/CauterizedModel.cpp +++ b/libraries/render-utils/src/CauterizedModel.cpp @@ -16,7 +16,6 @@ #include "CauterizedMeshPartPayload.h" #include "RenderUtilsLogging.h" - CauterizedModel::CauterizedModel(QObject* parent) : Model(parent) { } @@ -111,11 +110,13 @@ void CauterizedModel::updateClusterMatrices() { for (int j = 0; j < mesh.clusters.size(); j++) { const FBXCluster& cluster = mesh.clusters.at(j); - /* AJT: TODO REMOVE + /* AJT: TODO REMOVE */ +#ifdef SKIN_MATRIX + SKIN_ASSERT(false); auto jointMatrix = _rig.getJointTransform(cluster.jointIndex); glm_mat4u_mul(jointMatrix, cluster.inverseBindMatrix, state.clusterMatrices[j]); - */ - // AJT: TODO OPTOMIZE +#endif +#ifdef SKIN_COMP AnimPose jointPose = _rig.getJointPose(cluster.jointIndex); AnimPose result = jointPose * AnimPose(cluster.inverseBindMatrix); @@ -124,15 +125,15 @@ void CauterizedModel::updateClusterMatrices() { state.clusterMatrices[j][0].y = result.scale().y; state.clusterMatrices[j][0].z = result.scale().z; - DualQuaternion dq(result.rot(), result.trans()); - state.clusterMatrices[j][1].x = dq.real().x; - state.clusterMatrices[j][1].y = dq.real().y; - state.clusterMatrices[j][1].z = dq.real().z; - state.clusterMatrices[j][1].w = dq.real().w; - state.clusterMatrices[j][2].x = dq.imag().x; - state.clusterMatrices[j][2].y = dq.imag().y; - state.clusterMatrices[j][2].z = dq.imag().z; - state.clusterMatrices[j][2].w = dq.imag().w; + state.clusterMatrices[j][1].x = result.rot().x; + state.clusterMatrices[j][1].y = result.rot().y; + state.clusterMatrices[j][1].z = result.rot().z; + state.clusterMatrices[j][1].w = result.rot().w; + + state.clusterMatrices[j][2].x = result.trans().x; + state.clusterMatrices[j][2].y = result.trans().y; + state.clusterMatrices[j][2].z = result.trans().z; +#endif } } @@ -153,15 +154,16 @@ void CauterizedModel::updateClusterMatrices() { for (int j = 0; j < mesh.clusters.size(); j++) { const FBXCluster& cluster = mesh.clusters.at(j); +#ifdef SKIN_MATRIX + SKIN_ASSERT(false); // AJT: TODO REMOVE: - /* auto jointMatrix = _rig.getJointTransform(cluster.jointIndex); if (_cauterizeBoneSet.find(cluster.jointIndex) != _cauterizeBoneSet.end()) { jointMatrix = cauterizeMatrix; } glm_mat4u_mul(jointMatrix, cluster.inverseBindMatrix, state.clusterMatrices[j]); - */ - +#endif +#ifdef SKIN_COMP auto jointPose = _rig.getJointPose(cluster.jointIndex); if (_cauterizeBoneSet.find(cluster.jointIndex) != _cauterizeBoneSet.end()) { jointPose = cauterizePose; @@ -173,15 +175,15 @@ void CauterizedModel::updateClusterMatrices() { state.clusterMatrices[j][0].y = result.scale().y; state.clusterMatrices[j][0].z = result.scale().z; - DualQuaternion dq(result.rot(), result.trans()); - state.clusterMatrices[j][1].x = dq.real().x; - state.clusterMatrices[j][1].y = dq.real().y; - state.clusterMatrices[j][1].z = dq.real().z; - state.clusterMatrices[j][1].w = dq.real().w; - state.clusterMatrices[j][2].x = dq.imag().x; - state.clusterMatrices[j][2].y = dq.imag().y; - state.clusterMatrices[j][2].z = dq.imag().z; - state.clusterMatrices[j][2].w = dq.imag().w; + state.clusterMatrices[j][1].x = result.rot().x; + state.clusterMatrices[j][1].y = result.rot().y; + state.clusterMatrices[j][1].z = result.rot().z; + state.clusterMatrices[j][1].w = result.rot().w; + + state.clusterMatrices[j][2].x = result.trans().x; + state.clusterMatrices[j][2].y = result.trans().y; + state.clusterMatrices[j][2].z = result.trans().z; +#endif } } } @@ -249,13 +251,33 @@ void CauterizedModel::updateRenderItems() { Transform renderTransform = modelTransform; if (clusterMatrices.size() == 1) { +#ifdef SKIN_MATRIX + SKIN_ASSERT(false); renderTransform = modelTransform.worldTransform(Transform(clusterMatrices[0])); +#endif +#ifdef SKIN_COMP + glm::vec3 scale(clusterMatrices[0][0]); + glm::quat rot(clusterMatrices[0][1].w, clusterMatrices[0][1].x, clusterMatrices[0][1].y, clusterMatrices[0][1].z); + glm::vec3 trans(clusterMatrices[0][2]); + glm::mat4 m = createMatFromScaleQuatAndPos(scale, rot, trans); + renderTransform = modelTransform.worldTransform(Transform(m)); +#endif } data.updateTransformForSkinnedMesh(renderTransform, modelTransform); renderTransform = modelTransform; if (clusterMatricesCauterized.size() == 1) { +#ifdef SKIN_MATRIX + SKIN_ASSERT(false); renderTransform = modelTransform.worldTransform(Transform(clusterMatricesCauterized[0])); +#endif +#ifdef SKIN_COMP + glm::vec3 scale(clusterMatricesCauterized[0][0]); + glm::quat rot(clusterMatricesCauterized[0][1].w, clusterMatricesCauterized[0][1].x, clusterMatricesCauterized[0][1].y, clusterMatricesCauterized[0][1].z); + glm::vec3 trans(clusterMatricesCauterized[0][2]); + glm::mat4 m = createMatFromScaleQuatAndPos(scale, rot, trans); + renderTransform = modelTransform.worldTransform(Transform(m)); +#endif } data.updateTransformForCauterizedMesh(renderTransform); diff --git a/libraries/render-utils/src/MeshPartPayload.cpp b/libraries/render-utils/src/MeshPartPayload.cpp index eb3866df21..fcf6a61fb5 100644 --- a/libraries/render-utils/src/MeshPartPayload.cpp +++ b/libraries/render-utils/src/MeshPartPayload.cpp @@ -331,10 +331,17 @@ ModelMeshPartPayload::ModelMeshPartPayload(ModelPointer model, int meshIndex, in updateTransform(transform, offsetTransform); Transform renderTransform = transform; if (state.clusterMatrices.size() == 1) { - // convert form dual quaternion representation to a Transform. - glm::vec3 scale = glm::vec3(state.clusterMatrices[0][0]); - DualQuaternion dq(state.clusterMatrices[0][1], state.clusterMatrices[0][2]); - renderTransform = transform.worldTransform(Transform(dq.getRotation(), scale, dq.getTranslation())); +#ifdef SKIN_MATRIX + SKIN_ASSERT(false); + renderTransform = transform.worldTransform(Transform(state.clusterMatrices[0])); +#endif +#ifdef SKIN_COMP + glm::vec3 scale(state.clusterMatrices[0][0]); + glm::quat rot(state.clusterMatrices[0][1].w, state.clusterMatrices[0][1].x, state.clusterMatrices[0][1].y, state.clusterMatrices[0][1].z); + glm::vec3 trans(state.clusterMatrices[0][2]); + glm::mat4 m = createMatFromScaleQuatAndPos(scale, rot, trans); + renderTransform = transform.worldTransform(Transform(m)); +#endif } updateTransformForSkinnedMesh(renderTransform, transform); @@ -549,7 +556,18 @@ void ModelMeshPartPayload::computeAdjustedLocalBound(const std::vector maxWeight) { + maxWeight = clusterWeight; + scaleOut = s; + rotOut = r; + posOut = t; + } + } +} + void skinPosition(ivec4 skinClusterIndex, vec4 skinClusterWeight, vec4 inPosition, out vec4 skinnedPosition) { vec3 scale, pos; @@ -121,5 +143,55 @@ void skinPositionNormalTangent(ivec4 skinClusterIndex, vec4 skinClusterWeight, v skinnedTangent = rotateByQuat(rot, inTangent * scale); } +// ORIGINAL +/* +void skinPosition(ivec4 skinClusterIndex, vec4 skinClusterWeight, vec4 inPosition, out vec4 skinnedPosition) { + vec4 newPosition = vec4(0.0, 0.0, 0.0, 0.0); + + for (int i = 0; i < INDICES_PER_VERTEX; i++) { + mat4 clusterMatrix = clusterMatrices[(skinClusterIndex[i])]; + float clusterWeight = skinClusterWeight[i]; + newPosition += clusterMatrix * inPosition * clusterWeight; + } + + skinnedPosition = newPosition; +} + +void skinPositionNormal(ivec4 skinClusterIndex, vec4 skinClusterWeight, vec4 inPosition, vec3 inNormal, + out vec4 skinnedPosition, out vec3 skinnedNormal) { + vec4 newPosition = vec4(0.0, 0.0, 0.0, 0.0); + vec4 newNormal = vec4(0.0, 0.0, 0.0, 0.0); + + for (int i = 0; i < INDICES_PER_VERTEX; i++) { + mat4 clusterMatrix = clusterMatrices[(skinClusterIndex[i])]; + float clusterWeight = skinClusterWeight[i]; + newPosition += clusterMatrix * inPosition * clusterWeight; + newNormal += clusterMatrix * vec4(inNormal.xyz, 0.0) * clusterWeight; + } + + skinnedPosition = newPosition; + skinnedNormal = newNormal.xyz; +} + +void skinPositionNormalTangent(ivec4 skinClusterIndex, vec4 skinClusterWeight, vec4 inPosition, vec3 inNormal, vec3 inTangent, + out vec4 skinnedPosition, out vec3 skinnedNormal, out vec3 skinnedTangent) { + vec4 newPosition = vec4(0.0, 0.0, 0.0, 0.0); + vec4 newNormal = vec4(0.0, 0.0, 0.0, 0.0); + vec4 newTangent = vec4(0.0, 0.0, 0.0, 0.0); + + for (int i = 0; i < INDICES_PER_VERTEX; i++) { + mat4 clusterMatrix = clusterMatrices[(skinClusterIndex[i])]; + float clusterWeight = skinClusterWeight[i]; + newPosition += clusterMatrix * inPosition * clusterWeight; + newNormal += clusterMatrix * vec4(inNormal.xyz, 0.0) * clusterWeight; + newTangent += clusterMatrix * vec4(inTangent.xyz, 0.0) * clusterWeight; + } + + skinnedPosition = newPosition; + skinnedNormal = newNormal.xyz; + skinnedTangent = newTangent.xyz; +} +*/ + <@endif@> diff --git a/libraries/render-utils/src/SoftAttachmentModel.cpp b/libraries/render-utils/src/SoftAttachmentModel.cpp index 63991f9422..7d49a8cda1 100644 --- a/libraries/render-utils/src/SoftAttachmentModel.cpp +++ b/libraries/render-utils/src/SoftAttachmentModel.cpp @@ -50,6 +50,7 @@ void SoftAttachmentModel::updateClusterMatrices() { for (int j = 0; j < mesh.clusters.size(); j++) { const FBXCluster& cluster = mesh.clusters.at(j); +#ifdef SKIN_MATRIX // TODO: cache these look-ups as an optimization int jointIndexOverride = getJointIndexOverride(cluster.jointIndex); glm::mat4 jointMatrix; @@ -58,7 +59,35 @@ void SoftAttachmentModel::updateClusterMatrices() { } else { jointMatrix = _rig.getJointTransform(cluster.jointIndex); } + SKIN_ASSERT(false); glm_mat4u_mul(jointMatrix, cluster.inverseBindMatrix, state.clusterMatrices[j]); +#endif +#ifdef SKIN_COMP + // TODO: cache these look-ups as an optimization + int jointIndexOverride = getJointIndexOverride(cluster.jointIndex); + AnimPose jointPose; + if (jointIndexOverride >= 0 && jointIndexOverride < _rigOverride.getJointStateCount()) { + jointPose = _rigOverride.getJointPose(jointIndexOverride); + } else { + jointPose = _rig.getJointPose(cluster.jointIndex); + } + AnimPose result = jointPose * AnimPose(cluster.inverseBindMatrix); + + // pack scale rotation and translation into a mat4. + state.clusterMatrices[j][0].x = result.scale().x; + state.clusterMatrices[j][0].y = result.scale().y; + state.clusterMatrices[j][0].z = result.scale().z; + + state.clusterMatrices[j][1].x = result.rot().x; + state.clusterMatrices[j][1].y = result.rot().y; + state.clusterMatrices[j][1].z = result.rot().z; + state.clusterMatrices[j][1].w = result.rot().w; + + state.clusterMatrices[j][2].x = result.trans().x; + state.clusterMatrices[j][2].y = result.trans().y; + state.clusterMatrices[j][2].z = result.trans().z; +#endif + } } From 515d13a4c15297cf1f75820aa59aba72cf3c0e40 Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Mon, 18 Dec 2017 17:51:14 -0800 Subject: [PATCH 04/32] WIP checkpoint --- interface/src/avatar/MyAvatar.cpp | 7 ++- libraries/animation/src/AnimPose.cpp | 23 +++++++++ libraries/animation/src/AnimPose.h | 2 + .../render-utils/src/CauterizedModel.cpp | 50 ++++++++++++++++++- 4 files changed, 79 insertions(+), 3 deletions(-) diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 02a1959a95..0fd7f7f012 100755 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -1820,7 +1820,8 @@ void MyAvatar::postUpdate(float deltaTime, const render::ScenePointer& scene) { if (_skeletonModel->isLoaded() && !_skeletonModel->getRig().getAnimNode()) { initHeadBones(); - _skeletonModel->setCauterizeBoneSet(_headBoneSet); + // AJT HACK DISABLE CAUTERIZE + //_skeletonModel->setCauterizeBoneSet(_headBoneSet); _fstAnimGraphOverrideUrl = _skeletonModel->getGeometry()->getAnimGraphOverrideUrl(); initAnimGraph(); _isAnimatingScale = true; @@ -1912,7 +1913,9 @@ void MyAvatar::preDisplaySide(RenderArgs* renderArgs) { // toggle using the cauterizedBones depending on where the camera is and the rendering pass type. const bool shouldDrawHead = shouldRenderHead(renderArgs); if (shouldDrawHead != _prevShouldDrawHead) { - _skeletonModel->setEnableCauterization(!shouldDrawHead); + // AJT: DISABLE CAUTER + // _skeletonModel->setEnableCauterization(!shouldDrawHead); + _skeletonModel->setEnableCauterization(false); for (int i = 0; i < _attachmentData.size(); i++) { if (_attachmentData[i].jointName.compare("Head", Qt::CaseInsensitive) == 0 || diff --git a/libraries/animation/src/AnimPose.cpp b/libraries/animation/src/AnimPose.cpp index 470bbab8b6..29c4c46d6e 100644 --- a/libraries/animation/src/AnimPose.cpp +++ b/libraries/animation/src/AnimPose.cpp @@ -69,6 +69,27 @@ AnimPose AnimPose::mirror() const { return AnimPose(_scale, glm::quat(_rot.w, _rot.x, -_rot.y, -_rot.z), glm::vec3(-_trans.x, _trans.y, _trans.z)); } +bool AnimPose::fuzzyEqual(const AnimPose& rhs) const { + const float SCALE_EPSILON = 0.00001f; + const float ROT_EPSILON = 0.00001f; + const float TRANS_EPSILON = 0.001f; + if ((fabsf(rhs._scale.x - _scale.x) < SCALE_EPSILON) && + (fabsf(rhs._scale.y - _scale.y) < SCALE_EPSILON) && + (fabsf(rhs._scale.z - _scale.z) < SCALE_EPSILON)) { + if ((fabsf(rhs._rot.x - _rot.x) < ROT_EPSILON) && + (fabsf(rhs._rot.y - _rot.y) < ROT_EPSILON) && + (fabsf(rhs._rot.z - _rot.z) < ROT_EPSILON) && + (fabsf(rhs._rot.w - _rot.w) < ROT_EPSILON)) { + if ((fabsf(rhs._trans.x - _trans.x) < TRANS_EPSILON) && + (fabsf(rhs._trans.y - _trans.y) < TRANS_EPSILON) && + (fabsf(rhs._trans.z - _trans.z) < TRANS_EPSILON)) { + return true; + } + } + } + return false; +} + AnimPose::operator glm::mat4() const { glm::vec3 xAxis = _rot * glm::vec3(_scale.x, 0.0f, 0.0f); glm::vec3 yAxis = _rot * glm::vec3(0.0f, _scale.y, 0.0f); @@ -76,3 +97,5 @@ AnimPose::operator glm::mat4() const { return glm::mat4(glm::vec4(xAxis, 0.0f), glm::vec4(yAxis, 0.0f), glm::vec4(zAxis, 0.0f), glm::vec4(_trans, 1.0f)); } + + diff --git a/libraries/animation/src/AnimPose.h b/libraries/animation/src/AnimPose.h index 2df3d1f2e4..0f29bac7ce 100644 --- a/libraries/animation/src/AnimPose.h +++ b/libraries/animation/src/AnimPose.h @@ -46,6 +46,8 @@ public: const glm::vec3& trans() const { return _trans; } glm::vec3& trans() { return _trans; } + bool fuzzyEqual(const AnimPose& rhs) const; + private: friend QDebug operator<<(QDebug debug, const AnimPose& pose); glm::vec3 _scale { 1.0f }; diff --git a/libraries/render-utils/src/CauterizedModel.cpp b/libraries/render-utils/src/CauterizedModel.cpp index cba716312a..282aedf180 100644 --- a/libraries/render-utils/src/CauterizedModel.cpp +++ b/libraries/render-utils/src/CauterizedModel.cpp @@ -104,6 +104,12 @@ void CauterizedModel::updateClusterMatrices() { _needsUpdateClusterMatrices = false; const FBXGeometry& geometry = getFBXGeometry(); + bool debug = false; + + if (debug) { + qDebug() << "AJT: CauterizedModel::updateClusterMatrices(), url =" << _url; + } + for (int i = 0; i < (int)_meshStates.size(); i++) { Model::MeshState& state = _meshStates[i]; const FBXMesh& mesh = geometry.meshes.at(i); @@ -117,8 +123,14 @@ void CauterizedModel::updateClusterMatrices() { glm_mat4u_mul(jointMatrix, cluster.inverseBindMatrix, state.clusterMatrices[j]); #endif #ifdef SKIN_COMP + + if (debug) { + qDebug() << "AJT: _meshState[" << i << "], cluster[" << j << "]"; + } + AnimPose jointPose = _rig.getJointPose(cluster.jointIndex); AnimPose result = jointPose * AnimPose(cluster.inverseBindMatrix); + result.rot() = glm::normalize(result.rot()); // pack scale rotation and translation into a mat4. state.clusterMatrices[j][0].x = result.scale().x; @@ -133,6 +145,18 @@ void CauterizedModel::updateClusterMatrices() { state.clusterMatrices[j][2].x = result.trans().x; state.clusterMatrices[j][2].y = result.trans().y; state.clusterMatrices[j][2].z = result.trans().z; + + // AJT REMOVE + if (debug) { + glm::mat4 jointMatrix = _rig.getJointTransform(cluster.jointIndex); + glm::mat4 m; + glm_mat4u_mul(jointMatrix, cluster.inverseBindMatrix, m); + qDebug() << "AJT: m =" << m; + qDebug() << "AJT: (AnimPose)m =" << AnimPose(m); + qDebug() << "AJT: result =" << result; + qDebug() << "AJT: (mat4)result =" << (glm::mat4)result; + SKIN_ASSERT(result.fuzzyEqual(AnimPose(m))); + } #endif } } @@ -146,7 +170,6 @@ void CauterizedModel::updateClusterMatrices() { glm::vec4(0.0f, 0.0f, 0.0f, 0.0f), glm::vec4(0.0f, 0.0f, 0.0f, 1.0f)); auto cauterizeMatrix = _rig.getJointTransform(geometry.neckJointIndex) * zeroScale; - auto cauterizePose = AnimPose(cauterizeMatrix); for (int i = 0; i < _cauterizeMeshStates.size(); i++) { Model::MeshState& state = _cauterizeMeshStates[i]; @@ -164,11 +187,19 @@ void CauterizedModel::updateClusterMatrices() { glm_mat4u_mul(jointMatrix, cluster.inverseBindMatrix, state.clusterMatrices[j]); #endif #ifdef SKIN_COMP + + if (debug) { + qDebug() << "AJT: CAUTERIZED _meshState[" << i << "], cluster[" << j << "]"; + } + auto jointPose = _rig.getJointPose(cluster.jointIndex); + /* if (_cauterizeBoneSet.find(cluster.jointIndex) != _cauterizeBoneSet.end()) { jointPose = cauterizePose; } + */ AnimPose result = jointPose * AnimPose(cluster.inverseBindMatrix); + result.rot() = glm::normalize(result.rot()); // pack scale rotation and translation into a mat4. state.clusterMatrices[j][0].x = result.scale().x; @@ -183,6 +214,23 @@ void CauterizedModel::updateClusterMatrices() { state.clusterMatrices[j][2].x = result.trans().x; state.clusterMatrices[j][2].y = result.trans().y; state.clusterMatrices[j][2].z = result.trans().z; + + // AJT REMOVE + auto jointMatrix = _rig.getJointTransform(cluster.jointIndex); + /* + if (_cauterizeBoneSet.find(cluster.jointIndex) != _cauterizeBoneSet.end()) { + jointMatrix = cauterizeMatrix; + } + */ + if (debug) { + glm::mat4 m; + glm_mat4u_mul(jointMatrix, cluster.inverseBindMatrix, m); + qDebug() << "AJT: m =" << m; + qDebug() << "AJT: (AnimPose)m =" << AnimPose(m); + qDebug() << "AJT: result =" << result; + qDebug() << "AJT: (mat4)result =" << (glm::mat4)result; + SKIN_ASSERT(result.fuzzyEqual(AnimPose(m))); + } #endif } } From 18113d824c3f8c8340c6affc67c8bbf997728452 Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Tue, 19 Dec 2017 15:57:06 -0800 Subject: [PATCH 05/32] Created Model::TransformComponents class --- .../src/CauterizedMeshPartPayload.cpp | 30 +++- .../src/CauterizedMeshPartPayload.h | 7 +- .../render-utils/src/CauterizedModel.cpp | 139 ++++------------- .../render-utils/src/MeshPartPayload.cpp | 83 ++++++----- libraries/render-utils/src/MeshPartPayload.h | 10 +- libraries/render-utils/src/Model.cpp | 53 ++----- libraries/render-utils/src/Model.h | 45 +++++- libraries/render-utils/src/Skinning.slh | 140 ++++++++---------- .../render-utils/src/SoftAttachmentModel.cpp | 34 +---- 9 files changed, 237 insertions(+), 304 deletions(-) diff --git a/libraries/render-utils/src/CauterizedMeshPartPayload.cpp b/libraries/render-utils/src/CauterizedMeshPartPayload.cpp index 396cd13508..c64c98b271 100644 --- a/libraries/render-utils/src/CauterizedMeshPartPayload.cpp +++ b/libraries/render-utils/src/CauterizedMeshPartPayload.cpp @@ -20,19 +20,35 @@ using namespace render; CauterizedMeshPartPayload::CauterizedMeshPartPayload(ModelPointer model, int meshIndex, int partIndex, int shapeIndex, const Transform& transform, const Transform& offsetTransform) : ModelMeshPartPayload(model, meshIndex, partIndex, shapeIndex, transform, offsetTransform) {} -void CauterizedMeshPartPayload::updateClusterBuffer(const std::vector& clusterMatrices, const std::vector& cauterizedClusterMatrices) { - ModelMeshPartPayload::updateClusterBuffer(clusterMatrices); +#ifdef SKIN_COMP +void CauterizedMeshPartPayload::updateClusterBuffer(const std::vector& clusterTransforms, const std::vector& cauterizedClusterTransforms) { + ModelMeshPartPayload::updateClusterBuffer(clusterTransforms); - if (cauterizedClusterMatrices.size() > 1) { + if (cauterizedClusterTransforms.size() > 1) { if (!_cauterizedClusterBuffer) { - _cauterizedClusterBuffer = std::make_shared(cauterizedClusterMatrices.size() * sizeof(glm::mat4), - (const gpu::Byte*) cauterizedClusterMatrices.data()); + _cauterizedClusterBuffer = std::make_shared(cauterizedClusterTransforms.size() * sizeof(Model::TransformComponents), + (const gpu::Byte*) cauterizedClusterTransforms.data()); } else { - _cauterizedClusterBuffer->setSubData(0, cauterizedClusterMatrices.size() * sizeof(glm::mat4), - (const gpu::Byte*) cauterizedClusterMatrices.data()); + _cauterizedClusterBuffer->setSubData(0, cauterizedClusterTransforms.size() * sizeof(Model::TransformComponents), + (const gpu::Byte*) cauterizedClusterTransforms.data()); } } } +#else +void CauterizedMeshPartPayload::updateClusterBuffer(const std::vector& clusterTransforms, const std::vector& cauterizedClusterTransforms) { + ModelMeshPartPayload::updateClusterBuffer(clusterTransforms); + + if (cauterizedClusterTransforms.size() > 1) { + if (!_cauterizedClusterBuffer) { + _cauterizedClusterBuffer = std::make_shared(cauterizedClusterTransforms.size() * sizeof(glm::mat4), + (const gpu::Byte*) cauterizedClusterTransforms.data()); + } else { + _cauterizedClusterBuffer->setSubData(0, cauterizedClusterTransforms.size() * sizeof(glm::mat4), + (const gpu::Byte*) cauterizedClusterTransforms.data()); + } + } +} +#endif void CauterizedMeshPartPayload::updateTransformForCauterizedMesh(const Transform& renderTransform) { _cauterizedTransform = renderTransform; diff --git a/libraries/render-utils/src/CauterizedMeshPartPayload.h b/libraries/render-utils/src/CauterizedMeshPartPayload.h index 44eddc6e31..1ee77c300f 100644 --- a/libraries/render-utils/src/CauterizedMeshPartPayload.h +++ b/libraries/render-utils/src/CauterizedMeshPartPayload.h @@ -14,8 +14,11 @@ class CauterizedMeshPartPayload : public ModelMeshPartPayload { public: CauterizedMeshPartPayload(ModelPointer model, int meshIndex, int partIndex, int shapeIndex, const Transform& transform, const Transform& offsetTransform); - - void updateClusterBuffer(const std::vector& clusterMatrices, const std::vector& cauterizedClusterMatrices); +#ifdef SKIN_COMP + void updateClusterBuffer(const std::vector& clusterTransforms, const std::vector& cauterizedClusterTransforms); +#else + void updateClusterBuffer(const std::vector& clusterTransforms, const std::vector& cauterizedClusterTransforms); +#endif void updateTransformForCauterizedMesh(const Transform& renderTransform); diff --git a/libraries/render-utils/src/CauterizedModel.cpp b/libraries/render-utils/src/CauterizedModel.cpp index 282aedf180..606e1f0351 100644 --- a/libraries/render-utils/src/CauterizedModel.cpp +++ b/libraries/render-utils/src/CauterizedModel.cpp @@ -35,7 +35,7 @@ bool CauterizedModel::updateGeometry() { const FBXGeometry& fbxGeometry = getFBXGeometry(); foreach (const FBXMesh& mesh, fbxGeometry.meshes) { Model::MeshState state; - state.clusterMatrices.resize(mesh.clusters.size()); + state.clusterTransforms.resize(mesh.clusters.size()); _cauterizeMeshStates.append(state); } } @@ -116,47 +116,14 @@ void CauterizedModel::updateClusterMatrices() { for (int j = 0; j < mesh.clusters.size(); j++) { const FBXCluster& cluster = mesh.clusters.at(j); - /* AJT: TODO REMOVE */ -#ifdef SKIN_MATRIX - SKIN_ASSERT(false); auto jointMatrix = _rig.getJointTransform(cluster.jointIndex); - glm_mat4u_mul(jointMatrix, cluster.inverseBindMatrix, state.clusterMatrices[j]); -#endif #ifdef SKIN_COMP - - if (debug) { - qDebug() << "AJT: _meshState[" << i << "], cluster[" << j << "]"; - } - - AnimPose jointPose = _rig.getJointPose(cluster.jointIndex); - AnimPose result = jointPose * AnimPose(cluster.inverseBindMatrix); - result.rot() = glm::normalize(result.rot()); - - // pack scale rotation and translation into a mat4. - state.clusterMatrices[j][0].x = result.scale().x; - state.clusterMatrices[j][0].y = result.scale().y; - state.clusterMatrices[j][0].z = result.scale().z; - - state.clusterMatrices[j][1].x = result.rot().x; - state.clusterMatrices[j][1].y = result.rot().y; - state.clusterMatrices[j][1].z = result.rot().z; - state.clusterMatrices[j][1].w = result.rot().w; - - state.clusterMatrices[j][2].x = result.trans().x; - state.clusterMatrices[j][2].y = result.trans().y; - state.clusterMatrices[j][2].z = result.trans().z; - - // AJT REMOVE - if (debug) { - glm::mat4 jointMatrix = _rig.getJointTransform(cluster.jointIndex); - glm::mat4 m; - glm_mat4u_mul(jointMatrix, cluster.inverseBindMatrix, m); - qDebug() << "AJT: m =" << m; - qDebug() << "AJT: (AnimPose)m =" << AnimPose(m); - qDebug() << "AJT: result =" << result; - qDebug() << "AJT: (mat4)result =" << (glm::mat4)result; - SKIN_ASSERT(result.fuzzyEqual(AnimPose(m))); - } + // AJT: TODO: optimize + glm::mat4 m; + glm_mat4u_mul(jointMatrix, cluster.inverseBindMatrix, m); + state.clusterTransforms[j] = Model::TransformComponents(m); +#else + glm_mat4u_mul(jointMatrix, cluster.inverseBindMatrix, state.clusterTransforms[j]); #endif } } @@ -177,60 +144,18 @@ void CauterizedModel::updateClusterMatrices() { for (int j = 0; j < mesh.clusters.size(); j++) { const FBXCluster& cluster = mesh.clusters.at(j); -#ifdef SKIN_MATRIX - SKIN_ASSERT(false); - // AJT: TODO REMOVE: auto jointMatrix = _rig.getJointTransform(cluster.jointIndex); if (_cauterizeBoneSet.find(cluster.jointIndex) != _cauterizeBoneSet.end()) { jointMatrix = cauterizeMatrix; } - glm_mat4u_mul(jointMatrix, cluster.inverseBindMatrix, state.clusterMatrices[j]); -#endif + #ifdef SKIN_COMP - - if (debug) { - qDebug() << "AJT: CAUTERIZED _meshState[" << i << "], cluster[" << j << "]"; - } - - auto jointPose = _rig.getJointPose(cluster.jointIndex); - /* - if (_cauterizeBoneSet.find(cluster.jointIndex) != _cauterizeBoneSet.end()) { - jointPose = cauterizePose; - } - */ - AnimPose result = jointPose * AnimPose(cluster.inverseBindMatrix); - result.rot() = glm::normalize(result.rot()); - - // pack scale rotation and translation into a mat4. - state.clusterMatrices[j][0].x = result.scale().x; - state.clusterMatrices[j][0].y = result.scale().y; - state.clusterMatrices[j][0].z = result.scale().z; - - state.clusterMatrices[j][1].x = result.rot().x; - state.clusterMatrices[j][1].y = result.rot().y; - state.clusterMatrices[j][1].z = result.rot().z; - state.clusterMatrices[j][1].w = result.rot().w; - - state.clusterMatrices[j][2].x = result.trans().x; - state.clusterMatrices[j][2].y = result.trans().y; - state.clusterMatrices[j][2].z = result.trans().z; - - // AJT REMOVE - auto jointMatrix = _rig.getJointTransform(cluster.jointIndex); - /* - if (_cauterizeBoneSet.find(cluster.jointIndex) != _cauterizeBoneSet.end()) { - jointMatrix = cauterizeMatrix; - } - */ - if (debug) { - glm::mat4 m; - glm_mat4u_mul(jointMatrix, cluster.inverseBindMatrix, m); - qDebug() << "AJT: m =" << m; - qDebug() << "AJT: (AnimPose)m =" << AnimPose(m); - qDebug() << "AJT: result =" << result; - qDebug() << "AJT: (mat4)result =" << (glm::mat4)result; - SKIN_ASSERT(result.fuzzyEqual(AnimPose(m))); - } + // AJT: TODO: optimize + glm::mat4 m; + glm_mat4u_mul(jointMatrix, cluster.inverseBindMatrix, m); + state.clusterTransforms[j] = Model::TransformComponents(m); +#else + glm_mat4u_mul(jointMatrix, cluster.inverseBindMatrix, state.clusterTransforms[j]); #endif } } @@ -288,43 +213,31 @@ void CauterizedModel::updateRenderItems() { auto itemID = self->_modelMeshRenderItemIDs[i]; auto meshIndex = self->_modelMeshRenderItemShapes[i].meshIndex; - auto clusterMatrices(self->getMeshState(meshIndex).clusterMatrices); - auto clusterMatricesCauterized(self->getCauterizeMeshState(meshIndex).clusterMatrices); + auto clusterTransforms(self->getMeshState(meshIndex).clusterTransforms); + auto clusterTransformsCauterized(self->getCauterizeMeshState(meshIndex).clusterTransforms); bool invalidatePayloadShapeKey = self->shouldInvalidatePayloadShapeKey(meshIndex); - transaction.updateItem(itemID, [modelTransform, clusterMatrices, clusterMatricesCauterized, invalidatePayloadShapeKey, + transaction.updateItem(itemID, [modelTransform, clusterTransforms, clusterTransformsCauterized, invalidatePayloadShapeKey, isWireframe, isVisible, isLayeredInFront, isLayeredInHUD, enableCauterization](CauterizedMeshPartPayload& data) { - data.updateClusterBuffer(clusterMatrices, clusterMatricesCauterized); + data.updateClusterBuffer(clusterTransforms, clusterTransformsCauterized); Transform renderTransform = modelTransform; - if (clusterMatrices.size() == 1) { -#ifdef SKIN_MATRIX - SKIN_ASSERT(false); - renderTransform = modelTransform.worldTransform(Transform(clusterMatrices[0])); -#endif + if (clusterTransforms.size() == 1) { #ifdef SKIN_COMP - glm::vec3 scale(clusterMatrices[0][0]); - glm::quat rot(clusterMatrices[0][1].w, clusterMatrices[0][1].x, clusterMatrices[0][1].y, clusterMatrices[0][1].z); - glm::vec3 trans(clusterMatrices[0][2]); - glm::mat4 m = createMatFromScaleQuatAndPos(scale, rot, trans); - renderTransform = modelTransform.worldTransform(Transform(m)); + renderTransform = modelTransform.worldTransform(Transform(clusterTransforms[0].getMatrix())); +#else + renderTransform = modelTransform.worldTransform(Transform(clusterTransforms[0])); #endif } data.updateTransformForSkinnedMesh(renderTransform, modelTransform); renderTransform = modelTransform; - if (clusterMatricesCauterized.size() == 1) { -#ifdef SKIN_MATRIX - SKIN_ASSERT(false); - renderTransform = modelTransform.worldTransform(Transform(clusterMatricesCauterized[0])); -#endif + if (clusterTransformsCauterized.size() == 1) { #ifdef SKIN_COMP - glm::vec3 scale(clusterMatricesCauterized[0][0]); - glm::quat rot(clusterMatricesCauterized[0][1].w, clusterMatricesCauterized[0][1].x, clusterMatricesCauterized[0][1].y, clusterMatricesCauterized[0][1].z); - glm::vec3 trans(clusterMatricesCauterized[0][2]); - glm::mat4 m = createMatFromScaleQuatAndPos(scale, rot, trans); - renderTransform = modelTransform.worldTransform(Transform(m)); + renderTransform = modelTransform.worldTransform(Transform(clusterTransformsCauterized[0].getMatrix())); +#else + renderTransform = modelTransform.worldTransform(Transform(clusterTransformsCauterized[0])); #endif } data.updateTransformForCauterizedMesh(renderTransform); diff --git a/libraries/render-utils/src/MeshPartPayload.cpp b/libraries/render-utils/src/MeshPartPayload.cpp index fcf6a61fb5..3056dbc728 100644 --- a/libraries/render-utils/src/MeshPartPayload.cpp +++ b/libraries/render-utils/src/MeshPartPayload.cpp @@ -326,22 +326,17 @@ ModelMeshPartPayload::ModelMeshPartPayload(ModelPointer model, int meshIndex, in const Model::MeshState& state = model->getMeshState(_meshIndex); updateMeshPart(modelMesh, partIndex); - computeAdjustedLocalBound(state.clusterMatrices); + computeAdjustedLocalBound(state.clusterTransforms); updateTransform(transform, offsetTransform); Transform renderTransform = transform; - if (state.clusterMatrices.size() == 1) { -#ifdef SKIN_MATRIX - SKIN_ASSERT(false); - renderTransform = transform.worldTransform(Transform(state.clusterMatrices[0])); -#endif + if (state.clusterTransforms.size() == 1) { #ifdef SKIN_COMP - glm::vec3 scale(state.clusterMatrices[0][0]); - glm::quat rot(state.clusterMatrices[0][1].w, state.clusterMatrices[0][1].x, state.clusterMatrices[0][1].y, state.clusterMatrices[0][1].z); - glm::vec3 trans(state.clusterMatrices[0][2]); - glm::mat4 m = createMatFromScaleQuatAndPos(scale, rot, trans); - renderTransform = transform.worldTransform(Transform(m)); + renderTransform = transform.worldTransform(Transform(state.clusterTransforms[0].getMatrix())); +#else + renderTransform = transform.worldTransform(Transform(state.clusterTransforms[0])); #endif + } updateTransformForSkinnedMesh(renderTransform, transform); @@ -371,20 +366,36 @@ void ModelMeshPartPayload::notifyLocationChanged() { } - -void ModelMeshPartPayload::updateClusterBuffer(const std::vector& clusterMatrices) { +#ifdef SKIN_COMP +void ModelMeshPartPayload::updateClusterBuffer(const std::vector& clusterTransforms) { // Once computed the cluster matrices, update the buffer(s) - if (clusterMatrices.size() > 1) { + if (clusterTransforms.size() > 1) { if (!_clusterBuffer) { - _clusterBuffer = std::make_shared(clusterMatrices.size() * sizeof(glm::mat4), - (const gpu::Byte*) clusterMatrices.data()); + _clusterBuffer = std::make_shared(clusterTransforms.size() * sizeof(Model::TransformComponents), + (const gpu::Byte*) clusterTransforms.data()); } else { - _clusterBuffer->setSubData(0, clusterMatrices.size() * sizeof(glm::mat4), - (const gpu::Byte*) clusterMatrices.data()); + _clusterBuffer->setSubData(0, clusterTransforms.size() * sizeof(Model::TransformComponents), + (const gpu::Byte*) clusterTransforms.data()); } } } +#else +void ModelMeshPartPayload::updateClusterBuffer(const std::vector& clusterTransforms) { + // Once computed the cluster matrices, update the buffer(s) + if (clusterTransforms.size() > 1) { + if (!_clusterBuffer) { + _clusterBuffer = std::make_shared(clusterTransforms.size() * sizeof(glm::mat4), + (const gpu::Byte*) clusterTransforms.data()); + } + else { + _clusterBuffer->setSubData(0, clusterTransforms.size() * sizeof(glm::mat4), + (const gpu::Byte*) clusterTransforms.data()); + } + } +} +#endif + void ModelMeshPartPayload::updateTransformForSkinnedMesh(const Transform& renderTransform, const Transform& boundTransform) { _transform = renderTransform; @@ -550,23 +561,27 @@ void ModelMeshPartPayload::render(RenderArgs* args) { args->_details._trianglesRendered += _drawPart._numIndices / INDICES_PER_TRIANGLE; } -void ModelMeshPartPayload::computeAdjustedLocalBound(const std::vector& clusterMatrices) { - _adjustedLocalBound = _localBound; - if (clusterMatrices.size() > 0) { - _adjustedLocalBound.transform(clusterMatrices[0]); - for (int i = 1; i < (int)clusterMatrices.size(); ++i) { - AABox clusterBound = _localBound; -#ifdef SKIN_MATRIX - SKIN_ASSERT(false); - clusterBound.transform(clusterMatrices[i]); -#endif #ifdef SKIN_COMP - // AJT: FIXME: TODO: SLOW AS SHIT - glm::vec3 scale(clusterMatrices[i][0]); - glm::quat rot(clusterMatrices[i][1].w, clusterMatrices[i][1].x, clusterMatrices[i][1].y, clusterMatrices[i][1].z); - glm::vec3 trans(clusterMatrices[i][2]); - glm::mat4 m = createMatFromScaleQuatAndPos(scale, rot, trans); - clusterBound.transform(m); +void ModelMeshPartPayload::computeAdjustedLocalBound(const std::vector& clusterTransforms) { +#else +void ModelMeshPartPayload::computeAdjustedLocalBound(const std::vector& clusterTransforms) { +#endif + _adjustedLocalBound = _localBound; + if (clusterTransforms.size() > 0) { +#ifdef SKIN_COMP + // AJT: TODO: optimize + _adjustedLocalBound.transform(clusterTransforms[0].getMatrix()); +#else + _adjustedLocalBound.transform(clusterTransforms[0]); +#endif + + for (int i = 1; i < (int)clusterTransforms.size(); ++i) { + AABox clusterBound = _localBound; +#ifdef SKIN_COMP + // AJT: TODO: optimize + clusterBound.transform(clusterTransforms[i].getMatrix()); +#else + clusterBound.transform(clusterTransforms[i]); #endif _adjustedLocalBound += clusterBound; } diff --git a/libraries/render-utils/src/MeshPartPayload.h b/libraries/render-utils/src/MeshPartPayload.h index fb55883101..989a653b2f 100644 --- a/libraries/render-utils/src/MeshPartPayload.h +++ b/libraries/render-utils/src/MeshPartPayload.h @@ -87,7 +87,11 @@ public: typedef Payload::DataPointer Pointer; void notifyLocationChanged() override; - void updateClusterBuffer(const std::vector& clusterMatrices); +#ifdef SKIN_COMP + void updateClusterBuffer(const std::vector& clusterTransforms); +#else + void updateClusterBuffer(const std::vector& clusterTransforms); +#endif void updateTransformForSkinnedMesh(const Transform& renderTransform, const Transform& boundTransform); // Render Item interface @@ -104,7 +108,11 @@ public: void bindMesh(gpu::Batch& batch) override; void bindTransform(gpu::Batch& batch, const render::ShapePipeline::LocationsPointer locations, RenderArgs::RenderMode renderMode) const override; +#ifdef SKIN_COMP + void computeAdjustedLocalBound(const std::vector& clusterTransforms); +#else void computeAdjustedLocalBound(const std::vector& clusterMatrices); +#endif gpu::BufferPointer _clusterBuffer; diff --git a/libraries/render-utils/src/Model.cpp b/libraries/render-utils/src/Model.cpp index 36d26dea7b..0f014032a3 100644 --- a/libraries/render-utils/src/Model.cpp +++ b/libraries/render-utils/src/Model.cpp @@ -266,25 +266,20 @@ void Model::updateRenderItems() { auto itemID = self->_modelMeshRenderItemIDs[i]; auto meshIndex = self->_modelMeshRenderItemShapes[i].meshIndex; - auto clusterMatrices(self->getMeshState(meshIndex).clusterMatrices); + auto clusterTransforms(self->getMeshState(meshIndex).clusterTransforms); bool invalidatePayloadShapeKey = self->shouldInvalidatePayloadShapeKey(meshIndex); - transaction.updateItem(itemID, [modelTransform, clusterMatrices, invalidatePayloadShapeKey, + transaction.updateItem(itemID, [modelTransform, clusterTransforms, invalidatePayloadShapeKey, isWireframe, isVisible, isLayeredInFront, isLayeredInHUD](ModelMeshPartPayload& data) { - data.updateClusterBuffer(clusterMatrices); + data.updateClusterBuffer(clusterTransforms); + Transform renderTransform = modelTransform; - if (clusterMatrices.size() == 1) { -#ifdef SKIN_MATRIX - SKIN_ASSERT(false); - renderTransform = modelTransform.worldTransform(Transform(clusterMatrices[0])); -#endif + if (clusterTransforms.size() == 1) { #ifdef SKIN_COMP - glm::vec3 scale(clusterMatrices[0][0]); - glm::quat rot(clusterMatrices[0][1].w, clusterMatrices[0][1].x, clusterMatrices[0][1].y, clusterMatrices[0][1].z); - glm::vec3 trans(clusterMatrices[0][2]); - glm::mat4 m = createMatFromScaleQuatAndPos(scale, rot, trans); - renderTransform = modelTransform.worldTransform(Transform(m)); + renderTransform = modelTransform.worldTransform(Transform(clusterTransforms[0].getMatrix())); +#else + renderTransform = modelTransform.worldTransform(Transform(clusterTransforms[0])); #endif } data.updateTransformForSkinnedMesh(renderTransform, modelTransform); @@ -338,7 +333,7 @@ bool Model::updateGeometry() { const FBXGeometry& fbxGeometry = getFBXGeometry(); foreach (const FBXMesh& mesh, fbxGeometry.meshes) { MeshState state; - state.clusterMatrices.resize(mesh.clusters.size()); + state.clusterTransforms.resize(mesh.clusters.size()); _meshStates.push_back(state); // Note: we add empty buffers for meshes that lack blendshapes so we can access the buffers by index @@ -1169,7 +1164,7 @@ void Model::updateRig(float deltaTime, glm::mat4 parentTransform) { void Model::computeMeshPartLocalBounds() { for (auto& part : _modelMeshRenderItems) { const Model::MeshState& state = _meshStates.at(part->_meshIndex); - part->computeAdjustedLocalBound(state.clusterMatrices); + part->computeAdjustedLocalBound(state.clusterTransforms); } } @@ -1187,30 +1182,14 @@ void Model::updateClusterMatrices() { const FBXMesh& mesh = geometry.meshes.at(i); for (int j = 0; j < mesh.clusters.size(); j++) { const FBXCluster& cluster = mesh.clusters.at(j); - - // AJT: TODO FIXME -#ifdef SKIN_MATRIX - SKIN_ASSERT(false); auto jointMatrix = _rig.getJointTransform(cluster.jointIndex); - glm_mat4u_mul(jointMatrix, cluster.inverseBindMatrix, state.clusterMatrices[j]); -#endif #ifdef SKIN_COMP - AnimPose jointPose = _rig.getJointPose(cluster.jointIndex); - AnimPose result = jointPose * AnimPose(cluster.inverseBindMatrix); - - // pack scale rotation and translation into a mat4. - state.clusterMatrices[j][0].x = result.scale().x; - state.clusterMatrices[j][0].y = result.scale().y; - state.clusterMatrices[j][0].z = result.scale().z; - - state.clusterMatrices[j][1].x = result.rot().x; - state.clusterMatrices[j][1].y = result.rot().y; - state.clusterMatrices[j][1].z = result.rot().z; - state.clusterMatrices[j][1].w = result.rot().w; - - state.clusterMatrices[j][2].x = result.trans().x; - state.clusterMatrices[j][2].y = result.trans().y; - state.clusterMatrices[j][2].z = result.trans().z; + // AJT: TODO: optimize + glm::mat4 mat; + glm_mat4u_mul(jointMatrix, cluster.inverseBindMatrix, mat); + state.clusterTransforms[j] = TransformComponents(mat); +#else + glm_mat4u_mul(jointMatrix, cluster.inverseBindMatrix, state.clusterTransforms[j]); #endif } } diff --git a/libraries/render-utils/src/Model.h b/libraries/render-utils/src/Model.h index b798d77ab9..e8f11a421b 100644 --- a/libraries/render-utils/src/Model.h +++ b/libraries/render-utils/src/Model.h @@ -43,8 +43,6 @@ } \ } while(false) -//#define SKIN_MATRIX -//#define SKIN_DUAL_QUAT #define SKIN_COMP class AbstractViewStateInterface; @@ -257,9 +255,50 @@ public: int getRenderInfoDrawCalls() const { return _renderInfoDrawCalls; } bool getRenderInfoHasTransparent() const { return _renderInfoHasTransparent; } + class TransformComponents { + public: + TransformComponents() {} + TransformComponents(const glm::mat4& m) { + AnimPose p(m); + _scale.x = p.scale().x; + _scale.y = p.scale().y; + _scale.z = p.scale().z; + _rot = p.rot(); + _trans.x = p.trans().x; + _trans.y = p.trans().y; + _trans.z = p.trans().z; + } + + TransformComponents(const glm::vec3& scale, const glm::quat& rot, const glm::vec3& trans) { + _scale.x = scale.x; + _scale.y = scale.y; + _scale.z = scale.z; + _rot = rot; + _trans.x = trans.x; + _trans.y = trans.y; + _trans.z = trans.z; + } + + glm::vec3 getScale() const { return glm::vec3(_scale); } + glm::quat getRot() const { return _rot; } + glm::vec3 getTrans() const { return glm::vec3(_trans); } + glm::mat4 getMatrix() const { return createMatFromScaleQuatAndPos(getScale(), getRot(), getTrans()); }; + + protected: + glm::vec4 _scale { 1.0f, 1.0f, 1.0f, 0.0f }; + glm::quat _rot { 1.0f, 0.0f, 0.0f, 0.0f }; + glm::vec4 _trans { 0.0f, 0.0f, 0.0f, 0.0f }; + glm::vec4 _padding { 0.0f, 0.0f, 0.0f, 0.0f }; + }; + class MeshState { public: - std::vector clusterMatrices; +#ifdef SKIN_COMP + std::vector clusterTransforms; +#else + std::vector clusterTransforms; +#endif + }; const MeshState& getMeshState(int index) { return _meshStates.at(index); } diff --git a/libraries/render-utils/src/Skinning.slh b/libraries/render-utils/src/Skinning.slh index 371efef145..63bb6ba46e 100644 --- a/libraries/render-utils/src/Skinning.slh +++ b/libraries/render-utils/src/Skinning.slh @@ -38,60 +38,11 @@ void dqMul(vec4 lhsReal, vec4 lhsImag, vec4 rhsReal, vec4 rhsImag, out vec4 real imagOut = quatMul(lhsReal, rhsImag) + quatMul(lhsImag, rhsReal); } -// dual quat blend -void blendClusters_dual_quat(ivec4 skinClusterIndex, vec4 skinClusterWeight, out vec3 scaleOut, out vec4 rotOut, out vec3 posOut) { - vec3 scale = vec3(0.0, 0.0, 0.0); - vec4 dqReal = vec4(0.0, 0.0, 0.0, 0.0); - vec4 dqImag = vec4(0.0, 0.0, 0.0, 0.0); - for (int i = 0; i < INDICES_PER_VERTEX; i++) { - mat4 clusterMatrix = clusterMatrices[(skinClusterIndex[i])]; - float clusterWeight = skinClusterWeight[i]; +void skinPosition(ivec4 skinClusterIndex, vec4 skinClusterWeight, vec4 inPosition, out vec4 skinnedPosition) { + vec3 sAccum = vec3(0.0, 0.0, 0.0); + vec4 rAccum = vec4(0.0, 0.0, 0.0, 0.0); + vec3 tAccum = vec3(0.0, 0.0, 0.0); - vec3 s = vec3(clusterMatrix[0][0], clusterMatrix[0][1], clusterMatrix[0][2]); - vec4 dqr = vec4(clusterMatrix[1][0], clusterMatrix[1][1], clusterMatrix[1][2], clusterMatrix[1][3]); - vec4 dqi = vec4(clusterMatrix[2][0], clusterMatrix[2][1], clusterMatrix[2][2], clusterMatrix[2][3]); - - scale += s * clusterWeight; - dqReal += dqr * clusterWeight; - dqImag += dqi * clusterWeight; - } - - scaleOut = scale; - - float dqLen = length(dqReal); - dqReal *= 1.0 / dqLen; - dqImag *= 1.0 / dqLen; - - rotOut = dqReal; - - vec4 invReal = quatConj(dqReal); - posOut.xyz = 2.0 * quatMul(dqImag, invReal).xyz; -} - -// rigid dual quat blend -void blendClusters_rigid_dual_quat(ivec4 skinClusterIndex, vec4 skinClusterWeight, out vec3 scaleOut, out vec4 rotOut, out vec3 posOut) { - float maxWeight = 0.0; - for (int i = 0; i < INDICES_PER_VERTEX; i++) { - mat4 clusterMatrix = clusterMatrices[(skinClusterIndex[i])]; - float clusterWeight = skinClusterWeight[i]; - - vec3 s = vec3(clusterMatrix[0][0], clusterMatrix[0][1], clusterMatrix[0][2]); - vec4 dqr = vec4(clusterMatrix[1][0], clusterMatrix[1][1], clusterMatrix[1][2], clusterMatrix[1][3]); - vec4 dqi = vec4(clusterMatrix[2][0], clusterMatrix[2][1], clusterMatrix[2][2], clusterMatrix[2][3]); - - if (clusterWeight > maxWeight) { - maxWeight = clusterWeight; - scaleOut = s; - rotOut = dqr; - vec4 invReal = quatConj(dqr); - posOut = 2.0 * quatMul(dqi, invReal).xyz; - } - } -} - -// rigid componentwise blend -void blendClusters(ivec4 skinClusterIndex, vec4 skinClusterWeight, out vec3 scaleOut, out vec4 rotOut, out vec3 posOut) { - float maxWeight = 0.0; for (int i = 0; i < INDICES_PER_VERTEX; i++) { mat4 clusterMatrix = clusterMatrices[(skinClusterIndex[i])]; float clusterWeight = skinClusterWeight[i]; @@ -100,51 +51,83 @@ void blendClusters(ivec4 skinClusterIndex, vec4 skinClusterWeight, out vec3 scal vec4 r = vec4(clusterMatrix[1][0], clusterMatrix[1][1], clusterMatrix[1][2], clusterMatrix[1][3]); vec3 t = vec3(clusterMatrix[2][0], clusterMatrix[2][1], clusterMatrix[2][2]); - if (clusterWeight > maxWeight) { - maxWeight = clusterWeight; - scaleOut = s; - rotOut = r; - posOut = t; + if (dot(r, rAccum) < 0) { + r = -r; } + + sAccum += s * clusterWeight; + rAccum += r * clusterWeight; + tAccum += t * clusterWeight; } -} -void skinPosition(ivec4 skinClusterIndex, vec4 skinClusterWeight, vec4 inPosition, out vec4 skinnedPosition) { + rAccum = normalize(rAccum); - vec3 scale, pos; - vec4 rot; - blendClusters(skinClusterIndex, skinClusterWeight, scale, rot, pos); - - skinnedPosition.xyz = rotateByQuat(rot, (vec3(inPosition) * scale)) + pos; - skinnedPosition.w = 1; + skinnedPosition = vec4(rotateByQuat(rAccum, (vec3(inPosition) * sAccum)) + tAccum, 1); } void skinPositionNormal(ivec4 skinClusterIndex, vec4 skinClusterWeight, vec4 inPosition, vec3 inNormal, out vec4 skinnedPosition, out vec3 skinnedNormal) { - vec3 scale, pos; - vec4 rot; - blendClusters(skinClusterIndex, skinClusterWeight, scale, rot, pos); + vec3 sAccum = vec3(0.0, 0.0, 0.0); + vec4 rAccum = vec4(0.0, 0.0, 0.0, 0.0); + vec3 tAccum = vec3(0.0, 0.0, 0.0); - skinnedNormal.xyz = rotateByQuat(rot, inNormal * scale); + for (int i = 0; i < INDICES_PER_VERTEX; i++) { + mat4 clusterMatrix = clusterMatrices[(skinClusterIndex[i])]; + float clusterWeight = skinClusterWeight[i]; + + vec3 s = vec3(clusterMatrix[0][0], clusterMatrix[0][1], clusterMatrix[0][2]); + vec4 r = vec4(clusterMatrix[1][0], clusterMatrix[1][1], clusterMatrix[1][2], clusterMatrix[1][3]); + vec3 t = vec3(clusterMatrix[2][0], clusterMatrix[2][1], clusterMatrix[2][2]); + + if (dot(r, rAccum) < 0) { + r = -r; + } + + sAccum += s * clusterWeight; + rAccum += r * clusterWeight; + tAccum += t * clusterWeight; + } + + rAccum = normalize(rAccum); + + skinnedPosition = vec4(rotateByQuat(rAccum, (vec3(inPosition) * sAccum)) + tAccum, 1); + skinnedNormal = rotateByQuat(rAccum, inNormal); } void skinPositionNormalTangent(ivec4 skinClusterIndex, vec4 skinClusterWeight, vec4 inPosition, vec3 inNormal, vec3 inTangent, out vec4 skinnedPosition, out vec3 skinnedNormal, out vec3 skinnedTangent) { - vec3 scale, pos; - vec4 rot; - blendClusters(skinClusterIndex, skinClusterWeight, scale, rot, pos); + vec3 sAccum = vec3(0.0, 0.0, 0.0); + vec4 rAccum = vec4(0.0, 0.0, 0.0, 0.0); + vec3 tAccum = vec3(0.0, 0.0, 0.0); - skinnedPosition.xyz = rotateByQuat(rot, (vec3(inPosition) * scale)) + pos; - skinnedPosition.w = 1; + for (int i = 0; i < INDICES_PER_VERTEX; i++) { + mat4 clusterMatrix = clusterMatrices[(skinClusterIndex[i])]; + float clusterWeight = skinClusterWeight[i]; - skinnedNormal = rotateByQuat(rot, inNormal * scale); - skinnedTangent = rotateByQuat(rot, inTangent * scale); + vec3 s = vec3(clusterMatrix[0][0], clusterMatrix[0][1], clusterMatrix[0][2]); + vec4 r = vec4(clusterMatrix[1][0], clusterMatrix[1][1], clusterMatrix[1][2], clusterMatrix[1][3]); + vec3 t = vec3(clusterMatrix[2][0], clusterMatrix[2][1], clusterMatrix[2][2]); + + if (dot(r, rAccum) < 0) { + r = -r; + } + + sAccum += s * clusterWeight; + rAccum += r * clusterWeight; + tAccum += t * clusterWeight; + } + + rAccum = normalize(rAccum); + + skinnedPosition = vec4(rotateByQuat(rAccum, (vec3(inPosition) * sAccum)) + tAccum, 1); + skinnedNormal = rotateByQuat(rAccum, inNormal); + skinnedTangent = rotateByQuat(rAccum, inTangent); } -// ORIGINAL /* +// ORIGINAL void skinPosition(ivec4 skinClusterIndex, vec4 skinClusterWeight, vec4 inPosition, out vec4 skinnedPosition) { vec4 newPosition = vec4(0.0, 0.0, 0.0, 0.0); @@ -193,5 +176,4 @@ void skinPositionNormalTangent(ivec4 skinClusterIndex, vec4 skinClusterWeight, v } */ - <@endif@> diff --git a/libraries/render-utils/src/SoftAttachmentModel.cpp b/libraries/render-utils/src/SoftAttachmentModel.cpp index 7d49a8cda1..8fa609b241 100644 --- a/libraries/render-utils/src/SoftAttachmentModel.cpp +++ b/libraries/render-utils/src/SoftAttachmentModel.cpp @@ -50,7 +50,6 @@ void SoftAttachmentModel::updateClusterMatrices() { for (int j = 0; j < mesh.clusters.size(); j++) { const FBXCluster& cluster = mesh.clusters.at(j); -#ifdef SKIN_MATRIX // TODO: cache these look-ups as an optimization int jointIndexOverride = getJointIndexOverride(cluster.jointIndex); glm::mat4 jointMatrix; @@ -59,35 +58,14 @@ void SoftAttachmentModel::updateClusterMatrices() { } else { jointMatrix = _rig.getJointTransform(cluster.jointIndex); } - SKIN_ASSERT(false); - glm_mat4u_mul(jointMatrix, cluster.inverseBindMatrix, state.clusterMatrices[j]); -#endif #ifdef SKIN_COMP - // TODO: cache these look-ups as an optimization - int jointIndexOverride = getJointIndexOverride(cluster.jointIndex); - AnimPose jointPose; - if (jointIndexOverride >= 0 && jointIndexOverride < _rigOverride.getJointStateCount()) { - jointPose = _rigOverride.getJointPose(jointIndexOverride); - } else { - jointPose = _rig.getJointPose(cluster.jointIndex); - } - AnimPose result = jointPose * AnimPose(cluster.inverseBindMatrix); - - // pack scale rotation and translation into a mat4. - state.clusterMatrices[j][0].x = result.scale().x; - state.clusterMatrices[j][0].y = result.scale().y; - state.clusterMatrices[j][0].z = result.scale().z; - - state.clusterMatrices[j][1].x = result.rot().x; - state.clusterMatrices[j][1].y = result.rot().y; - state.clusterMatrices[j][1].z = result.rot().z; - state.clusterMatrices[j][1].w = result.rot().w; - - state.clusterMatrices[j][2].x = result.trans().x; - state.clusterMatrices[j][2].y = result.trans().y; - state.clusterMatrices[j][2].z = result.trans().z; + // AJT: TODO: Optimize + glm::mat4 m; + glm_mat4u_mul(jointMatrix, cluster.inverseBindMatrix, m); + state.clusterTransforms[j] = Model::TransformComponents(m); +#else + glm_mat4u_mul(jointMatrix, cluster.inverseBindMatrix, state.clusterTransforms[j]); #endif - } } From 3896b31a727728c04835c020e87a9380a07dfe22 Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Thu, 21 Dec 2017 14:20:41 -0800 Subject: [PATCH 06/32] Line numbers for shader when there are compilation errors --- libraries/gl/src/gl/GLShaders.cpp | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/libraries/gl/src/gl/GLShaders.cpp b/libraries/gl/src/gl/GLShaders.cpp index 8ef0198676..017c92b71c 100644 --- a/libraries/gl/src/gl/GLShaders.cpp +++ b/libraries/gl/src/gl/GLShaders.cpp @@ -63,12 +63,17 @@ namespace gl { } */ - qCWarning(glLogging) << "GLShader::compileShader - failed to compile the gl shader object:"; + qCCritical(glLogging) << "GLShader::compileShader - failed to compile the gl shader object:"; + int lineNumber = 0; for (auto s : srcstr) { - qCWarning(glLogging) << s; + QString str(s); + QStringList lines = str.split("\n"); + for (auto& line : lines) { + qCCritical(glLogging).noquote() << QString("%1: %2").arg(lineNumber++, 5, 10, QChar('0')).arg(line); + } } - qCWarning(glLogging) << "GLShader::compileShader - errors:"; - qCWarning(glLogging) << temp; + qCCritical(glLogging) << "GLShader::compileShader - errors:"; + qCCritical(glLogging) << temp; error = std::string(temp); delete[] temp; From 8bdddf721153a401344187352cb36945c47e4a0e Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Thu, 21 Dec 2017 14:22:22 -0800 Subject: [PATCH 07/32] Full Dual Quaternion support --- .../src/CauterizedMeshPartPayload.cpp | 22 +-- .../src/CauterizedMeshPartPayload.h | 11 +- .../render-utils/src/CauterizedModel.cpp | 22 ++- .../render-utils/src/MeshPartPayload.cpp | 36 +--- libraries/render-utils/src/MeshPartPayload.h | 17 +- libraries/render-utils/src/Model.cpp | 9 +- libraries/render-utils/src/Model.h | 36 +++- libraries/render-utils/src/Skinning.slh | 176 ++++++++++++++++++ .../render-utils/src/SoftAttachmentModel.cpp | 7 +- libraries/shared/src/DualQuaternion.cpp | 3 + libraries/shared/src/DualQuaternion.h | 1 + 11 files changed, 267 insertions(+), 73 deletions(-) diff --git a/libraries/render-utils/src/CauterizedMeshPartPayload.cpp b/libraries/render-utils/src/CauterizedMeshPartPayload.cpp index c64c98b271..969be6cdd7 100644 --- a/libraries/render-utils/src/CauterizedMeshPartPayload.cpp +++ b/libraries/render-utils/src/CauterizedMeshPartPayload.cpp @@ -20,35 +20,19 @@ using namespace render; CauterizedMeshPartPayload::CauterizedMeshPartPayload(ModelPointer model, int meshIndex, int partIndex, int shapeIndex, const Transform& transform, const Transform& offsetTransform) : ModelMeshPartPayload(model, meshIndex, partIndex, shapeIndex, transform, offsetTransform) {} -#ifdef SKIN_COMP -void CauterizedMeshPartPayload::updateClusterBuffer(const std::vector& clusterTransforms, const std::vector& cauterizedClusterTransforms) { +void CauterizedMeshPartPayload::updateClusterBuffer(const std::vector& clusterTransforms, const std::vector& cauterizedClusterTransforms) { ModelMeshPartPayload::updateClusterBuffer(clusterTransforms); if (cauterizedClusterTransforms.size() > 1) { if (!_cauterizedClusterBuffer) { - _cauterizedClusterBuffer = std::make_shared(cauterizedClusterTransforms.size() * sizeof(Model::TransformComponents), + _cauterizedClusterBuffer = std::make_shared(cauterizedClusterTransforms.size() * sizeof(TransformType), (const gpu::Byte*) cauterizedClusterTransforms.data()); } else { - _cauterizedClusterBuffer->setSubData(0, cauterizedClusterTransforms.size() * sizeof(Model::TransformComponents), + _cauterizedClusterBuffer->setSubData(0, cauterizedClusterTransforms.size() * sizeof(TransformType), (const gpu::Byte*) cauterizedClusterTransforms.data()); } } } -#else -void CauterizedMeshPartPayload::updateClusterBuffer(const std::vector& clusterTransforms, const std::vector& cauterizedClusterTransforms) { - ModelMeshPartPayload::updateClusterBuffer(clusterTransforms); - - if (cauterizedClusterTransforms.size() > 1) { - if (!_cauterizedClusterBuffer) { - _cauterizedClusterBuffer = std::make_shared(cauterizedClusterTransforms.size() * sizeof(glm::mat4), - (const gpu::Byte*) cauterizedClusterTransforms.data()); - } else { - _cauterizedClusterBuffer->setSubData(0, cauterizedClusterTransforms.size() * sizeof(glm::mat4), - (const gpu::Byte*) cauterizedClusterTransforms.data()); - } - } -} -#endif void CauterizedMeshPartPayload::updateTransformForCauterizedMesh(const Transform& renderTransform) { _cauterizedTransform = renderTransform; diff --git a/libraries/render-utils/src/CauterizedMeshPartPayload.h b/libraries/render-utils/src/CauterizedMeshPartPayload.h index 1ee77c300f..5dceb3debd 100644 --- a/libraries/render-utils/src/CauterizedMeshPartPayload.h +++ b/libraries/render-utils/src/CauterizedMeshPartPayload.h @@ -14,12 +14,17 @@ class CauterizedMeshPartPayload : public ModelMeshPartPayload { public: CauterizedMeshPartPayload(ModelPointer model, int meshIndex, int partIndex, int shapeIndex, const Transform& transform, const Transform& offsetTransform); -#ifdef SKIN_COMP - void updateClusterBuffer(const std::vector& clusterTransforms, const std::vector& cauterizedClusterTransforms); + +#if defined(SKIN_COMP) + using TransformType = Model::TransformComponents; +#elif defined(SKIN_DQ) + using TransformType = Model::TransformDualQuaternion; #else - void updateClusterBuffer(const std::vector& clusterTransforms, const std::vector& cauterizedClusterTransforms); + using TransformType = glm::mat4; #endif + void updateClusterBuffer(const std::vector& clusterTransforms, const std::vector& cauterizedClusterTransforms); + void updateTransformForCauterizedMesh(const Transform& renderTransform); void bindTransform(gpu::Batch& batch, const render::ShapePipeline::LocationsPointer locations, RenderArgs::RenderMode renderMode) const override; diff --git a/libraries/render-utils/src/CauterizedModel.cpp b/libraries/render-utils/src/CauterizedModel.cpp index 606e1f0351..cf6aebc952 100644 --- a/libraries/render-utils/src/CauterizedModel.cpp +++ b/libraries/render-utils/src/CauterizedModel.cpp @@ -117,11 +117,16 @@ void CauterizedModel::updateClusterMatrices() { const FBXCluster& cluster = mesh.clusters.at(j); auto jointMatrix = _rig.getJointTransform(cluster.jointIndex); -#ifdef SKIN_COMP - // AJT: TODO: optimize +#if defined(SKIN_COMP) glm::mat4 m; glm_mat4u_mul(jointMatrix, cluster.inverseBindMatrix, m); + AnimPose p(m); state.clusterTransforms[j] = Model::TransformComponents(m); +#elif defined(SKIN_DQ) + glm::mat4 m; + glm_mat4u_mul(jointMatrix, cluster.inverseBindMatrix, m); + AnimPose p(m); + state.clusterTransforms[j] = Model::TransformDualQuaternion(m); #else glm_mat4u_mul(jointMatrix, cluster.inverseBindMatrix, state.clusterTransforms[j]); #endif @@ -149,11 +154,16 @@ void CauterizedModel::updateClusterMatrices() { jointMatrix = cauterizeMatrix; } -#ifdef SKIN_COMP - // AJT: TODO: optimize +#if defined(SKIN_COMP) glm::mat4 m; glm_mat4u_mul(jointMatrix, cluster.inverseBindMatrix, m); + AnimPose p(m); state.clusterTransforms[j] = Model::TransformComponents(m); +#elif defined(SKIN_DQ) + glm::mat4 m; + glm_mat4u_mul(jointMatrix, cluster.inverseBindMatrix, m); + AnimPose p(m); + state.clusterTransforms[j] = Model::TransformDualQuaternion(m); #else glm_mat4u_mul(jointMatrix, cluster.inverseBindMatrix, state.clusterTransforms[j]); #endif @@ -224,7 +234,7 @@ void CauterizedModel::updateRenderItems() { Transform renderTransform = modelTransform; if (clusterTransforms.size() == 1) { -#ifdef SKIN_COMP +#if defined(SKIN_COMP) || defined(SKIN_DQ) renderTransform = modelTransform.worldTransform(Transform(clusterTransforms[0].getMatrix())); #else renderTransform = modelTransform.worldTransform(Transform(clusterTransforms[0])); @@ -234,7 +244,7 @@ void CauterizedModel::updateRenderItems() { renderTransform = modelTransform; if (clusterTransformsCauterized.size() == 1) { -#ifdef SKIN_COMP +#if defined(SKIN_COMP) || defined(SKIN_DQ) renderTransform = modelTransform.worldTransform(Transform(clusterTransformsCauterized[0].getMatrix())); #else renderTransform = modelTransform.worldTransform(Transform(clusterTransformsCauterized[0])); diff --git a/libraries/render-utils/src/MeshPartPayload.cpp b/libraries/render-utils/src/MeshPartPayload.cpp index 3056dbc728..a303c0b0d8 100644 --- a/libraries/render-utils/src/MeshPartPayload.cpp +++ b/libraries/render-utils/src/MeshPartPayload.cpp @@ -331,7 +331,7 @@ ModelMeshPartPayload::ModelMeshPartPayload(ModelPointer model, int meshIndex, in updateTransform(transform, offsetTransform); Transform renderTransform = transform; if (state.clusterTransforms.size() == 1) { -#ifdef SKIN_COMP +#if defined(SKIN_COMP) || defined(SKIN_DQ) renderTransform = transform.worldTransform(Transform(state.clusterTransforms[0].getMatrix())); #else renderTransform = transform.worldTransform(Transform(state.clusterTransforms[0])); @@ -366,26 +366,11 @@ void ModelMeshPartPayload::notifyLocationChanged() { } -#ifdef SKIN_COMP -void ModelMeshPartPayload::updateClusterBuffer(const std::vector& clusterTransforms) { +void ModelMeshPartPayload::updateClusterBuffer(const std::vector& clusterTransforms) { // Once computed the cluster matrices, update the buffer(s) if (clusterTransforms.size() > 1) { if (!_clusterBuffer) { - _clusterBuffer = std::make_shared(clusterTransforms.size() * sizeof(Model::TransformComponents), - (const gpu::Byte*) clusterTransforms.data()); - } - else { - _clusterBuffer->setSubData(0, clusterTransforms.size() * sizeof(Model::TransformComponents), - (const gpu::Byte*) clusterTransforms.data()); - } - } -} -#else -void ModelMeshPartPayload::updateClusterBuffer(const std::vector& clusterTransforms) { - // Once computed the cluster matrices, update the buffer(s) - if (clusterTransforms.size() > 1) { - if (!_clusterBuffer) { - _clusterBuffer = std::make_shared(clusterTransforms.size() * sizeof(glm::mat4), + _clusterBuffer = std::make_shared(clusterTransforms.size() * sizeof(TransformType), (const gpu::Byte*) clusterTransforms.data()); } else { @@ -394,8 +379,6 @@ void ModelMeshPartPayload::updateClusterBuffer(const std::vector& clu } } } -#endif - void ModelMeshPartPayload::updateTransformForSkinnedMesh(const Transform& renderTransform, const Transform& boundTransform) { _transform = renderTransform; @@ -561,15 +544,11 @@ void ModelMeshPartPayload::render(RenderArgs* args) { args->_details._trianglesRendered += _drawPart._numIndices / INDICES_PER_TRIANGLE; } -#ifdef SKIN_COMP -void ModelMeshPartPayload::computeAdjustedLocalBound(const std::vector& clusterTransforms) { -#else -void ModelMeshPartPayload::computeAdjustedLocalBound(const std::vector& clusterTransforms) { -#endif + +void ModelMeshPartPayload::computeAdjustedLocalBound(const std::vector& clusterTransforms) { _adjustedLocalBound = _localBound; if (clusterTransforms.size() > 0) { -#ifdef SKIN_COMP - // AJT: TODO: optimize +#if defined(SKIN_COMP) || defined(SKIN_DQ) _adjustedLocalBound.transform(clusterTransforms[0].getMatrix()); #else _adjustedLocalBound.transform(clusterTransforms[0]); @@ -577,8 +556,7 @@ void ModelMeshPartPayload::computeAdjustedLocalBound(const std::vector& clusterTransforms); + +#if defined(SKIN_COMP) + using TransformType = Model::TransformComponents; +#elif defined(SKIN_DQ) + using TransformType = Model::TransformDualQuaternion; #else - void updateClusterBuffer(const std::vector& clusterTransforms); + using TransformType = glm::mat4; #endif + + void updateClusterBuffer(const std::vector& clusterTransforms); void updateTransformForSkinnedMesh(const Transform& renderTransform, const Transform& boundTransform); // Render Item interface @@ -108,11 +113,7 @@ public: void bindMesh(gpu::Batch& batch) override; void bindTransform(gpu::Batch& batch, const render::ShapePipeline::LocationsPointer locations, RenderArgs::RenderMode renderMode) const override; -#ifdef SKIN_COMP - void computeAdjustedLocalBound(const std::vector& clusterTransforms); -#else - void computeAdjustedLocalBound(const std::vector& clusterMatrices); -#endif + void computeAdjustedLocalBound(const std::vector& clusterTransforms); gpu::BufferPointer _clusterBuffer; diff --git a/libraries/render-utils/src/Model.cpp b/libraries/render-utils/src/Model.cpp index 0f014032a3..6eb9662c46 100644 --- a/libraries/render-utils/src/Model.cpp +++ b/libraries/render-utils/src/Model.cpp @@ -276,7 +276,7 @@ void Model::updateRenderItems() { Transform renderTransform = modelTransform; if (clusterTransforms.size() == 1) { -#ifdef SKIN_COMP +#if defined(SKIN_COMP) || defined(SKIN_DQ) renderTransform = modelTransform.worldTransform(Transform(clusterTransforms[0].getMatrix())); #else renderTransform = modelTransform.worldTransform(Transform(clusterTransforms[0])); @@ -1183,11 +1183,14 @@ void Model::updateClusterMatrices() { for (int j = 0; j < mesh.clusters.size(); j++) { const FBXCluster& cluster = mesh.clusters.at(j); auto jointMatrix = _rig.getJointTransform(cluster.jointIndex); -#ifdef SKIN_COMP - // AJT: TODO: optimize +#if defined(SKIN_COMP) glm::mat4 mat; glm_mat4u_mul(jointMatrix, cluster.inverseBindMatrix, mat); state.clusterTransforms[j] = TransformComponents(mat); +#elif defined(SKIN_DQ) + glm::mat4 mat; + glm_mat4u_mul(jointMatrix, cluster.inverseBindMatrix, mat); + state.clusterTransforms[j] = TransformDualQuaternion(mat); #else glm_mat4u_mul(jointMatrix, cluster.inverseBindMatrix, state.clusterTransforms[j]); #endif diff --git a/libraries/render-utils/src/Model.h b/libraries/render-utils/src/Model.h index e8f11a421b..f869634dd2 100644 --- a/libraries/render-utils/src/Model.h +++ b/libraries/render-utils/src/Model.h @@ -30,6 +30,7 @@ #include #include #include +#include #include "GeometryCache.h" #include "TextureCache.h" @@ -43,7 +44,7 @@ } \ } while(false) -#define SKIN_COMP +#define SKIN_DQ class AbstractViewStateInterface; class QScriptEngine; @@ -255,6 +256,7 @@ public: int getRenderInfoDrawCalls() const { return _renderInfoDrawCalls; } bool getRenderInfoHasTransparent() const { return _renderInfoHasTransparent; } +#if defined(SKIN_COMP) class TransformComponents { public: TransformComponents() {} @@ -290,15 +292,43 @@ public: glm::vec4 _trans { 0.0f, 0.0f, 0.0f, 0.0f }; glm::vec4 _padding { 0.0f, 0.0f, 0.0f, 0.0f }; }; +#elif defined(SKIN_DQ) + class TransformDualQuaternion { + public: + TransformDualQuaternion() {} + TransformDualQuaternion(const glm::mat4& m) { + AnimPose p(m); + _scale.x = p.scale().x; + _scale.y = p.scale().y; + _scale.z = p.scale().z; + _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; + _dq = DualQuaternion(rot, trans); + } + glm::vec3 getScale() const { return glm::vec3(_scale); } + glm::quat getRot() const { return _dq.getRotation(); } + glm::vec3 getTrans() const { return _dq.getTranslation(); } + glm::mat4 getMatrix() const { return createMatFromScaleQuatAndPos(getScale(), getRot(), getTrans()); }; + protected: + glm::vec4 _scale { 1.0f, 1.0f, 1.0f, 0.0f }; + DualQuaternion _dq; + glm::vec4 _padding; + }; +#endif class MeshState { public: -#ifdef SKIN_COMP +#if defined(SKIN_COMP) std::vector clusterTransforms; +#elif defined(SKIN_DQ) + std::vector clusterTransforms; #else std::vector clusterTransforms; #endif - }; const MeshState& getMeshState(int index) { return _meshStates.at(index); } diff --git a/libraries/render-utils/src/Skinning.slh b/libraries/render-utils/src/Skinning.slh index 63bb6ba46e..17c3a26079 100644 --- a/libraries/render-utils/src/Skinning.slh +++ b/libraries/render-utils/src/Skinning.slh @@ -38,6 +38,181 @@ void dqMul(vec4 lhsReal, vec4 lhsImag, vec4 rhsReal, vec4 rhsImag, out vec4 real imagOut = quatMul(lhsReal, rhsImag) + quatMul(lhsImag, rhsReal); } +void skinPosition(ivec4 skinClusterIndex, vec4 skinClusterWeight, vec4 inPosition, out vec4 skinnedPosition) { + + vec3 sAccum = vec3(0.0, 0.0, 0.0); + vec4 rAccum = vec4(0.0, 0.0, 0.0, 0.0); + vec4 iAccum = vec4(0.0, 0.0, 0.0, 0.0); + + for (int i = 0; i < INDICES_PER_VERTEX; i++) { + mat4 clusterMatrix = clusterMatrices[(skinClusterIndex[i])]; + float clusterWeight = skinClusterWeight[i]; + + vec3 scale = vec3(clusterMatrix[0][0], clusterMatrix[0][1], clusterMatrix[0][2]); + vec4 real = vec4(clusterMatrix[1][0], clusterMatrix[1][1], clusterMatrix[1][2], clusterMatrix[1][3]); + vec4 imag = vec4(clusterMatrix[2][0], clusterMatrix[2][1], clusterMatrix[2][2], clusterMatrix[2][3]); + + sAccum += scale * clusterWeight; + rAccum += real * clusterWeight; + iAccum += imag * clusterWeight; + } + + float norm = length(rAccum); + rAccum /= norm; + iAccum /= norm; + + float xe = iAccum.x; + float ye = iAccum.y; + float ze = iAccum.z; + float we = iAccum.w; + + float x0 = rAccum.x; + float y0 = rAccum.y; + float z0 = rAccum.z; + float w0 = rAccum.w; + + float t0 = 2.0 * (-we * x0 + xe * w0 - ye * z0 + ze * y0); + float t1 = 2.0 * (-we * y0 + xe * z0 + ye * w0 - ze * x0); + float t2 = 2.0 * (-we * z0 - xe * y0 + ye * x0 + ze * w0); + + vec4 col0 = vec4(1.0 - 2.0 * y0 * y0 - 2.0 * z0 * z0, + 2.0 * x0 * y0 + 2.0 * w0 * z0, + 2.0 * x0 * z0 - 2.0 * w0 * y0, + 0.0); + vec4 col1 = vec4(2.0 * x0 * y0 - 2.0 * w0 * z0, + 1 - 2.0 * x0 * x0 - 2.0 * z0 * z0, + 2.0 * y0 * z0 + 2.0 * w0 * x0, + 0.0); + vec4 col2 = vec4(2.0 * x0 * z0 + 2.0 * w0 * y0, + 2.0 * y0 * z0 - 2.0 * w0 * x0, + 1 - 2.0 * x0 * x0 - 2.0 * y0 * y0, + 0.0); + vec4 col3 = vec4(t0, t1, t2, 1.0); + + mat4 m = mat4(col0, col1, col2, col3); + + skinnedPosition = m * (vec4(sAccum, 1) * inPosition); +} + +void skinPositionNormal(ivec4 skinClusterIndex, vec4 skinClusterWeight, vec4 inPosition, vec3 inNormal, + out vec4 skinnedPosition, out vec3 skinnedNormal) { + + vec3 sAccum = vec3(0.0, 0.0, 0.0); + vec4 rAccum = vec4(0.0, 0.0, 0.0, 0.0); + vec4 iAccum = vec4(0.0, 0.0, 0.0, 0.0); + + for (int i = 0; i < INDICES_PER_VERTEX; i++) { + mat4 clusterMatrix = clusterMatrices[(skinClusterIndex[i])]; + float clusterWeight = skinClusterWeight[i]; + + vec3 scale = vec3(clusterMatrix[0][0], clusterMatrix[0][1], clusterMatrix[0][2]); + vec4 real = vec4(clusterMatrix[1][0], clusterMatrix[1][1], clusterMatrix[1][2], clusterMatrix[1][3]); + vec4 imag = vec4(clusterMatrix[2][0], clusterMatrix[2][1], clusterMatrix[2][2], clusterMatrix[2][3]); + + sAccum += scale * clusterWeight; + rAccum += real * clusterWeight; + iAccum += imag * clusterWeight; + } + + float norm = length(rAccum); + rAccum /= norm; + iAccum /= norm; + + float xe = iAccum.x; + float ye = iAccum.y; + float ze = iAccum.z; + float we = iAccum.w; + + float x0 = rAccum.x; + float y0 = rAccum.y; + float z0 = rAccum.z; + float w0 = rAccum.w; + + float t0 = 2.0 * (-we * x0 + xe * w0 - ye * z0 + ze * y0); + float t1 = 2.0 * (-we * y0 + xe * z0 + ye * w0 - ze * x0); + float t2 = 2.0 * (-we * z0 - xe * y0 + ye * x0 + ze * w0); + + vec4 col0 = vec4(1.0 - 2.0 * y0 * y0 - 2.0 * z0 * z0, + 2.0 * x0 * y0 + 2.0 * w0 * z0, + 2.0 * x0 * z0 - 2.0 * w0 * y0, + 0.0); + vec4 col1 = vec4(2.0 * x0 * y0 - 2.0 * w0 * z0, + 1 - 2.0 * x0 * x0 - 2.0 * z0 * z0, + 2.0 * y0 * z0 + 2.0 * w0 * x0, + 0.0); + vec4 col2 = vec4(2.0 * x0 * z0 + 2.0 * w0 * y0, + 2.0 * y0 * z0 - 2.0 * w0 * x0, + 1 - 2.0 * x0 * x0 - 2.0 * y0 * y0, + 0.0); + vec4 col3 = vec4(t0, t1, t2, 1.0); + + mat4 m = mat4(col0, col1, col2, col3); + + skinnedPosition = m * (vec4(sAccum, 1) * inPosition); + skinnedNormal = vec3(m * vec4(inNormal, 0)); +} + +void skinPositionNormalTangent(ivec4 skinClusterIndex, vec4 skinClusterWeight, vec4 inPosition, vec3 inNormal, vec3 inTangent, + out vec4 skinnedPosition, out vec3 skinnedNormal, out vec3 skinnedTangent) { + + vec3 sAccum = vec3(0.0, 0.0, 0.0); + vec4 rAccum = vec4(0.0, 0.0, 0.0, 0.0); + vec4 iAccum = vec4(0.0, 0.0, 0.0, 0.0); + + for (int i = 0; i < INDICES_PER_VERTEX; i++) { + mat4 clusterMatrix = clusterMatrices[(skinClusterIndex[i])]; + float clusterWeight = skinClusterWeight[i]; + + vec3 scale = vec3(clusterMatrix[0][0], clusterMatrix[0][1], clusterMatrix[0][2]); + vec4 real = vec4(clusterMatrix[1][0], clusterMatrix[1][1], clusterMatrix[1][2], clusterMatrix[1][3]); + vec4 imag = vec4(clusterMatrix[2][0], clusterMatrix[2][1], clusterMatrix[2][2], clusterMatrix[2][3]); + + sAccum += scale * clusterWeight; + rAccum += real * clusterWeight; + iAccum += imag * clusterWeight; + } + + float norm = length(rAccum); + rAccum /= norm; + iAccum /= norm; + + float xe = iAccum.x; + float ye = iAccum.y; + float ze = iAccum.z; + float we = iAccum.w; + + float x0 = rAccum.x; + float y0 = rAccum.y; + float z0 = rAccum.z; + float w0 = rAccum.w; + + float t0 = 2.0 * (-we * x0 + xe * w0 - ye * z0 + ze * y0); + float t1 = 2.0 * (-we * y0 + xe * z0 + ye * w0 - ze * x0); + float t2 = 2.0 * (-we * z0 - xe * y0 + ye * x0 + ze * w0); + + vec4 col0 = vec4(1.0 - 2.0 * y0 * y0 - 2.0 * z0 * z0, + 2.0 * x0 * y0 + 2.0 * w0 * z0, + 2.0 * x0 * z0 - 2.0 * w0 * y0, + 0.0); + vec4 col1 = vec4(2.0 * x0 * y0 - 2.0 * w0 * z0, + 1 - 2.0 * x0 * x0 - 2.0 * z0 * z0, + 2.0 * y0 * z0 + 2.0 * w0 * x0, + 0.0); + vec4 col2 = vec4(2.0 * x0 * z0 + 2.0 * w0 * y0, + 2.0 * y0 * z0 - 2.0 * w0 * x0, + 1 - 2.0 * x0 * x0 - 2.0 * y0 * y0, + 0.0); + vec4 col3 = vec4(t0, t1, t2, 1.0); + + mat4 m = mat4(col0, col1, col2, col3); + + skinnedPosition = m * (vec4(sAccum, 1) * inPosition); + skinnedNormal = vec3(m * vec4(inNormal, 0)); + skinnedTangent = vec3(m * vec4(inTangent, 0)); +} + +// SKIN_COMP +/* void skinPosition(ivec4 skinClusterIndex, vec4 skinClusterWeight, vec4 inPosition, out vec4 skinnedPosition) { vec3 sAccum = vec3(0.0, 0.0, 0.0); vec4 rAccum = vec4(0.0, 0.0, 0.0, 0.0); @@ -125,6 +300,7 @@ void skinPositionNormalTangent(ivec4 skinClusterIndex, vec4 skinClusterWeight, v skinnedNormal = rotateByQuat(rAccum, inNormal); skinnedTangent = rotateByQuat(rAccum, inTangent); } +*/ /* // ORIGINAL diff --git a/libraries/render-utils/src/SoftAttachmentModel.cpp b/libraries/render-utils/src/SoftAttachmentModel.cpp index 8fa609b241..7b76f89184 100644 --- a/libraries/render-utils/src/SoftAttachmentModel.cpp +++ b/libraries/render-utils/src/SoftAttachmentModel.cpp @@ -58,11 +58,14 @@ void SoftAttachmentModel::updateClusterMatrices() { } else { jointMatrix = _rig.getJointTransform(cluster.jointIndex); } -#ifdef SKIN_COMP - // AJT: TODO: Optimize +#if defined(SKIN_COMP) glm::mat4 m; glm_mat4u_mul(jointMatrix, cluster.inverseBindMatrix, m); state.clusterTransforms[j] = Model::TransformComponents(m); +#elif defined(SKIN_DQ) + glm::mat4 m; + glm_mat4u_mul(jointMatrix, cluster.inverseBindMatrix, m); + state.clusterTransforms[j] = Model::TransformDualQuaternion(m); #else glm_mat4u_mul(jointMatrix, cluster.inverseBindMatrix, state.clusterTransforms[j]); #endif diff --git a/libraries/shared/src/DualQuaternion.cpp b/libraries/shared/src/DualQuaternion.cpp index 3b5aaf6d6d..e9b1a65635 100644 --- a/libraries/shared/src/DualQuaternion.cpp +++ b/libraries/shared/src/DualQuaternion.cpp @@ -12,6 +12,9 @@ #include "GLMHelpers.h" // delegating constructor +DualQuaternion::DualQuaternion() : _real(1.0f, 0.0f, 0.0f, 0.0), _imag(0.0f, 0.0f, 0.0f, 0.0f) { +} + DualQuaternion::DualQuaternion(const glm::mat4& m) : DualQuaternion(glmExtractRotation(m), extractTranslation(m)) { } diff --git a/libraries/shared/src/DualQuaternion.h b/libraries/shared/src/DualQuaternion.h index ed8cdf54ff..061317bde9 100644 --- a/libraries/shared/src/DualQuaternion.h +++ b/libraries/shared/src/DualQuaternion.h @@ -19,6 +19,7 @@ class DualQuaternion { public: + DualQuaternion(); explicit DualQuaternion(const glm::mat4& m); DualQuaternion(const glm::quat& real, const glm::quat& imag); DualQuaternion(const glm::quat& rotation, const glm::vec3& translation); From 5bb0b06061a97633f052e5368b2c218b2f0c342f Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Thu, 21 Dec 2017 18:15:30 -0800 Subject: [PATCH 08/32] WIP: Dual Quaternion compensation for spinning the right way. --- .../render-utils/src/CauterizedModel.cpp | 51 ++++++++++++++++++- libraries/render-utils/src/Model.h | 2 +- libraries/render-utils/src/Skinning.slh | 42 ++++++++++++--- libraries/shared/src/DualQuaternion.cpp | 17 +++++-- libraries/shared/src/DualQuaternion.h | 3 ++ 5 files changed, 104 insertions(+), 11 deletions(-) diff --git a/libraries/render-utils/src/CauterizedModel.cpp b/libraries/render-utils/src/CauterizedModel.cpp index cf6aebc952..5be93304ab 100644 --- a/libraries/render-utils/src/CauterizedModel.cpp +++ b/libraries/render-utils/src/CauterizedModel.cpp @@ -113,7 +113,12 @@ void CauterizedModel::updateClusterMatrices() { for (int i = 0; i < (int)_meshStates.size(); i++) { Model::MeshState& state = _meshStates[i]; const FBXMesh& mesh = geometry.meshes.at(i); +#if defined(SKIN_DQ) + // HACK: FOR DQ go thru reverse order! + for (int j = mesh.clusters.size() - 1; j >= 0; j--) { +#else for (int j = 0; j < mesh.clusters.size(); j++) { +#endif const FBXCluster& cluster = mesh.clusters.at(j); auto jointMatrix = _rig.getJointTransform(cluster.jointIndex); @@ -127,6 +132,25 @@ void CauterizedModel::updateClusterMatrices() { glm_mat4u_mul(jointMatrix, cluster.inverseBindMatrix, m); AnimPose p(m); state.clusterTransforms[j] = Model::TransformDualQuaternion(m); + + // AJT: HACK n^2!!!! fix me, find parent clusterTransform. + int parentIndex = _rig.getJointParentIndex(cluster.jointIndex); + int parentClusterIndex = -1; + // scan for parent! + for (int ii = mesh.clusters.size() - 1; ii > j; ii--) { + if (mesh.clusters[ii].jointIndex == parentIndex) { + parentClusterIndex = ii; + break; + } + } + + // ensure that we have the same polarity as our parent! + if (parentClusterIndex >= 0) { + //if (state.clusterTransforms[parentClusterIndex]._dq.dot(state.clusterTransforms[j]._dq) < 0.0f) { + if (glm::dot(state.clusterTransforms[parentClusterIndex]._dq.real(), state.clusterTransforms[j]._dq.real()) < 0.0f) { + state.clusterTransforms[j]._dq = -state.clusterTransforms[j]._dq; + } + } #else glm_mat4u_mul(jointMatrix, cluster.inverseBindMatrix, state.clusterTransforms[j]); #endif @@ -146,7 +170,13 @@ void CauterizedModel::updateClusterMatrices() { for (int i = 0; i < _cauterizeMeshStates.size(); i++) { Model::MeshState& state = _cauterizeMeshStates[i]; const FBXMesh& mesh = geometry.meshes.at(i); - for (int j = 0; j < mesh.clusters.size(); j++) { + +#if defined(SKIN_DQ) + // HACK: FOR DQ go thru reverse order! + for (int j = mesh.clusters.size() - 1; j >= 0; j--) { +#else + for (int j = 0; j < mesh.clusters.size(); j++) { +#endif const FBXCluster& cluster = mesh.clusters.at(j); auto jointMatrix = _rig.getJointTransform(cluster.jointIndex); @@ -164,6 +194,25 @@ void CauterizedModel::updateClusterMatrices() { glm_mat4u_mul(jointMatrix, cluster.inverseBindMatrix, m); AnimPose p(m); state.clusterTransforms[j] = Model::TransformDualQuaternion(m); + + // AJT: HACK n^2!!!! fix me, find parent clusterTransform. + int parentIndex = _rig.getJointParentIndex(cluster.jointIndex); + int parentClusterIndex = -1; + // scan for parent! + for (int ii = mesh.clusters.size() - 1; ii > j; ii--) { + if (mesh.clusters[ii].jointIndex == parentIndex) { + parentClusterIndex = ii; + break; + } + } + + // ensure that we have the same polarity as our parent! + if (parentClusterIndex >= 0) { + //if (state.clusterTransforms[parentClusterIndex]._dq.dot(state.clusterTransforms[j]._dq) < 0.0f) { + if (glm::dot(state.clusterTransforms[parentClusterIndex]._dq.real(), state.clusterTransforms[j]._dq.real()) < 0.0f) { + state.clusterTransforms[j]._dq = -state.clusterTransforms[j]._dq; + } + } #else glm_mat4u_mul(jointMatrix, cluster.inverseBindMatrix, state.clusterTransforms[j]); #endif diff --git a/libraries/render-utils/src/Model.h b/libraries/render-utils/src/Model.h index f869634dd2..cb211a1011 100644 --- a/libraries/render-utils/src/Model.h +++ b/libraries/render-utils/src/Model.h @@ -313,7 +313,7 @@ public: glm::quat getRot() const { return _dq.getRotation(); } glm::vec3 getTrans() const { return _dq.getTranslation(); } glm::mat4 getMatrix() const { return createMatFromScaleQuatAndPos(getScale(), getRot(), getTrans()); }; - protected: + public: // AJT: TODO FIX ME. glm::vec4 _scale { 1.0f, 1.0f, 1.0f, 0.0f }; DualQuaternion _dq; glm::vec4 _padding; diff --git a/libraries/render-utils/src/Skinning.slh b/libraries/render-utils/src/Skinning.slh index 17c3a26079..bdba724d43 100644 --- a/libraries/render-utils/src/Skinning.slh +++ b/libraries/render-utils/src/Skinning.slh @@ -43,6 +43,7 @@ void skinPosition(ivec4 skinClusterIndex, vec4 skinClusterWeight, vec4 inPositio vec3 sAccum = vec3(0.0, 0.0, 0.0); vec4 rAccum = vec4(0.0, 0.0, 0.0, 0.0); vec4 iAccum = vec4(0.0, 0.0, 0.0, 0.0); + vec4 prevR = vec4(0.0, 0.0, 0.0, 1.0); for (int i = 0; i < INDICES_PER_VERTEX; i++) { mat4 clusterMatrix = clusterMatrices[(skinClusterIndex[i])]; @@ -52,9 +53,18 @@ void skinPosition(ivec4 skinClusterIndex, vec4 skinClusterWeight, vec4 inPositio vec4 real = vec4(clusterMatrix[1][0], clusterMatrix[1][1], clusterMatrix[1][2], clusterMatrix[1][3]); vec4 imag = vec4(clusterMatrix[2][0], clusterMatrix[2][1], clusterMatrix[2][2], clusterMatrix[2][3]); + float dqClusterWeight = clusterWeight; + if (i == 0) { + prevR = real; + } else { + if (dot(prevR, real) < 0) { + dqClusterWeight = -clusterWeight; + } + } + sAccum += scale * clusterWeight; - rAccum += real * clusterWeight; - iAccum += imag * clusterWeight; + rAccum += real * dqClusterWeight; + iAccum += imag * dqClusterWeight; } float norm = length(rAccum); @@ -100,6 +110,7 @@ void skinPositionNormal(ivec4 skinClusterIndex, vec4 skinClusterWeight, vec4 inP vec3 sAccum = vec3(0.0, 0.0, 0.0); vec4 rAccum = vec4(0.0, 0.0, 0.0, 0.0); vec4 iAccum = vec4(0.0, 0.0, 0.0, 0.0); + vec4 prevR = vec4(0.0, 0.0, 0.0, 1.0); for (int i = 0; i < INDICES_PER_VERTEX; i++) { mat4 clusterMatrix = clusterMatrices[(skinClusterIndex[i])]; @@ -109,9 +120,18 @@ void skinPositionNormal(ivec4 skinClusterIndex, vec4 skinClusterWeight, vec4 inP vec4 real = vec4(clusterMatrix[1][0], clusterMatrix[1][1], clusterMatrix[1][2], clusterMatrix[1][3]); vec4 imag = vec4(clusterMatrix[2][0], clusterMatrix[2][1], clusterMatrix[2][2], clusterMatrix[2][3]); + float dqClusterWeight = clusterWeight; + if (i == 0) { + prevR = real; + } else { + if (dot(prevR, real) < 0) { + dqClusterWeight = -clusterWeight; + } + } + sAccum += scale * clusterWeight; - rAccum += real * clusterWeight; - iAccum += imag * clusterWeight; + rAccum += real * dqClusterWeight; + iAccum += imag * dqClusterWeight; } float norm = length(rAccum); @@ -158,6 +178,7 @@ void skinPositionNormalTangent(ivec4 skinClusterIndex, vec4 skinClusterWeight, v vec3 sAccum = vec3(0.0, 0.0, 0.0); vec4 rAccum = vec4(0.0, 0.0, 0.0, 0.0); vec4 iAccum = vec4(0.0, 0.0, 0.0, 0.0); + vec4 prevR = vec4(0.0, 0.0, 0.0, 1.0); for (int i = 0; i < INDICES_PER_VERTEX; i++) { mat4 clusterMatrix = clusterMatrices[(skinClusterIndex[i])]; @@ -167,9 +188,18 @@ void skinPositionNormalTangent(ivec4 skinClusterIndex, vec4 skinClusterWeight, v vec4 real = vec4(clusterMatrix[1][0], clusterMatrix[1][1], clusterMatrix[1][2], clusterMatrix[1][3]); vec4 imag = vec4(clusterMatrix[2][0], clusterMatrix[2][1], clusterMatrix[2][2], clusterMatrix[2][3]); + float dqClusterWeight = clusterWeight; + if (i == 0) { + prevR = real; + } else { + if (dot(prevR, real) < 0) { + dqClusterWeight = -clusterWeight; + } + } + sAccum += scale * clusterWeight; - rAccum += real * clusterWeight; - iAccum += imag * clusterWeight; + rAccum += real * dqClusterWeight; + iAccum += imag * dqClusterWeight; } float norm = length(rAccum); diff --git a/libraries/shared/src/DualQuaternion.cpp b/libraries/shared/src/DualQuaternion.cpp index e9b1a65635..44d7356463 100644 --- a/libraries/shared/src/DualQuaternion.cpp +++ b/libraries/shared/src/DualQuaternion.cpp @@ -59,26 +59,37 @@ glm::vec3 DualQuaternion::getTranslation() const { return glm::vec3(result.x, result.y, result.z); } - glm::vec3 DualQuaternion::xformVector(const glm::vec3& rhs) const { return _real * rhs; } +// AJT: UNTESTED DualQuaternion DualQuaternion::inverse() const { glm::quat invReal = glm::inverse(_real); return DualQuaternion(invReal, - invReal * _imag * invReal); } +// AJT: UNTESTED DualQuaternion DualQuaternion::conjugate() const { return DualQuaternion(glm::conjugate(_real), glm::conjugate(_imag)); } +// AJT: UNTESTED float DualQuaternion::length() const { - DualQuaternion result = *this * conjugate(); - return sqrtf(result._real.w); + float dot = this->dot(*this); + return sqrtf(dot); } DualQuaternion DualQuaternion::normalize() const { float invLen = 1.0f / length(); return *this * invLen; } + +float DualQuaternion::dot(const DualQuaternion& rhs) const { + DualQuaternion result = *this * conjugate(); + return result._real.w; +} + +DualQuaternion DualQuaternion::operator-() const { + return DualQuaternion(-_real, -_imag); +} diff --git a/libraries/shared/src/DualQuaternion.h b/libraries/shared/src/DualQuaternion.h index 061317bde9..508570a930 100644 --- a/libraries/shared/src/DualQuaternion.h +++ b/libraries/shared/src/DualQuaternion.h @@ -44,6 +44,8 @@ public: DualQuaternion conjugate() const; float length() const; DualQuaternion normalize() const; + float dot(const DualQuaternion& rhs) const; + DualQuaternion operator-() const; protected: friend QDebug operator<<(QDebug debug, const DualQuaternion& pose); @@ -51,6 +53,7 @@ protected: glm::quat _imag; }; + inline QDebug operator<<(QDebug debug, const DualQuaternion& dq) { debug << "AnimPose, real = (" << dq._real.x << dq._real.y << dq._real.z << dq._real.w << "), imag = (" << dq._imag.x << dq._imag.y << dq._imag.z << dq._imag.w << ")"; return debug; From b69edceb4f9fbfdfba8f1ef6f3e34e3366610056 Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Fri, 22 Dec 2017 10:54:48 -0800 Subject: [PATCH 09/32] Fixed rotation polarity and weights are now 16 bit. --- libraries/fbx/src/FBX.h | 2 +- libraries/fbx/src/FBXReader.cpp | 4 +- libraries/fbx/src/FBXReader_Mesh.cpp | 4 +- .../render-utils/src/CauterizedModel.cpp | 50 +------------------ libraries/render-utils/src/Skinning.slh | 18 +++---- 5 files changed, 12 insertions(+), 66 deletions(-) diff --git a/libraries/fbx/src/FBX.h b/libraries/fbx/src/FBX.h index 7d3328a2dd..2ec847104c 100644 --- a/libraries/fbx/src/FBX.h +++ b/libraries/fbx/src/FBX.h @@ -224,7 +224,7 @@ public: QVector texCoords; QVector texCoords1; QVector clusterIndices; - QVector clusterWeights; + QVector clusterWeights; QVector originalIndices; QVector clusters; diff --git a/libraries/fbx/src/FBXReader.cpp b/libraries/fbx/src/FBXReader.cpp index e4fea00a34..b1db5992e7 100644 --- a/libraries/fbx/src/FBXReader.cpp +++ b/libraries/fbx/src/FBXReader.cpp @@ -1727,9 +1727,9 @@ FBXGeometry* FBXReader::extractFBXGeometry(const QVariantHash& mapping, const QS } if (totalWeight > 0.0f) { const float ALMOST_HALF = 0.499f; - float weightScalingFactor = (float)(UINT8_MAX) / totalWeight; + float weightScalingFactor = (float)(UINT16_MAX) / totalWeight; for (int k = j; k < j + WEIGHTS_PER_VERTEX; ++k) { - extracted.mesh.clusterWeights[k] = (uint8_t)(weightScalingFactor * weightAccumulators[k] + ALMOST_HALF); + extracted.mesh.clusterWeights[k] = (uint16_t)(weightScalingFactor * weightAccumulators[k] + ALMOST_HALF); } } } diff --git a/libraries/fbx/src/FBXReader_Mesh.cpp b/libraries/fbx/src/FBXReader_Mesh.cpp index c64cbcc90d..f76435c216 100644 --- a/libraries/fbx/src/FBXReader_Mesh.cpp +++ b/libraries/fbx/src/FBXReader_Mesh.cpp @@ -583,7 +583,7 @@ void FBXReader::buildModelMesh(FBXMesh& extractedMesh, const QString& url) { // we need 16 bits instead of just 8 for clusterIndices clusterIndicesSize *= 2; } - int clusterWeightsSize = fbxMesh.clusterWeights.size() * sizeof(uint8_t); + int clusterWeightsSize = fbxMesh.clusterWeights.size() * sizeof(uint16_t); int normalsOffset = 0; int tangentsOffset = normalsOffset + normalsSize; @@ -662,7 +662,7 @@ void FBXReader::buildModelMesh(FBXMesh& extractedMesh, const QString& url) { if (clusterWeightsSize) { mesh->addAttribute(gpu::Stream::SKIN_CLUSTER_WEIGHT, model::BufferView(attribBuffer, clusterWeightsOffset, clusterWeightsSize, - gpu::Element(gpu::VEC4, gpu::NUINT8, gpu::XYZW))); + gpu::Element(gpu::VEC4, gpu::NUINT16, gpu::XYZW))); } diff --git a/libraries/render-utils/src/CauterizedModel.cpp b/libraries/render-utils/src/CauterizedModel.cpp index 5be93304ab..7bac720b86 100644 --- a/libraries/render-utils/src/CauterizedModel.cpp +++ b/libraries/render-utils/src/CauterizedModel.cpp @@ -113,12 +113,7 @@ void CauterizedModel::updateClusterMatrices() { for (int i = 0; i < (int)_meshStates.size(); i++) { Model::MeshState& state = _meshStates[i]; const FBXMesh& mesh = geometry.meshes.at(i); -#if defined(SKIN_DQ) - // HACK: FOR DQ go thru reverse order! - for (int j = mesh.clusters.size() - 1; j >= 0; j--) { -#else for (int j = 0; j < mesh.clusters.size(); j++) { -#endif const FBXCluster& cluster = mesh.clusters.at(j); auto jointMatrix = _rig.getJointTransform(cluster.jointIndex); @@ -132,25 +127,6 @@ void CauterizedModel::updateClusterMatrices() { glm_mat4u_mul(jointMatrix, cluster.inverseBindMatrix, m); AnimPose p(m); state.clusterTransforms[j] = Model::TransformDualQuaternion(m); - - // AJT: HACK n^2!!!! fix me, find parent clusterTransform. - int parentIndex = _rig.getJointParentIndex(cluster.jointIndex); - int parentClusterIndex = -1; - // scan for parent! - for (int ii = mesh.clusters.size() - 1; ii > j; ii--) { - if (mesh.clusters[ii].jointIndex == parentIndex) { - parentClusterIndex = ii; - break; - } - } - - // ensure that we have the same polarity as our parent! - if (parentClusterIndex >= 0) { - //if (state.clusterTransforms[parentClusterIndex]._dq.dot(state.clusterTransforms[j]._dq) < 0.0f) { - if (glm::dot(state.clusterTransforms[parentClusterIndex]._dq.real(), state.clusterTransforms[j]._dq.real()) < 0.0f) { - state.clusterTransforms[j]._dq = -state.clusterTransforms[j]._dq; - } - } #else glm_mat4u_mul(jointMatrix, cluster.inverseBindMatrix, state.clusterTransforms[j]); #endif @@ -171,12 +147,7 @@ void CauterizedModel::updateClusterMatrices() { Model::MeshState& state = _cauterizeMeshStates[i]; const FBXMesh& mesh = geometry.meshes.at(i); -#if defined(SKIN_DQ) - // HACK: FOR DQ go thru reverse order! - for (int j = mesh.clusters.size() - 1; j >= 0; j--) { -#else - for (int j = 0; j < mesh.clusters.size(); j++) { -#endif + for (int j = 0; j < mesh.clusters.size(); j++) { const FBXCluster& cluster = mesh.clusters.at(j); auto jointMatrix = _rig.getJointTransform(cluster.jointIndex); @@ -194,25 +165,6 @@ void CauterizedModel::updateClusterMatrices() { glm_mat4u_mul(jointMatrix, cluster.inverseBindMatrix, m); AnimPose p(m); state.clusterTransforms[j] = Model::TransformDualQuaternion(m); - - // AJT: HACK n^2!!!! fix me, find parent clusterTransform. - int parentIndex = _rig.getJointParentIndex(cluster.jointIndex); - int parentClusterIndex = -1; - // scan for parent! - for (int ii = mesh.clusters.size() - 1; ii > j; ii--) { - if (mesh.clusters[ii].jointIndex == parentIndex) { - parentClusterIndex = ii; - break; - } - } - - // ensure that we have the same polarity as our parent! - if (parentClusterIndex >= 0) { - //if (state.clusterTransforms[parentClusterIndex]._dq.dot(state.clusterTransforms[j]._dq) < 0.0f) { - if (glm::dot(state.clusterTransforms[parentClusterIndex]._dq.real(), state.clusterTransforms[j]._dq.real()) < 0.0f) { - state.clusterTransforms[j]._dq = -state.clusterTransforms[j]._dq; - } - } #else glm_mat4u_mul(jointMatrix, cluster.inverseBindMatrix, state.clusterTransforms[j]); #endif diff --git a/libraries/render-utils/src/Skinning.slh b/libraries/render-utils/src/Skinning.slh index bdba724d43..c11455a575 100644 --- a/libraries/render-utils/src/Skinning.slh +++ b/libraries/render-utils/src/Skinning.slh @@ -56,10 +56,8 @@ void skinPosition(ivec4 skinClusterIndex, vec4 skinClusterWeight, vec4 inPositio float dqClusterWeight = clusterWeight; if (i == 0) { prevR = real; - } else { - if (dot(prevR, real) < 0) { - dqClusterWeight = -clusterWeight; - } + } else if (dot(prevR, real) < 0) { + dqClusterWeight = -clusterWeight; } sAccum += scale * clusterWeight; @@ -123,10 +121,8 @@ void skinPositionNormal(ivec4 skinClusterIndex, vec4 skinClusterWeight, vec4 inP float dqClusterWeight = clusterWeight; if (i == 0) { prevR = real; - } else { - if (dot(prevR, real) < 0) { - dqClusterWeight = -clusterWeight; - } + } else if (dot(prevR, real) < 0) { + dqClusterWeight = -clusterWeight; } sAccum += scale * clusterWeight; @@ -191,10 +187,8 @@ void skinPositionNormalTangent(ivec4 skinClusterIndex, vec4 skinClusterWeight, v float dqClusterWeight = clusterWeight; if (i == 0) { prevR = real; - } else { - if (dot(prevR, real) < 0) { - dqClusterWeight = -clusterWeight; - } + } else if (dot(prevR, real) < 0) { + dqClusterWeight = -clusterWeight; } sAccum += scale * clusterWeight; From e86fd4f9929b51d64db1e767f1fa8406a750e4da Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Fri, 22 Dec 2017 12:23:17 -0800 Subject: [PATCH 10/32] Removed SKIN_COMP define, re-enabled cauterization --- interface/src/avatar/MyAvatar.cpp | 7 +- .../src/CauterizedMeshPartPayload.h | 4 +- .../render-utils/src/CauterizedModel.cpp | 30 ++----- .../render-utils/src/MeshPartPayload.cpp | 6 +- libraries/render-utils/src/MeshPartPayload.h | 4 +- libraries/render-utils/src/Model.cpp | 8 +- libraries/render-utils/src/Model.h | 43 +-------- libraries/render-utils/src/Skinning.slh | 89 ++++--------------- .../render-utils/src/SoftAttachmentModel.cpp | 6 +- libraries/shared/src/DualQuaternion.cpp | 3 - 10 files changed, 38 insertions(+), 162 deletions(-) diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 0fd7f7f012..02a1959a95 100755 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -1820,8 +1820,7 @@ void MyAvatar::postUpdate(float deltaTime, const render::ScenePointer& scene) { if (_skeletonModel->isLoaded() && !_skeletonModel->getRig().getAnimNode()) { initHeadBones(); - // AJT HACK DISABLE CAUTERIZE - //_skeletonModel->setCauterizeBoneSet(_headBoneSet); + _skeletonModel->setCauterizeBoneSet(_headBoneSet); _fstAnimGraphOverrideUrl = _skeletonModel->getGeometry()->getAnimGraphOverrideUrl(); initAnimGraph(); _isAnimatingScale = true; @@ -1913,9 +1912,7 @@ void MyAvatar::preDisplaySide(RenderArgs* renderArgs) { // toggle using the cauterizedBones depending on where the camera is and the rendering pass type. const bool shouldDrawHead = shouldRenderHead(renderArgs); if (shouldDrawHead != _prevShouldDrawHead) { - // AJT: DISABLE CAUTER - // _skeletonModel->setEnableCauterization(!shouldDrawHead); - _skeletonModel->setEnableCauterization(false); + _skeletonModel->setEnableCauterization(!shouldDrawHead); for (int i = 0; i < _attachmentData.size(); i++) { if (_attachmentData[i].jointName.compare("Head", Qt::CaseInsensitive) == 0 || diff --git a/libraries/render-utils/src/CauterizedMeshPartPayload.h b/libraries/render-utils/src/CauterizedMeshPartPayload.h index 5dceb3debd..2337632047 100644 --- a/libraries/render-utils/src/CauterizedMeshPartPayload.h +++ b/libraries/render-utils/src/CauterizedMeshPartPayload.h @@ -15,9 +15,7 @@ class CauterizedMeshPartPayload : public ModelMeshPartPayload { public: CauterizedMeshPartPayload(ModelPointer model, int meshIndex, int partIndex, int shapeIndex, const Transform& transform, const Transform& offsetTransform); -#if defined(SKIN_COMP) - using TransformType = Model::TransformComponents; -#elif defined(SKIN_DQ) +#if defined(SKIN_DQ) using TransformType = Model::TransformDualQuaternion; #else using TransformType = glm::mat4; diff --git a/libraries/render-utils/src/CauterizedModel.cpp b/libraries/render-utils/src/CauterizedModel.cpp index 7bac720b86..3ffacb8836 100644 --- a/libraries/render-utils/src/CauterizedModel.cpp +++ b/libraries/render-utils/src/CauterizedModel.cpp @@ -104,12 +104,6 @@ void CauterizedModel::updateClusterMatrices() { _needsUpdateClusterMatrices = false; const FBXGeometry& geometry = getFBXGeometry(); - bool debug = false; - - if (debug) { - qDebug() << "AJT: CauterizedModel::updateClusterMatrices(), url =" << _url; - } - for (int i = 0; i < (int)_meshStates.size(); i++) { Model::MeshState& state = _meshStates[i]; const FBXMesh& mesh = geometry.meshes.at(i); @@ -117,12 +111,7 @@ void CauterizedModel::updateClusterMatrices() { const FBXCluster& cluster = mesh.clusters.at(j); auto jointMatrix = _rig.getJointTransform(cluster.jointIndex); -#if defined(SKIN_COMP) - glm::mat4 m; - glm_mat4u_mul(jointMatrix, cluster.inverseBindMatrix, m); - AnimPose p(m); - state.clusterTransforms[j] = Model::TransformComponents(m); -#elif defined(SKIN_DQ) +#if defined(SKIN_DQ) glm::mat4 m; glm_mat4u_mul(jointMatrix, cluster.inverseBindMatrix, m); AnimPose p(m); @@ -137,9 +126,9 @@ void CauterizedModel::updateClusterMatrices() { if (!_cauterizeBoneSet.empty()) { static const glm::mat4 zeroScale( - glm::vec4(0.0f, 0.0f, 0.0f, 0.0f), - glm::vec4(0.0f, 0.0f, 0.0f, 0.0f), - glm::vec4(0.0f, 0.0f, 0.0f, 0.0f), + glm::vec4(0.0001f, 0.0f, 0.0f, 0.0f), + glm::vec4(0.0f, 0.0001f, 0.0f, 0.0f), + glm::vec4(0.0f, 0.0f, 0.0001f, 0.0f), glm::vec4(0.0f, 0.0f, 0.0f, 1.0f)); auto cauterizeMatrix = _rig.getJointTransform(geometry.neckJointIndex) * zeroScale; @@ -155,12 +144,7 @@ void CauterizedModel::updateClusterMatrices() { jointMatrix = cauterizeMatrix; } -#if defined(SKIN_COMP) - glm::mat4 m; - glm_mat4u_mul(jointMatrix, cluster.inverseBindMatrix, m); - AnimPose p(m); - state.clusterTransforms[j] = Model::TransformComponents(m); -#elif defined(SKIN_DQ) +#if defined(SKIN_DQ) glm::mat4 m; glm_mat4u_mul(jointMatrix, cluster.inverseBindMatrix, m); AnimPose p(m); @@ -235,7 +219,7 @@ void CauterizedModel::updateRenderItems() { Transform renderTransform = modelTransform; if (clusterTransforms.size() == 1) { -#if defined(SKIN_COMP) || defined(SKIN_DQ) +#if defined(SKIN_DQ) renderTransform = modelTransform.worldTransform(Transform(clusterTransforms[0].getMatrix())); #else renderTransform = modelTransform.worldTransform(Transform(clusterTransforms[0])); @@ -245,7 +229,7 @@ void CauterizedModel::updateRenderItems() { renderTransform = modelTransform; if (clusterTransformsCauterized.size() == 1) { -#if defined(SKIN_COMP) || defined(SKIN_DQ) +#if defined(SKIN_DQ) renderTransform = modelTransform.worldTransform(Transform(clusterTransformsCauterized[0].getMatrix())); #else renderTransform = modelTransform.worldTransform(Transform(clusterTransformsCauterized[0])); diff --git a/libraries/render-utils/src/MeshPartPayload.cpp b/libraries/render-utils/src/MeshPartPayload.cpp index a303c0b0d8..481d4574ab 100644 --- a/libraries/render-utils/src/MeshPartPayload.cpp +++ b/libraries/render-utils/src/MeshPartPayload.cpp @@ -331,7 +331,7 @@ ModelMeshPartPayload::ModelMeshPartPayload(ModelPointer model, int meshIndex, in updateTransform(transform, offsetTransform); Transform renderTransform = transform; if (state.clusterTransforms.size() == 1) { -#if defined(SKIN_COMP) || defined(SKIN_DQ) +#if defined(SKIN_DQ) renderTransform = transform.worldTransform(Transform(state.clusterTransforms[0].getMatrix())); #else renderTransform = transform.worldTransform(Transform(state.clusterTransforms[0])); @@ -548,7 +548,7 @@ void ModelMeshPartPayload::render(RenderArgs* args) { void ModelMeshPartPayload::computeAdjustedLocalBound(const std::vector& clusterTransforms) { _adjustedLocalBound = _localBound; if (clusterTransforms.size() > 0) { -#if defined(SKIN_COMP) || defined(SKIN_DQ) +#if defined(SKIN_DQ) _adjustedLocalBound.transform(clusterTransforms[0].getMatrix()); #else _adjustedLocalBound.transform(clusterTransforms[0]); @@ -556,7 +556,7 @@ void ModelMeshPartPayload::computeAdjustedLocalBound(const std::vector clusterTransforms; -#elif defined(SKIN_DQ) +#if defined(SKIN_DQ) std::vector clusterTransforms; #else std::vector clusterTransforms; diff --git a/libraries/render-utils/src/Skinning.slh b/libraries/render-utils/src/Skinning.slh index c11455a575..b90db26d9f 100644 --- a/libraries/render-utils/src/Skinning.slh +++ b/libraries/render-utils/src/Skinning.slh @@ -43,20 +43,19 @@ void skinPosition(ivec4 skinClusterIndex, vec4 skinClusterWeight, vec4 inPositio vec3 sAccum = vec3(0.0, 0.0, 0.0); vec4 rAccum = vec4(0.0, 0.0, 0.0, 0.0); vec4 iAccum = vec4(0.0, 0.0, 0.0, 0.0); - vec4 prevR = vec4(0.0, 0.0, 0.0, 1.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][0], clusterMatrix[0][1], clusterMatrix[0][2]); - vec4 real = vec4(clusterMatrix[1][0], clusterMatrix[1][1], clusterMatrix[1][2], clusterMatrix[1][3]); - vec4 imag = vec4(clusterMatrix[2][0], clusterMatrix[2][1], clusterMatrix[2][2], clusterMatrix[2][3]); + vec3 scale = vec3(clusterMatrix[0]); + vec4 real = clusterMatrix[1]; + vec4 imag = clusterMatrix[2]; + // to ensure that we rotate along the shortest arc, reverse dual quaternions with negative polarity. float dqClusterWeight = clusterWeight; - if (i == 0) { - prevR = real; - } else if (dot(prevR, real) < 0) { + if (dot(real, polarityReference) < 0) { dqClusterWeight = -clusterWeight; } @@ -108,20 +107,19 @@ void skinPositionNormal(ivec4 skinClusterIndex, vec4 skinClusterWeight, vec4 inP vec3 sAccum = vec3(0.0, 0.0, 0.0); vec4 rAccum = vec4(0.0, 0.0, 0.0, 0.0); vec4 iAccum = vec4(0.0, 0.0, 0.0, 0.0); - vec4 prevR = vec4(0.0, 0.0, 0.0, 1.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][0], clusterMatrix[0][1], clusterMatrix[0][2]); - vec4 real = vec4(clusterMatrix[1][0], clusterMatrix[1][1], clusterMatrix[1][2], clusterMatrix[1][3]); - vec4 imag = vec4(clusterMatrix[2][0], clusterMatrix[2][1], clusterMatrix[2][2], clusterMatrix[2][3]); + vec3 scale = vec3(clusterMatrix[0]); + vec4 real = clusterMatrix[1]; + vec4 imag = clusterMatrix[2]; + // to ensure that we rotate along the shortest arc, reverse dual quaternions with negative polarity. float dqClusterWeight = clusterWeight; - if (i == 0) { - prevR = real; - } else if (dot(prevR, real) < 0) { + if (dot(real, polarityReference) < 0) { dqClusterWeight = -clusterWeight; } @@ -174,20 +172,19 @@ void skinPositionNormalTangent(ivec4 skinClusterIndex, vec4 skinClusterWeight, v vec3 sAccum = vec3(0.0, 0.0, 0.0); vec4 rAccum = vec4(0.0, 0.0, 0.0, 0.0); vec4 iAccum = vec4(0.0, 0.0, 0.0, 0.0); - vec4 prevR = vec4(0.0, 0.0, 0.0, 1.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][0], clusterMatrix[0][1], clusterMatrix[0][2]); - vec4 real = vec4(clusterMatrix[1][0], clusterMatrix[1][1], clusterMatrix[1][2], clusterMatrix[1][3]); - vec4 imag = vec4(clusterMatrix[2][0], clusterMatrix[2][1], clusterMatrix[2][2], clusterMatrix[2][3]); + vec3 scale = vec3(clusterMatrix[0]); + vec4 real = clusterMatrix[1]; + vec4 imag = clusterMatrix[2]; + // to ensure that we rotate along the shortest arc, reverse dual quaternions with negative polarity. float dqClusterWeight = clusterWeight; - if (i == 0) { - prevR = real; - } else if (dot(prevR, real) < 0) { + if (dot(real, polarityReference) < 0) { dqClusterWeight = -clusterWeight; } @@ -326,54 +323,4 @@ void skinPositionNormalTangent(ivec4 skinClusterIndex, vec4 skinClusterWeight, v } */ -/* -// ORIGINAL -void skinPosition(ivec4 skinClusterIndex, vec4 skinClusterWeight, vec4 inPosition, out vec4 skinnedPosition) { - vec4 newPosition = vec4(0.0, 0.0, 0.0, 0.0); - - for (int i = 0; i < INDICES_PER_VERTEX; i++) { - mat4 clusterMatrix = clusterMatrices[(skinClusterIndex[i])]; - float clusterWeight = skinClusterWeight[i]; - newPosition += clusterMatrix * inPosition * clusterWeight; - } - - skinnedPosition = newPosition; -} - -void skinPositionNormal(ivec4 skinClusterIndex, vec4 skinClusterWeight, vec4 inPosition, vec3 inNormal, - out vec4 skinnedPosition, out vec3 skinnedNormal) { - vec4 newPosition = vec4(0.0, 0.0, 0.0, 0.0); - vec4 newNormal = vec4(0.0, 0.0, 0.0, 0.0); - - for (int i = 0; i < INDICES_PER_VERTEX; i++) { - mat4 clusterMatrix = clusterMatrices[(skinClusterIndex[i])]; - float clusterWeight = skinClusterWeight[i]; - newPosition += clusterMatrix * inPosition * clusterWeight; - newNormal += clusterMatrix * vec4(inNormal.xyz, 0.0) * clusterWeight; - } - - skinnedPosition = newPosition; - skinnedNormal = newNormal.xyz; -} - -void skinPositionNormalTangent(ivec4 skinClusterIndex, vec4 skinClusterWeight, vec4 inPosition, vec3 inNormal, vec3 inTangent, - out vec4 skinnedPosition, out vec3 skinnedNormal, out vec3 skinnedTangent) { - vec4 newPosition = vec4(0.0, 0.0, 0.0, 0.0); - vec4 newNormal = vec4(0.0, 0.0, 0.0, 0.0); - vec4 newTangent = vec4(0.0, 0.0, 0.0, 0.0); - - for (int i = 0; i < INDICES_PER_VERTEX; i++) { - mat4 clusterMatrix = clusterMatrices[(skinClusterIndex[i])]; - float clusterWeight = skinClusterWeight[i]; - newPosition += clusterMatrix * inPosition * clusterWeight; - newNormal += clusterMatrix * vec4(inNormal.xyz, 0.0) * clusterWeight; - newTangent += clusterMatrix * vec4(inTangent.xyz, 0.0) * clusterWeight; - } - - skinnedPosition = newPosition; - skinnedNormal = newNormal.xyz; - skinnedTangent = newTangent.xyz; -} -*/ - <@endif@> diff --git a/libraries/render-utils/src/SoftAttachmentModel.cpp b/libraries/render-utils/src/SoftAttachmentModel.cpp index 7b76f89184..58b5eea222 100644 --- a/libraries/render-utils/src/SoftAttachmentModel.cpp +++ b/libraries/render-utils/src/SoftAttachmentModel.cpp @@ -58,11 +58,7 @@ void SoftAttachmentModel::updateClusterMatrices() { } else { jointMatrix = _rig.getJointTransform(cluster.jointIndex); } -#if defined(SKIN_COMP) - glm::mat4 m; - glm_mat4u_mul(jointMatrix, cluster.inverseBindMatrix, m); - state.clusterTransforms[j] = Model::TransformComponents(m); -#elif defined(SKIN_DQ) +#if defined(SKIN_DQ) glm::mat4 m; glm_mat4u_mul(jointMatrix, cluster.inverseBindMatrix, m); state.clusterTransforms[j] = Model::TransformDualQuaternion(m); diff --git a/libraries/shared/src/DualQuaternion.cpp b/libraries/shared/src/DualQuaternion.cpp index 44d7356463..e2a9dc9bb2 100644 --- a/libraries/shared/src/DualQuaternion.cpp +++ b/libraries/shared/src/DualQuaternion.cpp @@ -63,18 +63,15 @@ glm::vec3 DualQuaternion::xformVector(const glm::vec3& rhs) const { return _real * rhs; } -// AJT: UNTESTED DualQuaternion DualQuaternion::inverse() const { glm::quat invReal = glm::inverse(_real); return DualQuaternion(invReal, - invReal * _imag * invReal); } -// AJT: UNTESTED DualQuaternion DualQuaternion::conjugate() const { return DualQuaternion(glm::conjugate(_real), glm::conjugate(_imag)); } -// AJT: UNTESTED float DualQuaternion::length() const { float dot = this->dot(*this); return sqrtf(dot); From bcd813ac62342712c6b3d4f16661317f44b58910 Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Fri, 22 Dec 2017 15:24:54 -0800 Subject: [PATCH 11/32] Optimizations and cleanup --- .../render-utils/src/CauterizedModel.cpp | 14 +- .../render-utils/src/MeshPartPayload.cpp | 15 +- libraries/render-utils/src/Model.cpp | 6 +- libraries/render-utils/src/Model.h | 16 +- libraries/render-utils/src/Skinning.slh | 242 +++++------------- .../render-utils/src/SoftAttachmentModel.cpp | 11 +- libraries/shared/src/Transform.h | 2 +- 7 files changed, 113 insertions(+), 193 deletions(-) diff --git a/libraries/render-utils/src/CauterizedModel.cpp b/libraries/render-utils/src/CauterizedModel.cpp index 3ffacb8836..86deff095b 100644 --- a/libraries/render-utils/src/CauterizedModel.cpp +++ b/libraries/render-utils/src/CauterizedModel.cpp @@ -112,9 +112,9 @@ void CauterizedModel::updateClusterMatrices() { auto jointMatrix = _rig.getJointTransform(cluster.jointIndex); #if defined(SKIN_DQ) + // AJT: TODO: optimize glm::mat4 m; glm_mat4u_mul(jointMatrix, cluster.inverseBindMatrix, m); - AnimPose p(m); state.clusterTransforms[j] = Model::TransformDualQuaternion(m); #else glm_mat4u_mul(jointMatrix, cluster.inverseBindMatrix, state.clusterTransforms[j]); @@ -145,9 +145,9 @@ void CauterizedModel::updateClusterMatrices() { } #if defined(SKIN_DQ) + // AJT: TODO: optimize glm::mat4 m; glm_mat4u_mul(jointMatrix, cluster.inverseBindMatrix, m); - AnimPose p(m); state.clusterTransforms[j] = Model::TransformDualQuaternion(m); #else glm_mat4u_mul(jointMatrix, cluster.inverseBindMatrix, state.clusterTransforms[j]); @@ -220,7 +220,10 @@ void CauterizedModel::updateRenderItems() { Transform renderTransform = modelTransform; if (clusterTransforms.size() == 1) { #if defined(SKIN_DQ) - renderTransform = modelTransform.worldTransform(Transform(clusterTransforms[0].getMatrix())); + Transform transform(clusterTransforms[0].getRotation(), + clusterTransforms[0].getScale(), + clusterTransforms[0].getTranslation()); + renderTransform = modelTransform.worldTransform(transform); #else renderTransform = modelTransform.worldTransform(Transform(clusterTransforms[0])); #endif @@ -230,7 +233,10 @@ void CauterizedModel::updateRenderItems() { renderTransform = modelTransform; if (clusterTransformsCauterized.size() == 1) { #if defined(SKIN_DQ) - renderTransform = modelTransform.worldTransform(Transform(clusterTransformsCauterized[0].getMatrix())); + Transform transform(clusterTransforms[0].getRotation(), + clusterTransforms[0].getScale(), + clusterTransforms[0].getTranslation()); + renderTransform = modelTransform.worldTransform(Transform(transform)); #else renderTransform = modelTransform.worldTransform(Transform(clusterTransformsCauterized[0])); #endif diff --git a/libraries/render-utils/src/MeshPartPayload.cpp b/libraries/render-utils/src/MeshPartPayload.cpp index 481d4574ab..e3bbfc1c03 100644 --- a/libraries/render-utils/src/MeshPartPayload.cpp +++ b/libraries/render-utils/src/MeshPartPayload.cpp @@ -332,7 +332,10 @@ ModelMeshPartPayload::ModelMeshPartPayload(ModelPointer model, int meshIndex, in Transform renderTransform = transform; if (state.clusterTransforms.size() == 1) { #if defined(SKIN_DQ) - renderTransform = transform.worldTransform(Transform(state.clusterTransforms[0].getMatrix())); + Transform transform(state.clusterTransforms[0].getRotation(), + state.clusterTransforms[0].getScale(), + state.clusterTransforms[0].getTranslation()); + renderTransform = transform.worldTransform(Transform(transform)); #else renderTransform = transform.worldTransform(Transform(state.clusterTransforms[0])); #endif @@ -549,7 +552,10 @@ void ModelMeshPartPayload::computeAdjustedLocalBound(const std::vector 0) { #if defined(SKIN_DQ) - _adjustedLocalBound.transform(clusterTransforms[0].getMatrix()); + Transform rootTransform(clusterTransforms[0].getRotation(), + clusterTransforms[0].getScale(), + clusterTransforms[0].getTranslation()); + _adjustedLocalBound.transform(rootTransform); #else _adjustedLocalBound.transform(clusterTransforms[0]); #endif @@ -557,7 +563,10 @@ void ModelMeshPartPayload::computeAdjustedLocalBound(const std::vector <@def SKINNING_SLH@> +// Use dual quaternion skinning +// Must match #define SKIN_DQ in Model.h +<@def SKIN_DQ@> + const int MAX_CLUSTERS = 128; const int INDICES_PER_VERTEX = 4; @@ -18,33 +22,46 @@ layout(std140) uniform skinClusterBuffer { mat4 clusterMatrices[MAX_CLUSTERS]; }; -vec4 quatConj(vec4 v) { - return vec4(-v.x, -v.y, -v.z, v.w); -} - -vec4 quatMul(vec4 q1, vec4 q2) { - return vec4( q1.x * q2.w + q1.y * q2.z - q1.z * q2.y + q1.w * q2.x, - -q1.x * q2.z + q1.y * q2.w + q1.z * q2.x + q1.w * q2.y, - q1.x * q2.y - q1.y * q2.x + q1.z * q2.w + q1.w * q2.z, - -q1.x * q2.x - q1.y * q2.y - q1.z * q2.z + q1.w * q2.w); -} - -vec3 rotateByQuat(vec4 q, vec3 p) { - return vec3(quatMul(quatMul(q, vec4(p.x, p.y, p.z, 0.0)), quatConj(q))); -} - -void dqMul(vec4 lhsReal, vec4 lhsImag, vec4 rhsReal, vec4 rhsImag, out vec4 realOut, out vec4 imagOut) { - realOut = quatMul(lhsReal, rhsReal); - imagOut = quatMul(lhsReal, rhsImag) + quatMul(lhsImag, rhsReal); +<@if SKIN_DQ@> + +mat4 dualQuatToMat4(vec4 real, vec4 imag) { + float twoRealXSq = 2.0 * real.x * real.x; + float twoRealYSq = 2.0 * real.y * real.y; + float twoRealZSq = 2.0 * real.z * real.z; + float twoRealXY = 2.0 * real.x * real.y; + float twoRealXZ = 2.0 * real.x * real.z; + float twoRealXW = 2.0 * real.x * real.w; + float twoRealZW = 2.0 * real.z * real.w; + float twoRealYZ = 2.0 * real.y * real.z; + float twoRealYW = 2.0 * real.y * real.w; + vec4 col0 = vec4(1.0 - twoRealYSq - twoRealZSq, + twoRealXY + twoRealZW, + twoRealXZ - twoRealYW, + 0.0); + vec4 col1 = vec4(twoRealXY - twoRealZW, + 1 - twoRealXSq - twoRealZSq, + twoRealYZ + twoRealXW, + 0.0); + vec4 col2 = vec4(twoRealXZ + twoRealYW, + twoRealYZ - twoRealXW, + 1 - twoRealXSq - twoRealYSq, + 0.0); + vec4 col3 = vec4(2.0 * (-imag.w * real.x + imag.x * real.w - imag.y * real.z + imag.z * real.y), + 2.0 * (-imag.w * real.y + imag.x * real.z + imag.y * real.w - imag.z * real.x), + 2.0 * (-imag.w * real.z - imag.x * real.y + imag.y * real.x + imag.z * real.w), + 1.0); + + return mat4(col0, col1, col2, col3); } +// dual quaternion linear blending 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 rAccum = vec4(0.0, 0.0, 0.0, 0.0); vec4 iAccum = 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]; @@ -64,46 +81,20 @@ void skinPosition(ivec4 skinClusterIndex, vec4 skinClusterWeight, vec4 inPositio iAccum += imag * dqClusterWeight; } + // normalize dual quaternion float norm = length(rAccum); rAccum /= norm; iAccum /= norm; - float xe = iAccum.x; - float ye = iAccum.y; - float ze = iAccum.z; - float we = iAccum.w; - - float x0 = rAccum.x; - float y0 = rAccum.y; - float z0 = rAccum.z; - float w0 = rAccum.w; - - float t0 = 2.0 * (-we * x0 + xe * w0 - ye * z0 + ze * y0); - float t1 = 2.0 * (-we * y0 + xe * z0 + ye * w0 - ze * x0); - float t2 = 2.0 * (-we * z0 - xe * y0 + ye * x0 + ze * w0); - - vec4 col0 = vec4(1.0 - 2.0 * y0 * y0 - 2.0 * z0 * z0, - 2.0 * x0 * y0 + 2.0 * w0 * z0, - 2.0 * x0 * z0 - 2.0 * w0 * y0, - 0.0); - vec4 col1 = vec4(2.0 * x0 * y0 - 2.0 * w0 * z0, - 1 - 2.0 * x0 * x0 - 2.0 * z0 * z0, - 2.0 * y0 * z0 + 2.0 * w0 * x0, - 0.0); - vec4 col2 = vec4(2.0 * x0 * z0 + 2.0 * w0 * y0, - 2.0 * y0 * z0 - 2.0 * w0 * x0, - 1 - 2.0 * x0 * x0 - 2.0 * y0 * y0, - 0.0); - vec4 col3 = vec4(t0, t1, t2, 1.0); - - mat4 m = mat4(col0, col1, col2, col3); - + // conversion from dual quaternion to 4x4 matrix. + mat4 m = dualQuatToMat4(rAccum, iAccum); skinnedPosition = m * (vec4(sAccum, 1) * 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 rAccum = vec4(0.0, 0.0, 0.0, 0.0); vec4 iAccum = vec4(0.0, 0.0, 0.0, 0.0); @@ -128,40 +119,13 @@ void skinPositionNormal(ivec4 skinClusterIndex, vec4 skinClusterWeight, vec4 inP iAccum += imag * dqClusterWeight; } + // normalize dual quaternion float norm = length(rAccum); rAccum /= norm; iAccum /= norm; - float xe = iAccum.x; - float ye = iAccum.y; - float ze = iAccum.z; - float we = iAccum.w; - - float x0 = rAccum.x; - float y0 = rAccum.y; - float z0 = rAccum.z; - float w0 = rAccum.w; - - float t0 = 2.0 * (-we * x0 + xe * w0 - ye * z0 + ze * y0); - float t1 = 2.0 * (-we * y0 + xe * z0 + ye * w0 - ze * x0); - float t2 = 2.0 * (-we * z0 - xe * y0 + ye * x0 + ze * w0); - - vec4 col0 = vec4(1.0 - 2.0 * y0 * y0 - 2.0 * z0 * z0, - 2.0 * x0 * y0 + 2.0 * w0 * z0, - 2.0 * x0 * z0 - 2.0 * w0 * y0, - 0.0); - vec4 col1 = vec4(2.0 * x0 * y0 - 2.0 * w0 * z0, - 1 - 2.0 * x0 * x0 - 2.0 * z0 * z0, - 2.0 * y0 * z0 + 2.0 * w0 * x0, - 0.0); - vec4 col2 = vec4(2.0 * x0 * z0 + 2.0 * w0 * y0, - 2.0 * y0 * z0 - 2.0 * w0 * x0, - 1 - 2.0 * x0 * x0 - 2.0 * y0 * y0, - 0.0); - vec4 col3 = vec4(t0, t1, t2, 1.0); - - mat4 m = mat4(col0, col1, col2, col3); - + // conversion from dual quaternion to 4x4 matrix. + mat4 m = dualQuatToMat4(rAccum, iAccum); skinnedPosition = m * (vec4(sAccum, 1) * inPosition); skinnedNormal = vec3(m * vec4(inNormal, 0)); } @@ -169,6 +133,7 @@ void skinPositionNormal(ivec4 skinClusterIndex, vec4 skinClusterWeight, vec4 inP void skinPositionNormalTangent(ivec4 skinClusterIndex, vec4 skinClusterWeight, vec4 inPosition, vec3 inNormal, vec3 inTangent, 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 rAccum = vec4(0.0, 0.0, 0.0, 0.0); vec4 iAccum = vec4(0.0, 0.0, 0.0, 0.0); @@ -193,134 +158,67 @@ void skinPositionNormalTangent(ivec4 skinClusterIndex, vec4 skinClusterWeight, v iAccum += imag * dqClusterWeight; } + // normalize dual quaternion float norm = length(rAccum); rAccum /= norm; iAccum /= norm; - float xe = iAccum.x; - float ye = iAccum.y; - float ze = iAccum.z; - float we = iAccum.w; - - float x0 = rAccum.x; - float y0 = rAccum.y; - float z0 = rAccum.z; - float w0 = rAccum.w; - - float t0 = 2.0 * (-we * x0 + xe * w0 - ye * z0 + ze * y0); - float t1 = 2.0 * (-we * y0 + xe * z0 + ye * w0 - ze * x0); - float t2 = 2.0 * (-we * z0 - xe * y0 + ye * x0 + ze * w0); - - vec4 col0 = vec4(1.0 - 2.0 * y0 * y0 - 2.0 * z0 * z0, - 2.0 * x0 * y0 + 2.0 * w0 * z0, - 2.0 * x0 * z0 - 2.0 * w0 * y0, - 0.0); - vec4 col1 = vec4(2.0 * x0 * y0 - 2.0 * w0 * z0, - 1 - 2.0 * x0 * x0 - 2.0 * z0 * z0, - 2.0 * y0 * z0 + 2.0 * w0 * x0, - 0.0); - vec4 col2 = vec4(2.0 * x0 * z0 + 2.0 * w0 * y0, - 2.0 * y0 * z0 - 2.0 * w0 * x0, - 1 - 2.0 * x0 * x0 - 2.0 * y0 * y0, - 0.0); - vec4 col3 = vec4(t0, t1, t2, 1.0); - - mat4 m = mat4(col0, col1, col2, col3); - + // conversion from dual quaternion to 4x4 matrix. + mat4 m = dualQuatToMat4(rAccum, iAccum); skinnedPosition = m * (vec4(sAccum, 1) * inPosition); skinnedNormal = vec3(m * vec4(inNormal, 0)); skinnedTangent = vec3(m * vec4(inTangent, 0)); } -// SKIN_COMP -/* +<@else@> // SKIN_DQ + void skinPosition(ivec4 skinClusterIndex, vec4 skinClusterWeight, vec4 inPosition, out vec4 skinnedPosition) { - vec3 sAccum = vec3(0.0, 0.0, 0.0); - vec4 rAccum = vec4(0.0, 0.0, 0.0, 0.0); - vec3 tAccum = vec3(0.0, 0.0, 0.0); + vec4 newPosition = vec4(0.0, 0.0, 0.0, 0.0); for (int i = 0; i < INDICES_PER_VERTEX; i++) { mat4 clusterMatrix = clusterMatrices[(skinClusterIndex[i])]; float clusterWeight = skinClusterWeight[i]; - - vec3 s = vec3(clusterMatrix[0][0], clusterMatrix[0][1], clusterMatrix[0][2]); - vec4 r = vec4(clusterMatrix[1][0], clusterMatrix[1][1], clusterMatrix[1][2], clusterMatrix[1][3]); - vec3 t = vec3(clusterMatrix[2][0], clusterMatrix[2][1], clusterMatrix[2][2]); - - if (dot(r, rAccum) < 0) { - r = -r; - } - - sAccum += s * clusterWeight; - rAccum += r * clusterWeight; - tAccum += t * clusterWeight; + newPosition += clusterMatrix * inPosition * clusterWeight; } - rAccum = normalize(rAccum); - - skinnedPosition = vec4(rotateByQuat(rAccum, (vec3(inPosition) * sAccum)) + tAccum, 1); + skinnedPosition = newPosition; } void skinPositionNormal(ivec4 skinClusterIndex, vec4 skinClusterWeight, vec4 inPosition, vec3 inNormal, out vec4 skinnedPosition, out vec3 skinnedNormal) { - - vec3 sAccum = vec3(0.0, 0.0, 0.0); - vec4 rAccum = vec4(0.0, 0.0, 0.0, 0.0); - vec3 tAccum = vec3(0.0, 0.0, 0.0); + vec4 newPosition = vec4(0.0, 0.0, 0.0, 0.0); + vec4 newNormal = vec4(0.0, 0.0, 0.0, 0.0); for (int i = 0; i < INDICES_PER_VERTEX; i++) { mat4 clusterMatrix = clusterMatrices[(skinClusterIndex[i])]; float clusterWeight = skinClusterWeight[i]; - - vec3 s = vec3(clusterMatrix[0][0], clusterMatrix[0][1], clusterMatrix[0][2]); - vec4 r = vec4(clusterMatrix[1][0], clusterMatrix[1][1], clusterMatrix[1][2], clusterMatrix[1][3]); - vec3 t = vec3(clusterMatrix[2][0], clusterMatrix[2][1], clusterMatrix[2][2]); - - if (dot(r, rAccum) < 0) { - r = -r; - } - - sAccum += s * clusterWeight; - rAccum += r * clusterWeight; - tAccum += t * clusterWeight; + newPosition += clusterMatrix * inPosition * clusterWeight; + newNormal += clusterMatrix * vec4(inNormal.xyz, 0.0) * clusterWeight; } - rAccum = normalize(rAccum); - - skinnedPosition = vec4(rotateByQuat(rAccum, (vec3(inPosition) * sAccum)) + tAccum, 1); - skinnedNormal = rotateByQuat(rAccum, inNormal); + skinnedPosition = newPosition; + skinnedNormal = newNormal.xyz; } void skinPositionNormalTangent(ivec4 skinClusterIndex, vec4 skinClusterWeight, vec4 inPosition, vec3 inNormal, vec3 inTangent, out vec4 skinnedPosition, out vec3 skinnedNormal, out vec3 skinnedTangent) { - - vec3 sAccum = vec3(0.0, 0.0, 0.0); - vec4 rAccum = vec4(0.0, 0.0, 0.0, 0.0); - vec3 tAccum = vec3(0.0, 0.0, 0.0); + vec4 newPosition = vec4(0.0, 0.0, 0.0, 0.0); + vec4 newNormal = vec4(0.0, 0.0, 0.0, 0.0); + vec4 newTangent = vec4(0.0, 0.0, 0.0, 0.0); for (int i = 0; i < INDICES_PER_VERTEX; i++) { mat4 clusterMatrix = clusterMatrices[(skinClusterIndex[i])]; float clusterWeight = skinClusterWeight[i]; - - vec3 s = vec3(clusterMatrix[0][0], clusterMatrix[0][1], clusterMatrix[0][2]); - vec4 r = vec4(clusterMatrix[1][0], clusterMatrix[1][1], clusterMatrix[1][2], clusterMatrix[1][3]); - vec3 t = vec3(clusterMatrix[2][0], clusterMatrix[2][1], clusterMatrix[2][2]); - - if (dot(r, rAccum) < 0) { - r = -r; - } - - sAccum += s * clusterWeight; - rAccum += r * clusterWeight; - tAccum += t * clusterWeight; + newPosition += clusterMatrix * inPosition * clusterWeight; + newNormal += clusterMatrix * vec4(inNormal.xyz, 0.0) * clusterWeight; + newTangent += clusterMatrix * vec4(inTangent.xyz, 0.0) * clusterWeight; } - rAccum = normalize(rAccum); - - skinnedPosition = vec4(rotateByQuat(rAccum, (vec3(inPosition) * sAccum)) + tAccum, 1); - skinnedNormal = rotateByQuat(rAccum, inNormal); - skinnedTangent = rotateByQuat(rAccum, inTangent); + skinnedPosition = newPosition; + skinnedNormal = newNormal.xyz; + skinnedTangent = newTangent.xyz; } -*/ -<@endif@> +<@endif@> // if SKIN_DQ + +<@endif@> // if not SKINNING_SLH diff --git a/libraries/render-utils/src/SoftAttachmentModel.cpp b/libraries/render-utils/src/SoftAttachmentModel.cpp index 58b5eea222..fe74dc3c87 100644 --- a/libraries/render-utils/src/SoftAttachmentModel.cpp +++ b/libraries/render-utils/src/SoftAttachmentModel.cpp @@ -52,17 +52,26 @@ void SoftAttachmentModel::updateClusterMatrices() { // TODO: cache these look-ups as an optimization int jointIndexOverride = getJointIndexOverride(cluster.jointIndex); +#if defined(SKIN_DQ) glm::mat4 jointMatrix; if (jointIndexOverride >= 0 && jointIndexOverride < _rigOverride.getJointStateCount()) { jointMatrix = _rigOverride.getJointTransform(jointIndexOverride); } else { jointMatrix = _rig.getJointTransform(cluster.jointIndex); } -#if defined(SKIN_DQ) + + // AJT: TODO: OPTIMIZE glm::mat4 m; glm_mat4u_mul(jointMatrix, cluster.inverseBindMatrix, m); state.clusterTransforms[j] = Model::TransformDualQuaternion(m); #else + glm::mat4 jointMatrix; + if (jointIndexOverride >= 0 && jointIndexOverride < _rigOverride.getJointStateCount()) { + jointMatrix = _rigOverride.getJointTransform(jointIndexOverride); + } else { + jointMatrix = _rig.getJointTransform(cluster.jointIndex); + } + glm_mat4u_mul(jointMatrix, cluster.inverseBindMatrix, state.clusterTransforms[j]); #endif } diff --git a/libraries/shared/src/Transform.h b/libraries/shared/src/Transform.h index 7a39314f4d..90bfc1aaa6 100644 --- a/libraries/shared/src/Transform.h +++ b/libraries/shared/src/Transform.h @@ -58,7 +58,7 @@ public: _rotation(rotation), _scale(scale), _translation(translation), - _flags(FLAG_CACHE_INVALID_BITSET) // invalid cache + _flags(0xf) // FLAG_TRANSLATION | FLAG_ROTATION | FLAG_SCALING | FLAG_NON_UNIFORM { if (!isValidScale(_scale)) { _scale = Vec3(1.0f); From aacf2d489fabd4e2186052caec26afc29f21d115 Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Wed, 3 Jan 2018 13:21:47 -0800 Subject: [PATCH 12/32] removed comments --- libraries/render-utils/src/CauterizedModel.cpp | 2 -- libraries/render-utils/src/Model.cpp | 2 +- libraries/render-utils/src/SoftAttachmentModel.cpp | 1 - 3 files changed, 1 insertion(+), 4 deletions(-) diff --git a/libraries/render-utils/src/CauterizedModel.cpp b/libraries/render-utils/src/CauterizedModel.cpp index 86deff095b..b8596af07f 100644 --- a/libraries/render-utils/src/CauterizedModel.cpp +++ b/libraries/render-utils/src/CauterizedModel.cpp @@ -112,7 +112,6 @@ void CauterizedModel::updateClusterMatrices() { auto jointMatrix = _rig.getJointTransform(cluster.jointIndex); #if defined(SKIN_DQ) - // AJT: TODO: optimize glm::mat4 m; glm_mat4u_mul(jointMatrix, cluster.inverseBindMatrix, m); state.clusterTransforms[j] = Model::TransformDualQuaternion(m); @@ -145,7 +144,6 @@ void CauterizedModel::updateClusterMatrices() { } #if defined(SKIN_DQ) - // AJT: TODO: optimize glm::mat4 m; glm_mat4u_mul(jointMatrix, cluster.inverseBindMatrix, m); state.clusterTransforms[j] = Model::TransformDualQuaternion(m); diff --git a/libraries/render-utils/src/Model.cpp b/libraries/render-utils/src/Model.cpp index 5ca9fc2bc4..b2459d27e3 100644 --- a/libraries/render-utils/src/Model.cpp +++ b/libraries/render-utils/src/Model.cpp @@ -1178,6 +1178,7 @@ void Model::updateClusterMatrices() { if (!_needsUpdateClusterMatrices || !isLoaded()) { return; } + _needsUpdateClusterMatrices = false; const FBXGeometry& geometry = getFBXGeometry(); for (int i = 0; i < (int) _meshStates.size(); i++) { @@ -1187,7 +1188,6 @@ void Model::updateClusterMatrices() { const FBXCluster& cluster = mesh.clusters.at(j); auto jointMatrix = _rig.getJointTransform(cluster.jointIndex); #if defined(SKIN_DQ) - // AJT: TODO: optimize glm::mat4 mat; glm_mat4u_mul(jointMatrix, cluster.inverseBindMatrix, mat); state.clusterTransforms[j] = TransformDualQuaternion(mat); diff --git a/libraries/render-utils/src/SoftAttachmentModel.cpp b/libraries/render-utils/src/SoftAttachmentModel.cpp index fe74dc3c87..0d0db7cbe3 100644 --- a/libraries/render-utils/src/SoftAttachmentModel.cpp +++ b/libraries/render-utils/src/SoftAttachmentModel.cpp @@ -60,7 +60,6 @@ void SoftAttachmentModel::updateClusterMatrices() { jointMatrix = _rig.getJointTransform(cluster.jointIndex); } - // AJT: TODO: OPTIMIZE glm::mat4 m; glm_mat4u_mul(jointMatrix, cluster.inverseBindMatrix, m); state.clusterTransforms[j] = Model::TransformDualQuaternion(m); From b8c88fca3b0e28be59dab4a3f26fb8294465120c Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Thu, 4 Jan 2018 13:36:15 -0800 Subject: [PATCH 13/32] Dual Quaternion skinning optimization Reduce expensive conversion from quat -> matrix -> quat, by keeping the inverseBindPose in a Transform instance instead of a mat4. --- libraries/fbx/src/FBX.h | 1 + libraries/fbx/src/FBXReader.cpp | 1 + .../render-utils/src/CauterizedModel.cpp | 37 +++++++++++-------- libraries/render-utils/src/Model.cpp | 9 +++-- libraries/render-utils/src/Model.h | 4 ++ 5 files changed, 33 insertions(+), 19 deletions(-) diff --git a/libraries/fbx/src/FBX.h b/libraries/fbx/src/FBX.h index 2ec847104c..6f771a702a 100644 --- a/libraries/fbx/src/FBX.h +++ b/libraries/fbx/src/FBX.h @@ -115,6 +115,7 @@ public: int jointIndex; glm::mat4 inverseBindMatrix; + Transform inverseBindTransform; }; const int MAX_NUM_PIXELS_FOR_FBX_TEXTURE = 2048 * 2048; diff --git a/libraries/fbx/src/FBXReader.cpp b/libraries/fbx/src/FBXReader.cpp index b1db5992e7..c52647802e 100644 --- a/libraries/fbx/src/FBXReader.cpp +++ b/libraries/fbx/src/FBXReader.cpp @@ -1613,6 +1613,7 @@ FBXGeometry* FBXReader::extractFBXGeometry(const QVariantHash& mapping, const QS fbxCluster.jointIndex = 0; } fbxCluster.inverseBindMatrix = glm::inverse(cluster.transformLink) * modelTransform; + fbxCluster.inverseBindTransform = Transform(fbxCluster.inverseBindMatrix); extracted.mesh.clusters.append(fbxCluster); // override the bind rotation with the transform link diff --git a/libraries/render-utils/src/CauterizedModel.cpp b/libraries/render-utils/src/CauterizedModel.cpp index b8596af07f..e3f26a43d8 100644 --- a/libraries/render-utils/src/CauterizedModel.cpp +++ b/libraries/render-utils/src/CauterizedModel.cpp @@ -109,13 +109,14 @@ void CauterizedModel::updateClusterMatrices() { const FBXMesh& mesh = geometry.meshes.at(i); for (int j = 0; j < mesh.clusters.size(); j++) { const FBXCluster& cluster = mesh.clusters.at(j); - - auto jointMatrix = _rig.getJointTransform(cluster.jointIndex); #if defined(SKIN_DQ) - glm::mat4 m; - glm_mat4u_mul(jointMatrix, cluster.inverseBindMatrix, m); - state.clusterTransforms[j] = Model::TransformDualQuaternion(m); + auto jointPose = _rig.getJointPose(cluster.jointIndex); + Transform jointTransform(jointPose.rot(), jointPose.scale(), jointPose.trans()); + Transform clusterTransform; + Transform::mult(clusterTransform, jointTransform, cluster.inverseBindTransform); + state.clusterTransforms[j] = Model::TransformDualQuaternion(clusterTransform); #else + auto jointMatrix = _rig.getJointTransform(cluster.jointIndex); glm_mat4u_mul(jointMatrix, cluster.inverseBindMatrix, state.clusterTransforms[j]); #endif } @@ -123,14 +124,17 @@ void CauterizedModel::updateClusterMatrices() { // as an optimization, don't build cautrizedClusterMatrices if the boneSet is empty. if (!_cauterizeBoneSet.empty()) { - +#if defined(SKIN_DQ) + AnimPose cauterizePose = _rig.getJointPose(geometry.neckJointIndex); + cauterizePose.scale() = glm::vec3(0.0001f, 0.0001f, 0.0001f); +#else static const glm::mat4 zeroScale( glm::vec4(0.0001f, 0.0f, 0.0f, 0.0f), glm::vec4(0.0f, 0.0001f, 0.0f, 0.0f), glm::vec4(0.0f, 0.0f, 0.0001f, 0.0f), glm::vec4(0.0f, 0.0f, 0.0f, 1.0f)); auto cauterizeMatrix = _rig.getJointTransform(geometry.neckJointIndex) * zeroScale; - +#endif for (int i = 0; i < _cauterizeMeshStates.size(); i++) { Model::MeshState& state = _cauterizeMeshStates[i]; const FBXMesh& mesh = geometry.meshes.at(i); @@ -138,18 +142,19 @@ void CauterizedModel::updateClusterMatrices() { for (int j = 0; j < mesh.clusters.size(); j++) { const FBXCluster& cluster = mesh.clusters.at(j); - auto jointMatrix = _rig.getJointTransform(cluster.jointIndex); - if (_cauterizeBoneSet.find(cluster.jointIndex) != _cauterizeBoneSet.end()) { - jointMatrix = cauterizeMatrix; - } - + if (_cauterizeBoneSet.find(cluster.jointIndex) == _cauterizeBoneSet.end()) { + // not cauterized so just copy the value from the non-cauterized version. + state.clusterTransforms[j] = _meshStates[i].clusterTransforms[j]; + } else { #if defined(SKIN_DQ) - glm::mat4 m; - glm_mat4u_mul(jointMatrix, cluster.inverseBindMatrix, m); - state.clusterTransforms[j] = Model::TransformDualQuaternion(m); + Transform jointTransform(cauterizePose.rot(), cauterizePose.scale(), cauterizePose.trans()); + Transform clusterTransform; + Transform::mult(clusterTransform, jointTransform, cluster.inverseBindTransform); + state.clusterTransforms[j] = Model::TransformDualQuaternion(clusterTransform); #else - glm_mat4u_mul(jointMatrix, cluster.inverseBindMatrix, state.clusterTransforms[j]); + glm_mat4u_mul(cauterizeMatrix, cluster.inverseBindMatrix, state.clusterTransforms[j]); #endif + } } } } diff --git a/libraries/render-utils/src/Model.cpp b/libraries/render-utils/src/Model.cpp index b2459d27e3..2fc4a778d9 100644 --- a/libraries/render-utils/src/Model.cpp +++ b/libraries/render-utils/src/Model.cpp @@ -1188,10 +1188,13 @@ void Model::updateClusterMatrices() { const FBXCluster& cluster = mesh.clusters.at(j); auto jointMatrix = _rig.getJointTransform(cluster.jointIndex); #if defined(SKIN_DQ) - glm::mat4 mat; - glm_mat4u_mul(jointMatrix, cluster.inverseBindMatrix, mat); - state.clusterTransforms[j] = TransformDualQuaternion(mat); + auto jointPose = _rig.getJointPose(cluster.jointIndex); + Transform jointTransform(jointPose.rot(), jointPose.scale(), jointPose.trans()); + Transform clusterTransform; + Transform::mult(clusterTransform, jointTransform, cluster.inverseBindTransform); + state.clusterTransforms[j] = Model::TransformDualQuaternion(clusterTransform); #else + auto jointMatrix = _rig.getJointTransform(cluster.jointIndex); glm_mat4u_mul(jointMatrix, cluster.inverseBindMatrix, state.clusterTransforms[j]); #endif } diff --git a/libraries/render-utils/src/Model.h b/libraries/render-utils/src/Model.h index 1461213c6f..d13a76847a 100644 --- a/libraries/render-utils/src/Model.h +++ b/libraries/render-utils/src/Model.h @@ -268,6 +268,10 @@ public: _scale.z = scale.z; _dq = DualQuaternion(rot, trans); } + TransformDualQuaternion(const Transform& transform) { + _scale = glm::vec4(transform.getScale(), 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(); } From fac0982c67ddcc5089cec483bbaf93cdd489e589 Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Thu, 4 Jan 2018 16:37:12 -0800 Subject: [PATCH 14/32] need to use sizeof(TransformType), not sizeof(mat4) --- libraries/render-utils/src/MeshPartPayload.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/render-utils/src/MeshPartPayload.cpp b/libraries/render-utils/src/MeshPartPayload.cpp index e3bbfc1c03..1907be9b42 100644 --- a/libraries/render-utils/src/MeshPartPayload.cpp +++ b/libraries/render-utils/src/MeshPartPayload.cpp @@ -377,7 +377,7 @@ void ModelMeshPartPayload::updateClusterBuffer(const std::vector& (const gpu::Byte*) clusterTransforms.data()); } else { - _clusterBuffer->setSubData(0, clusterTransforms.size() * sizeof(glm::mat4), + _clusterBuffer->setSubData(0, clusterTransforms.size() * sizeof(TransformType), (const gpu::Byte*) clusterTransforms.data()); } } From 51ab38f48415c1001b48c122ae4127232fe26940 Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Thu, 4 Jan 2018 17:32:22 -0800 Subject: [PATCH 15/32] warning fix --- libraries/render-utils/src/Model.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/libraries/render-utils/src/Model.cpp b/libraries/render-utils/src/Model.cpp index 2fc4a778d9..31e23f7660 100644 --- a/libraries/render-utils/src/Model.cpp +++ b/libraries/render-utils/src/Model.cpp @@ -1186,7 +1186,6 @@ void Model::updateClusterMatrices() { const FBXMesh& mesh = geometry.meshes.at(i); for (int j = 0; j < mesh.clusters.size(); j++) { const FBXCluster& cluster = mesh.clusters.at(j); - auto jointMatrix = _rig.getJointTransform(cluster.jointIndex); #if defined(SKIN_DQ) auto jointPose = _rig.getJointPose(cluster.jointIndex); Transform jointTransform(jointPose.rot(), jointPose.scale(), jointPose.trans()); From d08f94a74d09368bb19a7b4f5fe56f88d74f5e98 Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Mon, 8 Jan 2018 13:19:54 -0800 Subject: [PATCH 16/32] Code review feedback * Removed AnimPose::fuzzyEqual * Fixed DualQuaternion ctor --- libraries/animation/src/AnimPose.cpp | 21 --------------------- libraries/animation/src/AnimPose.h | 2 -- libraries/shared/src/DualQuaternion.cpp | 2 +- 3 files changed, 1 insertion(+), 24 deletions(-) diff --git a/libraries/animation/src/AnimPose.cpp b/libraries/animation/src/AnimPose.cpp index 29c4c46d6e..a0b8fba1da 100644 --- a/libraries/animation/src/AnimPose.cpp +++ b/libraries/animation/src/AnimPose.cpp @@ -69,27 +69,6 @@ AnimPose AnimPose::mirror() const { return AnimPose(_scale, glm::quat(_rot.w, _rot.x, -_rot.y, -_rot.z), glm::vec3(-_trans.x, _trans.y, _trans.z)); } -bool AnimPose::fuzzyEqual(const AnimPose& rhs) const { - const float SCALE_EPSILON = 0.00001f; - const float ROT_EPSILON = 0.00001f; - const float TRANS_EPSILON = 0.001f; - if ((fabsf(rhs._scale.x - _scale.x) < SCALE_EPSILON) && - (fabsf(rhs._scale.y - _scale.y) < SCALE_EPSILON) && - (fabsf(rhs._scale.z - _scale.z) < SCALE_EPSILON)) { - if ((fabsf(rhs._rot.x - _rot.x) < ROT_EPSILON) && - (fabsf(rhs._rot.y - _rot.y) < ROT_EPSILON) && - (fabsf(rhs._rot.z - _rot.z) < ROT_EPSILON) && - (fabsf(rhs._rot.w - _rot.w) < ROT_EPSILON)) { - if ((fabsf(rhs._trans.x - _trans.x) < TRANS_EPSILON) && - (fabsf(rhs._trans.y - _trans.y) < TRANS_EPSILON) && - (fabsf(rhs._trans.z - _trans.z) < TRANS_EPSILON)) { - return true; - } - } - } - return false; -} - AnimPose::operator glm::mat4() const { glm::vec3 xAxis = _rot * glm::vec3(_scale.x, 0.0f, 0.0f); glm::vec3 yAxis = _rot * glm::vec3(0.0f, _scale.y, 0.0f); diff --git a/libraries/animation/src/AnimPose.h b/libraries/animation/src/AnimPose.h index 0f29bac7ce..2df3d1f2e4 100644 --- a/libraries/animation/src/AnimPose.h +++ b/libraries/animation/src/AnimPose.h @@ -46,8 +46,6 @@ public: const glm::vec3& trans() const { return _trans; } glm::vec3& trans() { return _trans; } - bool fuzzyEqual(const AnimPose& rhs) const; - private: friend QDebug operator<<(QDebug debug, const AnimPose& pose); glm::vec3 _scale { 1.0f }; diff --git a/libraries/shared/src/DualQuaternion.cpp b/libraries/shared/src/DualQuaternion.cpp index e2a9dc9bb2..03628f8aba 100644 --- a/libraries/shared/src/DualQuaternion.cpp +++ b/libraries/shared/src/DualQuaternion.cpp @@ -28,7 +28,7 @@ DualQuaternion::DualQuaternion(const glm::vec4& real, const glm::vec4& imag) : DualQuaternion::DualQuaternion(const glm::quat& rotation, const glm::vec3& translation) { _real = rotation; - _imag = glm::quat(0, 0.5f * translation.x, 0.5f * translation.y, 0.5f * translation.z) * rotation; + _imag = glm::quat(0.0f, 0.5f * translation.x, 0.5f * translation.y, 0.5f * translation.z) * rotation; } DualQuaternion DualQuaternion::operator*(const DualQuaternion& rhs) const { From fbea22e0f0a00a99e0db008e3823aadebebbd083 Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Mon, 8 Jan 2018 18:21:23 -0800 Subject: [PATCH 17/32] Renamed imag component to dual, using the correct nomenclature Also removed incomplete test from unit tests. --- libraries/render-utils/src/Skinning.slh | 38 ++++++++++++------------ libraries/shared/src/DualQuaternion.cpp | 28 ++++++++--------- libraries/shared/src/DualQuaternion.h | 8 ++--- tests/shared/src/DualQuaternionTests.cpp | 38 ++---------------------- tests/shared/src/DualQuaternionTests.h | 1 - 5 files changed, 40 insertions(+), 73 deletions(-) diff --git a/libraries/render-utils/src/Skinning.slh b/libraries/render-utils/src/Skinning.slh index ddcdd2c71f..49d0df3d2c 100644 --- a/libraries/render-utils/src/Skinning.slh +++ b/libraries/render-utils/src/Skinning.slh @@ -24,7 +24,7 @@ layout(std140) uniform skinClusterBuffer { <@if SKIN_DQ@> -mat4 dualQuatToMat4(vec4 real, vec4 imag) { +mat4 dualQuatToMat4(vec4 real, vec4 dual) { float twoRealXSq = 2.0 * real.x * real.x; float twoRealYSq = 2.0 * real.y * real.y; float twoRealZSq = 2.0 * real.z * real.z; @@ -46,9 +46,9 @@ mat4 dualQuatToMat4(vec4 real, vec4 imag) { twoRealYZ - twoRealXW, 1 - twoRealXSq - twoRealYSq, 0.0); - vec4 col3 = vec4(2.0 * (-imag.w * real.x + imag.x * real.w - imag.y * real.z + imag.z * real.y), - 2.0 * (-imag.w * real.y + imag.x * real.z + imag.y * real.w - imag.z * real.x), - 2.0 * (-imag.w * real.z - imag.x * real.y + imag.y * real.x + imag.z * real.w), + vec4 col3 = vec4(2.0 * (-dual.w * real.x + dual.x * real.w - dual.y * real.z + dual.z * real.y), + 2.0 * (-dual.w * real.y + dual.x * real.z + dual.y * real.w - dual.z * real.x), + 2.0 * (-dual.w * real.z - dual.x * real.y + dual.y * real.x + dual.z * real.w), 1.0); return mat4(col0, col1, col2, col3); @@ -60,7 +60,7 @@ void skinPosition(ivec4 skinClusterIndex, vec4 skinClusterWeight, vec4 inPositio // linearly blend scale and dual quaternion components vec3 sAccum = vec3(0.0, 0.0, 0.0); vec4 rAccum = vec4(0.0, 0.0, 0.0, 0.0); - vec4 iAccum = vec4(0.0, 0.0, 0.0, 0.0); + vec4 dAccum = 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])]; @@ -68,7 +68,7 @@ void skinPosition(ivec4 skinClusterIndex, vec4 skinClusterWeight, vec4 inPositio vec3 scale = vec3(clusterMatrix[0]); vec4 real = clusterMatrix[1]; - vec4 imag = clusterMatrix[2]; + vec4 dual = clusterMatrix[2]; // to ensure that we rotate along the shortest arc, reverse dual quaternions with negative polarity. float dqClusterWeight = clusterWeight; @@ -78,16 +78,16 @@ void skinPosition(ivec4 skinClusterIndex, vec4 skinClusterWeight, vec4 inPositio sAccum += scale * clusterWeight; rAccum += real * dqClusterWeight; - iAccum += imag * dqClusterWeight; + dAccum += dual * dqClusterWeight; } // normalize dual quaternion float norm = length(rAccum); rAccum /= norm; - iAccum /= norm; + dAccum /= norm; // conversion from dual quaternion to 4x4 matrix. - mat4 m = dualQuatToMat4(rAccum, iAccum); + mat4 m = dualQuatToMat4(rAccum, dAccum); skinnedPosition = m * (vec4(sAccum, 1) * inPosition); } @@ -97,7 +97,7 @@ void skinPositionNormal(ivec4 skinClusterIndex, vec4 skinClusterWeight, vec4 inP // linearly blend scale and dual quaternion components vec3 sAccum = vec3(0.0, 0.0, 0.0); vec4 rAccum = vec4(0.0, 0.0, 0.0, 0.0); - vec4 iAccum = vec4(0.0, 0.0, 0.0, 0.0); + vec4 dAccum = vec4(0.0, 0.0, 0.0, 0.0); vec4 polarityReference = clusterMatrices[skinClusterIndex[0]][1]; for (int i = 0; i < INDICES_PER_VERTEX; i++) { @@ -106,7 +106,7 @@ void skinPositionNormal(ivec4 skinClusterIndex, vec4 skinClusterWeight, vec4 inP vec3 scale = vec3(clusterMatrix[0]); vec4 real = clusterMatrix[1]; - vec4 imag = clusterMatrix[2]; + vec4 dual = clusterMatrix[2]; // to ensure that we rotate along the shortest arc, reverse dual quaternions with negative polarity. float dqClusterWeight = clusterWeight; @@ -116,16 +116,16 @@ void skinPositionNormal(ivec4 skinClusterIndex, vec4 skinClusterWeight, vec4 inP sAccum += scale * clusterWeight; rAccum += real * dqClusterWeight; - iAccum += imag * dqClusterWeight; + dAccum += dual * dqClusterWeight; } // normalize dual quaternion float norm = length(rAccum); rAccum /= norm; - iAccum /= norm; + dAccum /= norm; // conversion from dual quaternion to 4x4 matrix. - mat4 m = dualQuatToMat4(rAccum, iAccum); + mat4 m = dualQuatToMat4(rAccum, dAccum); skinnedPosition = m * (vec4(sAccum, 1) * inPosition); skinnedNormal = vec3(m * vec4(inNormal, 0)); } @@ -136,7 +136,7 @@ void skinPositionNormalTangent(ivec4 skinClusterIndex, vec4 skinClusterWeight, v // linearly blend scale and dual quaternion components vec3 sAccum = vec3(0.0, 0.0, 0.0); vec4 rAccum = vec4(0.0, 0.0, 0.0, 0.0); - vec4 iAccum = vec4(0.0, 0.0, 0.0, 0.0); + vec4 dAccum = vec4(0.0, 0.0, 0.0, 0.0); vec4 polarityReference = clusterMatrices[skinClusterIndex[0]][1]; for (int i = 0; i < INDICES_PER_VERTEX; i++) { @@ -145,7 +145,7 @@ void skinPositionNormalTangent(ivec4 skinClusterIndex, vec4 skinClusterWeight, v vec3 scale = vec3(clusterMatrix[0]); vec4 real = clusterMatrix[1]; - vec4 imag = clusterMatrix[2]; + vec4 dual = clusterMatrix[2]; // to ensure that we rotate along the shortest arc, reverse dual quaternions with negative polarity. float dqClusterWeight = clusterWeight; @@ -155,16 +155,16 @@ void skinPositionNormalTangent(ivec4 skinClusterIndex, vec4 skinClusterWeight, v sAccum += scale * clusterWeight; rAccum += real * dqClusterWeight; - iAccum += imag * dqClusterWeight; + dAccum += dual * dqClusterWeight; } // normalize dual quaternion float norm = length(rAccum); rAccum /= norm; - iAccum /= norm; + dAccum /= norm; // conversion from dual quaternion to 4x4 matrix. - mat4 m = dualQuatToMat4(rAccum, iAccum); + mat4 m = dualQuatToMat4(rAccum, dAccum); skinnedPosition = m * (vec4(sAccum, 1) * inPosition); skinnedNormal = vec3(m * vec4(inNormal, 0)); skinnedTangent = vec3(m * vec4(inTangent, 0)); diff --git a/libraries/shared/src/DualQuaternion.cpp b/libraries/shared/src/DualQuaternion.cpp index 03628f8aba..2accbed2a9 100644 --- a/libraries/shared/src/DualQuaternion.cpp +++ b/libraries/shared/src/DualQuaternion.cpp @@ -12,42 +12,42 @@ #include "GLMHelpers.h" // delegating constructor -DualQuaternion::DualQuaternion() : _real(1.0f, 0.0f, 0.0f, 0.0), _imag(0.0f, 0.0f, 0.0f, 0.0f) { +DualQuaternion::DualQuaternion() : _real(1.0f, 0.0f, 0.0f, 0.0), _dual(0.0f, 0.0f, 0.0f, 0.0f) { } DualQuaternion::DualQuaternion(const glm::mat4& m) : DualQuaternion(glmExtractRotation(m), extractTranslation(m)) { } -DualQuaternion::DualQuaternion(const glm::quat& real, const glm::quat& imag) : _real(real), _imag(imag) { +DualQuaternion::DualQuaternion(const glm::quat& real, const glm::quat& dual) : _real(real), _dual(dual) { } -DualQuaternion::DualQuaternion(const glm::vec4& real, const glm::vec4& imag) : +DualQuaternion::DualQuaternion(const glm::vec4& real, const glm::vec4& dual) : _real(real.w, real.x, real.y, real.z), - _imag(imag.w, imag.x, imag.y, imag.z) { + _dual(dual.w, dual.x, dual.y, dual.z) { } DualQuaternion::DualQuaternion(const glm::quat& rotation, const glm::vec3& translation) { _real = rotation; - _imag = glm::quat(0.0f, 0.5f * translation.x, 0.5f * translation.y, 0.5f * translation.z) * rotation; + _dual = glm::quat(0.0f, 0.5f * translation.x, 0.5f * translation.y, 0.5f * translation.z) * rotation; } DualQuaternion DualQuaternion::operator*(const DualQuaternion& rhs) const { - return DualQuaternion(_real * rhs._real, _real * rhs._imag + _imag * rhs._real); + return DualQuaternion(_real * rhs._real, _real * rhs._dual + _dual * rhs._real); } DualQuaternion DualQuaternion::operator*(float scalar) const { - return DualQuaternion(_real * scalar, _imag * scalar); + return DualQuaternion(_real * scalar, _dual * scalar); } DualQuaternion DualQuaternion::operator+(const DualQuaternion& rhs) const { - return DualQuaternion(_real + rhs._real, _imag + rhs._imag); + return DualQuaternion(_real + rhs._real, _dual + rhs._dual); } glm::vec3 DualQuaternion::xformPoint(const glm::vec3& rhs) const { DualQuaternion v(glm::quat(), glm::quat(0.0f, rhs.x, rhs.y, rhs.z)); - DualQuaternion dualConj(glm::conjugate(_real), -glm::conjugate(_imag)); + DualQuaternion dualConj(glm::conjugate(_real), -glm::conjugate(_dual)); DualQuaternion result = *this * v * dualConj; - return vec3(result._imag.x, result._imag.y, result._imag.z); + return vec3(result._dual.x, result._dual.y, result._dual.z); } glm::quat DualQuaternion::getRotation() const { @@ -55,7 +55,7 @@ glm::quat DualQuaternion::getRotation() const { } glm::vec3 DualQuaternion::getTranslation() const { - glm::quat result = 2.0f * (_imag * glm::inverse(_real)); + glm::quat result = 2.0f * (_dual * glm::inverse(_real)); return glm::vec3(result.x, result.y, result.z); } @@ -65,11 +65,11 @@ glm::vec3 DualQuaternion::xformVector(const glm::vec3& rhs) const { DualQuaternion DualQuaternion::inverse() const { glm::quat invReal = glm::inverse(_real); - return DualQuaternion(invReal, - invReal * _imag * invReal); + return DualQuaternion(invReal, - invReal * _dual * invReal); } DualQuaternion DualQuaternion::conjugate() const { - return DualQuaternion(glm::conjugate(_real), glm::conjugate(_imag)); + return DualQuaternion(glm::conjugate(_real), glm::conjugate(_dual)); } float DualQuaternion::length() const { @@ -88,5 +88,5 @@ float DualQuaternion::dot(const DualQuaternion& rhs) const { } DualQuaternion DualQuaternion::operator-() const { - return DualQuaternion(-_real, -_imag); + return DualQuaternion(-_real, -_dual); } diff --git a/libraries/shared/src/DualQuaternion.h b/libraries/shared/src/DualQuaternion.h index 508570a930..709c089fdc 100644 --- a/libraries/shared/src/DualQuaternion.h +++ b/libraries/shared/src/DualQuaternion.h @@ -31,8 +31,8 @@ public: const glm::quat& real() const { return _real; } glm::quat& real() { return _real; } - const glm::quat& imag() const { return _imag; } - glm::quat& imag() { return _imag; } + const glm::quat& dual() const { return _dual; } + glm::quat& dual() { return _dual; } glm::quat getRotation() const; glm::vec3 getTranslation() const; @@ -50,12 +50,12 @@ public: protected: friend QDebug operator<<(QDebug debug, const DualQuaternion& pose); glm::quat _real; - glm::quat _imag; + glm::quat _dual; }; inline QDebug operator<<(QDebug debug, const DualQuaternion& dq) { - debug << "AnimPose, real = (" << dq._real.x << dq._real.y << dq._real.z << dq._real.w << "), imag = (" << dq._imag.x << dq._imag.y << dq._imag.z << dq._imag.w << ")"; + debug << "AnimPose, real = (" << dq._real.x << dq._real.y << dq._real.z << dq._real.w << "), dual = (" << dq._dual.x << dq._dual.y << dq._dual.z << dq._dual.w << ")"; return debug; } diff --git a/tests/shared/src/DualQuaternionTests.cpp b/tests/shared/src/DualQuaternionTests.cpp index 1c34bfbee7..fe14d9d166 100644 --- a/tests/shared/src/DualQuaternionTests.cpp +++ b/tests/shared/src/DualQuaternionTests.cpp @@ -31,11 +31,11 @@ static void quatComp(const glm::quat& q1, const glm::quat& q2) { void DualQuaternionTests::ctor() { glm::quat real = angleAxis(PI / 2.0f, Vectors::UNIT_Y); - glm::quat imag(0.0f, 1.0f, 2.0f, 3.0f); + glm::quat dual(0.0f, 1.0f, 2.0f, 3.0f); - DualQuaternion dq(real, imag); + DualQuaternion dq(real, dual); quatComp(real, dq.real()); - quatComp(imag, dq.imag()); + quatComp(dual, dq.dual()); glm::quat rotation = angleAxis(PI / 3.0f, Vectors::UNIT_X); glm::vec3 translation(1.0, 2.0f, 3.0f); @@ -113,35 +113,3 @@ void DualQuaternionTests::trans() { QCOMPARE_WITH_ABS_ERROR(t2, dq2.getTranslation(), 0.001f); QCOMPARE_WITH_ABS_ERROR(t3, dq3.getTranslation(), 0.001f); } - -// Dual Quaternion Linear Blending test -void DualQuaternionTests::dlb() { - DualQuaternion dq1(Quaternions::IDENTITY, glm::vec3()); - DualQuaternion dq2(angleAxis(PI / 2.0f, Vectors::UNIT_X), glm::vec3(0.0f, 1.0f, 0.0f)); - DualQuaternion dq2Alt(-angleAxis(PI / 2.0f, Vectors::UNIT_X), glm::vec3(0.0f, 1.0f, 0.0f)); - - qDebug() << "dq1 =" << dq1; - qDebug() << "dq2 =" << dq2; - - // linear blend between dq1 and dq2 - DualQuaternion dq3 = dq1 * 0.5f + dq2 * 0.5f; - - // alternate linear blend between dq1 and dq2 - DualQuaternion dq4 = dq1 * 0.5 + dq2Alt * 0.5f; - - qDebug() << "dq3 =" << dq3; - qDebug() << "dq4 =" << dq4; - - glm::vec3 p1(0.0f, 0.5f, -0.5f); - glm::vec3 p2(0.0f, 0.5f, 0.5f); - - glm::vec3 p3 = dq3.xformPoint(p1); - glm::vec3 p4 = dq3.xformPoint(p2); - glm::vec3 p5 = dq4.xformPoint(p1); - glm::vec3 p6 = dq4.xformPoint(p2); - - qDebug() << "p3 =" << p3; - qDebug() << "p4 =" << p4; - qDebug() << "p5 =" << p5; - qDebug() << "p6 =" << p6; -} diff --git a/tests/shared/src/DualQuaternionTests.h b/tests/shared/src/DualQuaternionTests.h index 988973b689..aa4b40cfd6 100644 --- a/tests/shared/src/DualQuaternionTests.h +++ b/tests/shared/src/DualQuaternionTests.h @@ -20,7 +20,6 @@ private slots: void mult(); void xform(); void trans(); - void dlb(); }; #endif // hifi_DualQuaternionTests_h From ad2a0310608816b7e0de13c1ae69f9bdc8a5f08d Mon Sep 17 00:00:00 2001 From: Ken Cooke Date: Tue, 9 Jan 2018 16:44:48 -0800 Subject: [PATCH 18/32] Fix bug in audio-mixer audio packet parsing. Codec string was being read as channel flag. --- assignment-client/src/audio/AudioMixerClientData.cpp | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/assignment-client/src/audio/AudioMixerClientData.cpp b/assignment-client/src/audio/AudioMixerClientData.cpp index 49453c6fc6..b7974c88fc 100644 --- a/assignment-client/src/audio/AudioMixerClientData.cpp +++ b/assignment-client/src/audio/AudioMixerClientData.cpp @@ -275,9 +275,13 @@ int AudioMixerClientData::parseData(ReceivedMessage& message) { if (micStreamIt == _audioStreams.end()) { // we don't have a mic stream yet, so add it - // read the channel flag to see if our stream is stereo or not + // hop past the sequence number that leads the packet message.seek(sizeof(quint16)); + // pull the codec string from the packet + auto codecString = message.readString(); + + // read the channel flag to see if our stream is stereo or not quint8 channelFlag; message.readPrimitive(&channelFlag); @@ -285,7 +289,7 @@ int AudioMixerClientData::parseData(ReceivedMessage& message) { auto avatarAudioStream = new AvatarAudioStream(isStereo, AudioMixer::getStaticJitterFrames()); avatarAudioStream->setupCodec(_codec, _selectedCodecName, AudioConstants::MONO); - qCDebug(audio) << "creating new AvatarAudioStream... codec:" << _selectedCodecName; + qCDebug(audio) << "creating new AvatarAudioStream... codec:" << _selectedCodecName << "channels:" << (channelFlag + 1); connect(avatarAudioStream, &InboundAudioStream::mismatchedAudioCodec, this, &AudioMixerClientData::handleMismatchAudioFormat); From c363a9281e3e1e373a3dff9bfa2765b0bcf4d81e Mon Sep 17 00:00:00 2001 From: Ken Cooke Date: Tue, 9 Jan 2018 16:54:14 -0800 Subject: [PATCH 19/32] Fix another bug in audio-mixer audio packet parsing. For SilentAudioFrame packets, numSamples was being read as channel flag. --- .../src/audio/AudioMixerClientData.cpp | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/assignment-client/src/audio/AudioMixerClientData.cpp b/assignment-client/src/audio/AudioMixerClientData.cpp index b7974c88fc..394224b8f4 100644 --- a/assignment-client/src/audio/AudioMixerClientData.cpp +++ b/assignment-client/src/audio/AudioMixerClientData.cpp @@ -281,15 +281,21 @@ int AudioMixerClientData::parseData(ReceivedMessage& message) { // pull the codec string from the packet auto codecString = message.readString(); - // read the channel flag to see if our stream is stereo or not - quint8 channelFlag; - message.readPrimitive(&channelFlag); - - bool isStereo = channelFlag == 1; + // determine if the stream is stereo or not + bool isStereo; + if (packetType == PacketType::SilentAudioFrame) { + quint16 numSilentSamples; + message.readPrimitive(&numSilentSamples); + isStereo = numSilentSamples == AudioConstants::NETWORK_FRAME_SAMPLES_STEREO; + } else { + quint8 channelFlag; + message.readPrimitive(&channelFlag); + isStereo = channelFlag == 1; + } auto avatarAudioStream = new AvatarAudioStream(isStereo, AudioMixer::getStaticJitterFrames()); avatarAudioStream->setupCodec(_codec, _selectedCodecName, AudioConstants::MONO); - qCDebug(audio) << "creating new AvatarAudioStream... codec:" << _selectedCodecName << "channels:" << (channelFlag + 1); + qCDebug(audio) << "creating new AvatarAudioStream... codec:" << _selectedCodecName << "channels:" << (isStereo ? 2 : 1); connect(avatarAudioStream, &InboundAudioStream::mismatchedAudioCodec, this, &AudioMixerClientData::handleMismatchAudioFormat); From 660032d8fb848322798b0fb8aeacd21c132be48b Mon Sep 17 00:00:00 2001 From: Ken Cooke Date: Wed, 10 Jan 2018 06:54:27 -0800 Subject: [PATCH 20/32] Fix bug in how emitAudioPacket() determines mono/stereo. Compressed bytes were counted as if audio samples. --- assignment-client/src/Agent.cpp | 2 +- libraries/audio-client/src/AudioClient.cpp | 2 +- libraries/audio/src/AbstractAudioInterface.cpp | 8 +++----- libraries/audio/src/AbstractAudioInterface.h | 2 +- 4 files changed, 6 insertions(+), 8 deletions(-) diff --git a/assignment-client/src/Agent.cpp b/assignment-client/src/Agent.cpp index 2fc905c6fd..d1c2c6597b 100644 --- a/assignment-client/src/Agent.cpp +++ b/assignment-client/src/Agent.cpp @@ -439,7 +439,7 @@ void Agent::executeScript() { encodedBuffer = audio; } - AbstractAudioInterface::emitAudioPacket(encodedBuffer.data(), encodedBuffer.size(), audioSequenceNumber, + AbstractAudioInterface::emitAudioPacket(encodedBuffer.data(), encodedBuffer.size(), audioSequenceNumber, false, audioTransform, scriptedAvatar->getWorldPosition(), glm::vec3(0), packetType, _selectedCodecName); }); diff --git a/libraries/audio-client/src/AudioClient.cpp b/libraries/audio-client/src/AudioClient.cpp index af86499101..e688b69266 100644 --- a/libraries/audio-client/src/AudioClient.cpp +++ b/libraries/audio-client/src/AudioClient.cpp @@ -1079,7 +1079,7 @@ void AudioClient::handleAudioInput(QByteArray& audioBuffer) { encodedBuffer = audioBuffer; } - emitAudioPacket(encodedBuffer.data(), encodedBuffer.size(), _outgoingAvatarAudioSequenceNumber, + emitAudioPacket(encodedBuffer.data(), encodedBuffer.size(), _outgoingAvatarAudioSequenceNumber, _isStereoInput, audioTransform, avatarBoundingBoxCorner, avatarBoundingBoxScale, packetType, _selectedCodecName); _stats.sentPacket(); diff --git a/libraries/audio/src/AbstractAudioInterface.cpp b/libraries/audio/src/AbstractAudioInterface.cpp index 4def97596f..376ecddd34 100644 --- a/libraries/audio/src/AbstractAudioInterface.cpp +++ b/libraries/audio/src/AbstractAudioInterface.cpp @@ -19,7 +19,7 @@ #include "AudioConstants.h" -void AbstractAudioInterface::emitAudioPacket(const void* audioData, size_t bytes, quint16& sequenceNumber, +void AbstractAudioInterface::emitAudioPacket(const void* audioData, size_t bytes, quint16& sequenceNumber, bool isStereo, const Transform& transform, glm::vec3 avatarBoundingBoxCorner, glm::vec3 avatarBoundingBoxScale, PacketType packetType, QString codecName) { static std::mutex _mutex; @@ -30,9 +30,6 @@ void AbstractAudioInterface::emitAudioPacket(const void* audioData, size_t bytes Locker lock(_mutex); auto audioPacket = NLPacket::create(packetType); - // FIXME - this is not a good way to determine stereoness with codecs.... - quint8 isStereo = bytes == AudioConstants::NETWORK_FRAME_BYTES_STEREO ? 1 : 0; - // write sequence number auto sequence = sequenceNumber++; audioPacket->writePrimitive(sequence); @@ -48,7 +45,8 @@ void AbstractAudioInterface::emitAudioPacket(const void* audioData, size_t bytes audioPacket->writePrimitive(numSilentSamples); } else { // set the mono/stereo byte - audioPacket->writePrimitive(isStereo); + quint8 channelFlag = isStereo ? 1 : 0; + audioPacket->writePrimitive(channelFlag); } // pack the three float positions diff --git a/libraries/audio/src/AbstractAudioInterface.h b/libraries/audio/src/AbstractAudioInterface.h index 8b48b55206..37731c31f7 100644 --- a/libraries/audio/src/AbstractAudioInterface.h +++ b/libraries/audio/src/AbstractAudioInterface.h @@ -29,7 +29,7 @@ class AbstractAudioInterface : public QObject { public: AbstractAudioInterface(QObject* parent = 0) : QObject(parent) {}; - static void emitAudioPacket(const void* audioData, size_t bytes, quint16& sequenceNumber, + static void emitAudioPacket(const void* audioData, size_t bytes, quint16& sequenceNumber, bool isStereo, const Transform& transform, glm::vec3 avatarBoundingBoxCorner, glm::vec3 avatarBoundingBoxScale, PacketType packetType, QString codecName = QString("")); From 6a2e3cc27250e01eacdcc06bfe45c1f4451d38ca Mon Sep 17 00:00:00 2001 From: Ken Cooke Date: Wed, 10 Jan 2018 15:14:18 -0800 Subject: [PATCH 21/32] Enable stereo codecs for AvatarAudioStream --- assignment-client/src/audio/AudioMixerClientData.cpp | 11 ++++++----- libraries/audio-client/src/AudioClient.cpp | 4 ++-- 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/assignment-client/src/audio/AudioMixerClientData.cpp b/assignment-client/src/audio/AudioMixerClientData.cpp index 394224b8f4..f6298ce1b9 100644 --- a/assignment-client/src/audio/AudioMixerClientData.cpp +++ b/assignment-client/src/audio/AudioMixerClientData.cpp @@ -283,7 +283,8 @@ int AudioMixerClientData::parseData(ReceivedMessage& message) { // determine if the stream is stereo or not bool isStereo; - if (packetType == PacketType::SilentAudioFrame) { + if (packetType == PacketType::SilentAudioFrame + || packetType == PacketType::ReplicatedSilentAudioFrame) { quint16 numSilentSamples; message.readPrimitive(&numSilentSamples); isStereo = numSilentSamples == AudioConstants::NETWORK_FRAME_SAMPLES_STEREO; @@ -294,8 +295,8 @@ int AudioMixerClientData::parseData(ReceivedMessage& message) { } auto avatarAudioStream = new AvatarAudioStream(isStereo, AudioMixer::getStaticJitterFrames()); - avatarAudioStream->setupCodec(_codec, _selectedCodecName, AudioConstants::MONO); - qCDebug(audio) << "creating new AvatarAudioStream... codec:" << _selectedCodecName << "channels:" << (isStereo ? 2 : 1); + avatarAudioStream->setupCodec(_codec, _selectedCodecName, isStereo ? AudioConstants::STEREO : AudioConstants::MONO); + qCDebug(audio) << "creating new AvatarAudioStream... codec:" << _selectedCodecName << "isStereo:" << isStereo; connect(avatarAudioStream, &InboundAudioStream::mismatchedAudioCodec, this, &AudioMixerClientData::handleMismatchAudioFormat); @@ -334,7 +335,7 @@ int AudioMixerClientData::parseData(ReceivedMessage& message) { #if INJECTORS_SUPPORT_CODECS injectorStream->setupCodec(_codec, _selectedCodecName, isStereo ? AudioConstants::STEREO : AudioConstants::MONO); - qCDebug(audio) << "creating new injectorStream... codec:" << _selectedCodecName; + qCDebug(audio) << "creating new injectorStream... codec:" << _selectedCodecName << "isStereo:" << isStereo; #endif auto emplaced = _audioStreams.emplace( @@ -577,7 +578,7 @@ void AudioMixerClientData::setupCodec(CodecPluginPointer codec, const QString& c auto avatarAudioStream = getAvatarAudioStream(); if (avatarAudioStream) { - avatarAudioStream->setupCodec(codec, codecName, AudioConstants::MONO); + avatarAudioStream->setupCodec(codec, codecName, avatarAudioStream->isStereo() ? AudioConstants::STEREO : AudioConstants::MONO); } #if INJECTORS_SUPPORT_CODECS diff --git a/libraries/audio-client/src/AudioClient.cpp b/libraries/audio-client/src/AudioClient.cpp index e688b69266..2e347f88df 100644 --- a/libraries/audio-client/src/AudioClient.cpp +++ b/libraries/audio-client/src/AudioClient.cpp @@ -782,7 +782,7 @@ void AudioClient::selectAudioFormat(const QString& selectedCodecName) { _selectedCodecName = selectedCodecName; - qCDebug(audioclient) << "Selected Codec:" << _selectedCodecName; + qCDebug(audioclient) << "Selected Codec:" << _selectedCodecName << "isStereoInput:" << _isStereoInput; // release any old codec encoder/decoder first... if (_codec && _encoder) { @@ -797,7 +797,7 @@ void AudioClient::selectAudioFormat(const QString& selectedCodecName) { if (_selectedCodecName == plugin->getName()) { _codec = plugin; _receivedAudioStream.setupCodec(plugin, _selectedCodecName, AudioConstants::STEREO); - _encoder = plugin->createEncoder(AudioConstants::SAMPLE_RATE, AudioConstants::MONO); + _encoder = plugin->createEncoder(AudioConstants::SAMPLE_RATE, _isStereoInput ? AudioConstants::STEREO : AudioConstants::MONO); qCDebug(audioclient) << "Selected Codec Plugin:" << _codec.get(); break; } From d225f803d0c2bc70abd4939b9efa474318053081 Mon Sep 17 00:00:00 2001 From: Nissim Hadar Date: Thu, 11 Jan 2018 12:28:44 -0800 Subject: [PATCH 22/32] Corrected default values. --- libraries/entities/src/EntityItemProperties.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/libraries/entities/src/EntityItemProperties.h b/libraries/entities/src/EntityItemProperties.h index d0dabbf5b6..e334e5f669 100644 --- a/libraries/entities/src/EntityItemProperties.h +++ b/libraries/entities/src/EntityItemProperties.h @@ -185,9 +185,9 @@ public: DEFINE_PROPERTY_REF(PROP_VOXEL_SURFACE_STYLE, VoxelSurfaceStyle, voxelSurfaceStyle, uint16_t, PolyVoxEntityItem::DEFAULT_VOXEL_SURFACE_STYLE); DEFINE_PROPERTY_REF(PROP_NAME, Name, name, QString, ENTITY_ITEM_DEFAULT_NAME); - DEFINE_PROPERTY_REF_ENUM(PROP_KEY_LIGHT_MODE, KeyLightMode, keyLightMode, uint32_t, (uint32_t)COMPONENT_MODE_ENABLED); - DEFINE_PROPERTY_REF_ENUM(PROP_SKYBOX_MODE, SkyboxMode, skyboxMode, uint32_t, (uint32_t)COMPONENT_MODE_ENABLED); - DEFINE_PROPERTY_REF_ENUM(PROP_AMBIENT_LIGHT_MODE, AmbientLightMode, ambientLightMode, uint32_t, (uint32_t)COMPONENT_MODE_ENABLED); + DEFINE_PROPERTY_REF_ENUM(PROP_KEY_LIGHT_MODE, KeyLightMode, keyLightMode, uint32_t, (uint32_t)COMPONENT_MODE_INHERIT); + DEFINE_PROPERTY_REF_ENUM(PROP_SKYBOX_MODE, SkyboxMode, skyboxMode, uint32_t, (uint32_t)COMPONENT_MODE_INHERIT); + DEFINE_PROPERTY_REF_ENUM(PROP_AMBIENT_LIGHT_MODE, AmbientLightMode, ambientLightMode, uint32_t, (uint32_t)COMPONENT_MODE_INHERIT); // This is the default mode for zone creation DEFINE_PROPERTY_REF_ENUM(PROP_HAZE_MODE, HazeMode, hazeMode, uint32_t, (uint32_t)COMPONENT_MODE_INHERIT); From 4bd09fd9afbd7e45d216cd0f182dfc51a73cc2ea Mon Sep 17 00:00:00 2001 From: Nissim Hadar Date: Thu, 11 Jan 2018 14:53:58 -0800 Subject: [PATCH 23/32] Corrected default values - for reading legacy content. --- libraries/entities/src/EntityTree.cpp | 38 +++++++++----------- libraries/networking/src/udt/PacketHeaders.h | 2 +- 2 files changed, 18 insertions(+), 22 deletions(-) diff --git a/libraries/entities/src/EntityTree.cpp b/libraries/entities/src/EntityTree.cpp index ae243623f6..50455ae6be 100644 --- a/libraries/entities/src/EntityTree.cpp +++ b/libraries/entities/src/EntityTree.cpp @@ -39,11 +39,9 @@ #include "EntityEditFilters.h" #include "EntityDynamicFactoryInterface.h" - static const quint64 DELETED_ENTITIES_EXTRA_USECS_TO_CONSIDER = USECS_PER_MSEC * 50; const float EntityTree::DEFAULT_MAX_TMP_ENTITY_LIFETIME = 60 * 60; // 1 hour - // combines the ray cast arguments into a single object class RayArgs { public: @@ -2281,28 +2279,25 @@ bool EntityTree::readFromMap(QVariantMap& map) { properties.setOwningAvatarID(myNodeID); } - // TEMPORARY fix for older content not containing these fields in the zones - if (properties.getType() == EntityTypes::EntityType::Zone) { - if (!entityMap.contains("keyLightMode")) { - properties.setKeyLightMode(COMPONENT_MODE_ENABLED); + // Fix for older content not containing these fields in the zones + int contentVersion = map["Version"].toInt(); + bool needsConversion = (contentVersion < (int)EntityVersion::ZoneLightInheritModes); + if (needsConversion && (properties.getType() == EntityTypes::EntityType::Zone)) { + // The background should be enabled if the mode is skybox + // Note that if the values are default then they are not stored in the JSON file + if (entityMap.contains("backgroundMode") && (entityMap["backgroundMode"].toString() == "skybox")) { + properties.setSkyboxMode(COMPONENT_MODE_ENABLED); + } else { + properties.setSkyboxMode(COMPONENT_MODE_INHERIT); } - if (!entityMap.contains("skyboxMode")) { - if (entityMap.contains("backgroundMode") && (entityMap["backgroundMode"].toString() == "nothing")) { - properties.setSkyboxMode(COMPONENT_MODE_INHERIT); - } else { - // Either the background mode field is missing (shouldn't happen) or the background mode is "skybox" - properties.setSkyboxMode(COMPONENT_MODE_ENABLED); + // The legacy version had no keylight/ambient modes - these are always on + properties.setKeyLightMode(COMPONENT_MODE_ENABLED); + properties.setAmbientLightMode(COMPONENT_MODE_ENABLED); - // Copy the skybox URL if the ambient URL is empty, as this is the legacy behaviour - if (properties.getAmbientLight().getAmbientURL() == "") { - properties.getAmbientLight().setAmbientURL(properties.getSkybox().getURL()); - } - } - } - - if (!entityMap.contains("ambientLightMode")) { - properties.setAmbientLightMode(COMPONENT_MODE_ENABLED); + // Copy the skybox URL if the ambient URL is empty, as this is the legacy behaviour + if (properties.getAmbientLight().getAmbientURL() == "") { + properties.getAmbientLight().setAmbientURL(properties.getSkybox().getURL()); } } @@ -2312,6 +2307,7 @@ bool EntityTree::readFromMap(QVariantMap& map) { success = false; } } + return success; } diff --git a/libraries/networking/src/udt/PacketHeaders.h b/libraries/networking/src/udt/PacketHeaders.h index 7eafbbccf5..deddeb4153 100644 --- a/libraries/networking/src/udt/PacketHeaders.h +++ b/libraries/networking/src/udt/PacketHeaders.h @@ -202,7 +202,7 @@ enum class EntityVersion : PacketVersion { HazeEffect, StaticCertJsonVersionOne, OwnershipChallengeFix, - ZoneLightInheritModes, + ZoneLightInheritModes = 82, ZoneStageRemoved }; From 311e95e0d0bbfed4d84324aa30d505c245f4ff06 Mon Sep 17 00:00:00 2001 From: Nissim Hadar Date: Thu, 11 Jan 2018 14:57:22 -0800 Subject: [PATCH 24/32] Corrected default values - for reading legacy content. --- libraries/entities/src/EntityItemProperties.h | 2 -- 1 file changed, 2 deletions(-) diff --git a/libraries/entities/src/EntityItemProperties.h b/libraries/entities/src/EntityItemProperties.h index e334e5f669..c3d04dc7ad 100644 --- a/libraries/entities/src/EntityItemProperties.h +++ b/libraries/entities/src/EntityItemProperties.h @@ -188,8 +188,6 @@ public: DEFINE_PROPERTY_REF_ENUM(PROP_KEY_LIGHT_MODE, KeyLightMode, keyLightMode, uint32_t, (uint32_t)COMPONENT_MODE_INHERIT); DEFINE_PROPERTY_REF_ENUM(PROP_SKYBOX_MODE, SkyboxMode, skyboxMode, uint32_t, (uint32_t)COMPONENT_MODE_INHERIT); DEFINE_PROPERTY_REF_ENUM(PROP_AMBIENT_LIGHT_MODE, AmbientLightMode, ambientLightMode, uint32_t, (uint32_t)COMPONENT_MODE_INHERIT); - - // This is the default mode for zone creation DEFINE_PROPERTY_REF_ENUM(PROP_HAZE_MODE, HazeMode, hazeMode, uint32_t, (uint32_t)COMPONENT_MODE_INHERIT); DEFINE_PROPERTY_GROUP(Skybox, skybox, SkyboxPropertyGroup); From 7c5085bd24d470f8f8f3f9d03091dc875d4aeb0e Mon Sep 17 00:00:00 2001 From: Ken Cooke Date: Thu, 11 Jan 2018 16:10:50 -0800 Subject: [PATCH 25/32] Handle stereo changes while active, by restarting the codec on both ends --- assignment-client/src/audio/AudioMixerClientData.cpp | 1 + assignment-client/src/audio/AvatarAudioStream.cpp | 10 ++++++++++ libraries/audio-client/src/AudioClient.cpp | 11 ++++++++++- 3 files changed, 21 insertions(+), 1 deletion(-) diff --git a/assignment-client/src/audio/AudioMixerClientData.cpp b/assignment-client/src/audio/AudioMixerClientData.cpp index f6298ce1b9..2560f43337 100644 --- a/assignment-client/src/audio/AudioMixerClientData.cpp +++ b/assignment-client/src/audio/AudioMixerClientData.cpp @@ -579,6 +579,7 @@ void AudioMixerClientData::setupCodec(CodecPluginPointer codec, const QString& c auto avatarAudioStream = getAvatarAudioStream(); if (avatarAudioStream) { avatarAudioStream->setupCodec(codec, codecName, avatarAudioStream->isStereo() ? AudioConstants::STEREO : AudioConstants::MONO); + qCDebug(audio) << "setting AvatarAudioStream... codec:" << _selectedCodecName << "isStereo:" << avatarAudioStream->isStereo(); } #if INJECTORS_SUPPORT_CODECS diff --git a/assignment-client/src/audio/AvatarAudioStream.cpp b/assignment-client/src/audio/AvatarAudioStream.cpp index 1e0c3ed9e6..42495b4dd0 100644 --- a/assignment-client/src/audio/AvatarAudioStream.cpp +++ b/assignment-client/src/audio/AvatarAudioStream.cpp @@ -11,6 +11,7 @@ #include +#include "AudioLogging.h" #include "AvatarAudioStream.h" AvatarAudioStream::AvatarAudioStream(bool isStereo, int numStaticJitterFrames) : @@ -41,6 +42,15 @@ int AvatarAudioStream::parseStreamProperties(PacketType type, const QByteArray& _ringBuffer.resizeForFrameSize(isStereo ? AudioConstants::NETWORK_FRAME_SAMPLES_STEREO : AudioConstants::NETWORK_FRAME_SAMPLES_PER_CHANNEL); + // restart the codec + if (_codec) { + if (_decoder) { + _codec->releaseDecoder(_decoder); + } + _decoder = _codec->createDecoder(AudioConstants::SAMPLE_RATE, isStereo ? AudioConstants::STEREO : AudioConstants::MONO); + } + qCDebug(audio) << "resetting AvatarAudioStream... codec:" << _selectedCodecName << "isStereo:" << isStereo; + _isStereo = isStereo; } diff --git a/libraries/audio-client/src/AudioClient.cpp b/libraries/audio-client/src/AudioClient.cpp index 2e347f88df..c274f53dca 100644 --- a/libraries/audio-client/src/AudioClient.cpp +++ b/libraries/audio-client/src/AudioClient.cpp @@ -1382,7 +1382,16 @@ void AudioClient::setIsStereoInput(bool isStereoInput) { _desiredInputFormat.setChannelCount(1); } - // change in channel count for desired input format, restart the input device + // restart the codec + if (_codec) { + if (_encoder) { + _codec->releaseEncoder(_encoder); + } + _encoder = _codec->createEncoder(AudioConstants::SAMPLE_RATE, _isStereoInput ? AudioConstants::STEREO : AudioConstants::MONO); + } + qCDebug(audioclient) << "Reset Codec:" << _selectedCodecName << "isStereoInput:" << _isStereoInput; + + // restart the input device switchInputToAudioDevice(_inputDeviceInfo); } } From 86cfeac95c4561ebfef13462ba79082771ace832 Mon Sep 17 00:00:00 2001 From: Nissim Hadar Date: Thu, 11 Jan 2018 16:39:44 -0800 Subject: [PATCH 26/32] Moved variable setting to outside of loop. --- libraries/entities/src/EntityTree.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/libraries/entities/src/EntityTree.cpp b/libraries/entities/src/EntityTree.cpp index 50455ae6be..b39d88bd46 100644 --- a/libraries/entities/src/EntityTree.cpp +++ b/libraries/entities/src/EntityTree.cpp @@ -2235,6 +2235,10 @@ bool EntityTree::writeToMap(QVariantMap& entityDescription, OctreeElementPointer } bool EntityTree::readFromMap(QVariantMap& map) { + // These are needed to deal with older content (before adding inheritance modes) + int contentVersion = map["Version"].toInt(); + bool needsConversion = (contentVersion < (int)EntityVersion::ZoneLightInheritModes); + // map will have a top-level list keyed as "Entities". This will be extracted // and iterated over. Each member of this list is converted to a QVariantMap, then // to a QScriptValue, and then to EntityItemProperties. These properties are used @@ -2280,8 +2284,6 @@ bool EntityTree::readFromMap(QVariantMap& map) { } // Fix for older content not containing these fields in the zones - int contentVersion = map["Version"].toInt(); - bool needsConversion = (contentVersion < (int)EntityVersion::ZoneLightInheritModes); if (needsConversion && (properties.getType() == EntityTypes::EntityType::Zone)) { // The background should be enabled if the mode is skybox // Note that if the values are default then they are not stored in the JSON file From db6e70cb249351833591773ee7a457558bfcd8bb Mon Sep 17 00:00:00 2001 From: Ken Cooke Date: Thu, 11 Jan 2018 18:14:16 -0800 Subject: [PATCH 27/32] Fix bug when calling switchInputToAudioDevice() with the active device. Pass QAudioDeviceInfo by value to avoid getting clobbered. --- libraries/audio-client/src/AudioClient.cpp | 2 +- libraries/audio-client/src/AudioClient.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/libraries/audio-client/src/AudioClient.cpp b/libraries/audio-client/src/AudioClient.cpp index c274f53dca..513ccb00e9 100644 --- a/libraries/audio-client/src/AudioClient.cpp +++ b/libraries/audio-client/src/AudioClient.cpp @@ -1427,7 +1427,7 @@ void AudioClient::outputFormatChanged() { _receivedAudioStream.outputFormatChanged(_outputFormat.sampleRate(), OUTPUT_CHANNEL_COUNT); } -bool AudioClient::switchInputToAudioDevice(const QAudioDeviceInfo& inputDeviceInfo, bool isShutdownRequest) { +bool AudioClient::switchInputToAudioDevice(const QAudioDeviceInfo inputDeviceInfo, bool isShutdownRequest) { qCDebug(audioclient) << __FUNCTION__ << "inputDeviceInfo: [" << inputDeviceInfo.deviceName() << "]"; bool supportedFormat = false; diff --git a/libraries/audio-client/src/AudioClient.h b/libraries/audio-client/src/AudioClient.h index 0ceb9c4dc3..c8db742ca2 100644 --- a/libraries/audio-client/src/AudioClient.h +++ b/libraries/audio-client/src/AudioClient.h @@ -378,7 +378,7 @@ private: void handleLocalEchoAndReverb(QByteArray& inputByteArray); - bool switchInputToAudioDevice(const QAudioDeviceInfo& inputDeviceInfo, bool isShutdownRequest = false); + bool switchInputToAudioDevice(const QAudioDeviceInfo inputDeviceInfo, bool isShutdownRequest = false); bool switchOutputToAudioDevice(const QAudioDeviceInfo& outputDeviceInfo, bool isShutdownRequest = false); // Callback acceleration dependent calculations From dfc8c86571397146c3dd53db06ed7fe9586f8bc2 Mon Sep 17 00:00:00 2001 From: Ken Cooke Date: Thu, 11 Jan 2018 18:22:34 -0800 Subject: [PATCH 28/32] Fix bug when calling switchOutputToAudioDevice() with the active device. Pass QAudioDeviceInfo by value to avoid getting clobbered. --- libraries/audio-client/src/AudioClient.cpp | 2 +- libraries/audio-client/src/AudioClient.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/libraries/audio-client/src/AudioClient.cpp b/libraries/audio-client/src/AudioClient.cpp index 513ccb00e9..579910d9f7 100644 --- a/libraries/audio-client/src/AudioClient.cpp +++ b/libraries/audio-client/src/AudioClient.cpp @@ -1610,7 +1610,7 @@ void AudioClient::outputNotify() { } } -bool AudioClient::switchOutputToAudioDevice(const QAudioDeviceInfo& outputDeviceInfo, bool isShutdownRequest) { +bool AudioClient::switchOutputToAudioDevice(const QAudioDeviceInfo outputDeviceInfo, bool isShutdownRequest) { qCDebug(audioclient) << "AudioClient::switchOutputToAudioDevice() outputDeviceInfo: [" << outputDeviceInfo.deviceName() << "]"; bool supportedFormat = false; diff --git a/libraries/audio-client/src/AudioClient.h b/libraries/audio-client/src/AudioClient.h index c8db742ca2..0643b8e52a 100644 --- a/libraries/audio-client/src/AudioClient.h +++ b/libraries/audio-client/src/AudioClient.h @@ -379,7 +379,7 @@ private: void handleLocalEchoAndReverb(QByteArray& inputByteArray); bool switchInputToAudioDevice(const QAudioDeviceInfo inputDeviceInfo, bool isShutdownRequest = false); - bool switchOutputToAudioDevice(const QAudioDeviceInfo& outputDeviceInfo, bool isShutdownRequest = false); + bool switchOutputToAudioDevice(const QAudioDeviceInfo outputDeviceInfo, bool isShutdownRequest = false); // Callback acceleration dependent calculations int calculateNumberOfInputCallbackBytes(const QAudioFormat& format) const; From a1f25bf49c3f60b41fa91095250f4310ed1f137c Mon Sep 17 00:00:00 2001 From: Nissim Hadar Date: Thu, 11 Jan 2018 19:35:13 -0800 Subject: [PATCH 29/32] Moved variable setting to outside of loop. Added copy of ambient URL. --- libraries/entities/src/EntityTree.cpp | 15 +++++++++------ libraries/networking/src/udt/PacketHeaders.h | 2 ++ 2 files changed, 11 insertions(+), 6 deletions(-) diff --git a/libraries/entities/src/EntityTree.cpp b/libraries/entities/src/EntityTree.cpp index b39d88bd46..b7b560290e 100644 --- a/libraries/entities/src/EntityTree.cpp +++ b/libraries/entities/src/EntityTree.cpp @@ -2285,22 +2285,25 @@ bool EntityTree::readFromMap(QVariantMap& map) { // Fix for older content not containing these fields in the zones if (needsConversion && (properties.getType() == EntityTypes::EntityType::Zone)) { + // The ambient URL has been moved from "keyLight" to "ambientLight" + properties.getAmbientLight().setAmbientURL(entityMap["ambientURL"].toString()); + // The background should be enabled if the mode is skybox // Note that if the values are default then they are not stored in the JSON file if (entityMap.contains("backgroundMode") && (entityMap["backgroundMode"].toString() == "skybox")) { properties.setSkyboxMode(COMPONENT_MODE_ENABLED); - } else { + + // Copy the skybox URL if the ambient URL is empty, as this is the legacy behaviour + if (properties.getAmbientLight().getAmbientURL() == "") { + properties.getAmbientLight().setAmbientURL(properties.getSkybox().getURL()); + } + } else { properties.setSkyboxMode(COMPONENT_MODE_INHERIT); } // The legacy version had no keylight/ambient modes - these are always on properties.setKeyLightMode(COMPONENT_MODE_ENABLED); properties.setAmbientLightMode(COMPONENT_MODE_ENABLED); - - // Copy the skybox URL if the ambient URL is empty, as this is the legacy behaviour - if (properties.getAmbientLight().getAmbientURL() == "") { - properties.getAmbientLight().setAmbientURL(properties.getSkybox().getURL()); - } } EntityItemPointer entity = addEntity(entityItemID, properties); diff --git a/libraries/networking/src/udt/PacketHeaders.h b/libraries/networking/src/udt/PacketHeaders.h index deddeb4153..acc1ff01db 100644 --- a/libraries/networking/src/udt/PacketHeaders.h +++ b/libraries/networking/src/udt/PacketHeaders.h @@ -196,6 +196,8 @@ void sendWrongProtocolVersionsSignature(bool sendWrongVersion); /// for debuggin uint qHash(const PacketType& key, uint seed); QDebug operator<<(QDebug debug, const PacketType& type); +// Due to the different legacy behaviour, we need special processing for domains that were created before +// the zone inheritance modes were added. These have version numbers up to 80 enum class EntityVersion : PacketVersion { StrokeColorProperty = 0, HasDynamicOwnershipTests, From 50a030b68b8d8f06a42be7e4f5ca91cb478febe6 Mon Sep 17 00:00:00 2001 From: Nissim Hadar Date: Fri, 12 Jan 2018 00:22:06 -0800 Subject: [PATCH 30/32] Corrected copy of ambient URL. --- libraries/entities/src/EntityTree.cpp | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/libraries/entities/src/EntityTree.cpp b/libraries/entities/src/EntityTree.cpp index b7b560290e..907426c922 100644 --- a/libraries/entities/src/EntityTree.cpp +++ b/libraries/entities/src/EntityTree.cpp @@ -2242,7 +2242,7 @@ bool EntityTree::readFromMap(QVariantMap& map) { // map will have a top-level list keyed as "Entities". This will be extracted // and iterated over. Each member of this list is converted to a QVariantMap, then // to a QScriptValue, and then to EntityItemProperties. These properties are used - // to add the new entity to the EnitytTree. + // to add the new entity to the EntityTree. QVariantList entitiesQList = map["Entities"].toList(); QScriptEngine scriptEngine; @@ -2286,7 +2286,10 @@ bool EntityTree::readFromMap(QVariantMap& map) { // Fix for older content not containing these fields in the zones if (needsConversion && (properties.getType() == EntityTypes::EntityType::Zone)) { // The ambient URL has been moved from "keyLight" to "ambientLight" - properties.getAmbientLight().setAmbientURL(entityMap["ambientURL"].toString()); + if (entityMap.contains("keyLight")) { + QVariantMap keyLightObject = entityMap["keyLight"].toMap(); + properties.getAmbientLight().setAmbientURL(keyLightObject["ambientURL"].toString()); + } // The background should be enabled if the mode is skybox // Note that if the values are default then they are not stored in the JSON file From 6920e576137e8f03e0a1b4b1ad57e581aac37c54 Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Fri, 12 Jan 2018 13:31:58 -0800 Subject: [PATCH 31/32] Fix 'Browse Avatars' button from Avatar Settings --- .../qml/dialogs/preferences/AvatarBrowser.qml | 85 -------------- .../dialogs/preferences/AvatarPreference.qml | 18 +-- .../preferences/TabletAvatarBrowser.qml | 111 ------------------ interface/src/Application.cpp | 9 ++ interface/src/Application.h | 1 + 5 files changed, 11 insertions(+), 213 deletions(-) delete mode 100644 interface/resources/qml/dialogs/preferences/AvatarBrowser.qml delete mode 100644 interface/resources/qml/hifi/tablet/tabletWindows/preferences/TabletAvatarBrowser.qml diff --git a/interface/resources/qml/dialogs/preferences/AvatarBrowser.qml b/interface/resources/qml/dialogs/preferences/AvatarBrowser.qml deleted file mode 100644 index 5949adffca..0000000000 --- a/interface/resources/qml/dialogs/preferences/AvatarBrowser.qml +++ /dev/null @@ -1,85 +0,0 @@ -// -// AvatarBrowser.qml -// -// Created by Bradley Austin Davis on 30 Aug 2015 -// Copyright 2015 High Fidelity, Inc. -// -// Distributed under the Apache License, Version 2.0. -// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -// - -import QtQuick 2.5 -import QtQuick.Controls 1.4 -import QtWebChannel 1.0 -import QtWebEngine 1.2 - -import "../../windows" -import "../../controls-uit" -import "../../styles-uit" - -Window { - id: root - HifiConstants { id: hifi } - width: 900; height: 700 - resizable: true - modality: Qt.ApplicationModal - - Item { - anchors.fill: parent - - property bool keyboardEnabled: false - property bool keyboardRaised: true - property bool punctuationMode: false - - BaseWebView { - id: webview - url: Account.metaverseServerURL + "/marketplace?category=avatars" - focus: true - - anchors { - top: parent.top - left: parent.left - right: parent.right - bottom: keyboard.top - } - - // Create a global EventBridge object for raiseAndLowerKeyboard. - WebEngineScript { - id: createGlobalEventBridge - sourceCode: eventBridgeJavaScriptToInject - injectionPoint: WebEngineScript.DocumentCreation - worldId: WebEngineScript.MainWorld - } - - // Detect when may want to raise and lower keyboard. - WebEngineScript { - id: raiseAndLowerKeyboard - injectionPoint: WebEngineScript.Deferred - sourceUrl: resourceDirectoryUrl + "html/raiseAndLowerKeyboard.js" - worldId: WebEngineScript.MainWorld - } - - userScripts: [ createGlobalEventBridge, raiseAndLowerKeyboard ] - - Component.onCompleted: { - webChannel.registerObject("eventBridge", eventBridge); - webChannel.registerObject("eventBridgeWrapper", eventBridgeWrapper); - } - } - - Keyboard { - id: keyboard - raised: parent.keyboardEnabled && parent.keyboardRaised - numeric: parent.punctuationMode - anchors { - left: parent.left - right: parent.right - bottom: parent.bottom - } - } - - Component.onCompleted: { - keyboardEnabled = HMD.active; - } - } -} diff --git a/interface/resources/qml/dialogs/preferences/AvatarPreference.qml b/interface/resources/qml/dialogs/preferences/AvatarPreference.qml index b27827d9d7..0efc3776b3 100644 --- a/interface/resources/qml/dialogs/preferences/AvatarPreference.qml +++ b/interface/resources/qml/dialogs/preferences/AvatarPreference.qml @@ -99,25 +99,9 @@ Preference { leftMargin: dataTextField.acceptableInput ? hifi.dimensions.contentSpacing.x : 0 } onClicked: { - if (typeof desktop !== "undefined") { - // Load dialog via OffscreenUi so that JavaScript EventBridge is available. - root.browser = OffscreenUi.load("dialogs/preferences/AvatarBrowser.qml"); - root.browser.windowDestroyed.connect(function(){ - root.browser = null; - }); - } else { - root.browser = tabletAvatarBrowserBuilder.createObject(tabletRoot); - - // Make dialog modal. - tabletRoot.openModal = root.browser; - } + ApplicationInterface.loadAvatarBrowser(); } } - Component { - id: tabletAvatarBrowserBuilder; - TabletAvatarBrowser { } - } - } } diff --git a/interface/resources/qml/hifi/tablet/tabletWindows/preferences/TabletAvatarBrowser.qml b/interface/resources/qml/hifi/tablet/tabletWindows/preferences/TabletAvatarBrowser.qml deleted file mode 100644 index 2ea12f1d3d..0000000000 --- a/interface/resources/qml/hifi/tablet/tabletWindows/preferences/TabletAvatarBrowser.qml +++ /dev/null @@ -1,111 +0,0 @@ -// -// TabletAvatarBrowser.qml -// -// Created by David Rowe on 14 Mar 2017 -// Copyright 2017 High Fidelity, Inc. -// -// Distributed under the Apache License, Version 2.0. -// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -// - -import QtQuick 2.5 -import QtQuick.Controls 1.4 -import QtWebChannel 1.0 -import QtWebEngine 1.2 - -import "../../../../windows" -import "../../../../controls-uit" -import "../../../../styles-uit" - -Item { - id: root - objectName: "ModelBrowserDialog" - - property string title: "Attachment Model" - - property bool keyboardEnabled: false - property bool keyboardRaised: false - property bool punctuationMode: false - - anchors.fill: parent - - BaseWebView { - id: webview - url: (Account.metaverseServerURL + "/marketplace?category=avatars") - focus: true - - anchors { - top: parent.top - left: parent.left - right: parent.right - bottom: footer.top - } - - // Create a global EventBridge object for raiseAndLowerKeyboard. - WebEngineScript { - id: createGlobalEventBridge - sourceCode: eventBridgeJavaScriptToInject - injectionPoint: WebEngineScript.DocumentCreation - worldId: WebEngineScript.MainWorld - } - - // Detect when may want to raise and lower keyboard. - WebEngineScript { - id: raiseAndLowerKeyboard - injectionPoint: WebEngineScript.Deferred - sourceUrl: resourceDirectoryUrl + "html/raiseAndLowerKeyboard.js" - worldId: WebEngineScript.MainWorld - } - - userScripts: [ createGlobalEventBridge, raiseAndLowerKeyboard ] - - Component.onCompleted: { - webChannel.registerObject("eventBridge", eventBridge); - webChannel.registerObject("eventBridgeWrapper", eventBridgeWrapper); - } - } - - Rectangle { - id: footer - height: 40 - - anchors { - left: parent.left - right: parent.right - bottom: keyboard.top - } - - color: hifi.colors.baseGray - - Row { - anchors { - verticalCenter: parent.verticalCenter - right: parent.right - rightMargin: hifi.dimensions.contentMargin.x - } - - Button { - text: "Cancel" - color: hifi.buttons.white - onClicked: root.destroy(); - } - } - } - - Keyboard { - id: keyboard - - raised: parent.keyboardEnabled && parent.keyboardRaised - numeric: parent.punctuationMode - - anchors { - left: parent.left - right: parent.right - bottom: parent.bottom - } - } - - Component.onCompleted: { - keyboardEnabled = HMD.active; - } -} diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index f9a994124b..e081e80360 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -6807,6 +6807,15 @@ void Application::loadAddAvatarBookmarkDialog() const { avatarBookmarks->addBookmark(); } +void Application::loadAvatarBrowser() const { + auto tablet = dynamic_cast(DependencyManager::get()->getTablet("com.highfidelity.interface.tablet.system")); + // construct the url to the marketplace item + QString url = NetworkingConstants::METAVERSE_SERVER_URL().toString() + "/marketplace?category=avatars"; + QString MARKETPLACES_INJECT_SCRIPT_PATH = "file:///" + qApp->applicationDirPath() + "/scripts/system/html/js/marketplacesInject.js"; + tablet->gotoWebScreen(url, MARKETPLACES_INJECT_SCRIPT_PATH); + DependencyManager::get()->openTablet(); +} + void Application::takeSnapshot(bool notify, bool includeAnimated, float aspectRatio) { postLambdaEvent([notify, includeAnimated, aspectRatio, this] { // Get a screenshot and save it diff --git a/interface/src/Application.h b/interface/src/Application.h index b01e998a21..effb35caee 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -309,6 +309,7 @@ public slots: void toggleEntityScriptServerLogDialog(); Q_INVOKABLE void showAssetServerWidget(QString filePath = ""); Q_INVOKABLE void loadAddAvatarBookmarkDialog() const; + Q_INVOKABLE void loadAvatarBrowser() const; Q_INVOKABLE SharedSoundPointer getSampleSound() const; void showDialog(const QUrl& widgetUrl, const QUrl& tabletUrl, const QString& name) const; From 522007af9dbd93511375e9d32aa16d607ee40258 Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Fri, 12 Jan 2018 14:58:14 -0800 Subject: [PATCH 32/32] Fix social media sharing buttons on Snapshot app --- interface/resources/qml/controls/TabletWebScreen.qml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/interface/resources/qml/controls/TabletWebScreen.qml b/interface/resources/qml/controls/TabletWebScreen.qml index 501e321f0d..bb037ad478 100644 --- a/interface/resources/qml/controls/TabletWebScreen.qml +++ b/interface/resources/qml/controls/TabletWebScreen.qml @@ -41,9 +41,9 @@ Item { onNewViewRequestedCallback: { // desktop is not defined for web-entities or tablet if (typeof desktop !== "undefined") { - desktop.openBrowserWindow(request, profile); + desktop.openBrowserWindow(request, webViewCoreProfile); } else { - tabletRoot.openBrowserWindow(request, profile); + tabletRoot.openBrowserWindow(request, webViewCoreProfile); } }