mirror of
https://github.com/overte-org/overte.git
synced 2025-04-21 09:24:00 +02:00
Merge branch 'master' of https://github.com/highfidelity/hifi into animGraph-cleanup
This commit is contained in:
commit
bcf16e52af
26 changed files with 322 additions and 41 deletions
|
@ -31,6 +31,7 @@
|
|||
// if we're currently being grabbed and if the person grabbing us is the current interfaces avatar.
|
||||
// we will watch this for state changes and print out if we're being grabbed or released when it changes.
|
||||
update: function() {
|
||||
//print("BreakdanceEntity.update() _this.entityID:" + _this.entityID);
|
||||
var GRAB_USER_DATA_KEY = "grabKey";
|
||||
|
||||
// because the update() signal doesn't have a valid this, we need to use our memorized _this to access our entityID
|
||||
|
@ -45,14 +46,15 @@
|
|||
|
||||
// if the grabData says we're being grabbed, and the owner ID is our session, then we are being grabbed by this interface
|
||||
if (grabData.activated && grabData.avatarId == MyAvatar.sessionUUID) {
|
||||
|
||||
//print("BreakdanceEntity.update() [I'm being grabbed] _this.entityID:" + _this.entityID);
|
||||
if (!_this.beingGrabbed) {
|
||||
print("I'm was grabbed... _this.entityID:" + _this.entityID);
|
||||
|
||||
// remember we're being grabbed so we can detect being released
|
||||
_this.beingGrabbed = true;
|
||||
var props = Entities.getEntityProperties(entityID);
|
||||
var puppetPosition = getPuppetPosition(props);
|
||||
breakdanceStart(props.modelURL, puppetPosition);
|
||||
print("I'm was grabbed...");
|
||||
} else {
|
||||
breakdanceUpdate();
|
||||
}
|
||||
|
@ -62,7 +64,7 @@
|
|||
// if we are not being grabbed, and we previously were, then we were just released, remember that
|
||||
// and print out a message
|
||||
_this.beingGrabbed = false;
|
||||
print("I'm was released...");
|
||||
print("I'm was released... _this.entityID:" + _this.entityID);
|
||||
breakdanceEnd();
|
||||
}
|
||||
},
|
||||
|
@ -73,6 +75,7 @@
|
|||
// * connecting to the update signal so we can check our grabbed state
|
||||
preload: function(entityID) {
|
||||
this.entityID = entityID;
|
||||
print("BreakdanceEntity.preload() this.entityID:" + this.entityID);
|
||||
Script.update.connect(this.update);
|
||||
},
|
||||
|
||||
|
@ -80,6 +83,7 @@
|
|||
// or because we've left the domain or quit the application. In all cases we want to unhook our connection
|
||||
// to the update signal
|
||||
unload: function(entityID) {
|
||||
print("BreakdanceEntity.unload() this.entityID:" + this.entityID);
|
||||
Script.update.disconnect(this.update);
|
||||
},
|
||||
|
||||
|
|
|
@ -52,6 +52,11 @@ Item {
|
|||
font.pixelSize: root.fontSize
|
||||
text: "Simrate: " + root.simrate
|
||||
}
|
||||
Text {
|
||||
color: root.fontColor;
|
||||
font.pixelSize: root.fontSize
|
||||
text: "Avatar Simrate: " + root.avatarSimrate
|
||||
}
|
||||
Text {
|
||||
color: root.fontColor;
|
||||
font.pixelSize: root.fontSize
|
||||
|
|
|
@ -802,6 +802,7 @@ void Application::cleanupBeforeQuit() {
|
|||
|
||||
// first stop all timers directly or by invokeMethod
|
||||
// depending on what thread they run in
|
||||
_avatarUpdate->terminate();
|
||||
locationUpdateTimer->stop();
|
||||
balanceUpdateTimer->stop();
|
||||
identityPacketTimer->stop();
|
||||
|
@ -1068,7 +1069,7 @@ void Application::paintGL() {
|
|||
|
||||
// Before anything else, let's sync up the gpuContext with the true glcontext used in case anything happened
|
||||
renderArgs._context->syncCache();
|
||||
|
||||
|
||||
if (Menu::getInstance()->isOptionChecked(MenuOption::Mirror)) {
|
||||
auto primaryFbo = DependencyManager::get<FramebufferCache>()->getPrimaryFramebufferDepthColor();
|
||||
|
||||
|
@ -1101,6 +1102,7 @@ void Application::paintGL() {
|
|||
_applicationOverlay.renderOverlay(&renderArgs);
|
||||
}
|
||||
|
||||
_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));
|
||||
|
@ -1150,7 +1152,7 @@ void Application::paintGL() {
|
|||
if (!isHMDMode()) {
|
||||
_myCamera.update(1.0f / _fps);
|
||||
}
|
||||
|
||||
_myAvatar->endCapture();
|
||||
|
||||
// Primary rendering pass
|
||||
auto framebufferCache = DependencyManager::get<FramebufferCache>();
|
||||
|
@ -2172,6 +2174,18 @@ float Application::getAverageSimsPerSecond() {
|
|||
}
|
||||
return _simsPerSecondReport;
|
||||
}
|
||||
void Application::setAvatarSimrateSample(float sample) {
|
||||
_avatarSimsPerSecond.updateAverage(sample);
|
||||
}
|
||||
float Application::getAvatarSimrate() {
|
||||
uint64_t now = usecTimestampNow();
|
||||
|
||||
if (now - _lastAvatarSimsPerSecondUpdate > USECS_PER_SECOND) {
|
||||
_avatarSimsPerSecondReport = _avatarSimsPerSecond.getAverage();
|
||||
_lastAvatarSimsPerSecondUpdate = now;
|
||||
}
|
||||
return _avatarSimsPerSecondReport;
|
||||
}
|
||||
|
||||
void Application::setLowVelocityFilter(bool lowVelocityFilter) {
|
||||
InputDevice::setLowVelocityFilter(lowVelocityFilter);
|
||||
|
@ -2457,8 +2471,20 @@ void Application::init() {
|
|||
// Make sure any new sounds are loaded as soon as know about them.
|
||||
connect(tree, &EntityTree::newCollisionSoundURL, DependencyManager::get<SoundCache>().data(), &SoundCache::getSound);
|
||||
connect(_myAvatar, &MyAvatar::newCollisionSoundURL, DependencyManager::get<SoundCache>().data(), &SoundCache::getSound);
|
||||
|
||||
setAvatarUpdateThreading(Menu::getInstance()->isOptionChecked(MenuOption::EnableAvatarUpdateThreading));
|
||||
}
|
||||
|
||||
void Application::setAvatarUpdateThreading(bool isThreaded) {
|
||||
if (_avatarUpdate) {
|
||||
getMyAvatar()->destroyAnimGraph();
|
||||
_avatarUpdate->terminate();
|
||||
}
|
||||
_avatarUpdate = new AvatarUpdate();
|
||||
_avatarUpdate->initialize(isThreaded);
|
||||
}
|
||||
|
||||
|
||||
void Application::closeMirrorView() {
|
||||
if (Menu::getInstance()->isOptionChecked(MenuOption::Mirror)) {
|
||||
Menu::getInstance()->triggerOption(MenuOption::Mirror);
|
||||
|
@ -2535,18 +2561,19 @@ void Application::updateMyAvatarLookAtPosition() {
|
|||
auto eyeTracker = DependencyManager::get<EyeTracker>();
|
||||
|
||||
bool isLookingAtSomeone = false;
|
||||
bool isHMD = _avatarUpdate->isHMDMode();
|
||||
glm::vec3 lookAtSpot;
|
||||
if (_myCamera.getMode() == CAMERA_MODE_MIRROR) {
|
||||
// When I am in mirror mode, just look right at the camera (myself); don't switch gaze points because when physically
|
||||
// looking in a mirror one's eyes appear steady.
|
||||
if (!isHMDMode()) {
|
||||
if (!isHMD) {
|
||||
lookAtSpot = _myCamera.getPosition();
|
||||
} else {
|
||||
lookAtSpot = _myCamera.getPosition() + transformPoint(_myAvatar->getSensorToWorldMatrix(), extractTranslation(getHMDSensorPose()));
|
||||
}
|
||||
} else if (eyeTracker->isTracking() && (isHMDMode() || eyeTracker->isSimulating())) {
|
||||
} else if (eyeTracker->isTracking() && (isHMD || eyeTracker->isSimulating())) {
|
||||
// Look at the point that the user is looking at.
|
||||
if (isHMDMode()) {
|
||||
if (isHMD) {
|
||||
glm::mat4 headPose = getActiveDisplayPlugin()->getHeadPose();
|
||||
glm::quat hmdRotation = glm::quat_cast(headPose);
|
||||
lookAtSpot = _myCamera.getPosition() +
|
||||
|
@ -2589,8 +2616,8 @@ void Application::updateMyAvatarLookAtPosition() {
|
|||
}
|
||||
} else {
|
||||
// I am not looking at anyone else, so just look forward
|
||||
if (isHMDMode()) {
|
||||
glm::mat4 headPose = getActiveDisplayPlugin()->getHeadPose();
|
||||
if (isHMD) {
|
||||
glm::mat4 headPose = _avatarUpdate->getHeadPose() ;
|
||||
glm::quat headRotation = glm::quat_cast(headPose);
|
||||
lookAtSpot = _myCamera.getPosition() +
|
||||
_myAvatar->getOrientation() * (headRotation * glm::vec3(0.0f, 0.0f, -TREE_SCALE));
|
||||
|
@ -2817,9 +2844,6 @@ void Application::update(float deltaTime) {
|
|||
|
||||
updateThreads(deltaTime); // If running non-threaded, then give the threads some time to process...
|
||||
|
||||
//loop through all the other avatars and simulate them...
|
||||
DependencyManager::get<AvatarManager>()->updateOtherAvatars(deltaTime);
|
||||
|
||||
updateCamera(deltaTime); // handle various camera tweaks like off axis projection
|
||||
updateDialogs(deltaTime); // update various stats dialogs if present
|
||||
updateCursor(deltaTime); // Handle cursor updates
|
||||
|
@ -2891,12 +2915,7 @@ void Application::update(float deltaTime) {
|
|||
_overlays.update(deltaTime);
|
||||
}
|
||||
|
||||
{
|
||||
PerformanceTimer perfTimer("myAvatar");
|
||||
updateMyAvatarLookAtPosition();
|
||||
// Sample hardware, update view frustum if needed, and send avatar data to mixer/nodes
|
||||
DependencyManager::get<AvatarManager>()->updateMyAvatar(deltaTime);
|
||||
}
|
||||
_avatarUpdate->synchronousProcess();
|
||||
|
||||
{
|
||||
PerformanceTimer perfTimer("emitSimulating");
|
||||
|
@ -3477,7 +3496,10 @@ 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.
|
||||
_myAvatar->startRender();
|
||||
_myAvatar->preRender(renderArgs);
|
||||
_myAvatar->endRender();
|
||||
|
||||
|
||||
activeRenderingThread = QThread::currentThread();
|
||||
PROFILE_RANGE(__FUNCTION__);
|
||||
|
@ -3591,7 +3613,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();
|
||||
|
||||
auto engineRC = _renderEngine->getRenderContext();
|
||||
sceneInterface->setEngineFeedOpaqueItems(engineRC->_numFeedOpaqueItems);
|
||||
|
|
|
@ -48,6 +48,7 @@
|
|||
#include "Menu.h"
|
||||
#include "Physics.h"
|
||||
#include "Stars.h"
|
||||
#include "avatar/AvatarUpdate.h"
|
||||
#include "avatar/Avatar.h"
|
||||
#include "avatar/MyAvatar.h"
|
||||
#include "scripting/ControllerScriptingInterface.h"
|
||||
|
@ -335,6 +336,12 @@ public:
|
|||
|
||||
const QRect& getMirrorViewRect() const { return _mirrorViewRect; }
|
||||
|
||||
void updateMyAvatarLookAtPosition();
|
||||
AvatarUpdate* getAvatarUpdater() { return _avatarUpdate; }
|
||||
MyAvatar* getMyAvatar() { return _myAvatar; }
|
||||
float getAvatarSimrate();
|
||||
void setAvatarSimrateSample(float sample);
|
||||
|
||||
float getAverageSimsPerSecond();
|
||||
|
||||
signals:
|
||||
|
@ -405,6 +412,7 @@ public slots:
|
|||
void openUrl(const QUrl& url);
|
||||
|
||||
void updateMyAvatarTransform();
|
||||
void setAvatarUpdateThreading(bool isThreaded);
|
||||
|
||||
void domainSettingsReceived(const QJsonObject& domainSettingsObject);
|
||||
|
||||
|
@ -482,7 +490,6 @@ private:
|
|||
// Various helper functions called during update()
|
||||
void updateLOD();
|
||||
void updateMouseRay();
|
||||
void updateMyAvatarLookAtPosition();
|
||||
void updateThreads(float deltaTime);
|
||||
void updateCamera(float deltaTime);
|
||||
void updateDialogs(float deltaTime);
|
||||
|
@ -551,6 +558,10 @@ private:
|
|||
|
||||
KeyboardMouseDevice* _keyboardMouseDevice{ nullptr }; // Default input device, the good old keyboard mouse and maybe touchpad
|
||||
MyAvatar* _myAvatar; // TODO: move this and relevant code to AvatarManager (or MyAvatar as the case may be)
|
||||
AvatarUpdate* _avatarUpdate {nullptr};
|
||||
SimpleMovingAverage _avatarSimsPerSecond {10};
|
||||
int _avatarSimsPerSecondReport {0};
|
||||
quint64 _lastAvatarSimsPerSecondUpdate {0};
|
||||
Camera _myCamera; // My view onto the world
|
||||
Camera _mirrorCamera; // Cammera for mirror view
|
||||
QRect _mirrorViewRect;
|
||||
|
|
|
@ -447,6 +447,8 @@ Menu::Menu() {
|
|||
addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::RenderFocusIndicator, 0, false);
|
||||
addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::ShowWhosLookingAtMe, 0, false);
|
||||
addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::FixGaze, 0, false);
|
||||
addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::EnableAvatarUpdateThreading, 0, false,
|
||||
qApp, SLOT(setAvatarUpdateThreading(bool)));
|
||||
addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::EnableRigAnimations, 0, true,
|
||||
avatar, SLOT(setEnableRigAnimations(bool)));
|
||||
addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::EnableAnimGraph, 0, false,
|
||||
|
|
|
@ -188,6 +188,7 @@ namespace MenuOption {
|
|||
const QString EchoServerAudio = "Echo Server Audio";
|
||||
const QString EditEntitiesHelp = "Edit Entities Help...";
|
||||
const QString Enable3DTVMode = "Enable 3DTV Mode";
|
||||
const QString EnableAvatarUpdateThreading = "Enable Avatar Update Threading";
|
||||
const QString EnableAnimGraph = "Enable Anim Graph";
|
||||
const QString EnableCharacterController = "Enable avatar collisions";
|
||||
const QString EnableRigAnimations = "Enable Rig Animations";
|
||||
|
|
|
@ -316,6 +316,7 @@ void Avatar::removeFromScene(AvatarSharedPointer self, std::shared_ptr<render::S
|
|||
}
|
||||
|
||||
void Avatar::render(RenderArgs* renderArgs, const glm::vec3& cameraPosition) {
|
||||
startRender();
|
||||
if (_referential) {
|
||||
_referential->update();
|
||||
}
|
||||
|
@ -390,6 +391,7 @@ void Avatar::render(RenderArgs* renderArgs, const glm::vec3& cameraPosition) {
|
|||
}
|
||||
|
||||
if (frustum->sphereInFrustum(getPosition(), boundingRadius) == ViewFrustum::OUTSIDE) {
|
||||
endRender();
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -540,6 +542,7 @@ void Avatar::render(RenderArgs* renderArgs, const glm::vec3& cameraPosition) {
|
|||
if (!isMyAvatar() || cameraMode != CAMERA_MODE_FIRST_PERSON) {
|
||||
renderDisplayName(batch, *renderArgs->_viewFrustum, renderArgs->_viewport);
|
||||
}
|
||||
endRender();
|
||||
}
|
||||
|
||||
glm::quat Avatar::computeRotationFromBodyToWorldUp(float proportion) const {
|
||||
|
@ -1019,6 +1022,7 @@ 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();
|
||||
|
@ -1034,6 +1038,7 @@ int Avatar::parseDataFromBuffer(const QByteArray& buffer) {
|
|||
if (_moving && _motionState) {
|
||||
_motionState->addDirtyFlags(EntityItem::DIRTY_POSITION);
|
||||
}
|
||||
endUpdate();
|
||||
|
||||
return bytesRead;
|
||||
}
|
||||
|
|
|
@ -156,6 +156,7 @@ public:
|
|||
void scaleVectorRelativeToPosition(glm::vec3 &positionToScale) const;
|
||||
|
||||
void slamPosition(const glm::vec3& position);
|
||||
virtual void updateAttitude() { _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
|
||||
|
|
|
@ -129,7 +129,9 @@ void AvatarManager::updateOtherAvatars(float deltaTime) {
|
|||
_avatarFades.push_back(avatarIterator.value());
|
||||
avatarIterator = _avatarHash.erase(avatarIterator);
|
||||
} else {
|
||||
avatar->startUpdate();
|
||||
avatar->simulate(deltaTime);
|
||||
avatar->endUpdate();
|
||||
++avatarIterator;
|
||||
}
|
||||
}
|
||||
|
@ -148,6 +150,7 @@ void AvatarManager::simulateAvatarFades(float deltaTime) {
|
|||
render::PendingChanges pendingChanges;
|
||||
while (fadingIterator != _avatarFades.end()) {
|
||||
auto avatar = std::static_pointer_cast<Avatar>(*fadingIterator);
|
||||
avatar->startUpdate();
|
||||
avatar->setTargetScale(avatar->getScale() * SHRINK_RATE, true);
|
||||
if (avatar->getTargetScale() < MIN_FADE_SCALE) {
|
||||
avatar->removeFromScene(*fadingIterator, scene, pendingChanges);
|
||||
|
@ -156,6 +159,7 @@ void AvatarManager::simulateAvatarFades(float deltaTime) {
|
|||
avatar->simulate(deltaTime);
|
||||
++fadingIterator;
|
||||
}
|
||||
avatar->endUpdate();
|
||||
}
|
||||
scene->enqueuePendingChanges(pendingChanges);
|
||||
}
|
||||
|
|
75
interface/src/avatar/AvatarUpdate.cpp
Normal file
75
interface/src/avatar/AvatarUpdate.cpp
Normal file
|
@ -0,0 +1,75 @@
|
|||
//
|
||||
// AvatarUpdate.cpp
|
||||
// interface/src/avatar
|
||||
//
|
||||
// Created by Howard Stearns on 8/18/15.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
//
|
||||
|
||||
#include <DependencyManager.h>
|
||||
#include "Application.h"
|
||||
#include "AvatarManager.h"
|
||||
#include "AvatarUpdate.h"
|
||||
#include <display-plugins/DisplayPlugin.h>
|
||||
#include "InterfaceLogging.h"
|
||||
|
||||
AvatarUpdate::AvatarUpdate() : GenericThread(), _lastAvatarUpdate(0) {
|
||||
setObjectName("Avatar Update"); // GenericThread::initialize uses this to set the thread name.
|
||||
Settings settings;
|
||||
const int DEFAULT_TARGET_AVATAR_SIMRATE = 60;
|
||||
_targetInterval = USECS_PER_SECOND / settings.value("AvatarUpdateTargetSimrate", DEFAULT_TARGET_AVATAR_SIMRATE).toInt();
|
||||
}
|
||||
// We could have the constructor call initialize(), but GenericThread::initialize can take parameters.
|
||||
// Keeping it separately called by the client allows that client to pass those without our
|
||||
// constructor needing to know about them.
|
||||
|
||||
void AvatarUpdate::synchronousProcess() {
|
||||
|
||||
// Keep our own updated value, so that our asynchronous code can consult it.
|
||||
_isHMDMode = Application::getInstance()->isHMDMode();
|
||||
_headPose = Application::getInstance()->getActiveDisplayPlugin()->getHeadPose();
|
||||
|
||||
if (_updateBillboard) {
|
||||
Application::getInstance()->getMyAvatar()->doUpdateBillboard();
|
||||
}
|
||||
|
||||
if (!isThreaded()) {
|
||||
process();
|
||||
}
|
||||
}
|
||||
|
||||
bool AvatarUpdate::process() {
|
||||
PerformanceTimer perfTimer("AvatarUpdate");
|
||||
quint64 start = usecTimestampNow();
|
||||
quint64 deltaMicroseconds = start - _lastAvatarUpdate;
|
||||
_lastAvatarUpdate = start;
|
||||
float deltaSeconds = (float) deltaMicroseconds / (float) USECS_PER_SECOND;
|
||||
Application::getInstance()->setAvatarSimrateSample(1.0f / deltaSeconds);
|
||||
|
||||
QSharedPointer<AvatarManager> manager = DependencyManager::get<AvatarManager>();
|
||||
MyAvatar* myAvatar = manager->getMyAvatar();
|
||||
|
||||
//loop through all the other avatars and simulate them...
|
||||
//gets current lookat data, removes missing avatars, etc.
|
||||
manager->updateOtherAvatars(deltaSeconds);
|
||||
|
||||
myAvatar->startUpdate();
|
||||
Application::getInstance()->updateMyAvatarLookAtPosition();
|
||||
// Sample hardware, update view frustum if needed, and send avatar data to mixer/nodes
|
||||
manager->updateMyAvatar(deltaSeconds);
|
||||
myAvatar->endUpdate();
|
||||
|
||||
if (!isThreaded()) {
|
||||
return true;
|
||||
}
|
||||
int elapsed = (usecTimestampNow() - start);
|
||||
int usecToSleep = _targetInterval - elapsed;
|
||||
if (usecToSleep < 0) {
|
||||
usecToSleep = 1; // always yield
|
||||
}
|
||||
usleep(usecToSleep);
|
||||
return true;
|
||||
}
|
43
interface/src/avatar/AvatarUpdate.h
Normal file
43
interface/src/avatar/AvatarUpdate.h
Normal file
|
@ -0,0 +1,43 @@
|
|||
//
|
||||
// AvatarUpdate.h
|
||||
// interface/src/avatar
|
||||
//
|
||||
// Created by Howard Stearns on 8/18/15.
|
||||
///
|
||||
// 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__AvatarUpdate__
|
||||
#define __hifi__AvatarUpdate__
|
||||
|
||||
#include <QtCore/QObject>
|
||||
#include <QTimer>
|
||||
|
||||
// Home for the avatarUpdate operations (e.g., whether on a separate thread, pipelined in various ways, etc.)
|
||||
// This might get folded into AvatarManager.
|
||||
class AvatarUpdate : public GenericThread {
|
||||
Q_OBJECT
|
||||
public:
|
||||
AvatarUpdate();
|
||||
void synchronousProcess();
|
||||
void setRequestBillboardUpdate(bool needsUpdate) { _updateBillboard = needsUpdate; }
|
||||
|
||||
private:
|
||||
virtual bool process(); // No reason for other classes to invoke this.
|
||||
quint64 _lastAvatarUpdate; // microsoeconds
|
||||
quint64 _targetInterval; // microseconds
|
||||
bool _updateBillboard;
|
||||
|
||||
// Goes away if Application::getActiveDisplayPlugin() and friends are made thread safe:
|
||||
public:
|
||||
bool isHMDMode() { return _isHMDMode; }
|
||||
glm::mat4 getHeadPose() { return _headPose; }
|
||||
private:
|
||||
bool _isHMDMode;
|
||||
glm::mat4 _headPose;
|
||||
};
|
||||
|
||||
|
||||
#endif /* defined(__hifi__AvatarUpdate__) */
|
|
@ -389,7 +389,7 @@ glm::quat Head::getCameraOrientation() const {
|
|||
// to change the driving direction while in Oculus mode. It is used to support driving toward where you're
|
||||
// head is looking. Note that in oculus mode, your actual camera view and where your head is looking is not
|
||||
// always the same.
|
||||
if (qApp->isHMDMode()) {
|
||||
if (qApp->getAvatarUpdater()->isHMDMode()) {
|
||||
MyAvatar* myAvatar = dynamic_cast<MyAvatar*>(_owningAvatar);
|
||||
if (myAvatar && myAvatar->getStandingHMDSensorMode()) {
|
||||
return glm::quat_cast(myAvatar->getSensorToWorldMatrix()) * myAvatar->getHMDSensorOrientation();
|
||||
|
|
|
@ -266,8 +266,7 @@ void MyAvatar::updateFromHMDSensorMatrix(const glm::mat4& hmdSensorMatrix) {
|
|||
if (getStandingHMDSensorMode()) {
|
||||
// set the body position/orientation to reflect motion due to the head.
|
||||
auto worldMat = _sensorToWorldMatrix * _bodySensorMatrix;
|
||||
setPosition(extractTranslation(worldMat));
|
||||
setOrientation(glm::quat_cast(worldMat));
|
||||
nextAttitude(extractTranslation(worldMat), glm::quat_cast(worldMat));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -285,7 +284,7 @@ void MyAvatar::updateSensorToWorldMatrix() {
|
|||
void MyAvatar::updateFromTrackers(float deltaTime) {
|
||||
glm::vec3 estimatedPosition, estimatedRotation;
|
||||
|
||||
bool inHmd = qApp->isHMDMode();
|
||||
bool inHmd = qApp->getAvatarUpdater()->isHMDMode();
|
||||
|
||||
if (isPlaying() && inHmd) {
|
||||
return;
|
||||
|
@ -705,19 +704,46 @@ float loadSetting(QSettings& settings, const char* name, float defaultValue) {
|
|||
return value;
|
||||
}
|
||||
|
||||
// Resource loading is not yet thread safe. If an animation is not loaded when requested by other than tha main thread,
|
||||
// we block in AnimationHandle::setURL => AnimationCache::getAnimation.
|
||||
// Meanwhile, the main thread will also eventually lock as it tries to render us.
|
||||
// If we demand the animation from the update thread while we're locked, we'll deadlock.
|
||||
// Until we untangle this, code puts the updates back on the main thread temporarilly and starts all the loading.
|
||||
void MyAvatar::safelyLoadAnimations() {
|
||||
qApp->setAvatarUpdateThreading(false);
|
||||
_rig->addAnimationByRole("idle");
|
||||
_rig->addAnimationByRole("walk");
|
||||
_rig->addAnimationByRole("backup");
|
||||
_rig->addAnimationByRole("leftTurn");
|
||||
_rig->addAnimationByRole("rightTurn");
|
||||
_rig->addAnimationByRole("leftStrafe");
|
||||
_rig->addAnimationByRole("rightStrafe");
|
||||
}
|
||||
|
||||
void MyAvatar::setEnableRigAnimations(bool isEnabled) {
|
||||
if (isEnabled) {
|
||||
safelyLoadAnimations();
|
||||
}
|
||||
_rig->setEnableRig(isEnabled);
|
||||
if (!isEnabled) {
|
||||
_rig->deleteAnimations();
|
||||
} else if (Menu::getInstance()->isOptionChecked(MenuOption::EnableAvatarUpdateThreading)) {
|
||||
qApp->setAvatarUpdateThreading(true);
|
||||
}
|
||||
}
|
||||
|
||||
void MyAvatar::setEnableAnimGraph(bool isEnabled) {
|
||||
if (isEnabled) {
|
||||
safelyLoadAnimations();
|
||||
}
|
||||
_rig->setEnableAnimGraph(isEnabled);
|
||||
if (isEnabled) {
|
||||
if (_skeletonModel.readyToAddToScene()) {
|
||||
initAnimGraph();
|
||||
}
|
||||
if (Menu::getInstance()->isOptionChecked(MenuOption::EnableAvatarUpdateThreading)) {
|
||||
qApp->setAvatarUpdateThreading(true);
|
||||
}
|
||||
} else {
|
||||
destroyAnimGraph();
|
||||
}
|
||||
|
@ -1341,7 +1367,7 @@ void MyAvatar::updateOrientation(float deltaTime) {
|
|||
setOrientation(getOrientation() *
|
||||
glm::quat(glm::radians(glm::vec3(0.0f, _bodyYawDelta * deltaTime, 0.0f))));
|
||||
|
||||
if (qApp->isHMDMode()) {
|
||||
if (qApp->getAvatarUpdater()->isHMDMode()) {
|
||||
glm::quat orientation = glm::quat_cast(getSensorToWorldMatrix()) * getHMDSensorOrientation();
|
||||
glm::quat bodyOrientation = getWorldBodyOrientation();
|
||||
glm::quat localOrientation = glm::inverse(bodyOrientation) * orientation;
|
||||
|
@ -1562,6 +1588,7 @@ bool findAvatarAvatarPenetration(const glm::vec3 positionA, float radiusA, float
|
|||
}
|
||||
|
||||
void MyAvatar::maybeUpdateBillboard() {
|
||||
qApp->getAvatarUpdater()->setRequestBillboardUpdate(false);
|
||||
if (_billboardValid || !(_skeletonModel.isLoadedWithTextures() && getHead()->getFaceModel().isLoadedWithTextures())) {
|
||||
return;
|
||||
}
|
||||
|
@ -1570,7 +1597,9 @@ void MyAvatar::maybeUpdateBillboard() {
|
|||
return;
|
||||
}
|
||||
}
|
||||
|
||||
qApp->getAvatarUpdater()->setRequestBillboardUpdate(true);
|
||||
}
|
||||
void MyAvatar::doUpdateBillboard() {
|
||||
RenderArgs renderArgs(qApp->getGPUContext());
|
||||
QImage image = qApp->renderAvatarBillboard(&renderArgs);
|
||||
_billboard.clear();
|
||||
|
|
|
@ -150,6 +150,8 @@ public:
|
|||
static const float ZOOM_DEFAULT;
|
||||
|
||||
bool getStandingHMDSensorMode() const { return _standingHMDSensorMode; }
|
||||
void doUpdateBillboard();
|
||||
void destroyAnimGraph();
|
||||
|
||||
public slots:
|
||||
void increaseSize();
|
||||
|
@ -289,7 +291,7 @@ private:
|
|||
void maybeUpdateBillboard();
|
||||
void initHeadBones();
|
||||
void initAnimGraph();
|
||||
void destroyAnimGraph();
|
||||
void safelyLoadAnimations();
|
||||
|
||||
// Avatar Preferences
|
||||
QUrl _fullAvatarURLFromPreferences;
|
||||
|
|
|
@ -111,7 +111,7 @@ void SkeletonModel::updateRig(float deltaTime, glm::mat4 parentTransform) {
|
|||
Rig::HeadParameters params;
|
||||
params.modelRotation = getRotation();
|
||||
params.modelTranslation = getTranslation();
|
||||
params.enableLean = qApp->isHMDMode() && !myAvatar->getStandingHMDSensorMode();
|
||||
params.enableLean = qApp->getAvatarUpdater()->isHMDMode() && !myAvatar->getStandingHMDSensorMode();
|
||||
params.leanSideways = head->getFinalLeanSideways();
|
||||
params.leanForward = head->getFinalLeanForward();
|
||||
params.torsoTwist = head->getTorsoTwist();
|
||||
|
@ -148,13 +148,17 @@ void SkeletonModel::updateRig(float deltaTime, glm::mat4 parentTransform) {
|
|||
}
|
||||
}
|
||||
|
||||
// Called by Avatar::simulate after it has set the joint states (fullUpdate true if changed),
|
||||
// but just before head has been simulated.
|
||||
void SkeletonModel::simulate(float deltaTime, bool fullUpdate) {
|
||||
void SkeletonModel::updateAttitude() {
|
||||
setTranslation(_owningAvatar->getSkeletonPosition());
|
||||
static const glm::quat refOrientation = glm::angleAxis(PI, glm::vec3(0.0f, 1.0f, 0.0f));
|
||||
setRotation(_owningAvatar->getOrientation() * refOrientation);
|
||||
setScale(glm::vec3(1.0f, 1.0f, 1.0f) * _owningAvatar->getScale());
|
||||
}
|
||||
|
||||
// Called by Avatar::simulate after it has set the joint states (fullUpdate true if changed),
|
||||
// but just before head has been simulated.
|
||||
void SkeletonModel::simulate(float deltaTime, bool fullUpdate) {
|
||||
updateAttitude();
|
||||
setBlendshapeCoefficients(_owningAvatar->getHead()->getBlendshapeCoefficients());
|
||||
|
||||
Model::simulate(deltaTime, fullUpdate);
|
||||
|
|
|
@ -31,6 +31,7 @@ public:
|
|||
|
||||
virtual void simulate(float deltaTime, bool fullUpdate = true);
|
||||
virtual void updateRig(float deltaTime, glm::mat4 parentTransform);
|
||||
void updateAttitude();
|
||||
|
||||
void renderIKConstraints(gpu::Batch& batch);
|
||||
|
||||
|
|
|
@ -115,6 +115,7 @@ void Stats::updateStats() {
|
|||
STAT_UPDATE(serverCount, nodeList->size());
|
||||
STAT_UPDATE(framerate, (int)qApp->getFps());
|
||||
STAT_UPDATE(simrate, (int)Application::getInstance()->getAverageSimsPerSecond());
|
||||
STAT_UPDATE(avatarSimrate, (int)qApp->getAvatarSimrate());
|
||||
|
||||
auto bandwidthRecorder = DependencyManager::get<BandwidthRecorder>();
|
||||
STAT_UPDATE(packetInCount, bandwidthRecorder->getCachedTotalAverageInputPacketsPerSecond());
|
||||
|
|
|
@ -31,6 +31,7 @@ class Stats : public QQuickItem {
|
|||
STATS_PROPERTY(int, serverCount, 0)
|
||||
STATS_PROPERTY(int, framerate, 0)
|
||||
STATS_PROPERTY(int, simrate, 0)
|
||||
STATS_PROPERTY(int, avatarSimrate, 0)
|
||||
STATS_PROPERTY(int, avatarCount, 0)
|
||||
STATS_PROPERTY(int, packetInCount, 0)
|
||||
STATS_PROPERTY(int, packetOutCount, 0)
|
||||
|
@ -98,6 +99,7 @@ signals:
|
|||
void serverCountChanged();
|
||||
void framerateChanged();
|
||||
void simrateChanged();
|
||||
void avatarSimrateChanged();
|
||||
void avatarCountChanged();
|
||||
void packetInCountChanged();
|
||||
void packetOutCountChanged();
|
||||
|
|
|
@ -110,6 +110,53 @@ void AvatarData::setOrientation(const glm::quat& orientation, bool overideRefere
|
|||
}
|
||||
}
|
||||
|
||||
// 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, true);
|
||||
setOrientation(orientation, true);
|
||||
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, true);
|
||||
setOrientation(_nextOrientation, true);
|
||||
updateAttitude();
|
||||
_nextPosition = pos;
|
||||
_nextOrientation = rot;
|
||||
}
|
||||
void AvatarData::endRender() {
|
||||
setPosition(_nextPosition, true);
|
||||
setOrientation(_nextOrientation, true);
|
||||
updateAttitude();
|
||||
_nextAllowed = true;
|
||||
}
|
||||
|
||||
float AvatarData::getTargetScale() const {
|
||||
if (_referential) {
|
||||
_referential->update();
|
||||
|
|
|
@ -200,6 +200,17 @@ public:
|
|||
glm::quat getOrientation() const;
|
||||
virtual void setOrientation(const glm::quat& orientation, bool overideReferential = false);
|
||||
|
||||
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(); }
|
||||
void setHeadOrientation(const glm::quat& orientation) { _headData->setOrientation(orientation); }
|
||||
|
||||
|
@ -358,6 +369,10 @@ protected:
|
|||
float _bodyPitch; // degrees
|
||||
float _bodyRoll; // degrees
|
||||
|
||||
glm::vec3 _nextPosition {};
|
||||
glm::quat _nextOrientation {};
|
||||
bool _nextAllowed {true};
|
||||
|
||||
// Body scale
|
||||
float _targetScale;
|
||||
|
||||
|
@ -407,6 +422,8 @@ 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
|
||||
|
|
|
@ -409,8 +409,7 @@ void DynamicCharacterController::postSimulation() {
|
|||
glm::quat rotation = bulletToGLM(avatarTransform.getRotation());
|
||||
glm::vec3 position = bulletToGLM(avatarTransform.getOrigin());
|
||||
|
||||
_avatarData->setOrientation(rotation);
|
||||
_avatarData->setPosition(position - rotation * _shapeLocalOffset);
|
||||
_avatarData->nextAttitude(position - rotation * _shapeLocalOffset, rotation);
|
||||
_avatarData->setVelocity(bulletToGLM(_rigidBody->getLinearVelocity()));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1291,7 +1291,9 @@ void Model::simulateInternal(float deltaTime) {
|
|||
const FBXGeometry& geometry = _geometry->getFBXGeometry();
|
||||
glm::mat4 parentTransform = glm::scale(_scale) * glm::translate(_offset) * geometry.offset;
|
||||
updateRig(deltaTime, parentTransform);
|
||||
|
||||
}
|
||||
void Model::updateClusterMatrices() {
|
||||
const FBXGeometry& geometry = _geometry->getFBXGeometry();
|
||||
glm::mat4 zeroScale(glm::vec4(0.0f, 0.0f, 0.0f, 0.0f),
|
||||
glm::vec4(0.0f, 0.0f, 0.0f, 0.0f),
|
||||
glm::vec4(0.0f, 0.0f, 0.0f, 0.0f),
|
||||
|
@ -1305,7 +1307,7 @@ void Model::simulateInternal(float deltaTime) {
|
|||
if (_showTrueJointTransforms) {
|
||||
for (int j = 0; j < mesh.clusters.size(); j++) {
|
||||
const FBXCluster& cluster = mesh.clusters.at(j);
|
||||
auto jointMatrix =_rig->getJointTransform(cluster.jointIndex);
|
||||
auto jointMatrix = _rig->getJointTransform(cluster.jointIndex);
|
||||
state.clusterMatrices[j] = modelToWorld * jointMatrix * cluster.inverseBindMatrix;
|
||||
|
||||
// as an optimization, don't build cautrizedClusterMatrices if the boneSet is empty.
|
||||
|
@ -1319,7 +1321,7 @@ void Model::simulateInternal(float deltaTime) {
|
|||
} else {
|
||||
for (int j = 0; j < mesh.clusters.size(); j++) {
|
||||
const FBXCluster& cluster = mesh.clusters.at(j);
|
||||
auto jointMatrix = _rig->getJointVisibleTransform(cluster.jointIndex);
|
||||
auto jointMatrix = _rig->getJointVisibleTransform(cluster.jointIndex); // differs from above only in using get...VisibleTransform
|
||||
state.clusterMatrices[j] = modelToWorld * jointMatrix * cluster.inverseBindMatrix;
|
||||
|
||||
// as an optimization, don't build cautrizedClusterMatrices if the boneSet is empty.
|
||||
|
@ -1434,7 +1436,6 @@ AABox Model::getPartBounds(int meshIndex, int partIndex) {
|
|||
return calculateScaledOffsetAABox(_geometry->getFBXGeometry().meshExtents);
|
||||
}
|
||||
}
|
||||
|
||||
if (_geometry->getFBXGeometry().meshes.size() > meshIndex) {
|
||||
|
||||
// FIX ME! - This is currently a hack because for some mesh parts our efforts to calculate the bounding
|
||||
|
@ -1489,6 +1490,8 @@ void Model::renderPart(RenderArgs* args, int meshIndex, int partIndex, bool tran
|
|||
return;
|
||||
}
|
||||
|
||||
updateClusterMatrices();
|
||||
|
||||
const NetworkMesh& networkMesh = *(networkMeshes.at(meshIndex).get());
|
||||
const FBXMesh& mesh = geometry.meshes.at(meshIndex);
|
||||
const MeshState& state = _meshStates.at(meshIndex);
|
||||
|
|
|
@ -111,6 +111,7 @@ public:
|
|||
bool getSnapModelToRegistrationPoint() { return _snapModelToRegistrationPoint; }
|
||||
|
||||
virtual void simulate(float deltaTime, bool fullUpdate = true);
|
||||
void updateClusterMatrices();
|
||||
|
||||
/// Returns a reference to the shared geometry.
|
||||
const QSharedPointer<NetworkGeometry>& getGeometry() const { return _geometry; }
|
||||
|
|
|
@ -24,7 +24,7 @@ class GenericQueueThread : public GenericThread {
|
|||
public:
|
||||
using Queue = QQueue<T>;
|
||||
GenericQueueThread(QObject* parent = nullptr)
|
||||
: GenericThread(parent) {}
|
||||
: GenericThread() {}
|
||||
|
||||
virtual ~GenericQueueThread() {}
|
||||
|
||||
|
|
|
@ -14,8 +14,8 @@
|
|||
#include "GenericThread.h"
|
||||
|
||||
|
||||
GenericThread::GenericThread(QObject* parent) :
|
||||
QObject(parent),
|
||||
GenericThread::GenericThread() :
|
||||
QObject(),
|
||||
_stopThread(false),
|
||||
_isThreaded(false) // assume non-threaded, must call initialize()
|
||||
{
|
||||
|
|
|
@ -23,7 +23,7 @@
|
|||
class GenericThread : public QObject {
|
||||
Q_OBJECT
|
||||
public:
|
||||
GenericThread(QObject* parent = nullptr);
|
||||
GenericThread();
|
||||
virtual ~GenericThread();
|
||||
|
||||
/// Call to start the thread.
|
||||
|
|
Loading…
Reference in a new issue