mirror of
https://github.com/HifiExperiments/overte.git
synced 2025-08-09 17:38:34 +02:00
Merge branch 'master' of github.com:highfidelity/hifi into arm-pucks
This commit is contained in:
commit
0cbdeaa1e8
74 changed files with 725 additions and 542 deletions
|
@ -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(
|
||||||
|
|
|
@ -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();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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 };
|
||||||
|
|
Binary file not shown.
|
@ -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)),
|
||||||
|
@ -3976,7 +3976,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();
|
||||||
|
@ -6432,7 +6432,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>());
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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) {
|
||||||
|
|
|
@ -106,8 +106,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),
|
||||||
|
@ -128,7 +128,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())
|
||||||
|
@ -137,7 +136,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);
|
||||||
|
|
||||||
|
|
||||||
|
@ -188,9 +187,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, [=] {
|
||||||
|
@ -241,12 +238,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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -363,9 +360,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) {
|
||||||
|
@ -536,12 +531,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);
|
||||||
_rig->setEnableDebugDrawIKChains(_enableDebugDrawIKChains);
|
|
||||||
}
|
|
||||||
|
|
||||||
_skeletonModel->simulate(deltaTime);
|
_skeletonModel->simulate(deltaTime);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -559,7 +551,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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -804,7 +796,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() {
|
||||||
|
@ -812,7 +804,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() {
|
||||||
|
@ -821,7 +813,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,
|
||||||
|
@ -831,7 +823,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) {
|
||||||
|
@ -839,7 +831,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() {
|
||||||
|
@ -981,7 +973,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() {
|
||||||
|
@ -1230,7 +1222,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) {
|
||||||
|
@ -1239,7 +1231,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) {
|
||||||
|
@ -1248,7 +1240,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) {
|
||||||
|
@ -1256,7 +1248,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() {
|
||||||
|
@ -1264,7 +1256,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) {
|
||||||
|
@ -1707,7 +1699,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
|
||||||
|
@ -1723,7 +1715,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..
|
||||||
|
@ -1731,7 +1723,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) {
|
||||||
|
@ -1747,22 +1739,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);
|
||||||
|
@ -2351,17 +2344,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);
|
||||||
|
@ -2727,8 +2721,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;
|
||||||
|
@ -2740,7 +2734,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);
|
||||||
|
@ -2752,7 +2746,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);
|
||||||
|
@ -2764,7 +2758,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);
|
||||||
|
@ -2776,7 +2770,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);
|
||||||
|
@ -2788,7 +2782,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);
|
||||||
|
@ -2856,7 +2850,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()) {
|
||||||
|
@ -2873,7 +2867,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;
|
||||||
|
@ -2882,7 +2876,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
|
||||||
|
|
|
@ -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; }
|
||||||
|
@ -713,7 +713,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 };
|
||||||
|
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -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();
|
||||||
|
|
|
@ -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;
|
||||||
};
|
};
|
||||||
|
|
|
@ -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);
|
});
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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.
|
||||||
|
|
|
@ -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()
|
||||||
|
|
|
@ -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; }
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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&);
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
|
@ -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 {};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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());
|
||||||
}
|
}
|
||||||
|
|
|
@ -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]));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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));
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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);
|
||||||
|
|
||||||
|
|
|
@ -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 };
|
||||||
|
|
|
@ -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([&] {
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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()) {
|
||||||
|
|
|
@ -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;
|
||||||
|
|
||||||
|
|
|
@ -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";
|
||||||
}
|
}
|
||||||
|
|
|
@ -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:
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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 };
|
||||||
|
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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();
|
||||||
|
|
|
@ -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++;
|
||||||
|
|
|
@ -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();
|
||||||
|
|
||||||
|
|
|
@ -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 };
|
||||||
|
|
|
@ -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";
|
||||||
|
|
|
@ -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.
|
||||||
|
|
|
@ -58,6 +58,10 @@ void ObjectAction::updateAction(btCollisionWorld* collisionWorld, btScalar delta
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (ownerEntity->getLocked()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
updateActionWorker(deltaTimeStep);
|
updateActionWorker(deltaTimeStep);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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) {
|
||||||
|
|
|
@ -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);
|
||||||
|
|
||||||
|
|
|
@ -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) {
|
||||||
|
|
|
@ -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; }
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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 };
|
||||||
|
|
||||||
|
|
|
@ -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]);
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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();
|
||||||
|
|
||||||
|
|
|
@ -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");
|
|
||||||
}
|
|
||||||
}
|
}
|
|
@ -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 };
|
||||||
|
|
|
@ -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) {
|
||||||
|
|
|
@ -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);
|
||||||
|
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
|
|
Loading…
Reference in a new issue