From 850fb51f729767635c8dc2b4a2b8521916e85abb Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Mon, 14 Mar 2016 14:45:00 -0700 Subject: [PATCH 01/30] Fix keylight direction naming and usage --- examples/html/entityProperties.html | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/examples/html/entityProperties.html b/examples/html/entityProperties.html index 1101a08acb..6f19bafdfb 100644 --- a/examples/html/entityProperties.html +++ b/examples/html/entityProperties.html @@ -151,7 +151,7 @@ data.properties[group][property] = { x: elX.value, y: elY.value, - z: elZ.value, + z: elZ ? elZ.value : 0, }; EventBridge.emitWebEvent(JSON.stringify(data)); } @@ -626,7 +626,6 @@ elZoneKeyLightAmbientIntensity.value = properties.keyLight.ambientIntensity.toFixed(2); elZoneKeyLightDirectionX.value = properties.keyLight.direction.x.toFixed(2); elZoneKeyLightDirectionY.value = properties.keyLight.direction.y.toFixed(2); - elZoneKeyLightDirectionZ.value = properties.keyLight.direction.z.toFixed(2); elZoneKeyLightAmbientURL.value = properties.keyLight.ambientURL; @@ -874,12 +873,10 @@ elZoneKeyLightColorBlue.addEventListener('change', zoneKeyLightColorChangeFunction); elZoneKeyLightIntensity.addEventListener('change', createEmitGroupNumberPropertyUpdateFunction('keyLight','intensity')); elZoneKeyLightAmbientIntensity.addEventListener('change', createEmitGroupNumberPropertyUpdateFunction('keyLight','ambientIntensity')); - var zoneKeyLightDirectionChangeFunction = createEmitGroupVec3PropertyUpdateFunction('keyLight','direction', elZoneKeyLightDirectionX, elZoneKeyLightDirectionY, elZoneKeyLightDirectionZ); + var zoneKeyLightDirectionChangeFunction = createEmitGroupVec3PropertyUpdateFunction('keyLight','direction', elZoneKeyLightDirectionX, elZoneKeyLightDirectionY); elZoneKeyLightDirectionX.addEventListener('change', zoneKeyLightDirectionChangeFunction); elZoneKeyLightDirectionY.addEventListener('change', zoneKeyLightDirectionChangeFunction); - elZoneKeyLightDirectionZ.addEventListener('change', zoneKeyLightDirectionChangeFunction); - elZoneKeyLightAmbientURL.addEventListener('change', createEmitGroupTextPropertyUpdateFunction('keyLight','ambientURL')); - + elZoneStageLatitude.addEventListener('change', createEmitGroupNumberPropertyUpdateFunction('stage','latitude')); elZoneStageLongitude.addEventListener('change', createEmitGroupNumberPropertyUpdateFunction('stage','longitude')); elZoneStageAltitude.addEventListener('change', createEmitGroupNumberPropertyUpdateFunction('stage','altitude')); @@ -1107,9 +1104,8 @@
Light Direction
-
Pitch
-
Yaw
-
Roll
+
Altitude
+
Azimuth
From bc2aea87748d685a9ac1936e62b5fb85db50c63c Mon Sep 17 00:00:00 2001 From: Mark Johnson Date: Mon, 14 Mar 2016 18:18:33 -0700 Subject: [PATCH 02/30] Show the (generally truncated) urls being downloaded --- interface/resources/qml/Stats.qml | 22 ++++++++++++++++++++++ interface/src/ui/Stats.cpp | 28 ++++++++++++++++++++++++---- interface/src/ui/Stats.h | 7 ++++++- 3 files changed, 52 insertions(+), 5 deletions(-) diff --git a/interface/resources/qml/Stats.qml b/interface/resources/qml/Stats.qml index 696695de68..002e0fa404 100644 --- a/interface/resources/qml/Stats.qml +++ b/interface/resources/qml/Stats.qml @@ -169,6 +169,28 @@ Item { text: "Downloads: " + root.downloads + "/" + root.downloadLimit + ", Pending: " + root.downloadsPending; } + Text { + color: root.fontColor; + font.pixelSize: root.fontSize + visible: root.expanded && root.downloadUrls.length > 0; + text: "Download URLs:" + } + ListView { + width: geoCol.width + height: root.downloadUrls.length * 15 + + visible: root.expanded && root.downloadUrls.length > 0; + + model: root.downloadUrls + delegate: Text { + color: root.fontColor; + font.pixelSize: root.fontSize + visible: root.expanded; + text: modelData.length > 30 + ? "..." + modelData.substring(modelData.length - 27) + : modelData + } + } } } Rectangle { diff --git a/interface/src/ui/Stats.cpp b/interface/src/ui/Stats.cpp index 23d2b87194..1dbd22c927 100644 --- a/interface/src/ui/Stats.cpp +++ b/interface/src/ui/Stats.cpp @@ -156,7 +156,7 @@ void Stats::updateStats(bool force) { } } }); - + // update the entities ping with the average for all connected entity servers STAT_UPDATE(entitiesPing, octreeServerCount ? totalPingOctree / octreeServerCount : -1); @@ -192,9 +192,29 @@ void Stats::updateStats(bool force) { STAT_UPDATE(audioMixerPps, -1); } - STAT_UPDATE(downloads, ResourceCache::getLoadingRequests().size()); + QList loadingRequests = ResourceCache::getLoadingRequests(); + STAT_UPDATE(downloads, loadingRequests.size()); STAT_UPDATE(downloadLimit, ResourceCache::getRequestLimit()) STAT_UPDATE(downloadsPending, ResourceCache::getPendingRequestCount()); + + // See if the active download urls have changed + bool updateUrls = _downloads != _downloadUrls.size(); + if (!updateUrls) { + for (int ii = 0; ii < _downloads; ii++) { + if (loadingRequests[ii]->getURL().toString() != _downloadUrls[ii]) { + updateUrls = true; + break; + } + } + } + // If the urls have changed, update the list + if (updateUrls) { + _downloadUrls.clear(); + foreach (Resource* resource, loadingRequests) { + _downloadUrls << resource->getURL().toString(); + } + emit downloadUrlsChanged(); + } // TODO fix to match original behavior //stringstream downloads; //downloads << "Downloads: "; @@ -306,7 +326,7 @@ void Stats::updateStats(bool force) { // we will also include room for 1 line per timing record and a header of 4 lines // Timing details... - // First iterate all the records, and for the ones that should be included, insert them into + // First iterate all the records, and for the ones that should be included, insert them into // a new Map sorted by average time... bool onlyDisplayTopTen = Menu::getInstance()->isOptionChecked(MenuOption::OnlyDisplayTopTen); QMap sortedRecords; @@ -366,7 +386,7 @@ void Stats::setRenderDetails(const RenderDetails& details) { /* // display expanded or contracted stats void Stats::display( - int voxelPacketsToProcess) + int voxelPacketsToProcess) { // iterate all the current voxel stats, and list their sending modes, and total voxel counts diff --git a/interface/src/ui/Stats.h b/interface/src/ui/Stats.h index c6bc13b52c..61069f2ab1 100644 --- a/interface/src/ui/Stats.h +++ b/interface/src/ui/Stats.h @@ -19,7 +19,7 @@ public: \ type name() { return _##name; }; \ private: \ - type _##name{ initialValue }; + type _##name{ initialValue }; class Stats : public QQuickItem { @@ -58,6 +58,7 @@ class Stats : public QQuickItem { STATS_PROPERTY(int, downloads, 0) STATS_PROPERTY(int, downloadLimit, 0) STATS_PROPERTY(int, downloadsPending, 0) + Q_PROPERTY(QStringList downloadUrls READ downloadUrls NOTIFY downloadUrlsChanged) STATS_PROPERTY(int, triangles, 0) STATS_PROPERTY(int, quads, 0) STATS_PROPERTY(int, materialSwitches, 0) @@ -105,6 +106,8 @@ public: } } + QStringList downloadUrls () { return _downloadUrls; } + public slots: void forceUpdateStats() { updateStats(true); } @@ -138,6 +141,7 @@ signals: void downloadsChanged(); void downloadLimitChanged(); void downloadsPendingChanged(); + void downloadUrlsChanged(); void trianglesChanged(); void quadsChanged(); void materialSwitchesChanged(); @@ -167,6 +171,7 @@ private: bool _timingExpanded{ false }; QString _monospaceFont; const AudioIOStats* _audioStats; + QStringList _downloadUrls = QStringList(); }; #endif // hifi_Stats_h From 6094613d069abe5866ff7d9a0a862fd81c2fc3e6 Mon Sep 17 00:00:00 2001 From: Mark Johnson Date: Tue, 15 Mar 2016 13:43:42 -0700 Subject: [PATCH 03/30] When truncating the URL, show the first 5 characters (for the protocol) --- interface/resources/qml/Stats.qml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/interface/resources/qml/Stats.qml b/interface/resources/qml/Stats.qml index 002e0fa404..7412e0168e 100644 --- a/interface/resources/qml/Stats.qml +++ b/interface/resources/qml/Stats.qml @@ -187,7 +187,7 @@ Item { font.pixelSize: root.fontSize visible: root.expanded; text: modelData.length > 30 - ? "..." + modelData.substring(modelData.length - 27) + ? modelData.substring(0, 5) + "..." + modelData.substring(modelData.length - 22) : modelData } } From 66f9effd124eb1daeb8715b0f6bfd2ea5a9f520c Mon Sep 17 00:00:00 2001 From: Mark Johnson Date: Wed, 16 Mar 2016 15:15:42 -0700 Subject: [PATCH 04/30] Update to match coding standards - Single letter for loop variable names - Preface bool variables with is/has/should/can/want --- interface/src/ui/Stats.cpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/interface/src/ui/Stats.cpp b/interface/src/ui/Stats.cpp index 1dbd22c927..c951c9fb43 100644 --- a/interface/src/ui/Stats.cpp +++ b/interface/src/ui/Stats.cpp @@ -198,17 +198,17 @@ void Stats::updateStats(bool force) { STAT_UPDATE(downloadsPending, ResourceCache::getPendingRequestCount()); // See if the active download urls have changed - bool updateUrls = _downloads != _downloadUrls.size(); - if (!updateUrls) { - for (int ii = 0; ii < _downloads; ii++) { - if (loadingRequests[ii]->getURL().toString() != _downloadUrls[ii]) { - updateUrls = true; + bool shouldUpdateUrls = _downloads != _downloadUrls.size(); + if (!shouldUpdateUrls) { + for (int i = 0; i < _downloads; i++) { + if (loadingRequests[i]->getURL().toString() != _downloadUrls[i]) { + shouldUpdateUrls = true; break; } } } // If the urls have changed, update the list - if (updateUrls) { + if (shouldUpdateUrls) { _downloadUrls.clear(); foreach (Resource* resource, loadingRequests) { _downloadUrls << resource->getURL().toString(); From 60f22c51b94a5be0f3f9b8a9f71ff69fad09db97 Mon Sep 17 00:00:00 2001 From: Zach Pomerantz Date: Thu, 17 Mar 2016 16:27:36 -0700 Subject: [PATCH 05/30] Check qml gl context on create --- libraries/gl/src/gl/OffscreenGLCanvas.cpp | 18 ++++++++++++++---- libraries/gl/src/gl/OffscreenGLCanvas.h | 2 +- libraries/gl/src/gl/OffscreenQmlSurface.cpp | 8 ++++++-- 3 files changed, 21 insertions(+), 7 deletions(-) diff --git a/libraries/gl/src/gl/OffscreenGLCanvas.cpp b/libraries/gl/src/gl/OffscreenGLCanvas.cpp index 6ce80296e9..1b10d6b032 100644 --- a/libraries/gl/src/gl/OffscreenGLCanvas.cpp +++ b/libraries/gl/src/gl/OffscreenGLCanvas.cpp @@ -32,17 +32,27 @@ OffscreenGLCanvas::~OffscreenGLCanvas() { _context->doneCurrent(); } -void OffscreenGLCanvas::create(QOpenGLContext* sharedContext) { +bool OffscreenGLCanvas::create(QOpenGLContext* sharedContext) { if (nullptr != sharedContext) { sharedContext->doneCurrent(); _context->setShareContext(sharedContext); } _context->setFormat(getDefaultOpenGLSurfaceFormat()); - _context->create(); + if (_context->create()) { + _offscreenSurface->setFormat(_context->format()); + _offscreenSurface->create(); + return true; + } - _offscreenSurface->setFormat(_context->format()); - _offscreenSurface->create(); + std::call_once(_reportOnce, []{ + qWarning() << "GL Version: " << QString((const char*) glGetString(GL_VERSION)); + qWarning() << "GL Shader Language Version: " << QString((const char*) glGetString(GL_SHADING_LANGUAGE_VERSION)); + qWarning() << "GL Vendor: " << QString((const char*) glGetString(GL_VENDOR)); + qWarning() << "GL Renderer: " << QString((const char*) glGetString(GL_RENDERER)); + qWarning() << "Failed to create OffscreenGLCanvas"; + }); + return false; } bool OffscreenGLCanvas::makeCurrent() { diff --git a/libraries/gl/src/gl/OffscreenGLCanvas.h b/libraries/gl/src/gl/OffscreenGLCanvas.h index 0f02e7c76f..9858c7cefa 100644 --- a/libraries/gl/src/gl/OffscreenGLCanvas.h +++ b/libraries/gl/src/gl/OffscreenGLCanvas.h @@ -23,7 +23,7 @@ class OffscreenGLCanvas : public QObject { public: OffscreenGLCanvas(); ~OffscreenGLCanvas(); - void create(QOpenGLContext* sharedContext = nullptr); + bool create(QOpenGLContext* sharedContext = nullptr); bool makeCurrent(); void doneCurrent(); QOpenGLContext* getContext() { diff --git a/libraries/gl/src/gl/OffscreenQmlSurface.cpp b/libraries/gl/src/gl/OffscreenQmlSurface.cpp index 6a780d74e2..33287493d4 100644 --- a/libraries/gl/src/gl/OffscreenQmlSurface.cpp +++ b/libraries/gl/src/gl/OffscreenQmlSurface.cpp @@ -65,7 +65,11 @@ class OffscreenQmlRenderer : public OffscreenGLCanvas { public: OffscreenQmlRenderer(OffscreenQmlSurface* surface, QOpenGLContext* shareContext) : _surface(surface) { - OffscreenGLCanvas::create(shareContext); + if (!OffscreenGLCanvas::create(shareContext)) { + static const char* error = "Failed to create OffscreenGLCanvas"; + qWarning() << error; + throw error; + }; _renderControl = new QMyQuickRenderControl(); @@ -153,7 +157,7 @@ private: qWarning("Failed to make context current on render thread"); return; } - _renderControl->initialize(_context); + _renderControl->initialize(getContext()); setupFbo(); _escrow.setRecycler([this](GLuint texture){ _textures.recycleTexture(texture); From 7483b8546b8e5003f87ea814a90fce144edafad9 Mon Sep 17 00:00:00 2001 From: Brad Hefta-Gaub Date: Thu, 17 Mar 2016 17:05:35 -0700 Subject: [PATCH 06/30] remove FaceModel, make Model support shared_from_this() so we can ave shared and weak pointers to it --- assignment-client/src/Agent.cpp | 1 - interface/src/avatar/Avatar.cpp | 33 +---------- interface/src/avatar/Avatar.h | 1 - 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 | 16 ----- interface/src/avatar/MyAvatar.h | 3 - 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.h | 10 +++- 19 files changed, 59 insertions(+), 238 deletions(-) delete mode 100644 interface/src/avatar/FaceModel.cpp delete mode 100644 interface/src/avatar/FaceModel.h diff --git a/assignment-client/src/Agent.cpp b/assignment-client/src/Agent.cpp index 1955b8f0c8..2734cdf01f 100644 --- a/assignment-client/src/Agent.cpp +++ b/assignment-client/src/Agent.cpp @@ -222,7 +222,6 @@ 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/avatar/Avatar.cpp b/interface/src/avatar/Avatar.cpp index 2a94ed30e2..2c53847925 100644 --- a/interface/src/avatar/Avatar.cpp +++ b/interface/src/avatar/Avatar.cpp @@ -296,7 +296,6 @@ bool Avatar::addToScene(AvatarSharedPointer self, std::shared_ptr _renderItemID = scene->allocateID(); pendingChanges.resetItem(_renderItemID, avatarPayloadPointer); _skeletonModel.addToScene(scene, pendingChanges); - getHead()->getFaceModel().addToScene(scene, pendingChanges); for (auto& attachmentModel : _attachmentModels) { attachmentModel->addToScene(scene, pendingChanges); @@ -309,7 +308,6 @@ void Avatar::removeFromScene(AvatarSharedPointer self, std::shared_ptrgetFaceModel().removeFromScene(scene, pendingChanges); for (auto& attachmentModel : _attachmentModels) { attachmentModel->removeFromScene(scene, pendingChanges); } @@ -426,7 +424,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 = glm::mix(_skeletonModel.getTranslation(), getHead()->getFaceModel().getTranslation(), 0.9f); + glm::vec3 position = _skeletonModel.getTranslation(); glm::quat orientation = getOrientation(); foreach (const AvatarManager::LocalLight& light, DependencyManager::get()->getLocalLights()) { glm::vec3 direction = orientation * light.direction; @@ -543,11 +541,6 @@ void Avatar::fixupModelsInScene() { _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()) { attachmentModel->removeFromScene(scene, pendingChanges); @@ -564,14 +557,7 @@ 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); } @@ -872,11 +858,6 @@ 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); @@ -1009,17 +990,7 @@ float Avatar::getSkeletonHeight() const { } float Avatar::getHeadHeight() const { - 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(); + Extents extents = _skeletonModel.getMeshExtents(); glm::vec3 neckPosition; if (!extents.isEmpty() && extents.isValid() && _skeletonModel.getNeckPosition(neckPosition)) { return extents.maximum.y / 2.0f - neckPosition.y + getPosition().y; diff --git a/interface/src/avatar/Avatar.h b/interface/src/avatar/Avatar.h index 01548c9066..5860760659 100644 --- a/interface/src/avatar/Avatar.h +++ b/interface/src/avatar/Avatar.h @@ -114,7 +114,6 @@ 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; diff --git a/interface/src/avatar/FaceModel.cpp b/interface/src/avatar/FaceModel.cpp deleted file mode 100644 index 9c22fc17ac..0000000000 --- a/interface/src/avatar/FaceModel.cpp +++ /dev/null @@ -1,58 +0,0 @@ -// -// 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 deleted file mode 100644 index 5a19a8ea29..0000000000 --- a/interface/src/avatar/FaceModel.h +++ /dev/null @@ -1,38 +0,0 @@ -// -// 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 b97fd2b0ea..5524b928de 100644 --- a/interface/src/avatar/Head.cpp +++ b/interface/src/avatar/Head.cpp @@ -62,20 +62,17 @@ 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) { @@ -233,12 +230,6 @@ 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(); } @@ -411,7 +402,7 @@ glm::quat Head::getEyeRotation(const glm::vec3& eyePosition) const { } glm::vec3 Head::getScalePivot() const { - return _faceModel.isActive() ? _faceModel.getTranslation() : _position; + return _position; } void Head::setFinalPitch(float finalPitch) { diff --git a/interface/src/avatar/Head.h b/interface/src/avatar/Head.h index 614e286329..d0bd4fdb77 100644 --- a/interface/src/avatar/Head.h +++ b/interface/src/avatar/Head.h @@ -18,7 +18,6 @@ #include -#include "FaceModel.h" #include "world.h" @@ -76,9 +75,6 @@ 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. @@ -150,7 +146,6 @@ private: bool _isLookingAtMe; quint64 _lookingAtMeStarted; quint64 _wasLastLookingAtMe; - FaceModel _faceModel; glm::vec3 _correctedLookAtPosition; @@ -162,8 +157,6 @@ 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 7198f32422..fa8e94e4a4 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -167,11 +167,6 @@ 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())) { @@ -1009,13 +1004,6 @@ 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); @@ -1051,10 +1039,6 @@ 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); diff --git a/interface/src/avatar/MyAvatar.h b/interface/src/avatar/MyAvatar.h index 37a2e752e6..79cad466e6 100644 --- a/interface/src/avatar/MyAvatar.h +++ b/interface/src/avatar/MyAvatar.h @@ -178,8 +178,6 @@ 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(); } @@ -328,7 +326,6 @@ 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/libraries/avatars/src/AvatarData.cpp b/libraries/avatars/src/AvatarData.cpp index adb942417d..9ab6b3b89c 100644 --- a/libraries/avatars/src/AvatarData.cpp +++ b/libraries/avatars/src/AvatarData.cpp @@ -57,7 +57,6 @@ AvatarData::AvatarData() : _hasNewJointRotations(true), _hasNewJointTranslations(true), _headData(NULL), - _faceModelURL("http://invalid.com"), _displayNameTargetAlpha(1.0f), _displayNameAlpha(1.0f), _billboard(), @@ -989,18 +988,14 @@ bool AvatarData::hasIdentityChangedAfterParsing(const QByteArray& data) { QDataStream packetStream(data); QUuid avatarUUID; - QUrl faceModelURL, skeletonModelURL; + QUrl unusedModelURL; // legacy faceModel support + QUrl skeletonModelURL; QVector attachmentData; QString displayName; - packetStream >> avatarUUID >> faceModelURL >> skeletonModelURL >> attachmentData >> displayName; + packetStream >> avatarUUID >> unusedModelURL >> skeletonModelURL >> attachmentData >> displayName; bool hasIdentityChanged = false; - if (faceModelURL != _faceModelURL) { - setFaceModelURL(faceModelURL); - hasIdentityChanged = true; - } - if (skeletonModelURL != _skeletonModelURL) { setSkeletonModelURL(skeletonModelURL); hasIdentityChanged = true; @@ -1025,7 +1020,9 @@ QByteArray AvatarData::identityByteArray() { QUrl emptyURL(""); const QUrl& urlToSend = (_skeletonModelURL == AvatarData::defaultFullAvatarModelUrl()) ? emptyURL : _skeletonModelURL; - identityStream << QUuid() << _faceModelURL << urlToSend << _attachmentData << _displayName; + QUrl unusedModelURL("http://invalid.com"); // legacy faceModel support + + identityStream << QUuid() << unusedModelURL << urlToSend << _attachmentData << _displayName; return identityData; } @@ -1038,12 +1035,6 @@ 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) { @@ -1445,9 +1436,6 @@ 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(); } @@ -1514,15 +1502,6 @@ 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 6ffcaed0da..25ee93485b 100644 --- a/libraries/avatars/src/AvatarData.h +++ b/libraries/avatars/src/AvatarData.h @@ -158,7 +158,6 @@ 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) @@ -296,11 +295,8 @@ 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); @@ -322,9 +318,6 @@ 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)); } @@ -383,7 +376,6 @@ 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 ef7ff9684a..75fb5e6028 100644 --- a/libraries/avatars/src/AvatarHashMap.cpp +++ b/libraries/avatars/src/AvatarHashMap.cpp @@ -122,10 +122,6 @@ 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 ece43f3ab3..3285a1c661 100644 --- a/libraries/entities-renderer/src/EntityTreeRenderer.cpp +++ b/libraries/entities-renderer/src/EntityTreeRenderer.cpp @@ -419,7 +419,7 @@ const FBXGeometry* EntityTreeRenderer::getGeometryForEntity(EntityItemPointer en std::shared_ptr modelEntityItem = std::dynamic_pointer_cast(entityItem); assert(modelEntityItem); // we need this!!! - Model* model = modelEntityItem->getModel(this); + ModelPointer model = modelEntityItem->getModel(this); if (model) { result = &model->getGeometry()->getFBXGeometry(); } @@ -427,8 +427,8 @@ const FBXGeometry* EntityTreeRenderer::getGeometryForEntity(EntityItemPointer en return result; } -const Model* EntityTreeRenderer::getModelForEntityItem(EntityItemPointer entityItem) { - const Model* result = NULL; +ModelPointer EntityTreeRenderer::getModelForEntityItem(EntityItemPointer entityItem) { + ModelPointer result = nullptr; if (entityItem->getType() == EntityTypes::Model) { std::shared_ptr modelEntityItem = std::dynamic_pointer_cast(entityItem); @@ -444,7 +444,7 @@ const FBXGeometry* EntityTreeRenderer::getCollisionGeometryForEntity(EntityItemP std::shared_ptr modelEntityItem = std::dynamic_pointer_cast(entityItem); if (modelEntityItem->hasCompoundShapeURL()) { - Model* model = modelEntityItem->getModel(this); + ModelPointer model = modelEntityItem->getModel(this); if (model) { const QSharedPointer collisionNetworkGeometry = model->getCollisionGeometry(); if (collisionNetworkGeometry && collisionNetworkGeometry->isLoaded()) { @@ -460,25 +460,25 @@ void EntityTreeRenderer::processEraseMessage(ReceivedMessage& message, const Sha std::static_pointer_cast(_tree)->processEraseMessage(message, sourceNode); } -Model* EntityTreeRenderer::allocateModel(const QString& url, const QString& collisionUrl) { - Model* model = NULL; +ModelPointer EntityTreeRenderer::allocateModel(const QString& url, const QString& collisionUrl) { + ModelPointer model = nullptr; // 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(Model*, model), + Q_RETURN_ARG(ModelPointer, model), Q_ARG(const QString&, url)); return model; } - model = new Model(std::make_shared()); + model = std::make_shared(std::make_shared()); model->init(); model->setURL(QUrl(url)); model->setCollisionModelURL(QUrl(collisionUrl)); return model; } -Model* EntityTreeRenderer::updateModel(Model* original, const QString& newUrl, const QString& collisionUrl) { - Model* model = NULL; +ModelPointer EntityTreeRenderer::updateModel(ModelPointer original, const QString& newUrl, const QString& collisionUrl) { + ModelPointer model = nullptr; // 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. @@ -489,8 +489,8 @@ Model* EntityTreeRenderer::updateModel(Model* original, const QString& newUrl, c // 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(Model*, model), - Q_ARG(Model*, original), + Q_RETURN_ARG(ModelPointer, model), + Q_ARG(ModelPointer, original), Q_ARG(const QString&, newUrl)); return model; @@ -499,11 +499,11 @@ Model* EntityTreeRenderer::updateModel(Model* original, const QString& newUrl, c // 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) { - delete original; // delete the old model... + original.reset(); // delete the old model... } // create the model and correctly initialize it with the new url - model = new Model(std::make_shared()); + model = std::make_shared(std::make_shared()); model->init(); model->setURL(QUrl(newUrl)); model->setCollisionModelURL(QUrl(collisionUrl)); @@ -511,19 +511,19 @@ Model* EntityTreeRenderer::updateModel(Model* original, const QString& newUrl, c return model; } -void EntityTreeRenderer::releaseModel(Model* model) { +void EntityTreeRenderer::releaseModel(ModelPointer 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 - delete model; + model.reset(); } } void EntityTreeRenderer::deleteReleasedModels() { if (_releasedModels.size() > 0) { - foreach(Model* model, _releasedModels) { - delete model; + foreach(ModelPointer model, _releasedModels) { + model.reset(); } _releasedModels.clear(); } diff --git a/libraries/entities-renderer/src/EntityTreeRenderer.h b/libraries/entities-renderer/src/EntityTreeRenderer.h index 8534be852d..095723dc9a 100644 --- a/libraries/entities-renderer/src/EntityTreeRenderer.h +++ b/libraries/entities-renderer/src/EntityTreeRenderer.h @@ -29,6 +29,9 @@ 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 { @@ -53,7 +56,7 @@ public: virtual void init(); virtual const FBXGeometry* getGeometryForEntity(EntityItemPointer entityItem); - virtual const Model* getModelForEntityItem(EntityItemPointer entityItem); + virtual ModelPointer getModelForEntityItem(EntityItemPointer entityItem); virtual const FBXGeometry* getCollisionGeometryForEntity(EntityItemPointer entityItem); /// clears the tree @@ -63,13 +66,13 @@ public: void reloadEntityScripts(); /// if a renderable entity item needs a model, we will allocate it for them - Q_INVOKABLE Model* allocateModel(const QString& url, const QString& collisionUrl); + Q_INVOKABLE ModelPointer 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 Model* updateModel(Model* original, const QString& newUrl, const QString& collisionUrl); + Q_INVOKABLE ModelPointer updateModel(ModelPointer 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(Model* model); + void releaseModel(ModelPointer model); void deleteReleasedModels(); @@ -129,7 +132,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 e2c7bb56c1..ff4ed28150 100644 --- a/libraries/entities-renderer/src/RenderableModelEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableModelEntityItem.cpp @@ -463,8 +463,8 @@ void RenderableModelEntityItem::render(RenderArgs* args) { } } -Model* RenderableModelEntityItem::getModel(EntityTreeRenderer* renderer) { - Model* result = NULL; +ModelPointer RenderableModelEntityItem::getModel(EntityTreeRenderer* renderer) { + ModelPointer result = nullptr; if (!renderer) { return result; @@ -506,7 +506,7 @@ Model* RenderableModelEntityItem::getModel(EntityTreeRenderer* renderer) { // release interest _myRenderer->releaseModel(_model); - result = _model = NULL; + result = _model = nullptr; _needsInitialSimulation = true; } diff --git a/libraries/entities-renderer/src/RenderableModelEntityItem.h b/libraries/entities-renderer/src/RenderableModelEntityItem.h index 69c1c13151..03226342da 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; - Model* getModel(EntityTreeRenderer* renderer); + ModelPointer 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(); - Model* _model = nullptr; + ModelPointer _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 1c5a696b17..677ab93b79 100644 --- a/libraries/entities/src/EntityTree.h +++ b/libraries/entities/src/EntityTree.h @@ -25,6 +25,9 @@ typedef std::shared_ptr EntityTreePointer; #include "DeleteEntityOperator.h" class Model; +using ModelPointer = std::shared_ptr; +using ModelWeakPointer = std::weak_ptr; + class EntitySimulation; class NewlyCreatedEntityHook { @@ -35,7 +38,7 @@ public: class EntityItemFBXService { public: virtual const FBXGeometry* getGeometryForEntity(EntityItemPointer entityItem) = 0; - virtual const Model* getModelForEntityItem(EntityItemPointer entityItem) = 0; + virtual ModelPointer getModelForEntityItem(EntityItemPointer entityItem) = 0; virtual const FBXGeometry* getCollisionGeometryForEntity(EntityItemPointer entityItem) = 0; }; @@ -171,7 +174,7 @@ public: const FBXGeometry* getGeometryForEntity(EntityItemPointer entityItem) { return _fbxService ? _fbxService->getGeometryForEntity(entityItem) : NULL; } - const Model* getModelForEntityItem(EntityItemPointer entityItem) { + ModelPointer getModelForEntityItem(EntityItemPointer entityItem) { return _fbxService ? _fbxService->getModelForEntityItem(entityItem) : NULL; } diff --git a/libraries/fbx/src/FBXReader.cpp b/libraries/fbx/src/FBXReader.cpp index a63cf78393..09aa4d5f59 100644 --- a/libraries/fbx/src/FBXReader.cpp +++ b/libraries/fbx/src/FBXReader.cpp @@ -48,9 +48,11 @@ QStringList FBXGeometry::getJointNames() const { } bool FBXGeometry::hasBlendedMeshes() const { - foreach (const FBXMesh& mesh, meshes) { - if (!mesh.blendshapes.isEmpty()) { - return true; + if (!meshes.isEmpty()) { + foreach (const FBXMesh& mesh, meshes) { + if (!mesh.blendshapes.isEmpty()) { + return true; + } } } return false; diff --git a/libraries/render-utils/src/Model.h b/libraries/render-utils/src/Model.h index e90d51813b..58189c0d1e 100644 --- a/libraries/render-utils/src/Model.h +++ b/libraries/render-utils/src/Model.h @@ -50,8 +50,13 @@ 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 { +class Model : public QObject, public std::enable_shared_from_this { Q_OBJECT public: @@ -63,6 +68,9 @@ 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); From 917c4644a5dca03c7f38cac34bcb6d4e00a77310 Mon Sep 17 00:00:00 2001 From: Brad Hefta-Gaub Date: Thu, 17 Mar 2016 18:03:49 -0700 Subject: [PATCH 07/30] make SkeletonModel be shared_ptr friendly as well --- interface/src/Application.cpp | 5 +- interface/src/avatar/Avatar.cpp | 125 ++++++++++++++------------- interface/src/avatar/Avatar.h | 8 +- interface/src/avatar/MyAvatar.cpp | 48 +++++----- interface/src/avatar/MyAvatar.h | 2 +- interface/src/avatar/SkeletonModel.h | 4 + 6 files changed, 99 insertions(+), 93 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index bd2a43a678..a2f39f2a56 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -754,7 +754,8 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer) : connect(&nodeList->getPacketReceiver(), &PacketReceiver::dataReceived, bandwidthRecorder.data(), &BandwidthRecorder::updateInboundData); - connect(&getMyAvatar()->getSkeletonModel(), &SkeletonModel::skeletonLoaded, + // FIXME -- I'm a little concerned about this. + connect(getMyAvatar()->getSkeletonModel().get(), &SkeletonModel::skeletonLoaded, this, &Application::checkSkeleton, Qt::QueuedConnection); // Setup the userInputMapper with the actions @@ -4595,7 +4596,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 2c53847925..114f0c7cd0 100644 --- a/interface/src/avatar/Avatar.cpp +++ b/interface/src/avatar/Avatar.cpp @@ -79,7 +79,6 @@ namespace render { Avatar::Avatar(RigPointer rig) : AvatarData(), - _skeletonModel(this, nullptr, rig), _skeletonOffset(0.0f), _bodyYawDelta(0.0f), _positionDeltaAccumulator(0.0f), @@ -100,6 +99,8 @@ 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() { @@ -112,19 +113,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(); } @@ -137,10 +138,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) { @@ -191,8 +192,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; @@ -200,7 +201,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()); @@ -295,7 +296,7 @@ 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); for (auto& attachmentModel : _attachmentModels) { attachmentModel->addToScene(scene, pendingChanges); @@ -307,7 +308,7 @@ 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); for (auto& attachmentModel : _attachmentModels) { attachmentModel->removeFromScene(scene, pendingChanges); } @@ -338,12 +339,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) { @@ -362,12 +363,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) { @@ -424,7 +425,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 = _skeletonModel->getTranslation(); glm::quat orientation = getOrientation(); foreach (const AvatarManager::LocalLight& light, DependencyManager::get()->getLocalLights()) { glm::vec3 direction = orientation * light.direction; @@ -434,10 +435,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 @@ -466,7 +467,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; @@ -537,9 +538,9 @@ 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); } for (auto& attachmentModel : _attachmentModels) { if (attachmentModel->isRenderable() && attachmentModel->needsFixupInScene()) { @@ -579,8 +580,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 @@ -621,7 +622,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); @@ -766,46 +767,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; } @@ -816,7 +817,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 { @@ -826,7 +827,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 { @@ -837,7 +838,7 @@ glm::vec3 Avatar::getJointPosition(int index) const { return position; } glm::vec3 position; - _skeletonModel.getJointPositionInWorldFrame(index, position); + _skeletonModel->getJointPositionInWorldFrame(index, position); return position; } @@ -849,7 +850,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; } @@ -860,7 +861,7 @@ void Avatar::scaleVectorRelativeToPosition(glm::vec3 &positionToScale) const { 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 @@ -893,12 +894,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); } @@ -985,14 +986,14 @@ 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 = _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; } @@ -1001,7 +1002,7 @@ float Avatar::getHeadHeight() const { } float Avatar::getPelvisFloatingHeight() const { - return -_skeletonModel.getBindExtents().minimum.y; + return -_skeletonModel->getBindExtents().minimum.y; } void Avatar::setShowDisplayName(bool showDisplayName) { @@ -1029,9 +1030,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) { @@ -1069,12 +1070,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; } @@ -1082,7 +1083,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; } @@ -1090,12 +1091,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; } @@ -1103,7 +1104,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 5860760659..cb35fbb5eb 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; } - SkeletonModel& getSkeletonModel() { return _skeletonModel; } - const SkeletonModel& getSkeletonModel() const { return _skeletonModel; } + SkeletonModelPointer getSkeletonModel() { return _skeletonModel; } + const SkeletonModelPointer getSkeletonModel() const { return _skeletonModel; } glm::vec3 getChestPosition() const; float getUniformScale() const { return getScale().y; } const Head* getHead() const { return static_cast(_headData); } @@ -143,7 +143,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 +187,7 @@ protected: void setMotionState(AvatarMotionState* motionState); - SkeletonModel _skeletonModel; + SkeletonModelPointer _skeletonModel; glm::vec3 _skeletonOffset; std::vector> _attachmentModels; std::vector> _attachmentsToRemove; diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index fa8e94e4a4..74ad3064f7 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -232,7 +232,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 +338,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 +356,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 +711,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 +778,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 +801,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 +946,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); } } @@ -1008,7 +1008,7 @@ void MyAvatar::setSkeletonModelURL(const QUrl& skeletonModelURL) { Avatar::setSkeletonModelURL(skeletonModelURL); render::ScenePointer scene = qApp->getMain3DScene(); - _skeletonModel.setVisibleInScene(true, scene); + _skeletonModel->setVisibleInScene(true, scene); _headBoneSet.clear(); } @@ -1072,10 +1072,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 +1227,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 +1267,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 +1281,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 +1296,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 +1335,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 +1346,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 +1386,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 79cad466e6..3554d9b8bc 100644 --- a/interface/src/avatar/MyAvatar.h +++ b/interface/src/avatar/MyAvatar.h @@ -277,7 +277,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); diff --git a/interface/src/avatar/SkeletonModel.h b/interface/src/avatar/SkeletonModel.h index 6c6a7472f7..26b2e9c666 100644 --- a/interface/src/avatar/SkeletonModel.h +++ b/interface/src/avatar/SkeletonModel.h @@ -18,6 +18,10 @@ 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 From eb8a6527c68e8bf4c513ae0cbfe199370f42afb3 Mon Sep 17 00:00:00 2001 From: Brad Hefta-Gaub Date: Thu, 17 Mar 2016 19:44:42 -0700 Subject: [PATCH 08/30] change ModelBlender to use a set to safely handle model lifetime --- interface/src/avatar/SoftAttachmentModel.cpp | 2 +- libraries/render-utils/src/Model.cpp | 13 ++++++------- libraries/render-utils/src/Model.h | 4 ++-- 3 files changed, 9 insertions(+), 10 deletions(-) diff --git a/interface/src/avatar/SoftAttachmentModel.cpp b/interface/src/avatar/SoftAttachmentModel.cpp index 92534f01ea..8f0404e36c 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(this); + DependencyManager::get()->noteRequiresBlend(getThisPointer()); } } diff --git a/libraries/render-utils/src/Model.cpp b/libraries/render-utils/src/Model.cpp index 8b6cc41f35..03ffbb0789 100644 --- a/libraries/render-utils/src/Model.cpp +++ b/libraries/render-utils/src/Model.cpp @@ -1068,7 +1068,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(this); + DependencyManager::get()->noteRequiresBlend(getThisPointer()); } } @@ -1276,16 +1276,14 @@ ModelBlender::ModelBlender() : ModelBlender::~ModelBlender() { } -void ModelBlender::noteRequiresBlend(Model* model) { +void ModelBlender::noteRequiresBlend(ModelPointer model) { if (_pendingBlenders < QThread::idealThreadCount()) { if (model->maybeStartBlender()) { _pendingBlenders++; } return; } - if (!_modelsRequiringBlends.contains(model)) { - _modelsRequiringBlends.append(model); - } + _modelsRequiringBlends.insert(model); } void ModelBlender::setBlendedVertices(const QPointer& model, int blendNumber, @@ -1294,8 +1292,9 @@ void ModelBlender::setBlendedVertices(const QPointer& model, int blendNum model->setBlendedVertices(blendNumber, geometry, vertices, normals); } _pendingBlenders--; - while (!_modelsRequiringBlends.isEmpty()) { - Model* nextModel = _modelsRequiringBlends.takeFirst(); + while (!_modelsRequiringBlends.empty()) { + auto fistItem = _modelsRequiringBlends.begin(); + ModelPointer nextModel = fistItem->lock(); if (nextModel && nextModel->maybeStartBlender()) { _pendingBlenders++; return; diff --git a/libraries/render-utils/src/Model.h b/libraries/render-utils/src/Model.h index 58189c0d1e..1e8b3f2af6 100644 --- a/libraries/render-utils/src/Model.h +++ b/libraries/render-utils/src/Model.h @@ -395,7 +395,7 @@ class ModelBlender : public QObject, public Dependency { public: /// Adds the specified model to the list requiring vertex blends. - void noteRequiresBlend(Model* model); + void noteRequiresBlend(ModelPointer model); public slots: void setBlendedVertices(const QPointer& model, int blendNumber, const QWeakPointer& geometry, @@ -405,7 +405,7 @@ private: ModelBlender(); virtual ~ModelBlender(); - QList > _modelsRequiringBlends; + std::set> _modelsRequiringBlends; int _pendingBlenders; }; From 0474be59c90969793d49f155bd7ba7510b6b99ec Mon Sep 17 00:00:00 2001 From: Brad Hefta-Gaub Date: Thu, 17 Mar 2016 19:49:45 -0700 Subject: [PATCH 09/30] remove invalid.com url --- libraries/avatars/src/AvatarData.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/avatars/src/AvatarData.cpp b/libraries/avatars/src/AvatarData.cpp index 9ab6b3b89c..858a5fe192 100644 --- a/libraries/avatars/src/AvatarData.cpp +++ b/libraries/avatars/src/AvatarData.cpp @@ -1020,7 +1020,7 @@ QByteArray AvatarData::identityByteArray() { QUrl emptyURL(""); const QUrl& urlToSend = (_skeletonModelURL == AvatarData::defaultFullAvatarModelUrl()) ? emptyURL : _skeletonModelURL; - QUrl unusedModelURL("http://invalid.com"); // legacy faceModel support + QUrl unusedModelURL; // legacy faceModel support identityStream << QUuid() << unusedModelURL << urlToSend << _attachmentData << _displayName; From a0ba5d3c290920d78c829836eedb47216189b8d6 Mon Sep 17 00:00:00 2001 From: Zach Pomerantz Date: Fri, 18 Mar 2016 10:39:27 -0700 Subject: [PATCH 10/30] Look at whole tex for alpha mask --- libraries/model/src/model/TextureMap.cpp | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/libraries/model/src/model/TextureMap.cpp b/libraries/model/src/model/TextureMap.cpp index d0416ec3b5..5173af438c 100755 --- a/libraries/model/src/model/TextureMap.cpp +++ b/libraries/model/src/model/TextureMap.cpp @@ -56,7 +56,7 @@ gpu::Texture* TextureUsage::create2DTextureFromImage(const QImage& srcImage, con bool validAlpha = false; bool alphaAsMask = true; const uint8 OPAQUE_ALPHA = 255; - const uint8 TRANSLUCENT_ALPHA = 0; + const uint8 TRANSPARENT_ALPHA = 0; if (image.hasAlphaChannel()) { std::map alphaHistogram; @@ -70,10 +70,7 @@ gpu::Texture* TextureUsage::create2DTextureFromImage(const QImage& srcImage, con for (int x = 0; x < image.width(); ++x) { auto alpha = qAlpha(data[x]); alphaHistogram[alpha] ++; - if (alpha != OPAQUE_ALPHA) { - validAlpha = true; - break; - } + validAlpha = validAlpha || (alpha != OPAQUE_ALPHA); } } @@ -81,10 +78,10 @@ gpu::Texture* TextureUsage::create2DTextureFromImage(const QImage& srcImage, con if (validAlpha && (alphaHistogram.size() > 1)) { auto totalNumPixels = image.height() * image.width(); auto numOpaques = alphaHistogram[OPAQUE_ALPHA]; - auto numTranslucents = alphaHistogram[TRANSLUCENT_ALPHA]; - auto numTransparents = totalNumPixels - numOpaques - numTranslucents; + auto numTransparents = alphaHistogram[TRANSPARENT_ALPHA]; + auto numTranslucents = totalNumPixels - numOpaques - numTransparents; - alphaAsMask = ((numTransparents / (double)totalNumPixels) < 0.05); + alphaAsMask = ((numTranslucents / (double)totalNumPixels) < 0.05); } } From 16e6d9a4f9f0ae141890ce7301ac6766914ebea2 Mon Sep 17 00:00:00 2001 From: Bradley Austin Davis Date: Thu, 17 Mar 2016 17:43:52 -0700 Subject: [PATCH 11/30] Make shared lists thread safe --- libraries/networking/src/ResourceCache.cpp | 101 ++++++++++++++------- libraries/networking/src/ResourceCache.h | 24 ++++- 2 files changed, 89 insertions(+), 36 deletions(-) diff --git a/libraries/networking/src/ResourceCache.cpp b/libraries/networking/src/ResourceCache.cpp index e345bed81c..86b4b74de4 100644 --- a/libraries/networking/src/ResourceCache.cpp +++ b/libraries/networking/src/ResourceCache.cpp @@ -159,44 +159,45 @@ void ResourceCache::clearUnusedResource() { } } -bool ResourceCache::attemptRequest(Resource* resource) { - auto sharedItems = DependencyManager::get(); - - // Disable request limiting for ATP - if (resource->getURL().scheme() != URL_SCHEME_ATP) { - if (_requestsActive >= _requestLimit) { - // wait until a slot becomes available - sharedItems->_pendingRequests.append(resource); - return false; - } - - ++_requestsActive; - } - - sharedItems->_loadingRequests.append(resource); - resource->makeRequest(); - return true; +void ResourceCacheSharedItems::appendActiveRequest(Resource* resource) { + Lock lock(_mutex); + _loadingRequests.append(resource); } -void ResourceCache::requestCompleted(Resource* resource) { - auto sharedItems = DependencyManager::get(); - sharedItems->_loadingRequests.removeOne(resource); - if (resource->getURL().scheme() != URL_SCHEME_ATP) { - --_requestsActive; - } - - attemptHighestPriorityRequest(); +void ResourceCacheSharedItems::appendPendingRequest(Resource* resource) { + Lock lock(_mutex); + _pendingRequests.append(resource); } -bool ResourceCache::attemptHighestPriorityRequest() { - auto sharedItems = DependencyManager::get(); +QList> ResourceCacheSharedItems::getPendingRequests() const { + Lock lock(_mutex); + return _pendingRequests; +} + +uint32_t ResourceCacheSharedItems::getPendingRequestsCount() const { + Lock lock(_mutex); + return _pendingRequests.size(); +} + +QList ResourceCacheSharedItems::getLoadingRequests() const { + Lock lock(_mutex); + return _loadingRequests; +} + +void ResourceCacheSharedItems::removeRequest(Resource* resource) { + Lock lock(_mutex); + _loadingRequests.removeOne(resource); +} + +Resource* ResourceCacheSharedItems::getHighestPendingRequest() { + Lock lock(_mutex); // look for the highest priority pending request int highestIndex = -1; float highestPriority = -FLT_MAX; - for (int i = 0; i < sharedItems->_pendingRequests.size(); ) { - Resource* resource = sharedItems->_pendingRequests.at(i).data(); + for (int i = 0; i < _pendingRequests.size();) { + Resource* resource = _pendingRequests.at(i).data(); if (!resource) { - sharedItems->_pendingRequests.removeAt(i); + _pendingRequests.removeAt(i); continue; } float priority = resource->getLoadPriority(); @@ -206,7 +207,45 @@ bool ResourceCache::attemptHighestPriorityRequest() { } i++; } - return (highestIndex >= 0) && attemptRequest(sharedItems->_pendingRequests.takeAt(highestIndex)); + if (highestIndex >= 0) { + return _pendingRequests.takeAt(highestIndex); + } + return nullptr; +} + +bool ResourceCache::attemptRequest(Resource* resource) { + auto sharedItems = DependencyManager::get(); + + // Disable request limiting for ATP + if (resource->getURL().scheme() != URL_SCHEME_ATP) { + if (_requestsActive >= _requestLimit) { + // wait until a slot becomes available + sharedItems->appendPendingRequest(resource); + return false; + } + + ++_requestsActive; + } + + sharedItems->appendActiveRequest(resource); + resource->makeRequest(); + return true; +} + +void ResourceCache::requestCompleted(Resource* resource) { + auto sharedItems = DependencyManager::get(); + sharedItems->removeRequest(resource); + if (resource->getURL().scheme() != URL_SCHEME_ATP) { + --_requestsActive; + } + + attemptHighestPriorityRequest(); +} + +bool ResourceCache::attemptHighestPriorityRequest() { + auto sharedItems = DependencyManager::get(); + auto resource = sharedItems->getHighestPendingRequest(); + return (resource && attemptRequest(resource)); } const int DEFAULT_REQUEST_LIMIT = 10; diff --git a/libraries/networking/src/ResourceCache.h b/libraries/networking/src/ResourceCache.h index a8ab51b7f7..ed938f6cf4 100644 --- a/libraries/networking/src/ResourceCache.h +++ b/libraries/networking/src/ResourceCache.h @@ -12,6 +12,7 @@ #ifndef hifi_ResourceCache_h #define hifi_ResourceCache_h +#include #include #include #include @@ -53,12 +54,25 @@ static const qint64 MAX_UNUSED_MAX_SIZE = 10 * BYTES_PER_GIGABYTES; // object instead class ResourceCacheSharedItems : public Dependency { SINGLETON_DEPENDENCY + + using Mutex = std::mutex; + using Lock = std::unique_lock; public: - QList> _pendingRequests; - QList _loadingRequests; + void appendPendingRequest(Resource* newRequest); + void appendActiveRequest(Resource* newRequest); + void removeRequest(Resource* doneRequest); + QList> getPendingRequests() const; + uint32_t getPendingRequestsCount() const; + QList getLoadingRequests() const; + Resource* getHighestPendingRequest(); + private: ResourceCacheSharedItems() { } virtual ~ResourceCacheSharedItems() { } + + mutable Mutex _mutex; + QList> _pendingRequests; + QList _loadingRequests; }; @@ -75,11 +89,11 @@ public: void setUnusedResourceCacheSize(qint64 unusedResourcesMaxSize); qint64 getUnusedResourceCacheSize() const { return _unusedResourcesMaxSize; } - static const QList& getLoadingRequests() - { return DependencyManager::get()->_loadingRequests; } + static const QList getLoadingRequests() + { return DependencyManager::get()->getLoadingRequests(); } static int getPendingRequestCount() - { return DependencyManager::get()->_pendingRequests.size(); } + { return DependencyManager::get()->getPendingRequestsCount(); } ResourceCache(QObject* parent = NULL); virtual ~ResourceCache(); From d218ca49609cdcea653e091d16803bc7b2e9a61c Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Fri, 18 Mar 2016 12:26:11 -0700 Subject: [PATCH 12/30] openvr: fix for crash on exit Before this fix, a script could call into HMD.getHUDLookAtPosition2D() while the app was shutting down, which in turn would call getHeadPose() on the currently active display plugin. This call could cause a crash within the openvr plugin, because the SDK was either shutdown, or in the process of shutting down on the main thread. This fixes this by spliting the previous DisplayPlugin::getHeadPose(int) into two parts: * updateHeadPose(int) which is only called once a frame and only by the main thread. * getHeadPose() which is thread-safe and will return a cached copy of the hmd pose sampled by the last updateHeadPose. --- interface/src/Application.cpp | 6 +++--- interface/src/avatar/AvatarUpdate.cpp | 2 +- interface/src/avatar/MyAvatar.cpp | 2 +- .../src/display-plugins/CompositorHelper.cpp | 2 +- libraries/plugins/src/plugins/DisplayPlugin.h | 10 ++++++++-- plugins/oculus/src/OculusBaseDisplayPlugin.cpp | 10 ++++++++-- plugins/oculus/src/OculusBaseDisplayPlugin.h | 5 ++++- plugins/openvr/src/OpenVrDisplayPlugin.cpp | 13 ++++++++++--- plugins/openvr/src/OpenVrDisplayPlugin.h | 5 ++++- 9 files changed, 40 insertions(+), 15 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index a971051311..786f29fc90 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -1603,7 +1603,7 @@ void Application::paintGL() { // unmodified head pose because the only plugin that cares (the Oculus plugin) uses it // for rotational timewarp. If we move to support positonal timewarp, we need to // ensure this contains the full pose composed with the eye offsets. - mat4 headPose = displayPlugin->getHeadPose(_frameCount); + mat4 headPose = displayPlugin->updateHeadPose(_frameCount); // FIXME we probably don't need to set the projection matrix every frame, // only when the display plugin changes (or in non-HMD modes when the user @@ -2975,7 +2975,7 @@ void Application::updateMyAvatarLookAtPosition() { lookAtPosition.x = -lookAtPosition.x; } if (isHMD) { - glm::mat4 headPose = getActiveDisplayPlugin()->getHeadPose(_frameCount); + glm::mat4 headPose = getActiveDisplayPlugin()->getHeadPose(); glm::quat hmdRotation = glm::quat_cast(headPose); lookAtSpot = _myCamera.getPosition() + myAvatar->getOrientation() * (hmdRotation * lookAtPosition); } else { @@ -4927,7 +4927,7 @@ mat4 Application::getEyeOffset(int eye) const { mat4 Application::getHMDSensorPose() const { if (isHMDMode()) { - return getActiveDisplayPlugin()->getHeadPose(_frameCount); + return getActiveDisplayPlugin()->getHeadPose(); } return mat4(); } diff --git a/interface/src/avatar/AvatarUpdate.cpp b/interface/src/avatar/AvatarUpdate.cpp index a52b584527..3e0d23cc1c 100644 --- a/interface/src/avatar/AvatarUpdate.cpp +++ b/interface/src/avatar/AvatarUpdate.cpp @@ -38,7 +38,7 @@ void AvatarUpdate::synchronousProcess() { // transform the head pose from the displayPlugin into avatar coordinates. glm::mat4 invAvatarMat = glm::inverse(createMatFromQuatAndPos(myAvatar->getOrientation(), myAvatar->getPosition())); - _headPose = invAvatarMat * (myAvatar->getSensorToWorldMatrix() * qApp->getActiveDisplayPlugin()->getHeadPose(frameCount)); + _headPose = invAvatarMat * (myAvatar->getSensorToWorldMatrix() * qApp->getActiveDisplayPlugin()->getHeadPose()); if (!isThreaded()) { process(); diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 7198f32422..550c11df4d 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -1258,7 +1258,7 @@ void MyAvatar::renderBody(RenderArgs* renderArgs, ViewFrustum* renderFrustum, fl if (qApp->isHMDMode()) { glm::vec3 cameraPosition = qApp->getCamera()->getPosition(); - glm::mat4 headPose = qApp->getActiveDisplayPlugin()->getHeadPose(qApp->getFrameCount()); + glm::mat4 headPose = qApp->getActiveDisplayPlugin()->getHeadPose(); glm::mat4 leftEyePose = qApp->getActiveDisplayPlugin()->getEyeToHeadTransform(Eye::Left); leftEyePose = leftEyePose * headPose; glm::vec3 leftEyePosition = extractTranslation(leftEyePose); diff --git a/libraries/display-plugins/src/display-plugins/CompositorHelper.cpp b/libraries/display-plugins/src/display-plugins/CompositorHelper.cpp index f2040eaa43..3e09e96704 100644 --- a/libraries/display-plugins/src/display-plugins/CompositorHelper.cpp +++ b/libraries/display-plugins/src/display-plugins/CompositorHelper.cpp @@ -342,7 +342,7 @@ void CompositorHelper::computeHmdPickRay(const glm::vec2& cursorPos, glm::vec3& } glm::mat4 CompositorHelper::getUiTransform() const { - return _currentCamera * glm::inverse(_currentDisplayPlugin->getHeadPose(_currentFrame)); + return _currentCamera * glm::inverse(_currentDisplayPlugin->getHeadPose()); } //Finds the collision point of a world space ray diff --git a/libraries/plugins/src/plugins/DisplayPlugin.h b/libraries/plugins/src/plugins/DisplayPlugin.h index 76db1ecbe2..376cec7b70 100644 --- a/libraries/plugins/src/plugins/DisplayPlugin.h +++ b/libraries/plugins/src/plugins/DisplayPlugin.h @@ -121,8 +121,14 @@ public: static const glm::mat4 transform; return transform; } - virtual glm::mat4 getHeadPose(uint32_t frameIndex) const { - static const glm::mat4 pose; return pose; + // will query the underlying hmd api to compute the most recent head pose + virtual glm::mat4 updateHeadPose(uint32_t frameIndex) { + return glm::mat4(); + } + + // returns a copy of the most recent head pose, computed via updateHeadPose + virtual glm::mat4 getHeadPose() const { + return glm::mat4(); } // Needed for timewarp style features diff --git a/plugins/oculus/src/OculusBaseDisplayPlugin.cpp b/plugins/oculus/src/OculusBaseDisplayPlugin.cpp index f1613176df..0f87d526d7 100644 --- a/plugins/oculus/src/OculusBaseDisplayPlugin.cpp +++ b/plugins/oculus/src/OculusBaseDisplayPlugin.cpp @@ -15,14 +15,20 @@ void OculusBaseDisplayPlugin::resetSensors() { ovr_RecenterPose(_session); } -glm::mat4 OculusBaseDisplayPlugin::getHeadPose(uint32_t frameIndex) const { +glm::mat4 OculusBaseDisplayPlugin::updateHeadPose(uint32_t frameIndex) { static uint32_t lastFrameSeen = 0; auto displayTime = ovr_GetPredictedDisplayTime(_session, frameIndex); auto trackingState = ovr_GetTrackingState(_session, displayTime, frameIndex > lastFrameSeen); if (frameIndex > lastFrameSeen) { lastFrameSeen = frameIndex; } - return toGlm(trackingState.HeadPose.ThePose); + mat4 headPose = toGlm(trackingState.HeadPose.ThePose); + _headPoseCache.set(headPose); + return headPose; +} + +glm::mat4 OculusBaseDisplayPlugin::getHeadPose() const { + return _headPoseCache.get(); } bool OculusBaseDisplayPlugin::isSupported() const { diff --git a/plugins/oculus/src/OculusBaseDisplayPlugin.h b/plugins/oculus/src/OculusBaseDisplayPlugin.h index e6f0b265a2..cf8f38fe3a 100644 --- a/plugins/oculus/src/OculusBaseDisplayPlugin.h +++ b/plugins/oculus/src/OculusBaseDisplayPlugin.h @@ -8,6 +8,7 @@ #pragma once #include +#include #include @@ -20,7 +21,8 @@ public: // Stereo specific methods virtual void resetSensors() override final; - virtual glm::mat4 getHeadPose(uint32_t frameIndex) const override; + virtual glm::mat4 updateHeadPose(uint32_t frameIndex) override; + virtual glm::mat4 getHeadPose() const override; protected: void customizeContext() override; @@ -36,4 +38,5 @@ protected: ovrHmdDesc _hmdDesc; ovrLayerEyeFov _sceneLayer; ovrViewScaleDesc _viewScaleDesc; + ThreadSafeValueCache _headPoseCache { glm::mat4() }; }; diff --git a/plugins/openvr/src/OpenVrDisplayPlugin.cpp b/plugins/openvr/src/OpenVrDisplayPlugin.cpp index d0363d4dcc..f559e902d9 100644 --- a/plugins/openvr/src/OpenVrDisplayPlugin.cpp +++ b/plugins/openvr/src/OpenVrDisplayPlugin.cpp @@ -38,7 +38,7 @@ static mat4 _sensorResetMat; static std::array VR_EYES { { vr::Eye_Left, vr::Eye_Right } }; bool OpenVrDisplayPlugin::isSupported() const { - return !isOculusPresent() && vr::VR_IsHmdPresent(); + return /*!isOculusPresent() &&*/ vr::VR_IsHmdPresent(); } void OpenVrDisplayPlugin::internalActivate() { @@ -112,7 +112,7 @@ void OpenVrDisplayPlugin::resetSensors() { _sensorResetMat = glm::inverse(cancelOutRollAndPitch(m)); } -glm::mat4 OpenVrDisplayPlugin::getHeadPose(uint32_t frameIndex) const { +glm::mat4 OpenVrDisplayPlugin::updateHeadPose(uint32_t frameIndex) { float displayFrequency = _system->GetFloatTrackedDeviceProperty(vr::k_unTrackedDeviceIndex_Hmd, vr::Prop_DisplayFrequency_Float); float frameDuration = 1.f / displayFrequency; @@ -139,14 +139,21 @@ glm::mat4 OpenVrDisplayPlugin::getHeadPose(uint32_t frameIndex) const { _trackedDeviceLinearVelocities[i] = transformVectorFast(_sensorResetMat, toGlm(_trackedDevicePose[i].vVelocity)); _trackedDeviceAngularVelocities[i] = transformVectorFast(_sensorResetMat, toGlm(_trackedDevicePose[i].vAngularVelocity)); } + + _headPoseCache.set(_trackedDevicePoseMat4[0]); + return _trackedDevicePoseMat4[0]; } +glm::mat4 OpenVrDisplayPlugin::getHeadPose() const { + return _headPoseCache.get(); +} + void OpenVrDisplayPlugin::hmdPresent() { // Flip y-axis since GL UV coords are backwards. static vr::VRTextureBounds_t leftBounds{ 0, 0, 0.5f, 1 }; static vr::VRTextureBounds_t rightBounds{ 0.5f, 0, 1, 1 }; - + vr::Texture_t texture { (void*)oglplus::GetName(_compositeFramebuffer->color), vr::API_OpenGL, vr::ColorSpace_Auto }; _compositor->Submit(vr::Eye_Left, &texture, &leftBounds); diff --git a/plugins/openvr/src/OpenVrDisplayPlugin.h b/plugins/openvr/src/OpenVrDisplayPlugin.h index c7577fd53f..7405aab82e 100644 --- a/plugins/openvr/src/OpenVrDisplayPlugin.h +++ b/plugins/openvr/src/OpenVrDisplayPlugin.h @@ -12,6 +12,7 @@ #include #include +#include const float TARGET_RATE_OpenVr = 90.0f; // FIXME: get from sdk tracked device property? This number is vive-only. @@ -27,7 +28,8 @@ public: // Stereo specific methods virtual void resetSensors() override; - virtual glm::mat4 getHeadPose(uint32_t frameIndex) const override; + virtual glm::mat4 updateHeadPose(uint32_t frameIndex) override; + virtual glm::mat4 getHeadPose() const override; protected: void internalActivate() override; @@ -41,4 +43,5 @@ private: std::atomic _hmdActivityLevel { vr::k_EDeviceActivityLevel_Unknown }; static const QString NAME; mutable Mutex _poseMutex; + ThreadSafeValueCache _headPoseCache { glm::mat4() }; }; From 2faaf243a131a0bb28a41bb062f53f5dccd75809 Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Fri, 18 Mar 2016 13:42:31 -0700 Subject: [PATCH 13/30] Revert change to OpenVrDisplayPlugin::isSupported() --- plugins/openvr/src/OpenVrDisplayPlugin.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/openvr/src/OpenVrDisplayPlugin.cpp b/plugins/openvr/src/OpenVrDisplayPlugin.cpp index f559e902d9..f5b80b0918 100644 --- a/plugins/openvr/src/OpenVrDisplayPlugin.cpp +++ b/plugins/openvr/src/OpenVrDisplayPlugin.cpp @@ -38,7 +38,7 @@ static mat4 _sensorResetMat; static std::array VR_EYES { { vr::Eye_Left, vr::Eye_Right } }; bool OpenVrDisplayPlugin::isSupported() const { - return /*!isOculusPresent() &&*/ vr::VR_IsHmdPresent(); + return !isOculusPresent() && vr::VR_IsHmdPresent(); } void OpenVrDisplayPlugin::internalActivate() { From b9a3e130cbe4600958a3f0c8d2e220e12758c2c8 Mon Sep 17 00:00:00 2001 From: howard-stearns Date: Fri, 18 Mar 2016 15:02:30 -0700 Subject: [PATCH 14/30] Safer early-bail for fetch of subresources of .obj models. --- libraries/fbx/src/OBJReader.cpp | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/libraries/fbx/src/OBJReader.cpp b/libraries/fbx/src/OBJReader.cpp index c0552f989b..c2d96f4c49 100644 --- a/libraries/fbx/src/OBJReader.cpp +++ b/libraries/fbx/src/OBJReader.cpp @@ -179,7 +179,7 @@ void OBJFace::addFrom(const OBJFace* face, int index) { // add using data from f } static bool replyOK(QNetworkReply* netReply, QUrl url) { // This will be reworked when we make things asynchronous - return (netReply->isFinished() && + return (netReply && netReply->isFinished() && (url.toString().startsWith("file", Qt::CaseInsensitive) ? // file urls don't have http status codes netReply->attribute(QNetworkRequest::HttpReasonPhraseAttribute).toString().isEmpty() : (netReply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt() == 200))); @@ -191,11 +191,10 @@ bool OBJReader::isValidTexture(const QByteArray &filename) { } QUrl candidateUrl = _url.resolved(QUrl(filename)); QNetworkReply *netReply = request(candidateUrl, true); - if (!netReply) { - return false; - } bool isValid = replyOK(netReply, candidateUrl); - netReply->deleteLater(); + if (netReply) { + netReply->deleteLater(); + } return isValid; } @@ -264,20 +263,19 @@ QNetworkReply* OBJReader::request(QUrl& url, bool isTest) { if (!qApp) { return nullptr; } + bool aboutToQuit{ false }; + auto connection = QObject::connect(qApp, &QCoreApplication::aboutToQuit, [&] { + aboutToQuit = true; + }); QNetworkAccessManager& networkAccessManager = NetworkAccessManager::getInstance(); QNetworkRequest netRequest(url); QNetworkReply* netReply = isTest ? networkAccessManager.head(netRequest) : networkAccessManager.get(netRequest); - if (!qApp) { - return netReply; + if (!qApp || aboutToQuit) { + return nullptr; } QEventLoop loop; // Create an event loop that will quit when we get the finished signal QObject::connect(netReply, SIGNAL(finished()), &loop, SLOT(quit())); loop.exec(); // Nothing is going to happen on this whole run thread until we get this - - bool aboutToQuit { false }; - auto connection = QObject::connect(qApp, &QCoreApplication::aboutToQuit, [&] { - aboutToQuit = true; - }); static const int WAIT_TIMEOUT_MS = 500; while (qApp && !aboutToQuit && !netReply->isReadable()) { netReply->waitForReadyRead(WAIT_TIMEOUT_MS); // so we might as well block this thread waiting for the response, rather than @@ -570,9 +568,11 @@ FBXGeometry* OBJReader::readOBJ(QByteArray& model, const QVariantHash& mapping, parseMaterialLibrary(netReply); } else { qCDebug(modelformat) << "OBJ Reader WARNING:" << libraryName << "did not answer. Got" - << netReply->attribute(QNetworkRequest::HttpReasonPhraseAttribute).toString(); + << (!netReply ? "aborted" : netReply->attribute(QNetworkRequest::HttpReasonPhraseAttribute).toString()); + } + if (netReply) { + netReply->deleteLater(); } - netReply->deleteLater(); } } From 960ffd9c9e41054046d5238a03b2028e5fecb40c Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Fri, 18 Mar 2016 15:16:58 -0700 Subject: [PATCH 15/30] DisplayPlugins: updateHeadPose() no longer returns a value. Also, moved updateHeadPose so that the overlay and the main rendering use the same headPose. Which should also be the same place the latencyMarker for the ovr_GetTrackingState is set. --- interface/src/Application.cpp | 13 +++++++------ libraries/plugins/src/plugins/DisplayPlugin.h | 4 +--- plugins/oculus/src/OculusBaseDisplayPlugin.cpp | 9 ++------- plugins/oculus/src/OculusBaseDisplayPlugin.h | 2 +- plugins/openvr/src/OpenVrDisplayPlugin.cpp | 4 +--- plugins/openvr/src/OpenVrDisplayPlugin.h | 2 +- 6 files changed, 13 insertions(+), 21 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 786f29fc90..dc969d4ab4 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -1418,6 +1418,12 @@ void Application::paintGL() { // FIXME not needed anymore? _offscreenContext->makeCurrent(); + // Tell the plugin what pose we're using to render. In this case we're just using the + // unmodified head pose because the only plugin that cares (the Oculus plugin) uses it + // for rotational timewarp. If we move to support positonal timewarp, we need to + // ensure this contains the full pose composed with the eye offsets. + displayPlugin->updateHeadPose(_frameCount); + // update the avatar with a fresh HMD pose getMyAvatar()->updateFromHMDSensorMatrix(getHMDSensorPose()); @@ -1598,12 +1604,7 @@ void Application::paintGL() { auto baseProjection = renderArgs._viewFrustum->getProjection(); auto hmdInterface = DependencyManager::get(); float IPDScale = hmdInterface->getIPDScale(); - - // Tell the plugin what pose we're using to render. In this case we're just using the - // unmodified head pose because the only plugin that cares (the Oculus plugin) uses it - // for rotational timewarp. If we move to support positonal timewarp, we need to - // ensure this contains the full pose composed with the eye offsets. - mat4 headPose = displayPlugin->updateHeadPose(_frameCount); + mat4 headPose = displayPlugin->getHeadPose(); // FIXME we probably don't need to set the projection matrix every frame, // only when the display plugin changes (or in non-HMD modes when the user diff --git a/libraries/plugins/src/plugins/DisplayPlugin.h b/libraries/plugins/src/plugins/DisplayPlugin.h index 376cec7b70..e5ac78036e 100644 --- a/libraries/plugins/src/plugins/DisplayPlugin.h +++ b/libraries/plugins/src/plugins/DisplayPlugin.h @@ -122,9 +122,7 @@ public: } // will query the underlying hmd api to compute the most recent head pose - virtual glm::mat4 updateHeadPose(uint32_t frameIndex) { - return glm::mat4(); - } + virtual void updateHeadPose(uint32_t frameIndex) {} // returns a copy of the most recent head pose, computed via updateHeadPose virtual glm::mat4 getHeadPose() const { diff --git a/plugins/oculus/src/OculusBaseDisplayPlugin.cpp b/plugins/oculus/src/OculusBaseDisplayPlugin.cpp index 0f87d526d7..7a4bcc665b 100644 --- a/plugins/oculus/src/OculusBaseDisplayPlugin.cpp +++ b/plugins/oculus/src/OculusBaseDisplayPlugin.cpp @@ -15,16 +15,11 @@ void OculusBaseDisplayPlugin::resetSensors() { ovr_RecenterPose(_session); } -glm::mat4 OculusBaseDisplayPlugin::updateHeadPose(uint32_t frameIndex) { - static uint32_t lastFrameSeen = 0; +void OculusBaseDisplayPlugin::updateHeadPose(uint32_t frameIndex) { auto displayTime = ovr_GetPredictedDisplayTime(_session, frameIndex); - auto trackingState = ovr_GetTrackingState(_session, displayTime, frameIndex > lastFrameSeen); - if (frameIndex > lastFrameSeen) { - lastFrameSeen = frameIndex; - } + auto trackingState = ovr_GetTrackingState(_session, displayTime, true); mat4 headPose = toGlm(trackingState.HeadPose.ThePose); _headPoseCache.set(headPose); - return headPose; } glm::mat4 OculusBaseDisplayPlugin::getHeadPose() const { diff --git a/plugins/oculus/src/OculusBaseDisplayPlugin.h b/plugins/oculus/src/OculusBaseDisplayPlugin.h index cf8f38fe3a..61179a632f 100644 --- a/plugins/oculus/src/OculusBaseDisplayPlugin.h +++ b/plugins/oculus/src/OculusBaseDisplayPlugin.h @@ -21,7 +21,7 @@ public: // Stereo specific methods virtual void resetSensors() override final; - virtual glm::mat4 updateHeadPose(uint32_t frameIndex) override; + virtual void updateHeadPose(uint32_t frameIndex) override; virtual glm::mat4 getHeadPose() const override; protected: diff --git a/plugins/openvr/src/OpenVrDisplayPlugin.cpp b/plugins/openvr/src/OpenVrDisplayPlugin.cpp index f5b80b0918..abc47577d1 100644 --- a/plugins/openvr/src/OpenVrDisplayPlugin.cpp +++ b/plugins/openvr/src/OpenVrDisplayPlugin.cpp @@ -112,7 +112,7 @@ void OpenVrDisplayPlugin::resetSensors() { _sensorResetMat = glm::inverse(cancelOutRollAndPitch(m)); } -glm::mat4 OpenVrDisplayPlugin::updateHeadPose(uint32_t frameIndex) { +void OpenVrDisplayPlugin::updateHeadPose(uint32_t frameIndex) { float displayFrequency = _system->GetFloatTrackedDeviceProperty(vr::k_unTrackedDeviceIndex_Hmd, vr::Prop_DisplayFrequency_Float); float frameDuration = 1.f / displayFrequency; @@ -141,8 +141,6 @@ glm::mat4 OpenVrDisplayPlugin::updateHeadPose(uint32_t frameIndex) { } _headPoseCache.set(_trackedDevicePoseMat4[0]); - - return _trackedDevicePoseMat4[0]; } glm::mat4 OpenVrDisplayPlugin::getHeadPose() const { diff --git a/plugins/openvr/src/OpenVrDisplayPlugin.h b/plugins/openvr/src/OpenVrDisplayPlugin.h index 7405aab82e..4f0320d237 100644 --- a/plugins/openvr/src/OpenVrDisplayPlugin.h +++ b/plugins/openvr/src/OpenVrDisplayPlugin.h @@ -28,7 +28,7 @@ public: // Stereo specific methods virtual void resetSensors() override; - virtual glm::mat4 updateHeadPose(uint32_t frameIndex) override; + virtual void updateHeadPose(uint32_t frameIndex) override; virtual glm::mat4 getHeadPose() const override; protected: From e81e49b32e74e05c819a03d0cc5a0af4b64baded Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Fri, 18 Mar 2016 15:59:25 -0700 Subject: [PATCH 16/30] prevent crash in SwingTwistConstraint --- interface/src/avatar/AvatarUpdate.cpp | 1 + .../animation/src/AnimInverseKinematics.cpp | 2 +- .../animation/src/SwingTwistConstraint.cpp | 19 ++++++++++++++----- 3 files changed, 16 insertions(+), 6 deletions(-) diff --git a/interface/src/avatar/AvatarUpdate.cpp b/interface/src/avatar/AvatarUpdate.cpp index a52b584527..1c91a21906 100644 --- a/interface/src/avatar/AvatarUpdate.cpp +++ b/interface/src/avatar/AvatarUpdate.cpp @@ -55,6 +55,7 @@ bool AvatarUpdate::process() { deltaMicroseconds = 10000; // 10 ms } float deltaSeconds = (float) deltaMicroseconds / (float) USECS_PER_SECOND; + assert(deltaSeconds > 0.0f); _lastAvatarUpdate = start; qApp->setAvatarSimrateSample(1.0f / deltaSeconds); diff --git a/libraries/animation/src/AnimInverseKinematics.cpp b/libraries/animation/src/AnimInverseKinematics.cpp index f4df7ada82..9ae992b86e 100644 --- a/libraries/animation/src/AnimInverseKinematics.cpp +++ b/libraries/animation/src/AnimInverseKinematics.cpp @@ -481,7 +481,7 @@ const AnimPoseVec& AnimInverseKinematics::overlay(const AnimVariantMap& animVars // smooth transitions by relaxing _hipsOffset toward the new value const float HIPS_OFFSET_SLAVE_TIMESCALE = 0.15f; - float tau = dt > HIPS_OFFSET_SLAVE_TIMESCALE ? 1.0f : dt / HIPS_OFFSET_SLAVE_TIMESCALE; + float tau = dt < HIPS_OFFSET_SLAVE_TIMESCALE ? dt / HIPS_OFFSET_SLAVE_TIMESCALE : 1.0f; _hipsOffset += (newHipsOffset - _hipsOffset) * tau; } } diff --git a/libraries/animation/src/SwingTwistConstraint.cpp b/libraries/animation/src/SwingTwistConstraint.cpp index d6d8c87344..3d0e4e7c5f 100644 --- a/libraries/animation/src/SwingTwistConstraint.cpp +++ b/libraries/animation/src/SwingTwistConstraint.cpp @@ -384,11 +384,20 @@ void SwingTwistConstraint::dynamicallyAdjustLimits(const glm::quat& rotation) { swingTwistDecomposition(postRotation, Vectors::UNIT_Y, swingRotation, twistRotation); - // adjust swing limits - glm::vec3 swungY = swingRotation * Vectors::UNIT_Y; - glm::vec3 swingAxis = glm::cross(Vectors::UNIT_Y, swungY); - float theta = atan2f(-swingAxis.z, swingAxis.x); - _swingLimitFunction.dynamicallyAdjustMinDots(theta, swungY.y); + { // adjust swing limits + glm::vec3 swungY = swingRotation * Vectors::UNIT_Y; + glm::vec3 swingAxis = glm::cross(Vectors::UNIT_Y, swungY); + float theta = atan2f(-swingAxis.z, swingAxis.x); + if (isnan(theta)) { + // atan2f() will only return NaN if either of its arguments is NaN, which can only + // happen if we've been given a bad rotation. Since a NaN value here could potentially + // cause a crash (we use the value of theta to compute indices into a std::vector) + // we specifically check for this case. + theta = 0.0f; + swungY.y = 1.0f; + } + _swingLimitFunction.dynamicallyAdjustMinDots(theta, swungY.y); + } // restore twist limits if (_twistAdjusted) { From 4321c5a62b873c82246f00a73bc194d6e0e56638 Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Fri, 18 Mar 2016 15:59:48 -0700 Subject: [PATCH 17/30] OculusLegacyDisplayPlugin: fixes for macosx and linux --- interface/src/avatar/AvatarUpdate.cpp | 1 - .../src/OculusLegacyDisplayPlugin.cpp | 15 ++++++++------- .../oculusLegacy/src/OculusLegacyDisplayPlugin.h | 5 ++++- 3 files changed, 12 insertions(+), 9 deletions(-) diff --git a/interface/src/avatar/AvatarUpdate.cpp b/interface/src/avatar/AvatarUpdate.cpp index 3e0d23cc1c..3b42b61fd2 100644 --- a/interface/src/avatar/AvatarUpdate.cpp +++ b/interface/src/avatar/AvatarUpdate.cpp @@ -30,7 +30,6 @@ void AvatarUpdate::synchronousProcess() { // Keep our own updated value, so that our asynchronous code can consult it. _isHMDMode = qApp->isHMDMode(); - auto frameCount = qApp->getFrameCount(); QSharedPointer manager = DependencyManager::get(); MyAvatar* myAvatar = manager->getMyAvatar(); diff --git a/plugins/oculusLegacy/src/OculusLegacyDisplayPlugin.cpp b/plugins/oculusLegacy/src/OculusLegacyDisplayPlugin.cpp index 2b0ace8bcc..c0d27f8699 100644 --- a/plugins/oculusLegacy/src/OculusLegacyDisplayPlugin.cpp +++ b/plugins/oculusLegacy/src/OculusLegacyDisplayPlugin.cpp @@ -35,14 +35,15 @@ void OculusLegacyDisplayPlugin::resetSensors() { ovrHmd_RecenterPose(_hmd); } +glm::mat4 OculusLegacyDisplayPlugin::updateHeadPose(uint32_t frameIndex) { + Lock lock(_mutex); + _trackingState = ovrHmd_GetTrackingState(_hmd, ovr_GetTimeInSeconds()); + lastFrameSeen = frameIndex; + _headPoseCache.set(toGlm(_trackingState.HeadPose.ThePose)); +} + glm::mat4 OculusLegacyDisplayPlugin::getHeadPose(uint32_t frameIndex) const { - static uint32_t lastFrameSeen = 0; - if (frameIndex > lastFrameSeen) { - Lock lock(_mutex); - _trackingState = ovrHmd_GetTrackingState(_hmd, ovr_GetTimeInSeconds()); - lastFrameSeen = frameIndex; - } - return toGlm(_trackingState.HeadPose.ThePose); + return _headPoseCache.get(); } bool OculusLegacyDisplayPlugin::isSupported() const { diff --git a/plugins/oculusLegacy/src/OculusLegacyDisplayPlugin.h b/plugins/oculusLegacy/src/OculusLegacyDisplayPlugin.h index f7d1be0244..4f13c49f2f 100644 --- a/plugins/oculusLegacy/src/OculusLegacyDisplayPlugin.h +++ b/plugins/oculusLegacy/src/OculusLegacyDisplayPlugin.h @@ -8,6 +8,7 @@ #pragma once #include +#include #include @@ -26,7 +27,8 @@ public: // Stereo specific methods virtual void resetSensors() override; - virtual glm::mat4 getHeadPose(uint32_t frameIndex) const override; + virtual void updateHeadPose(uint32_t frameIndex) override; + virtual glm::mat4 getHeadPose() const override; virtual float getTargetFrameRate() override; @@ -52,6 +54,7 @@ private: //ovrTexture _eyeTextures[2]; // FIXME - not currently in use mutable int _hmdScreen { -1 }; bool _hswDismissed { false }; + ThreadSafeValueCache _headPoseCache { glm::mat4() }; }; From 105b4e16a98db5945c558a776132f716349768b9 Mon Sep 17 00:00:00 2001 From: Zach Pomerantz Date: Fri, 18 Mar 2016 16:18:41 -0700 Subject: [PATCH 18/30] Add back ambientURL listener in editor --- examples/html/entityProperties.html | 1 + 1 file changed, 1 insertion(+) diff --git a/examples/html/entityProperties.html b/examples/html/entityProperties.html index f6026c14a1..e3eb19dc4a 100644 --- a/examples/html/entityProperties.html +++ b/examples/html/entityProperties.html @@ -936,6 +936,7 @@ elZoneKeyLightColorBlue.addEventListener('change', zoneKeyLightColorChangeFunction); elZoneKeyLightIntensity.addEventListener('change', createEmitGroupNumberPropertyUpdateFunction('keyLight','intensity')); elZoneKeyLightAmbientIntensity.addEventListener('change', createEmitGroupNumberPropertyUpdateFunction('keyLight','ambientIntensity')); + elZoneKeyLightAmbientURL.addEventListener('change', createEmitGroupTextPropertyUpdateFunction('keyLight','ambientURL')); var zoneKeyLightDirectionChangeFunction = createEmitGroupVec3PropertyUpdateFunction('keyLight','direction', elZoneKeyLightDirectionX, elZoneKeyLightDirectionY); elZoneKeyLightDirectionX.addEventListener('change', zoneKeyLightDirectionChangeFunction); elZoneKeyLightDirectionY.addEventListener('change', zoneKeyLightDirectionChangeFunction); From e1d47e702813f4f68a5588464d23b7beb000ae48 Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Fri, 18 Mar 2016 16:23:30 -0700 Subject: [PATCH 19/30] OculusLegacyDisplayPlugin: mac os x and linux fixes --- plugins/oculusLegacy/src/OculusLegacyDisplayPlugin.cpp | 5 ++--- plugins/oculusLegacy/src/OculusLegacyDisplayPlugin.h | 2 +- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/plugins/oculusLegacy/src/OculusLegacyDisplayPlugin.cpp b/plugins/oculusLegacy/src/OculusLegacyDisplayPlugin.cpp index c0d27f8699..b8a1d7c35f 100644 --- a/plugins/oculusLegacy/src/OculusLegacyDisplayPlugin.cpp +++ b/plugins/oculusLegacy/src/OculusLegacyDisplayPlugin.cpp @@ -35,14 +35,13 @@ void OculusLegacyDisplayPlugin::resetSensors() { ovrHmd_RecenterPose(_hmd); } -glm::mat4 OculusLegacyDisplayPlugin::updateHeadPose(uint32_t frameIndex) { +void OculusLegacyDisplayPlugin::updateHeadPose(uint32_t frameIndex) { Lock lock(_mutex); _trackingState = ovrHmd_GetTrackingState(_hmd, ovr_GetTimeInSeconds()); - lastFrameSeen = frameIndex; _headPoseCache.set(toGlm(_trackingState.HeadPose.ThePose)); } -glm::mat4 OculusLegacyDisplayPlugin::getHeadPose(uint32_t frameIndex) const { +glm::mat4 OculusLegacyDisplayPlugin::getHeadPose() const { return _headPoseCache.get(); } diff --git a/plugins/oculusLegacy/src/OculusLegacyDisplayPlugin.h b/plugins/oculusLegacy/src/OculusLegacyDisplayPlugin.h index 4f13c49f2f..f100028beb 100644 --- a/plugins/oculusLegacy/src/OculusLegacyDisplayPlugin.h +++ b/plugins/oculusLegacy/src/OculusLegacyDisplayPlugin.h @@ -54,7 +54,7 @@ private: //ovrTexture _eyeTextures[2]; // FIXME - not currently in use mutable int _hmdScreen { -1 }; bool _hswDismissed { false }; - ThreadSafeValueCache _headPoseCache { glm::mat4() }; + ThreadSafeValueCache _headPoseCache { glm::mat4() }; }; From 12aa057ff6dc671ab091d443daf9fd8327772211 Mon Sep 17 00:00:00 2001 From: Zach Pomerantz Date: Fri, 18 Mar 2016 18:37:14 -0700 Subject: [PATCH 20/30] Always log if gl context fails --- libraries/gl/src/gl/OffscreenGLCanvas.cpp | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/libraries/gl/src/gl/OffscreenGLCanvas.cpp b/libraries/gl/src/gl/OffscreenGLCanvas.cpp index 1b10d6b032..7406577814 100644 --- a/libraries/gl/src/gl/OffscreenGLCanvas.cpp +++ b/libraries/gl/src/gl/OffscreenGLCanvas.cpp @@ -44,13 +44,11 @@ bool OffscreenGLCanvas::create(QOpenGLContext* sharedContext) { return true; } - std::call_once(_reportOnce, []{ - qWarning() << "GL Version: " << QString((const char*) glGetString(GL_VERSION)); - qWarning() << "GL Shader Language Version: " << QString((const char*) glGetString(GL_SHADING_LANGUAGE_VERSION)); - qWarning() << "GL Vendor: " << QString((const char*) glGetString(GL_VENDOR)); - qWarning() << "GL Renderer: " << QString((const char*) glGetString(GL_RENDERER)); - qWarning() << "Failed to create OffscreenGLCanvas"; - }); + qWarning() << "GL Version: " << QString((const char*) glGetString(GL_VERSION)); + qWarning() << "GL Shader Language Version: " << QString((const char*) glGetString(GL_SHADING_LANGUAGE_VERSION)); + qWarning() << "GL Vendor: " << QString((const char*) glGetString(GL_VENDOR)); + qWarning() << "GL Renderer: " << QString((const char*) glGetString(GL_RENDERER)); + qWarning() << "Failed to create OffscreenGLCanvas"; return false; } From 6e872edcaeb057126a885ca4da3ff972360f6265 Mon Sep 17 00:00:00 2001 From: Anthony Thibault Date: Sun, 20 Mar 2016 12:13:54 -0700 Subject: [PATCH 21/30] NetworkGeometry: Fix crash for empty geometry/bad FBX versions Interpret empty geometry as an error. --- .../model-networking/src/model-networking/ModelCache.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/libraries/model-networking/src/model-networking/ModelCache.cpp b/libraries/model-networking/src/model-networking/ModelCache.cpp index c9ff8e681b..86aa20fa1c 100644 --- a/libraries/model-networking/src/model-networking/ModelCache.cpp +++ b/libraries/model-networking/src/model-networking/ModelCache.cpp @@ -72,11 +72,15 @@ void GeometryReader::run() { const bool grabLightmaps = true; const float lightmapLevel = 1.0f; fbxgeo = readFBX(_data, _mapping, _url.path(), grabLightmaps, lightmapLevel); + if (fbxgeo->meshes.size() == 0 && fbxgeo->joints.size() == 0) { + // empty fbx geometry, indicates error + throw QString("empty geometry, possibly due to an unsupported FBX version"); + } } else if (_url.path().toLower().endsWith(".obj")) { fbxgeo = OBJReader().readOBJ(_data, _mapping, _url); } else { QString errorStr("unsupported format"); - emit onError(NetworkGeometry::ModelParseError, errorStr); + throw errorStr; } emit onSuccess(fbxgeo); } else { From b71a56de3885fd708e123fa6c81725a3f3ba573f Mon Sep 17 00:00:00 2001 From: Anthony Thibault Date: Sun, 20 Mar 2016 12:18:33 -0700 Subject: [PATCH 22/30] Application: disable deadlock watchdog for debug builds Cause it will cause a crash if you sit on a breakpoint for too long. --- interface/src/Application.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index a971051311..f6ab94aa61 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -272,11 +272,13 @@ public: void run() override { while (!_quit) { QThread::sleep(HEARTBEAT_UPDATE_INTERVAL_SECS); +#ifdef NDEBUG auto now = usecTimestampNow(); auto lastHeartbeatAge = now - _heartbeat; if (lastHeartbeatAge > MAX_HEARTBEAT_AGE_USECS) { deadlockDetectionCrash(); } +#endif } } From 5b2a050df7161cc9d647839e9fa8c53245dff725 Mon Sep 17 00:00:00 2001 From: Anthony Thibault Date: Sun, 20 Mar 2016 14:08:35 -0700 Subject: [PATCH 23/30] display-plugins: Pulled getHeadPose into common base class. * Moved getHeadPose and _headPoseCache out of each derived class and into HmdDisplayPlugin. * updated comment in application. --- interface/src/Application.cpp | 8 ++++---- .../src/display-plugins/hmd/HmdDisplayPlugin.cpp | 6 +++++- .../src/display-plugins/hmd/HmdDisplayPlugin.h | 5 ++++- plugins/oculus/src/OculusBaseDisplayPlugin.cpp | 4 ---- plugins/oculus/src/OculusBaseDisplayPlugin.h | 3 --- plugins/oculusLegacy/src/OculusLegacyDisplayPlugin.cpp | 4 ---- plugins/oculusLegacy/src/OculusLegacyDisplayPlugin.h | 3 --- plugins/openvr/src/OpenVrDisplayPlugin.cpp | 4 ---- plugins/openvr/src/OpenVrDisplayPlugin.h | 3 --- 9 files changed, 13 insertions(+), 27 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index dc969d4ab4..d17b8b89fc 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -1418,10 +1418,6 @@ void Application::paintGL() { // FIXME not needed anymore? _offscreenContext->makeCurrent(); - // Tell the plugin what pose we're using to render. In this case we're just using the - // unmodified head pose because the only plugin that cares (the Oculus plugin) uses it - // for rotational timewarp. If we move to support positonal timewarp, we need to - // ensure this contains the full pose composed with the eye offsets. displayPlugin->updateHeadPose(_frameCount); // update the avatar with a fresh HMD pose @@ -1621,6 +1617,10 @@ void Application::paintGL() { mat4 eyeOffsetTransform = glm::translate(mat4(), eyeOffset * -1.0f * IPDScale); eyeOffsets[eye] = eyeOffsetTransform; + // Tell the plugin what pose we're using to render. In this case we're just using the + // unmodified head pose because the only plugin that cares (the Oculus plugin) uses it + // for rotational timewarp. If we move to support positonal timewarp, we need to + // ensure this contains the full pose composed with the eye offsets. displayPlugin->setEyeRenderPose(_frameCount, eye, headPose * glm::inverse(eyeOffsetTransform)); eyeProjections[eye] = displayPlugin->getEyeProjection(eye, baseProjection); diff --git a/libraries/display-plugins/src/display-plugins/hmd/HmdDisplayPlugin.cpp b/libraries/display-plugins/src/display-plugins/hmd/HmdDisplayPlugin.cpp index 67bd0fb369..c3782b907f 100644 --- a/libraries/display-plugins/src/display-plugins/hmd/HmdDisplayPlugin.cpp +++ b/libraries/display-plugins/src/display-plugins/hmd/HmdDisplayPlugin.cpp @@ -160,4 +160,8 @@ void HmdDisplayPlugin::updateFrameData() { Parent::updateFrameData(); Lock lock(_mutex); _currentRenderEyePoses = _renderEyePoses[_currentRenderFrameIndex]; -} \ No newline at end of file +} + +glm::mat4 HmdDisplayPlugin::getHeadPose() const { + return _headPoseCache.get(); +} diff --git a/libraries/display-plugins/src/display-plugins/hmd/HmdDisplayPlugin.h b/libraries/display-plugins/src/display-plugins/hmd/HmdDisplayPlugin.h index 2d1b995ee6..659a3a16fa 100644 --- a/libraries/display-plugins/src/display-plugins/hmd/HmdDisplayPlugin.h +++ b/libraries/display-plugins/src/display-plugins/hmd/HmdDisplayPlugin.h @@ -7,6 +7,8 @@ // #pragma once +#include + #include #include "../OpenGLDisplayPlugin.h" @@ -24,7 +26,7 @@ public: void setEyeRenderPose(uint32_t frameIndex, Eye eye, const glm::mat4& pose) override final; bool isDisplayVisible() const override { return isHmdMounted(); } - + virtual glm::mat4 getHeadPose() const override; protected: virtual void hmdPresent() = 0; @@ -46,6 +48,7 @@ protected: using EyePoses = std::array; QMap _renderEyePoses; EyePoses _currentRenderEyePoses; + ThreadSafeValueCache _headPoseCache { glm::mat4() }; private: bool _enablePreview { false }; diff --git a/plugins/oculus/src/OculusBaseDisplayPlugin.cpp b/plugins/oculus/src/OculusBaseDisplayPlugin.cpp index 7a4bcc665b..9d0838606e 100644 --- a/plugins/oculus/src/OculusBaseDisplayPlugin.cpp +++ b/plugins/oculus/src/OculusBaseDisplayPlugin.cpp @@ -22,10 +22,6 @@ void OculusBaseDisplayPlugin::updateHeadPose(uint32_t frameIndex) { _headPoseCache.set(headPose); } -glm::mat4 OculusBaseDisplayPlugin::getHeadPose() const { - return _headPoseCache.get(); -} - bool OculusBaseDisplayPlugin::isSupported() const { return oculusAvailable(); } diff --git a/plugins/oculus/src/OculusBaseDisplayPlugin.h b/plugins/oculus/src/OculusBaseDisplayPlugin.h index 61179a632f..5455b61a60 100644 --- a/plugins/oculus/src/OculusBaseDisplayPlugin.h +++ b/plugins/oculus/src/OculusBaseDisplayPlugin.h @@ -8,7 +8,6 @@ #pragma once #include -#include #include @@ -22,7 +21,6 @@ public: // Stereo specific methods virtual void resetSensors() override final; virtual void updateHeadPose(uint32_t frameIndex) override; - virtual glm::mat4 getHeadPose() const override; protected: void customizeContext() override; @@ -38,5 +36,4 @@ protected: ovrHmdDesc _hmdDesc; ovrLayerEyeFov _sceneLayer; ovrViewScaleDesc _viewScaleDesc; - ThreadSafeValueCache _headPoseCache { glm::mat4() }; }; diff --git a/plugins/oculusLegacy/src/OculusLegacyDisplayPlugin.cpp b/plugins/oculusLegacy/src/OculusLegacyDisplayPlugin.cpp index b8a1d7c35f..73e18aa697 100644 --- a/plugins/oculusLegacy/src/OculusLegacyDisplayPlugin.cpp +++ b/plugins/oculusLegacy/src/OculusLegacyDisplayPlugin.cpp @@ -41,10 +41,6 @@ void OculusLegacyDisplayPlugin::updateHeadPose(uint32_t frameIndex) { _headPoseCache.set(toGlm(_trackingState.HeadPose.ThePose)); } -glm::mat4 OculusLegacyDisplayPlugin::getHeadPose() const { - return _headPoseCache.get(); -} - bool OculusLegacyDisplayPlugin::isSupported() const { if (!ovr_Initialize(nullptr)) { return false; diff --git a/plugins/oculusLegacy/src/OculusLegacyDisplayPlugin.h b/plugins/oculusLegacy/src/OculusLegacyDisplayPlugin.h index f100028beb..bf4ae26205 100644 --- a/plugins/oculusLegacy/src/OculusLegacyDisplayPlugin.h +++ b/plugins/oculusLegacy/src/OculusLegacyDisplayPlugin.h @@ -8,7 +8,6 @@ #pragma once #include -#include #include @@ -28,7 +27,6 @@ public: // Stereo specific methods virtual void resetSensors() override; virtual void updateHeadPose(uint32_t frameIndex) override; - virtual glm::mat4 getHeadPose() const override; virtual float getTargetFrameRate() override; @@ -54,7 +52,6 @@ private: //ovrTexture _eyeTextures[2]; // FIXME - not currently in use mutable int _hmdScreen { -1 }; bool _hswDismissed { false }; - ThreadSafeValueCache _headPoseCache { glm::mat4() }; }; diff --git a/plugins/openvr/src/OpenVrDisplayPlugin.cpp b/plugins/openvr/src/OpenVrDisplayPlugin.cpp index abc47577d1..843b70807d 100644 --- a/plugins/openvr/src/OpenVrDisplayPlugin.cpp +++ b/plugins/openvr/src/OpenVrDisplayPlugin.cpp @@ -143,10 +143,6 @@ void OpenVrDisplayPlugin::updateHeadPose(uint32_t frameIndex) { _headPoseCache.set(_trackedDevicePoseMat4[0]); } -glm::mat4 OpenVrDisplayPlugin::getHeadPose() const { - return _headPoseCache.get(); -} - void OpenVrDisplayPlugin::hmdPresent() { // Flip y-axis since GL UV coords are backwards. static vr::VRTextureBounds_t leftBounds{ 0, 0, 0.5f, 1 }; diff --git a/plugins/openvr/src/OpenVrDisplayPlugin.h b/plugins/openvr/src/OpenVrDisplayPlugin.h index 4f0320d237..caaf75a4d0 100644 --- a/plugins/openvr/src/OpenVrDisplayPlugin.h +++ b/plugins/openvr/src/OpenVrDisplayPlugin.h @@ -12,7 +12,6 @@ #include #include -#include const float TARGET_RATE_OpenVr = 90.0f; // FIXME: get from sdk tracked device property? This number is vive-only. @@ -29,7 +28,6 @@ public: // Stereo specific methods virtual void resetSensors() override; virtual void updateHeadPose(uint32_t frameIndex) override; - virtual glm::mat4 getHeadPose() const override; protected: void internalActivate() override; @@ -43,5 +41,4 @@ private: std::atomic _hmdActivityLevel { vr::k_EDeviceActivityLevel_Unknown }; static const QString NAME; mutable Mutex _poseMutex; - ThreadSafeValueCache _headPoseCache { glm::mat4() }; }; From d4ed9993b228b59f6d7627737a8a13060c8d798a Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Sun, 20 Mar 2016 15:06:17 -0700 Subject: [PATCH 24/30] don't spam log when a zone is invisible --- libraries/entities-renderer/src/EntityTreeRenderer.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/libraries/entities-renderer/src/EntityTreeRenderer.cpp b/libraries/entities-renderer/src/EntityTreeRenderer.cpp index ece43f3ab3..d90e68cc49 100644 --- a/libraries/entities-renderer/src/EntityTreeRenderer.cpp +++ b/libraries/entities-renderer/src/EntityTreeRenderer.cpp @@ -182,7 +182,9 @@ bool EntityTreeRenderer::findBestZoneAndMaybeContainingEntities(const glm::vec3& // if this entity is a zone, use this time to determine the bestZone if (entity->getType() == EntityTypes::Zone) { if (!entity->getVisible()) { + #ifdef WANT_DEBUG qCDebug(entitiesrenderer) << "not visible"; + #endif } else { float entityVolumeEstimate = entity->getVolumeEstimate(); From f8b8877369916e0c6c9900501bec544d27852b8c Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Sun, 20 Mar 2016 17:37:06 -0700 Subject: [PATCH 25/30] coding standard --- libraries/entities-renderer/src/EntityTreeRenderer.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/libraries/entities-renderer/src/EntityTreeRenderer.cpp b/libraries/entities-renderer/src/EntityTreeRenderer.cpp index d90e68cc49..4a9c37debf 100644 --- a/libraries/entities-renderer/src/EntityTreeRenderer.cpp +++ b/libraries/entities-renderer/src/EntityTreeRenderer.cpp @@ -185,8 +185,7 @@ bool EntityTreeRenderer::findBestZoneAndMaybeContainingEntities(const glm::vec3& #ifdef WANT_DEBUG qCDebug(entitiesrenderer) << "not visible"; #endif - } - else { + } else { float entityVolumeEstimate = entity->getVolumeEstimate(); if (entityVolumeEstimate < _bestZoneVolume) { _bestZoneVolume = entityVolumeEstimate; From 21c688390cea000f05d1b49b288e42e07915956c Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Sun, 20 Mar 2016 17:40:03 -0700 Subject: [PATCH 26/30] make voxels.js buttons work, again --- examples/voxels.js | 25 ++++++++----------------- 1 file changed, 8 insertions(+), 17 deletions(-) diff --git a/examples/voxels.js b/examples/voxels.js index 0776bce627..623b0b5882 100644 --- a/examples/voxels.js +++ b/examples/voxels.js @@ -16,8 +16,8 @@ var deletingVoxels = false; var addingSpheres = false; var deletingSpheres = false; -var offAlpha = 0.5; -var onAlpha = 0.9; +var offAlpha = 0.8; +var onAlpha = 1.0; var editSphereRadius = 4; function floorVector(v) { @@ -48,52 +48,47 @@ var toolBar = (function () { height: toolHeight, alpha: onAlpha, visible: true, - }); + }, false); addVoxelButton = toolBar.addTool({ imageURL: toolIconUrl + "voxel-add.svg", - subImage: { x: 0, y: Tool.IMAGE_WIDTH, width: Tool.IMAGE_WIDTH, height: Tool.IMAGE_HEIGHT }, width: toolWidth, height: toolHeight, alpha: offAlpha, visible: false - }); + }, false); deleteVoxelButton = toolBar.addTool({ imageURL: toolIconUrl + "voxel-delete.svg", - subImage: { x: 0, y: Tool.IMAGE_WIDTH, width: Tool.IMAGE_WIDTH, height: Tool.IMAGE_HEIGHT }, width: toolWidth, height: toolHeight, alpha: offAlpha, visible: false - }); + }, false); addSphereButton = toolBar.addTool({ imageURL: toolIconUrl + "sphere-add.svg", - subImage: { x: 0, y: Tool.IMAGE_WIDTH, width: Tool.IMAGE_WIDTH, height: Tool.IMAGE_HEIGHT }, width: toolWidth, height: toolHeight, alpha: offAlpha, visible: false - }); + }, false); deleteSphereButton = toolBar.addTool({ imageURL: toolIconUrl + "sphere-delete.svg", - subImage: { x: 0, y: Tool.IMAGE_WIDTH, width: Tool.IMAGE_WIDTH, height: Tool.IMAGE_HEIGHT }, width: toolWidth, height: toolHeight, alpha: offAlpha, visible: false - }); + }, false); addTerrainButton = toolBar.addTool({ imageURL: toolIconUrl + "voxel-terrain.svg", - subImage: { x: 0, y: Tool.IMAGE_WIDTH, width: Tool.IMAGE_WIDTH, height: Tool.IMAGE_HEIGHT }, width: toolWidth, height: toolHeight, alpha: onAlpha, visible: false - }); + }, false); that.setActive(false); } @@ -193,7 +188,6 @@ var toolBar = (function () { that.cleanup = function () { toolBar.cleanup(); - // Overlays.deleteOverlay(activeButton); }; @@ -456,9 +450,6 @@ function keyReleaseEvent(event) { function cleanup() { - for (var i = 0; i < overlays.length; i++) { - Overlays.deleteOverlay(overlays[i]); - } toolBar.cleanup(); } From b60000030774550b8a6ec026213ef3efcbdb1c89 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Sun, 20 Mar 2016 17:59:15 -0700 Subject: [PATCH 27/30] refactor code a bit --- examples/voxels.js | 27 ++++++++++++++++++--------- 1 file changed, 18 insertions(+), 9 deletions(-) diff --git a/examples/voxels.js b/examples/voxels.js index 623b0b5882..4ca5d46ff7 100644 --- a/examples/voxels.js +++ b/examples/voxels.js @@ -231,7 +231,6 @@ function grabLowestJointY() { } - function addTerrainBlock() { var baseLocation = getTerrainAlignedLocation(Vec3.sum(MyAvatar.position, {x:8, y:8, z:8})); if (baseLocation.y > MyAvatar.position.y) { @@ -247,10 +246,26 @@ function addTerrainBlock() { baseLocation = getTerrainAlignedLocation(facingPosition); alreadyThere = lookupTerrainForLocation(baseLocation); if (alreadyThere) { - return; + return null; } } + var polyVoxID = addTerrainBlockNearLocation(baseLocation); + + if (polyVoxID) { + var AvatarPositionInVoxelCoords = Entities.worldCoordsToVoxelCoords(polyVoxID, MyAvatar.position); + // TODO -- how to find the avatar's feet? + var topY = Math.round(AvatarPositionInVoxelCoords.y) - 4; + Entities.setVoxelsInCuboid(polyVoxID, {x:0, y:0, z:0}, {x:16, y:topY, z:16}, 255); + } +} + +function addTerrainBlockNearLocation(baseLocation) { + var alreadyThere = lookupTerrainForLocation(baseLocation); + if (alreadyThere) { + return null; + } + var polyVoxID = Entities.addEntity({ type: "PolyVox", name: "terrain", @@ -263,12 +278,6 @@ function addTerrainBlock() { zTextureURL: "http://headache.hungry.com/~seth/hifi/dirt.jpeg" }); - var AvatarPositionInVoxelCoords = Entities.worldCoordsToVoxelCoords(polyVoxID, MyAvatar.position); - // TODO -- how to find the avatar's feet? - var topY = Math.round(AvatarPositionInVoxelCoords.y) - 4; - Entities.setVoxelsInCuboid(polyVoxID, {x:0, y:0, z:0}, {x:16, y:topY, z:16}, 255); - - ////////// // stitch together the terrain with x/y/z NeighorID properties ////////// @@ -324,7 +333,7 @@ function addTerrainBlock() { properties.zPNeighborID = lookupTerrainForLocation(Vec3.sum(baseLocation, {x:0, y:0, z:16})); Entities.editEntity(polyVoxID, properties); - return true; + return polyVoxID; } From b06f027f2ee9bad5e2f59dca9572130e7bc94494 Mon Sep 17 00:00:00 2001 From: Anthony Thibault Date: Sun, 20 Mar 2016 18:20:03 -0700 Subject: [PATCH 28/30] Model: fix for avatars with late loaded transparent textures. --- libraries/render-utils/src/Model.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/libraries/render-utils/src/Model.cpp b/libraries/render-utils/src/Model.cpp index 8b6cc41f35..4c487bc79c 100644 --- a/libraries/render-utils/src/Model.cpp +++ b/libraries/render-utils/src/Model.cpp @@ -571,6 +571,7 @@ void Model::removeFromScene(std::shared_ptr scene, render::Pendin } _renderItems.clear(); _renderItemsSet.clear(); + _meshGroupsKnown = false; _readyWhenAdded = false; } From f274cdcc7f885325b6613af2f71128cbd29f904a Mon Sep 17 00:00:00 2001 From: Brad Hefta-Gaub Date: Mon, 21 Mar 2016 10:47:31 -0700 Subject: [PATCH 29/30] 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; }; From 4f32749c176372f0067d2695454e4b5f624f72b2 Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Mon, 21 Mar 2016 11:54:41 -0700 Subject: [PATCH 30/30] Fix edge-case where resource can receive a signal after request deleted --- libraries/networking/src/ResourceCache.cpp | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/libraries/networking/src/ResourceCache.cpp b/libraries/networking/src/ResourceCache.cpp index e345bed81c..98c6b22089 100644 --- a/libraries/networking/src/ResourceCache.cpp +++ b/libraries/networking/src/ResourceCache.cpp @@ -228,9 +228,10 @@ Resource::Resource(const QUrl& url, bool delayLoad) : Resource::~Resource() { if (_request) { - ResourceCache::requestCompleted(this); + _request->disconnect(this); _request->deleteLater(); _request = nullptr; + ResourceCache::requestCompleted(this); } } @@ -375,7 +376,14 @@ void Resource::handleDownloadProgress(uint64_t bytesReceived, uint64_t bytesTota } void Resource::handleReplyFinished() { - Q_ASSERT(_request); + Q_ASSERT_X(_request, "Resource::handleReplyFinished", "Request should not be null while in handleReplyFinished"); + + if (!_request || _request != sender()) { + // This can happen in the edge case that a request is timed out, but a `finished` signal is emitted before it is deleted. + qWarning(networking) << "Received signal Resource::handleReplyFinished from ResourceRequest that is not the current" + << " request: " << sender() << ", " << _request; + return; + } ResourceCache::requestCompleted(this);