From bc99ef778cb16c94fb48875e30eb5940db6238ae Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Tue, 20 Oct 2015 12:04:29 -0700 Subject: [PATCH] change how render engine is told about model position changes --- interface/src/Application.cpp | 140 +++++++++--------- interface/src/avatar/Avatar.cpp | 44 +++--- interface/src/avatar/Avatar.h | 3 + interface/src/avatar/AvatarManager.cpp | 26 ++-- interface/src/avatar/AvatarUpdate.cpp | 10 +- libraries/avatars/src/AvatarData.cpp | 58 ++------ libraries/avatars/src/AvatarData.h | 22 +-- .../render-utils/src/MeshPartPayload.cpp | 22 ++- libraries/render-utils/src/MeshPartPayload.h | 10 +- libraries/render-utils/src/Model.cpp | 62 +++++--- libraries/render-utils/src/Model.h | 11 +- 11 files changed, 213 insertions(+), 195 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 9b5b83492c..cab189e7a0 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -1086,75 +1086,77 @@ void Application::paintGL() { { PerformanceTimer perfTimer("CameraUpdates"); - auto myAvatar = getMyAvatar(); - - myAvatar->startCapture(); - if (_myCamera.getMode() == CAMERA_MODE_FIRST_PERSON || _myCamera.getMode() == CAMERA_MODE_THIRD_PERSON) { - Menu::getInstance()->setIsOptionChecked(MenuOption::FirstPerson, myAvatar->getBoomLength() <= MyAvatar::ZOOM_MIN); - Menu::getInstance()->setIsOptionChecked(MenuOption::ThirdPerson, !(myAvatar->getBoomLength() <= MyAvatar::ZOOM_MIN)); - cameraMenuChanged(); - } - - // The render mode is default or mirror if the camera is in mirror mode, assigned further below - renderArgs._renderMode = RenderArgs::DEFAULT_RENDER_MODE; - - // Always use the default eye position, not the actual head eye position. - // Using the latter will cause the camera to wobble with idle animations, - // or with changes from the face tracker - if (_myCamera.getMode() == CAMERA_MODE_FIRST_PERSON) { - if (isHMDMode()) { - mat4 camMat = myAvatar->getSensorToWorldMatrix() * myAvatar->getHMDSensorMatrix(); - _myCamera.setPosition(extractTranslation(camMat)); - _myCamera.setRotation(glm::quat_cast(camMat)); - } else { - _myCamera.setPosition(myAvatar->getDefaultEyePosition()); - _myCamera.setRotation(myAvatar->getHead()->getCameraOrientation()); + myAvatar->withReadLock([&] { + if (_myCamera.getMode() == CAMERA_MODE_FIRST_PERSON || _myCamera.getMode() == CAMERA_MODE_THIRD_PERSON) { + Menu::getInstance()->setIsOptionChecked(MenuOption::FirstPerson, + myAvatar->getBoomLength() <= MyAvatar::ZOOM_MIN); + Menu::getInstance()->setIsOptionChecked(MenuOption::ThirdPerson, + !(myAvatar->getBoomLength() <= MyAvatar::ZOOM_MIN)); + cameraMenuChanged(); } - } else if (_myCamera.getMode() == CAMERA_MODE_THIRD_PERSON) { - if (isHMDMode()) { - auto hmdWorldMat = myAvatar->getSensorToWorldMatrix() * myAvatar->getHMDSensorMatrix(); - _myCamera.setRotation(glm::normalize(glm::quat_cast(hmdWorldMat))); - auto worldBoomOffset = myAvatar->getOrientation() * (myAvatar->getScale() * myAvatar->getBoomLength() * glm::vec3(0.0f, 0.0f, 1.0f)); - _myCamera.setPosition(extractTranslation(hmdWorldMat) + worldBoomOffset); - } else { - _myCamera.setRotation(myAvatar->getHead()->getOrientation()); - if (Menu::getInstance()->isOptionChecked(MenuOption::CenterPlayerInView)) { - _myCamera.setPosition(myAvatar->getDefaultEyePosition() - + _myCamera.getRotation() - * (myAvatar->getScale() * myAvatar->getBoomLength() * glm::vec3(0.0f, 0.0f, 1.0f))); + + // The render mode is default or mirror if the camera is in mirror mode, assigned further below + renderArgs._renderMode = RenderArgs::DEFAULT_RENDER_MODE; + + // Always use the default eye position, not the actual head eye position. + // Using the latter will cause the camera to wobble with idle animations, + // or with changes from the face tracker + if (_myCamera.getMode() == CAMERA_MODE_FIRST_PERSON) { + if (isHMDMode()) { + mat4 camMat = myAvatar->getSensorToWorldMatrix() * myAvatar->getHMDSensorMatrix(); + _myCamera.setPosition(extractTranslation(camMat)); + _myCamera.setRotation(glm::quat_cast(camMat)); } else { - _myCamera.setPosition(myAvatar->getDefaultEyePosition() - + myAvatar->getOrientation() - * (myAvatar->getScale() * myAvatar->getBoomLength() * glm::vec3(0.0f, 0.0f, 1.0f))); + _myCamera.setPosition(myAvatar->getDefaultEyePosition()); + _myCamera.setRotation(myAvatar->getHead()->getCameraOrientation()); } + } else if (_myCamera.getMode() == CAMERA_MODE_THIRD_PERSON) { + if (isHMDMode()) { + auto hmdWorldMat = myAvatar->getSensorToWorldMatrix() * myAvatar->getHMDSensorMatrix(); + _myCamera.setRotation(glm::normalize(glm::quat_cast(hmdWorldMat))); + auto worldBoomOffset = myAvatar->getOrientation() * + (myAvatar->getScale() * myAvatar->getBoomLength() * glm::vec3(0.0f, 0.0f, 1.0f)); + _myCamera.setPosition(extractTranslation(hmdWorldMat) + worldBoomOffset); + } else { + _myCamera.setRotation(myAvatar->getHead()->getOrientation()); + if (Menu::getInstance()->isOptionChecked(MenuOption::CenterPlayerInView)) { + _myCamera.setPosition(myAvatar->getDefaultEyePosition() + + _myCamera.getRotation() * + (myAvatar->getScale() * myAvatar->getBoomLength() * glm::vec3(0.0f, 0.0f, 1.0f))); + } else { + _myCamera.setPosition(myAvatar->getDefaultEyePosition() + + myAvatar->getOrientation() * + (myAvatar->getScale() * myAvatar->getBoomLength() * glm::vec3(0.0f, 0.0f, 1.0f))); + } + } + } else if (_myCamera.getMode() == CAMERA_MODE_MIRROR) { + if (isHMDMode()) { + glm::quat hmdRotation = extractRotation(myAvatar->getHMDSensorMatrix()); + _myCamera.setRotation(myAvatar->getWorldAlignedOrientation() + * glm::quat(glm::vec3(0.0f, PI + _rotateMirror, 0.0f)) * hmdRotation); + glm::vec3 hmdOffset = extractTranslation(myAvatar->getHMDSensorMatrix()); + _myCamera.setPosition(myAvatar->getDefaultEyePosition() + + glm::vec3(0, _raiseMirror * myAvatar->getAvatarScale(), 0) + + (myAvatar->getOrientation() * glm::quat(glm::vec3(0.0f, _rotateMirror, 0.0f))) * + glm::vec3(0.0f, 0.0f, -1.0f) * MIRROR_FULLSCREEN_DISTANCE * _scaleMirror + + (myAvatar->getOrientation() * + glm::quat(glm::vec3(0.0f, PI + _rotateMirror, 0.0f))) * hmdOffset); + } else { + _myCamera.setRotation(myAvatar->getWorldAlignedOrientation() + * glm::quat(glm::vec3(0.0f, PI + _rotateMirror, 0.0f))); + _myCamera.setPosition(myAvatar->getDefaultEyePosition() + + glm::vec3(0, _raiseMirror * myAvatar->getAvatarScale(), 0) + + (myAvatar->getOrientation() * glm::quat(glm::vec3(0.0f, _rotateMirror, 0.0f))) * + glm::vec3(0.0f, 0.0f, -1.0f) * MIRROR_FULLSCREEN_DISTANCE * _scaleMirror); + } + renderArgs._renderMode = RenderArgs::MIRROR_RENDER_MODE; } - } else if (_myCamera.getMode() == CAMERA_MODE_MIRROR) { - if (isHMDMode()) { - glm::quat hmdRotation = extractRotation(myAvatar->getHMDSensorMatrix()); - _myCamera.setRotation(myAvatar->getWorldAlignedOrientation() - * glm::quat(glm::vec3(0.0f, PI + _rotateMirror, 0.0f)) * hmdRotation); - glm::vec3 hmdOffset = extractTranslation(myAvatar->getHMDSensorMatrix()); - _myCamera.setPosition(myAvatar->getDefaultEyePosition() - + glm::vec3(0, _raiseMirror * myAvatar->getAvatarScale(), 0) - + (myAvatar->getOrientation() * glm::quat(glm::vec3(0.0f, _rotateMirror, 0.0f))) * - glm::vec3(0.0f, 0.0f, -1.0f) * MIRROR_FULLSCREEN_DISTANCE * _scaleMirror - + (myAvatar->getOrientation() * glm::quat(glm::vec3(0.0f, PI + _rotateMirror, 0.0f))) * hmdOffset); - } else { - _myCamera.setRotation(myAvatar->getWorldAlignedOrientation() - * glm::quat(glm::vec3(0.0f, PI + _rotateMirror, 0.0f))); - _myCamera.setPosition(myAvatar->getDefaultEyePosition() - + glm::vec3(0, _raiseMirror * myAvatar->getAvatarScale(), 0) - + (myAvatar->getOrientation() * glm::quat(glm::vec3(0.0f, _rotateMirror, 0.0f))) * - glm::vec3(0.0f, 0.0f, -1.0f) * MIRROR_FULLSCREEN_DISTANCE * _scaleMirror); + // Update camera position + if (!isHMDMode()) { + _myCamera.update(1.0f / _fps); } - renderArgs._renderMode = RenderArgs::MIRROR_RENDER_MODE; - } - // Update camera position - if (!isHMDMode()) { - _myCamera.update(1.0f / _fps); - } - myAvatar->endCapture(); + }); } // Primary rendering pass @@ -3386,9 +3388,9 @@ void Application::displaySide(RenderArgs* renderArgs, Camera& theCamera, bool se // FIXME: This preRender call is temporary until we create a separate render::scene for the mirror rendering. // Then we can move this logic into the Avatar::simulate call. auto myAvatar = getMyAvatar(); - myAvatar->startRender(); - myAvatar->preRender(renderArgs); - myAvatar->endRender(); + myAvatar->withReadLock([&] { + myAvatar->preRender(renderArgs); + }); activeRenderingThread = QThread::currentThread(); @@ -3502,9 +3504,9 @@ void Application::displaySide(RenderArgs* renderArgs, Camera& theCamera, bool se _renderEngine->setRenderContext(renderContext); // Before the deferred pass, let's try to use the render engine - myAvatar->startRenderRun(); - _renderEngine->run(); - myAvatar->endRenderRun(); + myAvatar->withReadLock([&] { + _renderEngine->run(); + }); auto engineRC = _renderEngine->getRenderContext(); sceneInterface->setEngineFeedOpaqueItems(engineRC->_numFeedOpaqueItems); diff --git a/interface/src/avatar/Avatar.cpp b/interface/src/avatar/Avatar.cpp index 92493ab23f..c70c1a5501 100644 --- a/interface/src/avatar/Avatar.cpp +++ b/interface/src/avatar/Avatar.cpp @@ -303,8 +303,6 @@ void Avatar::removeFromScene(AvatarSharedPointer self, std::shared_ptr_batch; if (glm::distance(DependencyManager::get()->getMyAvatar()->getPosition(), getPosition()) < 10.0f) { @@ -375,7 +373,6 @@ void Avatar::render(RenderArgs* renderArgs, const glm::vec3& cameraPosition) { } if (frustum->sphereInFrustum(getPosition(), boundingRadius) == ViewFrustum::OUTSIDE) { - endRender(); return; } @@ -529,7 +526,6 @@ void Avatar::render(RenderArgs* renderArgs, const glm::vec3& cameraPosition) { renderDisplayName(batch, frustum, textPosition); } } - endRender(); } glm::quat Avatar::computeRotationFromBodyToWorldUp(float proportion) const { @@ -993,23 +989,25 @@ void Avatar::setBillboard(const QByteArray& billboard) { } int Avatar::parseDataFromBuffer(const QByteArray& buffer) { - startUpdate(); - if (!_initialized) { - // now that we have data for this Avatar we are go for init - init(); - } + int bytesRead; - // change in position implies movement - glm::vec3 oldPosition = getPosition(); + withWriteLock([&] { + if (!_initialized) { + // now that we have data for this Avatar we are go for init + init(); + } - int bytesRead = AvatarData::parseDataFromBuffer(buffer); + // change in position implies movement + glm::vec3 oldPosition = getPosition(); - const float MOVE_DISTANCE_THRESHOLD = 0.001f; - _moving = glm::distance(oldPosition, getPosition()) > MOVE_DISTANCE_THRESHOLD; - if (_moving && _motionState) { - _motionState->addDirtyFlags(Simulation::DIRTY_POSITION); - } - endUpdate(); + bytesRead = AvatarData::parseDataFromBuffer(buffer); + + const float MOVE_DISTANCE_THRESHOLD = 0.001f; + _moving = glm::distance(oldPosition, getPosition()) > MOVE_DISTANCE_THRESHOLD; + if (_moving && _motionState) { + _motionState->addDirtyFlags(Simulation::DIRTY_POSITION); + } + }); return bytesRead; } @@ -1201,3 +1199,13 @@ glm::quat Avatar::getRightPalmRotation() { getSkeletonModel().getJointRotationInWorldFrame(getSkeletonModel().getRightHandJointIndex(), rightRotation); return rightRotation; } + +void Avatar::setPosition(const glm::vec3& position) { + AvatarData::setPosition(position); + updateAttitude(); +} + +void Avatar::setOrientation(const glm::quat& orientation) { + AvatarData::setOrientation(orientation); + updateAttitude(); +} diff --git a/interface/src/avatar/Avatar.h b/interface/src/avatar/Avatar.h index 2b7d27b2e3..2366e79c5b 100644 --- a/interface/src/avatar/Avatar.h +++ b/interface/src/avatar/Avatar.h @@ -174,6 +174,9 @@ public: void setMotionState(AvatarMotionState* motionState) { _motionState = motionState; } AvatarMotionState* getMotionState() { return _motionState; } + virtual void setPosition(const glm::vec3& position); + virtual void setOrientation(const glm::quat& orientation); + public slots: glm::vec3 getLeftPalmPosition(); glm::vec3 getLeftPalmVelocity(); diff --git a/interface/src/avatar/AvatarManager.cpp b/interface/src/avatar/AvatarManager.cpp index f42cdc200b..16e136f9d1 100644 --- a/interface/src/avatar/AvatarManager.cpp +++ b/interface/src/avatar/AvatarManager.cpp @@ -129,9 +129,9 @@ void AvatarManager::updateOtherAvatars(float deltaTime) { _avatarFades.push_back(avatarIterator.value()); avatarIterator = _avatarHash.erase(avatarIterator); } else { - avatar->startUpdate(); - avatar->simulate(deltaTime); - avatar->endUpdate(); + avatar->withWriteLock([&] { + avatar->simulate(deltaTime); + }); ++avatarIterator; } } @@ -150,16 +150,16 @@ void AvatarManager::simulateAvatarFades(float deltaTime) { render::PendingChanges pendingChanges; while (fadingIterator != _avatarFades.end()) { auto avatar = std::static_pointer_cast(*fadingIterator); - avatar->startUpdate(); - avatar->setTargetScale(avatar->getAvatarScale() * SHRINK_RATE, true); - if (avatar->getTargetScale() < MIN_FADE_SCALE) { - avatar->removeFromScene(*fadingIterator, scene, pendingChanges); - fadingIterator = _avatarFades.erase(fadingIterator); - } else { - avatar->simulate(deltaTime); - ++fadingIterator; - } - avatar->endUpdate(); + avatar->withWriteLock([&] { + avatar->setTargetScale(avatar->getAvatarScale() * SHRINK_RATE, true); + if (avatar->getTargetScale() < MIN_FADE_SCALE) { + avatar->removeFromScene(*fadingIterator, scene, pendingChanges); + fadingIterator = _avatarFades.erase(fadingIterator); + } else { + avatar->simulate(deltaTime); + ++fadingIterator; + } + }); } scene->enqueuePendingChanges(pendingChanges); } diff --git a/interface/src/avatar/AvatarUpdate.cpp b/interface/src/avatar/AvatarUpdate.cpp index acdb251950..a32948f598 100644 --- a/interface/src/avatar/AvatarUpdate.cpp +++ b/interface/src/avatar/AvatarUpdate.cpp @@ -56,11 +56,11 @@ bool AvatarUpdate::process() { //gets current lookat data, removes missing avatars, etc. manager->updateOtherAvatars(deltaSeconds); - myAvatar->startUpdate(); - qApp->updateMyAvatarLookAtPosition(); - // Sample hardware, update view frustum if needed, and send avatar data to mixer/nodes - manager->updateMyAvatar(deltaSeconds); - myAvatar->endUpdate(); + myAvatar->withWriteLock([&] { + qApp->updateMyAvatarLookAtPosition(); + // Sample hardware, update view frustum if needed, and send avatar data to mixer/nodes + manager->updateMyAvatar(deltaSeconds); + }); if (!isThreaded()) { return true; diff --git a/libraries/avatars/src/AvatarData.cpp b/libraries/avatars/src/AvatarData.cpp index 6f4c89abe7..9b2967cf5d 100644 --- a/libraries/avatars/src/AvatarData.cpp +++ b/libraries/avatars/src/AvatarData.cpp @@ -110,51 +110,25 @@ void AvatarData::setBodyRoll(float bodyRoll) { setOrientation(glm::quat(glm::radians(eulerAngles))); } +void AvatarData::setPosition(const glm::vec3& position) { + withWriteLock([&] { + SpatiallyNestable::setPosition(position); + }); +} + +void AvatarData::setOrientation(const glm::quat& orientation) { + withWriteLock([&] { + SpatiallyNestable::setOrientation(orientation); + }); +} + // There are a number of possible strategies for this set of tools through endRender, below. void AvatarData::nextAttitude(glm::vec3 position, glm::quat orientation) { - avatarLock.lock(); - setPosition(position); - setOrientation(orientation); - avatarLock.unlock(); -} -void AvatarData::startCapture() { - avatarLock.lock(); - assert(_nextAllowed); - _nextAllowed = false; - _nextPosition = getPosition(); - _nextOrientation = getOrientation(); -} -void AvatarData::endCapture() { - avatarLock.unlock(); -} -void AvatarData::startUpdate() { - avatarLock.lock(); -} -void AvatarData::endUpdate() { - avatarLock.unlock(); -} -void AvatarData::startRenderRun() { - // I'd like to get rid of this and just (un)lock at (end-)startRender. - // But somehow that causes judder in rotations. - avatarLock.lock(); -} -void AvatarData::endRenderRun() { - avatarLock.unlock(); -} -void AvatarData::startRender() { - glm::vec3 pos = getPosition(); - glm::quat rot = getOrientation(); - setPosition(_nextPosition); - setOrientation(_nextOrientation); + withWriteLock([&] { + SpatiallyNestable::setPosition(position); + SpatiallyNestable::setOrientation(orientation); + }); updateAttitude(); - _nextPosition = pos; - _nextOrientation = rot; -} -void AvatarData::endRender() { - setPosition(_nextPosition); - setOrientation(_nextOrientation); - updateAttitude(); - _nextAllowed = true; } float AvatarData::getTargetScale() const { diff --git a/libraries/avatars/src/AvatarData.h b/libraries/avatars/src/AvatarData.h index a7d216be6d..9f8224caf8 100644 --- a/libraries/avatars/src/AvatarData.h +++ b/libraries/avatars/src/AvatarData.h @@ -44,13 +44,13 @@ typedef unsigned long long quint64; #include #include #include -#include #include #include #include #include #include +#include #include "AABox.h" #include "HandData.h" @@ -59,6 +59,7 @@ typedef unsigned long long quint64; #include "Player.h" #include "Recorder.h" + using AvatarSharedPointer = std::shared_ptr; using AvatarWeakPointer = std::weak_ptr; using AvatarHash = QHash; @@ -135,7 +136,7 @@ class QDataStream; class AttachmentData; class JointData; -class AvatarData : public QObject, public SpatiallyNestable { +class AvatarData : public QObject, public ReadWriteLockable, public SpatiallyNestable { Q_OBJECT Q_PROPERTY(glm::vec3 position READ getPosition WRITE setPosition) @@ -195,15 +196,10 @@ public: float getBodyRoll() const; void setBodyRoll(float bodyRoll); + virtual void setPosition(const glm::vec3& position); + virtual void setOrientation(const glm::quat& orientation); + void nextAttitude(glm::vec3 position, glm::quat orientation); // Can be safely called at any time. - void startCapture(); // start/end of the period in which the latest values are about to be captured for camera, etc. - void endCapture(); - void startUpdate(); // start/end of update iteration - void endUpdate(); - void startRender(); // start/end of rendering of this object - void startRenderRun(); // start/end of entire scene. - void endRenderRun(); - void endRender(); virtual void updateAttitude() {} // Tell skeleton mesh about changes glm::quat getHeadOrientation() const { return _headData->getOrientation(); } @@ -360,10 +356,6 @@ protected: QUuid _sessionUUID; glm::vec3 _handPosition; - glm::vec3 _nextPosition {}; - glm::quat _nextOrientation {}; - bool _nextAllowed {true}; - // Body scale float _targetScale; @@ -413,8 +405,6 @@ protected: SimpleMovingAverage _averageBytesReceived; - QMutex avatarLock; // Name is redundant, but it aids searches. - private: static QUrl _defaultFullAvatarModelUrl; // privatize the copy constructor and assignment operator so they cannot be called diff --git a/libraries/render-utils/src/MeshPartPayload.cpp b/libraries/render-utils/src/MeshPartPayload.cpp index 80327b7e54..294bf015e0 100644 --- a/libraries/render-utils/src/MeshPartPayload.cpp +++ b/libraries/render-utils/src/MeshPartPayload.cpp @@ -39,9 +39,14 @@ namespace render { using namespace render; -MeshPartPayload::MeshPartPayload(Model* model, int meshIndex, int partIndex, int shapeIndex) : - model(model), meshIndex(meshIndex), partIndex(partIndex), _shapeID(shapeIndex) -{ +MeshPartPayload::MeshPartPayload(Model* model, int meshIndex, int partIndex, int shapeIndex, + glm::vec3 position, glm::quat orientation) : + model(model), + meshIndex(meshIndex), + partIndex(partIndex), + _shapeID(shapeIndex), + _modelPosition(position), + _modelOrientation(orientation) { initCache(); } @@ -66,6 +71,11 @@ void MeshPartPayload::initCache() { } +void MeshPartPayload::updateModelLocation(glm::vec3 position, glm::quat orientation) { + _modelPosition = position; + _modelOrientation = orientation; +} + render::ItemKey MeshPartPayload::getKey() const { ItemKey::Builder builder; builder.withTypeShape(); @@ -91,7 +101,7 @@ render::ItemKey MeshPartPayload::getKey() const { render::Item::Bound MeshPartPayload::getBound() const { // NOTE: we can't cache this bounds because we need to handle the case of a moving // entity or mesh part. - return model->getPartBounds(meshIndex, partIndex); + return model->getPartBounds(meshIndex, partIndex, _modelPosition, _modelOrientation); } void MeshPartPayload::drawCall(gpu::Batch& batch) const { @@ -222,7 +232,7 @@ void MeshPartPayload::bindTransform(gpu::Batch& batch, const ModelRender::Locati transform = Transform(state.clusterMatrices[0]); } } - transform.preTranslate(model->_translation); + transform.preTranslate(_modelPosition); batch.setModelTransform(transform); } @@ -247,7 +257,7 @@ void MeshPartPayload::render(RenderArgs* args) const { } // Back to model to update the cluster matrices right now - model->updateClusterMatrices(); + model->updateClusterMatrices(_modelPosition, _modelOrientation); const FBXMesh& mesh = geometry.meshes.at(meshIndex); diff --git a/libraries/render-utils/src/MeshPartPayload.h b/libraries/render-utils/src/MeshPartPayload.h index 51e577e7c7..b29d9510d1 100644 --- a/libraries/render-utils/src/MeshPartPayload.h +++ b/libraries/render-utils/src/MeshPartPayload.h @@ -24,7 +24,7 @@ class Model; class MeshPartPayload { public: - MeshPartPayload(Model* model, int meshIndex, int partIndex, int shapeIndex); + MeshPartPayload(Model* model, int meshIndex, int partIndex, int shapeIndex, glm::vec3 position, glm::quat orientation); typedef render::Payload Payload; typedef Payload::DataPointer Pointer; @@ -33,7 +33,11 @@ public: int meshIndex; int partIndex; int _shapeID; - + glm::vec3 _modelPosition; + glm::quat _modelOrientation; + + void updateModelLocation(glm::vec3 position, glm::quat orientation); + // Render Item interface render::ItemKey getKey() const; render::Item::Bound getBound() const; @@ -63,4 +67,4 @@ namespace render { template <> void payloadRender(const MeshPartPayload::Pointer& payload, RenderArgs* args); } -#endif // hifi_MeshPartPayload_h \ No newline at end of file +#endif // hifi_MeshPartPayload_h diff --git a/libraries/render-utils/src/Model.cpp b/libraries/render-utils/src/Model.cpp index 38f5ffdabe..9c3b8adc17 100644 --- a/libraries/render-utils/src/Model.cpp +++ b/libraries/render-utils/src/Model.cpp @@ -73,13 +73,16 @@ Model::~Model() { AbstractViewStateInterface* Model::_viewState = NULL; + void Model::setTranslation(const glm::vec3& translation) { _translation = translation; + enqueueLocationChange(); } - + void Model::setRotation(const glm::quat& rotation) { _rotation = rotation; -} + enqueueLocationChange(); +} void Model::setScale(const glm::vec3& scale) { setScaleInternal(scale); @@ -107,6 +110,20 @@ void Model::setOffset(const glm::vec3& offset) { _snappedToRegistrationPoint = false; } +void Model::enqueueLocationChange() { + render::ScenePointer scene = AbstractViewStateInterface::instance()->getMain3DScene(); + + render::PendingChanges pendingChanges; + foreach (auto itemID, _renderItems.keys()) { + pendingChanges.updateItem(itemID, [=](MeshPartPayload& data) { + data.updateModelLocation(_translation, _rotation); + data.model->_needsUpdateClusterMatrices = true; + }); + } + + scene->enqueuePendingChanges(pendingChanges); +} + QVector Model::createJointStates(const FBXGeometry& geometry) { QVector jointStates; for (int i = 0; i < geometry.joints.size(); ++i) { @@ -378,7 +395,7 @@ void Model::recalculateMeshBoxes(bool pickAgainstTriangles) { _calculatedMeshPartBoxes.clear(); for (int i = 0; i < numberOfMeshes; i++) { const FBXMesh& mesh = geometry.meshes.at(i); - Extents scaledMeshExtents = calculateScaledOffsetExtents(mesh.meshExtents); + Extents scaledMeshExtents = calculateScaledOffsetExtents(mesh.meshExtents, _translation, _rotation); _calculatedMeshBoxes[i] = AABox(scaledMeshExtents); @@ -659,7 +676,8 @@ Extents Model::getUnscaledMeshExtents() const { return scaledExtents; } -Extents Model::calculateScaledOffsetExtents(const Extents& extents) const { +Extents Model::calculateScaledOffsetExtents(const Extents& extents, + glm::vec3 modelPosition, glm::quat modelOrientation) const { // we need to include any fst scaling, translation, and rotation, which is captured in the offset matrix glm::vec3 minimum = glm::vec3(_geometry->getFBXGeometry().offset * glm::vec4(extents.minimum, 1.0f)); glm::vec3 maximum = glm::vec3(_geometry->getFBXGeometry().offset * glm::vec4(extents.maximum, 1.0f)); @@ -667,17 +685,17 @@ Extents Model::calculateScaledOffsetExtents(const Extents& extents) const { Extents scaledOffsetExtents = { ((minimum + _offset) * _scale), ((maximum + _offset) * _scale) }; - Extents rotatedExtents = scaledOffsetExtents.getRotated(_rotation); + Extents rotatedExtents = scaledOffsetExtents.getRotated(modelOrientation); - Extents translatedExtents = { rotatedExtents.minimum + _translation, - rotatedExtents.maximum + _translation }; + Extents translatedExtents = { rotatedExtents.minimum + modelPosition, + rotatedExtents.maximum + modelPosition }; return translatedExtents; } /// Returns the world space equivalent of some box in model space. -AABox Model::calculateScaledOffsetAABox(const AABox& box) const { - return AABox(calculateScaledOffsetExtents(Extents(box))); +AABox Model::calculateScaledOffsetAABox(const AABox& box, glm::vec3 modelPosition, glm::quat modelOrientation) const { + return AABox(calculateScaledOffsetExtents(Extents(box), modelPosition, modelOrientation)); } glm::vec3 Model::calculateScaledOffsetPoint(const glm::vec3& point) const { @@ -971,7 +989,7 @@ void Model::simulateInternal(float deltaTime) { glm::mat4 parentTransform = glm::scale(_scale) * glm::translate(_offset) * geometry.offset; updateRig(deltaTime, parentTransform); } -void Model::updateClusterMatrices() { +void Model::updateClusterMatrices(glm::vec3 modelPosition, glm::quat modelOrientation) { PerformanceTimer perfTimer("Model::updateClusterMatrices"); if (!_needsUpdateClusterMatrices) { @@ -985,7 +1003,7 @@ void Model::updateClusterMatrices() { glm::vec4(0.0f, 0.0f, 0.0f, 1.0f)); auto cauterizeMatrix = _rig->getJointTransform(geometry.neckJointIndex) * zeroScale; - glm::mat4 modelToWorld = glm::mat4_cast(_rotation); + glm::mat4 modelToWorld = glm::mat4_cast(modelOrientation); for (int i = 0; i < _meshStates.size(); i++) { MeshState& state = _meshStates[i]; const FBXMesh& mesh = geometry.meshes.at(i); @@ -1007,16 +1025,21 @@ void Model::updateClusterMatrices() { // Once computed the cluster matrices, update the buffer(s) if (mesh.clusters.size() > 1) { if (!state.clusterBuffer) { - state.clusterBuffer = std::make_shared(state.clusterMatrices.size() * sizeof(glm::mat4), (const gpu::Byte*) state.clusterMatrices.constData()); + state.clusterBuffer = std::make_shared(state.clusterMatrices.size() * sizeof(glm::mat4), + (const gpu::Byte*) state.clusterMatrices.constData()); } else { - state.clusterBuffer->setSubData(0, state.clusterMatrices.size() * sizeof(glm::mat4), (const gpu::Byte*) state.clusterMatrices.constData()); + state.clusterBuffer->setSubData(0, state.clusterMatrices.size() * sizeof(glm::mat4), + (const gpu::Byte*) state.clusterMatrices.constData()); } if (!_cauterizeBoneSet.empty() && (state.cauterizedClusterMatrices.size() > 1)) { if (!state.cauterizedClusterBuffer) { - state.cauterizedClusterBuffer = std::make_shared(state.cauterizedClusterMatrices.size() * sizeof(glm::mat4), (const gpu::Byte*) state.cauterizedClusterMatrices.constData()); + state.cauterizedClusterBuffer = + std::make_shared(state.cauterizedClusterMatrices.size() * sizeof(glm::mat4), + (const gpu::Byte*) state.cauterizedClusterMatrices.constData()); } else { - state.cauterizedClusterBuffer->setSubData(0, state.cauterizedClusterMatrices.size() * sizeof(glm::mat4), (const gpu::Byte*) state.cauterizedClusterMatrices.constData()); + state.cauterizedClusterBuffer->setSubData(0, state.cauterizedClusterMatrices.size() * sizeof(glm::mat4), + (const gpu::Byte*) state.cauterizedClusterMatrices.constData()); } } } @@ -1109,7 +1132,7 @@ void Model::deleteGeometry() { _blendedBlendshapeCoefficients.clear(); } -AABox Model::getPartBounds(int meshIndex, int partIndex) { +AABox Model::getPartBounds(int meshIndex, int partIndex, glm::vec3 modelPosition, glm::quat modelOrientation) { if (!_geometry || !_geometry->isLoaded()) { return AABox(); @@ -1120,7 +1143,7 @@ AABox Model::getPartBounds(int meshIndex, int partIndex) { bool isSkinned = state.clusterMatrices.size() > 1; if (isSkinned) { // if we're skinned return the entire mesh extents because we can't know for sure our clusters don't move us - return calculateScaledOffsetAABox(_geometry->getFBXGeometry().meshExtents); + return calculateScaledOffsetAABox(_geometry->getFBXGeometry().meshExtents, modelPosition, modelOrientation); } } if (_geometry->getFBXGeometry().meshes.size() > meshIndex) { @@ -1138,7 +1161,7 @@ AABox Model::getPartBounds(int meshIndex, int partIndex) { // // If we not skinned use the bounds of the subMesh for all it's parts const FBXMesh& mesh = _geometry->getFBXGeometry().meshes.at(meshIndex); - return calculateScaledOffsetExtents(mesh.meshExtents); + return calculateScaledOffsetExtents(mesh.meshExtents, modelPosition, modelOrientation); } return AABox(); } @@ -1164,7 +1187,7 @@ void Model::segregateMeshGroups() { // Create the render payloads int totalParts = mesh.parts.size(); for (int partIndex = 0; partIndex < totalParts; partIndex++) { - _renderItemsSet << std::make_shared(this, i, partIndex, shapeID); + _renderItemsSet << std::make_shared(this, i, partIndex, shapeID, _translation, _rotation); shapeID++; } } @@ -1184,6 +1207,7 @@ bool Model::initWhenReady(render::ScenePointer scene) { _renderItems.insert(item, renderPayload); pendingChanges.resetItem(item, renderPayload); pendingChanges.updateItem(item, [&](MeshPartPayload& data) { + data.updateModelLocation(_translation, _rotation); data.model->_needsUpdateClusterMatrices = true; }); } diff --git a/libraries/render-utils/src/Model.h b/libraries/render-utils/src/Model.h index e3a9ce9ac3..68468642da 100644 --- a/libraries/render-utils/src/Model.h +++ b/libraries/render-utils/src/Model.h @@ -86,7 +86,8 @@ public: bool isVisible() const { return _isVisible; } - AABox getPartBounds(int meshIndex, int partIndex); + void updateRenderItems(); + AABox getPartBounds(int meshIndex, int partIndex, glm::vec3 modelPosition, glm::quat modelOrientation); bool maybeStartBlender(); @@ -109,7 +110,7 @@ public: bool getSnapModelToRegistrationPoint() { return _snapModelToRegistrationPoint; } virtual void simulate(float deltaTime, bool fullUpdate = true); - void updateClusterMatrices(); + void updateClusterMatrices(glm::vec3 modelPosition, glm::quat modelOrientation); /// Returns a reference to the shared geometry. const QSharedPointer& getGeometry() const { return _geometry; } @@ -183,6 +184,8 @@ public: void setScale(const glm::vec3& scale); const glm::vec3& getScale() const { return _scale; } + void enqueueLocationChange(); + /// enables/disables scale to fit behavior, the model will be automatically scaled to the specified largest dimension bool getIsScaledToFit() const { return _scaledToFit; } /// is model scaled to fit const glm::vec3& getScaleToFitDimensions() const { return _scaleToFitDimensions; } /// the dimensions model is scaled to @@ -210,10 +213,10 @@ protected: Extents getUnscaledMeshExtents() const; /// Returns the scaled equivalent of some extents in model space. - Extents calculateScaledOffsetExtents(const Extents& extents) const; + Extents calculateScaledOffsetExtents(const Extents& extents, glm::vec3 modelPosition, glm::quat modelOrientation) const; /// Returns the world space equivalent of some box in model space. - AABox calculateScaledOffsetAABox(const AABox& box) const; + AABox calculateScaledOffsetAABox(const AABox& box, glm::vec3 modelPosition, glm::quat modelOrientation) const; /// Returns the scaled equivalent of a point in model space. glm::vec3 calculateScaledOffsetPoint(const glm::vec3& point) const;