From f274cdcc7f885325b6613af2f71128cbd29f904a Mon Sep 17 00:00:00 2001 From: Brad Hefta-Gaub Date: Mon, 21 Mar 2016 10:47:31 -0700 Subject: [PATCH] Revert "Fix ModelBlender crash" --- assignment-client/src/Agent.cpp | 1 + interface/src/Application.cpp | 5 +- interface/src/avatar/Avatar.cpp | 154 +++++++++++------- interface/src/avatar/Avatar.h | 9 +- interface/src/avatar/FaceModel.cpp | 58 +++++++ interface/src/avatar/FaceModel.h | 38 +++++ interface/src/avatar/Head.cpp | 11 +- interface/src/avatar/Head.h | 7 + interface/src/avatar/MyAvatar.cpp | 64 +++++--- interface/src/avatar/MyAvatar.h | 5 +- interface/src/avatar/SkeletonModel.h | 4 - interface/src/avatar/SoftAttachmentModel.cpp | 2 +- libraries/avatars/src/AvatarData.cpp | 33 +++- libraries/avatars/src/AvatarData.h | 8 + libraries/avatars/src/AvatarHashMap.cpp | 4 + .../src/EntityTreeRenderer.cpp | 36 ++-- .../src/EntityTreeRenderer.h | 13 +- .../src/RenderableModelEntityItem.cpp | 6 +- .../src/RenderableModelEntityItem.h | 4 +- libraries/entities/src/EntityTree.h | 7 +- libraries/fbx/src/FBXReader.cpp | 8 +- libraries/render-utils/src/Model.cpp | 13 +- libraries/render-utils/src/Model.h | 14 +- 23 files changed, 339 insertions(+), 165 deletions(-) create mode 100644 interface/src/avatar/FaceModel.cpp create mode 100644 interface/src/avatar/FaceModel.h diff --git a/assignment-client/src/Agent.cpp b/assignment-client/src/Agent.cpp index 2734cdf01f..1955b8f0c8 100644 --- a/assignment-client/src/Agent.cpp +++ b/assignment-client/src/Agent.cpp @@ -222,6 +222,7 @@ void Agent::executeScript() { scriptedAvatar->setForceFaceTrackerConnected(true); // call model URL setters with empty URLs so our avatar, if user, will have the default models + scriptedAvatar->setFaceModelURL(QUrl()); scriptedAvatar->setSkeletonModelURL(QUrl()); // give this AvatarData object to the script engine diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 3d13cdabb0..f6ab94aa61 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -757,8 +757,7 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer) : connect(&nodeList->getPacketReceiver(), &PacketReceiver::dataReceived, bandwidthRecorder.data(), &BandwidthRecorder::updateInboundData); - // FIXME -- I'm a little concerned about this. - connect(getMyAvatar()->getSkeletonModel().get(), &SkeletonModel::skeletonLoaded, + connect(&getMyAvatar()->getSkeletonModel(), &SkeletonModel::skeletonLoaded, this, &Application::checkSkeleton, Qt::QueuedConnection); // Setup the userInputMapper with the actions @@ -4604,7 +4603,7 @@ void Application::notifyPacketVersionMismatch() { } void Application::checkSkeleton() { - if (getMyAvatar()->getSkeletonModel()->isActive() && !getMyAvatar()->getSkeletonModel()->hasSkeleton()) { + if (getMyAvatar()->getSkeletonModel().isActive() && !getMyAvatar()->getSkeletonModel().hasSkeleton()) { qCDebug(interfaceapp) << "MyAvatar model has no skeleton"; QString message = "Your selected avatar body has no skeleton.\n\nThe default body will be loaded..."; diff --git a/interface/src/avatar/Avatar.cpp b/interface/src/avatar/Avatar.cpp index 114f0c7cd0..2a94ed30e2 100644 --- a/interface/src/avatar/Avatar.cpp +++ b/interface/src/avatar/Avatar.cpp @@ -79,6 +79,7 @@ namespace render { Avatar::Avatar(RigPointer rig) : AvatarData(), + _skeletonModel(this, nullptr, rig), _skeletonOffset(0.0f), _bodyYawDelta(0.0f), _positionDeltaAccumulator(0.0f), @@ -99,8 +100,6 @@ Avatar::Avatar(RigPointer rig) : // give the pointer to our head to inherited _headData variable from AvatarData _headData = static_cast(new Head(this)); - - _skeletonModel = std::make_shared(this, nullptr, rig); } Avatar::~Avatar() { @@ -113,19 +112,19 @@ Avatar::~Avatar() { void Avatar::init() { getHead()->init(); - _skeletonModel->init(); + _skeletonModel.init(); _initialized = true; } glm::vec3 Avatar::getChestPosition() const { // for now, let's just assume that the "chest" is halfway between the root and the neck glm::vec3 neckPosition; - return _skeletonModel->getNeckPosition(neckPosition) ? (getPosition() + neckPosition) * 0.5f : getPosition(); + return _skeletonModel.getNeckPosition(neckPosition) ? (getPosition() + neckPosition) * 0.5f : getPosition(); } glm::vec3 Avatar::getNeckPosition() const { glm::vec3 neckPosition; - return _skeletonModel->getNeckPosition(neckPosition) ? neckPosition : getPosition(); + return _skeletonModel.getNeckPosition(neckPosition) ? neckPosition : getPosition(); } @@ -138,10 +137,10 @@ AABox Avatar::getBounds() const { // Except, that getPartBounds produces an infinite, uncentered bounding box when the model is not yet parsed, // and we want a centered one. NOTE: There is code that may never try to render, and thus never load and get the // real model bounds, if this is unrealistically small. - if (!_skeletonModel->isRenderable()) { + if (!_skeletonModel.isRenderable()) { return AABox(getPosition(), getUniformScale()); // approximately 2m tall, scaled to user request. } - return _skeletonModel->getPartBounds(0, 0, getPosition(), getOrientation()); + return _skeletonModel.getPartBounds(0, 0, getPosition(), getOrientation()); } void Avatar::animateScaleChanges(float deltaTime) { @@ -192,8 +191,8 @@ void Avatar::simulate(float deltaTime) { if (_shouldAnimate && !_shouldSkipRender && inView) { { PerformanceTimer perfTimer("skeleton"); - _skeletonModel->getRig()->copyJointsFromJointData(_jointData); - _skeletonModel->simulate(deltaTime, _hasNewJointRotations || _hasNewJointTranslations); + _skeletonModel.getRig()->copyJointsFromJointData(_jointData); + _skeletonModel.simulate(deltaTime, _hasNewJointRotations || _hasNewJointTranslations); locationChanged(); // joints changed, so if there are any children, update them. _hasNewJointRotations = false; _hasNewJointTranslations = false; @@ -201,7 +200,7 @@ void Avatar::simulate(float deltaTime) { { PerformanceTimer perfTimer("head"); glm::vec3 headPosition = getPosition(); - _skeletonModel->getHeadPosition(headPosition); + _skeletonModel.getHeadPosition(headPosition); Head* head = getHead(); head->setPosition(headPosition); head->setScale(getUniformScale()); @@ -296,7 +295,8 @@ bool Avatar::addToScene(AvatarSharedPointer self, std::shared_ptr auto avatarPayloadPointer = Avatar::PayloadPointer(avatarPayload); _renderItemID = scene->allocateID(); pendingChanges.resetItem(_renderItemID, avatarPayloadPointer); - _skeletonModel->addToScene(scene, pendingChanges); + _skeletonModel.addToScene(scene, pendingChanges); + getHead()->getFaceModel().addToScene(scene, pendingChanges); for (auto& attachmentModel : _attachmentModels) { attachmentModel->addToScene(scene, pendingChanges); @@ -308,7 +308,8 @@ bool Avatar::addToScene(AvatarSharedPointer self, std::shared_ptr void Avatar::removeFromScene(AvatarSharedPointer self, std::shared_ptr scene, render::PendingChanges& pendingChanges) { pendingChanges.removeItem(_renderItemID); render::Item::clearID(_renderItemID); - _skeletonModel->removeFromScene(scene, pendingChanges); + _skeletonModel.removeFromScene(scene, pendingChanges); + getHead()->getFaceModel().removeFromScene(scene, pendingChanges); for (auto& attachmentModel : _attachmentModels) { attachmentModel->removeFromScene(scene, pendingChanges); } @@ -339,12 +340,12 @@ void Avatar::render(RenderArgs* renderArgs, const glm::vec3& cameraPosition) { if (_handState & IS_FINGER_POINTING_FLAG) { int leftIndexTip = getJointIndex("LeftHandIndex4"); int leftIndexTipJoint = getJointIndex("LeftHandIndex3"); - havePosition = _skeletonModel->getJointPositionInWorldFrame(leftIndexTip, position); - haveRotation = _skeletonModel->getJointRotationInWorldFrame(leftIndexTipJoint, rotation); + havePosition = _skeletonModel.getJointPositionInWorldFrame(leftIndexTip, position); + haveRotation = _skeletonModel.getJointRotationInWorldFrame(leftIndexTipJoint, rotation); } else { - int leftHand = _skeletonModel->getLeftHandJointIndex(); - havePosition = _skeletonModel->getJointPositionInWorldFrame(leftHand, position); - haveRotation = _skeletonModel->getJointRotationInWorldFrame(leftHand, rotation); + int leftHand = _skeletonModel.getLeftHandJointIndex(); + havePosition = _skeletonModel.getJointPositionInWorldFrame(leftHand, position); + haveRotation = _skeletonModel.getJointRotationInWorldFrame(leftHand, rotation); } if (havePosition && haveRotation) { @@ -363,12 +364,12 @@ void Avatar::render(RenderArgs* renderArgs, const glm::vec3& cameraPosition) { if (_handState & IS_FINGER_POINTING_FLAG) { int rightIndexTip = getJointIndex("RightHandIndex4"); int rightIndexTipJoint = getJointIndex("RightHandIndex3"); - havePosition = _skeletonModel->getJointPositionInWorldFrame(rightIndexTip, position); - haveRotation = _skeletonModel->getJointRotationInWorldFrame(rightIndexTipJoint, rotation); + havePosition = _skeletonModel.getJointPositionInWorldFrame(rightIndexTip, position); + haveRotation = _skeletonModel.getJointRotationInWorldFrame(rightIndexTipJoint, rotation); } else { - int rightHand = _skeletonModel->getRightHandJointIndex(); - havePosition = _skeletonModel->getJointPositionInWorldFrame(rightHand, position); - haveRotation = _skeletonModel->getJointRotationInWorldFrame(rightHand, rotation); + int rightHand = _skeletonModel.getRightHandJointIndex(); + havePosition = _skeletonModel.getJointPositionInWorldFrame(rightHand, position); + haveRotation = _skeletonModel.getJointRotationInWorldFrame(rightHand, rotation); } if (havePosition && haveRotation) { @@ -425,7 +426,7 @@ void Avatar::render(RenderArgs* renderArgs, const glm::vec3& cameraPosition) { const float LIGHT_EXPONENT = 1.0f; const float LIGHT_CUTOFF = glm::radians(80.0f); float distance = BASE_LIGHT_DISTANCE * getUniformScale(); - glm::vec3 position = _skeletonModel->getTranslation(); + glm::vec3 position = glm::mix(_skeletonModel.getTranslation(), getHead()->getFaceModel().getTranslation(), 0.9f); glm::quat orientation = getOrientation(); foreach (const AvatarManager::LocalLight& light, DependencyManager::get()->getLocalLights()) { glm::vec3 direction = orientation * light.direction; @@ -435,10 +436,10 @@ void Avatar::render(RenderArgs* renderArgs, const glm::vec3& cameraPosition) { } bool renderBounding = Menu::getInstance()->isOptionChecked(MenuOption::RenderBoundingCollisionShapes); - if (renderBounding && shouldRenderHead(renderArgs) && _skeletonModel->isRenderable()) { + if (renderBounding && shouldRenderHead(renderArgs) && _skeletonModel.isRenderable()) { PROFILE_RANGE_BATCH(batch, __FUNCTION__":skeletonBoundingCollisionShapes"); const float BOUNDING_SHAPE_ALPHA = 0.7f; - _skeletonModel->renderBoundingCollisionShapes(*renderArgs->_batch, getUniformScale(), BOUNDING_SHAPE_ALPHA); + _skeletonModel.renderBoundingCollisionShapes(*renderArgs->_batch, getUniformScale(), BOUNDING_SHAPE_ALPHA); } // If this is the avatar being looked at, render a little ball above their head @@ -467,7 +468,7 @@ void Avatar::render(RenderArgs* renderArgs, const glm::vec3& cameraPosition) { * (1.0f - ((float)(now - getHead()->getLookingAtMeStarted())) / (LOOKING_AT_ME_DURATION * (float)USECS_PER_SECOND)); if (alpha > 0.0f) { - QSharedPointer geometry = _skeletonModel->getGeometry(); + QSharedPointer geometry = _skeletonModel.getGeometry(); if (geometry && geometry->isLoaded()) { const float DEFAULT_EYE_DIAMETER = 0.048f; // Typical human eye const float RADIUS_INCREMENT = 0.005f; @@ -538,9 +539,14 @@ void Avatar::fixupModelsInScene() { // fix them up in the scene render::ScenePointer scene = qApp->getMain3DScene(); render::PendingChanges pendingChanges; - if (_skeletonModel->isRenderable() && _skeletonModel->needsFixupInScene()) { - _skeletonModel->removeFromScene(scene, pendingChanges); - _skeletonModel->addToScene(scene, pendingChanges); + if (_skeletonModel.isRenderable() && _skeletonModel.needsFixupInScene()) { + _skeletonModel.removeFromScene(scene, pendingChanges); + _skeletonModel.addToScene(scene, pendingChanges); + } + Model& faceModel = getHead()->getFaceModel(); + if (faceModel.isRenderable() && faceModel.needsFixupInScene()) { + faceModel.removeFromScene(scene, pendingChanges); + faceModel.addToScene(scene, pendingChanges); } for (auto& attachmentModel : _attachmentModels) { if (attachmentModel->isRenderable() && attachmentModel->needsFixupInScene()) { @@ -558,7 +564,14 @@ void Avatar::fixupModelsInScene() { } void Avatar::renderBody(RenderArgs* renderArgs, ViewFrustum* renderFrustum, float glowLevel) { + fixupModelsInScene(); + + { + if (_skeletonModel.isRenderable() && getHead()->getFaceModel().isRenderable()) { + getHead()->render(renderArgs, 1.0f, renderFrustum); + } + } getHead()->renderLookAts(renderArgs); } @@ -580,8 +593,8 @@ void Avatar::simulateAttachments(float deltaTime) { model->setRotation(getOrientation() * Quaternions::Y_180); model->simulate(deltaTime); } else { - if (_skeletonModel->getJointPositionInWorldFrame(jointIndex, jointPosition) && - _skeletonModel->getJointRotationInWorldFrame(jointIndex, jointRotation)) { + if (_skeletonModel.getJointPositionInWorldFrame(jointIndex, jointPosition) && + _skeletonModel.getJointRotationInWorldFrame(jointIndex, jointRotation)) { model->setTranslation(jointPosition + jointRotation * attachment.translation * getUniformScale()); model->setRotation(jointRotation * attachment.rotation); model->setScaleToFit(true, getUniformScale() * attachment.scale, true); // hack to force rescale @@ -622,7 +635,7 @@ glm::vec3 Avatar::getDisplayNamePosition() const { glm::vec3 bodyUpDirection = getBodyUpDirection(); DEBUG_VALUE("bodyUpDirection =", bodyUpDirection); - if (getSkeletonModel()->getNeckPosition(namePosition)) { + if (getSkeletonModel().getNeckPosition(namePosition)) { float headHeight = getHeadHeight(); DEBUG_VALUE("namePosition =", namePosition); DEBUG_VALUE("headHeight =", headHeight); @@ -767,46 +780,46 @@ QVector Avatar::getJointRotations() const { if (QThread::currentThread() != thread()) { return AvatarData::getJointRotations(); } - QVector jointRotations(_skeletonModel->getJointStateCount()); - for (int i = 0; i < _skeletonModel->getJointStateCount(); ++i) { - _skeletonModel->getJointRotation(i, jointRotations[i]); + QVector jointRotations(_skeletonModel.getJointStateCount()); + for (int i = 0; i < _skeletonModel.getJointStateCount(); ++i) { + _skeletonModel.getJointRotation(i, jointRotations[i]); } return jointRotations; } glm::quat Avatar::getJointRotation(int index) const { glm::quat rotation; - _skeletonModel->getJointRotation(index, rotation); + _skeletonModel.getJointRotation(index, rotation); return rotation; } glm::vec3 Avatar::getJointTranslation(int index) const { glm::vec3 translation; - _skeletonModel->getJointTranslation(index, translation); + _skeletonModel.getJointTranslation(index, translation); return translation; } glm::quat Avatar::getDefaultJointRotation(int index) const { glm::quat rotation; - _skeletonModel->getRelativeDefaultJointRotation(index, rotation); + _skeletonModel.getRelativeDefaultJointRotation(index, rotation); return rotation; } glm::vec3 Avatar::getDefaultJointTranslation(int index) const { glm::vec3 translation; - _skeletonModel->getRelativeDefaultJointTranslation(index, translation); + _skeletonModel.getRelativeDefaultJointTranslation(index, translation); return translation; } glm::quat Avatar::getAbsoluteJointRotationInObjectFrame(int index) const { glm::quat rotation; - _skeletonModel->getAbsoluteJointRotationInRigFrame(index, rotation); + _skeletonModel.getAbsoluteJointRotationInRigFrame(index, rotation); return Quaternions::Y_180 * rotation; } glm::vec3 Avatar::getAbsoluteJointTranslationInObjectFrame(int index) const { glm::vec3 translation; - _skeletonModel->getAbsoluteJointTranslationInRigFrame(index, translation); + _skeletonModel.getAbsoluteJointTranslationInRigFrame(index, translation); return Quaternions::Y_180 * translation; } @@ -817,7 +830,7 @@ int Avatar::getJointIndex(const QString& name) const { Q_RETURN_ARG(int, result), Q_ARG(const QString&, name)); return result; } - return _skeletonModel->isActive() ? _skeletonModel->getGeometry()->getFBXGeometry().getJointIndex(name) : -1; + return _skeletonModel.isActive() ? _skeletonModel.getGeometry()->getFBXGeometry().getJointIndex(name) : -1; } QStringList Avatar::getJointNames() const { @@ -827,7 +840,7 @@ QStringList Avatar::getJointNames() const { Q_RETURN_ARG(QStringList, result)); return result; } - return _skeletonModel->isActive() ? _skeletonModel->getGeometry()->getFBXGeometry().getJointNames() : QStringList(); + return _skeletonModel.isActive() ? _skeletonModel.getGeometry()->getFBXGeometry().getJointNames() : QStringList(); } glm::vec3 Avatar::getJointPosition(int index) const { @@ -838,7 +851,7 @@ glm::vec3 Avatar::getJointPosition(int index) const { return position; } glm::vec3 position; - _skeletonModel->getJointPositionInWorldFrame(index, position); + _skeletonModel.getJointPositionInWorldFrame(index, position); return position; } @@ -850,7 +863,7 @@ glm::vec3 Avatar::getJointPosition(const QString& name) const { return position; } glm::vec3 position; - _skeletonModel->getJointPositionInWorldFrame(getJointIndex(name), position); + _skeletonModel.getJointPositionInWorldFrame(getJointIndex(name), position); return position; } @@ -859,9 +872,14 @@ void Avatar::scaleVectorRelativeToPosition(glm::vec3 &positionToScale) const { positionToScale = getPosition() + getUniformScale() * (positionToScale - getPosition()); } +void Avatar::setFaceModelURL(const QUrl& faceModelURL) { + AvatarData::setFaceModelURL(faceModelURL); + getHead()->getFaceModel().setURL(_faceModelURL); +} + void Avatar::setSkeletonModelURL(const QUrl& skeletonModelURL) { AvatarData::setSkeletonModelURL(skeletonModelURL); - _skeletonModel->setURL(_skeletonModelURL); + _skeletonModel.setURL(_skeletonModelURL); } // create new model, can return an instance of a SoftAttachmentModel rather then Model @@ -894,12 +912,12 @@ void Avatar::setAttachmentData(const QVector& attachmentData) { for (int i = 0; i < attachmentData.size(); i++) { if (i == (int)_attachmentModels.size()) { // if number of attachments has been increased, we need to allocate a new model - _attachmentModels.push_back(allocateAttachmentModel(attachmentData[i].isSoft, _skeletonModel->getRig())); + _attachmentModels.push_back(allocateAttachmentModel(attachmentData[i].isSoft, _skeletonModel.getRig())); } else if (i < oldAttachmentData.size() && oldAttachmentData[i].isSoft != attachmentData[i].isSoft) { // if the attachment has changed type, we need to re-allocate a new one. _attachmentsToRemove.push_back(_attachmentModels[i]); - _attachmentModels[i] = allocateAttachmentModel(attachmentData[i].isSoft, _skeletonModel->getRig()); + _attachmentModels[i] = allocateAttachmentModel(attachmentData[i].isSoft, _skeletonModel.getRig()); } _attachmentModels[i]->setURL(attachmentData[i].modelURL); } @@ -986,14 +1004,24 @@ void Avatar::renderJointConnectingCone(gpu::Batch& batch, glm::vec3 position1, g } float Avatar::getSkeletonHeight() const { - Extents extents = _skeletonModel->getBindExtents(); + Extents extents = _skeletonModel.getBindExtents(); return extents.maximum.y - extents.minimum.y; } float Avatar::getHeadHeight() const { - Extents extents = _skeletonModel->getMeshExtents(); + Extents extents = getHead()->getFaceModel().getMeshExtents(); + if (!extents.isEmpty() && extents.isValid()) { + + // HACK: We have a really odd case when fading out for some models where this value explodes + float result = extents.maximum.y - extents.minimum.y; + if (result >= 0.0f && result < 100.0f * getUniformScale() ) { + return result; + } + } + + extents = _skeletonModel.getMeshExtents(); glm::vec3 neckPosition; - if (!extents.isEmpty() && extents.isValid() && _skeletonModel->getNeckPosition(neckPosition)) { + if (!extents.isEmpty() && extents.isValid() && _skeletonModel.getNeckPosition(neckPosition)) { return extents.maximum.y / 2.0f - neckPosition.y + getPosition().y; } @@ -1002,7 +1030,7 @@ float Avatar::getHeadHeight() const { } float Avatar::getPelvisFloatingHeight() const { - return -_skeletonModel->getBindExtents().minimum.y; + return -_skeletonModel.getBindExtents().minimum.y; } void Avatar::setShowDisplayName(bool showDisplayName) { @@ -1030,9 +1058,9 @@ void Avatar::setShowDisplayName(bool showDisplayName) { // virtual void Avatar::computeShapeInfo(ShapeInfo& shapeInfo) { float uniformScale = getUniformScale(); - shapeInfo.setCapsuleY(uniformScale * _skeletonModel->getBoundingCapsuleRadius(), - 0.5f * uniformScale * _skeletonModel->getBoundingCapsuleHeight()); - shapeInfo.setOffset(uniformScale * _skeletonModel->getBoundingCapsuleOffset()); + shapeInfo.setCapsuleY(uniformScale * _skeletonModel.getBoundingCapsuleRadius(), + 0.5f * uniformScale * _skeletonModel.getBoundingCapsuleHeight()); + shapeInfo.setOffset(uniformScale * _skeletonModel.getBoundingCapsuleOffset()); } void Avatar::setMotionState(AvatarMotionState* motionState) { @@ -1070,12 +1098,12 @@ glm::vec3 Avatar::getUncachedLeftPalmPosition() const { assert(QThread::currentThread() == thread()); // main thread access only glm::quat leftPalmRotation; glm::vec3 leftPalmPosition; - if (_skeletonModel->getLeftGrabPosition(leftPalmPosition)) { + if (_skeletonModel.getLeftGrabPosition(leftPalmPosition)) { return leftPalmPosition; } // avatar didn't have a LeftHandMiddle1 joint, fall back on this: - getSkeletonModel()->getJointRotationInWorldFrame(getSkeletonModel()->getLeftHandJointIndex(), leftPalmRotation); - getSkeletonModel()->getLeftHandPosition(leftPalmPosition); + getSkeletonModel().getJointRotationInWorldFrame(getSkeletonModel().getLeftHandJointIndex(), leftPalmRotation); + getSkeletonModel().getLeftHandPosition(leftPalmPosition); leftPalmPosition += HAND_TO_PALM_OFFSET * glm::inverse(leftPalmRotation); return leftPalmPosition; } @@ -1083,7 +1111,7 @@ glm::vec3 Avatar::getUncachedLeftPalmPosition() const { glm::quat Avatar::getUncachedLeftPalmRotation() const { assert(QThread::currentThread() == thread()); // main thread access only glm::quat leftPalmRotation; - getSkeletonModel()->getJointRotationInWorldFrame(getSkeletonModel()->getLeftHandJointIndex(), leftPalmRotation); + getSkeletonModel().getJointRotationInWorldFrame(getSkeletonModel().getLeftHandJointIndex(), leftPalmRotation); return leftPalmRotation; } @@ -1091,12 +1119,12 @@ glm::vec3 Avatar::getUncachedRightPalmPosition() const { assert(QThread::currentThread() == thread()); // main thread access only glm::quat rightPalmRotation; glm::vec3 rightPalmPosition; - if (_skeletonModel->getRightGrabPosition(rightPalmPosition)) { + if (_skeletonModel.getRightGrabPosition(rightPalmPosition)) { return rightPalmPosition; } // avatar didn't have a RightHandMiddle1 joint, fall back on this: - getSkeletonModel()->getJointRotationInWorldFrame(getSkeletonModel()->getRightHandJointIndex(), rightPalmRotation); - getSkeletonModel()->getRightHandPosition(rightPalmPosition); + getSkeletonModel().getJointRotationInWorldFrame(getSkeletonModel().getRightHandJointIndex(), rightPalmRotation); + getSkeletonModel().getRightHandPosition(rightPalmPosition); rightPalmPosition += HAND_TO_PALM_OFFSET * glm::inverse(rightPalmRotation); return rightPalmPosition; } @@ -1104,7 +1132,7 @@ glm::vec3 Avatar::getUncachedRightPalmPosition() const { glm::quat Avatar::getUncachedRightPalmRotation() const { assert(QThread::currentThread() == thread()); // main thread access only glm::quat rightPalmRotation; - getSkeletonModel()->getJointRotationInWorldFrame(getSkeletonModel()->getRightHandJointIndex(), rightPalmRotation); + getSkeletonModel().getJointRotationInWorldFrame(getSkeletonModel().getRightHandJointIndex(), rightPalmRotation); return rightPalmRotation; } diff --git a/interface/src/avatar/Avatar.h b/interface/src/avatar/Avatar.h index cb35fbb5eb..01548c9066 100644 --- a/interface/src/avatar/Avatar.h +++ b/interface/src/avatar/Avatar.h @@ -84,8 +84,8 @@ public: bool getIsLookAtTarget() const { return _isLookAtTarget; } //getters bool isInitialized() const { return _initialized; } - SkeletonModelPointer getSkeletonModel() { return _skeletonModel; } - const SkeletonModelPointer getSkeletonModel() const { return _skeletonModel; } + SkeletonModel& getSkeletonModel() { return _skeletonModel; } + const SkeletonModel& getSkeletonModel() const { return _skeletonModel; } glm::vec3 getChestPosition() const; float getUniformScale() const { return getScale().y; } const Head* getHead() const { return static_cast(_headData); } @@ -114,6 +114,7 @@ public: virtual bool setAbsoluteJointRotationInObjectFrame(int index, const glm::quat& rotation) override { return false; } virtual bool setAbsoluteJointTranslationInObjectFrame(int index, const glm::vec3& translation) override { return false; } + virtual void setFaceModelURL(const QUrl& faceModelURL) override; virtual void setSkeletonModelURL(const QUrl& skeletonModelURL) override; virtual void setAttachmentData(const QVector& attachmentData) override; @@ -143,7 +144,7 @@ public: void scaleVectorRelativeToPosition(glm::vec3 &positionToScale) const; void slamPosition(const glm::vec3& position); - virtual void updateAttitude() override { _skeletonModel->updateAttitude(); } + virtual void updateAttitude() override { _skeletonModel.updateAttitude(); } // Call this when updating Avatar position with a delta. This will allow us to // _accurately_ measure position changes and compute the resulting velocity @@ -187,7 +188,7 @@ protected: void setMotionState(AvatarMotionState* motionState); - SkeletonModelPointer _skeletonModel; + SkeletonModel _skeletonModel; glm::vec3 _skeletonOffset; std::vector> _attachmentModels; std::vector> _attachmentsToRemove; diff --git a/interface/src/avatar/FaceModel.cpp b/interface/src/avatar/FaceModel.cpp new file mode 100644 index 0000000000..9c22fc17ac --- /dev/null +++ b/interface/src/avatar/FaceModel.cpp @@ -0,0 +1,58 @@ +// +// FaceModel.cpp +// interface/src/avatar +// +// Created by Andrzej Kapolka on 9/16/13. +// Copyright 2013 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 "Avatar.h" +#include "FaceModel.h" +#include "Head.h" +#include "Menu.h" + +FaceModel::FaceModel(Head* owningHead, RigPointer rig) : + Model(rig, nullptr), + _owningHead(owningHead) +{ + assert(_rig); +} + +void FaceModel::simulate(float deltaTime, bool fullUpdate) { + updateGeometry(); + + Avatar* owningAvatar = static_cast(_owningHead->_owningAvatar); + glm::vec3 neckPosition; + if (!owningAvatar->getSkeletonModel().getNeckPosition(neckPosition)) { + neckPosition = owningAvatar->getPosition(); + } + setTranslation(neckPosition); + glm::quat neckParentRotation = owningAvatar->getOrientation(); + setRotation(neckParentRotation); + setScale(glm::vec3(1.0f, 1.0f, 1.0f) * _owningHead->getScale()); + + setPupilDilation(_owningHead->getPupilDilation()); + setBlendshapeCoefficients(_owningHead->getBlendshapeCoefficients()); + + // FIXME - this is very expensive, we shouldn't do it if we don't have to + //invalidCalculatedMeshBoxes(); + + if (isActive()) { + setOffset(-_geometry->getFBXGeometry().neckPivot); + Model::simulateInternal(deltaTime); + } +} + +bool FaceModel::getEyePositions(glm::vec3& firstEyePosition, glm::vec3& secondEyePosition) const { + if (!isActive()) { + return false; + } + const FBXGeometry& geometry = _geometry->getFBXGeometry(); + return getJointPositionInWorldFrame(geometry.leftEyeJointIndex, firstEyePosition) && + getJointPositionInWorldFrame(geometry.rightEyeJointIndex, secondEyePosition); +} diff --git a/interface/src/avatar/FaceModel.h b/interface/src/avatar/FaceModel.h new file mode 100644 index 0000000000..5a19a8ea29 --- /dev/null +++ b/interface/src/avatar/FaceModel.h @@ -0,0 +1,38 @@ +// +// FaceModel.h +// interface/src/avatar +// +// Created by Andrzej Kapolka on 9/16/13. +// Copyright 2013 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_FaceModel_h +#define hifi_FaceModel_h + +#include + +class Head; + +/// A face formed from a linear mix of blendshapes according to a set of coefficients. +class FaceModel : public Model { + Q_OBJECT + +public: + + FaceModel(Head* owningHead, RigPointer rig); + + virtual void simulate(float deltaTime, bool fullUpdate = true); + + /// Retrieve the positions of up to two eye meshes. + /// \return whether or not both eye meshes were found + bool getEyePositions(glm::vec3& firstEyePosition, glm::vec3& secondEyePosition) const; + +private: + + Head* _owningHead; +}; + +#endif // hifi_FaceModel_h diff --git a/interface/src/avatar/Head.cpp b/interface/src/avatar/Head.cpp index 5524b928de..b97fd2b0ea 100644 --- a/interface/src/avatar/Head.cpp +++ b/interface/src/avatar/Head.cpp @@ -62,17 +62,20 @@ Head::Head(Avatar* owningAvatar) : _isLookingAtMe(false), _lookingAtMeStarted(0), _wasLastLookingAtMe(0), + _faceModel(this, std::make_shared()), _leftEyeLookAtID(DependencyManager::get()->allocateID()), _rightEyeLookAtID(DependencyManager::get()->allocateID()) { } void Head::init() { + _faceModel.init(); } void Head::reset() { _baseYaw = _basePitch = _baseRoll = 0.0f; _leanForward = _leanSideways = 0.0f; + _faceModel.reset(); } void Head::simulate(float deltaTime, bool isMine, bool billboard) { @@ -230,6 +233,12 @@ void Head::simulate(float deltaTime, bool isMine, bool billboard) { } _leftEyePosition = _rightEyePosition = getPosition(); + if (!billboard) { + _faceModel.simulate(deltaTime); + if (!_faceModel.getEyePositions(_leftEyePosition, _rightEyePosition)) { + static_cast(_owningAvatar)->getSkeletonModel().getEyePositions(_leftEyePosition, _rightEyePosition); + } + } _eyePosition = calculateAverageEyePosition(); } @@ -402,7 +411,7 @@ glm::quat Head::getEyeRotation(const glm::vec3& eyePosition) const { } glm::vec3 Head::getScalePivot() const { - return _position; + return _faceModel.isActive() ? _faceModel.getTranslation() : _position; } void Head::setFinalPitch(float finalPitch) { diff --git a/interface/src/avatar/Head.h b/interface/src/avatar/Head.h index d0bd4fdb77..614e286329 100644 --- a/interface/src/avatar/Head.h +++ b/interface/src/avatar/Head.h @@ -18,6 +18,7 @@ #include +#include "FaceModel.h" #include "world.h" @@ -75,6 +76,9 @@ public: glm::vec3 getLeftEarPosition() const { return _leftEyePosition + (getRightDirection() * -EYE_EAR_GAP) + (getFrontDirection() * -EYE_EAR_GAP); } glm::vec3 getMouthPosition() const { return _eyePosition - getUpDirection() * glm::length(_rightEyePosition - _leftEyePosition); } + FaceModel& getFaceModel() { return _faceModel; } + const FaceModel& getFaceModel() const { return _faceModel; } + bool getReturnToCenter() const { return _returnHeadToCenter; } // Do you want head to try to return to center (depends on interface detected) float getAverageLoudness() const { return _averageLoudness; } /// \return the point about which scaling occurs. @@ -146,6 +150,7 @@ private: bool _isLookingAtMe; quint64 _lookingAtMeStarted; quint64 _wasLastLookingAtMe; + FaceModel _faceModel; glm::vec3 _correctedLookAtPosition; @@ -157,6 +162,8 @@ private: void renderLookatTarget(RenderArgs* renderArgs, glm::vec3 lookatPosition); void calculateMouthShapes(); void applyEyelidOffset(glm::quat headOrientation); + + friend class FaceModel; }; #endif // hifi_Head_h diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 74ad3064f7..7198f32422 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -167,6 +167,11 @@ MyAvatar::MyAvatar(RigPointer rig) : } auto recordingInterface = DependencyManager::get(); + if (recordingInterface->getPlayerUseHeadModel() && dummyAvatar.getFaceModelURL().isValid() && + (dummyAvatar.getFaceModelURL() != getFaceModelURL())) { + // FIXME + //myAvatar->setFaceModelURL(_dummyAvatar.getFaceModelURL()); + } if (recordingInterface->getPlayerUseSkeletonModel() && dummyAvatar.getSkeletonModelURL().isValid() && (dummyAvatar.getSkeletonModelURL() != getSkeletonModelURL())) { @@ -232,7 +237,7 @@ void MyAvatar::reset(bool andReload) { // Reset dynamic state. _wasPushing = _isPushing = _isBraking = false; _follow.deactivate(); - _skeletonModel->reset(); + _skeletonModel.reset(); getHead()->reset(); _targetVelocity = glm::vec3(0.0f); setThrust(glm::vec3(0.0f)); @@ -338,10 +343,10 @@ void MyAvatar::simulate(float deltaTime) { { PerformanceTimer perfTimer("skeleton"); - _skeletonModel->simulate(deltaTime); + _skeletonModel.simulate(deltaTime); } - if (!_skeletonModel->hasSkeleton()) { + if (!_skeletonModel.hasSkeleton()) { // All the simulation that can be done has been done return; } @@ -356,7 +361,7 @@ void MyAvatar::simulate(float deltaTime) { PerformanceTimer perfTimer("head"); Head* head = getHead(); glm::vec3 headPosition; - if (!_skeletonModel->getHeadPosition(headPosition)) { + if (!_skeletonModel.getHeadPosition(headPosition)) { headPosition = getPosition(); } head->setPosition(headPosition); @@ -711,7 +716,7 @@ void MyAvatar::setEnableDebugDrawSensorToWorldMatrix(bool isEnabled) { void MyAvatar::setEnableMeshVisible(bool isEnabled) { render::ScenePointer scene = qApp->getMain3DScene(); - _skeletonModel->setVisibleInScene(isEnabled, scene); + _skeletonModel.setVisibleInScene(isEnabled, scene); } void MyAvatar::setUseAnimPreAndPostRotations(bool isEnabled) { @@ -778,7 +783,7 @@ void MyAvatar::loadData() { void MyAvatar::saveAttachmentData(const AttachmentData& attachment) const { Settings settings; settings.beginGroup("savedAttachmentData"); - settings.beginGroup(_skeletonModel->getURL().toString()); + settings.beginGroup(_skeletonModel.getURL().toString()); settings.beginGroup(attachment.modelURL.toString()); settings.setValue("jointName", attachment.jointName); @@ -801,7 +806,7 @@ void MyAvatar::saveAttachmentData(const AttachmentData& attachment) const { AttachmentData MyAvatar::loadAttachmentData(const QUrl& modelURL, const QString& jointName) const { Settings settings; settings.beginGroup("savedAttachmentData"); - settings.beginGroup(_skeletonModel->getURL().toString()); + settings.beginGroup(_skeletonModel.getURL().toString()); settings.beginGroup(modelURL.toString()); AttachmentData attachment; @@ -946,17 +951,17 @@ eyeContactTarget MyAvatar::getEyeContactTarget() { } glm::vec3 MyAvatar::getDefaultEyePosition() const { - return getPosition() + getWorldAlignedOrientation() * Quaternions::Y_180 * _skeletonModel->getDefaultEyeModelPosition(); + return getPosition() + getWorldAlignedOrientation() * Quaternions::Y_180 * _skeletonModel.getDefaultEyeModelPosition(); } const float SCRIPT_PRIORITY = 1.0f + 1.0f; const float RECORDER_PRIORITY = 1.0f + 1.0f; void MyAvatar::setJointRotations(QVector jointRotations) { - int numStates = glm::min(_skeletonModel->getJointStateCount(), jointRotations.size()); + int numStates = glm::min(_skeletonModel.getJointStateCount(), jointRotations.size()); for (int i = 0; i < numStates; ++i) { // HACK: ATM only Recorder calls setJointRotations() so we hardcode its priority here - _skeletonModel->setJointRotation(i, true, jointRotations[i], RECORDER_PRIORITY); + _skeletonModel.setJointRotation(i, true, jointRotations[i], RECORDER_PRIORITY); } } @@ -1004,11 +1009,18 @@ void MyAvatar::clearJointsData() { _rig->clearJointStates(); } +void MyAvatar::setFaceModelURL(const QUrl& faceModelURL) { + + Avatar::setFaceModelURL(faceModelURL); + render::ScenePointer scene = qApp->getMain3DScene(); + getHead()->getFaceModel().setVisibleInScene(_prevShouldDrawHead, scene); +} + void MyAvatar::setSkeletonModelURL(const QUrl& skeletonModelURL) { Avatar::setSkeletonModelURL(skeletonModelURL); render::ScenePointer scene = qApp->getMain3DScene(); - _skeletonModel->setVisibleInScene(true, scene); + _skeletonModel.setVisibleInScene(true, scene); _headBoneSet.clear(); } @@ -1039,6 +1051,10 @@ void MyAvatar::useFullAvatarURL(const QUrl& fullAvatarURL, const QString& modelN } } + if (!getFaceModelURLString().isEmpty()) { + setFaceModelURL(QString()); + } + const QString& urlString = fullAvatarURL.toString(); if (urlString.isEmpty() || (fullAvatarURL != getSkeletonModelURL())) { qApp->setRawAvatarUpdateThreading(false); @@ -1072,10 +1088,10 @@ glm::vec3 MyAvatar::getSkeletonPosition() const { void MyAvatar::rebuildCollisionShape() { // compute localAABox float scale = getUniformScale(); - float radius = scale * _skeletonModel->getBoundingCapsuleRadius(); - float height = scale * _skeletonModel->getBoundingCapsuleHeight() + 2.0f * radius; + float radius = scale * _skeletonModel.getBoundingCapsuleRadius(); + float height = scale * _skeletonModel.getBoundingCapsuleHeight() + 2.0f * radius; glm::vec3 corner(-radius, -0.5f * height, -radius); - corner += scale * _skeletonModel->getBoundingCapsuleOffset(); + corner += scale * _skeletonModel.getBoundingCapsuleOffset(); glm::vec3 diagonal(2.0f * radius, height, 2.0f * radius); _characterController.setLocalBoundingBox(corner, diagonal); } @@ -1227,7 +1243,7 @@ void MyAvatar::attach(const QString& modelURL, const QString& jointName, void MyAvatar::renderBody(RenderArgs* renderArgs, ViewFrustum* renderFrustum, float glowLevel) { - if (!_skeletonModel->isRenderable()) { + if (!_skeletonModel.isRenderable()) { return; // wait until all models are loaded } @@ -1267,8 +1283,8 @@ void MyAvatar::setVisibleInSceneIfReady(Model* model, render::ScenePointer scene void MyAvatar::initHeadBones() { int neckJointIndex = -1; - if (_skeletonModel->getGeometry()) { - neckJointIndex = _skeletonModel->getGeometry()->getFBXGeometry().neckJointIndex; + if (_skeletonModel.getGeometry()) { + neckJointIndex = _skeletonModel.getGeometry()->getFBXGeometry().neckJointIndex; } if (neckJointIndex == -1) { return; @@ -1281,8 +1297,8 @@ void MyAvatar::initHeadBones() { // fbxJoints only hold links to parents not children, so we have to do a bit of extra work here. while (q.size() > 0) { int jointIndex = q.front(); - for (int i = 0; i < _skeletonModel->getJointStateCount(); i++) { - if (jointIndex == _skeletonModel->getParentJointIndex(i)) { + for (int i = 0; i < _skeletonModel.getJointStateCount(); i++) { + if (jointIndex == _skeletonModel.getParentJointIndex(i)) { _headBoneSet.insert(i); q.push(i); } @@ -1296,7 +1312,7 @@ void MyAvatar::setAnimGraphUrl(const QUrl& url) { return; } destroyAnimGraph(); - _skeletonModel->reset(); // Why is this necessary? Without this, we crash in the next render. + _skeletonModel.reset(); // Why is this necessary? Without this, we crash in the next render. _animGraphUrl = url; initAnimGraph(); } @@ -1335,9 +1351,9 @@ void MyAvatar::preRender(RenderArgs* renderArgs) { render::ScenePointer scene = qApp->getMain3DScene(); const bool shouldDrawHead = shouldRenderHead(renderArgs); - if (_skeletonModel->initWhenReady(scene)) { + if (_skeletonModel.initWhenReady(scene)) { initHeadBones(); - _skeletonModel->setCauterizeBoneSet(_headBoneSet); + _skeletonModel.setCauterizeBoneSet(_headBoneSet); initAnimGraph(); } @@ -1346,7 +1362,7 @@ void MyAvatar::preRender(RenderArgs* renderArgs) { auto animSkeleton = _rig->getAnimSkeleton(); // the rig is in the skeletonModel frame - AnimPose xform(glm::vec3(1), _skeletonModel->getRotation(), _skeletonModel->getTranslation()); + AnimPose xform(glm::vec3(1), _skeletonModel.getRotation(), _skeletonModel.getTranslation()); if (_enableDebugDrawDefaultPose && animSkeleton) { glm::vec4 gray(0.2f, 0.2f, 0.2f, 0.2f); @@ -1386,7 +1402,7 @@ void MyAvatar::preRender(RenderArgs* renderArgs) { DebugDraw::getInstance().updateMyAvatarRot(getOrientation()); if (shouldDrawHead != _prevShouldDrawHead) { - _skeletonModel->setCauterizeBones(!shouldDrawHead); + _skeletonModel.setCauterizeBones(!shouldDrawHead); } _prevShouldDrawHead = shouldDrawHead; } diff --git a/interface/src/avatar/MyAvatar.h b/interface/src/avatar/MyAvatar.h index 3554d9b8bc..37a2e752e6 100644 --- a/interface/src/avatar/MyAvatar.h +++ b/interface/src/avatar/MyAvatar.h @@ -178,6 +178,8 @@ public: Q_INVOKABLE float getHeadFinalRoll() const { return getHead()->getFinalRoll(); } Q_INVOKABLE float getHeadFinalPitch() const { return getHead()->getFinalPitch(); } Q_INVOKABLE float getHeadDeltaPitch() const { return getHead()->getDeltaPitch(); } + Q_INVOKABLE int getFaceBlendCoefNum() const { return getHead()->getFaceModel().getBlendshapeCoefficientsNum(); } + Q_INVOKABLE float getFaceBlendCoef(int index) const { return getHead()->getFaceModel().getBlendshapeCoefficient(index); } Q_INVOKABLE glm::vec3 getEyePosition() const { return getHead()->getEyePosition(); } @@ -277,7 +279,7 @@ public slots: void setEnableDebugDrawPosition(bool isEnabled); void setEnableDebugDrawHandControllers(bool isEnabled); void setEnableDebugDrawSensorToWorldMatrix(bool isEnabled); - bool getEnableMeshVisible() const { return _skeletonModel->isVisible(); } + bool getEnableMeshVisible() const { return _skeletonModel.isVisible(); } void setEnableMeshVisible(bool isEnabled); void setUseAnimPreAndPostRotations(bool isEnabled); void setEnableInverseKinematics(bool isEnabled); @@ -326,6 +328,7 @@ private: bool cameraInsideHead() const; // These are made private for MyAvatar so that you will use the "use" methods instead + virtual void setFaceModelURL(const QUrl& faceModelURL) override; virtual void setSkeletonModelURL(const QUrl& skeletonModelURL) override; void setVisibleInSceneIfReady(Model* model, render::ScenePointer scene, bool visiblity); diff --git a/interface/src/avatar/SkeletonModel.h b/interface/src/avatar/SkeletonModel.h index 26b2e9c666..6c6a7472f7 100644 --- a/interface/src/avatar/SkeletonModel.h +++ b/interface/src/avatar/SkeletonModel.h @@ -18,10 +18,6 @@ class Avatar; class MuscleConstraint; -class SkeletonModel; -using SkeletonModelPointer = std::shared_ptr; -using SkeletonModelWeakPointer = std::weak_ptr; - /// A skeleton loaded from a model. class SkeletonModel : public Model { Q_OBJECT diff --git a/interface/src/avatar/SoftAttachmentModel.cpp b/interface/src/avatar/SoftAttachmentModel.cpp index 8f0404e36c..92534f01ea 100644 --- a/interface/src/avatar/SoftAttachmentModel.cpp +++ b/interface/src/avatar/SoftAttachmentModel.cpp @@ -79,6 +79,6 @@ void SoftAttachmentModel::updateClusterMatrices(glm::vec3 modelPosition, glm::qu // post the blender if we're not currently waiting for one to finish if (geometry.hasBlendedMeshes() && _blendshapeCoefficients != _blendedBlendshapeCoefficients) { _blendedBlendshapeCoefficients = _blendshapeCoefficients; - DependencyManager::get()->noteRequiresBlend(getThisPointer()); + DependencyManager::get()->noteRequiresBlend(this); } } diff --git a/libraries/avatars/src/AvatarData.cpp b/libraries/avatars/src/AvatarData.cpp index 858a5fe192..adb942417d 100644 --- a/libraries/avatars/src/AvatarData.cpp +++ b/libraries/avatars/src/AvatarData.cpp @@ -57,6 +57,7 @@ AvatarData::AvatarData() : _hasNewJointRotations(true), _hasNewJointTranslations(true), _headData(NULL), + _faceModelURL("http://invalid.com"), _displayNameTargetAlpha(1.0f), _displayNameAlpha(1.0f), _billboard(), @@ -988,14 +989,18 @@ bool AvatarData::hasIdentityChangedAfterParsing(const QByteArray& data) { QDataStream packetStream(data); QUuid avatarUUID; - QUrl unusedModelURL; // legacy faceModel support - QUrl skeletonModelURL; + QUrl faceModelURL, skeletonModelURL; QVector attachmentData; QString displayName; - packetStream >> avatarUUID >> unusedModelURL >> skeletonModelURL >> attachmentData >> displayName; + packetStream >> avatarUUID >> faceModelURL >> skeletonModelURL >> attachmentData >> displayName; bool hasIdentityChanged = false; + if (faceModelURL != _faceModelURL) { + setFaceModelURL(faceModelURL); + hasIdentityChanged = true; + } + if (skeletonModelURL != _skeletonModelURL) { setSkeletonModelURL(skeletonModelURL); hasIdentityChanged = true; @@ -1020,9 +1025,7 @@ QByteArray AvatarData::identityByteArray() { QUrl emptyURL(""); const QUrl& urlToSend = (_skeletonModelURL == AvatarData::defaultFullAvatarModelUrl()) ? emptyURL : _skeletonModelURL; - QUrl unusedModelURL; // legacy faceModel support - - identityStream << QUuid() << unusedModelURL << urlToSend << _attachmentData << _displayName; + identityStream << QUuid() << _faceModelURL << urlToSend << _attachmentData << _displayName; return identityData; } @@ -1035,6 +1038,12 @@ bool AvatarData::hasBillboardChangedAfterParsing(const QByteArray& data) { return true; } +void AvatarData::setFaceModelURL(const QUrl& faceModelURL) { + _faceModelURL = faceModelURL; + + qCDebug(avatars) << "Changing face model for avatar to" << _faceModelURL.toString(); +} + void AvatarData::setSkeletonModelURL(const QUrl& skeletonModelURL) { const QUrl& expanded = skeletonModelURL.isEmpty() ? AvatarData::defaultFullAvatarModelUrl() : skeletonModelURL; if (expanded == _skeletonModelURL) { @@ -1436,6 +1445,9 @@ JointData jointDataFromJsonValue(const QJsonValue& json) { QJsonObject AvatarData::toJson() const { QJsonObject root; + if (!getFaceModelURL().isEmpty()) { + root[JSON_AVATAR_HEAD_MODEL] = getFaceModelURL().toString(); + } if (!getSkeletonModelURL().isEmpty()) { root[JSON_AVATAR_BODY_MODEL] = getSkeletonModelURL().toString(); } @@ -1502,6 +1514,15 @@ void AvatarData::fromJson(const QJsonObject& json) { _headData->fromJson(json[JSON_AVATAR_HEAD].toObject()); } + if (json.contains(JSON_AVATAR_HEAD_MODEL)) { + auto faceModelURL = json[JSON_AVATAR_HEAD_MODEL].toString(); + if (faceModelURL != getFaceModelURL().toString()) { + QUrl faceModel(faceModelURL); + if (faceModel.isValid()) { + setFaceModelURL(faceModel); + } + } + } if (json.contains(JSON_AVATAR_BODY_MODEL)) { auto bodyModelURL = json[JSON_AVATAR_BODY_MODEL].toString(); if (bodyModelURL != getSkeletonModelURL().toString()) { diff --git a/libraries/avatars/src/AvatarData.h b/libraries/avatars/src/AvatarData.h index 25ee93485b..6ffcaed0da 100644 --- a/libraries/avatars/src/AvatarData.h +++ b/libraries/avatars/src/AvatarData.h @@ -158,6 +158,7 @@ class AvatarData : public QObject, public SpatiallyNestable { Q_PROPERTY(float audioAverageLoudness READ getAudioAverageLoudness WRITE setAudioAverageLoudness) Q_PROPERTY(QString displayName READ getDisplayName WRITE setDisplayName) + Q_PROPERTY(QString faceModelURL READ getFaceModelURLFromScript WRITE setFaceModelURLFromScript) Q_PROPERTY(QString skeletonModelURL READ getSkeletonModelURLFromScript WRITE setSkeletonModelURLFromScript) Q_PROPERTY(QVector attachmentData READ getAttachmentData WRITE setAttachmentData) Q_PROPERTY(QString billboardURL READ getBillboardURL WRITE setBillboardFromURL) @@ -295,8 +296,11 @@ public: bool hasBillboardChangedAfterParsing(const QByteArray& data); + const QUrl& getFaceModelURL() const { return _faceModelURL; } + QString getFaceModelURLString() const { return _faceModelURL.toString(); } const QUrl& getSkeletonModelURL() const { return _skeletonModelURL; } const QString& getDisplayName() const { return _displayName; } + virtual void setFaceModelURL(const QUrl& faceModelURL); virtual void setSkeletonModelURL(const QUrl& skeletonModelURL); virtual void setDisplayName(const QString& displayName); @@ -318,6 +322,9 @@ public: void setBillboardFromURL(const QString& billboardURL); const QString& getBillboardURL() { return _billboardURL; } + QString getFaceModelURLFromScript() const { return _faceModelURL.toString(); } + void setFaceModelURLFromScript(const QString& faceModelString) { setFaceModelURL(faceModelString); } + QString getSkeletonModelURLFromScript() const { return _skeletonModelURL.toString(); } void setSkeletonModelURLFromScript(const QString& skeletonModelString) { setSkeletonModelURL(QUrl(skeletonModelString)); } @@ -376,6 +383,7 @@ protected: HeadData* _headData; + QUrl _faceModelURL; // These need to be empty so that on first time setting them they will not short circuit QUrl _skeletonModelURL; // These need to be empty so that on first time setting them they will not short circuit QUrl _skeletonFBXURL; QVector _attachmentData; diff --git a/libraries/avatars/src/AvatarHashMap.cpp b/libraries/avatars/src/AvatarHashMap.cpp index 75fb5e6028..ef7ff9684a 100644 --- a/libraries/avatars/src/AvatarHashMap.cpp +++ b/libraries/avatars/src/AvatarHashMap.cpp @@ -122,6 +122,10 @@ void AvatarHashMap::processAvatarIdentityPacket(QSharedPointer // mesh URL for a UUID, find avatar in our list auto avatar = newOrExistingAvatar(sessionUUID, sendingNode); + if (avatar->getFaceModelURL() != faceMeshURL) { + avatar->setFaceModelURL(faceMeshURL); + } + if (avatar->getSkeletonModelURL().isEmpty() || (avatar->getSkeletonModelURL() != skeletonURL)) { avatar->setSkeletonModelURL(skeletonURL); // Will expand "" to default and so will not continuously fire } diff --git a/libraries/entities-renderer/src/EntityTreeRenderer.cpp b/libraries/entities-renderer/src/EntityTreeRenderer.cpp index 22695663e3..4a9c37debf 100644 --- a/libraries/entities-renderer/src/EntityTreeRenderer.cpp +++ b/libraries/entities-renderer/src/EntityTreeRenderer.cpp @@ -420,7 +420,7 @@ const FBXGeometry* EntityTreeRenderer::getGeometryForEntity(EntityItemPointer en std::shared_ptr modelEntityItem = std::dynamic_pointer_cast(entityItem); assert(modelEntityItem); // we need this!!! - ModelPointer model = modelEntityItem->getModel(this); + Model* model = modelEntityItem->getModel(this); if (model) { result = &model->getGeometry()->getFBXGeometry(); } @@ -428,8 +428,8 @@ const FBXGeometry* EntityTreeRenderer::getGeometryForEntity(EntityItemPointer en return result; } -ModelPointer EntityTreeRenderer::getModelForEntityItem(EntityItemPointer entityItem) { - ModelPointer result = nullptr; +const Model* EntityTreeRenderer::getModelForEntityItem(EntityItemPointer entityItem) { + const Model* result = NULL; if (entityItem->getType() == EntityTypes::Model) { std::shared_ptr modelEntityItem = std::dynamic_pointer_cast(entityItem); @@ -445,7 +445,7 @@ const FBXGeometry* EntityTreeRenderer::getCollisionGeometryForEntity(EntityItemP std::shared_ptr modelEntityItem = std::dynamic_pointer_cast(entityItem); if (modelEntityItem->hasCompoundShapeURL()) { - ModelPointer model = modelEntityItem->getModel(this); + Model* model = modelEntityItem->getModel(this); if (model) { const QSharedPointer collisionNetworkGeometry = model->getCollisionGeometry(); if (collisionNetworkGeometry && collisionNetworkGeometry->isLoaded()) { @@ -461,25 +461,25 @@ void EntityTreeRenderer::processEraseMessage(ReceivedMessage& message, const Sha std::static_pointer_cast(_tree)->processEraseMessage(message, sourceNode); } -ModelPointer EntityTreeRenderer::allocateModel(const QString& url, const QString& collisionUrl) { - ModelPointer model = nullptr; +Model* EntityTreeRenderer::allocateModel(const QString& url, const QString& collisionUrl) { + Model* model = NULL; // Make sure we only create and delete models on the thread that owns the EntityTreeRenderer if (QThread::currentThread() != thread()) { QMetaObject::invokeMethod(this, "allocateModel", Qt::BlockingQueuedConnection, - Q_RETURN_ARG(ModelPointer, model), + Q_RETURN_ARG(Model*, model), Q_ARG(const QString&, url)); return model; } - model = std::make_shared(std::make_shared()); + model = new Model(std::make_shared()); model->init(); model->setURL(QUrl(url)); model->setCollisionModelURL(QUrl(collisionUrl)); return model; } -ModelPointer EntityTreeRenderer::updateModel(ModelPointer original, const QString& newUrl, const QString& collisionUrl) { - ModelPointer model = nullptr; +Model* EntityTreeRenderer::updateModel(Model* original, const QString& newUrl, const QString& collisionUrl) { + Model* model = NULL; // The caller shouldn't call us if the URL doesn't need to change. But if they // do, we just return their original back to them. @@ -490,8 +490,8 @@ ModelPointer EntityTreeRenderer::updateModel(ModelPointer original, const QStrin // Before we do any creating or deleting, make sure we're on our renderer thread if (QThread::currentThread() != thread()) { QMetaObject::invokeMethod(this, "updateModel", Qt::BlockingQueuedConnection, - Q_RETURN_ARG(ModelPointer, model), - Q_ARG(ModelPointer, original), + Q_RETURN_ARG(Model*, model), + Q_ARG(Model*, original), Q_ARG(const QString&, newUrl)); return model; @@ -500,11 +500,11 @@ ModelPointer EntityTreeRenderer::updateModel(ModelPointer original, const QStrin // at this point we know we need to replace the model, and we know we're on the // correct thread, so we can do all our work. if (original) { - original.reset(); // delete the old model... + delete original; // delete the old model... } // create the model and correctly initialize it with the new url - model = std::make_shared(std::make_shared()); + model = new Model(std::make_shared()); model->init(); model->setURL(QUrl(newUrl)); model->setCollisionModelURL(QUrl(collisionUrl)); @@ -512,19 +512,19 @@ ModelPointer EntityTreeRenderer::updateModel(ModelPointer original, const QStrin return model; } -void EntityTreeRenderer::releaseModel(ModelPointer model) { +void EntityTreeRenderer::releaseModel(Model* model) { // If we're not on the renderer's thread, then remember this model to be deleted later if (QThread::currentThread() != thread()) { _releasedModels << model; } else { // otherwise just delete it right away - model.reset(); + delete model; } } void EntityTreeRenderer::deleteReleasedModels() { if (_releasedModels.size() > 0) { - foreach(ModelPointer model, _releasedModels) { - model.reset(); + foreach(Model* model, _releasedModels) { + delete model; } _releasedModels.clear(); } diff --git a/libraries/entities-renderer/src/EntityTreeRenderer.h b/libraries/entities-renderer/src/EntityTreeRenderer.h index 095723dc9a..8534be852d 100644 --- a/libraries/entities-renderer/src/EntityTreeRenderer.h +++ b/libraries/entities-renderer/src/EntityTreeRenderer.h @@ -29,9 +29,6 @@ class Model; class ScriptEngine; class ZoneEntityItem; -class Model; -using ModelPointer = std::shared_ptr; -using ModelWeakPointer = std::weak_ptr; // Generic client side Octree renderer class. class EntityTreeRenderer : public OctreeRenderer, public EntityItemFBXService, public Dependency { @@ -56,7 +53,7 @@ public: virtual void init(); virtual const FBXGeometry* getGeometryForEntity(EntityItemPointer entityItem); - virtual ModelPointer getModelForEntityItem(EntityItemPointer entityItem); + virtual const Model* getModelForEntityItem(EntityItemPointer entityItem); virtual const FBXGeometry* getCollisionGeometryForEntity(EntityItemPointer entityItem); /// clears the tree @@ -66,13 +63,13 @@ public: void reloadEntityScripts(); /// if a renderable entity item needs a model, we will allocate it for them - Q_INVOKABLE ModelPointer allocateModel(const QString& url, const QString& collisionUrl); + Q_INVOKABLE Model* allocateModel(const QString& url, const QString& collisionUrl); /// if a renderable entity item needs to update the URL of a model, we will handle that for the entity - Q_INVOKABLE ModelPointer updateModel(ModelPointer original, const QString& newUrl, const QString& collisionUrl); + Q_INVOKABLE Model* updateModel(Model* original, const QString& newUrl, const QString& collisionUrl); /// if a renderable entity item is done with a model, it should return it to us - void releaseModel(ModelPointer model); + void releaseModel(Model* model); void deleteReleasedModels(); @@ -132,7 +129,7 @@ private: void applyZonePropertiesToScene(std::shared_ptr zone); void checkAndCallPreload(const EntityItemID& entityID, const bool reload = false); - QList _releasedModels; + QList _releasedModels; RayToEntityIntersectionResult findRayIntersectionWorker(const PickRay& ray, Octree::lockType lockType, bool precisionPicking, const QVector& entityIdsToInclude = QVector(), const QVector& entityIdsToDiscard = QVector()); diff --git a/libraries/entities-renderer/src/RenderableModelEntityItem.cpp b/libraries/entities-renderer/src/RenderableModelEntityItem.cpp index ff4ed28150..e2c7bb56c1 100644 --- a/libraries/entities-renderer/src/RenderableModelEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableModelEntityItem.cpp @@ -463,8 +463,8 @@ void RenderableModelEntityItem::render(RenderArgs* args) { } } -ModelPointer RenderableModelEntityItem::getModel(EntityTreeRenderer* renderer) { - ModelPointer result = nullptr; +Model* RenderableModelEntityItem::getModel(EntityTreeRenderer* renderer) { + Model* result = NULL; if (!renderer) { return result; @@ -506,7 +506,7 @@ ModelPointer RenderableModelEntityItem::getModel(EntityTreeRenderer* renderer) { // release interest _myRenderer->releaseModel(_model); - result = _model = nullptr; + result = _model = NULL; _needsInitialSimulation = true; } diff --git a/libraries/entities-renderer/src/RenderableModelEntityItem.h b/libraries/entities-renderer/src/RenderableModelEntityItem.h index 03226342da..69c1c13151 100644 --- a/libraries/entities-renderer/src/RenderableModelEntityItem.h +++ b/libraries/entities-renderer/src/RenderableModelEntityItem.h @@ -57,7 +57,7 @@ public: BoxFace& face, glm::vec3& surfaceNormal, void** intersectedObject, bool precisionPicking) const override; - ModelPointer getModel(EntityTreeRenderer* renderer); + Model* getModel(EntityTreeRenderer* renderer); virtual bool needsToCallUpdate() const override; virtual void update(const quint64& now) override; @@ -87,7 +87,7 @@ private: QVariantMap parseTexturesToMap(QString textures); void remapTextures(); - ModelPointer _model = nullptr; + Model* _model = nullptr; bool _needsInitialSimulation = true; bool _needsModelReload = true; EntityTreeRenderer* _myRenderer = nullptr; diff --git a/libraries/entities/src/EntityTree.h b/libraries/entities/src/EntityTree.h index 677ab93b79..1c5a696b17 100644 --- a/libraries/entities/src/EntityTree.h +++ b/libraries/entities/src/EntityTree.h @@ -25,9 +25,6 @@ typedef std::shared_ptr EntityTreePointer; #include "DeleteEntityOperator.h" class Model; -using ModelPointer = std::shared_ptr; -using ModelWeakPointer = std::weak_ptr; - class EntitySimulation; class NewlyCreatedEntityHook { @@ -38,7 +35,7 @@ public: class EntityItemFBXService { public: virtual const FBXGeometry* getGeometryForEntity(EntityItemPointer entityItem) = 0; - virtual ModelPointer getModelForEntityItem(EntityItemPointer entityItem) = 0; + virtual const Model* getModelForEntityItem(EntityItemPointer entityItem) = 0; virtual const FBXGeometry* getCollisionGeometryForEntity(EntityItemPointer entityItem) = 0; }; @@ -174,7 +171,7 @@ public: const FBXGeometry* getGeometryForEntity(EntityItemPointer entityItem) { return _fbxService ? _fbxService->getGeometryForEntity(entityItem) : NULL; } - ModelPointer getModelForEntityItem(EntityItemPointer entityItem) { + const Model* getModelForEntityItem(EntityItemPointer entityItem) { return _fbxService ? _fbxService->getModelForEntityItem(entityItem) : NULL; } diff --git a/libraries/fbx/src/FBXReader.cpp b/libraries/fbx/src/FBXReader.cpp index b2ede33e01..cf11706ede 100644 --- a/libraries/fbx/src/FBXReader.cpp +++ b/libraries/fbx/src/FBXReader.cpp @@ -48,11 +48,9 @@ QStringList FBXGeometry::getJointNames() const { } bool FBXGeometry::hasBlendedMeshes() const { - if (!meshes.isEmpty()) { - foreach (const FBXMesh& mesh, meshes) { - if (!mesh.blendshapes.isEmpty()) { - return true; - } + foreach (const FBXMesh& mesh, meshes) { + if (!mesh.blendshapes.isEmpty()) { + return true; } } return false; diff --git a/libraries/render-utils/src/Model.cpp b/libraries/render-utils/src/Model.cpp index d9c79f0722..4c487bc79c 100644 --- a/libraries/render-utils/src/Model.cpp +++ b/libraries/render-utils/src/Model.cpp @@ -1069,7 +1069,7 @@ void Model::updateClusterMatrices(glm::vec3 modelPosition, glm::quat modelOrient // post the blender if we're not currently waiting for one to finish if (geometry.hasBlendedMeshes() && _blendshapeCoefficients != _blendedBlendshapeCoefficients) { _blendedBlendshapeCoefficients = _blendshapeCoefficients; - DependencyManager::get()->noteRequiresBlend(getThisPointer()); + DependencyManager::get()->noteRequiresBlend(this); } } @@ -1277,14 +1277,16 @@ ModelBlender::ModelBlender() : ModelBlender::~ModelBlender() { } -void ModelBlender::noteRequiresBlend(ModelPointer model) { +void ModelBlender::noteRequiresBlend(Model* model) { if (_pendingBlenders < QThread::idealThreadCount()) { if (model->maybeStartBlender()) { _pendingBlenders++; } return; } - _modelsRequiringBlends.insert(model); + if (!_modelsRequiringBlends.contains(model)) { + _modelsRequiringBlends.append(model); + } } void ModelBlender::setBlendedVertices(const QPointer& model, int blendNumber, @@ -1293,9 +1295,8 @@ void ModelBlender::setBlendedVertices(const QPointer& model, int blendNum model->setBlendedVertices(blendNumber, geometry, vertices, normals); } _pendingBlenders--; - while (!_modelsRequiringBlends.empty()) { - auto fistItem = _modelsRequiringBlends.begin(); - ModelPointer nextModel = fistItem->lock(); + while (!_modelsRequiringBlends.isEmpty()) { + Model* nextModel = _modelsRequiringBlends.takeFirst(); if (nextModel && nextModel->maybeStartBlender()) { _pendingBlenders++; return; diff --git a/libraries/render-utils/src/Model.h b/libraries/render-utils/src/Model.h index 1e8b3f2af6..e90d51813b 100644 --- a/libraries/render-utils/src/Model.h +++ b/libraries/render-utils/src/Model.h @@ -50,13 +50,8 @@ inline uint qHash(const std::shared_ptr& a, uint seed) { return qHash(a.get(), seed); } -class Model; -using ModelPointer = std::shared_ptr; -using ModelWeakPointer = std::weak_ptr; - - /// A generic 3D model displaying geometry loaded from a URL. -class Model : public QObject, public std::enable_shared_from_this { +class Model : public QObject { Q_OBJECT public: @@ -68,9 +63,6 @@ public: Model(RigPointer rig, QObject* parent = nullptr); virtual ~Model(); - inline ModelPointer getThisPointer() const { - return std::static_pointer_cast(std::const_pointer_cast(shared_from_this())); - } /// Sets the URL of the model to render. Q_INVOKABLE void setURL(const QUrl& url); @@ -395,7 +387,7 @@ class ModelBlender : public QObject, public Dependency { public: /// Adds the specified model to the list requiring vertex blends. - void noteRequiresBlend(ModelPointer model); + void noteRequiresBlend(Model* model); public slots: void setBlendedVertices(const QPointer& model, int blendNumber, const QWeakPointer& geometry, @@ -405,7 +397,7 @@ private: ModelBlender(); virtual ~ModelBlender(); - std::set> _modelsRequiringBlends; + QList > _modelsRequiringBlends; int _pendingBlenders; };