change how render engine is told about model position changes

This commit is contained in:
Seth Alves 2015-10-20 12:04:29 -07:00
parent 5c28c0bba0
commit bc99ef778c
11 changed files with 213 additions and 195 deletions

View file

@ -1086,75 +1086,77 @@ void Application::paintGL() {
{ {
PerformanceTimer perfTimer("CameraUpdates"); PerformanceTimer perfTimer("CameraUpdates");
auto myAvatar = getMyAvatar(); auto myAvatar = getMyAvatar();
myAvatar->withReadLock([&] {
myAvatar->startCapture(); if (_myCamera.getMode() == CAMERA_MODE_FIRST_PERSON || _myCamera.getMode() == CAMERA_MODE_THIRD_PERSON) {
if (_myCamera.getMode() == CAMERA_MODE_FIRST_PERSON || _myCamera.getMode() == CAMERA_MODE_THIRD_PERSON) { Menu::getInstance()->setIsOptionChecked(MenuOption::FirstPerson,
Menu::getInstance()->setIsOptionChecked(MenuOption::FirstPerson, myAvatar->getBoomLength() <= MyAvatar::ZOOM_MIN); myAvatar->getBoomLength() <= MyAvatar::ZOOM_MIN);
Menu::getInstance()->setIsOptionChecked(MenuOption::ThirdPerson, !(myAvatar->getBoomLength() <= MyAvatar::ZOOM_MIN)); Menu::getInstance()->setIsOptionChecked(MenuOption::ThirdPerson,
cameraMenuChanged(); !(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());
} }
} else if (_myCamera.getMode() == CAMERA_MODE_THIRD_PERSON) {
if (isHMDMode()) { // The render mode is default or mirror if the camera is in mirror mode, assigned further below
auto hmdWorldMat = myAvatar->getSensorToWorldMatrix() * myAvatar->getHMDSensorMatrix(); renderArgs._renderMode = RenderArgs::DEFAULT_RENDER_MODE;
_myCamera.setRotation(glm::normalize(glm::quat_cast(hmdWorldMat)));
auto worldBoomOffset = myAvatar->getOrientation() * (myAvatar->getScale() * myAvatar->getBoomLength() * glm::vec3(0.0f, 0.0f, 1.0f)); // Always use the default eye position, not the actual head eye position.
_myCamera.setPosition(extractTranslation(hmdWorldMat) + worldBoomOffset); // Using the latter will cause the camera to wobble with idle animations,
} else { // or with changes from the face tracker
_myCamera.setRotation(myAvatar->getHead()->getOrientation()); if (_myCamera.getMode() == CAMERA_MODE_FIRST_PERSON) {
if (Menu::getInstance()->isOptionChecked(MenuOption::CenterPlayerInView)) { if (isHMDMode()) {
_myCamera.setPosition(myAvatar->getDefaultEyePosition() mat4 camMat = myAvatar->getSensorToWorldMatrix() * myAvatar->getHMDSensorMatrix();
+ _myCamera.getRotation() _myCamera.setPosition(extractTranslation(camMat));
* (myAvatar->getScale() * myAvatar->getBoomLength() * glm::vec3(0.0f, 0.0f, 1.0f))); _myCamera.setRotation(glm::quat_cast(camMat));
} else { } else {
_myCamera.setPosition(myAvatar->getDefaultEyePosition() _myCamera.setPosition(myAvatar->getDefaultEyePosition());
+ myAvatar->getOrientation() _myCamera.setRotation(myAvatar->getHead()->getCameraOrientation());
* (myAvatar->getScale() * myAvatar->getBoomLength() * glm::vec3(0.0f, 0.0f, 1.0f)));
} }
} 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) { // Update camera position
if (isHMDMode()) { if (!isHMDMode()) {
glm::quat hmdRotation = extractRotation(myAvatar->getHMDSensorMatrix()); _myCamera.update(1.0f / _fps);
_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; });
}
// Update camera position
if (!isHMDMode()) {
_myCamera.update(1.0f / _fps);
}
myAvatar->endCapture();
} }
// Primary rendering pass // 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. // 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. // Then we can move this logic into the Avatar::simulate call.
auto myAvatar = getMyAvatar(); auto myAvatar = getMyAvatar();
myAvatar->startRender(); myAvatar->withReadLock([&] {
myAvatar->preRender(renderArgs); myAvatar->preRender(renderArgs);
myAvatar->endRender(); });
activeRenderingThread = QThread::currentThread(); activeRenderingThread = QThread::currentThread();
@ -3502,9 +3504,9 @@ void Application::displaySide(RenderArgs* renderArgs, Camera& theCamera, bool se
_renderEngine->setRenderContext(renderContext); _renderEngine->setRenderContext(renderContext);
// Before the deferred pass, let's try to use the render engine // Before the deferred pass, let's try to use the render engine
myAvatar->startRenderRun(); myAvatar->withReadLock([&] {
_renderEngine->run(); _renderEngine->run();
myAvatar->endRenderRun(); });
auto engineRC = _renderEngine->getRenderContext(); auto engineRC = _renderEngine->getRenderContext();
sceneInterface->setEngineFeedOpaqueItems(engineRC->_numFeedOpaqueItems); sceneInterface->setEngineFeedOpaqueItems(engineRC->_numFeedOpaqueItems);

View file

@ -303,8 +303,6 @@ void Avatar::removeFromScene(AvatarSharedPointer self, std::shared_ptr<render::S
} }
void Avatar::render(RenderArgs* renderArgs, const glm::vec3& cameraPosition) { void Avatar::render(RenderArgs* renderArgs, const glm::vec3& cameraPosition) {
startRender();
auto& batch = *renderArgs->_batch; auto& batch = *renderArgs->_batch;
if (glm::distance(DependencyManager::get<AvatarManager>()->getMyAvatar()->getPosition(), getPosition()) < 10.0f) { if (glm::distance(DependencyManager::get<AvatarManager>()->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) { if (frustum->sphereInFrustum(getPosition(), boundingRadius) == ViewFrustum::OUTSIDE) {
endRender();
return; return;
} }
@ -529,7 +526,6 @@ void Avatar::render(RenderArgs* renderArgs, const glm::vec3& cameraPosition) {
renderDisplayName(batch, frustum, textPosition); renderDisplayName(batch, frustum, textPosition);
} }
} }
endRender();
} }
glm::quat Avatar::computeRotationFromBodyToWorldUp(float proportion) const { glm::quat Avatar::computeRotationFromBodyToWorldUp(float proportion) const {
@ -993,23 +989,25 @@ void Avatar::setBillboard(const QByteArray& billboard) {
} }
int Avatar::parseDataFromBuffer(const QByteArray& buffer) { int Avatar::parseDataFromBuffer(const QByteArray& buffer) {
startUpdate(); int bytesRead;
if (!_initialized) {
// now that we have data for this Avatar we are go for init
init();
}
// change in position implies movement withWriteLock([&] {
glm::vec3 oldPosition = getPosition(); 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; bytesRead = AvatarData::parseDataFromBuffer(buffer);
_moving = glm::distance(oldPosition, getPosition()) > MOVE_DISTANCE_THRESHOLD;
if (_moving && _motionState) { const float MOVE_DISTANCE_THRESHOLD = 0.001f;
_motionState->addDirtyFlags(Simulation::DIRTY_POSITION); _moving = glm::distance(oldPosition, getPosition()) > MOVE_DISTANCE_THRESHOLD;
} if (_moving && _motionState) {
endUpdate(); _motionState->addDirtyFlags(Simulation::DIRTY_POSITION);
}
});
return bytesRead; return bytesRead;
} }
@ -1201,3 +1199,13 @@ glm::quat Avatar::getRightPalmRotation() {
getSkeletonModel().getJointRotationInWorldFrame(getSkeletonModel().getRightHandJointIndex(), rightRotation); getSkeletonModel().getJointRotationInWorldFrame(getSkeletonModel().getRightHandJointIndex(), rightRotation);
return 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();
}

View file

@ -174,6 +174,9 @@ public:
void setMotionState(AvatarMotionState* motionState) { _motionState = motionState; } void setMotionState(AvatarMotionState* motionState) { _motionState = motionState; }
AvatarMotionState* getMotionState() { return _motionState; } AvatarMotionState* getMotionState() { return _motionState; }
virtual void setPosition(const glm::vec3& position);
virtual void setOrientation(const glm::quat& orientation);
public slots: public slots:
glm::vec3 getLeftPalmPosition(); glm::vec3 getLeftPalmPosition();
glm::vec3 getLeftPalmVelocity(); glm::vec3 getLeftPalmVelocity();

View file

@ -129,9 +129,9 @@ void AvatarManager::updateOtherAvatars(float deltaTime) {
_avatarFades.push_back(avatarIterator.value()); _avatarFades.push_back(avatarIterator.value());
avatarIterator = _avatarHash.erase(avatarIterator); avatarIterator = _avatarHash.erase(avatarIterator);
} else { } else {
avatar->startUpdate(); avatar->withWriteLock([&] {
avatar->simulate(deltaTime); avatar->simulate(deltaTime);
avatar->endUpdate(); });
++avatarIterator; ++avatarIterator;
} }
} }
@ -150,16 +150,16 @@ void AvatarManager::simulateAvatarFades(float deltaTime) {
render::PendingChanges pendingChanges; render::PendingChanges pendingChanges;
while (fadingIterator != _avatarFades.end()) { while (fadingIterator != _avatarFades.end()) {
auto avatar = std::static_pointer_cast<Avatar>(*fadingIterator); auto avatar = std::static_pointer_cast<Avatar>(*fadingIterator);
avatar->startUpdate(); avatar->withWriteLock([&] {
avatar->setTargetScale(avatar->getAvatarScale() * SHRINK_RATE, true); avatar->setTargetScale(avatar->getAvatarScale() * SHRINK_RATE, true);
if (avatar->getTargetScale() < MIN_FADE_SCALE) { if (avatar->getTargetScale() < MIN_FADE_SCALE) {
avatar->removeFromScene(*fadingIterator, scene, pendingChanges); avatar->removeFromScene(*fadingIterator, scene, pendingChanges);
fadingIterator = _avatarFades.erase(fadingIterator); fadingIterator = _avatarFades.erase(fadingIterator);
} else { } else {
avatar->simulate(deltaTime); avatar->simulate(deltaTime);
++fadingIterator; ++fadingIterator;
} }
avatar->endUpdate(); });
} }
scene->enqueuePendingChanges(pendingChanges); scene->enqueuePendingChanges(pendingChanges);
} }

View file

@ -56,11 +56,11 @@ bool AvatarUpdate::process() {
//gets current lookat data, removes missing avatars, etc. //gets current lookat data, removes missing avatars, etc.
manager->updateOtherAvatars(deltaSeconds); manager->updateOtherAvatars(deltaSeconds);
myAvatar->startUpdate(); myAvatar->withWriteLock([&] {
qApp->updateMyAvatarLookAtPosition(); qApp->updateMyAvatarLookAtPosition();
// Sample hardware, update view frustum if needed, and send avatar data to mixer/nodes // Sample hardware, update view frustum if needed, and send avatar data to mixer/nodes
manager->updateMyAvatar(deltaSeconds); manager->updateMyAvatar(deltaSeconds);
myAvatar->endUpdate(); });
if (!isThreaded()) { if (!isThreaded()) {
return true; return true;

View file

@ -110,51 +110,25 @@ void AvatarData::setBodyRoll(float bodyRoll) {
setOrientation(glm::quat(glm::radians(eulerAngles))); 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. // There are a number of possible strategies for this set of tools through endRender, below.
void AvatarData::nextAttitude(glm::vec3 position, glm::quat orientation) { void AvatarData::nextAttitude(glm::vec3 position, glm::quat orientation) {
avatarLock.lock(); withWriteLock([&] {
setPosition(position); SpatiallyNestable::setPosition(position);
setOrientation(orientation); SpatiallyNestable::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);
updateAttitude(); updateAttitude();
_nextPosition = pos;
_nextOrientation = rot;
}
void AvatarData::endRender() {
setPosition(_nextPosition);
setOrientation(_nextOrientation);
updateAttitude();
_nextAllowed = true;
} }
float AvatarData::getTargetScale() const { float AvatarData::getTargetScale() const {

View file

@ -44,13 +44,13 @@ typedef unsigned long long quint64;
#include <QVariantMap> #include <QVariantMap>
#include <QVector> #include <QVector>
#include <QtScript/QScriptable> #include <QtScript/QScriptable>
#include <QReadWriteLock>
#include <NLPacket.h> #include <NLPacket.h>
#include <Node.h> #include <Node.h>
#include <RegisteredMetaTypes.h> #include <RegisteredMetaTypes.h>
#include <SimpleMovingAverage.h> #include <SimpleMovingAverage.h>
#include <SpatiallyNestable.h> #include <SpatiallyNestable.h>
#include <shared/ReadWriteLockable.h>
#include "AABox.h" #include "AABox.h"
#include "HandData.h" #include "HandData.h"
@ -59,6 +59,7 @@ typedef unsigned long long quint64;
#include "Player.h" #include "Player.h"
#include "Recorder.h" #include "Recorder.h"
using AvatarSharedPointer = std::shared_ptr<AvatarData>; using AvatarSharedPointer = std::shared_ptr<AvatarData>;
using AvatarWeakPointer = std::weak_ptr<AvatarData>; using AvatarWeakPointer = std::weak_ptr<AvatarData>;
using AvatarHash = QHash<QUuid, AvatarSharedPointer>; using AvatarHash = QHash<QUuid, AvatarSharedPointer>;
@ -135,7 +136,7 @@ class QDataStream;
class AttachmentData; class AttachmentData;
class JointData; class JointData;
class AvatarData : public QObject, public SpatiallyNestable { class AvatarData : public QObject, public ReadWriteLockable, public SpatiallyNestable {
Q_OBJECT Q_OBJECT
Q_PROPERTY(glm::vec3 position READ getPosition WRITE setPosition) Q_PROPERTY(glm::vec3 position READ getPosition WRITE setPosition)
@ -195,15 +196,10 @@ public:
float getBodyRoll() const; float getBodyRoll() const;
void setBodyRoll(float bodyRoll); 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 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 virtual void updateAttitude() {} // Tell skeleton mesh about changes
glm::quat getHeadOrientation() const { return _headData->getOrientation(); } glm::quat getHeadOrientation() const { return _headData->getOrientation(); }
@ -360,10 +356,6 @@ protected:
QUuid _sessionUUID; QUuid _sessionUUID;
glm::vec3 _handPosition; glm::vec3 _handPosition;
glm::vec3 _nextPosition {};
glm::quat _nextOrientation {};
bool _nextAllowed {true};
// Body scale // Body scale
float _targetScale; float _targetScale;
@ -413,8 +405,6 @@ protected:
SimpleMovingAverage _averageBytesReceived; SimpleMovingAverage _averageBytesReceived;
QMutex avatarLock; // Name is redundant, but it aids searches.
private: private:
static QUrl _defaultFullAvatarModelUrl; static QUrl _defaultFullAvatarModelUrl;
// privatize the copy constructor and assignment operator so they cannot be called // privatize the copy constructor and assignment operator so they cannot be called

View file

@ -39,9 +39,14 @@ namespace render {
using namespace render; using namespace render;
MeshPartPayload::MeshPartPayload(Model* model, int meshIndex, int partIndex, int shapeIndex) : MeshPartPayload::MeshPartPayload(Model* model, int meshIndex, int partIndex, int shapeIndex,
model(model), meshIndex(meshIndex), partIndex(partIndex), _shapeID(shapeIndex) glm::vec3 position, glm::quat orientation) :
{ model(model),
meshIndex(meshIndex),
partIndex(partIndex),
_shapeID(shapeIndex),
_modelPosition(position),
_modelOrientation(orientation) {
initCache(); 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 { render::ItemKey MeshPartPayload::getKey() const {
ItemKey::Builder builder; ItemKey::Builder builder;
builder.withTypeShape(); builder.withTypeShape();
@ -91,7 +101,7 @@ render::ItemKey MeshPartPayload::getKey() const {
render::Item::Bound MeshPartPayload::getBound() const { render::Item::Bound MeshPartPayload::getBound() const {
// NOTE: we can't cache this bounds because we need to handle the case of a moving // NOTE: we can't cache this bounds because we need to handle the case of a moving
// entity or mesh part. // entity or mesh part.
return model->getPartBounds(meshIndex, partIndex); return model->getPartBounds(meshIndex, partIndex, _modelPosition, _modelOrientation);
} }
void MeshPartPayload::drawCall(gpu::Batch& batch) const { 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 = Transform(state.clusterMatrices[0]);
} }
} }
transform.preTranslate(model->_translation); transform.preTranslate(_modelPosition);
batch.setModelTransform(transform); batch.setModelTransform(transform);
} }
@ -247,7 +257,7 @@ void MeshPartPayload::render(RenderArgs* args) const {
} }
// Back to model to update the cluster matrices right now // Back to model to update the cluster matrices right now
model->updateClusterMatrices(); model->updateClusterMatrices(_modelPosition, _modelOrientation);
const FBXMesh& mesh = geometry.meshes.at(meshIndex); const FBXMesh& mesh = geometry.meshes.at(meshIndex);

View file

@ -24,7 +24,7 @@ class Model;
class MeshPartPayload { class MeshPartPayload {
public: 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<MeshPartPayload> Payload; typedef render::Payload<MeshPartPayload> Payload;
typedef Payload::DataPointer Pointer; typedef Payload::DataPointer Pointer;
@ -33,7 +33,11 @@ public:
int meshIndex; int meshIndex;
int partIndex; int partIndex;
int _shapeID; int _shapeID;
glm::vec3 _modelPosition;
glm::quat _modelOrientation;
void updateModelLocation(glm::vec3 position, glm::quat orientation);
// Render Item interface // Render Item interface
render::ItemKey getKey() const; render::ItemKey getKey() const;
render::Item::Bound getBound() const; render::Item::Bound getBound() const;
@ -63,4 +67,4 @@ namespace render {
template <> void payloadRender(const MeshPartPayload::Pointer& payload, RenderArgs* args); template <> void payloadRender(const MeshPartPayload::Pointer& payload, RenderArgs* args);
} }
#endif // hifi_MeshPartPayload_h #endif // hifi_MeshPartPayload_h

View file

@ -73,13 +73,16 @@ Model::~Model() {
AbstractViewStateInterface* Model::_viewState = NULL; AbstractViewStateInterface* Model::_viewState = NULL;
void Model::setTranslation(const glm::vec3& translation) { void Model::setTranslation(const glm::vec3& translation) {
_translation = translation; _translation = translation;
enqueueLocationChange();
} }
void Model::setRotation(const glm::quat& rotation) { void Model::setRotation(const glm::quat& rotation) {
_rotation = rotation; _rotation = rotation;
} enqueueLocationChange();
}
void Model::setScale(const glm::vec3& scale) { void Model::setScale(const glm::vec3& scale) {
setScaleInternal(scale); setScaleInternal(scale);
@ -107,6 +110,20 @@ void Model::setOffset(const glm::vec3& offset) {
_snappedToRegistrationPoint = false; _snappedToRegistrationPoint = false;
} }
void Model::enqueueLocationChange() {
render::ScenePointer scene = AbstractViewStateInterface::instance()->getMain3DScene();
render::PendingChanges pendingChanges;
foreach (auto itemID, _renderItems.keys()) {
pendingChanges.updateItem<MeshPartPayload>(itemID, [=](MeshPartPayload& data) {
data.updateModelLocation(_translation, _rotation);
data.model->_needsUpdateClusterMatrices = true;
});
}
scene->enqueuePendingChanges(pendingChanges);
}
QVector<JointState> Model::createJointStates(const FBXGeometry& geometry) { QVector<JointState> Model::createJointStates(const FBXGeometry& geometry) {
QVector<JointState> jointStates; QVector<JointState> jointStates;
for (int i = 0; i < geometry.joints.size(); ++i) { for (int i = 0; i < geometry.joints.size(); ++i) {
@ -378,7 +395,7 @@ void Model::recalculateMeshBoxes(bool pickAgainstTriangles) {
_calculatedMeshPartBoxes.clear(); _calculatedMeshPartBoxes.clear();
for (int i = 0; i < numberOfMeshes; i++) { for (int i = 0; i < numberOfMeshes; i++) {
const FBXMesh& mesh = geometry.meshes.at(i); const FBXMesh& mesh = geometry.meshes.at(i);
Extents scaledMeshExtents = calculateScaledOffsetExtents(mesh.meshExtents); Extents scaledMeshExtents = calculateScaledOffsetExtents(mesh.meshExtents, _translation, _rotation);
_calculatedMeshBoxes[i] = AABox(scaledMeshExtents); _calculatedMeshBoxes[i] = AABox(scaledMeshExtents);
@ -659,7 +676,8 @@ Extents Model::getUnscaledMeshExtents() const {
return scaledExtents; 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 // 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 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)); 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), Extents scaledOffsetExtents = { ((minimum + _offset) * _scale),
((maximum + _offset) * _scale) }; ((maximum + _offset) * _scale) };
Extents rotatedExtents = scaledOffsetExtents.getRotated(_rotation); Extents rotatedExtents = scaledOffsetExtents.getRotated(modelOrientation);
Extents translatedExtents = { rotatedExtents.minimum + _translation, Extents translatedExtents = { rotatedExtents.minimum + modelPosition,
rotatedExtents.maximum + _translation }; rotatedExtents.maximum + modelPosition };
return translatedExtents; return translatedExtents;
} }
/// Returns the world space equivalent of some box in model space. /// Returns the world space equivalent of some box in model space.
AABox Model::calculateScaledOffsetAABox(const AABox& box) const { AABox Model::calculateScaledOffsetAABox(const AABox& box, glm::vec3 modelPosition, glm::quat modelOrientation) const {
return AABox(calculateScaledOffsetExtents(Extents(box))); return AABox(calculateScaledOffsetExtents(Extents(box), modelPosition, modelOrientation));
} }
glm::vec3 Model::calculateScaledOffsetPoint(const glm::vec3& point) const { 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; glm::mat4 parentTransform = glm::scale(_scale) * glm::translate(_offset) * geometry.offset;
updateRig(deltaTime, parentTransform); updateRig(deltaTime, parentTransform);
} }
void Model::updateClusterMatrices() { void Model::updateClusterMatrices(glm::vec3 modelPosition, glm::quat modelOrientation) {
PerformanceTimer perfTimer("Model::updateClusterMatrices"); PerformanceTimer perfTimer("Model::updateClusterMatrices");
if (!_needsUpdateClusterMatrices) { if (!_needsUpdateClusterMatrices) {
@ -985,7 +1003,7 @@ void Model::updateClusterMatrices() {
glm::vec4(0.0f, 0.0f, 0.0f, 1.0f)); glm::vec4(0.0f, 0.0f, 0.0f, 1.0f));
auto cauterizeMatrix = _rig->getJointTransform(geometry.neckJointIndex) * zeroScale; 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++) { for (int i = 0; i < _meshStates.size(); i++) {
MeshState& state = _meshStates[i]; MeshState& state = _meshStates[i];
const FBXMesh& mesh = geometry.meshes.at(i); const FBXMesh& mesh = geometry.meshes.at(i);
@ -1007,16 +1025,21 @@ void Model::updateClusterMatrices() {
// Once computed the cluster matrices, update the buffer(s) // Once computed the cluster matrices, update the buffer(s)
if (mesh.clusters.size() > 1) { if (mesh.clusters.size() > 1) {
if (!state.clusterBuffer) { if (!state.clusterBuffer) {
state.clusterBuffer = std::make_shared<gpu::Buffer>(state.clusterMatrices.size() * sizeof(glm::mat4), (const gpu::Byte*) state.clusterMatrices.constData()); state.clusterBuffer = std::make_shared<gpu::Buffer>(state.clusterMatrices.size() * sizeof(glm::mat4),
(const gpu::Byte*) state.clusterMatrices.constData());
} else { } 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 (!_cauterizeBoneSet.empty() && (state.cauterizedClusterMatrices.size() > 1)) {
if (!state.cauterizedClusterBuffer) { if (!state.cauterizedClusterBuffer) {
state.cauterizedClusterBuffer = std::make_shared<gpu::Buffer>(state.cauterizedClusterMatrices.size() * sizeof(glm::mat4), (const gpu::Byte*) state.cauterizedClusterMatrices.constData()); state.cauterizedClusterBuffer =
std::make_shared<gpu::Buffer>(state.cauterizedClusterMatrices.size() * sizeof(glm::mat4),
(const gpu::Byte*) state.cauterizedClusterMatrices.constData());
} else { } 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(); _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()) { if (!_geometry || !_geometry->isLoaded()) {
return AABox(); return AABox();
@ -1120,7 +1143,7 @@ AABox Model::getPartBounds(int meshIndex, int partIndex) {
bool isSkinned = state.clusterMatrices.size() > 1; bool isSkinned = state.clusterMatrices.size() > 1;
if (isSkinned) { if (isSkinned) {
// if we're skinned return the entire mesh extents because we can't know for sure our clusters don't move us // 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) { 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 // If we not skinned use the bounds of the subMesh for all it's parts
const FBXMesh& mesh = _geometry->getFBXGeometry().meshes.at(meshIndex); const FBXMesh& mesh = _geometry->getFBXGeometry().meshes.at(meshIndex);
return calculateScaledOffsetExtents(mesh.meshExtents); return calculateScaledOffsetExtents(mesh.meshExtents, modelPosition, modelOrientation);
} }
return AABox(); return AABox();
} }
@ -1164,7 +1187,7 @@ void Model::segregateMeshGroups() {
// Create the render payloads // Create the render payloads
int totalParts = mesh.parts.size(); int totalParts = mesh.parts.size();
for (int partIndex = 0; partIndex < totalParts; partIndex++) { for (int partIndex = 0; partIndex < totalParts; partIndex++) {
_renderItemsSet << std::make_shared<MeshPartPayload>(this, i, partIndex, shapeID); _renderItemsSet << std::make_shared<MeshPartPayload>(this, i, partIndex, shapeID, _translation, _rotation);
shapeID++; shapeID++;
} }
} }
@ -1184,6 +1207,7 @@ bool Model::initWhenReady(render::ScenePointer scene) {
_renderItems.insert(item, renderPayload); _renderItems.insert(item, renderPayload);
pendingChanges.resetItem(item, renderPayload); pendingChanges.resetItem(item, renderPayload);
pendingChanges.updateItem<MeshPartPayload>(item, [&](MeshPartPayload& data) { pendingChanges.updateItem<MeshPartPayload>(item, [&](MeshPartPayload& data) {
data.updateModelLocation(_translation, _rotation);
data.model->_needsUpdateClusterMatrices = true; data.model->_needsUpdateClusterMatrices = true;
}); });
} }

View file

@ -86,7 +86,8 @@ public:
bool isVisible() const { return _isVisible; } 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(); bool maybeStartBlender();
@ -109,7 +110,7 @@ public:
bool getSnapModelToRegistrationPoint() { return _snapModelToRegistrationPoint; } bool getSnapModelToRegistrationPoint() { return _snapModelToRegistrationPoint; }
virtual void simulate(float deltaTime, bool fullUpdate = true); 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. /// Returns a reference to the shared geometry.
const QSharedPointer<NetworkGeometry>& getGeometry() const { return _geometry; } const QSharedPointer<NetworkGeometry>& getGeometry() const { return _geometry; }
@ -183,6 +184,8 @@ public:
void setScale(const glm::vec3& scale); void setScale(const glm::vec3& scale);
const glm::vec3& getScale() const { return _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 /// 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 bool getIsScaledToFit() const { return _scaledToFit; } /// is model scaled to fit
const glm::vec3& getScaleToFitDimensions() const { return _scaleToFitDimensions; } /// the dimensions model is scaled to const glm::vec3& getScaleToFitDimensions() const { return _scaleToFitDimensions; } /// the dimensions model is scaled to
@ -210,10 +213,10 @@ protected:
Extents getUnscaledMeshExtents() const; Extents getUnscaledMeshExtents() const;
/// Returns the scaled equivalent of some extents in model space. /// 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. /// 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. /// Returns the scaled equivalent of a point in model space.
glm::vec3 calculateScaledOffsetPoint(const glm::vec3& point) const; glm::vec3 calculateScaledOffsetPoint(const glm::vec3& point) const;