diff --git a/interface/src/avatar/Avatar.cpp b/interface/src/avatar/Avatar.cpp index e8ac93234c..3f670709ff 100644 --- a/interface/src/avatar/Avatar.cpp +++ b/interface/src/avatar/Avatar.cpp @@ -126,6 +126,7 @@ void Avatar::simulate(float deltaTime) { _skeletonModel.simulate(deltaTime); } _skeletonModel.simulate(deltaTime, _hasNewJointRotations); + simulateAttachments(deltaTime, _hasNewJointRotations); _hasNewJointRotations = false; glm::vec3 headPosition = _position; @@ -338,6 +339,7 @@ void Avatar::renderBody(RenderMode renderMode, float glowLevel) { return; } _skeletonModel.render(1.0f, modelRenderMode); + renderAttachments(modelRenderMode); getHand()->render(false); } getHead()->render(1.0f, modelRenderMode); @@ -347,6 +349,32 @@ bool Avatar::shouldRenderHead(const glm::vec3& cameraPosition, RenderMode render return true; } +void Avatar::simulateAttachments(float deltaTime, bool fullUpdate) { + if (!fullUpdate) { + return; // only simulate if we have new data + } + for (int i = 0; i < _attachmentModels.size(); i++) { + const AttachmentData& attachment = _attachmentData.at(i); + Model* model = _attachmentModels.at(i); + int jointIndex = getJointIndex(attachment.jointName); + glm::vec3 jointPosition; + glm::quat jointRotation; + if (_skeletonModel.getJointPosition(jointIndex, jointPosition) && + _skeletonModel.getJointRotation(jointIndex, jointRotation)) { + model->setTranslation(jointPosition + jointRotation * attachment.translation * _skeletonModel.getScale()); + model->setRotation(jointRotation * attachment.rotation); + model->setScale(_skeletonModel.getScale() * attachment.scale); + model->simulate(deltaTime); + } + } +} + +void Avatar::renderAttachments(Model::RenderMode renderMode) { + foreach (Model* model, _attachmentModels) { + model->render(1.0f, renderMode); + } +} + void Avatar::updateJointMappings() { // no-op; joint mappings come from skeleton model } @@ -667,6 +695,25 @@ void Avatar::setSkeletonModelURL(const QUrl& skeletonModelURL) { _skeletonModel.setURL(_skeletonModelURL, DEFAULT_SKELETON_MODEL_URL, true, !isMyAvatar()); } +void Avatar::setAttachmentData(const QVector& attachmentData) { + AvatarData::setAttachmentData(attachmentData); + + // make sure we have as many models as attachments + while (_attachmentModels.size() < attachmentData.size()) { + Model* model = new Model(this); + model->init(); + _attachmentModels.append(model); + } + while (_attachmentModels.size() > attachmentData.size()) { + delete _attachmentModels.takeLast(); + } + + // update the urls + for (int i = 0; i < attachmentData.size(); i++) { + _attachmentModels[i]->setURL(attachmentData.at(i).modelURL); + } +} + void Avatar::setDisplayName(const QString& displayName) { AvatarData::setDisplayName(displayName); _displayNameBoundingRect = textRenderer(DISPLAYNAME)->metrics().tightBoundingRect(displayName); diff --git a/interface/src/avatar/Avatar.h b/interface/src/avatar/Avatar.h index 4263e606a5..4b0bf85ea1 100755 --- a/interface/src/avatar/Avatar.h +++ b/interface/src/avatar/Avatar.h @@ -131,6 +131,7 @@ public: virtual void setFaceModelURL(const QUrl& faceModelURL); virtual void setSkeletonModelURL(const QUrl& skeletonModelURL); + virtual void setAttachmentData(const QVector& attachmentData); virtual void setDisplayName(const QString& displayName); virtual void setBillboard(const QByteArray& billboard); @@ -160,6 +161,7 @@ signals: protected: SkeletonModel _skeletonModel; + QVector _attachmentModels; float _bodyYawDelta; glm::vec3 _velocity; float _leanScale; @@ -188,6 +190,9 @@ protected: virtual void renderBody(RenderMode renderMode, float glowLevel = 0.0f); virtual bool shouldRenderHead(const glm::vec3& cameraPosition, RenderMode renderMode) const; + void simulateAttachments(float deltaTime, bool fullUpdate = true); + void renderAttachments(Model::RenderMode renderMode); + virtual void updateJointMappings(); private: diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 6af7235117..b52b77f691 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -244,6 +244,7 @@ void MyAvatar::simulate(float deltaTime) { getHand()->simulate(deltaTime, true); _skeletonModel.simulate(deltaTime); + simulateAttachments(deltaTime); // copy out the skeleton joints from the model _jointData.resize(_skeletonModel.getJointStateCount()); @@ -657,7 +658,8 @@ void MyAvatar::renderBody(RenderMode renderMode, float glowLevel) { Model::RenderMode modelRenderMode = (renderMode == SHADOW_RENDER_MODE) ? Model::SHADOW_RENDER_MODE : Model::DEFAULT_RENDER_MODE; _skeletonModel.render(1.0f, modelRenderMode); - + renderAttachments(modelRenderMode); + // Render head so long as the camera isn't inside it if (shouldRenderHead(Application::getInstance()->getCamera()->getPosition(), renderMode)) { getHead()->render(1.0f, modelRenderMode); diff --git a/interface/src/renderer/Model.cpp b/interface/src/renderer/Model.cpp index a177783955..db86b37d0d 100644 --- a/interface/src/renderer/Model.cpp +++ b/interface/src/renderer/Model.cpp @@ -509,6 +509,24 @@ void Model::setURL(const QUrl& url, const QUrl& fallback, bool retainCurrent, bo } } +bool Model::getJointPosition(int jointIndex, glm::vec3& position) const { + if (jointIndex == -1 || _jointStates.isEmpty()) { + return false; + } + position = _translation + extractTranslation(_jointStates[jointIndex].transform); + return true; +} + +bool Model::getJointRotation(int jointIndex, glm::quat& rotation, bool fromBind) const { + if (jointIndex == -1 || _jointStates.isEmpty()) { + return false; + } + rotation = _jointStates[jointIndex].combinedRotation * + (fromBind ? _geometry->getFBXGeometry().joints[jointIndex].inverseBindRotation : + _geometry->getFBXGeometry().joints[jointIndex].inverseDefaultRotation); + return true; +} + void Model::clearShapes() { for (int i = 0; i < _jointShapes.size(); ++i) { delete _jointShapes[i]; @@ -957,24 +975,6 @@ void Model::maybeUpdateEyeRotation(const JointState& parentState, const FBXJoint // nothing by default } -bool Model::getJointPosition(int jointIndex, glm::vec3& position) const { - if (jointIndex == -1 || _jointStates.isEmpty()) { - return false; - } - position = _translation + extractTranslation(_jointStates[jointIndex].transform); - return true; -} - -bool Model::getJointRotation(int jointIndex, glm::quat& rotation, bool fromBind) const { - if (jointIndex == -1 || _jointStates.isEmpty()) { - return false; - } - rotation = _jointStates[jointIndex].combinedRotation * - (fromBind ? _geometry->getFBXGeometry().joints[jointIndex].inverseBindRotation : - _geometry->getFBXGeometry().joints[jointIndex].inverseDefaultRotation); - return true; -} - bool Model::setJointPosition(int jointIndex, const glm::vec3& translation, const glm::quat& rotation, bool useRotation, int lastFreeIndex, bool allIntermediatesFree, const glm::vec3& alignment) { if (jointIndex == -1 || _jointStates.isEmpty()) { diff --git a/interface/src/renderer/Model.h b/interface/src/renderer/Model.h index ae2bcd79b8..6a79772ca7 100644 --- a/interface/src/renderer/Model.h +++ b/interface/src/renderer/Model.h @@ -180,6 +180,9 @@ public: /// Returns the extended length from the right hand to its first free ancestor. float getRightArmLength() const; + bool getJointPosition(int jointIndex, glm::vec3& position) const; + bool getJointRotation(int jointIndex, glm::quat& rotation, bool fromBind = false) const; + void clearShapes(); void rebuildShapes(); void updateShapePositions(); @@ -269,9 +272,6 @@ protected: virtual void maybeUpdateNeckRotation(const JointState& parentState, const FBXJoint& joint, JointState& state); virtual void maybeUpdateEyeRotation(const JointState& parentState, const FBXJoint& joint, JointState& state); - bool getJointPosition(int jointIndex, glm::vec3& position) const; - bool getJointRotation(int jointIndex, glm::quat& rotation, bool fromBind = false) const; - bool setJointPosition(int jointIndex, const glm::vec3& translation, const glm::quat& rotation = glm::quat(), bool useRotation = false, int lastFreeIndex = -1, bool allIntermediatesFree = false, const glm::vec3& alignment = glm::vec3(0.0f, -1.0f, 0.0f));