Merge branch 'master' of github.com:highfidelity/hifi into improve-rotation-jitter

This commit is contained in:
Dante Ruiz 2017-05-23 18:19:38 +01:00
commit 54d63f5155
83 changed files with 938 additions and 624 deletions

View file

@ -208,9 +208,10 @@ foreach(CUSTOM_MACRO ${HIFI_CUSTOM_MACROS})
include(${CUSTOM_MACRO}) include(${CUSTOM_MACRO})
endforeach() endforeach()
file(GLOB_RECURSE JS_SRC scripts/*.js) file(GLOB_RECURSE JS_SRC scripts/*.js unpublishedScripts/*.js)
add_custom_target(js SOURCES ${JS_SRC}) add_custom_target(js SOURCES ${JS_SRC})
GroupSources("scripts") GroupSources("scripts")
GroupSources("unpublishedScripts")
if (UNIX) if (UNIX)
install( install(

View file

@ -584,6 +584,7 @@ void Agent::setIsAvatar(bool isAvatar) {
void Agent::sendAvatarIdentityPacket() { void Agent::sendAvatarIdentityPacket() {
if (_isAvatar) { if (_isAvatar) {
auto scriptedAvatar = DependencyManager::get<ScriptableAvatar>(); auto scriptedAvatar = DependencyManager::get<ScriptableAvatar>();
scriptedAvatar->markIdentityDataChanged();
scriptedAvatar->sendIdentityPacket(); scriptedAvatar->sendIdentityPacket();
} }
} }

View file

@ -38,13 +38,14 @@
#include "AudioMixer.h" #include "AudioMixer.h"
static const float DEFAULT_ATTENUATION_PER_DOUBLING_IN_DISTANCE = 0.5f; // attenuation = -6dB * log2(distance) static const float DEFAULT_ATTENUATION_PER_DOUBLING_IN_DISTANCE = 0.5f; // attenuation = -6dB * log2(distance)
static const int DISABLE_STATIC_JITTER_FRAMES = -1;
static const float DEFAULT_NOISE_MUTING_THRESHOLD = 1.0f; static const float DEFAULT_NOISE_MUTING_THRESHOLD = 1.0f;
static const QString AUDIO_MIXER_LOGGING_TARGET_NAME = "audio-mixer"; static const QString AUDIO_MIXER_LOGGING_TARGET_NAME = "audio-mixer";
static const QString AUDIO_ENV_GROUP_KEY = "audio_env"; static const QString AUDIO_ENV_GROUP_KEY = "audio_env";
static const QString AUDIO_BUFFER_GROUP_KEY = "audio_buffer"; static const QString AUDIO_BUFFER_GROUP_KEY = "audio_buffer";
static const QString AUDIO_THREADING_GROUP_KEY = "audio_threading"; static const QString AUDIO_THREADING_GROUP_KEY = "audio_threading";
int AudioMixer::_numStaticJitterFrames{ -1 }; int AudioMixer::_numStaticJitterFrames{ DISABLE_STATIC_JITTER_FRAMES };
float AudioMixer::_noiseMutingThreshold{ DEFAULT_NOISE_MUTING_THRESHOLD }; float AudioMixer::_noiseMutingThreshold{ DEFAULT_NOISE_MUTING_THRESHOLD };
float AudioMixer::_attenuationPerDoublingInDistance{ DEFAULT_ATTENUATION_PER_DOUBLING_IN_DISTANCE }; float AudioMixer::_attenuationPerDoublingInDistance{ DEFAULT_ATTENUATION_PER_DOUBLING_IN_DISTANCE };
std::map<QString, std::shared_ptr<CodecPlugin>> AudioMixer::_availableCodecs{ }; std::map<QString, std::shared_ptr<CodecPlugin>> AudioMixer::_availableCodecs{ };
@ -56,7 +57,12 @@ QVector<AudioMixer::ReverbSettings> AudioMixer::_zoneReverbSettings;
AudioMixer::AudioMixer(ReceivedMessage& message) : AudioMixer::AudioMixer(ReceivedMessage& message) :
ThreadedAssignment(message) { ThreadedAssignment(message) {
// Always clear settings first
// This prevents previous assignment settings from sticking around
clearDomainSettings();
// hash the available codecs (on the mixer) // hash the available codecs (on the mixer)
_availableCodecs.clear(); // Make sure struct is clean
auto codecPlugins = PluginManager::getInstance()->getCodecPlugins(); auto codecPlugins = PluginManager::getInstance()->getCodecPlugins();
std::for_each(codecPlugins.cbegin(), codecPlugins.cend(), std::for_each(codecPlugins.cbegin(), codecPlugins.cend(),
[&](const CodecPluginPointer& codec) { [&](const CodecPluginPointer& codec) {
@ -232,7 +238,7 @@ void AudioMixer::sendStatsPacket() {
} }
// general stats // general stats
statsObject["useDynamicJitterBuffers"] = _numStaticJitterFrames == -1; statsObject["useDynamicJitterBuffers"] = _numStaticJitterFrames == DISABLE_STATIC_JITTER_FRAMES;
statsObject["threads"] = _slavePool.numThreads(); statsObject["threads"] = _slavePool.numThreads();
@ -490,6 +496,16 @@ int AudioMixer::prepareFrame(const SharedNodePointer& node, unsigned int frame)
return data->checkBuffersBeforeFrameSend(); return data->checkBuffersBeforeFrameSend();
} }
void AudioMixer::clearDomainSettings() {
_numStaticJitterFrames = DISABLE_STATIC_JITTER_FRAMES;
_attenuationPerDoublingInDistance = DEFAULT_ATTENUATION_PER_DOUBLING_IN_DISTANCE;
_noiseMutingThreshold = DEFAULT_NOISE_MUTING_THRESHOLD;
_codecPreferenceOrder.clear();
_audioZones.clear();
_zoneSettings.clear();
_zoneReverbSettings.clear();
}
void AudioMixer::parseSettingsObject(const QJsonObject &settingsObject) { void AudioMixer::parseSettingsObject(const QJsonObject &settingsObject) {
qDebug() << "AVX2 Support:" << (cpuSupportsAVX2() ? "enabled" : "disabled"); qDebug() << "AVX2 Support:" << (cpuSupportsAVX2() ? "enabled" : "disabled");
@ -525,7 +541,7 @@ void AudioMixer::parseSettingsObject(const QJsonObject &settingsObject) {
qDebug() << "Static desired jitter buffer frames:" << _numStaticJitterFrames; qDebug() << "Static desired jitter buffer frames:" << _numStaticJitterFrames;
} else { } else {
qDebug() << "Disabling dynamic jitter buffers."; qDebug() << "Disabling dynamic jitter buffers.";
_numStaticJitterFrames = -1; _numStaticJitterFrames = DISABLE_STATIC_JITTER_FRAMES;
} }
// check for deprecated audio settings // check for deprecated audio settings

View file

@ -79,6 +79,7 @@ private:
QString percentageForMixStats(int counter); QString percentageForMixStats(int counter);
void parseSettingsObject(const QJsonObject& settingsObject); void parseSettingsObject(const QJsonObject& settingsObject);
void clearDomainSettings();
float _trailingMixRatio { 0.0f }; float _trailingMixRatio { 0.0f };
float _throttlingRatio { 0.0f }; float _throttlingRatio { 0.0f };

View file

@ -68,7 +68,7 @@
"typeVar": "rightHandType", "typeVar": "rightHandType",
"weightVar": "rightHandWeight", "weightVar": "rightHandWeight",
"weight": 1.0, "weight": 1.0,
"flexCoefficients": [1, 0.5, 0.5, 0.25, 0.1, 0.05, 0.01, 0.0, 0.0] "flexCoefficients": [1, 0.5, 0.5, 0.2, 0.01, 0.005, 0.001, 0.0, 0.0]
}, },
{ {
"jointName": "LeftHand", "jointName": "LeftHand",
@ -77,7 +77,7 @@
"typeVar": "leftHandType", "typeVar": "leftHandType",
"weightVar": "leftHandWeight", "weightVar": "leftHandWeight",
"weight": 1.0, "weight": 1.0,
"flexCoefficients": [1, 0.5, 0.5, 0.25, 0.1, 0.05, 0.01, 0.0, 0.0] "flexCoefficients": [1, 0.5, 0.5, 0.2, 0.01, 0.005, 0.001, 0.0, 0.0]
}, },
{ {
"jointName": "RightFoot", "jointName": "RightFoot",
@ -104,7 +104,7 @@
"typeVar": "spine2Type", "typeVar": "spine2Type",
"weightVar": "spine2Weight", "weightVar": "spine2Weight",
"weight": 1.0, "weight": 1.0,
"flexCoefficients": [0.45, 0.45] "flexCoefficients": [1.0, 0.5, 0.5]
}, },
{ {
"jointName": "Head", "jointName": "Head",
@ -113,7 +113,7 @@
"typeVar": "headType", "typeVar": "headType",
"weightVar": "headWeight", "weightVar": "headWeight",
"weight": 4.0, "weight": 4.0,
"flexCoefficients": [1, 0.5, 0.5, 0.5, 0.5] "flexCoefficients": [1, 0.05, 0.25, 0.25, 0.25]
} }
] ]
}, },

View file

@ -431,7 +431,7 @@ static const QString STATE_ADVANCED_MOVEMENT_CONTROLS = "AdvancedMovement";
static const QString STATE_GROUNDED = "Grounded"; static const QString STATE_GROUNDED = "Grounded";
static const QString STATE_NAV_FOCUSED = "NavigationFocused"; static const QString STATE_NAV_FOCUSED = "NavigationFocused";
bool setupEssentials(int& argc, char** argv) { bool setupEssentials(int& argc, char** argv, bool runningMarkerExisted) {
const char** constArgv = const_cast<const char**>(argv); const char** constArgv = const_cast<const char**>(argv);
const char* portStr = getCmdOption(argc, constArgv, "--listenPort"); const char* portStr = getCmdOption(argc, constArgv, "--listenPort");
const int listenPort = portStr ? atoi(portStr) : INVALID_PORT; const int listenPort = portStr ? atoi(portStr) : INVALID_PORT;
@ -458,7 +458,7 @@ bool setupEssentials(int& argc, char** argv) {
static const auto SUPPRESS_SETTINGS_RESET = "--suppress-settings-reset"; static const auto SUPPRESS_SETTINGS_RESET = "--suppress-settings-reset";
bool suppressPrompt = cmdOptionExists(argc, const_cast<const char**>(argv), SUPPRESS_SETTINGS_RESET); bool suppressPrompt = cmdOptionExists(argc, const_cast<const char**>(argv), SUPPRESS_SETTINGS_RESET);
bool previousSessionCrashed = CrashHandler::checkForResetSettings(suppressPrompt); bool previousSessionCrashed = CrashHandler::checkForResetSettings(runningMarkerExisted, suppressPrompt);
DependencyManager::registerInheritance<LimitedNodeList, NodeList>(); DependencyManager::registerInheritance<LimitedNodeList, NodeList>();
DependencyManager::registerInheritance<AvatarHashMap, AvatarManager>(); DependencyManager::registerInheritance<AvatarHashMap, AvatarManager>();
@ -563,11 +563,11 @@ const bool DEFAULT_DESKTOP_TABLET_BECOMES_TOOLBAR = true;
const bool DEFAULT_HMD_TABLET_BECOMES_TOOLBAR = false; const bool DEFAULT_HMD_TABLET_BECOMES_TOOLBAR = false;
const bool DEFAULT_PREFER_AVATAR_FINGER_OVER_STYLUS = false; const bool DEFAULT_PREFER_AVATAR_FINGER_OVER_STYLUS = false;
Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer) : Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bool runningMarkerExisted) :
QApplication(argc, argv), QApplication(argc, argv),
_window(new MainWindow(desktop())), _window(new MainWindow(desktop())),
_sessionRunTimer(startupTimer), _sessionRunTimer(startupTimer),
_previousSessionCrashed(setupEssentials(argc, argv)), _previousSessionCrashed(setupEssentials(argc, argv, runningMarkerExisted)),
_undoStackScriptingInterface(&_undoStack), _undoStackScriptingInterface(&_undoStack),
_entitySimulation(new PhysicalEntitySimulation()), _entitySimulation(new PhysicalEntitySimulation()),
_physicsEngine(new PhysicsEngine(Vectors::ZERO)), _physicsEngine(new PhysicsEngine(Vectors::ZERO)),
@ -3972,7 +3972,7 @@ void Application::updateMyAvatarLookAtPosition() {
if (faceAngle < MAXIMUM_FACE_ANGLE) { if (faceAngle < MAXIMUM_FACE_ANGLE) {
// Randomly look back and forth between look targets // Randomly look back and forth between look targets
eyeContactTarget target = Menu::getInstance()->isOptionChecked(MenuOption::FixGaze) ? eyeContactTarget target = Menu::getInstance()->isOptionChecked(MenuOption::FixGaze) ?
LEFT_EYE : myAvatar->getEyeContactTarget(); LEFT_EYE : myAvatar->getEyeContactTarget();
switch (target) { switch (target) {
case LEFT_EYE: case LEFT_EYE:
lookAtSpot = lookingAtHead->getLeftEyePosition(); lookAtSpot = lookingAtHead->getLeftEyePosition();
@ -6425,7 +6425,7 @@ void Application::takeSnapshot(bool notify, bool includeAnimated, float aspectRa
if (!includeAnimated) { if (!includeAnimated) {
// Tell the dependency manager that the capture of the still snapshot has taken place. // Tell the dependency manager that the capture of the still snapshot has taken place.
emit DependencyManager::get<WindowScriptingInterface>()->stillSnapshotTaken(path, notify); emit DependencyManager::get<WindowScriptingInterface>()->stillSnapshotTaken(path, notify);
} else { } else if (!SnapshotAnimated::isAlreadyTakingSnapshotAnimated()) {
// Get an animated GIF snapshot and save it // Get an animated GIF snapshot and save it
SnapshotAnimated::saveSnapshotAnimated(path, aspectRatio, qApp, DependencyManager::get<WindowScriptingInterface>()); SnapshotAnimated::saveSnapshotAnimated(path, aspectRatio, qApp, DependencyManager::get<WindowScriptingInterface>());
} }

View file

@ -136,7 +136,7 @@ public:
static void initPlugins(const QStringList& arguments); static void initPlugins(const QStringList& arguments);
static void shutdownPlugins(); static void shutdownPlugins();
Application(int& argc, char** argv, QElapsedTimer& startup_time); Application(int& argc, char** argv, QElapsedTimer& startup_time, bool runningMarkerExisted);
~Application(); ~Application();
void postLambdaEvent(std::function<void()> f) override; void postLambdaEvent(std::function<void()> f) override;

View file

@ -28,7 +28,7 @@
#include <RunningMarker.h> #include <RunningMarker.h>
bool CrashHandler::checkForResetSettings(bool suppressPrompt) { bool CrashHandler::checkForResetSettings(bool wasLikelyCrash, bool suppressPrompt) {
Settings settings; Settings settings;
settings.beginGroup("Developer"); settings.beginGroup("Developer");
QVariant displayCrashOptions = settings.value(MenuOption::DisplayCrashOptions); QVariant displayCrashOptions = settings.value(MenuOption::DisplayCrashOptions);
@ -39,9 +39,6 @@ bool CrashHandler::checkForResetSettings(bool suppressPrompt) {
// If option does not exist in Interface.ini so assume default behavior. // If option does not exist in Interface.ini so assume default behavior.
bool displaySettingsResetOnCrash = !displayCrashOptions.isValid() || displayCrashOptions.toBool(); bool displaySettingsResetOnCrash = !displayCrashOptions.isValid() || displayCrashOptions.toBool();
QFile runningMarkerFile(RunningMarker::getMarkerFilePath(RUNNING_MARKER_FILENAME));
bool wasLikelyCrash = runningMarkerFile.exists();
if (suppressPrompt) { if (suppressPrompt) {
return wasLikelyCrash; return wasLikelyCrash;
} }

View file

@ -17,7 +17,7 @@
class CrashHandler { class CrashHandler {
public: public:
static bool checkForResetSettings(bool suppressPrompt = false); static bool checkForResetSettings(bool wasLikelyCrash, bool suppressPrompt = false);
private: private:
enum Action { enum Action {

View file

@ -525,6 +525,8 @@ Menu::Menu() {
avatar.get(), SLOT(setEnableDebugDrawIKTargets(bool))); avatar.get(), SLOT(setEnableDebugDrawIKTargets(bool)));
addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::RenderIKConstraints, 0, false, addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::RenderIKConstraints, 0, false,
avatar.get(), SLOT(setEnableDebugDrawIKConstraints(bool))); avatar.get(), SLOT(setEnableDebugDrawIKConstraints(bool)));
addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::RenderIKChains, 0, false,
avatar.get(), SLOT(setEnableDebugDrawIKChains(bool)));
addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::ActionMotorControl, addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::ActionMotorControl,
Qt::CTRL | Qt::SHIFT | Qt::Key_K, true, avatar.get(), SLOT(updateMotionBehaviorFromMenu()), Qt::CTRL | Qt::SHIFT | Qt::Key_K, true, avatar.get(), SLOT(updateMotionBehaviorFromMenu()),

View file

@ -162,6 +162,7 @@ namespace MenuOption {
const QString RenderSensorToWorldMatrix = "Show SensorToWorld Matrix"; const QString RenderSensorToWorldMatrix = "Show SensorToWorld Matrix";
const QString RenderIKTargets = "Show IK Targets"; const QString RenderIKTargets = "Show IK Targets";
const QString RenderIKConstraints = "Show IK Constraints"; const QString RenderIKConstraints = "Show IK Constraints";
const QString RenderIKChains = "Show IK Chains";
const QString ResetAvatarSize = "Reset Avatar Size"; const QString ResetAvatarSize = "Reset Avatar Size";
const QString ResetSensors = "Reset Sensors"; const QString ResetSensors = "Reset Sensors";
const QString RunningScripts = "Running Scripts..."; const QString RunningScripts = "Running Scripts...";

View file

@ -52,7 +52,7 @@ const QUuid MY_AVATAR_KEY; // NULL key
AvatarManager::AvatarManager(QObject* parent) : AvatarManager::AvatarManager(QObject* parent) :
_avatarsToFade(), _avatarsToFade(),
_myAvatar(std::make_shared<MyAvatar>(qApp->thread(), std::make_shared<Rig>())) _myAvatar(std::make_shared<MyAvatar>(qApp->thread()))
{ {
// register a meta type for the weak pointer we'll use for the owning avatar mixer for each avatar // register a meta type for the weak pointer we'll use for the owning avatar mixer for each avatar
qRegisterMetaType<QWeakPointer<Node> >("NodeWeakPointer"); qRegisterMetaType<QWeakPointer<Node> >("NodeWeakPointer");
@ -300,7 +300,7 @@ void AvatarManager::simulateAvatarFades(float deltaTime) {
} }
AvatarSharedPointer AvatarManager::newSharedAvatar() { AvatarSharedPointer AvatarManager::newSharedAvatar() {
return std::make_shared<OtherAvatar>(qApp->thread(), std::make_shared<Rig>()); return std::make_shared<OtherAvatar>(qApp->thread());
} }
void AvatarManager::handleRemovedAvatar(const AvatarSharedPointer& removedAvatar, KillAvatarReason removalReason) { void AvatarManager::handleRemovedAvatar(const AvatarSharedPointer& removedAvatar, KillAvatarReason removalReason) {

View file

@ -98,8 +98,8 @@ static const glm::quat DEFAULT_AVATAR_LEFTFOOT_ROT { -0.40167322754859924f, 0.91
static const glm::vec3 DEFAULT_AVATAR_RIGHTFOOT_POS { 0.08f, -0.96f, 0.029f }; static const glm::vec3 DEFAULT_AVATAR_RIGHTFOOT_POS { 0.08f, -0.96f, 0.029f };
static const glm::quat DEFAULT_AVATAR_RIGHTFOOT_ROT { -0.4016716778278351f, 0.9154615998268127f, 0.0053307069465518f, 0.023696165531873703f }; static const glm::quat DEFAULT_AVATAR_RIGHTFOOT_ROT { -0.4016716778278351f, 0.9154615998268127f, 0.0053307069465518f, 0.023696165531873703f };
MyAvatar::MyAvatar(QThread* thread, RigPointer rig) : MyAvatar::MyAvatar(QThread* thread) :
Avatar(thread, rig), Avatar(thread),
_yawSpeed(YAW_SPEED_DEFAULT), _yawSpeed(YAW_SPEED_DEFAULT),
_pitchSpeed(PITCH_SPEED_DEFAULT), _pitchSpeed(PITCH_SPEED_DEFAULT),
_scriptedMotorTimescale(DEFAULT_SCRIPTED_MOTOR_TIMESCALE), _scriptedMotorTimescale(DEFAULT_SCRIPTED_MOTOR_TIMESCALE),
@ -120,7 +120,6 @@ MyAvatar::MyAvatar(QThread* thread, RigPointer rig) :
_goToPending(false), _goToPending(false),
_goToPosition(), _goToPosition(),
_goToOrientation(), _goToOrientation(),
_rig(rig),
_prevShouldDrawHead(true), _prevShouldDrawHead(true),
_audioListenerMode(FROM_HEAD), _audioListenerMode(FROM_HEAD),
_hmdAtRestDetector(glm::vec3(0), glm::quat()) _hmdAtRestDetector(glm::vec3(0), glm::quat())
@ -129,7 +128,7 @@ MyAvatar::MyAvatar(QThread* thread, RigPointer rig) :
// give the pointer to our head to inherited _headData variable from AvatarData // give the pointer to our head to inherited _headData variable from AvatarData
_headData = new MyHead(this); _headData = new MyHead(this);
_skeletonModel = std::make_shared<MySkeletonModel>(this, nullptr, rig); _skeletonModel = std::make_shared<MySkeletonModel>(this, nullptr);
connect(_skeletonModel.get(), &Model::setURLFinished, this, &Avatar::setModelURLFinished); connect(_skeletonModel.get(), &Model::setURLFinished, this, &Avatar::setModelURLFinished);
@ -180,9 +179,7 @@ MyAvatar::MyAvatar(QThread* thread, RigPointer rig) :
auto audioIO = DependencyManager::get<AudioClient>(); auto audioIO = DependencyManager::get<AudioClient>();
audioIO->setIsPlayingBackRecording(isPlaying); audioIO->setIsPlayingBackRecording(isPlaying);
if (_rig) { _skeletonModel->getRig().setEnableAnimations(!isPlaying);
_rig->setEnableAnimations(!isPlaying);
}
}); });
connect(recorder.data(), &Recorder::recordingStateChanged, [=] { connect(recorder.data(), &Recorder::recordingStateChanged, [=] {
@ -233,12 +230,12 @@ MyAvatar::MyAvatar(QThread* thread, RigPointer rig) :
} }
auto jointData = dummyAvatar.getRawJointData(); auto jointData = dummyAvatar.getRawJointData();
if (jointData.length() > 0 && _rig) { if (jointData.length() > 0) {
_rig->copyJointsFromJointData(jointData); _skeletonModel->getRig().copyJointsFromJointData(jointData);
} }
}); });
connect(rig.get(), SIGNAL(onLoadComplete()), this, SIGNAL(onLoadComplete())); connect(&(_skeletonModel->getRig()), SIGNAL(onLoadComplete()), this, SIGNAL(onLoadComplete()));
_characterController.setDensity(_density); _characterController.setDensity(_density);
} }
@ -355,9 +352,7 @@ void MyAvatar::clearIKJointLimitHistory() {
return; return;
} }
if (_rig) { _skeletonModel->getRig().clearIKJointLimitHistory();
_rig->clearIKJointLimitHistory();
}
} }
void MyAvatar::reset(bool andRecenter, bool andReload, bool andHead) { void MyAvatar::reset(bool andRecenter, bool andReload, bool andHead) {
@ -528,11 +523,9 @@ void MyAvatar::simulate(float deltaTime) {
{ {
PerformanceTimer perfTimer("skeleton"); PerformanceTimer perfTimer("skeleton");
if (_rig) { _skeletonModel->getRig().setEnableDebugDrawIKTargets(_enableDebugDrawIKTargets);
_rig->setEnableDebugDrawIKTargets(_enableDebugDrawIKTargets); _skeletonModel->getRig().setEnableDebugDrawIKConstraints(_enableDebugDrawIKConstraints);
_rig->setEnableDebugDrawIKConstraints(_enableDebugDrawIKConstraints); _skeletonModel->getRig().setEnableDebugDrawIKChains(_enableDebugDrawIKChains);
}
_skeletonModel->simulate(deltaTime); _skeletonModel->simulate(deltaTime);
} }
@ -550,7 +543,7 @@ void MyAvatar::simulate(float deltaTime) {
PerformanceTimer perfTimer("joints"); PerformanceTimer perfTimer("joints");
// copy out the skeleton joints from the model // copy out the skeleton joints from the model
if (_rigEnabled) { if (_rigEnabled) {
_rig->copyJointsIntoJointData(_jointData); _skeletonModel->getRig().copyJointsIntoJointData(_jointData);
} }
} }
@ -795,7 +788,7 @@ void MyAvatar::overrideAnimation(const QString& url, float fps, bool loop, float
Q_ARG(bool, loop), Q_ARG(float, firstFrame), Q_ARG(float, lastFrame)); Q_ARG(bool, loop), Q_ARG(float, firstFrame), Q_ARG(float, lastFrame));
return; return;
} }
_rig->overrideAnimation(url, fps, loop, firstFrame, lastFrame); _skeletonModel->getRig().overrideAnimation(url, fps, loop, firstFrame, lastFrame);
} }
void MyAvatar::restoreAnimation() { void MyAvatar::restoreAnimation() {
@ -803,7 +796,7 @@ void MyAvatar::restoreAnimation() {
QMetaObject::invokeMethod(this, "restoreAnimation"); QMetaObject::invokeMethod(this, "restoreAnimation");
return; return;
} }
_rig->restoreAnimation(); _skeletonModel->getRig().restoreAnimation();
} }
QStringList MyAvatar::getAnimationRoles() { QStringList MyAvatar::getAnimationRoles() {
@ -812,7 +805,7 @@ QStringList MyAvatar::getAnimationRoles() {
QMetaObject::invokeMethod(this, "getAnimationRoles", Qt::BlockingQueuedConnection, Q_RETURN_ARG(QStringList, result)); QMetaObject::invokeMethod(this, "getAnimationRoles", Qt::BlockingQueuedConnection, Q_RETURN_ARG(QStringList, result));
return result; return result;
} }
return _rig->getAnimationRoles(); return _skeletonModel->getRig().getAnimationRoles();
} }
void MyAvatar::overrideRoleAnimation(const QString& role, const QString& url, float fps, bool loop, void MyAvatar::overrideRoleAnimation(const QString& role, const QString& url, float fps, bool loop,
@ -822,7 +815,7 @@ void MyAvatar::overrideRoleAnimation(const QString& role, const QString& url, fl
Q_ARG(float, fps), Q_ARG(bool, loop), Q_ARG(float, firstFrame), Q_ARG(float, lastFrame)); Q_ARG(float, fps), Q_ARG(bool, loop), Q_ARG(float, firstFrame), Q_ARG(float, lastFrame));
return; return;
} }
_rig->overrideRoleAnimation(role, url, fps, loop, firstFrame, lastFrame); _skeletonModel->getRig().overrideRoleAnimation(role, url, fps, loop, firstFrame, lastFrame);
} }
void MyAvatar::restoreRoleAnimation(const QString& role) { void MyAvatar::restoreRoleAnimation(const QString& role) {
@ -830,7 +823,7 @@ void MyAvatar::restoreRoleAnimation(const QString& role) {
QMetaObject::invokeMethod(this, "restoreRoleAnimation", Q_ARG(const QString&, role)); QMetaObject::invokeMethod(this, "restoreRoleAnimation", Q_ARG(const QString&, role));
return; return;
} }
_rig->restoreRoleAnimation(role); _skeletonModel->getRig().restoreRoleAnimation(role);
} }
void MyAvatar::saveData() { void MyAvatar::saveData() {
@ -958,6 +951,10 @@ void MyAvatar::setEnableDebugDrawIKConstraints(bool isEnabled) {
_enableDebugDrawIKConstraints = isEnabled; _enableDebugDrawIKConstraints = isEnabled;
} }
void MyAvatar::setEnableDebugDrawIKChains(bool isEnabled) {
_enableDebugDrawIKChains = isEnabled;
}
void MyAvatar::setEnableMeshVisible(bool isEnabled) { void MyAvatar::setEnableMeshVisible(bool isEnabled) {
_skeletonModel->setVisibleInScene(isEnabled, qApp->getMain3DScene()); _skeletonModel->setVisibleInScene(isEnabled, qApp->getMain3DScene());
} }
@ -968,7 +965,7 @@ void MyAvatar::setUseAnimPreAndPostRotations(bool isEnabled) {
} }
void MyAvatar::setEnableInverseKinematics(bool isEnabled) { void MyAvatar::setEnableInverseKinematics(bool isEnabled) {
_rig->setEnableInverseKinematics(isEnabled); _skeletonModel->getRig().setEnableInverseKinematics(isEnabled);
} }
void MyAvatar::loadData() { void MyAvatar::loadData() {
@ -1217,7 +1214,7 @@ void MyAvatar::setJointData(int index, const glm::quat& rotation, const glm::vec
return; return;
} }
// HACK: ATM only JS scripts call setJointData() on MyAvatar so we hardcode the priority // HACK: ATM only JS scripts call setJointData() on MyAvatar so we hardcode the priority
_rig->setJointState(index, true, rotation, translation, SCRIPT_PRIORITY); _skeletonModel->getRig().setJointState(index, true, rotation, translation, SCRIPT_PRIORITY);
} }
void MyAvatar::setJointRotation(int index, const glm::quat& rotation) { void MyAvatar::setJointRotation(int index, const glm::quat& rotation) {
@ -1226,7 +1223,7 @@ void MyAvatar::setJointRotation(int index, const glm::quat& rotation) {
return; return;
} }
// HACK: ATM only JS scripts call setJointData() on MyAvatar so we hardcode the priority // HACK: ATM only JS scripts call setJointData() on MyAvatar so we hardcode the priority
_rig->setJointRotation(index, true, rotation, SCRIPT_PRIORITY); _skeletonModel->getRig().setJointRotation(index, true, rotation, SCRIPT_PRIORITY);
} }
void MyAvatar::setJointTranslation(int index, const glm::vec3& translation) { void MyAvatar::setJointTranslation(int index, const glm::vec3& translation) {
@ -1235,7 +1232,7 @@ void MyAvatar::setJointTranslation(int index, const glm::vec3& translation) {
return; return;
} }
// HACK: ATM only JS scripts call setJointData() on MyAvatar so we hardcode the priority // HACK: ATM only JS scripts call setJointData() on MyAvatar so we hardcode the priority
_rig->setJointTranslation(index, true, translation, SCRIPT_PRIORITY); _skeletonModel->getRig().setJointTranslation(index, true, translation, SCRIPT_PRIORITY);
} }
void MyAvatar::clearJointData(int index) { void MyAvatar::clearJointData(int index) {
@ -1243,7 +1240,7 @@ void MyAvatar::clearJointData(int index) {
QMetaObject::invokeMethod(this, "clearJointData", Q_ARG(int, index)); QMetaObject::invokeMethod(this, "clearJointData", Q_ARG(int, index));
return; return;
} }
_rig->clearJointAnimationPriority(index); _skeletonModel->getRig().clearJointAnimationPriority(index);
} }
void MyAvatar::clearJointsData() { void MyAvatar::clearJointsData() {
@ -1251,7 +1248,7 @@ void MyAvatar::clearJointsData() {
QMetaObject::invokeMethod(this, "clearJointsData"); QMetaObject::invokeMethod(this, "clearJointsData");
return; return;
} }
_rig->clearJointStates(); _skeletonModel->getRig().clearJointStates();
} }
void MyAvatar::setSkeletonModelURL(const QUrl& skeletonModelURL) { void MyAvatar::setSkeletonModelURL(const QUrl& skeletonModelURL) {
@ -1657,7 +1654,7 @@ void MyAvatar::setAnimGraphUrl(const QUrl& url) {
_skeletonModel->reset(); // Why is this necessary? Without this, we crash in the next render. _skeletonModel->reset(); // Why is this necessary? Without this, we crash in the next render.
_currentAnimGraphUrl.set(url); _currentAnimGraphUrl.set(url);
_rig->initAnimGraph(url); _skeletonModel->getRig().initAnimGraph(url);
_bodySensorMatrix = deriveBodyFromHMDSensor(); // Based on current cached HMD position/rotation.. _bodySensorMatrix = deriveBodyFromHMDSensor(); // Based on current cached HMD position/rotation..
updateSensorToWorldMatrix(); // Uses updated position/orientation and _bodySensorMatrix changes updateSensorToWorldMatrix(); // Uses updated position/orientation and _bodySensorMatrix changes
@ -1673,7 +1670,7 @@ void MyAvatar::initAnimGraph() {
graphUrl = QUrl::fromLocalFile(PathUtils::resourcesPath() + "avatar/avatar-animation.json"); graphUrl = QUrl::fromLocalFile(PathUtils::resourcesPath() + "avatar/avatar-animation.json");
} }
_rig->initAnimGraph(graphUrl); _skeletonModel->getRig().initAnimGraph(graphUrl);
_currentAnimGraphUrl.set(graphUrl); _currentAnimGraphUrl.set(graphUrl);
_bodySensorMatrix = deriveBodyFromHMDSensor(); // Based on current cached HMD position/rotation.. _bodySensorMatrix = deriveBodyFromHMDSensor(); // Based on current cached HMD position/rotation..
@ -1681,7 +1678,7 @@ void MyAvatar::initAnimGraph() {
} }
void MyAvatar::destroyAnimGraph() { void MyAvatar::destroyAnimGraph() {
_rig->destroyAnimGraph(); _skeletonModel->getRig().destroyAnimGraph();
} }
void MyAvatar::postUpdate(float deltaTime) { void MyAvatar::postUpdate(float deltaTime) {
@ -1697,22 +1694,23 @@ void MyAvatar::postUpdate(float deltaTime) {
if (_enableDebugDrawDefaultPose || _enableDebugDrawAnimPose) { if (_enableDebugDrawDefaultPose || _enableDebugDrawAnimPose) {
auto animSkeleton = _rig->getAnimSkeleton(); auto animSkeleton = _skeletonModel->getRig().getAnimSkeleton();
// the rig is in the skeletonModel frame // the rig is in the skeletonModel frame
AnimPose xform(glm::vec3(1), _skeletonModel->getRotation(), _skeletonModel->getTranslation()); AnimPose xform(glm::vec3(1), _skeletonModel->getRotation(), _skeletonModel->getTranslation());
if (_enableDebugDrawDefaultPose && animSkeleton) { if (_enableDebugDrawDefaultPose && animSkeleton) {
glm::vec4 gray(0.2f, 0.2f, 0.2f, 0.2f); glm::vec4 gray(0.2f, 0.2f, 0.2f, 0.2f);
AnimDebugDraw::getInstance().addAbsolutePoses("myAvatarDefaultPoses", animSkeleton, _rig->getAbsoluteDefaultPoses(), xform, gray); AnimDebugDraw::getInstance().addAbsolutePoses("myAvatarDefaultPoses", animSkeleton, _skeletonModel->getRig().getAbsoluteDefaultPoses(), xform, gray);
} }
if (_enableDebugDrawAnimPose && animSkeleton) { if (_enableDebugDrawAnimPose && animSkeleton) {
// build absolute AnimPoseVec from rig // build absolute AnimPoseVec from rig
AnimPoseVec absPoses; AnimPoseVec absPoses;
absPoses.reserve(_rig->getJointStateCount()); const Rig& rig = _skeletonModel->getRig();
for (int i = 0; i < _rig->getJointStateCount(); i++) { absPoses.reserve(rig.getJointStateCount());
absPoses.push_back(AnimPose(_rig->getJointTransform(i))); for (int i = 0; i < rig.getJointStateCount(); i++) {
absPoses.push_back(AnimPose(rig.getJointTransform(i)));
} }
glm::vec4 cyan(0.1f, 0.6f, 0.6f, 1.0f); glm::vec4 cyan(0.1f, 0.6f, 0.6f, 1.0f);
AnimDebugDraw::getInstance().addAbsolutePoses("myAvatarAnimPoses", animSkeleton, absPoses, xform, cyan); AnimDebugDraw::getInstance().addAbsolutePoses("myAvatarAnimPoses", animSkeleton, absPoses, xform, cyan);
@ -2301,17 +2299,18 @@ glm::mat4 MyAvatar::deriveBodyFromHMDSensor() const {
const glm::quat hmdOrientation = getHMDSensorOrientation(); const glm::quat hmdOrientation = getHMDSensorOrientation();
const glm::quat hmdOrientationYawOnly = cancelOutRollAndPitch(hmdOrientation); const glm::quat hmdOrientationYawOnly = cancelOutRollAndPitch(hmdOrientation);
int rightEyeIndex = _rig->indexOfJoint("RightEye"); const Rig& rig = _skeletonModel->getRig();
int leftEyeIndex = _rig->indexOfJoint("LeftEye"); int rightEyeIndex = rig.indexOfJoint("RightEye");
int neckIndex = _rig->indexOfJoint("Neck"); int leftEyeIndex = rig.indexOfJoint("LeftEye");
int hipsIndex = _rig->indexOfJoint("Hips"); int neckIndex = rig.indexOfJoint("Neck");
int hipsIndex = rig.indexOfJoint("Hips");
glm::vec3 rigMiddleEyePos = DEFAULT_AVATAR_MIDDLE_EYE_POS; glm::vec3 rigMiddleEyePos = DEFAULT_AVATAR_MIDDLE_EYE_POS;
if (leftEyeIndex >= 0 && rightEyeIndex >= 0) { if (leftEyeIndex >= 0 && rightEyeIndex >= 0) {
rigMiddleEyePos = (_rig->getAbsoluteDefaultPose(leftEyeIndex).trans() + _rig->getAbsoluteDefaultPose(rightEyeIndex).trans()) / 2.0f; rigMiddleEyePos = (rig.getAbsoluteDefaultPose(leftEyeIndex).trans() + rig.getAbsoluteDefaultPose(rightEyeIndex).trans()) / 2.0f;
} }
glm::vec3 rigNeckPos = neckIndex != -1 ? _rig->getAbsoluteDefaultPose(neckIndex).trans() : DEFAULT_AVATAR_NECK_POS; glm::vec3 rigNeckPos = neckIndex != -1 ? rig.getAbsoluteDefaultPose(neckIndex).trans() : DEFAULT_AVATAR_NECK_POS;
glm::vec3 rigHipsPos = hipsIndex != -1 ? _rig->getAbsoluteDefaultPose(hipsIndex).trans() : DEFAULT_AVATAR_HIPS_POS; glm::vec3 rigHipsPos = hipsIndex != -1 ? rig.getAbsoluteDefaultPose(hipsIndex).trans() : DEFAULT_AVATAR_HIPS_POS;
glm::vec3 localEyes = (rigMiddleEyePos - rigHipsPos); glm::vec3 localEyes = (rigMiddleEyePos - rigHipsPos);
glm::vec3 localNeck = (rigNeckPos - rigHipsPos); glm::vec3 localNeck = (rigNeckPos - rigHipsPos);
@ -2677,8 +2676,8 @@ glm::vec3 MyAvatar::getAbsoluteJointTranslationInObjectFrame(int index) const {
glm::mat4 MyAvatar::getCenterEyeCalibrationMat() const { glm::mat4 MyAvatar::getCenterEyeCalibrationMat() const {
// TODO: as an optimization cache this computation, then invalidate the cache when the avatar model is changed. // TODO: as an optimization cache this computation, then invalidate the cache when the avatar model is changed.
int rightEyeIndex = _rig->indexOfJoint("RightEye"); int rightEyeIndex = _skeletonModel->getRig().indexOfJoint("RightEye");
int leftEyeIndex = _rig->indexOfJoint("LeftEye"); int leftEyeIndex = _skeletonModel->getRig().indexOfJoint("LeftEye");
if (rightEyeIndex >= 0 && leftEyeIndex >= 0) { if (rightEyeIndex >= 0 && leftEyeIndex >= 0) {
auto centerEyePos = (getAbsoluteDefaultJointTranslationInObjectFrame(rightEyeIndex) + getAbsoluteDefaultJointTranslationInObjectFrame(leftEyeIndex)) * 0.5f; auto centerEyePos = (getAbsoluteDefaultJointTranslationInObjectFrame(rightEyeIndex) + getAbsoluteDefaultJointTranslationInObjectFrame(leftEyeIndex)) * 0.5f;
auto centerEyeRot = Quaternions::Y_180; auto centerEyeRot = Quaternions::Y_180;
@ -2690,7 +2689,7 @@ glm::mat4 MyAvatar::getCenterEyeCalibrationMat() const {
glm::mat4 MyAvatar::getHeadCalibrationMat() const { glm::mat4 MyAvatar::getHeadCalibrationMat() const {
// TODO: as an optimization cache this computation, then invalidate the cache when the avatar model is changed. // TODO: as an optimization cache this computation, then invalidate the cache when the avatar model is changed.
int headIndex = _rig->indexOfJoint("Head"); int headIndex = _skeletonModel->getRig().indexOfJoint("Head");
if (headIndex >= 0) { if (headIndex >= 0) {
auto headPos = getAbsoluteDefaultJointTranslationInObjectFrame(headIndex); auto headPos = getAbsoluteDefaultJointTranslationInObjectFrame(headIndex);
auto headRot = getAbsoluteDefaultJointRotationInObjectFrame(headIndex); auto headRot = getAbsoluteDefaultJointRotationInObjectFrame(headIndex);
@ -2702,7 +2701,7 @@ glm::mat4 MyAvatar::getHeadCalibrationMat() const {
glm::mat4 MyAvatar::getSpine2CalibrationMat() const { glm::mat4 MyAvatar::getSpine2CalibrationMat() const {
// TODO: as an optimization cache this computation, then invalidate the cache when the avatar model is changed. // TODO: as an optimization cache this computation, then invalidate the cache when the avatar model is changed.
int spine2Index = _rig->indexOfJoint("Spine2"); int spine2Index = _skeletonModel->getRig().indexOfJoint("Spine2");
if (spine2Index >= 0) { if (spine2Index >= 0) {
auto spine2Pos = getAbsoluteDefaultJointTranslationInObjectFrame(spine2Index); auto spine2Pos = getAbsoluteDefaultJointTranslationInObjectFrame(spine2Index);
auto spine2Rot = getAbsoluteDefaultJointRotationInObjectFrame(spine2Index); auto spine2Rot = getAbsoluteDefaultJointRotationInObjectFrame(spine2Index);
@ -2714,7 +2713,7 @@ glm::mat4 MyAvatar::getSpine2CalibrationMat() const {
glm::mat4 MyAvatar::getHipsCalibrationMat() const { glm::mat4 MyAvatar::getHipsCalibrationMat() const {
// TODO: as an optimization cache this computation, then invalidate the cache when the avatar model is changed. // TODO: as an optimization cache this computation, then invalidate the cache when the avatar model is changed.
int hipsIndex = _rig->indexOfJoint("Hips"); int hipsIndex = _skeletonModel->getRig().indexOfJoint("Hips");
if (hipsIndex >= 0) { if (hipsIndex >= 0) {
auto hipsPos = getAbsoluteDefaultJointTranslationInObjectFrame(hipsIndex); auto hipsPos = getAbsoluteDefaultJointTranslationInObjectFrame(hipsIndex);
auto hipsRot = getAbsoluteDefaultJointRotationInObjectFrame(hipsIndex); auto hipsRot = getAbsoluteDefaultJointRotationInObjectFrame(hipsIndex);
@ -2726,7 +2725,7 @@ glm::mat4 MyAvatar::getHipsCalibrationMat() const {
glm::mat4 MyAvatar::getLeftFootCalibrationMat() const { glm::mat4 MyAvatar::getLeftFootCalibrationMat() const {
// TODO: as an optimization cache this computation, then invalidate the cache when the avatar model is changed. // TODO: as an optimization cache this computation, then invalidate the cache when the avatar model is changed.
int leftFootIndex = _rig->indexOfJoint("LeftFoot"); int leftFootIndex = _skeletonModel->getRig().indexOfJoint("LeftFoot");
if (leftFootIndex >= 0) { if (leftFootIndex >= 0) {
auto leftFootPos = getAbsoluteDefaultJointTranslationInObjectFrame(leftFootIndex); auto leftFootPos = getAbsoluteDefaultJointTranslationInObjectFrame(leftFootIndex);
auto leftFootRot = getAbsoluteDefaultJointRotationInObjectFrame(leftFootIndex); auto leftFootRot = getAbsoluteDefaultJointRotationInObjectFrame(leftFootIndex);
@ -2738,7 +2737,7 @@ glm::mat4 MyAvatar::getLeftFootCalibrationMat() const {
glm::mat4 MyAvatar::getRightFootCalibrationMat() const { glm::mat4 MyAvatar::getRightFootCalibrationMat() const {
// TODO: as an optimization cache this computation, then invalidate the cache when the avatar model is changed. // TODO: as an optimization cache this computation, then invalidate the cache when the avatar model is changed.
int rightFootIndex = _rig->indexOfJoint("RightFoot"); int rightFootIndex = _skeletonModel->getRig().indexOfJoint("RightFoot");
if (rightFootIndex >= 0) { if (rightFootIndex >= 0) {
auto rightFootPos = getAbsoluteDefaultJointTranslationInObjectFrame(rightFootIndex); auto rightFootPos = getAbsoluteDefaultJointTranslationInObjectFrame(rightFootIndex);
auto rightFootRot = getAbsoluteDefaultJointRotationInObjectFrame(rightFootIndex); auto rightFootRot = getAbsoluteDefaultJointRotationInObjectFrame(rightFootIndex);
@ -2758,7 +2757,7 @@ bool MyAvatar::pinJoint(int index, const glm::vec3& position, const glm::quat& o
slamPosition(position); slamPosition(position);
setOrientation(orientation); setOrientation(orientation);
_rig->setMaxHipsOffsetLength(0.05f); _skeletonModel->getRig().setMaxHipsOffsetLength(0.05f);
auto it = std::find(_pinnedJoints.begin(), _pinnedJoints.end(), index); auto it = std::find(_pinnedJoints.begin(), _pinnedJoints.end(), index);
if (it == _pinnedJoints.end()) { if (it == _pinnedJoints.end()) {
@ -2775,7 +2774,7 @@ bool MyAvatar::clearPinOnJoint(int index) {
auto hipsIndex = getJointIndex("Hips"); auto hipsIndex = getJointIndex("Hips");
if (index == hipsIndex) { if (index == hipsIndex) {
_rig->setMaxHipsOffsetLength(FLT_MAX); _skeletonModel->getRig().setMaxHipsOffsetLength(FLT_MAX);
} }
return true; return true;
@ -2784,7 +2783,7 @@ bool MyAvatar::clearPinOnJoint(int index) {
} }
float MyAvatar::getIKErrorOnLastSolve() const { float MyAvatar::getIKErrorOnLastSolve() const {
return _rig->getIKErrorOnLastSolve(); return _skeletonModel->getRig().getIKErrorOnLastSolve();
} }
// thread-safe // thread-safe

View file

@ -148,7 +148,7 @@ public:
}; };
Q_ENUM(DriveKeys) Q_ENUM(DriveKeys)
explicit MyAvatar(QThread* thread, RigPointer rig); explicit MyAvatar(QThread* thread);
~MyAvatar(); ~MyAvatar();
void instantiableAvatar() override {}; void instantiableAvatar() override {};
@ -322,9 +322,9 @@ public:
// adding one of the other handlers. While any handler may change a value in animStateDictionaryIn (or supply different values in animStateDictionaryOut) // adding one of the other handlers. While any handler may change a value in animStateDictionaryIn (or supply different values in animStateDictionaryOut)
// a handler must not remove properties from animStateDictionaryIn, nor change property values that it does not intend to change. // a handler must not remove properties from animStateDictionaryIn, nor change property values that it does not intend to change.
// It is not specified in what order multiple handlers are called. // It is not specified in what order multiple handlers are called.
Q_INVOKABLE QScriptValue addAnimationStateHandler(QScriptValue handler, QScriptValue propertiesList) { return _rig->addAnimationStateHandler(handler, propertiesList); } Q_INVOKABLE QScriptValue addAnimationStateHandler(QScriptValue handler, QScriptValue propertiesList) { return _skeletonModel->getRig().addAnimationStateHandler(handler, propertiesList); }
// Removes a handler previously added by addAnimationStateHandler. // Removes a handler previously added by addAnimationStateHandler.
Q_INVOKABLE void removeAnimationStateHandler(QScriptValue handler) { _rig->removeAnimationStateHandler(handler); } Q_INVOKABLE void removeAnimationStateHandler(QScriptValue handler) { _skeletonModel->getRig().removeAnimationStateHandler(handler); }
Q_INVOKABLE bool getSnapTurn() const { return _useSnapTurn; } Q_INVOKABLE bool getSnapTurn() const { return _useSnapTurn; }
Q_INVOKABLE void setSnapTurn(bool on) { _useSnapTurn = on; } Q_INVOKABLE void setSnapTurn(bool on) { _useSnapTurn = on; }
@ -524,6 +524,7 @@ public slots:
void setEnableDebugDrawSensorToWorldMatrix(bool isEnabled); void setEnableDebugDrawSensorToWorldMatrix(bool isEnabled);
void setEnableDebugDrawIKTargets(bool isEnabled); void setEnableDebugDrawIKTargets(bool isEnabled);
void setEnableDebugDrawIKConstraints(bool isEnabled); void setEnableDebugDrawIKConstraints(bool isEnabled);
void setEnableDebugDrawIKChains(bool isEnabled);
bool getEnableMeshVisible() const { return _skeletonModel->isVisible(); } bool getEnableMeshVisible() const { return _skeletonModel->isVisible(); }
void setEnableMeshVisible(bool isEnabled); void setEnableMeshVisible(bool isEnabled);
void setUseAnimPreAndPostRotations(bool isEnabled); void setUseAnimPreAndPostRotations(bool isEnabled);
@ -708,7 +709,6 @@ private:
glm::quat _goToOrientation; glm::quat _goToOrientation;
std::unordered_set<int> _headBoneSet; std::unordered_set<int> _headBoneSet;
RigPointer _rig;
bool _prevShouldDrawHead; bool _prevShouldDrawHead;
bool _rigEnabled { true }; bool _rigEnabled { true };
@ -718,6 +718,7 @@ private:
bool _enableDebugDrawSensorToWorldMatrix { false }; bool _enableDebugDrawSensorToWorldMatrix { false };
bool _enableDebugDrawIKTargets { false }; bool _enableDebugDrawIKTargets { false };
bool _enableDebugDrawIKConstraints { false }; bool _enableDebugDrawIKConstraints { false };
bool _enableDebugDrawIKChains { false };
AudioListenerMode _audioListenerMode; AudioListenerMode _audioListenerMode;
glm::vec3 _customListenPosition; glm::vec3 _customListenPosition;

View file

@ -44,17 +44,14 @@ glm::quat MyHead::getCameraOrientation() const {
void MyHead::simulate(float deltaTime) { void MyHead::simulate(float deltaTime) {
auto player = DependencyManager::get<recording::Deck>(); auto player = DependencyManager::get<recording::Deck>();
// Only use face trackers when not playing back a recording. // Only use face trackers when not playing back a recording.
if (player->isPlaying()) { if (!player->isPlaying()) {
Parent::simulate(deltaTime);
} else {
computeAudioLoudness(deltaTime);
FaceTracker* faceTracker = qApp->getActiveFaceTracker(); FaceTracker* faceTracker = qApp->getActiveFaceTracker();
_isFaceTrackerConnected = faceTracker && !faceTracker->isMuted(); _isFaceTrackerConnected = faceTracker != nullptr && !faceTracker->isMuted();
if (_isFaceTrackerConnected) { if (_isFaceTrackerConnected) {
_transientBlendshapeCoefficients = faceTracker->getBlendshapeCoefficients(); _transientBlendshapeCoefficients = faceTracker->getBlendshapeCoefficients();
if (typeid(*faceTracker) == typeid(DdeFaceTracker)) { if (typeid(*faceTracker) == typeid(DdeFaceTracker)) {
if (Menu::getInstance()->isOptionChecked(MenuOption::UseAudioForMouth)) { if (Menu::getInstance()->isOptionChecked(MenuOption::UseAudioForMouth)) {
calculateMouthShapes(deltaTime); calculateMouthShapes(deltaTime);
@ -71,19 +68,9 @@ void MyHead::simulate(float deltaTime) {
} }
applyEyelidOffset(getFinalOrientationInWorldFrame()); applyEyelidOffset(getFinalOrientationInWorldFrame());
} }
} else {
computeFaceMovement(deltaTime);
}
auto eyeTracker = DependencyManager::get<EyeTracker>();
_isEyeTrackerConnected = eyeTracker && eyeTracker->isTracking();
if (_isEyeTrackerConnected) {
// TODO? figure out where EyeTracker data harvested. Move it here?
_saccade = glm::vec3();
} else {
computeEyeMovement(deltaTime);
} }
auto eyeTracker = DependencyManager::get<EyeTracker>();
_isEyeTrackerConnected = eyeTracker->isTracking();
} }
computeEyePosition(); Parent::simulate(deltaTime);
} }

View file

@ -13,7 +13,7 @@
#include "Application.h" #include "Application.h"
#include "InterfaceLogging.h" #include "InterfaceLogging.h"
MySkeletonModel::MySkeletonModel(Avatar* owningAvatar, QObject* parent, RigPointer rig) : SkeletonModel(owningAvatar, parent, rig) { MySkeletonModel::MySkeletonModel(Avatar* owningAvatar, QObject* parent) : SkeletonModel(owningAvatar, parent) {
} }
Rig::CharacterControllerState convertCharacterControllerState(CharacterController::State state) { Rig::CharacterControllerState convertCharacterControllerState(CharacterController::State state) {
@ -63,7 +63,7 @@ void MySkeletonModel::updateRig(float deltaTime, glm::mat4 parentTransform) {
glm::mat4 rigToWorld = createMatFromQuatAndPos(getRotation(), getTranslation()); glm::mat4 rigToWorld = createMatFromQuatAndPos(getRotation(), getTranslation());
glm::mat4 worldToRig = glm::inverse(rigToWorld); glm::mat4 worldToRig = glm::inverse(rigToWorld);
glm::mat4 rigHMDMat = worldToRig * worldHMDMat; glm::mat4 rigHMDMat = worldToRig * worldHMDMat;
_rig->computeHeadFromHMD(AnimPose(rigHMDMat), headParams.rigHeadPosition, headParams.rigHeadOrientation); _rig.computeHeadFromHMD(AnimPose(rigHMDMat), headParams.rigHeadPosition, headParams.rigHeadOrientation);
headParams.headEnabled = true; headParams.headEnabled = true;
} else { } else {
// even though full head IK is disabled, the rig still needs the head orientation to rotate the head up and down in desktop mode. // even though full head IK is disabled, the rig still needs the head orientation to rotate the head up and down in desktop mode.
@ -94,7 +94,7 @@ void MySkeletonModel::updateRig(float deltaTime, glm::mat4 parentTransform) {
headParams.isTalking = head->getTimeWithoutTalking() <= 1.5f; headParams.isTalking = head->getTimeWithoutTalking() <= 1.5f;
_rig->updateFromHeadParameters(headParams, deltaTime); _rig.updateFromHeadParameters(headParams, deltaTime);
Rig::HandAndFeetParameters handAndFeetParams; Rig::HandAndFeetParameters handAndFeetParams;
@ -138,14 +138,14 @@ void MySkeletonModel::updateRig(float deltaTime, glm::mat4 parentTransform) {
handAndFeetParams.bodyCapsuleHalfHeight = myAvatar->getCharacterController()->getCapsuleHalfHeight(); handAndFeetParams.bodyCapsuleHalfHeight = myAvatar->getCharacterController()->getCapsuleHalfHeight();
handAndFeetParams.bodyCapsuleLocalOffset = myAvatar->getCharacterController()->getCapsuleLocalOffset(); handAndFeetParams.bodyCapsuleLocalOffset = myAvatar->getCharacterController()->getCapsuleLocalOffset();
_rig->updateFromHandAndFeetParameters(handAndFeetParams, deltaTime); _rig.updateFromHandAndFeetParameters(handAndFeetParams, deltaTime);
Rig::CharacterControllerState ccState = convertCharacterControllerState(myAvatar->getCharacterController()->getState()); Rig::CharacterControllerState ccState = convertCharacterControllerState(myAvatar->getCharacterController()->getState());
auto velocity = myAvatar->getLocalVelocity(); auto velocity = myAvatar->getLocalVelocity();
auto position = myAvatar->getLocalPosition(); auto position = myAvatar->getLocalPosition();
auto orientation = myAvatar->getLocalOrientation(); auto orientation = myAvatar->getLocalOrientation();
_rig->computeMotionAnimationState(deltaTime, position, velocity, orientation, ccState); _rig.computeMotionAnimationState(deltaTime, position, velocity, orientation, ccState);
// evaluate AnimGraph animation and update jointStates. // evaluate AnimGraph animation and update jointStates.
Model::updateRig(deltaTime, parentTransform); Model::updateRig(deltaTime, parentTransform);
@ -158,6 +158,6 @@ void MySkeletonModel::updateRig(float deltaTime, glm::mat4 parentTransform) {
eyeParams.leftEyeJointIndex = geometry.leftEyeJointIndex; eyeParams.leftEyeJointIndex = geometry.leftEyeJointIndex;
eyeParams.rightEyeJointIndex = geometry.rightEyeJointIndex; eyeParams.rightEyeJointIndex = geometry.rightEyeJointIndex;
_rig->updateFromEyeParameters(eyeParams); _rig.updateFromEyeParameters(eyeParams);
} }

View file

@ -19,7 +19,7 @@ private:
using Parent = SkeletonModel; using Parent = SkeletonModel;
public: public:
MySkeletonModel(Avatar* owningAvatar, QObject* parent = nullptr, RigPointer rig = nullptr); MySkeletonModel(Avatar* owningAvatar, QObject* parent = nullptr);
void updateRig(float deltaTime, glm::mat4 parentTransform) override; void updateRig(float deltaTime, glm::mat4 parentTransform) override;
}; };

View file

@ -192,6 +192,7 @@ int main(int argc, const char* argv[]) {
int exitCode; int exitCode;
{ {
RunningMarker runningMarker(nullptr, RUNNING_MARKER_FILENAME); RunningMarker runningMarker(nullptr, RUNNING_MARKER_FILENAME);
bool runningMarkerExisted = runningMarker.fileExists();
runningMarker.writeRunningMarkerFile(); runningMarker.writeRunningMarkerFile();
bool noUpdater = parser.isSet(noUpdaterOption); bool noUpdater = parser.isSet(noUpdaterOption);
@ -202,7 +203,7 @@ int main(int argc, const char* argv[]) {
SandboxUtils::runLocalSandbox(serverContentPath, true, RUNNING_MARKER_FILENAME, noUpdater); SandboxUtils::runLocalSandbox(serverContentPath, true, RUNNING_MARKER_FILENAME, noUpdater);
} }
Application app(argc, const_cast<char**>(argv), startupTime); Application app(argc, const_cast<char**>(argv), startupTime, runningMarkerExisted);
// Now that the main event loop is setup, launch running marker thread // Now that the main event loop is setup, launch running marker thread
runningMarker.startRunningMarker(); runningMarker.startRunningMarker();

View file

@ -51,6 +51,7 @@ private:
static void processFrames(); static void processFrames();
public: public:
static void saveSnapshotAnimated(QString pathStill, float aspectRatio, Application* app, QSharedPointer<WindowScriptingInterface> dm); static void saveSnapshotAnimated(QString pathStill, float aspectRatio, Application* app, QSharedPointer<WindowScriptingInterface> dm);
static bool isAlreadyTakingSnapshotAnimated() { return snapshotAnimatedFirstFrameTimestamp != 0; };
static Setting::Handle<bool> alsoTakeAnimatedSnapshot; static Setting::Handle<bool> alsoTakeAnimatedSnapshot;
static Setting::Handle<float> snapshotAnimatedDuration; static Setting::Handle<float> snapshotAnimatedDuration;
}; };

View file

@ -18,7 +18,7 @@
QString const ModelOverlay::TYPE = "model"; QString const ModelOverlay::TYPE = "model";
ModelOverlay::ModelOverlay() ModelOverlay::ModelOverlay()
: _model(std::make_shared<Model>(std::make_shared<Rig>(), nullptr, this)), : _model(std::make_shared<Model>(nullptr, this)),
_modelTextures(QVariantMap()) _modelTextures(QVariantMap())
{ {
_model->init(); _model->init();
@ -28,7 +28,7 @@ ModelOverlay::ModelOverlay()
ModelOverlay::ModelOverlay(const ModelOverlay* modelOverlay) : ModelOverlay::ModelOverlay(const ModelOverlay* modelOverlay) :
Volume3DOverlay(modelOverlay), Volume3DOverlay(modelOverlay),
_model(std::make_shared<Model>(std::make_shared<Rig>(), nullptr, this)), _model(std::make_shared<Model>(nullptr, this)),
_modelTextures(QVariantMap()), _modelTextures(QVariantMap()),
_url(modelOverlay->_url), _url(modelOverlay->_url),
_updateModel(false), _updateModel(false),
@ -211,12 +211,10 @@ QVariant ModelOverlay::getProperty(const QString& property) {
if (property == "jointNames") { if (property == "jointNames") {
if (_model && _model->isActive()) { if (_model && _model->isActive()) {
// note: going through Rig because Model::getJointNames() (which proxies to FBXGeometry) was always empty // note: going through Rig because Model::getJointNames() (which proxies to FBXGeometry) was always empty
const RigPointer rig = _model->getRig(); const Rig* rig = &(_model->getRig());
if (rig) { return mapJoints<QStringList, QString>([rig](int jointIndex) -> QString {
return mapJoints<QStringList, QString>([rig](int jointIndex) -> QString { return rig->nameOfJoint(jointIndex);
return rig->nameOfJoint(jointIndex); });
});
}
} }
} }

View file

@ -10,10 +10,11 @@
#include "AnimContext.h" #include "AnimContext.h"
AnimContext::AnimContext(bool enableDebugDrawIKTargets, bool enableDebugDrawIKConstraints, AnimContext::AnimContext(bool enableDebugDrawIKTargets, bool enableDebugDrawIKConstraints, bool enableDebugDrawIKChains,
const glm::mat4& geometryToRigMatrix, const glm::mat4& rigToWorldMatrix) : const glm::mat4& geometryToRigMatrix, const glm::mat4& rigToWorldMatrix) :
_enableDebugDrawIKTargets(enableDebugDrawIKTargets), _enableDebugDrawIKTargets(enableDebugDrawIKTargets),
_enableDebugDrawIKConstraints(enableDebugDrawIKConstraints), _enableDebugDrawIKConstraints(enableDebugDrawIKConstraints),
_enableDebugDrawIKChains(enableDebugDrawIKChains),
_geometryToRigMatrix(geometryToRigMatrix), _geometryToRigMatrix(geometryToRigMatrix),
_rigToWorldMatrix(rigToWorldMatrix) _rigToWorldMatrix(rigToWorldMatrix)
{ {

View file

@ -16,18 +16,20 @@
class AnimContext { class AnimContext {
public: public:
AnimContext(bool enableDebugDrawIKTargets, bool enableDebugDrawIKConstraints, AnimContext(bool enableDebugDrawIKTargets, bool enableDebugDrawIKConstraints, bool enableDebugDrawIKChains,
const glm::mat4& geometryToRigMatrix, const glm::mat4& rigToWorldMatrix); const glm::mat4& geometryToRigMatrix, const glm::mat4& rigToWorldMatrix);
bool getEnableDebugDrawIKTargets() const { return _enableDebugDrawIKTargets; } bool getEnableDebugDrawIKTargets() const { return _enableDebugDrawIKTargets; }
bool getEnableDebugDrawIKConstraints() const { return _enableDebugDrawIKConstraints; } bool getEnableDebugDrawIKConstraints() const { return _enableDebugDrawIKConstraints; }
bool getEnableDebugDrawIKChains() const { return _enableDebugDrawIKChains; }
const glm::mat4& getGeometryToRigMatrix() const { return _geometryToRigMatrix; } const glm::mat4& getGeometryToRigMatrix() const { return _geometryToRigMatrix; }
const glm::mat4& getRigToWorldMatrix() const { return _rigToWorldMatrix; } const glm::mat4& getRigToWorldMatrix() const { return _rigToWorldMatrix; }
protected: protected:
bool _enableDebugDrawIKTargets { false }; bool _enableDebugDrawIKTargets { false };
bool _enableDebugDrawIKConstraints{ false }; bool _enableDebugDrawIKConstraints { false };
bool _enableDebugDrawIKChains { false };
glm::mat4 _geometryToRigMatrix; glm::mat4 _geometryToRigMatrix;
glm::mat4 _rigToWorldMatrix; glm::mat4 _rigToWorldMatrix;
}; };

View file

@ -175,7 +175,7 @@ void AnimInverseKinematics::computeTargets(const AnimVariantMap& animVars, std::
} }
} }
void AnimInverseKinematics::solveWithCyclicCoordinateDescent(const std::vector<IKTarget>& targets) { void AnimInverseKinematics::solveWithCyclicCoordinateDescent(const AnimContext& context, const std::vector<IKTarget>& targets) {
// compute absolute poses that correspond to relative target poses // compute absolute poses that correspond to relative target poses
AnimPoseVec absolutePoses; AnimPoseVec absolutePoses;
absolutePoses.resize(_relativePoses.size()); absolutePoses.resize(_relativePoses.size());
@ -193,25 +193,23 @@ void AnimInverseKinematics::solveWithCyclicCoordinateDescent(const std::vector<I
while (maxError > MAX_ERROR_TOLERANCE && numLoops < MAX_IK_LOOPS) { while (maxError > MAX_ERROR_TOLERANCE && numLoops < MAX_IK_LOOPS) {
++numLoops; ++numLoops;
bool debug = context.getEnableDebugDrawIKChains() && numLoops == MAX_IK_LOOPS;
// solve all targets // solve all targets
int lowestMovedIndex = (int)_relativePoses.size();
for (auto& target: targets) { for (auto& target: targets) {
int lowIndex = solveTargetWithCCD(target, absolutePoses); solveTargetWithCCD(context, target, absolutePoses, debug);
if (lowIndex < lowestMovedIndex) {
lowestMovedIndex = lowIndex;
}
} }
// harvest accumulated rotations and apply the average // harvest accumulated rotations and apply the average
for (int i = lowestMovedIndex; i < _maxTargetIndex; ++i) { for (int i = 0; i < (int)_relativePoses.size(); ++i) {
if (_accumulators[i].size() > 0) { if (_accumulators[i].size() > 0) {
_relativePoses[i].rot() = _accumulators[i].getAverage(); _relativePoses[i].rot() = _accumulators[i].getAverage();
_accumulators[i].clear(); _accumulators[i].clear();
} }
} }
// update the absolutePoses that need it (from lowestMovedIndex to _maxTargetIndex) // update the absolutePoses
for (auto i = lowestMovedIndex; i <= _maxTargetIndex; ++i) { for (int i = 0; i < (int)_relativePoses.size(); ++i) {
auto parentIndex = _skeleton->getParentIndex((int)i); auto parentIndex = _skeleton->getParentIndex((int)i);
if (parentIndex != -1) { if (parentIndex != -1) {
absolutePoses[i] = absolutePoses[parentIndex] * _relativePoses[i]; absolutePoses[i] = absolutePoses[parentIndex] * _relativePoses[i];
@ -236,7 +234,9 @@ void AnimInverseKinematics::solveWithCyclicCoordinateDescent(const std::vector<I
for (auto& target: targets) { for (auto& target: targets) {
int tipIndex = target.getIndex(); int tipIndex = target.getIndex();
int parentIndex = _skeleton->getParentIndex(tipIndex); int parentIndex = _skeleton->getParentIndex(tipIndex);
if (parentIndex != -1) {
// update rotationOnly targets that don't lie on the ik chain of other ik targets.
if (parentIndex != -1 && !_accumulators[tipIndex].isDirty() && target.getType() == IKTarget::Type::RotationOnly) {
const glm::quat& targetRotation = target.getRotation(); const glm::quat& targetRotation = target.getRotation();
// compute tip's new parent-relative rotation // compute tip's new parent-relative rotation
// Q = Qp * q --> q' = Qp^ * Q // Q = Qp * q --> q' = Qp^ * Q
@ -254,24 +254,25 @@ void AnimInverseKinematics::solveWithCyclicCoordinateDescent(const std::vector<I
} }
} }
int AnimInverseKinematics::solveTargetWithCCD(const IKTarget& target, AnimPoseVec& absolutePoses) { void AnimInverseKinematics::solveTargetWithCCD(const AnimContext& context, const IKTarget& target, const AnimPoseVec& absolutePoses, bool debug) {
int lowestMovedIndex = (int)_relativePoses.size(); size_t chainDepth = 0;
IKTarget::Type targetType = target.getType(); IKTarget::Type targetType = target.getType();
if (targetType == IKTarget::Type::RotationOnly) { if (targetType == IKTarget::Type::RotationOnly) {
// the final rotation will be enforced after the iterations // the final rotation will be enforced after the iterations
// TODO: solve this correctly // TODO: solve this correctly
return lowestMovedIndex; return;
} }
int tipIndex = target.getIndex(); int tipIndex = target.getIndex();
int pivotIndex = _skeleton->getParentIndex(tipIndex); int pivotIndex = _skeleton->getParentIndex(tipIndex);
if (pivotIndex == -1 || pivotIndex == _hipsIndex) { if (pivotIndex == -1 || pivotIndex == _hipsIndex) {
return lowestMovedIndex; return;
} }
int pivotsParentIndex = _skeleton->getParentIndex(pivotIndex); int pivotsParentIndex = _skeleton->getParentIndex(pivotIndex);
if (pivotsParentIndex == -1) { if (pivotsParentIndex == -1) {
// TODO?: handle case where tip's parent is root? // TODO?: handle case where tip's parent is root?
return lowestMovedIndex; return;
} }
// cache tip's absolute orientation // cache tip's absolute orientation
@ -281,31 +282,46 @@ int AnimInverseKinematics::solveTargetWithCCD(const IKTarget& target, AnimPoseVe
// the tip's parent-relative as we proceed up the chain // the tip's parent-relative as we proceed up the chain
glm::quat tipParentOrientation = absolutePoses[pivotIndex].rot(); glm::quat tipParentOrientation = absolutePoses[pivotIndex].rot();
std::map<int, DebugJoint> debugJointMap;
// NOTE: if this code is removed, the head will remain rigid, causing the spine/hips to thrust forward backward // NOTE: if this code is removed, the head will remain rigid, causing the spine/hips to thrust forward backward
// as the head is nodded. // as the head is nodded.
if (targetType == IKTarget::Type::HmdHead) { if (targetType == IKTarget::Type::HmdHead ||
targetType == IKTarget::Type::RotationAndPosition ||
targetType == IKTarget::Type::HipsRelativeRotationAndPosition) {
// rotate tip directly to target orientation // rotate tip toward target orientation
tipOrientation = target.getRotation(); glm::quat deltaRot = target.getRotation() * glm::inverse(tipOrientation);
glm::quat tipRelativeRotation = glm::inverse(tipParentOrientation) * tipOrientation;
deltaRot *= target.getFlexCoefficient(chainDepth);
glm::normalize(deltaRot);
// compute parent relative rotation
glm::quat tipRelativeRotation = glm::inverse(tipParentOrientation) * deltaRot * tipOrientation;
// then enforce tip's constraint // then enforce tip's constraint
RotationConstraint* constraint = getConstraint(tipIndex); RotationConstraint* constraint = getConstraint(tipIndex);
bool constrained = false;
if (constraint) { if (constraint) {
bool constrained = constraint->apply(tipRelativeRotation); constrained = constraint->apply(tipRelativeRotation);
if (constrained) { if (constrained) {
tipOrientation = tipParentOrientation * tipRelativeRotation; tipOrientation = tipParentOrientation * tipRelativeRotation;
tipRelativeRotation = tipRelativeRotation; tipRelativeRotation = tipRelativeRotation;
} }
} }
// store the relative rotation change in the accumulator // store the relative rotation change in the accumulator
_accumulators[tipIndex].add(tipRelativeRotation, target.getWeight()); _accumulators[tipIndex].add(tipRelativeRotation, target.getWeight());
if (debug) {
debugJointMap[tipIndex] = DebugJoint(tipRelativeRotation, constrained);
}
} }
// cache tip absolute position // cache tip absolute position
glm::vec3 tipPosition = absolutePoses[tipIndex].trans(); glm::vec3 tipPosition = absolutePoses[tipIndex].trans();
size_t chainDepth = 1; chainDepth++;
// descend toward root, pivoting each joint to get tip closer to target position // descend toward root, pivoting each joint to get tip closer to target position
while (pivotIndex != _hipsIndex && pivotsParentIndex != -1) { while (pivotIndex != _hipsIndex && pivotsParentIndex != -1) {
@ -388,15 +404,15 @@ int AnimInverseKinematics::solveTargetWithCCD(const IKTarget& target, AnimPoseVe
// compute joint's new parent-relative rotation after swing // compute joint's new parent-relative rotation after swing
// Q' = dQ * Q and Q = Qp * q --> q' = Qp^ * dQ * Q // Q' = dQ * Q and Q = Qp * q --> q' = Qp^ * dQ * Q
glm::quat newRot = glm::normalize(glm::inverse( glm::quat newRot = glm::normalize(glm::inverse(absolutePoses[pivotsParentIndex].rot()) *
absolutePoses[pivotsParentIndex].rot()) * deltaRotation *
deltaRotation * absolutePoses[pivotIndex].rot());
absolutePoses[pivotIndex].rot());
// enforce pivot's constraint // enforce pivot's constraint
RotationConstraint* constraint = getConstraint(pivotIndex); RotationConstraint* constraint = getConstraint(pivotIndex);
bool constrained = false;
if (constraint) { if (constraint) {
bool constrained = constraint->apply(newRot); constrained = constraint->apply(newRot);
if (constrained) { if (constrained) {
// the constraint will modify the local rotation of the tip so we must // the constraint will modify the local rotation of the tip so we must
// compute the corresponding model-frame deltaRotation // compute the corresponding model-frame deltaRotation
@ -408,9 +424,8 @@ int AnimInverseKinematics::solveTargetWithCCD(const IKTarget& target, AnimPoseVe
// store the relative rotation change in the accumulator // store the relative rotation change in the accumulator
_accumulators[pivotIndex].add(newRot, target.getWeight()); _accumulators[pivotIndex].add(newRot, target.getWeight());
// this joint has been changed so we check to see if it has the lowest index if (debug) {
if (pivotIndex < lowestMovedIndex) { debugJointMap[pivotIndex] = DebugJoint(newRot, constrained);
lowestMovedIndex = pivotIndex;
} }
// keep track of tip's new transform as we descend towards root // keep track of tip's new transform as we descend towards root
@ -423,7 +438,10 @@ int AnimInverseKinematics::solveTargetWithCCD(const IKTarget& target, AnimPoseVe
chainDepth++; chainDepth++;
} }
return lowestMovedIndex;
if (debug) {
debugDrawIKChain(debugJointMap, context);
}
} }
//virtual //virtual
@ -551,7 +569,7 @@ const AnimPoseVec& AnimInverseKinematics::overlay(const AnimVariantMap& animVars
{ {
PROFILE_RANGE_EX(simulation_animation, "ik/ccd", 0xffff00ff, 0); PROFILE_RANGE_EX(simulation_animation, "ik/ccd", 0xffff00ff, 0);
solveWithCyclicCoordinateDescent(targets); solveWithCyclicCoordinateDescent(context, targets);
} }
if (_hipsTargetIndex < 0) { if (_hipsTargetIndex < 0) {
@ -562,6 +580,7 @@ const AnimPoseVec& AnimInverseKinematics::overlay(const AnimVariantMap& animVars
} }
} }
} }
return _relativePoses; return _relativePoses;
} }
@ -658,8 +677,8 @@ static void setEllipticalSwingLimits(SwingTwistConstraint* stConstraint, float l
float dTheta = TWO_PI / NUM_SUBDIVISIONS; float dTheta = TWO_PI / NUM_SUBDIVISIONS;
float theta = 0.0f; float theta = 0.0f;
for (int i = 0; i < NUM_SUBDIVISIONS; i++) { for (int i = 0; i < NUM_SUBDIVISIONS; i++) {
float theta_prime = atanf((lateralSwingPhi / anteriorSwingPhi) * tanf(theta)); float theta_prime = atanf((anteriorSwingPhi / lateralSwingPhi) * tanf(theta));
float phi = (cosf(2.0f * theta_prime) * ((lateralSwingPhi - anteriorSwingPhi) / 2.0f)) + ((lateralSwingPhi + anteriorSwingPhi) / 2.0f); float phi = (cosf(2.0f * theta_prime) * ((anteriorSwingPhi - lateralSwingPhi) / 2.0f)) + ((anteriorSwingPhi + lateralSwingPhi) / 2.0f);
minDots.push_back(cosf(phi)); minDots.push_back(cosf(phi));
theta += dTheta; theta += dTheta;
} }
@ -771,27 +790,36 @@ void AnimInverseKinematics::initConstraints() {
std::vector<glm::vec3> swungDirections; std::vector<glm::vec3> swungDirections;
float deltaTheta = PI / 4.0f; float deltaTheta = PI / 4.0f;
float theta = 0.0f; float theta = 0.0f;
swungDirections.push_back(glm::vec3(mirror * cosf(theta), -0.25f, sinf(theta))); swungDirections.push_back(glm::vec3(mirror * cosf(theta), 1.0f, sinf(theta))); // posterior
theta += deltaTheta; theta += deltaTheta;
swungDirections.push_back(glm::vec3(mirror * cosf(theta), 0.0f, sinf(theta))); swungDirections.push_back(glm::vec3(mirror * cosf(theta), 0.5f, sinf(theta)));
theta += deltaTheta; theta += deltaTheta;
swungDirections.push_back(glm::vec3(mirror * cosf(theta), 0.25f, sinf(theta))); // posterior swungDirections.push_back(glm::vec3(mirror * cosf(theta), 0.25f, sinf(theta)));
theta += deltaTheta; theta += deltaTheta;
swungDirections.push_back(glm::vec3(mirror * cosf(theta), 0.0f, sinf(theta))); swungDirections.push_back(glm::vec3(mirror * cosf(theta), -1.5f, sinf(theta)));
theta += deltaTheta; theta += deltaTheta;
swungDirections.push_back(glm::vec3(mirror * cosf(theta), -0.25f, sinf(theta))); swungDirections.push_back(glm::vec3(mirror * cosf(theta), -3.0f, sinf(theta))); // anterior
theta += deltaTheta; theta += deltaTheta;
swungDirections.push_back(glm::vec3(mirror * cosf(theta), -0.5f, sinf(theta))); swungDirections.push_back(glm::vec3(mirror * cosf(theta), -1.5f, sinf(theta)));
theta += deltaTheta; theta += deltaTheta;
swungDirections.push_back(glm::vec3(mirror * cosf(theta), -0.5f, sinf(theta))); // anterior swungDirections.push_back(glm::vec3(mirror * cosf(theta), 0.25f, sinf(theta)));
theta += deltaTheta; theta += deltaTheta;
swungDirections.push_back(glm::vec3(mirror * cosf(theta), -0.5f, sinf(theta))); swungDirections.push_back(glm::vec3(mirror * cosf(theta), 0.5f, sinf(theta)));
std::vector<float> minDots; std::vector<float> minDots;
for (size_t i = 0; i < swungDirections.size(); i++) { for (size_t i = 0; i < swungDirections.size(); i++) {
minDots.push_back(glm::dot(glm::normalize(swungDirections[i]), Vectors::UNIT_Y)); minDots.push_back(glm::dot(glm::normalize(swungDirections[i]), Vectors::UNIT_Y));
} }
stConstraint->setSwingLimits(minDots); stConstraint->setSwingLimits(minDots);
/*
// simple cone
std::vector<float> minDots;
const float MAX_HAND_SWING = 2.9f; // 170 deg //2 * PI / 3.0f;
minDots.push_back(cosf(MAX_HAND_SWING));
stConstraint->setSwingLimits(minDots);
*/
constraint = static_cast<RotationConstraint*>(stConstraint); constraint = static_cast<RotationConstraint*>(stConstraint);
} else if (0 == baseName.compare("Hand", Qt::CaseSensitive)) { } else if (0 == baseName.compare("Hand", Qt::CaseSensitive)) {
SwingTwistConstraint* stConstraint = new SwingTwistConstraint(); SwingTwistConstraint* stConstraint = new SwingTwistConstraint();
@ -839,11 +867,11 @@ void AnimInverseKinematics::initConstraints() {
} else if (baseName.startsWith("Shoulder", Qt::CaseSensitive)) { } else if (baseName.startsWith("Shoulder", Qt::CaseSensitive)) {
SwingTwistConstraint* stConstraint = new SwingTwistConstraint(); SwingTwistConstraint* stConstraint = new SwingTwistConstraint();
stConstraint->setReferenceRotation(_defaultRelativePoses[i].rot()); stConstraint->setReferenceRotation(_defaultRelativePoses[i].rot());
const float MAX_SHOULDER_TWIST = PI / 20.0f; const float MAX_SHOULDER_TWIST = PI / 10.0f;
stConstraint->setTwistLimits(-MAX_SHOULDER_TWIST, MAX_SHOULDER_TWIST); stConstraint->setTwistLimits(-MAX_SHOULDER_TWIST, MAX_SHOULDER_TWIST);
std::vector<float> minDots; std::vector<float> minDots;
const float MAX_SHOULDER_SWING = PI / 16.0f; const float MAX_SHOULDER_SWING = PI / 12.0f;
minDots.push_back(cosf(MAX_SHOULDER_SWING)); minDots.push_back(cosf(MAX_SHOULDER_SWING));
stConstraint->setSwingLimits(minDots); stConstraint->setSwingLimits(minDots);
@ -855,8 +883,8 @@ void AnimInverseKinematics::initConstraints() {
stConstraint->setTwistLimits(-MAX_SPINE_TWIST, MAX_SPINE_TWIST); stConstraint->setTwistLimits(-MAX_SPINE_TWIST, MAX_SPINE_TWIST);
// limit lateral swings more then forward-backward swings // limit lateral swings more then forward-backward swings
const float MAX_SPINE_LATERAL_SWING = PI / 30.0f; const float MAX_SPINE_LATERAL_SWING = PI / 15.0f;
const float MAX_SPINE_ANTERIOR_SWING = PI / 20.0f; const float MAX_SPINE_ANTERIOR_SWING = PI / 10.0f;
setEllipticalSwingLimits(stConstraint, MAX_SPINE_LATERAL_SWING, MAX_SPINE_ANTERIOR_SWING); setEllipticalSwingLimits(stConstraint, MAX_SPINE_LATERAL_SWING, MAX_SPINE_ANTERIOR_SWING);
if (0 == baseName.compare("Spine1", Qt::CaseSensitive) if (0 == baseName.compare("Spine1", Qt::CaseSensitive)
@ -869,12 +897,12 @@ void AnimInverseKinematics::initConstraints() {
} else if (0 == baseName.compare("Neck", Qt::CaseSensitive)) { } else if (0 == baseName.compare("Neck", Qt::CaseSensitive)) {
SwingTwistConstraint* stConstraint = new SwingTwistConstraint(); SwingTwistConstraint* stConstraint = new SwingTwistConstraint();
stConstraint->setReferenceRotation(_defaultRelativePoses[i].rot()); stConstraint->setReferenceRotation(_defaultRelativePoses[i].rot());
const float MAX_NECK_TWIST = PI / 10.0f; const float MAX_NECK_TWIST = PI / 8.0f;
stConstraint->setTwistLimits(-MAX_NECK_TWIST, MAX_NECK_TWIST); stConstraint->setTwistLimits(-MAX_NECK_TWIST, MAX_NECK_TWIST);
// limit lateral swings more then forward-backward swings // limit lateral swings more then forward-backward swings
const float MAX_NECK_LATERAL_SWING = PI / 10.0f; const float MAX_NECK_LATERAL_SWING = PI / 12.0f;
const float MAX_NECK_ANTERIOR_SWING = PI / 8.0f; const float MAX_NECK_ANTERIOR_SWING = PI / 10.0f;
setEllipticalSwingLimits(stConstraint, MAX_NECK_LATERAL_SWING, MAX_NECK_ANTERIOR_SWING); setEllipticalSwingLimits(stConstraint, MAX_NECK_LATERAL_SWING, MAX_NECK_ANTERIOR_SWING);
constraint = static_cast<RotationConstraint*>(stConstraint); constraint = static_cast<RotationConstraint*>(stConstraint);
@ -884,10 +912,10 @@ void AnimInverseKinematics::initConstraints() {
const float MAX_HEAD_TWIST = PI / 6.0f; const float MAX_HEAD_TWIST = PI / 6.0f;
stConstraint->setTwistLimits(-MAX_HEAD_TWIST, MAX_HEAD_TWIST); stConstraint->setTwistLimits(-MAX_HEAD_TWIST, MAX_HEAD_TWIST);
std::vector<float> minDots; // limit lateral swings more then forward-backward swings
const float MAX_HEAD_SWING = PI / 6.0f; const float MAX_NECK_LATERAL_SWING = PI / 4.0f;
minDots.push_back(cosf(MAX_HEAD_SWING)); const float MAX_NECK_ANTERIOR_SWING = PI / 3.0f;
stConstraint->setSwingLimits(minDots); setEllipticalSwingLimits(stConstraint, MAX_NECK_LATERAL_SWING, MAX_NECK_ANTERIOR_SWING);
constraint = static_cast<RotationConstraint*>(stConstraint); constraint = static_cast<RotationConstraint*>(stConstraint);
} else if (0 == baseName.compare("ForeArm", Qt::CaseSensitive)) { } else if (0 == baseName.compare("ForeArm", Qt::CaseSensitive)) {
@ -1044,7 +1072,96 @@ void AnimInverseKinematics::setSkeletonInternal(AnimSkeleton::ConstPointer skele
static glm::vec3 sphericalToCartesian(float phi, float theta) { static glm::vec3 sphericalToCartesian(float phi, float theta) {
float cos_phi = cosf(phi); float cos_phi = cosf(phi);
float sin_phi = sinf(phi); float sin_phi = sinf(phi);
return glm::vec3(sin_phi * cosf(theta), cos_phi, -sin_phi * sinf(theta)); return glm::vec3(sin_phi * cosf(theta), cos_phi, sin_phi * sinf(theta));
}
void AnimInverseKinematics::debugDrawRelativePoses(const AnimContext& context) const {
AnimPoseVec poses = _relativePoses;
// convert relative poses to absolute
_skeleton->convertRelativePosesToAbsolute(poses);
mat4 geomToWorldMatrix = context.getRigToWorldMatrix() * context.getGeometryToRigMatrix();
const vec4 RED(1.0f, 0.0f, 0.0f, 1.0f);
const vec4 GREEN(0.0f, 1.0f, 0.0f, 1.0f);
const vec4 BLUE(0.0f, 0.0f, 1.0f, 1.0f);
const vec4 GRAY(0.2f, 0.2f, 0.2f, 1.0f);
const float AXIS_LENGTH = 2.0f; // cm
// draw each pose
for (int i = 0; i < (int)poses.size(); i++) {
// transform local axes into world space.
auto pose = poses[i];
glm::vec3 xAxis = transformVectorFast(geomToWorldMatrix, pose.rot() * Vectors::UNIT_X);
glm::vec3 yAxis = transformVectorFast(geomToWorldMatrix, pose.rot() * Vectors::UNIT_Y);
glm::vec3 zAxis = transformVectorFast(geomToWorldMatrix, pose.rot() * Vectors::UNIT_Z);
glm::vec3 pos = transformPoint(geomToWorldMatrix, pose.trans());
DebugDraw::getInstance().drawRay(pos, pos + AXIS_LENGTH * xAxis, RED);
DebugDraw::getInstance().drawRay(pos, pos + AXIS_LENGTH * yAxis, GREEN);
DebugDraw::getInstance().drawRay(pos, pos + AXIS_LENGTH * zAxis, BLUE);
// draw line to parent
int parentIndex = _skeleton->getParentIndex(i);
if (parentIndex != -1) {
glm::vec3 parentPos = transformPoint(geomToWorldMatrix, poses[parentIndex].trans());
DebugDraw::getInstance().drawRay(pos, parentPos, GRAY);
}
}
}
void AnimInverseKinematics::debugDrawIKChain(std::map<int, DebugJoint>& debugJointMap, const AnimContext& context) const {
AnimPoseVec poses = _relativePoses;
// copy debug joint rotations into the relative poses
for (auto& debugJoint : debugJointMap) {
poses[debugJoint.first].rot() = debugJoint.second.relRot;
}
// convert relative poses to absolute
_skeleton->convertRelativePosesToAbsolute(poses);
mat4 geomToWorldMatrix = context.getRigToWorldMatrix() * context.getGeometryToRigMatrix();
const vec4 RED(1.0f, 0.0f, 0.0f, 1.0f);
const vec4 GREEN(0.0f, 1.0f, 0.0f, 1.0f);
const vec4 BLUE(0.0f, 0.0f, 1.0f, 1.0f);
const vec4 GRAY(0.2f, 0.2f, 0.2f, 1.0f);
const float AXIS_LENGTH = 2.0f; // cm
// draw each pose
for (int i = 0; i < (int)poses.size(); i++) {
// only draw joints that are actually in debugJointMap, or their parents
auto iter = debugJointMap.find(i);
auto parentIter = debugJointMap.find(_skeleton->getParentIndex(i));
if (iter != debugJointMap.end() || parentIter != debugJointMap.end()) {
// transform local axes into world space.
auto pose = poses[i];
glm::vec3 xAxis = transformVectorFast(geomToWorldMatrix, pose.rot() * Vectors::UNIT_X);
glm::vec3 yAxis = transformVectorFast(geomToWorldMatrix, pose.rot() * Vectors::UNIT_Y);
glm::vec3 zAxis = transformVectorFast(geomToWorldMatrix, pose.rot() * Vectors::UNIT_Z);
glm::vec3 pos = transformPoint(geomToWorldMatrix, pose.trans());
DebugDraw::getInstance().drawRay(pos, pos + AXIS_LENGTH * xAxis, RED);
DebugDraw::getInstance().drawRay(pos, pos + AXIS_LENGTH * yAxis, GREEN);
DebugDraw::getInstance().drawRay(pos, pos + AXIS_LENGTH * zAxis, BLUE);
// draw line to parent
int parentIndex = _skeleton->getParentIndex(i);
if (parentIndex != -1) {
glm::vec3 parentPos = transformPoint(geomToWorldMatrix, poses[parentIndex].trans());
glm::vec4 color = GRAY;
// draw constrained joints with a RED link to their parent.
if (parentIter != debugJointMap.end() && parentIter->second.constrained) {
color = RED;
}
DebugDraw::getInstance().drawRay(pos, parentPos, color);
}
}
}
} }
void AnimInverseKinematics::debugDrawConstraints(const AnimContext& context) const { void AnimInverseKinematics::debugDrawConstraints(const AnimContext& context) const {
@ -1056,20 +1173,12 @@ void AnimInverseKinematics::debugDrawConstraints(const AnimContext& context) con
const vec4 CYAN(0.0f, 1.0f, 1.0f, 1.0f); const vec4 CYAN(0.0f, 1.0f, 1.0f, 1.0f);
const vec4 GRAY(0.2f, 0.2f, 0.2f, 1.0f); const vec4 GRAY(0.2f, 0.2f, 0.2f, 1.0f);
const vec4 MAGENTA(1.0f, 0.0f, 1.0f, 1.0f); const vec4 MAGENTA(1.0f, 0.0f, 1.0f, 1.0f);
const float AXIS_LENGTH = 2.0f; // cm const float AXIS_LENGTH = 5.0f; // cm
const float TWIST_LENGTH = 4.0f; // cm const float TWIST_LENGTH = 4.0f; // cm
const float HINGE_LENGTH = 6.0f; // cm const float HINGE_LENGTH = 4.0f; // cm
const float SWING_LENGTH = 5.0f; // cm const float SWING_LENGTH = 4.0f; // cm
AnimPoseVec poses = _skeleton->getRelativeDefaultPoses(); AnimPoseVec poses = _relativePoses;
// copy reference rotations into the relative poses
for (int i = 0; i < (int)poses.size(); i++) {
const RotationConstraint* constraint = getConstraint(i);
if (constraint) {
poses[i].rot() = constraint->getReferenceRotation();
}
}
// convert relative poses to absolute // convert relative poses to absolute
_skeleton->convertRelativePosesToAbsolute(poses); _skeleton->convertRelativePosesToAbsolute(poses);
@ -1127,8 +1236,8 @@ void AnimInverseKinematics::debugDrawConstraints(const AnimContext& context) con
glm::vec3 hingeAxis = transformVectorFast(geomToWorldMatrix, parentAbsRot * refRot * Vectors::UNIT_Y); glm::vec3 hingeAxis = transformVectorFast(geomToWorldMatrix, parentAbsRot * refRot * Vectors::UNIT_Y);
DebugDraw::getInstance().drawRay(pos, pos + HINGE_LENGTH * hingeAxis, MAGENTA); DebugDraw::getInstance().drawRay(pos, pos + HINGE_LENGTH * hingeAxis, MAGENTA);
glm::quat minRot = glm::angleAxis(swingTwistConstraint->getMinTwist(), Vectors::UNIT_Y); glm::quat minRot = glm::angleAxis(swingTwistConstraint->getMinTwist(), refRot * Vectors::UNIT_Y);
glm::quat maxRot = glm::angleAxis(swingTwistConstraint->getMaxTwist(), Vectors::UNIT_Y); glm::quat maxRot = glm::angleAxis(swingTwistConstraint->getMaxTwist(), refRot * Vectors::UNIT_Y);
const int NUM_SWING_STEPS = 10; const int NUM_SWING_STEPS = 10;
for (int i = 0; i < NUM_SWING_STEPS + 1; i++) { for (int i = 0; i < NUM_SWING_STEPS + 1; i++) {
@ -1140,17 +1249,18 @@ void AnimInverseKinematics::debugDrawConstraints(const AnimContext& context) con
// draw swing constraints. // draw swing constraints.
const size_t NUM_MIN_DOTS = swingTwistConstraint->getMinDots().size(); const size_t NUM_MIN_DOTS = swingTwistConstraint->getMinDots().size();
const float D_THETA = TWO_PI / (NUM_MIN_DOTS - 1); const float D_THETA = TWO_PI / (NUM_MIN_DOTS - 1);
const float PI_2 = PI / 2.0f;
float theta = 0.0f; float theta = 0.0f;
for (size_t i = 0, j = NUM_MIN_DOTS - 2; i < NUM_MIN_DOTS - 1; j = i, i++, theta += D_THETA) { for (size_t i = 0, j = NUM_MIN_DOTS - 2; i < NUM_MIN_DOTS - 1; j = i, i++, theta += D_THETA) {
// compute swing rotation from theta and phi angles. // compute swing rotation from theta and phi angles.
float phi = acosf(swingTwistConstraint->getMinDots()[i]); float phi = acosf(swingTwistConstraint->getMinDots()[i]);
glm::vec3 swungAxis = sphericalToCartesian(phi, theta); glm::vec3 swungAxis = sphericalToCartesian(phi, theta - PI_2);
glm::vec3 worldSwungAxis = transformVectorFast(geomToWorldMatrix, parentAbsRot * refRot * swungAxis); glm::vec3 worldSwungAxis = transformVectorFast(geomToWorldMatrix, parentAbsRot * refRot * swungAxis);
glm::vec3 swingTip = pos + SWING_LENGTH * worldSwungAxis; glm::vec3 swingTip = pos + SWING_LENGTH * worldSwungAxis;
float prevPhi = acos(swingTwistConstraint->getMinDots()[j]); float prevPhi = acos(swingTwistConstraint->getMinDots()[j]);
float prevTheta = theta - D_THETA; float prevTheta = theta - D_THETA;
glm::vec3 prevSwungAxis = sphericalToCartesian(prevPhi, prevTheta); glm::vec3 prevSwungAxis = sphericalToCartesian(prevPhi, prevTheta - PI_2);
glm::vec3 prevWorldSwungAxis = transformVectorFast(geomToWorldMatrix, parentAbsRot * refRot * prevSwungAxis); glm::vec3 prevWorldSwungAxis = transformVectorFast(geomToWorldMatrix, parentAbsRot * refRot * prevSwungAxis);
glm::vec3 prevSwingTip = pos + SWING_LENGTH * prevWorldSwungAxis; glm::vec3 prevSwingTip = pos + SWING_LENGTH * prevWorldSwungAxis;
@ -1159,7 +1269,6 @@ void AnimInverseKinematics::debugDrawConstraints(const AnimContext& context) con
} }
} }
} }
pose.rot() = constraint->computeCenterRotation();
} }
} }
} }

View file

@ -58,13 +58,22 @@ public:
protected: protected:
void computeTargets(const AnimVariantMap& animVars, std::vector<IKTarget>& targets, const AnimPoseVec& underPoses); void computeTargets(const AnimVariantMap& animVars, std::vector<IKTarget>& targets, const AnimPoseVec& underPoses);
void solveWithCyclicCoordinateDescent(const std::vector<IKTarget>& targets); void solveWithCyclicCoordinateDescent(const AnimContext& context, const std::vector<IKTarget>& targets);
int solveTargetWithCCD(const IKTarget& target, AnimPoseVec& absolutePoses); void solveTargetWithCCD(const AnimContext& context, const IKTarget& target, const AnimPoseVec& absolutePoses, bool debug);
virtual void setSkeletonInternal(AnimSkeleton::ConstPointer skeleton) override; virtual void setSkeletonInternal(AnimSkeleton::ConstPointer skeleton) override;
struct DebugJoint {
DebugJoint() : relRot(), constrained(false) {}
DebugJoint(const glm::quat& relRotIn, bool constrainedIn) : relRot(relRotIn), constrained(constrainedIn) {}
glm::quat relRot;
bool constrained;
};
void debugDrawIKChain(std::map<int, DebugJoint>& debugJointMap, const AnimContext& context) const;
void debugDrawRelativePoses(const AnimContext& context) const;
void debugDrawConstraints(const AnimContext& context) const; void debugDrawConstraints(const AnimContext& context) const;
void initRelativePosesFromSolutionSource(SolutionSource solutionSource, const AnimPoseVec& underPose); void initRelativePosesFromSolutionSource(SolutionSource solutionSource, const AnimPoseVec& underPose);
void blendToPoses(const AnimPoseVec& targetPoses, const AnimPoseVec& underPose, float blendFactor); void blendToPoses(const AnimPoseVec& targetPoses, const AnimPoseVec& underPose, float blendFactor);
// for AnimDebugDraw rendering // for AnimDebugDraw rendering
virtual const AnimPoseVec& getPosesInternal() const override { return _relativePoses; } virtual const AnimPoseVec& getPosesInternal() const override { return _relativePoses; }

View file

@ -23,7 +23,6 @@ void IKTarget::setFlexCoefficients(size_t numFlexCoefficientsIn, const float* fl
float IKTarget::getFlexCoefficient(size_t chainDepth) const { float IKTarget::getFlexCoefficient(size_t chainDepth) const {
const float DEFAULT_FLEX_COEFFICIENT = 0.5f; const float DEFAULT_FLEX_COEFFICIENT = 0.5f;
if (chainDepth < _numFlexCoefficients) { if (chainDepth < _numFlexCoefficients) {
return _flexCoefficients[chainDepth]; return _flexCoefficients[chainDepth];
} else { } else {

View file

@ -954,7 +954,7 @@ void Rig::updateAnimations(float deltaTime, const glm::mat4& rootTransform, cons
updateAnimationStateHandlers(); updateAnimationStateHandlers();
_animVars.setRigToGeometryTransform(_rigToGeometryTransform); _animVars.setRigToGeometryTransform(_rigToGeometryTransform);
AnimContext context(_enableDebugDrawIKTargets, _enableDebugDrawIKConstraints, AnimContext context(_enableDebugDrawIKTargets, _enableDebugDrawIKConstraints, _enableDebugDrawIKChains,
getGeometryToRigTransform(), rigToWorldTransform); getGeometryToRigTransform(), rigToWorldTransform);
// evaluate the animation // evaluate the animation
@ -1403,7 +1403,6 @@ void Rig::computeAvatarBoundingCapsule(
AnimInverseKinematics ikNode("boundingShape"); AnimInverseKinematics ikNode("boundingShape");
ikNode.setSkeleton(_animSkeleton); ikNode.setSkeleton(_animSkeleton);
// AJT: FIX ME!!!!! ensure that empty weights vector does something reasonable....
ikNode.setTargetVars("LeftHand", ikNode.setTargetVars("LeftHand",
"leftHandPosition", "leftHandPosition",
"leftHandRotation", "leftHandRotation",
@ -1452,7 +1451,7 @@ void Rig::computeAvatarBoundingCapsule(
// call overlay twice: once to verify AnimPoseVec joints and again to do the IK // call overlay twice: once to verify AnimPoseVec joints and again to do the IK
AnimNode::Triggers triggersOut; AnimNode::Triggers triggersOut;
AnimContext context(false, false, glm::mat4(), glm::mat4()); AnimContext context(false, false, false, glm::mat4(), glm::mat4());
float dt = 1.0f; // the value of this does not matter float dt = 1.0f; // the value of this does not matter
ikNode.overlay(animVars, context, dt, triggersOut, _animSkeleton->getRelativeBindPoses()); ikNode.overlay(animVars, context, dt, triggersOut, _animSkeleton->getRelativeBindPoses());
AnimPoseVec finalPoses = ikNode.overlay(animVars, context, dt, triggersOut, _animSkeleton->getRelativeBindPoses()); AnimPoseVec finalPoses = ikNode.overlay(animVars, context, dt, triggersOut, _animSkeleton->getRelativeBindPoses());

View file

@ -27,7 +27,6 @@
class Rig; class Rig;
class AnimInverseKinematics; class AnimInverseKinematics;
typedef std::shared_ptr<Rig> RigPointer;
// Rig instances are reentrant. // Rig instances are reentrant.
// However only specific methods thread-safe. Noted below. // However only specific methods thread-safe. Noted below.
@ -232,6 +231,7 @@ public:
void setEnableDebugDrawIKTargets(bool enableDebugDrawIKTargets) { _enableDebugDrawIKTargets = enableDebugDrawIKTargets; } void setEnableDebugDrawIKTargets(bool enableDebugDrawIKTargets) { _enableDebugDrawIKTargets = enableDebugDrawIKTargets; }
void setEnableDebugDrawIKConstraints(bool enableDebugDrawIKConstraints) { _enableDebugDrawIKConstraints = enableDebugDrawIKConstraints; } void setEnableDebugDrawIKConstraints(bool enableDebugDrawIKConstraints) { _enableDebugDrawIKConstraints = enableDebugDrawIKConstraints; }
void setEnableDebugDrawIKChains(bool enableDebugDrawIKChains) { _enableDebugDrawIKChains = enableDebugDrawIKChains; }
// input assumed to be in rig space // input assumed to be in rig space
void computeHeadFromHMD(const AnimPose& hmdPose, glm::vec3& headPositionOut, glm::quat& headOrientationOut) const; void computeHeadFromHMD(const AnimPose& hmdPose, glm::vec3& headPositionOut, glm::quat& headOrientationOut) const;
@ -343,6 +343,7 @@ protected:
bool _enableDebugDrawIKTargets { false }; bool _enableDebugDrawIKTargets { false };
bool _enableDebugDrawIKConstraints { false }; bool _enableDebugDrawIKConstraints { false };
bool _enableDebugDrawIKChains { false };
private: private:
QMap<int, StateHandler> _stateHandlers; QMap<int, StateHandler> _stateHandlers;

View file

@ -1506,6 +1506,7 @@ bool AudioClient::switchOutputToAudioDevice(const QAudioDeviceInfo& outputDevice
// cleanup any previously initialized device // cleanup any previously initialized device
if (_audioOutput) { if (_audioOutput) {
_audioOutputIODevice.close();
_audioOutput->stop(); _audioOutput->stop();
//must be deleted in next eventloop cycle when its called from notify() //must be deleted in next eventloop cycle when its called from notify()

View file

@ -94,7 +94,6 @@ public:
_audio(audio), _unfulfilledReads(0) {} _audio(audio), _unfulfilledReads(0) {}
void start() { open(QIODevice::ReadOnly | QIODevice::Unbuffered); } void start() { open(QIODevice::ReadOnly | QIODevice::Unbuffered); }
void stop() { close(); }
qint64 readData(char * data, qint64 maxSize) override; qint64 readData(char * data, qint64 maxSize) override;
qint64 writeData(const char * data, qint64 maxSize) override { return 0; } qint64 writeData(const char * data, qint64 maxSize) override { return 0; }
int getRecentUnfulfilledReads() { int unfulfilledReads = _unfulfilledReads; _unfulfilledReads = 0; return unfulfilledReads; } int getRecentUnfulfilledReads() { int unfulfilledReads = _unfulfilledReads; _unfulfilledReads = 0; return unfulfilledReads; }

View file

@ -98,7 +98,7 @@ void Avatar::setShowNamesAboveHeads(bool show) {
showNamesAboveHeads = show; showNamesAboveHeads = show;
} }
Avatar::Avatar(QThread* thread, RigPointer rig) : Avatar::Avatar(QThread* thread) :
_voiceSphereID(GeometryCache::UNKNOWN_ID) _voiceSphereID(GeometryCache::UNKNOWN_ID)
{ {
// we may have been created in the network thread, but we live in the main thread // we may have been created in the network thread, but we live in the main thread
@ -344,9 +344,9 @@ void Avatar::simulate(float deltaTime, bool inView) {
if (inView) { if (inView) {
Head* head = getHead(); Head* head = getHead();
if (_hasNewJointData) { if (_hasNewJointData) {
_skeletonModel->getRig()->copyJointsFromJointData(_jointData); _skeletonModel->getRig().copyJointsFromJointData(_jointData);
glm::mat4 rootTransform = glm::scale(_skeletonModel->getScale()) * glm::translate(_skeletonModel->getOffset()); glm::mat4 rootTransform = glm::scale(_skeletonModel->getScale()) * glm::translate(_skeletonModel->getOffset());
_skeletonModel->getRig()->computeExternalPoses(rootTransform); _skeletonModel->getRig().computeExternalPoses(rootTransform);
_jointDataSimulationRate.increment(); _jointDataSimulationRate.increment();
_skeletonModel->simulate(deltaTime, true); _skeletonModel->simulate(deltaTime, true);
@ -907,17 +907,16 @@ glm::vec3 Avatar::getDefaultJointTranslation(int index) const {
glm::quat Avatar::getAbsoluteDefaultJointRotationInObjectFrame(int index) const { glm::quat Avatar::getAbsoluteDefaultJointRotationInObjectFrame(int index) const {
glm::quat rotation; glm::quat rotation;
auto rig = _skeletonModel->getRig(); glm::quat rot = _skeletonModel->getRig().getAnimSkeleton()->getAbsoluteDefaultPose(index).rot();
glm::quat rot = rig->getAnimSkeleton()->getAbsoluteDefaultPose(index).rot();
return Quaternions::Y_180 * rot; return Quaternions::Y_180 * rot;
} }
glm::vec3 Avatar::getAbsoluteDefaultJointTranslationInObjectFrame(int index) const { glm::vec3 Avatar::getAbsoluteDefaultJointTranslationInObjectFrame(int index) const {
glm::vec3 translation; glm::vec3 translation;
auto rig = _skeletonModel->getRig(); const Rig& rig = _skeletonModel->getRig();
glm::vec3 trans = rig->getAnimSkeleton()->getAbsoluteDefaultPose(index).trans(); glm::vec3 trans = rig.getAnimSkeleton()->getAbsoluteDefaultPose(index).trans();
glm::mat4 y180Mat = createMatFromQuatAndPos(Quaternions::Y_180, glm::vec3()); glm::mat4 y180Mat = createMatFromQuatAndPos(Quaternions::Y_180, glm::vec3());
return transformPoint(y180Mat * rig->getGeometryToRigTransform(), trans); return transformPoint(y180Mat * rig.getGeometryToRigTransform(), trans);
} }
glm::quat Avatar::getAbsoluteJointRotationInObjectFrame(int index) const { glm::quat Avatar::getAbsoluteJointRotationInObjectFrame(int index) const {
@ -1083,16 +1082,16 @@ void Avatar::setModelURLFinished(bool success) {
// create new model, can return an instance of a SoftAttachmentModel rather then Model // create new model, can return an instance of a SoftAttachmentModel rather then Model
static std::shared_ptr<Model> allocateAttachmentModel(bool isSoft, RigPointer rigOverride, bool isCauterized) { static std::shared_ptr<Model> allocateAttachmentModel(bool isSoft, const Rig& rigOverride, bool isCauterized) {
if (isSoft) { if (isSoft) {
// cast to std::shared_ptr<Model> // cast to std::shared_ptr<Model>
std::shared_ptr<SoftAttachmentModel> softModel = std::make_shared<SoftAttachmentModel>(std::make_shared<Rig>(), nullptr, rigOverride); std::shared_ptr<SoftAttachmentModel> softModel = std::make_shared<SoftAttachmentModel>(nullptr, rigOverride);
if (isCauterized) { if (isCauterized) {
softModel->flagAsCauterized(); softModel->flagAsCauterized();
} }
return std::dynamic_pointer_cast<Model>(softModel); return std::dynamic_pointer_cast<Model>(softModel);
} else { } else {
return std::make_shared<Model>(std::make_shared<Rig>()); return std::make_shared<Model>();
} }
} }
@ -1409,21 +1408,19 @@ void Avatar::setParentJointIndex(quint16 parentJointIndex) {
QList<QVariant> Avatar::getSkeleton() { QList<QVariant> Avatar::getSkeleton() {
SkeletonModelPointer skeletonModel = _skeletonModel; SkeletonModelPointer skeletonModel = _skeletonModel;
if (skeletonModel) { if (skeletonModel) {
RigPointer rig = skeletonModel->getRig(); const Rig& rig = skeletonModel->getRig();
if (rig) { AnimSkeleton::ConstPointer skeleton = rig.getAnimSkeleton();
AnimSkeleton::ConstPointer skeleton = rig->getAnimSkeleton(); if (skeleton) {
if (skeleton) { QList<QVariant> list;
QList<QVariant> list; list.reserve(skeleton->getNumJoints());
list.reserve(skeleton->getNumJoints()); for (int i = 0; i < skeleton->getNumJoints(); i++) {
for (int i = 0; i < skeleton->getNumJoints(); i++) { QVariantMap obj;
QVariantMap obj; obj["name"] = skeleton->getJointName(i);
obj["name"] = skeleton->getJointName(i); obj["index"] = i;
obj["index"] = i; obj["parentIndex"] = skeleton->getParentIndex(i);
obj["parentIndex"] = skeleton->getParentIndex(i); list.push_back(obj);
list.push_back(obj);
}
return list;
} }
return list;
} }
} }

View file

@ -75,7 +75,7 @@ public:
static void setShowCollisionShapes(bool render); static void setShowCollisionShapes(bool render);
static void setShowNamesAboveHeads(bool show); static void setShowNamesAboveHeads(bool show);
explicit Avatar(QThread* thread, RigPointer rig = nullptr); explicit Avatar(QThread* thread);
~Avatar(); ~Avatar();
virtual void instantiableAvatar() = 0; virtual void instantiableAvatar() = 0;

View file

@ -23,8 +23,6 @@
#include "Avatar.h" #include "Avatar.h"
const float NORMAL_HZ = 60.0f; // the update rate the constant values were tuned for
using namespace std; using namespace std;
static bool disableEyelidAdjustment { false }; static bool disableEyelidAdjustment { false };
@ -43,7 +41,9 @@ void Head::reset() {
_baseYaw = _basePitch = _baseRoll = 0.0f; _baseYaw = _basePitch = _baseRoll = 0.0f;
} }
void Head::computeAudioLoudness(float deltaTime) { void Head::simulate(float deltaTime) {
const float NORMAL_HZ = 60.0f; // the update rate the constant values were tuned for
// grab the audio loudness from the owning avatar, if we have one // grab the audio loudness from the owning avatar, if we have one
float audioLoudness = _owningAvatar ? _owningAvatar->getAudioLoudness() : 0.0f; float audioLoudness = _owningAvatar ? _owningAvatar->getAudioLoudness() : 0.0f;
@ -58,99 +58,102 @@ void Head::computeAudioLoudness(float deltaTime) {
_longTermAverageLoudness = glm::mix(_longTermAverageLoudness, _averageLoudness, glm::min(deltaTime / AUDIO_LONG_TERM_AVERAGING_SECS, 1.0f)); _longTermAverageLoudness = glm::mix(_longTermAverageLoudness, _averageLoudness, glm::min(deltaTime / AUDIO_LONG_TERM_AVERAGING_SECS, 1.0f));
} }
float audioAttackAveragingRate = (10.0f - deltaTime * NORMAL_HZ) / 10.0f; // --> 0.9 at 60 Hz if (!_isFaceTrackerConnected) {
_audioAttack = audioAttackAveragingRate * _audioAttack + if (!_isEyeTrackerConnected) {
(1.0f - audioAttackAveragingRate) * fabs((audioLoudness - _longTermAverageLoudness) - _lastLoudness); // Update eye saccades
_lastLoudness = (audioLoudness - _longTermAverageLoudness); const float AVERAGE_MICROSACCADE_INTERVAL = 1.0f;
} const float AVERAGE_SACCADE_INTERVAL = 6.0f;
const float MICROSACCADE_MAGNITUDE = 0.002f;
const float SACCADE_MAGNITUDE = 0.04f;
const float NOMINAL_FRAME_RATE = 60.0f;
void Head::computeEyeMovement(float deltaTime) { if (randFloat() < deltaTime / AVERAGE_MICROSACCADE_INTERVAL) {
// Update eye saccades _saccadeTarget = MICROSACCADE_MAGNITUDE * randVector();
const float AVERAGE_MICROSACCADE_INTERVAL = 1.0f; } else if (randFloat() < deltaTime / AVERAGE_SACCADE_INTERVAL) {
const float AVERAGE_SACCADE_INTERVAL = 6.0f; _saccadeTarget = SACCADE_MAGNITUDE * randVector();
const float MICROSACCADE_MAGNITUDE = 0.002f; }
const float SACCADE_MAGNITUDE = 0.04f; _saccade += (_saccadeTarget - _saccade) * pow(0.5f, NOMINAL_FRAME_RATE * deltaTime);
const float NOMINAL_FRAME_RATE = 60.0f; } else {
_saccade = glm::vec3();
}
if (randFloat() < deltaTime / AVERAGE_MICROSACCADE_INTERVAL) { // Detect transition from talking to not; force blink after that and a delay
_saccadeTarget = MICROSACCADE_MAGNITUDE * randVector(); bool forceBlink = false;
} else if (randFloat() < deltaTime / AVERAGE_SACCADE_INTERVAL) { const float TALKING_LOUDNESS = 100.0f;
_saccadeTarget = SACCADE_MAGNITUDE * randVector(); const float BLINK_AFTER_TALKING = 0.25f;
} _timeWithoutTalking += deltaTime;
_saccade += (_saccadeTarget - _saccade) * pow(0.5f, NOMINAL_FRAME_RATE * deltaTime); if ((_averageLoudness - _longTermAverageLoudness) > TALKING_LOUDNESS) {
_timeWithoutTalking = 0.0f;
} else if (_timeWithoutTalking - deltaTime < BLINK_AFTER_TALKING && _timeWithoutTalking >= BLINK_AFTER_TALKING) {
forceBlink = true;
}
// Detect transition from talking to not; force blink after that and a delay // Update audio attack data for facial animation (eyebrows and mouth)
bool forceBlink = false; float audioAttackAveragingRate = (10.0f - deltaTime * NORMAL_HZ) / 10.0f; // --> 0.9 at 60 Hz
const float TALKING_LOUDNESS = 100.0f; _audioAttack = audioAttackAveragingRate * _audioAttack +
const float BLINK_AFTER_TALKING = 0.25f; (1.0f - audioAttackAveragingRate) * fabs((audioLoudness - _longTermAverageLoudness) - _lastLoudness);
_timeWithoutTalking += deltaTime; _lastLoudness = (audioLoudness - _longTermAverageLoudness);
if ((_averageLoudness - _longTermAverageLoudness) > TALKING_LOUDNESS) {
_timeWithoutTalking = 0.0f;
} else if (_timeWithoutTalking - deltaTime < BLINK_AFTER_TALKING && _timeWithoutTalking >= BLINK_AFTER_TALKING) {
forceBlink = true;
}
const float BLINK_SPEED = 10.0f; const float BROW_LIFT_THRESHOLD = 100.0f;
const float BLINK_SPEED_VARIABILITY = 1.0f; if (_audioAttack > BROW_LIFT_THRESHOLD) {
const float BLINK_START_VARIABILITY = 0.25f; _browAudioLift += sqrtf(_audioAttack) * 0.01f;
const float FULLY_OPEN = 0.0f; }
const float FULLY_CLOSED = 1.0f; _browAudioLift = glm::clamp(_browAudioLift *= 0.7f, 0.0f, 1.0f);
if (_leftEyeBlinkVelocity == 0.0f && _rightEyeBlinkVelocity == 0.0f) {
// no blinking when brows are raised; blink less with increasing loudness const float BLINK_SPEED = 10.0f;
const float BASE_BLINK_RATE = 15.0f / 60.0f; const float BLINK_SPEED_VARIABILITY = 1.0f;
const float ROOT_LOUDNESS_TO_BLINK_INTERVAL = 0.25f; const float BLINK_START_VARIABILITY = 0.25f;
if (forceBlink || (_browAudioLift < EPSILON && shouldDo(glm::max(1.0f, sqrt(fabs(_averageLoudness - _longTermAverageLoudness)) * const float FULLY_OPEN = 0.0f;
ROOT_LOUDNESS_TO_BLINK_INTERVAL) / BASE_BLINK_RATE, deltaTime))) { const float FULLY_CLOSED = 1.0f;
_leftEyeBlinkVelocity = BLINK_SPEED + randFloat() * BLINK_SPEED_VARIABILITY; if (_leftEyeBlinkVelocity == 0.0f && _rightEyeBlinkVelocity == 0.0f) {
_rightEyeBlinkVelocity = BLINK_SPEED + randFloat() * BLINK_SPEED_VARIABILITY; // no blinking when brows are raised; blink less with increasing loudness
if (randFloat() < 0.5f) { const float BASE_BLINK_RATE = 15.0f / 60.0f;
_leftEyeBlink = BLINK_START_VARIABILITY; const float ROOT_LOUDNESS_TO_BLINK_INTERVAL = 0.25f;
} else { if (forceBlink || (_browAudioLift < EPSILON && shouldDo(glm::max(1.0f, sqrt(fabs(_averageLoudness - _longTermAverageLoudness)) *
_rightEyeBlink = BLINK_START_VARIABILITY; ROOT_LOUDNESS_TO_BLINK_INTERVAL) / BASE_BLINK_RATE, deltaTime))) {
_leftEyeBlinkVelocity = BLINK_SPEED + randFloat() * BLINK_SPEED_VARIABILITY;
_rightEyeBlinkVelocity = BLINK_SPEED + randFloat() * BLINK_SPEED_VARIABILITY;
if (randFloat() < 0.5f) {
_leftEyeBlink = BLINK_START_VARIABILITY;
} else {
_rightEyeBlink = BLINK_START_VARIABILITY;
}
}
} else {
_leftEyeBlink = glm::clamp(_leftEyeBlink + _leftEyeBlinkVelocity * deltaTime, FULLY_OPEN, FULLY_CLOSED);
_rightEyeBlink = glm::clamp(_rightEyeBlink + _rightEyeBlinkVelocity * deltaTime, FULLY_OPEN, FULLY_CLOSED);
if (_leftEyeBlink == FULLY_CLOSED) {
_leftEyeBlinkVelocity = -BLINK_SPEED;
} else if (_leftEyeBlink == FULLY_OPEN) {
_leftEyeBlinkVelocity = 0.0f;
}
if (_rightEyeBlink == FULLY_CLOSED) {
_rightEyeBlinkVelocity = -BLINK_SPEED;
} else if (_rightEyeBlink == FULLY_OPEN) {
_rightEyeBlinkVelocity = 0.0f;
} }
} }
// use data to update fake Faceshift blendshape coefficients
calculateMouthShapes(deltaTime);
FaceTracker::updateFakeCoefficients(_leftEyeBlink,
_rightEyeBlink,
_browAudioLift,
_audioJawOpen,
_mouth2,
_mouth3,
_mouth4,
_transientBlendshapeCoefficients);
applyEyelidOffset(getOrientation());
} else { } else {
_leftEyeBlink = glm::clamp(_leftEyeBlink + _leftEyeBlinkVelocity * deltaTime, FULLY_OPEN, FULLY_CLOSED); _saccade = glm::vec3();
_rightEyeBlink = glm::clamp(_rightEyeBlink + _rightEyeBlinkVelocity * deltaTime, FULLY_OPEN, FULLY_CLOSED);
if (_leftEyeBlink == FULLY_CLOSED) {
_leftEyeBlinkVelocity = -BLINK_SPEED;
} else if (_leftEyeBlink == FULLY_OPEN) {
_leftEyeBlinkVelocity = 0.0f;
}
if (_rightEyeBlink == FULLY_CLOSED) {
_rightEyeBlinkVelocity = -BLINK_SPEED;
} else if (_rightEyeBlink == FULLY_OPEN) {
_rightEyeBlinkVelocity = 0.0f;
}
} }
applyEyelidOffset(getOrientation());
}
void Head::computeFaceMovement(float deltaTime) {
// Update audio attack data for facial animation (eyebrows and mouth)
const float BROW_LIFT_THRESHOLD = 100.0f;
if (_audioAttack > BROW_LIFT_THRESHOLD) {
_browAudioLift += sqrtf(_audioAttack) * 0.01f;
}
_browAudioLift = glm::clamp(_browAudioLift *= 0.7f, 0.0f, 1.0f);
// use data to update fake Faceshift blendshape coefficients
calculateMouthShapes(deltaTime);
FaceTracker::updateFakeCoefficients(_leftEyeBlink,
_rightEyeBlink,
_browAudioLift,
_audioJawOpen,
_mouth2,
_mouth3,
_mouth4,
_transientBlendshapeCoefficients);
}
void Head::computeEyePosition() {
_leftEyePosition = _rightEyePosition = getPosition(); _leftEyePosition = _rightEyePosition = getPosition();
if (_owningAvatar) { if (_owningAvatar) {
auto skeletonModel = static_cast<Avatar*>(_owningAvatar)->getSkeletonModel(); auto skeletonModel = static_cast<Avatar*>(_owningAvatar)->getSkeletonModel();
@ -161,13 +164,6 @@ void Head::computeEyePosition() {
_eyePosition = 0.5f * (_leftEyePosition + _rightEyePosition); _eyePosition = 0.5f * (_leftEyePosition + _rightEyePosition);
} }
void Head::simulate(float deltaTime) {
computeAudioLoudness(deltaTime);
computeFaceMovement(deltaTime);
computeEyeMovement(deltaTime);
computeEyePosition();
}
void Head::calculateMouthShapes(float deltaTime) { void Head::calculateMouthShapes(float deltaTime) {
const float JAW_OPEN_SCALE = 0.015f; const float JAW_OPEN_SCALE = 0.015f;
const float JAW_OPEN_RATE = 0.9f; const float JAW_OPEN_RATE = 0.9f;

View file

@ -83,11 +83,6 @@ public:
float getTimeWithoutTalking() const { return _timeWithoutTalking; } float getTimeWithoutTalking() const { return _timeWithoutTalking; }
protected: protected:
void computeAudioLoudness(float deltaTime);
void computeEyeMovement(float deltaTime);
void computeFaceMovement(float deltaTime);
void computeEyePosition();
// disallow copies of the Head, copy of owning Avatar is disallowed too // disallow copies of the Head, copy of owning Avatar is disallowed too
Head(const Head&); Head(const Head&);
Head& operator= (const Head&); Head& operator= (const Head&);

View file

@ -8,9 +8,9 @@
#include "OtherAvatar.h" #include "OtherAvatar.h"
OtherAvatar::OtherAvatar(QThread* thread, RigPointer rig) : Avatar(thread, rig) { OtherAvatar::OtherAvatar(QThread* thread) : Avatar(thread) {
// give the pointer to our head to inherited _headData variable from AvatarData // give the pointer to our head to inherited _headData variable from AvatarData
_headData = new Head(this); _headData = new Head(this);
_skeletonModel = std::make_shared<SkeletonModel>(this, nullptr, rig); _skeletonModel = std::make_shared<SkeletonModel>(this, nullptr);
connect(_skeletonModel.get(), &Model::setURLFinished, this, &Avatar::setModelURLFinished); connect(_skeletonModel.get(), &Model::setURLFinished, this, &Avatar::setModelURLFinished);
} }

View file

@ -13,7 +13,7 @@
class OtherAvatar : public Avatar { class OtherAvatar : public Avatar {
public: public:
explicit OtherAvatar(QThread* thread, RigPointer rig = nullptr); explicit OtherAvatar(QThread* thread);
virtual void instantiableAvatar() override {}; virtual void instantiableAvatar() override {};
}; };

View file

@ -22,8 +22,8 @@
#include "Avatar.h" #include "Avatar.h"
#include "Logging.h" #include "Logging.h"
SkeletonModel::SkeletonModel(Avatar* owningAvatar, QObject* parent, RigPointer rig) : SkeletonModel::SkeletonModel(Avatar* owningAvatar, QObject* parent) :
CauterizedModel(rig, parent), CauterizedModel(parent),
_owningAvatar(owningAvatar), _owningAvatar(owningAvatar),
_boundingCapsuleLocalOffset(0.0f), _boundingCapsuleLocalOffset(0.0f),
_boundingCapsuleRadius(0.0f), _boundingCapsuleRadius(0.0f),
@ -31,7 +31,6 @@ SkeletonModel::SkeletonModel(Avatar* owningAvatar, QObject* parent, RigPointer r
_defaultEyeModelPosition(glm::vec3(0.0f, 0.0f, 0.0f)), _defaultEyeModelPosition(glm::vec3(0.0f, 0.0f, 0.0f)),
_headClipDistance(DEFAULT_NEAR_CLIP) _headClipDistance(DEFAULT_NEAR_CLIP)
{ {
assert(_rig);
assert(_owningAvatar); assert(_owningAvatar);
} }
@ -41,12 +40,12 @@ SkeletonModel::~SkeletonModel() {
void SkeletonModel::initJointStates() { void SkeletonModel::initJointStates() {
const FBXGeometry& geometry = getFBXGeometry(); const FBXGeometry& geometry = getFBXGeometry();
glm::mat4 modelOffset = glm::scale(_scale) * glm::translate(_offset); glm::mat4 modelOffset = glm::scale(_scale) * glm::translate(_offset);
_rig->initJointStates(geometry, modelOffset); _rig.initJointStates(geometry, modelOffset);
// Determine the default eye position for avatar scale = 1.0 // Determine the default eye position for avatar scale = 1.0
int headJointIndex = geometry.headJointIndex; int headJointIndex = geometry.headJointIndex;
if (0 > headJointIndex || headJointIndex >= _rig->getJointStateCount()) { if (0 > headJointIndex || headJointIndex >= _rig.getJointStateCount()) {
qCWarning(avatars_renderer) << "Bad head joint! Got:" << headJointIndex << "jointCount:" << _rig->getJointStateCount(); qCWarning(avatars_renderer) << "Bad head joint! Got:" << headJointIndex << "jointCount:" << _rig.getJointStateCount();
} }
glm::vec3 leftEyePosition, rightEyePosition; glm::vec3 leftEyePosition, rightEyePosition;
getEyeModelPositions(leftEyePosition, rightEyePosition); getEyeModelPositions(leftEyePosition, rightEyePosition);
@ -102,7 +101,7 @@ void SkeletonModel::updateRig(float deltaTime, glm::mat4 parentTransform) {
// If the head is not positioned, updateEyeJoints won't get the math right // If the head is not positioned, updateEyeJoints won't get the math right
glm::quat headOrientation; glm::quat headOrientation;
_rig->getJointRotation(geometry.headJointIndex, headOrientation); _rig.getJointRotation(geometry.headJointIndex, headOrientation);
glm::vec3 eulers = safeEulerAngles(headOrientation); glm::vec3 eulers = safeEulerAngles(headOrientation);
head->setBasePitch(glm::degrees(-eulers.x)); head->setBasePitch(glm::degrees(-eulers.x));
head->setBaseYaw(glm::degrees(eulers.y)); head->setBaseYaw(glm::degrees(eulers.y));
@ -116,7 +115,7 @@ void SkeletonModel::updateRig(float deltaTime, glm::mat4 parentTransform) {
eyeParams.leftEyeJointIndex = geometry.leftEyeJointIndex; eyeParams.leftEyeJointIndex = geometry.leftEyeJointIndex;
eyeParams.rightEyeJointIndex = geometry.rightEyeJointIndex; eyeParams.rightEyeJointIndex = geometry.rightEyeJointIndex;
_rig->updateFromEyeParameters(eyeParams); _rig.updateFromEyeParameters(eyeParams);
} }
void SkeletonModel::updateAttitude() { void SkeletonModel::updateAttitude() {
@ -136,7 +135,7 @@ void SkeletonModel::simulate(float deltaTime, bool fullUpdate) {
// let rig compute the model offset // let rig compute the model offset
glm::vec3 registrationPoint; glm::vec3 registrationPoint;
if (_rig->getModelRegistrationPoint(registrationPoint)) { if (_rig.getModelRegistrationPoint(registrationPoint)) {
setOffset(registrationPoint); setOffset(registrationPoint);
} }
} else { } else {
@ -164,8 +163,8 @@ bool operator<(const IndexValue& firstIndex, const IndexValue& secondIndex) {
} }
bool SkeletonModel::getLeftGrabPosition(glm::vec3& position) const { bool SkeletonModel::getLeftGrabPosition(glm::vec3& position) const {
int knuckleIndex = _rig->indexOfJoint("LeftHandMiddle1"); int knuckleIndex = _rig.indexOfJoint("LeftHandMiddle1");
int handIndex = _rig->indexOfJoint("LeftHand"); int handIndex = _rig.indexOfJoint("LeftHand");
if (knuckleIndex >= 0 && handIndex >= 0) { if (knuckleIndex >= 0 && handIndex >= 0) {
glm::quat handRotation; glm::quat handRotation;
glm::vec3 knucklePosition; glm::vec3 knucklePosition;
@ -189,8 +188,8 @@ bool SkeletonModel::getLeftGrabPosition(glm::vec3& position) const {
} }
bool SkeletonModel::getRightGrabPosition(glm::vec3& position) const { bool SkeletonModel::getRightGrabPosition(glm::vec3& position) const {
int knuckleIndex = _rig->indexOfJoint("RightHandMiddle1"); int knuckleIndex = _rig.indexOfJoint("RightHandMiddle1");
int handIndex = _rig->indexOfJoint("RightHand"); int handIndex = _rig.indexOfJoint("RightHand");
if (knuckleIndex >= 0 && handIndex >= 0) { if (knuckleIndex >= 0 && handIndex >= 0) {
glm::quat handRotation; glm::quat handRotation;
glm::vec3 knucklePosition; glm::vec3 knucklePosition;
@ -304,7 +303,7 @@ float VERY_BIG_MASS = 1.0e6f;
// virtual // virtual
void SkeletonModel::computeBoundingShape() { void SkeletonModel::computeBoundingShape() {
if (!isLoaded() || _rig->jointStatesEmpty()) { if (!isLoaded() || _rig.jointStatesEmpty()) {
return; return;
} }
@ -316,7 +315,7 @@ void SkeletonModel::computeBoundingShape() {
float radius, height; float radius, height;
glm::vec3 offset; glm::vec3 offset;
_rig->computeAvatarBoundingCapsule(geometry, radius, height, offset); _rig.computeAvatarBoundingCapsule(geometry, radius, height, offset);
float invScale = 1.0f / _owningAvatar->getUniformScale(); float invScale = 1.0f / _owningAvatar->getUniformScale();
_boundingCapsuleRadius = invScale * radius; _boundingCapsuleRadius = invScale * radius;
_boundingCapsuleHeight = invScale * height; _boundingCapsuleHeight = invScale * height;

View file

@ -28,7 +28,7 @@ class SkeletonModel : public CauterizedModel {
public: public:
SkeletonModel(Avatar* owningAvatar, QObject* parent = nullptr, RigPointer rig = nullptr); SkeletonModel(Avatar* owningAvatar, QObject* parent = nullptr);
~SkeletonModel(); ~SkeletonModel();
void initJointStates() override; void initJointStates() override;

View file

@ -2030,17 +2030,6 @@ void AvatarData::fromJson(const QJsonObject& json, bool useFrameSkeleton) {
version = JSON_AVATAR_JOINT_ROTATIONS_IN_RELATIVE_FRAME_VERSION; version = JSON_AVATAR_JOINT_ROTATIONS_IN_RELATIVE_FRAME_VERSION;
} }
// The head setOrientation likes to overwrite the avatar orientation,
// so lets do the head first
// Most head data is relative to the avatar, and needs no basis correction,
// but the lookat vector does need correction
if (json.contains(JSON_AVATAR_HEAD)) {
if (!_headData) {
_headData = new HeadData(this);
}
_headData->fromJson(json[JSON_AVATAR_HEAD].toObject());
}
if (json.contains(JSON_AVATAR_BODY_MODEL)) { if (json.contains(JSON_AVATAR_BODY_MODEL)) {
auto bodyModelURL = json[JSON_AVATAR_BODY_MODEL].toString(); auto bodyModelURL = json[JSON_AVATAR_BODY_MODEL].toString();
if (useFrameSkeleton && bodyModelURL != getSkeletonModelURL().toString()) { if (useFrameSkeleton && bodyModelURL != getSkeletonModelURL().toString()) {
@ -2079,6 +2068,14 @@ void AvatarData::fromJson(const QJsonObject& json, bool useFrameSkeleton) {
setOrientation(currentBasis->getRotation()); setOrientation(currentBasis->getRotation());
} }
// Do after avatar orientation because head look-at needs avatar orientation.
if (json.contains(JSON_AVATAR_HEAD)) {
if (!_headData) {
_headData = new HeadData(this);
}
_headData->fromJson(json[JSON_AVATAR_HEAD].toObject());
}
if (json.contains(JSON_AVATAR_SCALE)) { if (json.contains(JSON_AVATAR_SCALE)) {
setTargetScale((float)json[JSON_AVATAR_SCALE].toDouble()); setTargetScale((float)json[JSON_AVATAR_SCALE].toDouble());
} }

View file

@ -52,6 +52,13 @@ glm::quat HeadData::getOrientation() const {
return _owningAvatar->getOrientation() * getRawOrientation(); return _owningAvatar->getOrientation() * getRawOrientation();
} }
void HeadData::setHeadOrientation(const glm::quat& orientation) {
glm::quat bodyOrientation = _owningAvatar->getOrientation();
glm::vec3 eulers = glm::degrees(safeEulerAngles(glm::inverse(bodyOrientation) * orientation));
_basePitch = eulers.x;
_baseYaw = eulers.y;
_baseRoll = eulers.z;
}
void HeadData::setOrientation(const glm::quat& orientation) { void HeadData::setOrientation(const glm::quat& orientation) {
// rotate body about vertical axis // rotate body about vertical axis
@ -61,10 +68,7 @@ void HeadData::setOrientation(const glm::quat& orientation) {
_owningAvatar->setOrientation(bodyOrientation); _owningAvatar->setOrientation(bodyOrientation);
// the rest goes to the head // the rest goes to the head
glm::vec3 eulers = glm::degrees(safeEulerAngles(glm::inverse(bodyOrientation) * orientation)); setHeadOrientation(orientation);
_basePitch = eulers.x;
_baseYaw = eulers.y;
_baseRoll = eulers.z;
} }
//Lazily construct a lookup map from the blendshapes //Lazily construct a lookup map from the blendshapes
@ -173,14 +177,14 @@ void HeadData::fromJson(const QJsonObject& json) {
} }
} }
if (json.contains(JSON_AVATAR_HEAD_ROTATION)) {
setOrientation(quatFromJsonValue(json[JSON_AVATAR_HEAD_ROTATION]));
}
if (json.contains(JSON_AVATAR_HEAD_LOOKAT)) { if (json.contains(JSON_AVATAR_HEAD_LOOKAT)) {
auto relativeLookAt = vec3FromJsonValue(json[JSON_AVATAR_HEAD_LOOKAT]); auto relativeLookAt = vec3FromJsonValue(json[JSON_AVATAR_HEAD_LOOKAT]);
if (glm::length2(relativeLookAt) > 0.01f) { if (glm::length2(relativeLookAt) > 0.01f) {
setLookAtPosition((_owningAvatar->getOrientation() * relativeLookAt) + _owningAvatar->getPosition()); setLookAtPosition((_owningAvatar->getOrientation() * relativeLookAt) + _owningAvatar->getPosition());
} }
} }
if (json.contains(JSON_AVATAR_HEAD_ROTATION)) {
setHeadOrientation(quatFromJsonValue(json[JSON_AVATAR_HEAD_ROTATION]));
}
} }

View file

@ -101,6 +101,8 @@ private:
// privatize copy ctor and assignment operator so copies of this object cannot be made // privatize copy ctor and assignment operator so copies of this object cannot be made
HeadData(const HeadData&); HeadData(const HeadData&);
HeadData& operator= (const HeadData&); HeadData& operator= (const HeadData&);
void setHeadOrientation(const glm::quat& orientation);
}; };
#endif // hifi_HeadData_h #endif // hifi_HeadData_h

View file

@ -582,7 +582,7 @@ ModelPointer EntityTreeRenderer::allocateModel(const QString& url, float loading
return model; return model;
} }
model = std::make_shared<Model>(std::make_shared<Rig>(), nullptr, spatiallyNestableOverride); model = std::make_shared<Model>(nullptr, spatiallyNestableOverride);
model->setLoadingPriority(loadingPriority); model->setLoadingPriority(loadingPriority);
model->init(); model->init();
model->setURL(QUrl(url)); model->setURL(QUrl(url));

View file

@ -807,7 +807,7 @@ void RenderableModelEntityItem::computeShapeInfo(ShapeInfo& shapeInfo) {
const FBXMesh& mesh = fbxGeometry.meshes.at(i); const FBXMesh& mesh = fbxGeometry.meshes.at(i);
if (mesh.clusters.size() > 0) { if (mesh.clusters.size() > 0) {
const FBXCluster& cluster = mesh.clusters.at(0); const FBXCluster& cluster = mesh.clusters.at(0);
auto jointMatrix = _model->getRig()->getJointTransform(cluster.jointIndex); auto jointMatrix = _model->getRig().getJointTransform(cluster.jointIndex);
// we backtranslate by the registration offset so we can apply that offset to the shapeInfo later // we backtranslate by the registration offset so we can apply that offset to the shapeInfo later
localTransforms.push_back(invRegistraionOffset * jointMatrix * cluster.inverseBindMatrix); localTransforms.push_back(invRegistraionOffset * jointMatrix * cluster.inverseBindMatrix);
} else { } else {
@ -1080,26 +1080,22 @@ bool RenderableModelEntityItem::setAbsoluteJointRotationInObjectFrame(int index,
if (!_model) { if (!_model) {
return false; return false;
} }
RigPointer rig = _model->getRig(); const Rig& rig = _model->getRig();
if (!rig) { int jointParentIndex = rig.getJointParentIndex(index);
return false;
}
int jointParentIndex = rig->getJointParentIndex(index);
if (jointParentIndex == -1) { if (jointParentIndex == -1) {
return setLocalJointRotation(index, rotation); return setLocalJointRotation(index, rotation);
} }
bool success; bool success;
AnimPose jointParentPose; AnimPose jointParentPose;
success = rig->getAbsoluteJointPoseInRigFrame(jointParentIndex, jointParentPose); success = rig.getAbsoluteJointPoseInRigFrame(jointParentIndex, jointParentPose);
if (!success) { if (!success) {
return false; return false;
} }
AnimPose jointParentInversePose = jointParentPose.inverse(); AnimPose jointParentInversePose = jointParentPose.inverse();
AnimPose jointAbsolutePose; // in rig frame AnimPose jointAbsolutePose; // in rig frame
success = rig->getAbsoluteJointPoseInRigFrame(index, jointAbsolutePose); success = rig.getAbsoluteJointPoseInRigFrame(index, jointAbsolutePose);
if (!success) { if (!success) {
return false; return false;
} }
@ -1113,26 +1109,23 @@ bool RenderableModelEntityItem::setAbsoluteJointTranslationInObjectFrame(int ind
if (!_model) { if (!_model) {
return false; return false;
} }
RigPointer rig = _model->getRig(); const Rig& rig = _model->getRig();
if (!rig) {
return false;
}
int jointParentIndex = rig->getJointParentIndex(index); int jointParentIndex = rig.getJointParentIndex(index);
if (jointParentIndex == -1) { if (jointParentIndex == -1) {
return setLocalJointTranslation(index, translation); return setLocalJointTranslation(index, translation);
} }
bool success; bool success;
AnimPose jointParentPose; AnimPose jointParentPose;
success = rig->getAbsoluteJointPoseInRigFrame(jointParentIndex, jointParentPose); success = rig.getAbsoluteJointPoseInRigFrame(jointParentIndex, jointParentPose);
if (!success) { if (!success) {
return false; return false;
} }
AnimPose jointParentInversePose = jointParentPose.inverse(); AnimPose jointParentInversePose = jointParentPose.inverse();
AnimPose jointAbsolutePose; // in rig frame AnimPose jointAbsolutePose; // in rig frame
success = rig->getAbsoluteJointPoseInRigFrame(index, jointAbsolutePose); success = rig.getAbsoluteJointPoseInRigFrame(index, jointAbsolutePose);
if (!success) { if (!success) {
return false; return false;
} }
@ -1248,20 +1241,16 @@ void RenderableModelEntityItem::locationChanged(bool tellPhysics) {
} }
int RenderableModelEntityItem::getJointIndex(const QString& name) const { int RenderableModelEntityItem::getJointIndex(const QString& name) const {
if (_model && _model->isActive()) { return (_model && _model->isActive()) ? _model->getRig().indexOfJoint(name) : -1;
RigPointer rig = _model->getRig();
return rig->indexOfJoint(name);
}
return -1;
} }
QStringList RenderableModelEntityItem::getJointNames() const { QStringList RenderableModelEntityItem::getJointNames() const {
QStringList result; QStringList result;
if (_model && _model->isActive()) { if (_model && _model->isActive()) {
RigPointer rig = _model->getRig(); const Rig& rig = _model->getRig();
int jointCount = rig->getJointStateCount(); int jointCount = rig.getJointStateCount();
for (int jointIndex = 0; jointIndex < jointCount; jointIndex++) { for (int jointIndex = 0; jointIndex < jointCount; jointIndex++) {
result << rig->nameOfJoint(jointIndex); result << rig.nameOfJoint(jointIndex);
} }
} }
return result; return result;

View file

@ -36,15 +36,17 @@ void RenderableZoneEntityItem::changeProperties(Lambda setNewProperties) {
QString oldShapeURL = getCompoundShapeURL(); QString oldShapeURL = getCompoundShapeURL();
glm::vec3 oldPosition = getPosition(), oldDimensions = getDimensions(); glm::vec3 oldPosition = getPosition(), oldDimensions = getDimensions();
glm::quat oldRotation = getRotation(); glm::quat oldRotation = getRotation();
setNewProperties(); setNewProperties();
if (oldShapeURL != getCompoundShapeURL()) { if (oldShapeURL != getCompoundShapeURL()) {
if (_model) { if (_model) {
delete _model; _model.reset();
} }
_model = getModel(); _model = std::make_shared<Model>();
_model->setIsWireframe(true);
_model->init();
_needsInitialSimulation = true; _needsInitialSimulation = true;
_model->setURL(getCompoundShapeURL()); _model->setURL(getCompoundShapeURL());
} }
@ -80,35 +82,24 @@ int RenderableZoneEntityItem::readEntitySubclassDataFromBuffer(const unsigned ch
return bytesRead; return bytesRead;
} }
Model* RenderableZoneEntityItem::getModel() {
Model* model = new Model(nullptr);
model->setIsWireframe(true);
model->init();
return model;
}
void RenderableZoneEntityItem::initialSimulation() {
_model->setScaleToFit(true, getDimensions());
_model->setSnapModelToRegistrationPoint(true, getRegistrationPoint());
_model->setRotation(getRotation());
_model->setTranslation(getPosition());
_model->simulate(0.0f);
_needsInitialSimulation = false;
}
void RenderableZoneEntityItem::updateGeometry() { void RenderableZoneEntityItem::updateGeometry() {
if (_model && !_model->isActive() && hasCompoundShapeURL()) { if (_model && !_model->isActive() && hasCompoundShapeURL()) {
// Since we have a delayload, we need to update the geometry if it has been downloaded // Since we have a delayload, we need to update the geometry if it has been downloaded
_model->setURL(getCompoundShapeURL()); _model->setURL(getCompoundShapeURL());
} }
if (_model && _model->isActive() && _needsInitialSimulation) { if (_model && _model->isActive() && _needsInitialSimulation) {
initialSimulation(); _model->setScaleToFit(true, getDimensions());
_model->setSnapModelToRegistrationPoint(true, getRegistrationPoint());
_model->setRotation(getRotation());
_model->setTranslation(getPosition());
_model->simulate(0.0f);
_needsInitialSimulation = false;
} }
} }
void RenderableZoneEntityItem::render(RenderArgs* args) { void RenderableZoneEntityItem::render(RenderArgs* args) {
Q_ASSERT(getType() == EntityTypes::Zone); Q_ASSERT(getType() == EntityTypes::Zone);
if (_drawZoneBoundaries) { if (_drawZoneBoundaries) {
switch (getShapeType()) { switch (getShapeType()) {
case SHAPE_TYPE_COMPOUND: { case SHAPE_TYPE_COMPOUND: {
@ -123,9 +114,9 @@ void RenderableZoneEntityItem::render(RenderArgs* args) {
render::Item::Status::Getters statusGetters; render::Item::Status::Getters statusGetters;
makeEntityItemStatusGetters(getThisPointer(), statusGetters); makeEntityItemStatusGetters(getThisPointer(), statusGetters);
_model->addToScene(scene, transaction); _model->addToScene(scene, transaction);
scene->enqueueTransaction(transaction); scene->enqueueTransaction(transaction);
_model->setVisibleInScene(getVisible(), scene); _model->setVisibleInScene(getVisible(), scene);
} }
break; break;
@ -134,7 +125,7 @@ void RenderableZoneEntityItem::render(RenderArgs* args) {
case SHAPE_TYPE_SPHERE: { case SHAPE_TYPE_SPHERE: {
PerformanceTimer perfTimer("zone->renderPrimitive"); PerformanceTimer perfTimer("zone->renderPrimitive");
glm::vec4 DEFAULT_COLOR(1.0f, 1.0f, 1.0f, 1.0f); glm::vec4 DEFAULT_COLOR(1.0f, 1.0f, 1.0f, 1.0f);
Q_ASSERT(args->_batch); Q_ASSERT(args->_batch);
gpu::Batch& batch = *args->_batch; gpu::Batch& batch = *args->_batch;
@ -159,7 +150,7 @@ void RenderableZoneEntityItem::render(RenderArgs* args) {
break; break;
} }
} }
if ((!_drawZoneBoundaries || getShapeType() != SHAPE_TYPE_COMPOUND) && if ((!_drawZoneBoundaries || getShapeType() != SHAPE_TYPE_COMPOUND) &&
_model && !_model->needsFixupInScene()) { _model && !_model->needsFixupInScene()) {
// If the model is in the scene but doesn't need to be, remove it. // If the model is in the scene but doesn't need to be, remove it.
@ -175,11 +166,11 @@ bool RenderableZoneEntityItem::contains(const glm::vec3& point) const {
return EntityItem::contains(point); return EntityItem::contains(point);
} }
const_cast<RenderableZoneEntityItem*>(this)->updateGeometry(); const_cast<RenderableZoneEntityItem*>(this)->updateGeometry();
if (_model && _model->isActive() && EntityItem::contains(point)) { if (_model && _model->isActive() && EntityItem::contains(point)) {
return _model->convexHullContains(point); return _model->convexHullContains(point);
} }
return false; return false;
} }
@ -188,7 +179,7 @@ public:
RenderableZoneEntityItemMeta(EntityItemPointer entity) : entity(entity){ } RenderableZoneEntityItemMeta(EntityItemPointer entity) : entity(entity){ }
typedef render::Payload<RenderableZoneEntityItemMeta> Payload; typedef render::Payload<RenderableZoneEntityItemMeta> Payload;
typedef Payload::DataPointer Pointer; typedef Payload::DataPointer Pointer;
EntityItemPointer entity; EntityItemPointer entity;
}; };
@ -196,7 +187,7 @@ namespace render {
template <> const ItemKey payloadGetKey(const RenderableZoneEntityItemMeta::Pointer& payload) { template <> const ItemKey payloadGetKey(const RenderableZoneEntityItemMeta::Pointer& payload) {
return ItemKey::Builder::opaqueShape(); return ItemKey::Builder::opaqueShape();
} }
template <> const Item::Bound payloadGetBound(const RenderableZoneEntityItemMeta::Pointer& payload) { template <> const Item::Bound payloadGetBound(const RenderableZoneEntityItemMeta::Pointer& payload) {
if (payload && payload->entity) { if (payload && payload->entity) {
bool success; bool success;
@ -220,7 +211,7 @@ namespace render {
bool RenderableZoneEntityItem::addToScene(EntityItemPointer self, const render::ScenePointer& scene, bool RenderableZoneEntityItem::addToScene(EntityItemPointer self, const render::ScenePointer& scene,
render::Transaction& transaction) { render::Transaction& transaction) {
_myMetaItem = scene->allocateID(); _myMetaItem = scene->allocateID();
auto renderData = std::make_shared<RenderableZoneEntityItemMeta>(self); auto renderData = std::make_shared<RenderableZoneEntityItemMeta>(self);
auto renderPayload = std::make_shared<RenderableZoneEntityItemMeta::Payload>(renderData); auto renderPayload = std::make_shared<RenderableZoneEntityItemMeta::Payload>(renderData);

View file

@ -23,7 +23,7 @@ public:
RenderableZoneEntityItem(const EntityItemID& entityItemID) : RenderableZoneEntityItem(const EntityItemID& entityItemID) :
ZoneEntityItem(entityItemID), ZoneEntityItem(entityItemID),
_model(NULL), _model(nullptr),
_needsInitialSimulation(true) _needsInitialSimulation(true)
{ } { }
@ -48,14 +48,12 @@ private:
virtual void dimensionsChanged() override { EntityItem::dimensionsChanged(); notifyBoundChanged(); } virtual void dimensionsChanged() override { EntityItem::dimensionsChanged(); notifyBoundChanged(); }
void notifyBoundChanged(); void notifyBoundChanged();
Model* getModel();
void initialSimulation();
void updateGeometry(); void updateGeometry();
template<typename Lambda> template<typename Lambda>
void changeProperties(Lambda functor); void changeProperties(Lambda functor);
Model* _model; ModelPointer _model;
bool _needsInitialSimulation; bool _needsInitialSimulation;
render::ItemID _myMetaItem{ render::Item::INVALID_ITEM_ID }; render::ItemID _myMetaItem{ render::Item::INVALID_ITEM_ID };

View file

@ -810,7 +810,7 @@ int EntityItem::readEntityDataFromBuffer(const unsigned char* data, int bytesLef
READ_ENTITY_PROPERTY(PROP_COLLISIONLESS, bool, updateCollisionless); READ_ENTITY_PROPERTY(PROP_COLLISIONLESS, bool, updateCollisionless);
READ_ENTITY_PROPERTY(PROP_COLLISION_MASK, uint8_t, updateCollisionMask); READ_ENTITY_PROPERTY(PROP_COLLISION_MASK, uint8_t, updateCollisionMask);
READ_ENTITY_PROPERTY(PROP_DYNAMIC, bool, updateDynamic); READ_ENTITY_PROPERTY(PROP_DYNAMIC, bool, updateDynamic);
READ_ENTITY_PROPERTY(PROP_LOCKED, bool, setLocked); READ_ENTITY_PROPERTY(PROP_LOCKED, bool, updateLocked);
READ_ENTITY_PROPERTY(PROP_USER_DATA, QString, setUserData); READ_ENTITY_PROPERTY(PROP_USER_DATA, QString, setUserData);
if (args.bitstreamVersion >= VERSION_ENTITIES_HAS_MARKETPLACE_ID) { if (args.bitstreamVersion >= VERSION_ENTITIES_HAS_MARKETPLACE_ID) {
@ -1344,6 +1344,7 @@ bool EntityItem::setProperties(const EntityItemProperties& properties) {
SET_ENTITY_PROPERTY_FROM_PROPERTIES(dynamic, updateDynamic); SET_ENTITY_PROPERTY_FROM_PROPERTIES(dynamic, updateDynamic);
SET_ENTITY_PROPERTY_FROM_PROPERTIES(created, updateCreated); SET_ENTITY_PROPERTY_FROM_PROPERTIES(created, updateCreated);
SET_ENTITY_PROPERTY_FROM_PROPERTIES(lifetime, updateLifetime); SET_ENTITY_PROPERTY_FROM_PROPERTIES(lifetime, updateLifetime);
SET_ENTITY_PROPERTY_FROM_PROPERTIES(locked, updateLocked);
// non-simulation properties below // non-simulation properties below
SET_ENTITY_PROPERTY_FROM_PROPERTIES(script, setScript); SET_ENTITY_PROPERTY_FROM_PROPERTIES(script, setScript);
@ -1352,7 +1353,6 @@ bool EntityItem::setProperties(const EntityItemProperties& properties) {
SET_ENTITY_PROPERTY_FROM_PROPERTIES(collisionSoundURL, setCollisionSoundURL); SET_ENTITY_PROPERTY_FROM_PROPERTIES(collisionSoundURL, setCollisionSoundURL);
SET_ENTITY_PROPERTY_FROM_PROPERTIES(localRenderAlpha, setLocalRenderAlpha); SET_ENTITY_PROPERTY_FROM_PROPERTIES(localRenderAlpha, setLocalRenderAlpha);
SET_ENTITY_PROPERTY_FROM_PROPERTIES(visible, setVisible); SET_ENTITY_PROPERTY_FROM_PROPERTIES(visible, setVisible);
SET_ENTITY_PROPERTY_FROM_PROPERTIES(locked, setLocked);
SET_ENTITY_PROPERTY_FROM_PROPERTIES(userData, setUserData); SET_ENTITY_PROPERTY_FROM_PROPERTIES(userData, setUserData);
SET_ENTITY_PROPERTY_FROM_PROPERTIES(marketplaceID, setMarketplaceID); SET_ENTITY_PROPERTY_FROM_PROPERTIES(marketplaceID, setMarketplaceID);
SET_ENTITY_PROPERTY_FROM_PROPERTIES(name, setName); SET_ENTITY_PROPERTY_FROM_PROPERTIES(name, setName);
@ -2769,6 +2769,23 @@ void EntityItem::setLocked(bool value) {
}); });
} }
void EntityItem::updateLocked(bool value) {
bool changed { false };
withWriteLock([&] {
if (_locked != value) {
_locked = value;
changed = true;
}
});
if (changed) {
markDirtyFlags(Simulation::DIRTY_MOTION_TYPE);
EntityTreePointer tree = getTree();
if (tree) {
tree->entityChanged(getThisPointer());
}
}
}
QString EntityItem::getUserData() const { QString EntityItem::getUserData() const {
QString result; QString result;
withReadLock([&] { withReadLock([&] {

View file

@ -305,6 +305,7 @@ public:
bool getLocked() const; bool getLocked() const;
void setLocked(bool value); void setLocked(bool value);
void updateLocked(bool value);
QString getUserData() const; QString getUserData() const;
virtual void setUserData(const QString& value); virtual void setUserData(const QString& value);

View file

@ -957,6 +957,24 @@ void EntityTree::bumpTimestamp(EntityItemProperties& properties) { //fixme put c
properties.setLastEdited(properties.getLastEdited() + LAST_EDITED_SERVERSIDE_BUMP); properties.setLastEdited(properties.getLastEdited() + LAST_EDITED_SERVERSIDE_BUMP);
} }
bool EntityTree::isScriptInWhitelist(const QString& scriptProperty) {
// grab a URL representation of the entity script so we can check the host for this script
auto entityScriptURL = QUrl::fromUserInput(scriptProperty);
for (const auto& whiteListedPrefix : _entityScriptSourceWhitelist) {
auto whiteListURL = QUrl::fromUserInput(whiteListedPrefix);
// check if this script URL matches the whitelist domain and, optionally, is beneath the path
if (entityScriptURL.host().compare(whiteListURL.host(), Qt::CaseInsensitive) == 0 &&
entityScriptURL.path().startsWith(whiteListURL.path(), Qt::CaseInsensitive)) {
return true;
}
}
return false;
}
int EntityTree::processEditPacketData(ReceivedMessage& message, const unsigned char* editData, int maxLength, int EntityTree::processEditPacketData(ReceivedMessage& message, const unsigned char* editData, int maxLength,
const SharedNodePointer& senderNode) { const SharedNodePointer& senderNode) {
@ -986,7 +1004,8 @@ int EntityTree::processEditPacketData(ReceivedMessage& message, const unsigned c
quint64 startFilter = 0, endFilter = 0; quint64 startFilter = 0, endFilter = 0;
quint64 startLogging = 0, endLogging = 0; quint64 startLogging = 0, endLogging = 0;
bool suppressDisallowedScript = false; bool suppressDisallowedClientScript = false;
bool suppressDisallowedServerScript = false;
bool isPhysics = message.getType() == PacketType::EntityPhysics; bool isPhysics = message.getType() == PacketType::EntityPhysics;
_totalEditMessages++; _totalEditMessages++;
@ -1011,36 +1030,57 @@ int EntityTree::processEditPacketData(ReceivedMessage& message, const unsigned c
} }
} }
if (validEditPacket && !_entityScriptSourceWhitelist.isEmpty() && !properties.getScript().isEmpty()) { if (validEditPacket && !_entityScriptSourceWhitelist.isEmpty()) {
bool passedWhiteList = false;
// grab a URL representation of the entity script so we can check the host for this script bool wasDeletedBecauseOfClientScript = false;
auto entityScriptURL = QUrl::fromUserInput(properties.getScript());
for (const auto& whiteListedPrefix : _entityScriptSourceWhitelist) { // check the client entity script to make sure its URL is in the whitelist
auto whiteListURL = QUrl::fromUserInput(whiteListedPrefix); if (!properties.getScript().isEmpty()) {
bool clientScriptPassedWhitelist = isScriptInWhitelist(properties.getScript());
// check if this script URL matches the whitelist domain and, optionally, is beneath the path if (!clientScriptPassedWhitelist) {
if (entityScriptURL.host().compare(whiteListURL.host(), Qt::CaseInsensitive) == 0 && if (wantEditLogging()) {
entityScriptURL.path().startsWith(whiteListURL.path(), Qt::CaseInsensitive)) { qCDebug(entities) << "User [" << senderNode->getUUID()
passedWhiteList = true; << "] attempting to set entity script not on whitelist, edit rejected";
break; }
// If this was an add, we also want to tell the client that sent this edit that the entity was not added.
if (isAdd) {
QWriteLocker locker(&_recentlyDeletedEntitiesLock);
_recentlyDeletedEntityItemIDs.insert(usecTimestampNow(), entityItemID);
validEditPacket = false;
wasDeletedBecauseOfClientScript = true;
} else {
suppressDisallowedClientScript = true;
}
} }
} }
if (!passedWhiteList) {
if (wantEditLogging()) {
qCDebug(entities) << "User [" << senderNode->getUUID() << "] attempting to set entity script not on whitelist, edit rejected";
}
// If this was an add, we also want to tell the client that sent this edit that the entity was not added. // check all server entity scripts to make sure their URLs are in the whitelist
if (isAdd) { if (!properties.getServerScripts().isEmpty()) {
QWriteLocker locker(&_recentlyDeletedEntitiesLock); bool serverScriptPassedWhitelist = isScriptInWhitelist(properties.getServerScripts());
_recentlyDeletedEntityItemIDs.insert(usecTimestampNow(), entityItemID);
validEditPacket = passedWhiteList; if (!serverScriptPassedWhitelist) {
} else { if (wantEditLogging()) {
suppressDisallowedScript = true; qCDebug(entities) << "User [" << senderNode->getUUID()
<< "] attempting to set server entity script not on whitelist, edit rejected";
}
// If this was an add, we also want to tell the client that sent this edit that the entity was not added.
if (isAdd) {
// Make sure we didn't already need to send back a delete because the client script failed
// the whitelist check
if (!wasDeletedBecauseOfClientScript) {
QWriteLocker locker(&_recentlyDeletedEntitiesLock);
_recentlyDeletedEntityItemIDs.insert(usecTimestampNow(), entityItemID);
validEditPacket = false;
}
} else {
suppressDisallowedServerScript = true;
}
} }
} }
} }
if ((isAdd || properties.lifetimeChanged()) && if ((isAdd || properties.lifetimeChanged()) &&
@ -1075,11 +1115,16 @@ int EntityTree::processEditPacketData(ReceivedMessage& message, const unsigned c
if (existingEntity && !isAdd) { if (existingEntity && !isAdd) {
if (suppressDisallowedScript) { if (suppressDisallowedClientScript) {
bumpTimestamp(properties); bumpTimestamp(properties);
properties.setScript(existingEntity->getScript()); properties.setScript(existingEntity->getScript());
} }
if (suppressDisallowedServerScript) {
bumpTimestamp(properties);
properties.setServerScripts(existingEntity->getServerScripts());
}
// if the EntityItem exists, then update it // if the EntityItem exists, then update it
startLogging = usecTimestampNow(); startLogging = usecTimestampNow();
if (wantEditLogging()) { if (wantEditLogging()) {

View file

@ -303,6 +303,8 @@ protected:
void notifyNewlyCreatedEntity(const EntityItem& newEntity, const SharedNodePointer& senderNode); void notifyNewlyCreatedEntity(const EntityItem& newEntity, const SharedNodePointer& senderNode);
bool isScriptInWhitelist(const QString& scriptURL);
QReadWriteLock _newlyCreatedHooksLock; QReadWriteLock _newlyCreatedHooksLock;
QVector<NewlyCreatedEntityHook*> _newlyCreatedHooks; QVector<NewlyCreatedEntityHook*> _newlyCreatedHooks;

View file

@ -18,11 +18,10 @@ bool GLTexelFormat::isCompressed() const {
case GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT: case GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT:
case GL_COMPRESSED_RED_RGTC1: case GL_COMPRESSED_RED_RGTC1:
case GL_COMPRESSED_RG_RGTC2: case GL_COMPRESSED_RG_RGTC2:
case GL_COMPRESSED_SRGB_ALPHA_BPTC_UNORM:
return true; return true;
break;
default: default:
return false; return false;
break;
} }
} }
@ -238,6 +237,9 @@ GLenum GLTexelFormat::evalGLTexelFormatInternal(const gpu::Element& dstFormat) {
case gpu::COMPRESSED_BC5_XY: case gpu::COMPRESSED_BC5_XY:
result = GL_COMPRESSED_RG_RGTC2; result = GL_COMPRESSED_RG_RGTC2;
break; break;
case gpu::COMPRESSED_BC7_SRGBA:
result = GL_COMPRESSED_SRGB_ALPHA_BPTC_UNORM;
break;
default: default:
qCWarning(gpugllogging) << "Unknown combination of texel format"; qCWarning(gpugllogging) << "Unknown combination of texel format";
@ -364,6 +366,9 @@ GLTexelFormat GLTexelFormat::evalGLTexelFormat(const Element& dstFormat, const E
case gpu::COMPRESSED_BC5_XY: case gpu::COMPRESSED_BC5_XY:
texel.internalFormat = GL_COMPRESSED_RG_RGTC2; texel.internalFormat = GL_COMPRESSED_RG_RGTC2;
break; break;
case gpu::COMPRESSED_BC7_SRGBA:
texel.internalFormat = GL_COMPRESSED_SRGB_ALPHA_BPTC_UNORM;
break;
default: default:
qCWarning(gpugllogging) << "Unknown combination of texel format"; qCWarning(gpugllogging) << "Unknown combination of texel format";
@ -634,6 +639,10 @@ GLTexelFormat GLTexelFormat::evalGLTexelFormat(const Element& dstFormat, const E
case gpu::COMPRESSED_BC5_XY: case gpu::COMPRESSED_BC5_XY:
texel.internalFormat = GL_COMPRESSED_RG_RGTC2; texel.internalFormat = GL_COMPRESSED_RG_RGTC2;
break; break;
case gpu::COMPRESSED_BC7_SRGBA:
texel.internalFormat = GL_COMPRESSED_SRGB_ALPHA_BPTC_UNORM;
break;
default: default:
qCWarning(gpugllogging) << "Unknown combination of texel format"; qCWarning(gpugllogging) << "Unknown combination of texel format";
} }

View file

@ -535,12 +535,14 @@ void GLVariableAllocationSupport::processWorkQueue(WorkQueue& workQueue) {
vartexture->demote(); vartexture->demote();
workQueue.pop(); workQueue.pop();
addToWorkQueue(texture); addToWorkQueue(texture);
_memoryPressureStateStale = true;
break; break;
case MemoryPressureState::Undersubscribed: case MemoryPressureState::Undersubscribed:
vartexture->promote(); vartexture->promote();
workQueue.pop(); workQueue.pop();
addToWorkQueue(texture); addToWorkQueue(texture);
_memoryPressureStateStale = true;
break; break;
case MemoryPressureState::Transfer: case MemoryPressureState::Transfer:

View file

@ -112,6 +112,7 @@ void GL41Texture::copyMipFaceLinesFromTexture(uint16_t mip, uint8_t face, const
case GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT: case GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT:
case GL_COMPRESSED_RED_RGTC1: case GL_COMPRESSED_RED_RGTC1:
case GL_COMPRESSED_RG_RGTC2: case GL_COMPRESSED_RG_RGTC2:
case GL_COMPRESSED_SRGB_ALPHA_BPTC_UNORM:
glCompressedTexSubImage2D(_target, mip, 0, yOffset, size.x, size.y, internalFormat, glCompressedTexSubImage2D(_target, mip, 0, yOffset, size.x, size.y, internalFormat,
static_cast<GLsizei>(sourceSize), sourcePointer); static_cast<GLsizei>(sourceSize), sourcePointer);
break; break;
@ -128,6 +129,7 @@ void GL41Texture::copyMipFaceLinesFromTexture(uint16_t mip, uint8_t face, const
case GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT: case GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT:
case GL_COMPRESSED_RED_RGTC1: case GL_COMPRESSED_RED_RGTC1:
case GL_COMPRESSED_RG_RGTC2: case GL_COMPRESSED_RG_RGTC2:
case GL_COMPRESSED_SRGB_ALPHA_BPTC_UNORM:
glCompressedTexSubImage2D(target, mip, 0, yOffset, size.x, size.y, internalFormat, glCompressedTexSubImage2D(target, mip, 0, yOffset, size.x, size.y, internalFormat,
static_cast<GLsizei>(sourceSize), sourcePointer); static_cast<GLsizei>(sourceSize), sourcePointer);
break; break;

View file

@ -142,6 +142,7 @@ void GL45Texture::copyMipFaceLinesFromTexture(uint16_t mip, uint8_t face, const
case GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT: case GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT:
case GL_COMPRESSED_RED_RGTC1: case GL_COMPRESSED_RED_RGTC1:
case GL_COMPRESSED_RG_RGTC2: case GL_COMPRESSED_RG_RGTC2:
case GL_COMPRESSED_SRGB_ALPHA_BPTC_UNORM:
glCompressedTextureSubImage2D(_id, mip, 0, yOffset, size.x, size.y, internalFormat, glCompressedTextureSubImage2D(_id, mip, 0, yOffset, size.x, size.y, internalFormat,
static_cast<GLsizei>(sourceSize), sourcePointer); static_cast<GLsizei>(sourceSize), sourcePointer);
break; break;
@ -156,6 +157,7 @@ void GL45Texture::copyMipFaceLinesFromTexture(uint16_t mip, uint8_t face, const
case GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT: case GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT:
case GL_COMPRESSED_RED_RGTC1: case GL_COMPRESSED_RED_RGTC1:
case GL_COMPRESSED_RG_RGTC2: case GL_COMPRESSED_RG_RGTC2:
case GL_COMPRESSED_SRGB_ALPHA_BPTC_UNORM:
if (glCompressedTextureSubImage2DEXT) { if (glCompressedTextureSubImage2DEXT) {
auto target = GLTexture::CUBE_FACE_LAYOUT[face]; auto target = GLTexture::CUBE_FACE_LAYOUT[face];
glCompressedTextureSubImage2DEXT(_id, target, mip, 0, yOffset, size.x, size.y, internalFormat, glCompressedTextureSubImage2DEXT(_id, target, mip, 0, yOffset, size.x, size.y, internalFormat,

View file

@ -24,6 +24,7 @@ const Element Element::COLOR_COMPRESSED_SRGB{ VEC4, NUINT8, COMPRESSED_BC1_SRGB
const Element Element::COLOR_COMPRESSED_SRGBA_MASK{ VEC4, NUINT8, COMPRESSED_BC1_SRGBA }; const Element Element::COLOR_COMPRESSED_SRGBA_MASK{ VEC4, NUINT8, COMPRESSED_BC1_SRGBA };
const Element Element::COLOR_COMPRESSED_SRGBA{ VEC4, NUINT8, COMPRESSED_BC3_SRGBA }; const Element Element::COLOR_COMPRESSED_SRGBA{ VEC4, NUINT8, COMPRESSED_BC3_SRGBA };
const Element Element::COLOR_COMPRESSED_XY{ VEC4, NUINT8, COMPRESSED_BC5_XY }; const Element Element::COLOR_COMPRESSED_XY{ VEC4, NUINT8, COMPRESSED_BC5_XY };
const Element Element::COLOR_COMPRESSED_SRGBA_HIGH{ VEC4, NUINT8, COMPRESSED_BC7_SRGBA };
const Element Element::VEC2NU8_XY{ VEC2, NUINT8, XY }; const Element Element::VEC2NU8_XY{ VEC2, NUINT8, XY };

View file

@ -163,6 +163,7 @@ enum Semantic {
COMPRESSED_BC3_SRGBA, COMPRESSED_BC3_SRGBA,
COMPRESSED_BC4_RED, COMPRESSED_BC4_RED,
COMPRESSED_BC5_XY, COMPRESSED_BC5_XY,
COMPRESSED_BC7_SRGBA,
_LAST_COMPRESSED, _LAST_COMPRESSED,
@ -234,6 +235,7 @@ public:
static const Element COLOR_COMPRESSED_SRGBA_MASK; static const Element COLOR_COMPRESSED_SRGBA_MASK;
static const Element COLOR_COMPRESSED_SRGBA; static const Element COLOR_COMPRESSED_SRGBA;
static const Element COLOR_COMPRESSED_XY; static const Element COLOR_COMPRESSED_XY;
static const Element COLOR_COMPRESSED_SRGBA_HIGH;
static const Element VEC2NU8_XY; static const Element VEC2NU8_XY;
static const Element VEC4F_COLOR_RGBA; static const Element VEC4F_COLOR_RGBA;
static const Element VEC2F_UV; static const Element VEC2F_UV;

View file

@ -184,6 +184,11 @@ KtxStorage::KtxStorage(const std::string& filename) : _filename(filename) {
} }
std::shared_ptr<storage::FileStorage> KtxStorage::maybeOpenFile() const { std::shared_ptr<storage::FileStorage> KtxStorage::maybeOpenFile() const {
// 1. Try to get the shared ptr
// 2. If it doesn't exist, grab the mutex around its creation
// 3. If it was created before we got the mutex, return it
// 4. Otherwise, create it
std::shared_ptr<storage::FileStorage> file = _cacheFile.lock(); std::shared_ptr<storage::FileStorage> file = _cacheFile.lock();
if (file) { if (file) {
return file; return file;
@ -205,7 +210,6 @@ std::shared_ptr<storage::FileStorage> KtxStorage::maybeOpenFile() const {
} }
PixelsPointer KtxStorage::getMipFace(uint16 level, uint8 face) const { PixelsPointer KtxStorage::getMipFace(uint16 level, uint8 face) const {
storage::StoragePointer result;
auto faceOffset = _ktxDescriptor->getMipFaceTexelsOffset(level, face); auto faceOffset = _ktxDescriptor->getMipFaceTexelsOffset(level, face);
auto faceSize = _ktxDescriptor->getMipFaceTexelsSize(level, face); auto faceSize = _ktxDescriptor->getMipFaceTexelsSize(level, face);
if (faceSize != 0 && faceOffset != 0) { if (faceSize != 0 && faceOffset != 0) {
@ -221,7 +225,7 @@ PixelsPointer KtxStorage::getMipFace(uint16 level, uint8 face) const {
qWarning() << "Failed to get a valid file out of maybeOpenFile " << QString::fromStdString(_filename); qWarning() << "Failed to get a valid file out of maybeOpenFile " << QString::fromStdString(_filename);
} }
} }
return result; return nullptr;
} }
Size KtxStorage::getMipFaceSize(uint16 level, uint8 face) const { Size KtxStorage::getMipFaceSize(uint16 level, uint8 face) const {
@ -255,8 +259,18 @@ void KtxStorage::assignMipData(uint16 level, const storage::StoragePointer& stor
} }
auto file = maybeOpenFile(); auto file = maybeOpenFile();
if (!file) {
qWarning() << "Failed to open file to assign mip data " << QString::fromStdString(_filename);
return;
}
auto imageData = file->mutableData(); auto fileData = file->mutableData();
if (!fileData) {
qWarning() << "Failed to get mutable data for " << QString::fromStdString(_filename);
return;
}
auto imageData = fileData;
imageData += ktx::KTX_HEADER_SIZE + _ktxDescriptor->header.bytesOfKeyValueData + _ktxDescriptor->images[level]._imageOffset; imageData += ktx::KTX_HEADER_SIZE + _ktxDescriptor->header.bytesOfKeyValueData + _ktxDescriptor->images[level]._imageOffset;
imageData += ktx::IMAGE_SIZE_WIDTH; imageData += ktx::IMAGE_SIZE_WIDTH;
@ -271,7 +285,7 @@ void KtxStorage::assignMipData(uint16 level, const storage::StoragePointer& stor
memcpy(imageData, storage->data(), storage->size()); memcpy(imageData, storage->data(), storage->size());
_minMipLevelAvailable = level; _minMipLevelAvailable = level;
if (_offsetToMinMipKV > 0) { if (_offsetToMinMipKV > 0) {
auto minMipKeyData = file->mutableData() + ktx::KTX_HEADER_SIZE + _offsetToMinMipKV; auto minMipKeyData = fileData + ktx::KTX_HEADER_SIZE + _offsetToMinMipKV;
memcpy(minMipKeyData, (void*)&_minMipLevelAvailable, 1); memcpy(minMipKeyData, (void*)&_minMipLevelAvailable, 1);
} }
} }
@ -517,6 +531,8 @@ bool Texture::evalKTXFormat(const Element& mipFormat, const Element& texelFormat
header.setCompressed(ktx::GLInternalFormat_Compressed::COMPRESSED_RED_RGTC1, ktx::GLBaseInternalFormat::RED); header.setCompressed(ktx::GLInternalFormat_Compressed::COMPRESSED_RED_RGTC1, ktx::GLBaseInternalFormat::RED);
} else if (texelFormat == Format::COLOR_COMPRESSED_XY && mipFormat == Format::COLOR_COMPRESSED_XY) { } else if (texelFormat == Format::COLOR_COMPRESSED_XY && mipFormat == Format::COLOR_COMPRESSED_XY) {
header.setCompressed(ktx::GLInternalFormat_Compressed::COMPRESSED_RG_RGTC2, ktx::GLBaseInternalFormat::RG); header.setCompressed(ktx::GLInternalFormat_Compressed::COMPRESSED_RG_RGTC2, ktx::GLBaseInternalFormat::RG);
} else if (texelFormat == Format::COLOR_COMPRESSED_SRGBA_HIGH && mipFormat == Format::COLOR_COMPRESSED_SRGBA_HIGH) {
header.setCompressed(ktx::GLInternalFormat_Compressed::COMPRESSED_SRGB_ALPHA_BPTC_UNORM, ktx::GLBaseInternalFormat::RGBA);
} else { } else {
return false; return false;
} }
@ -575,6 +591,9 @@ bool Texture::evalTextureFormat(const ktx::Header& header, Element& mipFormat, E
} else if (header.getGLInternaFormat_Compressed() == ktx::GLInternalFormat_Compressed::COMPRESSED_RG_RGTC2) { } else if (header.getGLInternaFormat_Compressed() == ktx::GLInternalFormat_Compressed::COMPRESSED_RG_RGTC2) {
mipFormat = Format::COLOR_COMPRESSED_XY; mipFormat = Format::COLOR_COMPRESSED_XY;
texelFormat = Format::COLOR_COMPRESSED_XY; texelFormat = Format::COLOR_COMPRESSED_XY;
} else if (header.getGLInternaFormat_Compressed() == ktx::GLInternalFormat_Compressed::COMPRESSED_SRGB_ALPHA_BPTC_UNORM) {
mipFormat = Format::COLOR_COMPRESSED_SRGBA_HIGH;
texelFormat = Format::COLOR_COMPRESSED_SRGBA_HIGH;
} else { } else {
return false; return false;
} }

View file

@ -344,7 +344,7 @@ void generateMips(gpu::Texture* texture, QImage& image, int face = -1) {
nvtt::TextureType textureType = nvtt::TextureType_2D; nvtt::TextureType textureType = nvtt::TextureType_2D;
nvtt::InputFormat inputFormat = nvtt::InputFormat_BGRA_8UB; nvtt::InputFormat inputFormat = nvtt::InputFormat_BGRA_8UB;
nvtt::WrapMode wrapMode = nvtt::WrapMode_Repeat; nvtt::WrapMode wrapMode = nvtt::WrapMode_Mirror;
nvtt::RoundMode roundMode = nvtt::RoundMode_None; nvtt::RoundMode roundMode = nvtt::RoundMode_None;
nvtt::AlphaMode alphaMode = nvtt::AlphaMode_None; nvtt::AlphaMode alphaMode = nvtt::AlphaMode_None;
@ -380,6 +380,9 @@ void generateMips(gpu::Texture* texture, QImage& image, int face = -1) {
compressionOptions.setFormat(nvtt::Format_BC4); compressionOptions.setFormat(nvtt::Format_BC4);
} else if (mipFormat == gpu::Element::COLOR_COMPRESSED_XY) { } else if (mipFormat == gpu::Element::COLOR_COMPRESSED_XY) {
compressionOptions.setFormat(nvtt::Format_BC5); compressionOptions.setFormat(nvtt::Format_BC5);
} else if (mipFormat == gpu::Element::COLOR_COMPRESSED_SRGBA_HIGH) {
alphaMode = nvtt::AlphaMode_Transparency;
compressionOptions.setFormat(nvtt::Format_BC7);
} else if (mipFormat == gpu::Element::COLOR_RGBA_32) { } else if (mipFormat == gpu::Element::COLOR_RGBA_32) {
compressionOptions.setFormat(nvtt::Format_RGBA); compressionOptions.setFormat(nvtt::Format_RGBA);
compressionOptions.setPixelType(nvtt::PixelType_UnsignedNorm); compressionOptions.setPixelType(nvtt::PixelType_UnsignedNorm);
@ -934,8 +937,8 @@ gpu::TexturePointer TextureUsage::processCubeTextureColorFromImage(const QImage&
gpu::Element formatMip; gpu::Element formatMip;
gpu::Element formatGPU; gpu::Element formatGPU;
if (isCubeTexturesCompressionEnabled()) { if (isCubeTexturesCompressionEnabled()) {
formatMip = gpu::Element::COLOR_COMPRESSED_SRGBA; formatMip = gpu::Element::COLOR_COMPRESSED_SRGBA_HIGH;
formatGPU = gpu::Element::COLOR_COMPRESSED_SRGBA; formatGPU = gpu::Element::COLOR_COMPRESSED_SRGBA_HIGH;
} else { } else {
formatMip = gpu::Element::COLOR_SRGBA_32; formatMip = gpu::Element::COLOR_SRGBA_32;
formatGPU = gpu::Element::COLOR_SRGBA_32; formatGPU = gpu::Element::COLOR_SRGBA_32;

View file

@ -56,6 +56,7 @@ uint32_t Header::evalPixelOrBlockHeight(uint32_t level) const {
case GLInternalFormat_Compressed::COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT: // BC3 case GLInternalFormat_Compressed::COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT: // BC3
case GLInternalFormat_Compressed::COMPRESSED_RED_RGTC1: // BC4 case GLInternalFormat_Compressed::COMPRESSED_RED_RGTC1: // BC4
case GLInternalFormat_Compressed::COMPRESSED_RG_RGTC2: // BC5 case GLInternalFormat_Compressed::COMPRESSED_RG_RGTC2: // BC5
case GLInternalFormat_Compressed::COMPRESSED_SRGB_ALPHA_BPTC_UNORM: // BC7
return (pixelWidth + 3) / 4; return (pixelWidth + 3) / 4;
default: default:
throw std::runtime_error("Unknown format"); throw std::runtime_error("Unknown format");
@ -81,6 +82,8 @@ size_t Header::evalPixelOrBlockSize() const {
return 8; return 8;
} else if (format == GLInternalFormat_Compressed::COMPRESSED_RG_RGTC2) { } else if (format == GLInternalFormat_Compressed::COMPRESSED_RG_RGTC2) {
return 16; return 16;
} else if (format == GLInternalFormat_Compressed::COMPRESSED_SRGB_ALPHA_BPTC_UNORM) {
return 16;
} }
} else { } else {
auto baseFormat = getGLBaseInternalFormat(); auto baseFormat = getGLBaseInternalFormat();

View file

@ -149,7 +149,8 @@ namespace ktx {
for (size_t i = 0; i < descriptors.size(); ++i) { for (size_t i = 0; i < descriptors.size(); ++i) {
auto ptr = reinterpret_cast<uint32_t*>(currentDestPtr); auto ptr = reinterpret_cast<uint32_t*>(currentDestPtr);
*ptr = descriptors[i]._imageSize; uint32_t imageFaceSize = descriptors[i]._faceSize;
*ptr = imageFaceSize; // the imageSize written in the ktx is the FACE size
#ifdef DEBUG #ifdef DEBUG
ptr++; ptr++;

View file

@ -28,6 +28,7 @@
#include <SettingHandle.h> #include <SettingHandle.h>
#include "NetworkingConstants.h"
#include "NetworkLogging.h" #include "NetworkLogging.h"
#include "NodeList.h" #include "NodeList.h"
#include "udt/PacketHeaders.h" #include "udt/PacketHeaders.h"
@ -92,6 +93,7 @@ AccountManager::AccountManager(UserAgentGetter userAgentGetter) :
} }
const QString DOUBLE_SLASH_SUBSTITUTE = "slashslash"; const QString DOUBLE_SLASH_SUBSTITUTE = "slashslash";
const QString ACCOUNT_MANAGER_REQUESTED_SCOPE = "owner";
void AccountManager::logout() { void AccountManager::logout() {
// a logout means we want to delete the DataServerAccountInfo we currently have for this URL, in-memory and in file // a logout means we want to delete the DataServerAccountInfo we currently have for this URL, in-memory and in file
@ -189,6 +191,12 @@ void AccountManager::setAuthURL(const QUrl& authURL) {
requestProfile(); requestProfile();
} }
// prepare to refresh our token if it is about to expire
if (needsToRefreshToken()) {
qCDebug(networking) << "Refreshing access token since it will be expiring soon.";
refreshAccessToken();
}
// tell listeners that the auth endpoint has changed // tell listeners that the auth endpoint has changed
emit authEndpointChanged(); emit authEndpointChanged();
} }
@ -225,6 +233,10 @@ void AccountManager::sendRequest(const QString& path,
uuidStringWithoutCurlyBraces(_sessionID).toLocal8Bit()); uuidStringWithoutCurlyBraces(_sessionID).toLocal8Bit());
QUrl requestURL = _authURL; QUrl requestURL = _authURL;
if (requestURL.isEmpty()) { // Assignment client doesn't set _authURL.
requestURL = NetworkingConstants::METAVERSE_SERVER_URL;
}
if (path.startsWith("/")) { if (path.startsWith("/")) {
requestURL.setPath(path); requestURL.setPath(path);
@ -443,6 +455,12 @@ bool AccountManager::hasValidAccessToken() {
return false; return false;
} else { } else {
if (!_isWaitingForTokenRefresh && needsToRefreshToken()) {
qCDebug(networking) << "Refreshing access token since it will be expiring soon.";
refreshAccessToken();
}
return true; return true;
} }
} }
@ -458,6 +476,15 @@ bool AccountManager::checkAndSignalForAccessToken() {
return hasToken; return hasToken;
} }
bool AccountManager::needsToRefreshToken() {
if (!_accountInfo.getAccessToken().token.isEmpty()) {
qlonglong expireThreshold = QDateTime::currentDateTime().addSecs(1 * 60 * 60).toMSecsSinceEpoch();
return _accountInfo.getAccessToken().expiryTimestamp < expireThreshold;
} else {
return false;
}
}
void AccountManager::setAccessTokenForCurrentAuthURL(const QString& accessToken) { void AccountManager::setAccessTokenForCurrentAuthURL(const QString& accessToken) {
// replace the account info access token with a new OAuthAccessToken // replace the account info access token with a new OAuthAccessToken
OAuthAccessToken newOAuthToken; OAuthAccessToken newOAuthToken;
@ -490,8 +517,6 @@ void AccountManager::requestAccessToken(const QString& login, const QString& pas
QUrl grantURL = _authURL; QUrl grantURL = _authURL;
grantURL.setPath("/oauth/token"); grantURL.setPath("/oauth/token");
const QString ACCOUNT_MANAGER_REQUESTED_SCOPE = "owner";
QByteArray postData; QByteArray postData;
postData.append("grant_type=password&"); postData.append("grant_type=password&");
postData.append("username=" + login + "&"); postData.append("username=" + login + "&");
@ -515,8 +540,6 @@ void AccountManager::requestAccessTokenWithSteam(QByteArray authSessionTicket) {
QUrl grantURL = _authURL; QUrl grantURL = _authURL;
grantURL.setPath("/oauth/token"); grantURL.setPath("/oauth/token");
const QString ACCOUNT_MANAGER_REQUESTED_SCOPE = "owner";
QByteArray postData; QByteArray postData;
postData.append("grant_type=password&"); postData.append("grant_type=password&");
postData.append("steam_auth_ticket=" + QUrl::toPercentEncoding(authSessionTicket) + "&"); postData.append("steam_auth_ticket=" + QUrl::toPercentEncoding(authSessionTicket) + "&");
@ -530,6 +553,32 @@ void AccountManager::requestAccessTokenWithSteam(QByteArray authSessionTicket) {
connect(requestReply, SIGNAL(error(QNetworkReply::NetworkError)), this, SLOT(requestAccessTokenError(QNetworkReply::NetworkError))); connect(requestReply, SIGNAL(error(QNetworkReply::NetworkError)), this, SLOT(requestAccessTokenError(QNetworkReply::NetworkError)));
} }
void AccountManager::refreshAccessToken() {
_isWaitingForTokenRefresh = true;
QNetworkAccessManager& networkAccessManager = NetworkAccessManager::getInstance();
QNetworkRequest request;
request.setAttribute(QNetworkRequest::FollowRedirectsAttribute, true);
request.setHeader(QNetworkRequest::UserAgentHeader, _userAgentGetter());
QUrl grantURL = _authURL;
grantURL.setPath("/oauth/token");
QByteArray postData;
postData.append("grant_type=refresh_token&");
postData.append("refresh_token=" + QUrl::toPercentEncoding(_accountInfo.getAccessToken().refreshToken) + "&");
postData.append("scope=" + ACCOUNT_MANAGER_REQUESTED_SCOPE);
request.setUrl(grantURL);
request.setHeader(QNetworkRequest::ContentTypeHeader, "application/x-www-form-urlencoded");
QNetworkReply* requestReply = networkAccessManager.post(request, postData);
connect(requestReply, &QNetworkReply::finished, this, &AccountManager::refreshAccessTokenFinished);
connect(requestReply, SIGNAL(error(QNetworkReply::NetworkError)), this, SLOT(refreshAccessTokenError(QNetworkReply::NetworkError)));
}
void AccountManager::requestAccessTokenFinished() { void AccountManager::requestAccessTokenFinished() {
QNetworkReply* requestReply = reinterpret_cast<QNetworkReply*>(sender()); QNetworkReply* requestReply = reinterpret_cast<QNetworkReply*>(sender());
@ -568,10 +617,47 @@ void AccountManager::requestAccessTokenFinished() {
void AccountManager::requestAccessTokenError(QNetworkReply::NetworkError error) { void AccountManager::requestAccessTokenError(QNetworkReply::NetworkError error) {
// TODO: error handling // TODO: error handling
qCDebug(networking) << "AccountManager requestError - " << error; qCDebug(networking) << "AccountManager: failed to fetch access token - " << error;
emit loginFailed(); emit loginFailed();
} }
void AccountManager::refreshAccessTokenFinished() {
QNetworkReply* requestReply = reinterpret_cast<QNetworkReply*>(sender());
QJsonDocument jsonResponse = QJsonDocument::fromJson(requestReply->readAll());
const QJsonObject& rootObject = jsonResponse.object();
if (!rootObject.contains("error")) {
// construct an OAuthAccessToken from the json object
if (!rootObject.contains("access_token") || !rootObject.contains("expires_in")
|| !rootObject.contains("token_type")) {
// TODO: error handling - malformed token response
qCDebug(networking) << "Received a response for refresh grant that is missing one or more expected values.";
} else {
// clear the path from the response URL so we have the right root URL for this access token
QUrl rootURL = requestReply->url();
rootURL.setPath("");
qCDebug(networking) << "Storing an account with a refreshed access-token for" << qPrintable(rootURL.toString());
_accountInfo.setAccessTokenFromJSON(rootObject);
persistAccountToFile();
}
} else {
qCWarning(networking) << "Error in response for refresh grant - " << rootObject["error_description"].toString();
}
_isWaitingForTokenRefresh = false;
}
void AccountManager::refreshAccessTokenError(QNetworkReply::NetworkError error) {
// TODO: error handling
qCDebug(networking) << "AccountManager: failed to refresh access token - " << error;
_isWaitingForTokenRefresh = false;
}
void AccountManager::requestProfile() { void AccountManager::requestProfile() {
QNetworkAccessManager& networkAccessManager = NetworkAccessManager::getInstance(); QNetworkAccessManager& networkAccessManager = NetworkAccessManager::getInstance();

View file

@ -79,6 +79,7 @@ public:
bool isLoggedIn() { return !_authURL.isEmpty() && hasValidAccessToken(); } bool isLoggedIn() { return !_authURL.isEmpty() && hasValidAccessToken(); }
bool hasValidAccessToken(); bool hasValidAccessToken();
bool needsToRefreshToken();
Q_INVOKABLE bool checkAndSignalForAccessToken(); Q_INVOKABLE bool checkAndSignalForAccessToken();
void setAccessTokenForCurrentAuthURL(const QString& accessToken); void setAccessTokenForCurrentAuthURL(const QString& accessToken);
@ -97,10 +98,13 @@ public:
public slots: public slots:
void requestAccessToken(const QString& login, const QString& password); void requestAccessToken(const QString& login, const QString& password);
void requestAccessTokenWithSteam(QByteArray authSessionTicket); void requestAccessTokenWithSteam(QByteArray authSessionTicket);
void refreshAccessToken();
void requestAccessTokenFinished(); void requestAccessTokenFinished();
void refreshAccessTokenFinished();
void requestProfileFinished(); void requestProfileFinished();
void requestAccessTokenError(QNetworkReply::NetworkError error); void requestAccessTokenError(QNetworkReply::NetworkError error);
void refreshAccessTokenError(QNetworkReply::NetworkError error);
void requestProfileError(QNetworkReply::NetworkError error); void requestProfileError(QNetworkReply::NetworkError error);
void logout(); void logout();
void generateNewUserKeypair() { generateNewKeypair(); } void generateNewUserKeypair() { generateNewKeypair(); }
@ -141,6 +145,7 @@ private:
QMap<QNetworkReply*, JSONCallbackParameters> _pendingCallbackMap; QMap<QNetworkReply*, JSONCallbackParameters> _pendingCallbackMap;
DataServerAccountInfo _accountInfo; DataServerAccountInfo _accountInfo;
bool _isWaitingForTokenRefresh { false };
bool _isAgent { false }; bool _isAgent { false };
bool _isWaitingForKeypairResponse { false }; bool _isWaitingForKeypairResponse { false };

View file

@ -30,9 +30,9 @@
#include "udt/PacketHeaders.h" #include "udt/PacketHeaders.h"
#if USE_STABLE_GLOBAL_SERVICES #if USE_STABLE_GLOBAL_SERVICES
const QString DEFAULT_HIFI_ADDRESS = "hifi://welcome"; const QString DEFAULT_HIFI_ADDRESS = "hifi://welcome/hello";
#else #else
const QString DEFAULT_HIFI_ADDRESS = "hifi://dev-welcome"; const QString DEFAULT_HIFI_ADDRESS = "hifi://dev-welcome/hello";
#endif #endif
const QString ADDRESS_MANAGER_SETTINGS_GROUP = "AddressManager"; const QString ADDRESS_MANAGER_SETTINGS_GROUP = "AddressManager";

View file

@ -185,6 +185,10 @@ PhysicsMotionType EntityMotionState::computePhysicsMotionType() const {
return MOTION_TYPE_STATIC; return MOTION_TYPE_STATIC;
} }
if (_entity->getLocked()) {
return MOTION_TYPE_STATIC;
}
if (_entity->getDynamic()) { if (_entity->getDynamic()) {
if (!_entity->getParentID().isNull()) { if (!_entity->getParentID().isNull()) {
// if something would have been dynamic but is a child of something else, force it to be kinematic, instead. // if something would have been dynamic but is a child of something else, force it to be kinematic, instead.

View file

@ -58,6 +58,10 @@ void ObjectAction::updateAction(btCollisionWorld* collisionWorld, btScalar delta
return; return;
} }
if (ownerEntity->getLocked()) {
return;
}
updateActionWorker(deltaTimeStep); updateActionWorker(deltaTimeStep);
} }

View file

@ -17,7 +17,7 @@
using namespace render; using namespace render;
CauterizedMeshPartPayload::CauterizedMeshPartPayload(Model* model, int meshIndex, int partIndex, int shapeIndex, const Transform& transform, const Transform& offsetTransform) CauterizedMeshPartPayload::CauterizedMeshPartPayload(ModelPointer model, int meshIndex, int partIndex, int shapeIndex, const Transform& transform, const Transform& offsetTransform)
: ModelMeshPartPayload(model, meshIndex, partIndex, shapeIndex, transform, offsetTransform) {} : ModelMeshPartPayload(model, meshIndex, partIndex, shapeIndex, transform, offsetTransform) {}
void CauterizedMeshPartPayload::updateTransformForCauterizedMesh( void CauterizedMeshPartPayload::updateTransformForCauterizedMesh(
@ -29,8 +29,16 @@ void CauterizedMeshPartPayload::updateTransformForCauterizedMesh(
void CauterizedMeshPartPayload::bindTransform(gpu::Batch& batch, const render::ShapePipeline::LocationsPointer locations, RenderArgs::RenderMode renderMode) const { void CauterizedMeshPartPayload::bindTransform(gpu::Batch& batch, const render::ShapePipeline::LocationsPointer locations, RenderArgs::RenderMode renderMode) const {
// Still relying on the raw data from the model // Still relying on the raw data from the model
CauterizedModel* skeleton = static_cast<CauterizedModel*>(_model); bool useCauterizedMesh = (renderMode != RenderArgs::RenderMode::SHADOW_RENDER_MODE);
bool useCauterizedMesh = (renderMode != RenderArgs::RenderMode::SHADOW_RENDER_MODE) && skeleton->getEnableCauterization(); if (useCauterizedMesh) {
ModelPointer model = _model.lock();
if (model) {
CauterizedModel* skeleton = static_cast<CauterizedModel*>(model.get());
useCauterizedMesh = useCauterizedMesh && skeleton->getEnableCauterization();
} else {
useCauterizedMesh = false;
}
}
if (useCauterizedMesh) { if (useCauterizedMesh) {
if (_cauterizedClusterBuffer) { if (_cauterizedClusterBuffer) {

View file

@ -13,7 +13,7 @@
class CauterizedMeshPartPayload : public ModelMeshPartPayload { class CauterizedMeshPartPayload : public ModelMeshPartPayload {
public: public:
CauterizedMeshPartPayload(Model* model, int meshIndex, int partIndex, int shapeIndex, const Transform& transform, const Transform& offsetTransform); CauterizedMeshPartPayload(ModelPointer model, int meshIndex, int partIndex, int shapeIndex, const Transform& transform, const Transform& offsetTransform);
void updateTransformForCauterizedMesh(const Transform& renderTransform, const gpu::BufferPointer& buffer); void updateTransformForCauterizedMesh(const Transform& renderTransform, const gpu::BufferPointer& buffer);

View file

@ -16,8 +16,8 @@
#include "RenderUtilsLogging.h" #include "RenderUtilsLogging.h"
CauterizedModel::CauterizedModel(RigPointer rig, QObject* parent) : CauterizedModel::CauterizedModel(QObject* parent) :
Model(rig, parent) { Model(parent) {
} }
CauterizedModel::~CauterizedModel() { CauterizedModel::~CauterizedModel() {
@ -78,7 +78,7 @@ void CauterizedModel::createVisibleRenderItemSet() {
// Create the render payloads // Create the render payloads
int numParts = (int)mesh->getNumParts(); int numParts = (int)mesh->getNumParts();
for (int partIndex = 0; partIndex < numParts; partIndex++) { for (int partIndex = 0; partIndex < numParts; partIndex++) {
auto ptr = std::make_shared<CauterizedMeshPartPayload>(this, i, partIndex, shapeID, transform, offset); auto ptr = std::make_shared<CauterizedMeshPartPayload>(shared_from_this(), i, partIndex, shapeID, transform, offset);
_modelMeshRenderItems << std::static_pointer_cast<ModelMeshPartPayload>(ptr); _modelMeshRenderItems << std::static_pointer_cast<ModelMeshPartPayload>(ptr);
shapeID++; shapeID++;
} }
@ -107,7 +107,7 @@ void CauterizedModel::updateClusterMatrices() {
const FBXMesh& mesh = geometry.meshes.at(i); const FBXMesh& mesh = geometry.meshes.at(i);
for (int j = 0; j < mesh.clusters.size(); j++) { for (int j = 0; j < mesh.clusters.size(); j++) {
const FBXCluster& cluster = mesh.clusters.at(j); const FBXCluster& cluster = mesh.clusters.at(j);
auto jointMatrix = _rig->getJointTransform(cluster.jointIndex); auto jointMatrix = _rig.getJointTransform(cluster.jointIndex);
glm_mat4u_mul(jointMatrix, cluster.inverseBindMatrix, state.clusterMatrices[j]); glm_mat4u_mul(jointMatrix, cluster.inverseBindMatrix, state.clusterMatrices[j]);
} }
@ -130,14 +130,14 @@ void CauterizedModel::updateClusterMatrices() {
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), glm::vec4(0.0f, 0.0f, 0.0f, 0.0f),
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;
for (int i = 0; i < _cauterizeMeshStates.size(); i++) { for (int i = 0; i < _cauterizeMeshStates.size(); i++) {
Model::MeshState& state = _cauterizeMeshStates[i]; Model::MeshState& state = _cauterizeMeshStates[i];
const FBXMesh& mesh = geometry.meshes.at(i); const FBXMesh& mesh = geometry.meshes.at(i);
for (int j = 0; j < mesh.clusters.size(); j++) { for (int j = 0; j < mesh.clusters.size(); j++) {
const FBXCluster& cluster = mesh.clusters.at(j); const FBXCluster& cluster = mesh.clusters.at(j);
auto jointMatrix = _rig->getJointTransform(cluster.jointIndex); auto jointMatrix = _rig.getJointTransform(cluster.jointIndex);
if (_cauterizeBoneSet.find(cluster.jointIndex) != _cauterizeBoneSet.end()) { if (_cauterizeBoneSet.find(cluster.jointIndex) != _cauterizeBoneSet.end()) {
jointMatrix = cauterizeMatrix; jointMatrix = cauterizeMatrix;
} }
@ -207,11 +207,12 @@ void CauterizedModel::updateRenderItems() {
QList<render::ItemID> keys = self->getRenderItems().keys(); QList<render::ItemID> keys = self->getRenderItems().keys();
foreach (auto itemID, keys) { foreach (auto itemID, keys) {
transaction.updateItem<CauterizedMeshPartPayload>(itemID, [modelTransform, deleteGeometryCounter](CauterizedMeshPartPayload& data) { transaction.updateItem<CauterizedMeshPartPayload>(itemID, [modelTransform, deleteGeometryCounter](CauterizedMeshPartPayload& data) {
if (data._model && data._model->isLoaded()) { ModelPointer model = data._model.lock();
if (model && model->isLoaded()) {
// Ensure the model geometry was not reset between frames // Ensure the model geometry was not reset between frames
if (deleteGeometryCounter == data._model->getGeometryCounter()) { if (deleteGeometryCounter == model->getGeometryCounter()) {
// this stuff identical to what happens in regular Model // this stuff identical to what happens in regular Model
const Model::MeshState& state = data._model->getMeshState(data._meshIndex); const Model::MeshState& state = model->getMeshState(data._meshIndex);
Transform renderTransform = modelTransform; Transform renderTransform = modelTransform;
if (state.clusterMatrices.size() == 1) { if (state.clusterMatrices.size() == 1) {
renderTransform = modelTransform.worldTransform(Transform(state.clusterMatrices[0])); renderTransform = modelTransform.worldTransform(Transform(state.clusterMatrices[0]));
@ -219,7 +220,7 @@ void CauterizedModel::updateRenderItems() {
data.updateTransformForSkinnedMesh(renderTransform, modelTransform, state.clusterBuffer); data.updateTransformForSkinnedMesh(renderTransform, modelTransform, state.clusterBuffer);
// this stuff for cauterized mesh // this stuff for cauterized mesh
CauterizedModel* cModel = static_cast<CauterizedModel*>(data._model); CauterizedModel* cModel = static_cast<CauterizedModel*>(model.get());
const Model::MeshState& cState = cModel->getCauterizeMeshState(data._meshIndex); const Model::MeshState& cState = cModel->getCauterizeMeshState(data._meshIndex);
renderTransform = modelTransform; renderTransform = modelTransform;
if (cState.clusterMatrices.size() == 1) { if (cState.clusterMatrices.size() == 1) {

View file

@ -16,7 +16,7 @@ class CauterizedModel : public Model {
Q_OBJECT Q_OBJECT
public: public:
CauterizedModel(RigPointer rig, QObject* parent); CauterizedModel(QObject* parent);
virtual ~CauterizedModel(); virtual ~CauterizedModel();
void flagAsCauterized() { _isCauterized = true; } void flagAsCauterized() { _isCauterized = true; }

View file

@ -14,7 +14,6 @@
#include <PerfStat.h> #include <PerfStat.h>
#include "DeferredLightingEffect.h" #include "DeferredLightingEffect.h"
#include "Model.h"
#include "EntityItem.h" #include "EntityItem.h"
using namespace render; using namespace render;
@ -321,13 +320,13 @@ template <> void payloadRender(const ModelMeshPartPayload::Pointer& payload, Ren
} }
} }
ModelMeshPartPayload::ModelMeshPartPayload(Model* model, int _meshIndex, int partIndex, int shapeIndex, const Transform& transform, const Transform& offsetTransform) : ModelMeshPartPayload::ModelMeshPartPayload(ModelPointer model, int _meshIndex, int partIndex, int shapeIndex, const Transform& transform, const Transform& offsetTransform) :
_model(model),
_meshIndex(_meshIndex), _meshIndex(_meshIndex),
_shapeID(shapeIndex) { _shapeID(shapeIndex) {
assert(_model && _model->isLoaded()); assert(model && model->isLoaded());
auto& modelMesh = _model->getGeometry()->getMeshes().at(_meshIndex); _model = model;
auto& modelMesh = model->getGeometry()->getMeshes().at(_meshIndex);
updateMeshPart(modelMesh, partIndex); updateMeshPart(modelMesh, partIndex);
updateTransform(transform, offsetTransform); updateTransform(transform, offsetTransform);
@ -335,20 +334,21 @@ ModelMeshPartPayload::ModelMeshPartPayload(Model* model, int _meshIndex, int par
} }
void ModelMeshPartPayload::initCache() { void ModelMeshPartPayload::initCache() {
assert(_model->isLoaded()); ModelPointer model = _model.lock();
assert(model && model->isLoaded());
if (_drawMesh) { if (_drawMesh) {
auto vertexFormat = _drawMesh->getVertexFormat(); auto vertexFormat = _drawMesh->getVertexFormat();
_hasColorAttrib = vertexFormat->hasAttribute(gpu::Stream::COLOR); _hasColorAttrib = vertexFormat->hasAttribute(gpu::Stream::COLOR);
_isSkinned = vertexFormat->hasAttribute(gpu::Stream::SKIN_CLUSTER_WEIGHT) && vertexFormat->hasAttribute(gpu::Stream::SKIN_CLUSTER_INDEX); _isSkinned = vertexFormat->hasAttribute(gpu::Stream::SKIN_CLUSTER_WEIGHT) && vertexFormat->hasAttribute(gpu::Stream::SKIN_CLUSTER_INDEX);
const FBXGeometry& geometry = _model->getFBXGeometry(); const FBXGeometry& geometry = model->getFBXGeometry();
const FBXMesh& mesh = geometry.meshes.at(_meshIndex); const FBXMesh& mesh = geometry.meshes.at(_meshIndex);
_isBlendShaped = !mesh.blendshapes.isEmpty(); _isBlendShaped = !mesh.blendshapes.isEmpty();
} }
auto networkMaterial = _model->getGeometry()->getShapeMaterial(_shapeID); auto networkMaterial = model->getGeometry()->getShapeMaterial(_shapeID);
if (networkMaterial) { if (networkMaterial) {
_drawMaterial = networkMaterial; _drawMaterial = networkMaterial;
} }
@ -370,29 +370,31 @@ ItemKey ModelMeshPartPayload::getKey() const {
ItemKey::Builder builder; ItemKey::Builder builder;
builder.withTypeShape(); builder.withTypeShape();
if (!_model->isVisible()) { ModelPointer model = _model.lock();
builder.withInvisible(); if (model) {
} if (!model->isVisible()) {
builder.withInvisible();
}
if (_model->isLayeredInFront()) { if (model->isLayeredInFront()) {
builder.withLayered(); builder.withLayered();
} }
if (_isBlendShaped || _isSkinned) { if (_isBlendShaped || _isSkinned) {
builder.withDeformed(); builder.withDeformed();
} }
if (_drawMaterial) { if (_drawMaterial) {
auto matKey = _drawMaterial->getKey(); auto matKey = _drawMaterial->getKey();
if (matKey.isTranslucent()) { if (matKey.isTranslucent()) {
builder.withTransparent();
}
}
if (_fadeState != FADE_COMPLETE) {
builder.withTransparent(); builder.withTransparent();
} }
} }
if (_fadeState != FADE_COMPLETE) {
builder.withTransparent();
}
return builder.build(); return builder.build();
} }
@ -400,7 +402,8 @@ int ModelMeshPartPayload::getLayer() const {
// MAgic number while we are defining the layering mechanism: // MAgic number while we are defining the layering mechanism:
const int LAYER_3D_FRONT = 1; const int LAYER_3D_FRONT = 1;
const int LAYER_3D = 0; const int LAYER_3D = 0;
if (_model->isLayeredInFront()) { ModelPointer model = _model.lock();
if (model && model->isLayeredInFront()) {
return LAYER_3D_FRONT; return LAYER_3D_FRONT;
} else { } else {
return LAYER_3D; return LAYER_3D;
@ -410,15 +413,16 @@ int ModelMeshPartPayload::getLayer() const {
ShapeKey ModelMeshPartPayload::getShapeKey() const { ShapeKey ModelMeshPartPayload::getShapeKey() const {
// guard against partially loaded meshes // guard against partially loaded meshes
if (!_model || !_model->isLoaded() || !_model->getGeometry()) { ModelPointer model = _model.lock();
if (!model || !model->isLoaded() || !model->getGeometry()) {
return ShapeKey::Builder::invalid(); return ShapeKey::Builder::invalid();
} }
const FBXGeometry& geometry = _model->getFBXGeometry(); const FBXGeometry& geometry = model->getFBXGeometry();
const auto& networkMeshes = _model->getGeometry()->getMeshes(); const auto& networkMeshes = model->getGeometry()->getMeshes();
// guard against partially loaded meshes // guard against partially loaded meshes
if (_meshIndex >= (int)networkMeshes.size() || _meshIndex >= (int)geometry.meshes.size() || _meshIndex >= (int)_model->_meshStates.size()) { if (_meshIndex >= (int)networkMeshes.size() || _meshIndex >= (int)geometry.meshes.size() || _meshIndex >= (int)model->_meshStates.size()) {
return ShapeKey::Builder::invalid(); return ShapeKey::Builder::invalid();
} }
@ -427,8 +431,8 @@ ShapeKey ModelMeshPartPayload::getShapeKey() const {
// if our index is ever out of range for either meshes or networkMeshes, then skip it, and set our _meshGroupsKnown // if our index is ever out of range for either meshes or networkMeshes, then skip it, and set our _meshGroupsKnown
// to false to rebuild out mesh groups. // to false to rebuild out mesh groups.
if (_meshIndex < 0 || _meshIndex >= (int)networkMeshes.size() || _meshIndex > geometry.meshes.size()) { if (_meshIndex < 0 || _meshIndex >= (int)networkMeshes.size() || _meshIndex > geometry.meshes.size()) {
_model->_needsFixupInScene = true; // trigger remove/add cycle model->_needsFixupInScene = true; // trigger remove/add cycle
_model->invalidCalculatedMeshBoxes(); // if we have to reload, we need to assume our mesh boxes are all invalid model->invalidCalculatedMeshBoxes(); // if we have to reload, we need to assume our mesh boxes are all invalid
return ShapeKey::Builder::invalid(); return ShapeKey::Builder::invalid();
} }
@ -452,7 +456,7 @@ ShapeKey ModelMeshPartPayload::getShapeKey() const {
bool isUnlit = drawMaterialKey.isUnlit(); bool isUnlit = drawMaterialKey.isUnlit();
bool isSkinned = _isSkinned; bool isSkinned = _isSkinned;
bool wireframe = _model->isWireframe(); bool wireframe = model->isWireframe();
if (wireframe) { if (wireframe) {
isTranslucent = hasTangents = hasSpecular = hasLightmap = isSkinned = false; isTranslucent = hasTangents = hasSpecular = hasLightmap = isSkinned = false;
@ -488,18 +492,22 @@ ShapeKey ModelMeshPartPayload::getShapeKey() const {
void ModelMeshPartPayload::bindMesh(gpu::Batch& batch) { void ModelMeshPartPayload::bindMesh(gpu::Batch& batch) {
if (!_isBlendShaped) { if (!_isBlendShaped) {
batch.setIndexBuffer(gpu::UINT32, (_drawMesh->getIndexBuffer()._buffer), 0); batch.setIndexBuffer(gpu::UINT32, (_drawMesh->getIndexBuffer()._buffer), 0);
batch.setInputFormat((_drawMesh->getVertexFormat())); batch.setInputFormat((_drawMesh->getVertexFormat()));
batch.setInputStream(0, _drawMesh->getVertexStream()); batch.setInputStream(0, _drawMesh->getVertexStream());
} else { } else {
batch.setIndexBuffer(gpu::UINT32, (_drawMesh->getIndexBuffer()._buffer), 0); batch.setIndexBuffer(gpu::UINT32, (_drawMesh->getIndexBuffer()._buffer), 0);
batch.setInputFormat((_drawMesh->getVertexFormat())); batch.setInputFormat((_drawMesh->getVertexFormat()));
batch.setInputBuffer(0, _model->_blendedVertexBuffers[_meshIndex], 0, sizeof(glm::vec3)); ModelPointer model = _model.lock();
batch.setInputBuffer(1, _model->_blendedVertexBuffers[_meshIndex], _drawMesh->getNumVertices() * sizeof(glm::vec3), sizeof(glm::vec3)); if (model) {
batch.setInputStream(2, _drawMesh->getVertexStream().makeRangedStream(2)); batch.setInputBuffer(0, model->_blendedVertexBuffers[_meshIndex], 0, sizeof(glm::vec3));
batch.setInputBuffer(1, model->_blendedVertexBuffers[_meshIndex], _drawMesh->getNumVertices() * sizeof(glm::vec3), sizeof(glm::vec3));
batch.setInputStream(2, _drawMesh->getVertexStream().makeRangedStream(2));
} else {
batch.setIndexBuffer(gpu::UINT32, (_drawMesh->getIndexBuffer()._buffer), 0);
batch.setInputFormat((_drawMesh->getVertexFormat()));
batch.setInputStream(0, _drawMesh->getVertexStream());
}
} }
if (_fadeState != FADE_COMPLETE) { if (_fadeState != FADE_COMPLETE) {
@ -530,7 +538,10 @@ float ModelMeshPartPayload::computeFadeAlpha() {
if (fadeAlpha >= 1.0f) { if (fadeAlpha >= 1.0f) {
_fadeState = FADE_COMPLETE; _fadeState = FADE_COMPLETE;
// when fade-in completes we flag model for one last "render item update" // when fade-in completes we flag model for one last "render item update"
_model->setRenderItemsNeedUpdate(); ModelPointer model = _model.lock();
if (model) {
model->setRenderItemsNeedUpdate();
}
return 1.0f; return 1.0f;
} }
return Interpolate::simpleNonLinearBlend(fadeAlpha); return Interpolate::simpleNonLinearBlend(fadeAlpha);
@ -539,26 +550,27 @@ float ModelMeshPartPayload::computeFadeAlpha() {
void ModelMeshPartPayload::render(RenderArgs* args) { void ModelMeshPartPayload::render(RenderArgs* args) {
PerformanceTimer perfTimer("ModelMeshPartPayload::render"); PerformanceTimer perfTimer("ModelMeshPartPayload::render");
if (!_model->addedToScene() || !_model->isVisible()) { ModelPointer model = _model.lock();
if (!model || !model->addedToScene() || !model->isVisible()) {
return; // bail asap return; // bail asap
} }
if (_fadeState == FADE_WAITING_TO_START) { if (_fadeState == FADE_WAITING_TO_START) {
if (_model->isLoaded()) { if (model->isLoaded()) {
if (EntityItem::getEntitiesShouldFadeFunction()()) { if (EntityItem::getEntitiesShouldFadeFunction()()) {
_fadeStartTime = usecTimestampNow(); _fadeStartTime = usecTimestampNow();
_fadeState = FADE_IN_PROGRESS; _fadeState = FADE_IN_PROGRESS;
} else { } else {
_fadeState = FADE_COMPLETE; _fadeState = FADE_COMPLETE;
} }
_model->setRenderItemsNeedUpdate(); model->setRenderItemsNeedUpdate();
} else { } else {
return; return;
} }
} }
if (_materialNeedsUpdate && _model->getGeometry()->areTexturesLoaded()) { if (_materialNeedsUpdate && model->getGeometry()->areTexturesLoaded()) {
_model->setRenderItemsNeedUpdate(); model->setRenderItemsNeedUpdate();
_materialNeedsUpdate = false; _materialNeedsUpdate = false;
} }

View file

@ -21,6 +21,8 @@
#include <model/Geometry.h> #include <model/Geometry.h>
#include "Model.h"
const uint8_t FADE_WAITING_TO_START = 0; const uint8_t FADE_WAITING_TO_START = 0;
const uint8_t FADE_IN_PROGRESS = 1; const uint8_t FADE_IN_PROGRESS = 1;
const uint8_t FADE_COMPLETE = 2; const uint8_t FADE_COMPLETE = 2;
@ -83,7 +85,7 @@ namespace render {
class ModelMeshPartPayload : public MeshPartPayload { class ModelMeshPartPayload : public MeshPartPayload {
public: public:
ModelMeshPartPayload(Model* model, int meshIndex, int partIndex, int shapeIndex, const Transform& transform, const Transform& offsetTransform); ModelMeshPartPayload(ModelPointer model, int meshIndex, int partIndex, int shapeIndex, const Transform& transform, const Transform& offsetTransform);
typedef render::Payload<ModelMeshPartPayload> Payload; typedef render::Payload<ModelMeshPartPayload> Payload;
typedef Payload::DataPointer Pointer; typedef Payload::DataPointer Pointer;
@ -110,7 +112,7 @@ public:
void computeAdjustedLocalBound(const QVector<glm::mat4>& clusterMatrices); void computeAdjustedLocalBound(const QVector<glm::mat4>& clusterMatrices);
gpu::BufferPointer _clusterBuffer; gpu::BufferPointer _clusterBuffer;
Model* _model; ModelWeakPointer _model;
int _meshIndex; int _meshIndex;
int _shapeID; int _shapeID;

View file

@ -78,7 +78,7 @@ void initCollisionMaterials() {
} }
} }
Model::Model(RigPointer rig, QObject* parent, SpatiallyNestable* spatiallyNestableOverride) : Model::Model(QObject* parent, SpatiallyNestable* spatiallyNestableOverride) :
QObject(parent), QObject(parent),
_renderGeometry(), _renderGeometry(),
_collisionGeometry(), _collisionGeometry(),
@ -96,8 +96,7 @@ Model::Model(RigPointer rig, QObject* parent, SpatiallyNestable* spatiallyNestab
_isVisible(true), _isVisible(true),
_blendNumber(0), _blendNumber(0),
_appliedBlendNumber(0), _appliedBlendNumber(0),
_isWireframe(false), _isWireframe(false)
_rig(rig)
{ {
// we may have been created in the network thread, but we live in the main thread // we may have been created in the network thread, but we live in the main thread
if (_viewState) { if (_viewState) {
@ -236,13 +235,14 @@ void Model::updateRenderItems() {
render::Transaction transaction; render::Transaction transaction;
foreach (auto itemID, self->_modelMeshRenderItemsMap.keys()) { foreach (auto itemID, self->_modelMeshRenderItemsMap.keys()) {
transaction.updateItem<ModelMeshPartPayload>(itemID, [deleteGeometryCounter](ModelMeshPartPayload& data) { transaction.updateItem<ModelMeshPartPayload>(itemID, [deleteGeometryCounter](ModelMeshPartPayload& data) {
if (data._model && data._model->isLoaded()) { ModelPointer model = data._model.lock();
if (model && model->isLoaded()) {
// Ensure the model geometry was not reset between frames // Ensure the model geometry was not reset between frames
if (deleteGeometryCounter == data._model->_deleteGeometryCounter) { if (deleteGeometryCounter == model->_deleteGeometryCounter) {
Transform modelTransform = data._model->getTransform(); Transform modelTransform = model->getTransform();
modelTransform.setScale(glm::vec3(1.0f)); modelTransform.setScale(glm::vec3(1.0f));
const Model::MeshState& state = data._model->getMeshState(data._meshIndex); const Model::MeshState& state = model->getMeshState(data._meshIndex);
Transform renderTransform = modelTransform; Transform renderTransform = modelTransform;
if (state.clusterMatrices.size() == 1) { if (state.clusterMatrices.size() == 1) {
renderTransform = modelTransform.worldTransform(Transform(state.clusterMatrices[0])); renderTransform = modelTransform.worldTransform(Transform(state.clusterMatrices[0]));
@ -271,7 +271,7 @@ void Model::updateRenderItems() {
void Model::initJointTransforms() { void Model::initJointTransforms() {
if (isLoaded()) { if (isLoaded()) {
glm::mat4 modelOffset = glm::scale(_scale) * glm::translate(_offset); glm::mat4 modelOffset = glm::scale(_scale) * glm::translate(_offset);
_rig->setModelOffset(modelOffset); _rig.setModelOffset(modelOffset);
} }
} }
@ -281,7 +281,7 @@ void Model::init() {
void Model::reset() { void Model::reset() {
if (isLoaded()) { if (isLoaded()) {
const FBXGeometry& geometry = getFBXGeometry(); const FBXGeometry& geometry = getFBXGeometry();
_rig->reset(geometry); _rig.reset(geometry);
} }
} }
@ -294,7 +294,8 @@ bool Model::updateGeometry() {
_needsReload = false; _needsReload = false;
if (_rig->jointStatesEmpty() && getFBXGeometry().joints.size() > 0) { // TODO: should all Models have a valid _rig?
if (_rig.jointStatesEmpty() && getFBXGeometry().joints.size() > 0) {
initJointStates(); initJointStates();
assert(_meshStates.empty()); assert(_meshStates.empty());
@ -326,7 +327,7 @@ void Model::initJointStates() {
const FBXGeometry& geometry = getFBXGeometry(); const FBXGeometry& geometry = getFBXGeometry();
glm::mat4 modelOffset = glm::scale(_scale) * glm::translate(_offset); glm::mat4 modelOffset = glm::scale(_scale) * glm::translate(_offset);
_rig->initJointStates(geometry, modelOffset); _rig.initJointStates(geometry, modelOffset);
} }
bool Model::findRayIntersectionAgainstSubMeshes(const glm::vec3& origin, const glm::vec3& direction, float& distance, bool Model::findRayIntersectionAgainstSubMeshes(const glm::vec3& origin, const glm::vec3& direction, float& distance,
@ -746,19 +747,19 @@ Extents Model::getUnscaledMeshExtents() const {
} }
void Model::clearJointState(int index) { void Model::clearJointState(int index) {
_rig->clearJointState(index); _rig.clearJointState(index);
} }
void Model::setJointState(int index, bool valid, const glm::quat& rotation, const glm::vec3& translation, float priority) { void Model::setJointState(int index, bool valid, const glm::quat& rotation, const glm::vec3& translation, float priority) {
_rig->setJointState(index, valid, rotation, translation, priority); _rig.setJointState(index, valid, rotation, translation, priority);
} }
void Model::setJointRotation(int index, bool valid, const glm::quat& rotation, float priority) { void Model::setJointRotation(int index, bool valid, const glm::quat& rotation, float priority) {
_rig->setJointRotation(index, valid, rotation, priority); _rig.setJointRotation(index, valid, rotation, priority);
} }
void Model::setJointTranslation(int index, bool valid, const glm::vec3& translation, float priority) { void Model::setJointTranslation(int index, bool valid, const glm::vec3& translation, float priority) {
_rig->setJointTranslation(index, valid, translation, priority); _rig.setJointTranslation(index, valid, translation, priority);
} }
int Model::getParentJointIndex(int jointIndex) const { int Model::getParentJointIndex(int jointIndex) const {
@ -810,8 +811,10 @@ void Model::setURL(const QUrl& url) {
deleteGeometry(); deleteGeometry();
auto resource = DependencyManager::get<ModelCache>()->getGeometryResource(url); auto resource = DependencyManager::get<ModelCache>()->getGeometryResource(url);
resource->setLoadPriority(this, _loadingPriority); if (resource) {
_renderWatcher.setResource(resource); resource->setLoadPriority(this, _loadingPriority);
_renderWatcher.setResource(resource);
}
onInvalidate(); onInvalidate();
} }
@ -823,43 +826,43 @@ void Model::loadURLFinished(bool success) {
} }
bool Model::getJointPositionInWorldFrame(int jointIndex, glm::vec3& position) const { bool Model::getJointPositionInWorldFrame(int jointIndex, glm::vec3& position) const {
return _rig->getJointPositionInWorldFrame(jointIndex, position, _translation, _rotation); return _rig.getJointPositionInWorldFrame(jointIndex, position, _translation, _rotation);
} }
bool Model::getJointPosition(int jointIndex, glm::vec3& position) const { bool Model::getJointPosition(int jointIndex, glm::vec3& position) const {
return _rig->getJointPosition(jointIndex, position); return _rig.getJointPosition(jointIndex, position);
} }
bool Model::getJointRotationInWorldFrame(int jointIndex, glm::quat& rotation) const { bool Model::getJointRotationInWorldFrame(int jointIndex, glm::quat& rotation) const {
return _rig->getJointRotationInWorldFrame(jointIndex, rotation, _rotation); return _rig.getJointRotationInWorldFrame(jointIndex, rotation, _rotation);
} }
bool Model::getJointRotation(int jointIndex, glm::quat& rotation) const { bool Model::getJointRotation(int jointIndex, glm::quat& rotation) const {
return _rig->getJointRotation(jointIndex, rotation); return _rig.getJointRotation(jointIndex, rotation);
} }
bool Model::getJointTranslation(int jointIndex, glm::vec3& translation) const { bool Model::getJointTranslation(int jointIndex, glm::vec3& translation) const {
return _rig->getJointTranslation(jointIndex, translation); return _rig.getJointTranslation(jointIndex, translation);
} }
bool Model::getAbsoluteJointRotationInRigFrame(int jointIndex, glm::quat& rotationOut) const { bool Model::getAbsoluteJointRotationInRigFrame(int jointIndex, glm::quat& rotationOut) const {
return _rig->getAbsoluteJointRotationInRigFrame(jointIndex, rotationOut); return _rig.getAbsoluteJointRotationInRigFrame(jointIndex, rotationOut);
} }
bool Model::getAbsoluteJointTranslationInRigFrame(int jointIndex, glm::vec3& translationOut) const { bool Model::getAbsoluteJointTranslationInRigFrame(int jointIndex, glm::vec3& translationOut) const {
return _rig->getAbsoluteJointTranslationInRigFrame(jointIndex, translationOut); return _rig.getAbsoluteJointTranslationInRigFrame(jointIndex, translationOut);
} }
bool Model::getRelativeDefaultJointRotation(int jointIndex, glm::quat& rotationOut) const { bool Model::getRelativeDefaultJointRotation(int jointIndex, glm::quat& rotationOut) const {
return _rig->getRelativeDefaultJointRotation(jointIndex, rotationOut); return _rig.getRelativeDefaultJointRotation(jointIndex, rotationOut);
} }
bool Model::getRelativeDefaultJointTranslation(int jointIndex, glm::vec3& translationOut) const { bool Model::getRelativeDefaultJointTranslation(int jointIndex, glm::vec3& translationOut) const {
return _rig->getRelativeDefaultJointTranslation(jointIndex, translationOut); return _rig.getRelativeDefaultJointTranslation(jointIndex, translationOut);
} }
bool Model::getJointCombinedRotation(int jointIndex, glm::quat& rotation) const { bool Model::getJointCombinedRotation(int jointIndex, glm::quat& rotation) const {
return _rig->getJointCombinedRotation(jointIndex, rotation, _rotation); return _rig.getJointCombinedRotation(jointIndex, rotation, _rotation);
} }
QStringList Model::getJointNames() const { QStringList Model::getJointNames() const {
@ -1047,7 +1050,7 @@ void Model::simulate(float deltaTime, bool fullUpdate) {
void Model::updateRig(float deltaTime, glm::mat4 parentTransform) { void Model::updateRig(float deltaTime, glm::mat4 parentTransform) {
_needsUpdateClusterMatrices = true; _needsUpdateClusterMatrices = true;
glm::mat4 rigToWorldTransform = createMatFromQuatAndPos(getRotation(), getTranslation()); glm::mat4 rigToWorldTransform = createMatFromQuatAndPos(getRotation(), getTranslation());
_rig->updateAnimations(deltaTime, parentTransform, rigToWorldTransform); _rig.updateAnimations(deltaTime, parentTransform, rigToWorldTransform);
} }
void Model::computeMeshPartLocalBounds() { void Model::computeMeshPartLocalBounds() {
@ -1071,7 +1074,7 @@ void Model::updateClusterMatrices() {
const FBXMesh& mesh = geometry.meshes.at(i); const FBXMesh& mesh = geometry.meshes.at(i);
for (int j = 0; j < mesh.clusters.size(); j++) { for (int j = 0; j < mesh.clusters.size(); j++) {
const FBXCluster& cluster = mesh.clusters.at(j); const FBXCluster& cluster = mesh.clusters.at(j);
auto jointMatrix = _rig->getJointTransform(cluster.jointIndex); auto jointMatrix = _rig.getJointTransform(cluster.jointIndex);
glm_mat4u_mul(jointMatrix, cluster.inverseBindMatrix, state.clusterMatrices[j]); glm_mat4u_mul(jointMatrix, cluster.inverseBindMatrix, state.clusterMatrices[j]);
} }
@ -1098,19 +1101,19 @@ void Model::inverseKinematics(int endIndex, glm::vec3 targetPosition, const glm:
const FBXGeometry& geometry = getFBXGeometry(); const FBXGeometry& geometry = getFBXGeometry();
const QVector<int>& freeLineage = geometry.joints.at(endIndex).freeLineage; const QVector<int>& freeLineage = geometry.joints.at(endIndex).freeLineage;
glm::mat4 parentTransform = glm::scale(_scale) * glm::translate(_offset); glm::mat4 parentTransform = glm::scale(_scale) * glm::translate(_offset);
_rig->inverseKinematics(endIndex, targetPosition, targetRotation, priority, freeLineage, parentTransform); _rig.inverseKinematics(endIndex, targetPosition, targetRotation, priority, freeLineage, parentTransform);
} }
bool Model::restoreJointPosition(int jointIndex, float fraction, float priority) { bool Model::restoreJointPosition(int jointIndex, float fraction, float priority) {
const FBXGeometry& geometry = getFBXGeometry(); const FBXGeometry& geometry = getFBXGeometry();
const QVector<int>& freeLineage = geometry.joints.at(jointIndex).freeLineage; const QVector<int>& freeLineage = geometry.joints.at(jointIndex).freeLineage;
return _rig->restoreJointPosition(jointIndex, fraction, priority, freeLineage); return _rig.restoreJointPosition(jointIndex, fraction, priority, freeLineage);
} }
float Model::getLimbLength(int jointIndex) const { float Model::getLimbLength(int jointIndex) const {
const FBXGeometry& geometry = getFBXGeometry(); const FBXGeometry& geometry = getFBXGeometry();
const QVector<int>& freeLineage = geometry.joints.at(jointIndex).freeLineage; const QVector<int>& freeLineage = geometry.joints.at(jointIndex).freeLineage;
return _rig->getLimbLength(jointIndex, freeLineage, _scale, geometry.joints); return _rig.getLimbLength(jointIndex, freeLineage, _scale, geometry.joints);
} }
bool Model::maybeStartBlender() { bool Model::maybeStartBlender() {
@ -1153,7 +1156,7 @@ void Model::deleteGeometry() {
_deleteGeometryCounter++; _deleteGeometryCounter++;
_blendedVertexBuffers.clear(); _blendedVertexBuffers.clear();
_meshStates.clear(); _meshStates.clear();
_rig->destroyAnimGraph(); _rig.destroyAnimGraph();
_blendedBlendshapeCoefficients.clear(); _blendedBlendshapeCoefficients.clear();
_renderGeometry.reset(); _renderGeometry.reset();
_collisionGeometry.reset(); _collisionGeometry.reset();
@ -1223,7 +1226,7 @@ void Model::createVisibleRenderItemSet() {
// Create the render payloads // Create the render payloads
int numParts = (int)mesh->getNumParts(); int numParts = (int)mesh->getNumParts();
for (int partIndex = 0; partIndex < numParts; partIndex++) { for (int partIndex = 0; partIndex < numParts; partIndex++) {
_modelMeshRenderItems << std::make_shared<ModelMeshPartPayload>(this, i, partIndex, shapeID, transform, offset); _modelMeshRenderItems << std::make_shared<ModelMeshPartPayload>(shared_from_this(), i, partIndex, shapeID, transform, offset);
shapeID++; shapeID++;
} }
} }

View file

@ -68,7 +68,7 @@ public:
static void setAbstractViewStateInterface(AbstractViewStateInterface* viewState) { _viewState = viewState; } static void setAbstractViewStateInterface(AbstractViewStateInterface* viewState) { _viewState = viewState; }
Model(RigPointer rig, QObject* parent = nullptr, SpatiallyNestable* spatiallyNestableOverride = nullptr); Model(QObject* parent = nullptr, SpatiallyNestable* spatiallyNestableOverride = nullptr);
virtual ~Model(); virtual ~Model();
inline ModelPointer getThisPointer() const { inline ModelPointer getThisPointer() const {
@ -174,7 +174,7 @@ public:
} }
/// Returns the number of joint states in the model. /// Returns the number of joint states in the model.
int getJointStateCount() const { return (int)_rig->getJointStateCount(); } int getJointStateCount() const { return (int)_rig.getJointStateCount(); }
bool getJointPositionInWorldFrame(int jointIndex, glm::vec3& position) const; bool getJointPositionInWorldFrame(int jointIndex, glm::vec3& position) const;
bool getJointRotationInWorldFrame(int jointIndex, glm::quat& rotation) const; bool getJointRotationInWorldFrame(int jointIndex, glm::quat& rotation) const;
bool getJointCombinedRotation(int jointIndex, glm::quat& rotation) const; bool getJointCombinedRotation(int jointIndex, glm::quat& rotation) const;
@ -223,7 +223,8 @@ public:
return ((index < 0) && (index >= _blendshapeCoefficients.size())) ? 0.0f : _blendshapeCoefficients.at(index); return ((index < 0) && (index >= _blendshapeCoefficients.size())) ? 0.0f : _blendshapeCoefficients.at(index);
} }
virtual RigPointer getRig() const { return _rig; } Rig& getRig() { return _rig; }
const Rig& getRig() const { return _rig; }
const glm::vec3& getRegistrationPoint() const { return _registrationPoint; } const glm::vec3& getRegistrationPoint() const { return _registrationPoint; }
@ -390,7 +391,7 @@ protected:
mutable bool _needsUpdateTextures { true }; mutable bool _needsUpdateTextures { true };
friend class ModelMeshPartPayload; friend class ModelMeshPartPayload;
RigPointer _rig; Rig _rig;
uint32_t _deleteGeometryCounter { 0 }; uint32_t _deleteGeometryCounter { 0 };

View file

@ -8,11 +8,9 @@
#include "SoftAttachmentModel.h" #include "SoftAttachmentModel.h"
SoftAttachmentModel::SoftAttachmentModel(RigPointer rig, QObject* parent, RigPointer rigOverride) : SoftAttachmentModel::SoftAttachmentModel(QObject* parent, const Rig& rigOverride) :
CauterizedModel(rig, parent), CauterizedModel(parent),
_rigOverride(rigOverride) { _rigOverride(rigOverride) {
assert(_rig);
assert(_rigOverride);
} }
SoftAttachmentModel::~SoftAttachmentModel() { SoftAttachmentModel::~SoftAttachmentModel() {
@ -24,11 +22,11 @@ void SoftAttachmentModel::updateRig(float deltaTime, glm::mat4 parentTransform)
} }
int SoftAttachmentModel::getJointIndexOverride(int i) const { int SoftAttachmentModel::getJointIndexOverride(int i) const {
QString name = _rig->nameOfJoint(i); QString name = _rig.nameOfJoint(i);
if (name.isEmpty()) { if (name.isEmpty()) {
return -1; return -1;
} }
return _rigOverride->indexOfJoint(name); return _rigOverride.indexOfJoint(name);
} }
// virtual // virtual
@ -51,10 +49,10 @@ void SoftAttachmentModel::updateClusterMatrices() {
// TODO: cache these look-ups as an optimization // TODO: cache these look-ups as an optimization
int jointIndexOverride = getJointIndexOverride(cluster.jointIndex); int jointIndexOverride = getJointIndexOverride(cluster.jointIndex);
glm::mat4 jointMatrix; glm::mat4 jointMatrix;
if (jointIndexOverride >= 0 && jointIndexOverride < _rigOverride->getJointStateCount()) { if (jointIndexOverride >= 0 && jointIndexOverride < _rigOverride.getJointStateCount()) {
jointMatrix = _rigOverride->getJointTransform(jointIndexOverride); jointMatrix = _rigOverride.getJointTransform(jointIndexOverride);
} else { } else {
jointMatrix = _rig->getJointTransform(cluster.jointIndex); jointMatrix = _rig.getJointTransform(cluster.jointIndex);
} }
glm_mat4u_mul(jointMatrix, cluster.inverseBindMatrix, state.clusterMatrices[j]); glm_mat4u_mul(jointMatrix, cluster.inverseBindMatrix, state.clusterMatrices[j]);
} }

View file

@ -23,7 +23,7 @@ class SoftAttachmentModel : public CauterizedModel {
Q_OBJECT Q_OBJECT
public: public:
SoftAttachmentModel(RigPointer rig, QObject* parent, RigPointer rigOverride); SoftAttachmentModel(QObject* parent, const Rig& rigOverride);
~SoftAttachmentModel(); ~SoftAttachmentModel();
void updateRig(float deltaTime, glm::mat4 parentTransform) override; void updateRig(float deltaTime, glm::mat4 parentTransform) override;
@ -32,7 +32,7 @@ public:
protected: protected:
int getJointIndexOverride(int i) const; int getJointIndexOverride(int i) const;
RigPointer _rigOverride; const Rig& _rigOverride;
}; };
#endif // hifi_SoftAttachmentModel_h #endif // hifi_SoftAttachmentModel_h

View file

@ -53,6 +53,11 @@ RunningMarker::~RunningMarker() {
_runningMarkerThread->deleteLater(); _runningMarkerThread->deleteLater();
} }
bool RunningMarker::fileExists() const {
QFile runningMarkerFile(getFilePath());
return runningMarkerFile.exists();
}
void RunningMarker::writeRunningMarkerFile() { void RunningMarker::writeRunningMarkerFile() {
QFile runningMarkerFile(getFilePath()); QFile runningMarkerFile(getFilePath());
@ -69,7 +74,7 @@ void RunningMarker::deleteRunningMarkerFile() {
} }
} }
QString RunningMarker::getFilePath() { QString RunningMarker::getFilePath() const {
return QStandardPaths::writableLocation(QStandardPaths::DataLocation) + "/" + _name; return QStandardPaths::writableLocation(QStandardPaths::DataLocation) + "/" + _name;
} }

View file

@ -25,9 +25,11 @@ public:
void startRunningMarker(); void startRunningMarker();
QString getFilePath(); QString getFilePath() const;
static QString getMarkerFilePath(QString name); static QString getMarkerFilePath(QString name);
bool fileExists() const;
void writeRunningMarkerFile(); void writeRunningMarkerFile();
void deleteRunningMarkerFile(); void deleteRunningMarkerFile();

View file

@ -70,7 +70,15 @@ StoragePointer FileStorage::create(const QString& filename, size_t size, const u
} }
FileStorage::FileStorage(const QString& filename) : _file(filename) { FileStorage::FileStorage(const QString& filename) : _file(filename) {
if (_file.open(QFile::ReadOnly)) { bool opened = _file.open(QFile::ReadWrite);
if (opened) {
_hasWriteAccess = true;
} else {
_hasWriteAccess = false;
opened = _file.open(QFile::ReadOnly);
}
if (opened) {
_mapped = _file.map(0, _file.size()); _mapped = _file.map(0, _file.size());
if (_mapped) { if (_mapped) {
_valid = true; _valid = true;
@ -91,35 +99,4 @@ FileStorage::~FileStorage() {
if (_file.isOpen()) { if (_file.isOpen()) {
_file.close(); _file.close();
} }
}
void FileStorage::ensureWriteAccess() {
if (_hasWriteAccess) {
return;
}
if (_mapped) {
if (!_file.unmap(_mapped)) {
throw std::runtime_error("Unable to unmap file");
}
}
if (_file.isOpen()) {
_file.close();
}
_valid = false;
_mapped = nullptr;
if (_file.open(QFile::ReadWrite)) {
_mapped = _file.map(0, _file.size());
if (_mapped) {
_valid = true;
_hasWriteAccess = true;
} else {
qCWarning(storagelogging) << "Failed to map file " << _file.fileName();
throw std::runtime_error("Failed to map file");
}
} else {
qCWarning(storagelogging) << "Failed to open file " << _file.fileName();
throw std::runtime_error("Failed to open file");
}
} }

View file

@ -60,11 +60,10 @@ namespace storage {
FileStorage& operator=(const FileStorage& other) = delete; FileStorage& operator=(const FileStorage& other) = delete;
const uint8_t* data() const override { return _mapped; } const uint8_t* data() const override { return _mapped; }
uint8_t* mutableData() override { ensureWriteAccess(); return _mapped; } uint8_t* mutableData() override { return _hasWriteAccess ? _mapped : nullptr; }
size_t size() const override { return _file.size(); } size_t size() const override { return _file.size(); }
operator bool() const override { return _valid; } operator bool() const override { return _valid; }
private: private:
void ensureWriteAccess();
bool _valid { false }; bool _valid { false };
bool _hasWriteAccess { false }; bool _hasWriteAccess { false };

View file

@ -44,6 +44,7 @@ function showSetupComplete() {
'<p>Snapshot location set.</p>' + '<p>Snapshot location set.</p>' +
'<p>Press the big red button to take a snap!</p>' + '<p>Press the big red button to take a snap!</p>' +
'</div>'; '</div>';
document.getElementById("snap-button").disabled = false;
} }
function showSnapshotInstructions() { function showSnapshotInstructions() {
var snapshotImagesDiv = document.getElementById("snapshot-images"); var snapshotImagesDiv = document.getElementById("snapshot-images");
@ -69,7 +70,6 @@ function login() {
})); }));
} }
function clearImages() { function clearImages() {
document.getElementById("snap-button").disabled = false;
var snapshotImagesDiv = document.getElementById("snapshot-images"); var snapshotImagesDiv = document.getElementById("snapshot-images");
snapshotImagesDiv.classList.remove("snapshotInstructions"); snapshotImagesDiv.classList.remove("snapshotInstructions");
while (snapshotImagesDiv.hasChildNodes()) { while (snapshotImagesDiv.hasChildNodes()) {
@ -300,7 +300,7 @@ function addImage(image_data, isLoggedIn, canShare, isGifLoading, isShowingPrevi
if (!isGifLoading) { if (!isGifLoading) {
appendShareBar(id, isLoggedIn, canShare, isGif, blastButtonDisabled, hifiButtonDisabled, canBlast); appendShareBar(id, isLoggedIn, canShare, isGif, blastButtonDisabled, hifiButtonDisabled, canBlast);
} }
if (!isGifLoading && !isShowingPreviousImages) { if (!isGifLoading || (isShowingPreviousImages && !image_data.story_id)) {
shareForUrl(id); shareForUrl(id);
} }
if (isShowingPreviousImages && isLoggedIn && image_data.story_id) { if (isShowingPreviousImages && isLoggedIn && image_data.story_id) {
@ -650,6 +650,7 @@ window.onload = function () {
shareForUrl("p1"); shareForUrl("p1");
appendShareBar("p1", messageOptions.isLoggedIn, messageOptions.canShare, true, false, false, messageOptions.canBlast); appendShareBar("p1", messageOptions.isLoggedIn, messageOptions.canShare, true, false, false, messageOptions.canBlast);
document.getElementById("p1").classList.remove("processingGif"); document.getElementById("p1").classList.remove("processingGif");
document.getElementById("snap-button").disabled = false;
} }
} else { } else {
imageCount = message.image_data.length; imageCount = message.image_data.length;
@ -688,6 +689,9 @@ function takeSnapshot() {
type: "snapshot", type: "snapshot",
action: "takeSnapshot" action: "takeSnapshot"
})); }));
if (document.getElementById('stillAndGif').checked === true) {
document.getElementById("snap-button").disabled = true;
}
} }
function testInBrowser(test) { function testInBrowser(test) {

View file

@ -138,10 +138,10 @@ function onMessage(message) {
isLoggedIn = Account.isLoggedIn(); isLoggedIn = Account.isLoggedIn();
if (isLoggedIn) { if (isLoggedIn) {
print('Sharing snapshot with audience "for_url":', message.data); print('Sharing snapshot with audience "for_url":', message.data);
Window.shareSnapshot(message.data, message.href || href); Window.shareSnapshot(message.data, Settings.getValue("previousSnapshotHref"));
} else { } else {
shareAfterLogin = true; shareAfterLogin = true;
snapshotToShareAfterLogin.push({ path: message.data, href: message.href || href }); snapshotToShareAfterLogin.push({ path: message.data, href: Settings.getValue("previousSnapshotHref") });
} }
} }
}); });
@ -349,6 +349,7 @@ function takeSnapshot() {
// We will record snapshots based on the starting location. That could change, e.g., when recording a .gif. // We will record snapshots based on the starting location. That could change, e.g., when recording a .gif.
// Even the domainId could change (e.g., if the user falls into a teleporter while recording). // Even the domainId could change (e.g., if the user falls into a teleporter while recording).
href = location.href; href = location.href;
Settings.setValue("previousSnapshotHref", href);
domainId = location.domainId; domainId = location.domainId;
Settings.setValue("previousSnapshotDomainID", domainId); Settings.setValue("previousSnapshotDomainID", domainId);

View file

@ -42,7 +42,7 @@ const appIcon = path.join(__dirname, '../resources/console.png');
const DELETE_LOG_FILES_OLDER_THAN_X_SECONDS = 60 * 60 * 24 * 7; // 7 Days const DELETE_LOG_FILES_OLDER_THAN_X_SECONDS = 60 * 60 * 24 * 7; // 7 Days
const LOG_FILE_REGEX = /(domain-server|ac-monitor|ac)-.*-std(out|err).txt/; const LOG_FILE_REGEX = /(domain-server|ac-monitor|ac)-.*-std(out|err).txt/;
const HOME_CONTENT_URL = "http://cdn.highfidelity.com/content-sets/home-tutorial-RC39.tar.gz"; const HOME_CONTENT_URL = "http://cdn.highfidelity.com/content-sets/home-tutorial-RC40.tar.gz";
function getBuildInfo() { function getBuildInfo() {
var buildInfoPath = null; var buildInfoPath = null;

View file

@ -47,7 +47,7 @@
#include <Rig.h> #include <Rig.h>
#include "RigTests.h" #include "RigTests.h"
static void reportJoint(RigPointer rig, int index) { // Handy for debugging static void reportJoint(const Rig& rig, int index) { // Handy for debugging
std::cout << "\n"; std::cout << "\n";
std::cout << index << " " << rig->getAnimSkeleton()->getJointName(index).toUtf8().data() << "\n"; std::cout << index << " " << rig->getAnimSkeleton()->getJointName(index).toUtf8().data() << "\n";
glm::vec3 pos; glm::vec3 pos;
@ -58,16 +58,16 @@ static void reportJoint(RigPointer rig, int index) { // Handy for debugging
std::cout << " rot:" << safeEulerAngles(rot) << "\n"; std::cout << " rot:" << safeEulerAngles(rot) << "\n";
std::cout << "\n"; std::cout << "\n";
} }
static void reportByName(RigPointer rig, const QString& name) { static void reportByName(const Rig& rig, const QString& name) {
int jointIndex = rig->indexOfJoint(name); int jointIndex = rig->indexOfJoint(name);
reportJoint(rig, jointIndex); reportJoint(rig, jointIndex);
} }
static void reportAll(RigPointer rig) { static void reportAll(const Rig& rig) {
for (int i = 0; i < rig->getJointStateCount(); i++) { for (int i = 0; i < rig->getJointStateCount(); i++) {
reportJoint(rig, i); reportJoint(rig, i);
} }
} }
static void reportSome(RigPointer rig) { static void reportSome(const Rig& rig) {
QString names[] = {"Head", "Neck", "RightShoulder", "RightArm", "RightForeArm", "RightHand", "Spine2", "Spine1", "Spine", "Hips", "RightUpLeg", "RightLeg", "RightFoot", "RightToeBase", "RightToe_End"}; QString names[] = {"Head", "Neck", "RightShoulder", "RightArm", "RightForeArm", "RightHand", "Spine2", "Spine1", "Spine", "Hips", "RightUpLeg", "RightLeg", "RightFoot", "RightToeBase", "RightToe_End"};
for (auto name : names) { for (auto name : names) {
reportByName(rig, name); reportByName(rig, name);
@ -91,8 +91,7 @@ void RigTests::initTestCase() {
#endif #endif
QVERIFY((bool)geometry); QVERIFY((bool)geometry);
_rig = std::make_shared<Rig>(); _rig.initJointStates(*geometry, glm::mat4());
_rig->initJointStates(*geometry, glm::mat4());
std::cout << "Rig is ready " << geometry->joints.count() << " joints " << std::endl; std::cout << "Rig is ready " << geometry->joints.count() << " joints " << std::endl;
reportAll(_rig); reportAll(_rig);
} }

View file

@ -43,13 +43,13 @@
class RigTests : public QObject { class RigTests : public QObject {
Q_OBJECT Q_OBJECT
private slots: private slots:
void initTestCase(); void initTestCase();
void initialPoseArmsDown(); void initialPoseArmsDown();
private: private:
RigPointer _rig; Rig _rig;
}; };
#endif // hifi_RigTests_h #endif // hifi_RigTests_h