Merge branch 'master' of https://github.com/highfidelity/hifi into loginOnLaunch

This commit is contained in:
Wayne Chen 2018-09-10 10:10:16 -07:00
commit e721844b10
61 changed files with 861 additions and 214 deletions

View file

@ -654,6 +654,15 @@ void AvatarMixer::handleNodeIgnoreRequestPacket(QSharedPointer<ReceivedMessage>
if (addToIgnore) { if (addToIgnore) {
senderNode->addIgnoredNode(ignoredUUID); senderNode->addIgnoredNode(ignoredUUID);
if (ignoredNode) {
// send a reliable kill packet to remove the sending avatar for the ignored avatar
auto killPacket = NLPacket::create(PacketType::KillAvatar,
NUM_BYTES_RFC4122_UUID + sizeof(KillAvatarReason), true);
killPacket->write(senderNode->getUUID().toRfc4122());
killPacket->writePrimitive(KillAvatarReason::AvatarDisconnected);
nodeList->sendPacket(std::move(killPacket), *ignoredNode);
}
} else { } else {
senderNode->removeIgnoredNode(ignoredUUID); senderNode->removeIgnoredNode(ignoredUUID);
} }

View file

@ -0,0 +1,157 @@
import Hifi 1.0 as Hifi
import QtQuick 2.3
import '.'
Item {
id: animStats
anchors.leftMargin: 300
objectName: "StatsItem"
property int modality: Qt.NonModal
implicitHeight: row.height
implicitWidth: row.width
Component.onCompleted: {
animStats.parentChanged.connect(fill);
fill();
}
Component.onDestruction: {
animStats.parentChanged.disconnect(fill);
}
function fill() {
// This will cause a warning at shutdown, need to find another way to remove
// the warning other than filling the anchors to the parent
anchors.horizontalCenter = parent.horizontalCenter
}
Hifi.AnimStats {
id: root
objectName: "AnimStats"
implicitHeight: row.height
implicitWidth: row.width
anchors.horizontalCenter: parent.horizontalCenter
readonly property string bgColor: "#AA111111"
Row {
id: row
spacing: 8
Rectangle {
width: firstCol.width + 8;
height: firstCol.height + 8;
color: root.bgColor;
Column {
id: firstCol
spacing: 4; x: 4; y: 4;
StatText {
text: "State Machines:---------------------------------------------------------------------------"
}
ListView {
width: firstCol.width
height: root.animStateMachines.length * 15
visible: root.animStateMchines.length > 0;
model: root.animStateMachines
delegate: StatText {
text: {
return modelData;
}
}
}
}
}
Rectangle {
width: secondCol.width + 8
height: secondCol.height + 8
color: root.bgColor;
Column {
id: secondCol
spacing: 4; x: 4; y: 4;
StatText {
text: "Anim Vars:--------------------------------------------------------------------------------"
}
ListView {
width: secondCol.width
height: root.animVars.length * 15
visible: root.animVars.length > 0;
model: root.animVars
delegate: StatText {
text: {
var actualText = modelData.split("|")[1];
if (actualText) {
return actualText;
} else {
return modelData;
}
}
color: {
var grayScale = parseFloat(modelData.split("|")[0]);
return Qt.rgba(1.0, 1.0, 1.0, grayScale);
}
styleColor: {
var grayScale = parseFloat(modelData.split("|")[0]);
return Qt.rgba(0.0, 0.0, 0.0, grayScale);
}
}
}
}
}
Rectangle {
width: thirdCol.width + 8
height: thirdCol.height + 8
color: root.bgColor;
Column {
id: thirdCol
spacing: 4; x: 4; y: 4;
StatText {
text: "Alpha Values:--------------------------------------------------------------------------"
}
ListView {
width: thirdCol.width
height: root.animAlphaValues.length * 15
visible: root.animAlphaValues.length > 0;
model: root.animAlphaValues
delegate: StatText {
text: {
var actualText = modelData.split("|")[1];
if (actualText) {
return actualText;
} else {
return modelData;
}
}
color: {
var grayScale = parseFloat(modelData.split("|")[0]);
return Qt.rgba(1.0, 1.0, 1.0, grayScale);
}
styleColor: {
var grayScale = parseFloat(modelData.split("|")[0]);
return Qt.rgba(0.0, 0.0, 0.0, grayScale);
}
}
}
}
}
}
Connections {
target: root.parent
onWidthChanged: {
root.x = root.parent.width - root.width;
}
}
}
}

View file

@ -146,7 +146,8 @@ Windows.Window {
Qt.WindowCloseButtonHint | Qt.WindowCloseButtonHint |
Qt.WindowMaximizeButtonHint | Qt.WindowMaximizeButtonHint |
Qt.WindowMinimizeButtonHint; Qt.WindowMinimizeButtonHint;
if ((flags & Desktop.ALWAYS_ON_TOP) === Desktop.ALWAYS_ON_TOP) { // only use the always on top feature for non Windows OS
if (Qt.platform.os !== "windows" && (flags & Desktop.ALWAYS_ON_TOP)) {
nativeWindowFlags |= Qt.WindowStaysOnTopHint; nativeWindowFlags |= Qt.WindowStaysOnTopHint;
} }
nativeWindow.flags = nativeWindowFlags; nativeWindow.flags = nativeWindowFlags;

View file

@ -192,21 +192,6 @@ Item {
StatText { StatText {
text: "Yaw: " + root.yaw.toFixed(1) text: "Yaw: " + root.yaw.toFixed(1)
} }
StatText {
visible: root.animStackNames.length > 0;
text: "Anim Stack Names:"
}
ListView {
width: geoCol.width
height: root.animStackNames.length * 15
visible: root.animStackNames.length > 0;
model: root.animStackNames
delegate: StatText {
text: modelData.length > 30
? modelData.substring(0, 5) + "..." + modelData.substring(modelData.length - 22)
: modelData
}
}
StatText { StatText {
visible: root.expanded; visible: root.expanded;
text: "Avatar Mixer In: " + root.avatarMixerInKbps + " kbps, " + text: "Avatar Mixer In: " + root.avatarMixerInKbps + " kbps, " +

View file

@ -530,9 +530,7 @@ Item {
maximumValue: 20.0 maximumValue: 20.0
stepSize: 5 stepSize: 5
updateValueWhileDragging: true updateValueWhileDragging: true
Component.onCompleted: { value: Users.getAvatarGain(uuid)
value = Users.getAvatarGain(uuid);
}
onValueChanged: { onValueChanged: {
updateGainFromQML(uuid, value, false); updateGainFromQML(uuid, value, false);
} }

View file

@ -780,6 +780,12 @@ Rectangle {
headerVisible: true; headerVisible: true;
sortIndicatorColumn: settings.connectionsSortIndicatorColumn; sortIndicatorColumn: settings.connectionsSortIndicatorColumn;
sortIndicatorOrder: settings.connectionsSortIndicatorOrder; sortIndicatorOrder: settings.connectionsSortIndicatorOrder;
onSortIndicatorColumnChanged: {
settings.connectionsSortIndicatorColumn = sortIndicatorColumn;
}
onSortIndicatorOrderChanged: {
settings.connectionsSortIndicatorOrder = sortIndicatorOrder;
}
TableViewColumn { TableViewColumn {
id: connectionsUserNameHeader; id: connectionsUserNameHeader;

View file

@ -195,6 +195,7 @@
#include "ui/SnapshotAnimated.h" #include "ui/SnapshotAnimated.h"
#include "ui/StandAloneJSConsole.h" #include "ui/StandAloneJSConsole.h"
#include "ui/Stats.h" #include "ui/Stats.h"
#include "ui/AnimStats.h"
#include "ui/UpdateDialog.h" #include "ui/UpdateDialog.h"
#include "ui/overlays/Overlays.h" #include "ui/overlays/Overlays.h"
#include "ui/DomainConnectionModel.h" #include "ui/DomainConnectionModel.h"
@ -3094,8 +3095,10 @@ void Application::onDesktopRootContextCreated(QQmlContext* surfaceContext) {
void Application::onDesktopRootItemCreated(QQuickItem* rootItem) { void Application::onDesktopRootItemCreated(QQuickItem* rootItem) {
Stats::show(); Stats::show();
AnimStats::show();
auto surfaceContext = DependencyManager::get<OffscreenUi>()->getSurfaceContext(); auto surfaceContext = DependencyManager::get<OffscreenUi>()->getSurfaceContext();
surfaceContext->setContextProperty("Stats", Stats::getInstance()); surfaceContext->setContextProperty("Stats", Stats::getInstance());
surfaceContext->setContextProperty("AnimStats", AnimStats::getInstance());
#if !defined(Q_OS_ANDROID) #if !defined(Q_OS_ANDROID)
auto offscreenUi = DependencyManager::get<OffscreenUi>(); auto offscreenUi = DependencyManager::get<OffscreenUi>();
@ -4631,6 +4634,7 @@ void Application::idle() {
checkChangeCursor(); checkChangeCursor();
Stats::getInstance()->updateStats(); Stats::getInstance()->updateStats();
AnimStats::getInstance()->updateStats();
// Normally we check PipelineWarnings, but since idle will often take more than 10ms we only show these idle timing // Normally we check PipelineWarnings, but since idle will often take more than 10ms we only show these idle timing
// details if we're in ExtraDebugging mode. However, the ::update() and its subcomponents will show their timing // details if we're in ExtraDebugging mode. However, the ::update() and its subcomponents will show their timing
@ -5868,9 +5872,7 @@ void Application::update(float deltaTime) {
bool showWarnings = Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings); bool showWarnings = Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings);
PerformanceWarning warn(showWarnings, "Application::update()"); PerformanceWarning warn(showWarnings, "Application::update()");
#if !defined(Q_OS_ANDROID)
updateLOD(deltaTime); updateLOD(deltaTime);
#endif
// TODO: break these out into distinct perfTimers when they prove interesting // TODO: break these out into distinct perfTimers when they prove interesting
{ {

View file

@ -19,8 +19,11 @@
#include <SimpleMovingAverage.h> #include <SimpleMovingAverage.h>
#include <render/Args.h> #include <render/Args.h>
#ifdef Q_OS_ANDROID
const float LOD_DEFAULT_QUALITY_LEVEL = 0.75f; // default quality level setting is High (lower framerate)
#else
const float LOD_DEFAULT_QUALITY_LEVEL = 0.5f; // default quality level setting is Mid const float LOD_DEFAULT_QUALITY_LEVEL = 0.5f; // default quality level setting is Mid
#endif
const float LOD_MAX_LIKELY_DESKTOP_FPS = 60.0f; // this is essentially, V-synch fps const float LOD_MAX_LIKELY_DESKTOP_FPS = 60.0f; // this is essentially, V-synch fps
const float LOD_MAX_LIKELY_HMD_FPS = 90.0f; // this is essentially, V-synch fps const float LOD_MAX_LIKELY_HMD_FPS = 90.0f; // this is essentially, V-synch fps
const float LOD_OFFSET_FPS = 5.0f; // offset of FPS to add for computing the target framerate const float LOD_OFFSET_FPS = 5.0f; // offset of FPS to add for computing the target framerate

View file

@ -737,6 +737,7 @@ Menu::Menu() {
// Developer > Stats // Developer > Stats
addCheckableActionToQMenuAndActionHash(developerMenu, MenuOption::Stats); addCheckableActionToQMenuAndActionHash(developerMenu, MenuOption::Stats);
addCheckableActionToQMenuAndActionHash(developerMenu, MenuOption::AnimStats);
// Settings > Enable Speech Control API // Settings > Enable Speech Control API
#if defined(Q_OS_MAC) || defined(Q_OS_WIN) #if defined(Q_OS_MAC) || defined(Q_OS_WIN)

View file

@ -197,6 +197,7 @@ namespace MenuOption {
const QString SMIEyeTracking = "SMI Eye Tracking"; const QString SMIEyeTracking = "SMI Eye Tracking";
const QString SparseTextureManagement = "Enable Sparse Texture Management"; const QString SparseTextureManagement = "Enable Sparse Texture Management";
const QString Stats = "Show Statistics"; const QString Stats = "Show Statistics";
const QString AnimStats = "Show Animation Stats";
const QString StopAllScripts = "Stop All Scripts"; const QString StopAllScripts = "Stop All Scripts";
const QString SuppressShortTimings = "Suppress Timings Less than 10ms"; const QString SuppressShortTimings = "Suppress Timings Less than 10ms";
const QString ThirdPerson = "Third Person"; const QString ThirdPerson = "Third Person";

View file

@ -516,6 +516,10 @@ void MyAvatar::update(float deltaTime) {
head->relax(deltaTime); head->relax(deltaTime);
updateFromTrackers(deltaTime); updateFromTrackers(deltaTime);
if (getIsInWalkingState() && glm::length(getControllerPoseInAvatarFrame(controller::Action::HEAD).getVelocity()) < DEFAULT_AVATAR_WALK_SPEED_THRESHOLD) {
setIsInWalkingState(false);
}
// Get audio loudness data from audio input device // Get audio loudness data from audio input device
// Also get the AudioClient so we can update the avatar bounding box data // Also get the AudioClient so we can update the avatar bounding box data
// on the AudioClient side. // on the AudioClient side.
@ -3678,10 +3682,10 @@ static bool headAngularVelocityBelowThreshold(const controller::Pose& head) {
return isBelowThreshold; return isBelowThreshold;
} }
static bool isWithinThresholdHeightMode(const controller::Pose& head,const float& newMode) { static bool isWithinThresholdHeightMode(const controller::Pose& head, const float& newMode, const float& scale) {
bool isWithinThreshold = true; bool isWithinThreshold = true;
if (head.isValid()) { if (head.isValid()) {
isWithinThreshold = (head.getTranslation().y - newMode) > DEFAULT_AVATAR_MODE_HEIGHT_STEPPING_THRESHOLD; isWithinThreshold = (head.getTranslation().y - newMode) > (DEFAULT_AVATAR_MODE_HEIGHT_STEPPING_THRESHOLD * scale);
} }
return isWithinThreshold; return isWithinThreshold;
} }
@ -3802,6 +3806,10 @@ float MyAvatar::getUserEyeHeight() const {
return userHeight - userHeight * ratio; return userHeight - userHeight * ratio;
} }
bool MyAvatar::getIsInWalkingState() const {
return _isInWalkingState;
}
float MyAvatar::getWalkSpeed() const { float MyAvatar::getWalkSpeed() const {
return _walkSpeed.get() * _walkSpeedScalar; return _walkSpeed.get() * _walkSpeedScalar;
} }
@ -3818,6 +3826,10 @@ void MyAvatar::setSprintMode(bool sprint) {
_walkSpeedScalar = sprint ? _sprintSpeed.get() : AVATAR_WALK_SPEED_SCALAR; _walkSpeedScalar = sprint ? _sprintSpeed.get() : AVATAR_WALK_SPEED_SCALAR;
} }
void MyAvatar::setIsInWalkingState(bool isWalking) {
_isInWalkingState = isWalking;
}
void MyAvatar::setWalkSpeed(float value) { void MyAvatar::setWalkSpeed(float value) {
_walkSpeed.set(value); _walkSpeed.set(value);
} }
@ -3912,7 +3924,6 @@ void MyAvatar::lateUpdatePalms() {
Avatar::updatePalms(); Avatar::updatePalms();
} }
static const float FOLLOW_TIME = 0.5f; static const float FOLLOW_TIME = 0.5f;
MyAvatar::FollowHelper::FollowHelper() { MyAvatar::FollowHelper::FollowHelper() {
@ -4004,15 +4015,23 @@ bool MyAvatar::FollowHelper::shouldActivateHorizontalCG(MyAvatar& myAvatar) cons
controller::Pose currentRightHandPose = myAvatar.getControllerPoseInAvatarFrame(controller::Action::RIGHT_HAND); controller::Pose currentRightHandPose = myAvatar.getControllerPoseInAvatarFrame(controller::Action::RIGHT_HAND);
bool stepDetected = false; bool stepDetected = false;
float myScale = myAvatar.getAvatarScale();
if (myAvatar.getIsInWalkingState()) {
stepDetected = true;
} else {
if (!withinBaseOfSupport(currentHeadPose) && if (!withinBaseOfSupport(currentHeadPose) &&
headAngularVelocityBelowThreshold(currentHeadPose) && headAngularVelocityBelowThreshold(currentHeadPose) &&
isWithinThresholdHeightMode(currentHeadPose, myAvatar.getCurrentStandingHeight()) && isWithinThresholdHeightMode(currentHeadPose, myAvatar.getCurrentStandingHeight(), myScale) &&
handDirectionMatchesHeadDirection(currentLeftHandPose, currentRightHandPose, currentHeadPose) && handDirectionMatchesHeadDirection(currentLeftHandPose, currentRightHandPose, currentHeadPose) &&
handAngularVelocityBelowThreshold(currentLeftHandPose, currentRightHandPose) && handAngularVelocityBelowThreshold(currentLeftHandPose, currentRightHandPose) &&
headVelocityGreaterThanThreshold(currentHeadPose) && headVelocityGreaterThanThreshold(currentHeadPose) &&
isHeadLevel(currentHeadPose, myAvatar.getAverageHeadRotation())) { isHeadLevel(currentHeadPose, myAvatar.getAverageHeadRotation())) {
// a step is detected // a step is detected
stepDetected = true; stepDetected = true;
if (glm::length(currentHeadPose.velocity) > DEFAULT_AVATAR_WALK_SPEED_THRESHOLD) {
myAvatar.setIsInWalkingState(true);
}
} else { } else {
glm::vec3 defaultHipsPosition = myAvatar.getAbsoluteDefaultJointTranslationInObjectFrame(myAvatar.getJointIndex("Hips")); glm::vec3 defaultHipsPosition = myAvatar.getAbsoluteDefaultJointTranslationInObjectFrame(myAvatar.getJointIndex("Hips"));
glm::vec3 defaultHeadPosition = myAvatar.getAbsoluteDefaultJointTranslationInObjectFrame(myAvatar.getJointIndex("Head")); glm::vec3 defaultHeadPosition = myAvatar.getAbsoluteDefaultJointTranslationInObjectFrame(myAvatar.getJointIndex("Head"));
@ -4022,6 +4041,10 @@ bool MyAvatar::FollowHelper::shouldActivateHorizontalCG(MyAvatar& myAvatar) cons
(glm::length(currentHeadPosition - defaultHipsPosition) > (anatomicalHeadToHipsDistance + (DEFAULT_AVATAR_SPINE_STRETCH_LIMIT * anatomicalHeadToHipsDistance)))) { (glm::length(currentHeadPosition - defaultHipsPosition) > (anatomicalHeadToHipsDistance + (DEFAULT_AVATAR_SPINE_STRETCH_LIMIT * anatomicalHeadToHipsDistance)))) {
myAvatar.setResetMode(true); myAvatar.setResetMode(true);
stepDetected = true; stepDetected = true;
if (glm::length(currentHeadPose.velocity) > DEFAULT_AVATAR_WALK_SPEED_THRESHOLD) {
myAvatar.setIsInWalkingState(true);
}
}
} }
} }
return stepDetected; return stepDetected;

View file

@ -1086,6 +1086,8 @@ public:
const QUuid& getSelfID() const { return AVATAR_SELF_ID; } const QUuid& getSelfID() const { return AVATAR_SELF_ID; }
void setIsInWalkingState(bool isWalking);
bool getIsInWalkingState() const;
void setWalkSpeed(float value); void setWalkSpeed(float value);
float getWalkSpeed() const; float getWalkSpeed() const;
void setWalkBackwardSpeed(float value); void setWalkBackwardSpeed(float value);
@ -1788,6 +1790,7 @@ private:
ThreadSafeValueCache<float> _walkBackwardSpeed { DEFAULT_AVATAR_MAX_WALKING_BACKWARD_SPEED }; ThreadSafeValueCache<float> _walkBackwardSpeed { DEFAULT_AVATAR_MAX_WALKING_BACKWARD_SPEED };
ThreadSafeValueCache<float> _sprintSpeed { AVATAR_SPRINT_SPEED_SCALAR }; ThreadSafeValueCache<float> _sprintSpeed { AVATAR_SPRINT_SPEED_SCALAR };
float _walkSpeedScalar { AVATAR_WALK_SPEED_SCALAR }; float _walkSpeedScalar { AVATAR_WALK_SPEED_SCALAR };
bool _isInWalkingState { false };
// load avatar scripts once when rig is ready // load avatar scripts once when rig is ready
bool _shouldLoadScripts { false }; bool _shouldLoadScripts { false };

View file

@ -16,8 +16,9 @@ Transform MyAvatarHeadTransformNode::getTransform() {
auto myAvatar = DependencyManager::get<AvatarManager>()->getMyAvatar(); auto myAvatar = DependencyManager::get<AvatarManager>()->getMyAvatar();
glm::vec3 pos = myAvatar->getHeadPosition(); glm::vec3 pos = myAvatar->getHeadPosition();
glm::vec3 scale = glm::vec3(myAvatar->scaleForChildren());
glm::quat headOri = myAvatar->getHeadOrientation(); glm::quat headOri = myAvatar->getHeadOrientation();
glm::quat ori = headOri * glm::angleAxis(-PI / 2.0f, Vectors::RIGHT); glm::quat ori = headOri * glm::angleAxis(-PI / 2.0f, Vectors::RIGHT);
return Transform(ori, glm::vec3(1.0f), pos); return Transform(ori, scale, pos);
} }

View file

@ -46,7 +46,7 @@ static AnimPose computeHipsInSensorFrame(MyAvatar* myAvatar, bool isFlying) {
} }
glm::mat4 hipsMat; glm::mat4 hipsMat;
if (myAvatar->getCenterOfGravityModelEnabled() && !isFlying) { if (myAvatar->getCenterOfGravityModelEnabled() && !isFlying && !(myAvatar->getIsInWalkingState())) {
// then we use center of gravity model // then we use center of gravity model
hipsMat = myAvatar->deriveBodyUsingCgModel(); hipsMat = myAvatar->deriveBodyUsingCgModel();
} else { } else {

View file

@ -75,7 +75,6 @@ void SafeLanding::addTrackedEntity(const EntityItemID& entityID) {
if (hasAABox && downloadedCollisionTypes.count(modelEntity->getShapeType()) != 0) { if (hasAABox && downloadedCollisionTypes.count(modelEntity->getShapeType()) != 0) {
// Only track entities with downloaded collision bodies. // Only track entities with downloaded collision bodies.
_trackedEntities.emplace(entityID, entity); _trackedEntities.emplace(entityID, entity);
qCDebug(interfaceapp) << "Safe Landing: Tracking entity " << entity->getItemName();
} }
} }
} }
@ -110,7 +109,6 @@ bool SafeLanding::isLoadSequenceComplete() {
_initialEnd = INVALID_SEQUENCE; _initialEnd = INVALID_SEQUENCE;
_entityTree = nullptr; _entityTree = nullptr;
EntityTreeRenderer::setEntityLoadingPriorityFunction(StandardPriority); EntityTreeRenderer::setEntityLoadingPriorityFunction(StandardPriority);
qCDebug(interfaceapp) << "Safe Landing: load sequence complete";
} }
return !_trackingEntities; return !_trackingEntities;

View file

@ -86,23 +86,23 @@ bool CollisionPick::isLoaded() const {
return !_mathPick.shouldComputeShapeInfo() || (_cachedResource && _cachedResource->isLoaded()); return !_mathPick.shouldComputeShapeInfo() || (_cachedResource && _cachedResource->isLoaded());
} }
bool CollisionPick::getShapeInfoReady() { bool CollisionPick::getShapeInfoReady(const CollisionRegion& pick) {
if (_mathPick.shouldComputeShapeInfo()) { if (_mathPick.shouldComputeShapeInfo()) {
if (_cachedResource && _cachedResource->isLoaded()) { if (_cachedResource && _cachedResource->isLoaded()) {
computeShapeInfo(_mathPick, *_mathPick.shapeInfo, _cachedResource); computeShapeInfo(pick, *_mathPick.shapeInfo, _cachedResource);
_mathPick.loaded = true; _mathPick.loaded = true;
} else { } else {
_mathPick.loaded = false; _mathPick.loaded = false;
} }
} else { } else {
computeShapeInfoDimensionsOnly(_mathPick, *_mathPick.shapeInfo, _cachedResource); computeShapeInfoDimensionsOnly(pick, *_mathPick.shapeInfo, _cachedResource);
_mathPick.loaded = true; _mathPick.loaded = true;
} }
return _mathPick.loaded; return _mathPick.loaded;
} }
void CollisionPick::computeShapeInfoDimensionsOnly(CollisionRegion& pick, ShapeInfo& shapeInfo, QSharedPointer<GeometryResource> resource) { void CollisionPick::computeShapeInfoDimensionsOnly(const CollisionRegion& pick, ShapeInfo& shapeInfo, QSharedPointer<GeometryResource> resource) {
ShapeType type = shapeInfo.getType(); ShapeType type = shapeInfo.getType();
glm::vec3 dimensions = pick.transform.getScale(); glm::vec3 dimensions = pick.transform.getScale();
QString modelURL = (resource ? resource->getURL().toString() : ""); QString modelURL = (resource ? resource->getURL().toString() : "");
@ -115,7 +115,7 @@ void CollisionPick::computeShapeInfoDimensionsOnly(CollisionRegion& pick, ShapeI
} }
} }
void CollisionPick::computeShapeInfo(CollisionRegion& pick, ShapeInfo& shapeInfo, QSharedPointer<GeometryResource> resource) { void CollisionPick::computeShapeInfo(const CollisionRegion& pick, ShapeInfo& shapeInfo, QSharedPointer<GeometryResource> resource) {
// This code was copied and modified from RenderableModelEntityItem::computeShapeInfo // This code was copied and modified from RenderableModelEntityItem::computeShapeInfo
// TODO: Move to some shared code area (in entities-renderer? model-networking?) // TODO: Move to some shared code area (in entities-renderer? model-networking?)
// after we verify this is working and do a diff comparison with RenderableModelEntityItem::computeShapeInfo // after we verify this is working and do a diff comparison with RenderableModelEntityItem::computeShapeInfo
@ -357,12 +357,14 @@ CollisionPick::CollisionPick(const PickFilter& filter, float maxDistance, bool e
CollisionRegion CollisionPick::getMathematicalPick() const { CollisionRegion CollisionPick::getMathematicalPick() const {
CollisionRegion mathPick = _mathPick; CollisionRegion mathPick = _mathPick;
mathPick.loaded = isLoaded(); mathPick.loaded = isLoaded();
if (!parentTransform) { if (parentTransform) {
return mathPick; Transform parentTransformValue = parentTransform->getTransform();
} else { mathPick.transform = parentTransformValue.worldTransform(mathPick.transform);
mathPick.transform = parentTransform->getTransform().worldTransform(mathPick.transform); glm::vec3 scale = parentTransformValue.getScale();
return mathPick; float largestDimension = glm::max(glm::max(scale.x, scale.y), scale.z);
mathPick.threshold *= largestDimension;
} }
return mathPick;
} }
void CollisionPick::filterIntersections(std::vector<ContactTestResult>& intersections) const { void CollisionPick::filterIntersections(std::vector<ContactTestResult>& intersections) const {
@ -393,9 +395,9 @@ PickResultPointer CollisionPick::getEntityIntersection(const CollisionRegion& pi
// Cannot compute result // Cannot compute result
return std::make_shared<CollisionPickResult>(pick.toVariantMap(), std::vector<ContactTestResult>(), std::vector<ContactTestResult>()); return std::make_shared<CollisionPickResult>(pick.toVariantMap(), std::vector<ContactTestResult>(), std::vector<ContactTestResult>());
} }
getShapeInfoReady(); getShapeInfoReady(pick);
auto entityIntersections = _physicsEngine->contactTest(USER_COLLISION_MASK_ENTITIES, *pick.shapeInfo, pick.transform, USER_COLLISION_GROUP_DYNAMIC, pick.threshold); auto entityIntersections = _physicsEngine->contactTest(USER_COLLISION_MASK_ENTITIES, *_mathPick.shapeInfo, pick.transform, USER_COLLISION_GROUP_DYNAMIC, pick.threshold);
filterIntersections(entityIntersections); filterIntersections(entityIntersections);
return std::make_shared<CollisionPickResult>(pick, entityIntersections, std::vector<ContactTestResult>()); return std::make_shared<CollisionPickResult>(pick, entityIntersections, std::vector<ContactTestResult>());
} }
@ -409,9 +411,9 @@ PickResultPointer CollisionPick::getAvatarIntersection(const CollisionRegion& pi
// Cannot compute result // Cannot compute result
return std::make_shared<CollisionPickResult>(pick, std::vector<ContactTestResult>(), std::vector<ContactTestResult>()); return std::make_shared<CollisionPickResult>(pick, std::vector<ContactTestResult>(), std::vector<ContactTestResult>());
} }
getShapeInfoReady(); getShapeInfoReady(pick);
auto avatarIntersections = _physicsEngine->contactTest(USER_COLLISION_MASK_AVATARS, *pick.shapeInfo, pick.transform, USER_COLLISION_GROUP_DYNAMIC, pick.threshold); auto avatarIntersections = _physicsEngine->contactTest(USER_COLLISION_MASK_AVATARS, *_mathPick.shapeInfo, pick.transform, USER_COLLISION_GROUP_DYNAMIC, pick.threshold);
filterIntersections(avatarIntersections); filterIntersections(avatarIntersections);
return std::make_shared<CollisionPickResult>(pick, std::vector<ContactTestResult>(), avatarIntersections); return std::make_shared<CollisionPickResult>(pick, std::vector<ContactTestResult>(), avatarIntersections);
} }

View file

@ -62,9 +62,9 @@ protected:
// Returns true if the resource for _mathPick.shapeInfo is loaded or if a resource is not needed. // Returns true if the resource for _mathPick.shapeInfo is loaded or if a resource is not needed.
bool isLoaded() const; bool isLoaded() const;
// Returns true if _mathPick.shapeInfo is valid. Otherwise, attempts to get the _mathPick ready for use. // Returns true if _mathPick.shapeInfo is valid. Otherwise, attempts to get the _mathPick ready for use.
bool getShapeInfoReady(); bool getShapeInfoReady(const CollisionRegion& pick);
void computeShapeInfo(CollisionRegion& pick, ShapeInfo& shapeInfo, QSharedPointer<GeometryResource> resource); void computeShapeInfo(const CollisionRegion& pick, ShapeInfo& shapeInfo, QSharedPointer<GeometryResource> resource);
void computeShapeInfoDimensionsOnly(CollisionRegion& pick, ShapeInfo& shapeInfo, QSharedPointer<GeometryResource> resource); void computeShapeInfoDimensionsOnly(const CollisionRegion& pick, ShapeInfo& shapeInfo, QSharedPointer<GeometryResource> resource);
void filterIntersections(std::vector<ContactTestResult>& intersections) const; void filterIntersections(std::vector<ContactTestResult>& intersections) const;
CollisionRegion _mathPick; CollisionRegion _mathPick;

View file

@ -24,11 +24,14 @@
#include "CollisionPick.h" #include "CollisionPick.h"
#include "SpatialParentFinder.h" #include "SpatialParentFinder.h"
#include "NestableTransformNode.h"
#include "PickTransformNode.h" #include "PickTransformNode.h"
#include "MouseTransformNode.h" #include "MouseTransformNode.h"
#include "avatar/MyAvatarHeadTransformNode.h" #include "avatar/MyAvatarHeadTransformNode.h"
#include "avatar/AvatarManager.h" #include "avatar/AvatarManager.h"
#include "NestableTransformNode.h"
#include "avatars-renderer/AvatarTransformNode.h"
#include "ui/overlays/OverlayTransformNode.h"
#include "EntityTransformNode.h"
#include <ScriptEngine.h> #include <ScriptEngine.h>
@ -260,9 +263,16 @@ unsigned int PickScriptingInterface::createParabolaPick(const QVariant& properti
* A set of properties that can be passed to {@link Picks.createPick} to create a new Collision Pick. * A set of properties that can be passed to {@link Picks.createPick} to create a new Collision Pick.
* @typedef {object} Picks.CollisionPickProperties * @typedef {object} Picks.CollisionPickProperties
* @property {Shape} shape - The information about the collision region's size and shape. * @property {boolean} [enabled=false] If this Pick should start enabled or not. Disabled Picks do not updated their pick results.
* @property {Vec3} position - The position of the collision region. * @property {number} [filter=Picks.PICK_NOTHING] The filter for this Pick to use, constructed using filter flags combined using bitwise OR.
* @property {Quat} orientation - The orientation of the collision region. * @property {Shape} shape - The information about the collision region's size and shape. Dimensions are in world space, but will scale with the parent if defined.
* @property {Vec3} position - The position of the collision region, relative to a parent if defined.
* @property {Quat} orientation - The orientation of the collision region, relative to a parent if defined.
* @property {float} threshold - The approximate minimum penetration depth for a test object to be considered in contact with the collision region.
* The depth is measured in world space, but will scale with the parent if defined.
* @property {Uuid} parentID - The ID of the parent, either an avatar, an entity, or an overlay.
* @property {number} parentJointIndex - The joint of the parent to parent to, for example, the joints on the model of an avatar. (default = 0, no joint)
* @property {string} joint - If "Mouse," parents the pick to the mouse. If "Avatar," parents the pick to MyAvatar's head. Otherwise, parents to the joint of the given name on MyAvatar.
*/ */
unsigned int PickScriptingInterface::createCollisionPick(const QVariant& properties) { unsigned int PickScriptingInterface::createCollisionPick(const QVariant& properties) {
QVariantMap propMap = properties.toMap(); QVariantMap propMap = properties.toMap();
@ -375,9 +385,18 @@ std::shared_ptr<TransformNode> PickScriptingInterface::createTransformNode(const
} }
auto sharedNestablePointer = nestablePointer.lock(); auto sharedNestablePointer = nestablePointer.lock();
if (success && sharedNestablePointer) { if (success && sharedNestablePointer) {
NestableType nestableType = sharedNestablePointer->getNestableType();
if (nestableType == NestableType::Avatar) {
return std::make_shared<AvatarTransformNode>(std::static_pointer_cast<Avatar>(sharedNestablePointer), parentJointIndex);
} else if (nestableType == NestableType::Overlay) {
return std::make_shared<OverlayTransformNode>(std::static_pointer_cast<Base3DOverlay>(sharedNestablePointer), parentJointIndex);
} else if (nestableType == NestableType::Entity) {
return std::make_shared<EntityTransformNode>(std::static_pointer_cast<EntityItem>(sharedNestablePointer), parentJointIndex);
} else {
return std::make_shared<NestableTransformNode>(nestablePointer, parentJointIndex); return std::make_shared<NestableTransformNode>(nestablePointer, parentJointIndex);
} }
} }
}
unsigned int pickID = propMap["parentID"].toUInt(); unsigned int pickID = propMap["parentID"].toUInt();
if (pickID != 0) { if (pickID != 0) {
@ -394,7 +413,7 @@ std::shared_ptr<TransformNode> PickScriptingInterface::createTransformNode(const
} else if (!joint.isNull()) { } else if (!joint.isNull()) {
auto myAvatar = DependencyManager::get<AvatarManager>()->getMyAvatar(); auto myAvatar = DependencyManager::get<AvatarManager>()->getMyAvatar();
int jointIndex = myAvatar->getJointIndex(joint); int jointIndex = myAvatar->getJointIndex(joint);
return std::make_shared<NestableTransformNode>(myAvatar, jointIndex); return std::make_shared<AvatarTransformNode>(myAvatar, jointIndex);
} }
} }

View file

@ -0,0 +1,141 @@
//
// Created by Anthony J. Thibault 2018/08/06
// Copyright 2018 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#include "AnimStats.h"
#include <avatar/AvatarManager.h>
#include <OffscreenUi.h>
#include "Menu.h"
HIFI_QML_DEF(AnimStats)
static AnimStats* INSTANCE{ nullptr };
AnimStats* AnimStats::getInstance() {
Q_ASSERT(INSTANCE);
return INSTANCE;
}
AnimStats::AnimStats(QQuickItem* parent) : QQuickItem(parent) {
INSTANCE = this;
}
void AnimStats::updateStats(bool force) {
QQuickItem* parent = parentItem();
if (!force) {
if (!Menu::getInstance()->isOptionChecked(MenuOption::AnimStats)) {
if (parent->isVisible()) {
parent->setVisible(false);
}
return;
} else if (!parent->isVisible()) {
parent->setVisible(true);
}
}
auto avatarManager = DependencyManager::get<AvatarManager>();
auto myAvatar = avatarManager->getMyAvatar();
auto debugAlphaMap = myAvatar->getSkeletonModel()->getRig().getDebugAlphaMap();
// update animation debug alpha values
QStringList newAnimAlphaValues;
qint64 now = usecTimestampNow();
for (auto& iter : debugAlphaMap) {
QString key = iter.first;
float alpha = std::get<0>(iter.second);
auto prevIter = _prevDebugAlphaMap.find(key);
if (prevIter != _prevDebugAlphaMap.end()) {
float prevAlpha = std::get<0>(iter.second);
if (prevAlpha != alpha) {
// change detected: reset timer
_animAlphaValueChangedTimers[key] = now;
}
} else {
// new value: start timer
_animAlphaValueChangedTimers[key] = now;
}
AnimNodeType type = std::get<1>(iter.second);
if (type == AnimNodeType::Clip) {
// figure out the grayScale color of this line.
const float LIT_TIME = 2.0f;
const float FADE_OUT_TIME = 1.0f;
float grayScale = 0.0f;
float secondsElapsed = (float)(now - _animAlphaValueChangedTimers[key]) / (float)USECS_PER_SECOND;
if (secondsElapsed < LIT_TIME) {
grayScale = 1.0f;
} else if (secondsElapsed < LIT_TIME + FADE_OUT_TIME) {
grayScale = (FADE_OUT_TIME - (secondsElapsed - LIT_TIME)) / FADE_OUT_TIME;
} else {
grayScale = 0.0f;
}
if (grayScale > 0.0f) {
// append grayScaleColor to start of debug string
newAnimAlphaValues << QString::number(grayScale, 'f', 2) + "|" + key + ": " + QString::number(alpha, 'f', 3);
}
}
}
_animAlphaValues = newAnimAlphaValues;
_prevDebugAlphaMap = debugAlphaMap;
emit animAlphaValuesChanged();
// update animation anim vars
_animVarsList.clear();
auto animVars = myAvatar->getSkeletonModel()->getRig().getAnimVars().toDebugMap();
for (auto& iter : animVars) {
QString key = iter.first;
QString value = iter.second;
auto prevIter = _prevAnimVars.find(key);
if (prevIter != _prevAnimVars.end()) {
QString prevValue = prevIter->second;
if (value != prevValue) {
// change detected: reset timer
_animVarChangedTimers[key] = now;
}
} else {
// new value: start timer
_animVarChangedTimers[key] = now;
}
// figure out the grayScale color of this line.
const float LIT_TIME = 2.0f;
const float FADE_OUT_TIME = 0.5f;
float grayScale = 0.0f;
float secondsElapsed = (float)(now - _animVarChangedTimers[key]) / (float)USECS_PER_SECOND;
if (secondsElapsed < LIT_TIME) {
grayScale = 1.0f;
} else if (secondsElapsed < LIT_TIME + FADE_OUT_TIME) {
grayScale = (FADE_OUT_TIME - (secondsElapsed - LIT_TIME)) / FADE_OUT_TIME;
} else {
grayScale = 0.0f;
}
if (grayScale > 0.0f) {
// append grayScaleColor to start of debug string
_animVarsList << QString::number(grayScale, 'f', 2) + "|" + key + ": " + value;
}
}
_prevAnimVars = animVars;
emit animVarsChanged();
// animation state machines
_animStateMachines.clear();
auto stateMachineMap = myAvatar->getSkeletonModel()->getRig().getStateMachineMap();
for (auto& iter : stateMachineMap) {
_animStateMachines << iter.second;
}
emit animStateMachinesChanged();
}

View file

@ -0,0 +1,55 @@
//
// Created by Anthony J. Thibault 2018/08/06
// Copyright 2018 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#ifndef hifi_AnimStats_h
#define hifi_AnimStats_h
#include <OffscreenQmlElement.h>
#include <AnimContext.h>
class AnimStats : public QQuickItem {
Q_OBJECT
HIFI_QML_DECL
Q_PROPERTY(QStringList animAlphaValues READ animAlphaValues NOTIFY animAlphaValuesChanged)
Q_PROPERTY(QStringList animVars READ animVars NOTIFY animVarsChanged)
Q_PROPERTY(QStringList animStateMachines READ animStateMachines NOTIFY animStateMachinesChanged)
public:
static AnimStats* getInstance();
AnimStats(QQuickItem* parent = nullptr);
void updateStats(bool force = false);
QStringList animAlphaValues() { return _animAlphaValues; }
QStringList animVars() { return _animVarsList; }
QStringList animStateMachines() { return _animStateMachines; }
public slots:
void forceUpdateStats() { updateStats(true); }
signals:
void animAlphaValuesChanged();
void animVarsChanged();
void animStateMachinesChanged();
private:
QStringList _animAlphaValues;
AnimContext::DebugAlphaMap _prevDebugAlphaMap; // alpha values from previous frame
std::map<QString, qint64> _animAlphaValueChangedTimers; // last time alpha value has changed
QStringList _animVarsList;
std::map<QString, QString> _prevAnimVars; // anim vars from previous frame
std::map<QString, qint64> _animVarChangedTimers; // last time animVar value has changed.
QStringList _animStateMachines;
};
#endif // hifi_AnimStats_h

View file

@ -207,14 +207,6 @@ void Stats::updateStats(bool force) {
// Third column, avatar stats // Third column, avatar stats
auto myAvatar = avatarManager->getMyAvatar(); auto myAvatar = avatarManager->getMyAvatar();
auto animStack = myAvatar->getSkeletonModel()->getRig().getAnimStack();
_animStackNames.clear();
for (auto animStackIterator = animStack.begin(); animStackIterator != animStack.end(); ++animStackIterator) {
_animStackNames << animStackIterator->first + ": " + QString::number(animStackIterator->second,'f',3);
}
emit animStackNamesChanged();
glm::vec3 avatarPos = myAvatar->getWorldPosition(); glm::vec3 avatarPos = myAvatar->getWorldPosition();
STAT_UPDATE(position, QVector3D(avatarPos.x, avatarPos.y, avatarPos.z)); STAT_UPDATE(position, QVector3D(avatarPos.x, avatarPos.y, avatarPos.z));
STAT_UPDATE_FLOAT(speed, glm::length(myAvatar->getWorldVelocity()), 0.01f); STAT_UPDATE_FLOAT(speed, glm::length(myAvatar->getWorldVelocity()), 0.01f);

View file

@ -134,7 +134,6 @@ private: \
* @property {number} batchFrameTime - <em>Read-only.</em> * @property {number} batchFrameTime - <em>Read-only.</em>
* @property {number} engineFrameTime - <em>Read-only.</em> * @property {number} engineFrameTime - <em>Read-only.</em>
* @property {number} avatarSimulationTime - <em>Read-only.</em> * @property {number} avatarSimulationTime - <em>Read-only.</em>
* @property {string[]} animStackNames - <em>Read-only.</em>
* *
* *
* @property {number} x * @property {number} x
@ -292,7 +291,6 @@ class Stats : public QQuickItem {
STATS_PROPERTY(float, batchFrameTime, 0) STATS_PROPERTY(float, batchFrameTime, 0)
STATS_PROPERTY(float, engineFrameTime, 0) STATS_PROPERTY(float, engineFrameTime, 0)
STATS_PROPERTY(float, avatarSimulationTime, 0) STATS_PROPERTY(float, avatarSimulationTime, 0)
Q_PROPERTY(QStringList animStackNames READ animStackNames NOTIFY animStackNamesChanged)
STATS_PROPERTY(int, stylusPicksCount, 0) STATS_PROPERTY(int, stylusPicksCount, 0)
STATS_PROPERTY(int, rayPicksCount, 0) STATS_PROPERTY(int, rayPicksCount, 0)
@ -326,7 +324,6 @@ public:
} }
QStringList downloadUrls () { return _downloadUrls; } QStringList downloadUrls () { return _downloadUrls; }
QStringList animStackNames() { return _animStackNames; }
public slots: public slots:
void forceUpdateStats() { updateStats(true); } void forceUpdateStats() { updateStats(true); }
@ -1028,13 +1025,6 @@ signals:
*/ */
void avatarSimulationTimeChanged(); void avatarSimulationTimeChanged();
/**jsdoc
* Triggered when the value of the <code>animStackNames</code> property changes.
* @function Stats.animStackNamesChanged
* @returns {Signal}
*/
void animStackNamesChanged();
/**jsdoc /**jsdoc
* Triggered when the value of the <code>rectifiedTextureCount</code> property changes. * Triggered when the value of the <code>rectifiedTextureCount</code> property changes.
* @function Stats.rectifiedTextureCountChanged * @function Stats.rectifiedTextureCountChanged
@ -1049,7 +1039,6 @@ signals:
*/ */
void decimatedTextureCountChanged(); void decimatedTextureCountChanged();
// QQuickItem signals. // QQuickItem signals.
/**jsdoc /**jsdoc
@ -1336,7 +1325,6 @@ private:
QString _monospaceFont; QString _monospaceFont;
const AudioIOStats* _audioStats; const AudioIOStats* _audioStats;
QStringList _downloadUrls = QStringList(); QStringList _downloadUrls = QStringList();
QStringList _animStackNames = QStringList();
}; };
#endif // hifi_Stats_h #endif // hifi_Stats_h

View file

@ -238,7 +238,9 @@ void Base3DOverlay::setProperties(const QVariantMap& originalProperties) {
*/ */
QVariant Base3DOverlay::getProperty(const QString& property) { QVariant Base3DOverlay::getProperty(const QString& property) {
if (property == "name") { if (property == "name") {
return _nameLock.resultWithReadLock<QString>([&] {
return _name; return _name;
});
} }
if (property == "position" || property == "start" || property == "p1" || property == "point") { if (property == "position" || property == "start" || property == "p1" || property == "point") {
return vec3toVariant(getWorldPosition()); return vec3toVariant(getWorldPosition());
@ -346,6 +348,20 @@ void Base3DOverlay::setVisible(bool visible) {
notifyRenderVariableChange(); notifyRenderVariableChange();
} }
QString Base3DOverlay::getName() const {
return _nameLock.resultWithReadLock<QString>([&] {
return QString("Overlay:") + _name;
});
}
void Base3DOverlay::setName(QString name) {
_nameLock.withWriteLock([&] {
_name = name;
});
}
render::ItemKey Base3DOverlay::getKey() { render::ItemKey Base3DOverlay::getKey() {
auto builder = render::ItemKey::Builder(Overlay::getKey()); auto builder = render::ItemKey::Builder(Overlay::getKey());

View file

@ -29,8 +29,8 @@ public:
virtual OverlayID getOverlayID() const override { return OverlayID(getID().toString()); } virtual OverlayID getOverlayID() const override { return OverlayID(getID().toString()); }
void setOverlayID(OverlayID overlayID) override { setID(overlayID); } void setOverlayID(OverlayID overlayID) override { setID(overlayID); }
virtual QString getName() const override { return QString("Overlay:") + _name; } virtual QString getName() const override;
void setName(QString name) { _name = name; } void setName(QString name);
// getters // getters
virtual bool is3D() const override { return true; } virtual bool is3D() const override { return true; }
@ -107,6 +107,7 @@ protected:
mutable bool _renderVariableDirty { true }; mutable bool _renderVariableDirty { true };
QString _name; QString _name;
mutable ReadWriteLockable _nameLock;
}; };
#endif // hifi_Base3DOverlay_h #endif // hifi_Base3DOverlay_h

View file

@ -0,0 +1,13 @@
//
// Created by Sabrina Shanman 9/5/2018
// Copyright 2018 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#include "OverlayTransformNode.h"
template<>
glm::vec3 BaseNestableTransformNode<Base3DOverlay>::getActualScale(const std::shared_ptr<Base3DOverlay>& nestablePointer) const {
return nestablePointer->getBounds().getScale();
}

View file

@ -0,0 +1,21 @@
//
// Created by Sabrina Shanman 9/5/2018
// Copyright 2018 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#ifndef hifi_OverlayTransformNode_h
#define hifi_OverlayTransformNode_h
#include "NestableTransformNode.h"
#include "Base3DOverlay.h"
// For 3D overlays only
class OverlayTransformNode : public BaseNestableTransformNode<Base3DOverlay> {
public:
OverlayTransformNode(std::weak_ptr<Base3DOverlay> spatiallyNestable, int jointIndex) : BaseNestableTransformNode(spatiallyNestable, jointIndex) {};
};
#endif // hifi_OverlayTransformNode_h

View file

@ -27,7 +27,7 @@ AnimBlendLinear::~AnimBlendLinear() {
const AnimPoseVec& AnimBlendLinear::evaluate(const AnimVariantMap& animVars, const AnimContext& context, float dt, AnimVariantMap& triggersOut) { const AnimPoseVec& AnimBlendLinear::evaluate(const AnimVariantMap& animVars, const AnimContext& context, float dt, AnimVariantMap& triggersOut) {
_alpha = animVars.lookup(_alphaVar, _alpha); _alpha = animVars.lookup(_alphaVar, _alpha);
float parentAlpha = _animStack[_id]; float parentDebugAlpha = context.getDebugAlpha(_id);
if (_children.size() == 0) { if (_children.size() == 0) {
for (auto&& pose : _poses) { for (auto&& pose : _poses) {
@ -35,7 +35,7 @@ const AnimPoseVec& AnimBlendLinear::evaluate(const AnimVariantMap& animVars, con
} }
} else if (_children.size() == 1) { } else if (_children.size() == 1) {
_poses = _children[0]->evaluate(animVars, context, dt, triggersOut); _poses = _children[0]->evaluate(animVars, context, dt, triggersOut);
_animStack[_children[0]->getID()] = parentAlpha; context.setDebugAlpha(_children[0]->getID(), parentDebugAlpha, _children[0]->getType());
} else { } else {
float clampedAlpha = glm::clamp(_alpha, 0.0f, (float)(_children.size() - 1)); float clampedAlpha = glm::clamp(_alpha, 0.0f, (float)(_children.size() - 1));
size_t prevPoseIndex = glm::floor(clampedAlpha); size_t prevPoseIndex = glm::floor(clampedAlpha);
@ -48,12 +48,12 @@ const AnimPoseVec& AnimBlendLinear::evaluate(const AnimVariantMap& animVars, con
float weight2 = 0.0f; float weight2 = 0.0f;
if (prevPoseIndex == nextPoseIndex) { if (prevPoseIndex == nextPoseIndex) {
weight2 = 1.0f; weight2 = 1.0f;
_animStack[_children[nextPoseIndex]->getID()] = weight2 * parentAlpha; context.setDebugAlpha(_children[nextPoseIndex]->getID(), weight2 * parentDebugAlpha, _children[nextPoseIndex]->getType());
} else { } else {
weight2 = alpha; weight2 = alpha;
weight1 = 1.0f - weight2; weight1 = 1.0f - weight2;
_animStack[_children[prevPoseIndex]->getID()] = weight1 * parentAlpha; context.setDebugAlpha(_children[prevPoseIndex]->getID(), weight1 * parentDebugAlpha, _children[prevPoseIndex]->getType());
_animStack[_children[nextPoseIndex]->getID()] = weight2 * parentAlpha; context.setDebugAlpha(_children[nextPoseIndex]->getID(), weight2 * parentDebugAlpha, _children[nextPoseIndex]->getType());
} }
} }
processOutputJoints(triggersOut); processOutputJoints(triggersOut);

View file

@ -62,9 +62,7 @@ const AnimPoseVec& AnimBlendLinearMove::evaluate(const AnimVariantMap& animVars,
speed = animVars.lookup("moveForwardSpeed", speed); speed = animVars.lookup("moveForwardSpeed", speed);
} }
_alpha = calculateAlpha(speed, _characteristicSpeeds); _alpha = calculateAlpha(speed, _characteristicSpeeds);
float parentAlpha = _animStack[_id]; float parentDebugAlpha = context.getDebugAlpha(_id);
_animStack["speed"] = speed;
if (_children.size() == 0) { if (_children.size() == 0) {
for (auto&& pose : _poses) { for (auto&& pose : _poses) {
@ -77,7 +75,7 @@ const AnimPoseVec& AnimBlendLinearMove::evaluate(const AnimVariantMap& animVars,
float prevDeltaTime, nextDeltaTime; float prevDeltaTime, nextDeltaTime;
setFrameAndPhase(dt, alpha, prevPoseIndex, nextPoseIndex, &prevDeltaTime, &nextDeltaTime, triggersOut); setFrameAndPhase(dt, alpha, prevPoseIndex, nextPoseIndex, &prevDeltaTime, &nextDeltaTime, triggersOut);
evaluateAndBlendChildren(animVars, context, triggersOut, alpha, prevPoseIndex, nextPoseIndex, prevDeltaTime, nextDeltaTime); evaluateAndBlendChildren(animVars, context, triggersOut, alpha, prevPoseIndex, nextPoseIndex, prevDeltaTime, nextDeltaTime);
_animStack[_children[0]->getID()] = parentAlpha; context.setDebugAlpha(_children[0]->getID(), parentDebugAlpha, _children[0]->getType());
} else { } else {
auto clampedAlpha = glm::clamp(_alpha, 0.0f, (float)(_children.size() - 1)); auto clampedAlpha = glm::clamp(_alpha, 0.0f, (float)(_children.size() - 1));
auto prevPoseIndex = glm::floor(clampedAlpha); auto prevPoseIndex = glm::floor(clampedAlpha);
@ -87,17 +85,11 @@ const AnimPoseVec& AnimBlendLinearMove::evaluate(const AnimVariantMap& animVars,
setFrameAndPhase(dt, alpha, prevPoseIndex, nextPoseIndex, &prevDeltaTime, &nextDeltaTime, triggersOut); setFrameAndPhase(dt, alpha, prevPoseIndex, nextPoseIndex, &prevDeltaTime, &nextDeltaTime, triggersOut);
evaluateAndBlendChildren(animVars, context, triggersOut, alpha, prevPoseIndex, nextPoseIndex, prevDeltaTime, nextDeltaTime); evaluateAndBlendChildren(animVars, context, triggersOut, alpha, prevPoseIndex, nextPoseIndex, prevDeltaTime, nextDeltaTime);
// weights are for animation stack debug purposes only.
float weight1 = 0.0f;
float weight2 = 0.0f;
if (prevPoseIndex == nextPoseIndex) { if (prevPoseIndex == nextPoseIndex) {
weight2 = 1.0f; context.setDebugAlpha(_children[nextPoseIndex]->getID(), parentDebugAlpha, _children[nextPoseIndex]->getType());
_animStack[_children[nextPoseIndex]->getID()] = weight2 * parentAlpha;
} else { } else {
weight2 = alpha; context.setDebugAlpha(_children[prevPoseIndex]->getID(), (1.0f - alpha) * parentDebugAlpha, _children[prevPoseIndex]->getType());
weight1 = 1.0f - weight2; context.setDebugAlpha(_children[nextPoseIndex]->getID(), alpha * parentDebugAlpha, _children[nextPoseIndex]->getType());
_animStack[_children[prevPoseIndex]->getID()] = weight1 * parentAlpha;
_animStack[_children[nextPoseIndex]->getID()] = weight2 * parentAlpha;
} }
} }

View file

@ -14,8 +14,27 @@
#include <glm/glm.hpp> #include <glm/glm.hpp>
#include <glm/gtc/quaternion.hpp> #include <glm/gtc/quaternion.hpp>
#include <QString>
#include <QStringList>
#include <map>
enum class AnimNodeType {
Clip = 0,
BlendLinear,
BlendLinearMove,
Overlay,
StateMachine,
Manipulator,
InverseKinematics,
DefaultPose,
TwoBoneIK,
PoleVectorConstraint,
NumTypes
};
class AnimContext { class AnimContext {
public: public:
AnimContext() {}
AnimContext(bool enableDebugDrawIKTargets, bool enableDebugDrawIKConstraints, bool enableDebugDrawIKChains, AnimContext(bool enableDebugDrawIKTargets, bool enableDebugDrawIKConstraints, bool enableDebugDrawIKChains,
const glm::mat4& geometryToRigMatrix, const glm::mat4& rigToWorldMatrix); const glm::mat4& geometryToRigMatrix, const glm::mat4& rigToWorldMatrix);
@ -25,6 +44,39 @@ public:
const glm::mat4& getGeometryToRigMatrix() const { return _geometryToRigMatrix; } const glm::mat4& getGeometryToRigMatrix() const { return _geometryToRigMatrix; }
const glm::mat4& getRigToWorldMatrix() const { return _rigToWorldMatrix; } const glm::mat4& getRigToWorldMatrix() const { return _rigToWorldMatrix; }
float getDebugAlpha(const QString& key) const {
auto it = _debugAlphaMap.find(key);
if (it != _debugAlphaMap.end()) {
return std::get<0>(it->second);
} else {
return 1.0f;
}
}
using DebugAlphaMapValue = std::tuple<float, AnimNodeType>;
using DebugAlphaMap = std::map<QString, DebugAlphaMapValue>;
void setDebugAlpha(const QString& key, float alpha, AnimNodeType type) const {
_debugAlphaMap[key] = DebugAlphaMapValue(alpha, type);
}
const DebugAlphaMap& getDebugAlphaMap() const {
return _debugAlphaMap;
}
using DebugStateMachineMapValue = QString;
using DebugStateMachineMap = std::map<QString, DebugStateMachineMapValue>;
void addStateMachineInfo(const QString& stateMachineName, const QString& currentState, const QString& previousState, bool duringInterp, float alpha) const {
if (duringInterp) {
_stateMachineMap[stateMachineName] = QString("%1: %2 -> %3 (%4)").arg(stateMachineName).arg(previousState).arg(currentState).arg(QString::number(alpha, 'f', 2));
} else {
_stateMachineMap[stateMachineName] = QString("%1: %2").arg(stateMachineName).arg(currentState);
}
}
const DebugStateMachineMap& getStateMachineMap() const { return _stateMachineMap; }
protected: protected:
bool _enableDebugDrawIKTargets { false }; bool _enableDebugDrawIKTargets { false };
@ -32,6 +84,10 @@ protected:
bool _enableDebugDrawIKChains { false }; bool _enableDebugDrawIKChains { false };
glm::mat4 _geometryToRigMatrix; glm::mat4 _geometryToRigMatrix;
glm::mat4 _rigToWorldMatrix; glm::mat4 _rigToWorldMatrix;
// used for debugging internal state of animation system.
mutable DebugAlphaMap _debugAlphaMap;
mutable DebugStateMachineMap _stateMachineMap;
}; };
#endif // hifi_AnimContext_h #endif // hifi_AnimContext_h

View file

@ -12,10 +12,6 @@
#include <QtGlobal> #include <QtGlobal>
std::map<QString, float> AnimNode::_animStack = {
{"none", 0.0f}
};
AnimNode::Pointer AnimNode::getParent() { AnimNode::Pointer AnimNode::getParent() {
return _parent.lock(); return _parent.lock();
} }

View file

@ -36,19 +36,7 @@ class QJsonObject;
class AnimNode : public std::enable_shared_from_this<AnimNode> { class AnimNode : public std::enable_shared_from_this<AnimNode> {
public: public:
enum class Type { using Type = AnimNodeType;
Clip = 0,
BlendLinear,
BlendLinearMove,
Overlay,
StateMachine,
Manipulator,
InverseKinematics,
DefaultPose,
TwoBoneIK,
PoleVectorConstraint,
NumTypes
};
using Pointer = std::shared_ptr<AnimNode>; using Pointer = std::shared_ptr<AnimNode>;
using ConstPointer = std::shared_ptr<const AnimNode>; using ConstPointer = std::shared_ptr<const AnimNode>;
@ -84,7 +72,6 @@ public:
} }
void setCurrentFrame(float frame); void setCurrentFrame(float frame);
const std::map<QString, float> getAnimStack() { return _animStack; }
template <typename F> template <typename F>
bool traverse(F func) { bool traverse(F func) {
@ -127,9 +114,6 @@ protected:
std::weak_ptr<AnimNode> _parent; std::weak_ptr<AnimNode> _parent;
std::vector<QString> _outputJointNames; std::vector<QString> _outputJointNames;
// global available to Stats.h
static std::map<QString, float> _animStack;
// no copies // no copies
AnimNode(const AnimNode&) = delete; AnimNode(const AnimNode&) = delete;
AnimNode& operator=(const AnimNode&) = delete; AnimNode& operator=(const AnimNode&) = delete;

View file

@ -23,9 +23,7 @@ AnimStateMachine::~AnimStateMachine() {
const AnimPoseVec& AnimStateMachine::evaluate(const AnimVariantMap& animVars, const AnimContext& context, float dt, AnimVariantMap& triggersOut) { const AnimPoseVec& AnimStateMachine::evaluate(const AnimVariantMap& animVars, const AnimContext& context, float dt, AnimVariantMap& triggersOut) {
if (_id.contains("userAnimStateMachine")) { float parentDebugAlpha = context.getDebugAlpha(_id);
_animStack.clear();
}
QString desiredStateID = animVars.lookup(_currentStateVar, _currentState->getID()); QString desiredStateID = animVars.lookup(_currentStateVar, _currentState->getID());
if (_currentState->getID() != desiredStateID) { if (_currentState->getID() != desiredStateID) {
@ -33,8 +31,6 @@ const AnimPoseVec& AnimStateMachine::evaluate(const AnimVariantMap& animVars, co
bool foundState = false; bool foundState = false;
for (auto& state : _states) { for (auto& state : _states) {
if (state->getID() == desiredStateID) { if (state->getID() == desiredStateID) {
// parenthesis means previous state, which is a snapshot.
_previousStateID = "(" + _currentState->getID() + ")";
switchState(animVars, context, state); switchState(animVars, context, state);
foundState = true; foundState = true;
break; break;
@ -48,8 +44,6 @@ const AnimPoseVec& AnimStateMachine::evaluate(const AnimVariantMap& animVars, co
// evaluate currentState transitions // evaluate currentState transitions
auto desiredState = evaluateTransitions(animVars); auto desiredState = evaluateTransitions(animVars);
if (desiredState != _currentState) { if (desiredState != _currentState) {
// parenthesis means previous state, which is a snapshot.
_previousStateID = "(" + _currentState->getID() + ")";
switchState(animVars, context, desiredState); switchState(animVars, context, desiredState);
} }
@ -57,17 +51,8 @@ const AnimPoseVec& AnimStateMachine::evaluate(const AnimVariantMap& animVars, co
auto currentStateNode = _children[_currentState->getChildIndex()]; auto currentStateNode = _children[_currentState->getChildIndex()];
assert(currentStateNode); assert(currentStateNode);
if (!_previousStateID.contains("none")) {
_animStack[_previousStateID] = 1.0f - _alpha;
}
if (_duringInterp) { if (_duringInterp) {
_alpha += _alphaVel * dt; _alpha += _alphaVel * dt;
if (_alpha > 1.0f) {
_animStack[_currentState->getID()] = 1.0f;
} else {
_animStack[_currentState->getID()] = _alpha;
}
if (_alpha < 1.0f) { if (_alpha < 1.0f) {
AnimPoseVec* nextPoses = nullptr; AnimPoseVec* nextPoses = nullptr;
AnimPoseVec* prevPoses = nullptr; AnimPoseVec* prevPoses = nullptr;
@ -88,26 +73,27 @@ const AnimPoseVec& AnimStateMachine::evaluate(const AnimVariantMap& animVars, co
if (_poses.size() > 0 && nextPoses && prevPoses && nextPoses->size() > 0 && prevPoses->size() > 0) { if (_poses.size() > 0 && nextPoses && prevPoses && nextPoses->size() > 0 && prevPoses->size() > 0) {
::blend(_poses.size(), &(prevPoses->at(0)), &(nextPoses->at(0)), _alpha, &_poses[0]); ::blend(_poses.size(), &(prevPoses->at(0)), &(nextPoses->at(0)), _alpha, &_poses[0]);
} }
context.setDebugAlpha(_currentState->getID(), _alpha * parentDebugAlpha, _children[_currentState->getChildIndex()]->getType());
} else { } else {
_duringInterp = false; _duringInterp = false;
if (_animStack.count(_previousStateID) > 0) {
_animStack.erase(_previousStateID);
}
_previousStateID = "none";
_prevPoses.clear(); _prevPoses.clear();
_nextPoses.clear(); _nextPoses.clear();
} }
} }
if (!_duringInterp) { if (!_duringInterp) {
_animStack[_currentState->getID()] = 1.0f; context.setDebugAlpha(_currentState->getID(), parentDebugAlpha, _children[_currentState->getChildIndex()]->getType());
_poses = currentStateNode->evaluate(animVars, context, dt, triggersOut); _poses = currentStateNode->evaluate(animVars, context, dt, triggersOut);
} }
processOutputJoints(triggersOut); processOutputJoints(triggersOut);
context.addStateMachineInfo(_id, _currentState->getID(), _previousState->getID(), _duringInterp, _alpha);
return _poses; return _poses;
} }
void AnimStateMachine::setCurrentState(State::Pointer state) { void AnimStateMachine::setCurrentState(State::Pointer state) {
_previousState = _currentState ? _currentState : state;
_currentState = state; _currentState = state;
} }
@ -152,7 +138,7 @@ void AnimStateMachine::switchState(const AnimVariantMap& animVars, const AnimCon
qCDebug(animation) << "AnimStateMachine::switchState:" << _currentState->getID() << "->" << desiredState->getID() << "duration =" << duration << "targetFrame =" << desiredState->_interpTarget << "interpType = " << (int)_interpType; qCDebug(animation) << "AnimStateMachine::switchState:" << _currentState->getID() << "->" << desiredState->getID() << "duration =" << duration << "targetFrame =" << desiredState->_interpTarget << "interpType = " << (int)_interpType;
#endif #endif
_currentState = desiredState; setCurrentState(desiredState);
} }
AnimStateMachine::State::Pointer AnimStateMachine::evaluateTransitions(const AnimVariantMap& animVars) const { AnimStateMachine::State::Pointer AnimStateMachine::evaluateTransitions(const AnimVariantMap& animVars) const {

View file

@ -138,9 +138,9 @@ protected:
float _alpha = 0.0f; float _alpha = 0.0f;
AnimPoseVec _prevPoses; AnimPoseVec _prevPoses;
AnimPoseVec _nextPoses; AnimPoseVec _nextPoses;
QString _previousStateID { "none" };
State::Pointer _currentState; State::Pointer _currentState;
State::Pointer _previousState;
std::vector<State::Pointer> _states; std::vector<State::Pointer> _states;
QString _currentStateVar; QString _currentStateVar;

View file

@ -67,6 +67,7 @@ QScriptValue AnimVariantMap::animVariantMapToScriptValue(QScriptEngine* engine,
} }
return target; return target;
} }
void AnimVariantMap::copyVariantsFrom(const AnimVariantMap& other) { void AnimVariantMap::copyVariantsFrom(const AnimVariantMap& other) {
for (auto& pair : other._map) { for (auto& pair : other._map) {
_map[pair.first] = pair.second; _map[pair.first] = pair.second;
@ -124,3 +125,43 @@ void AnimVariantMap::animVariantMapFromScriptValue(const QScriptValue& source) {
} }
} }
} }
std::map<QString, QString> AnimVariantMap::toDebugMap() const {
std::map<QString, QString> result;
for (auto& pair : _map) {
switch (pair.second.getType()) {
case AnimVariant::Type::Bool:
result[pair.first] = QString("%1").arg(pair.second.getBool());
break;
case AnimVariant::Type::Int:
result[pair.first] = QString("%1").arg(pair.second.getInt());
break;
case AnimVariant::Type::Float:
result[pair.first] = QString::number(pair.second.getFloat(), 'f', 3);
break;
case AnimVariant::Type::Vec3: {
glm::vec3 value = pair.second.getVec3();
result[pair.first] = QString("(%1, %2, %3)").
arg(QString::number(value.x, 'f', 3)).
arg(QString::number(value.y, 'f', 3)).
arg(QString::number(value.z, 'f', 3));
break;
}
case AnimVariant::Type::Quat: {
glm::quat value = pair.second.getQuat();
result[pair.first] = QString("(%1, %2, %3, %4)").
arg(QString::number(value.x, 'f', 3)).
arg(QString::number(value.y, 'f', 3)).
arg(QString::number(value.z, 'f', 3)).
arg(QString::number(value.w, 'f', 3));
break;
}
case AnimVariant::Type::String:
result[pair.first] = pair.second.getString();
break;
default:
assert(("invalid AnimVariant::Type", false));
}
}
return result;
}

View file

@ -235,6 +235,9 @@ public:
void animVariantMapFromScriptValue(const QScriptValue& object); void animVariantMapFromScriptValue(const QScriptValue& object);
void copyVariantsFrom(const AnimVariantMap& other); void copyVariantsFrom(const AnimVariantMap& other);
// For stat debugging.
std::map<QString, QString> toDebugMap() const;
#ifdef NDEBUG #ifdef NDEBUG
void dump() const { void dump() const {
qCDebug(animation) << "AnimVariantMap ="; qCDebug(animation) << "AnimVariantMap =";

View file

@ -1061,8 +1061,10 @@ void Rig::updateAnimations(float deltaTime, const glm::mat4& rootTransform, cons
// animations haven't fully loaded yet. // animations haven't fully loaded yet.
_internalPoseSet._relativePoses = _animSkeleton->getRelativeDefaultPoses(); _internalPoseSet._relativePoses = _animSkeleton->getRelativeDefaultPoses();
} }
_lastAnimVars = _animVars;
_animVars.clearTriggers(); _animVars.clearTriggers();
_animVars = triggersOut; _animVars = triggersOut;
_lastContext = context;
} }
applyOverridePoses(); applyOverridePoses();
buildAbsoluteRigPoses(_internalPoseSet._relativePoses, _internalPoseSet._absolutePoses); buildAbsoluteRigPoses(_internalPoseSet._relativePoses, _internalPoseSet._absolutePoses);

View file

@ -222,7 +222,10 @@ public:
// input assumed to be in rig space // input assumed to be in rig space
void computeHeadFromHMD(const AnimPose& hmdPose, glm::vec3& headPositionOut, glm::quat& headOrientationOut) const; void computeHeadFromHMD(const AnimPose& hmdPose, glm::vec3& headPositionOut, glm::quat& headOrientationOut) const;
const std::map<QString, float> getAnimStack() { return _animNode->getAnimStack(); } // used to debug animation playback
const AnimContext::DebugAlphaMap& getDebugAlphaMap() const { return _lastContext.getDebugAlphaMap(); }
const AnimVariantMap& getAnimVars() const { return _lastAnimVars; }
const AnimContext::DebugStateMachineMap& getStateMachineMap() const { return _lastContext.getStateMachineMap(); }
void toggleSmoothPoleVectors() { _smoothPoleVectors = !_smoothPoleVectors; }; void toggleSmoothPoleVectors() { _smoothPoleVectors = !_smoothPoleVectors; };
signals: signals:
@ -388,6 +391,9 @@ protected:
int _rigId; int _rigId;
bool _headEnabled { false }; bool _headEnabled { false };
AnimContext _lastContext;
AnimVariantMap _lastAnimVars;
}; };
#endif /* defined(__hifi__Rig__) */ #endif /* defined(__hifi__Rig__) */

View file

@ -0,0 +1,13 @@
//
// Created by Sabrina Shanman 9/5/2018
// Copyright 2018 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#include "AvatarTransformNode.h"
template<>
glm::vec3 BaseNestableTransformNode<Avatar>::getActualScale(const std::shared_ptr<Avatar>& nestablePointer) const {
return nestablePointer->scaleForChildren();
}

View file

@ -0,0 +1,20 @@
//
// Created by Sabrina Shanman 9/5/2018
// Copyright 2018 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#ifndef hifi_AvatarTransformNode_h
#define hifi_AvatarTransformNode_h
#include "NestableTransformNode.h"
#include "Avatar.h"
class AvatarTransformNode : public BaseNestableTransformNode<Avatar> {
public:
AvatarTransformNode(std::weak_ptr<Avatar> spatiallyNestable, int jointIndex) : BaseNestableTransformNode(spatiallyNestable, jointIndex) {};
};
#endif // hifi_AvatarTransformNode_h

View file

@ -21,6 +21,7 @@
#include "AvatarLogging.h" #include "AvatarLogging.h"
#include "AvatarTraits.h" #include "AvatarTraits.h"
#include "Profile.h"
void AvatarReplicas::addReplica(const QUuid& parentID, AvatarSharedPointer replica) { void AvatarReplicas::addReplica(const QUuid& parentID, AvatarSharedPointer replica) {
if (parentID == QUuid()) { if (parentID == QUuid()) {
@ -214,6 +215,7 @@ AvatarSharedPointer AvatarHashMap::findAvatar(const QUuid& sessionUUID) const {
} }
void AvatarHashMap::processAvatarDataPacket(QSharedPointer<ReceivedMessage> message, SharedNodePointer sendingNode) { void AvatarHashMap::processAvatarDataPacket(QSharedPointer<ReceivedMessage> message, SharedNodePointer sendingNode) {
DETAILED_PROFILE_RANGE(network, __FUNCTION__);
PerformanceTimer perfTimer("receiveAvatar"); PerformanceTimer perfTimer("receiveAvatar");
// enumerate over all of the avatars in this packet // enumerate over all of the avatars in this packet
// only add them if mixerWeakPointer points to something (meaning that mixer is still around) // only add them if mixerWeakPointer points to something (meaning that mixer is still around)

View file

@ -0,0 +1,13 @@
//
// Created by Sabrina Shanman 9/5/2018
// Copyright 2018 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#include "EntityTransformNode.h"
template<>
glm::vec3 BaseNestableTransformNode<EntityItem>::getActualScale(const std::shared_ptr<EntityItem>& nestablePointer) const {
return nestablePointer->getScaledDimensions();
}

View file

@ -0,0 +1,20 @@
//
// Created by Sabrina Shanman 9/5/2018
// Copyright 2018 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#ifndef hifi_EntityTransformNode_h
#define hifi_EntityTransformNode_h
#include "NestableTransformNode.h"
#include "EntityItem.h"
class EntityTransformNode : public BaseNestableTransformNode<EntityItem> {
public:
EntityTransformNode(std::weak_ptr<EntityItem> spatiallyNestable, int jointIndex) : BaseNestableTransformNode(spatiallyNestable, jointIndex) {};
};
#endif // hifi_EntityTransformNode_h

View file

@ -56,13 +56,20 @@ LimitedNodeList::LimitedNodeList(int socketListenPort, int dtlsListenPort) :
qRegisterMetaType<ConnectionStep>("ConnectionStep"); qRegisterMetaType<ConnectionStep>("ConnectionStep");
auto port = (socketListenPort != INVALID_PORT) ? socketListenPort : LIMITED_NODELIST_LOCAL_PORT.get(); auto port = (socketListenPort != INVALID_PORT) ? socketListenPort : LIMITED_NODELIST_LOCAL_PORT.get();
_nodeSocket.bind(QHostAddress::AnyIPv4, port); _nodeSocket.bind(QHostAddress::AnyIPv4, port);
qCDebug(networking) << "NodeList socket is listening on" << _nodeSocket.localPort(); quint16 assignedPort = _nodeSocket.localPort();
if (socketListenPort != INVALID_PORT && socketListenPort != 0 && socketListenPort != assignedPort) {
qCCritical(networking) << "NodeList is unable to assign requested port of" << socketListenPort;
}
qCDebug(networking) << "NodeList socket is listening on" << assignedPort;
if (dtlsListenPort != INVALID_PORT) { if (dtlsListenPort != INVALID_PORT) {
// only create the DTLS socket during constructor if a custom port is passed // only create the DTLS socket during constructor if a custom port is passed
_dtlsSocket = new QUdpSocket(this); _dtlsSocket = new QUdpSocket(this);
_dtlsSocket->bind(QHostAddress::AnyIPv4, dtlsListenPort); _dtlsSocket->bind(QHostAddress::AnyIPv4, dtlsListenPort);
if (dtlsListenPort != 0 && _dtlsSocket->localPort() != dtlsListenPort) {
qCDebug(networking) << "NodeList is unable to assign requested DTLS port of" << dtlsListenPort;
}
qCDebug(networking) << "NodeList DTLS socket is listening on" << _dtlsSocket->localPort(); qCDebug(networking) << "NodeList DTLS socket is listening on" << _dtlsSocket->localPort();
} }

View file

@ -102,7 +102,7 @@ void CauterizedModel::createRenderItemSet() {
} }
} }
void CauterizedModel::updateClusterMatrices(bool triggerBlendshapes) { void CauterizedModel::updateClusterMatrices() {
PerformanceTimer perfTimer("CauterizedModel::updateClusterMatrices"); PerformanceTimer perfTimer("CauterizedModel::updateClusterMatrices");
if (!_needsUpdateClusterMatrices || !isLoaded()) { if (!_needsUpdateClusterMatrices || !isLoaded()) {
@ -175,7 +175,7 @@ void CauterizedModel::updateClusterMatrices(bool triggerBlendshapes) {
// post the blender if we're not currently waiting for one to finish // post the blender if we're not currently waiting for one to finish
auto modelBlender = DependencyManager::get<ModelBlender>(); auto modelBlender = DependencyManager::get<ModelBlender>();
if (triggerBlendshapes && modelBlender->shouldComputeBlendshapes() && geometry.hasBlendedMeshes() && _blendshapeCoefficients != _blendedBlendshapeCoefficients) { if (_blendedVertexBuffersInitialized && modelBlender->shouldComputeBlendshapes() && geometry.hasBlendedMeshes() && _blendshapeCoefficients != _blendedBlendshapeCoefficients) {
_blendedBlendshapeCoefficients = _blendshapeCoefficients; _blendedBlendshapeCoefficients = _blendshapeCoefficients;
modelBlender->noteRequiresBlend(getThisPointer()); modelBlender->noteRequiresBlend(getThisPointer());
} }

View file

@ -33,7 +33,7 @@ public:
void createRenderItemSet() override; void createRenderItemSet() override;
virtual void updateClusterMatrices(bool triggerBlendshapes = true) override; virtual void updateClusterMatrices() override;
void updateRenderItems() override; void updateRenderItems() override;
const Model::MeshState& getCauterizeMeshState(int index) const; const Model::MeshState& getCauterizeMeshState(int index) const;

View file

@ -73,7 +73,7 @@ void DeferredFramebuffer::allocate() {
_deferredFramebufferDepthColor->setDepthStencilBuffer(_primaryDepthTexture, depthFormat); _deferredFramebufferDepthColor->setDepthStencilBuffer(_primaryDepthTexture, depthFormat);
auto smoothSampler = gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_LINEAR_MIP_POINT); auto smoothSampler = gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_LINEAR_MIP_POINT, gpu::Sampler::WRAP_CLAMP);
_lightingTexture = gpu::Texture::createRenderBuffer(gpu::Element(gpu::SCALAR, gpu::FLOAT, gpu::R11G11B10), width, height, gpu::Texture::SINGLE_MIP, smoothSampler); _lightingTexture = gpu::Texture::createRenderBuffer(gpu::Element(gpu::SCALAR, gpu::FLOAT, gpu::R11G11B10), width, height, gpu::Texture::SINGLE_MIP, smoothSampler);
_lightingFramebuffer = gpu::FramebufferPointer(gpu::Framebuffer::create("lighting")); _lightingFramebuffer = gpu::FramebufferPointer(gpu::Framebuffer::create("lighting"));

View file

@ -976,7 +976,7 @@ bool Model::addToScene(const render::ScenePointer& scene,
render::Transaction& transaction, render::Transaction& transaction,
render::Item::Status::Getters& statusGetters) { render::Item::Status::Getters& statusGetters) {
if (!_addedToScene && isLoaded()) { if (!_addedToScene && isLoaded()) {
updateClusterMatrices(false); updateClusterMatrices();
if (_modelMeshRenderItems.empty()) { if (_modelMeshRenderItems.empty()) {
createRenderItemSet(); createRenderItemSet();
} }
@ -1307,6 +1307,7 @@ void Blender::run() {
if (mesh.blendshapes.isEmpty()) { if (mesh.blendshapes.isEmpty()) {
continue; continue;
} }
vertices += mesh.vertices; vertices += mesh.vertices;
normalsAndTangents += mesh.normalsAndTangents; normalsAndTangents += mesh.normalsAndTangents;
glm::vec3* meshVertices = vertices.data() + offset; glm::vec3* meshVertices = vertices.data() + offset;
@ -1486,7 +1487,7 @@ void Model::computeMeshPartLocalBounds() {
} }
// virtual // virtual
void Model::updateClusterMatrices(bool triggerBlendshapes) { void Model::updateClusterMatrices() {
DETAILED_PERFORMANCE_TIMER("Model::updateClusterMatrices"); DETAILED_PERFORMANCE_TIMER("Model::updateClusterMatrices");
if (!_needsUpdateClusterMatrices || !isLoaded()) { if (!_needsUpdateClusterMatrices || !isLoaded()) {
@ -1515,7 +1516,7 @@ void Model::updateClusterMatrices(bool triggerBlendshapes) {
// post the blender if we're not currently waiting for one to finish // post the blender if we're not currently waiting for one to finish
auto modelBlender = DependencyManager::get<ModelBlender>(); auto modelBlender = DependencyManager::get<ModelBlender>();
if (triggerBlendshapes && modelBlender->shouldComputeBlendshapes() && geometry.hasBlendedMeshes() && _blendshapeCoefficients != _blendedBlendshapeCoefficients) { if (_blendedVertexBuffersInitialized && modelBlender->shouldComputeBlendshapes() && geometry.hasBlendedMeshes() && _blendshapeCoefficients != _blendedBlendshapeCoefficients) {
_blendedBlendshapeCoefficients = _blendshapeCoefficients; _blendedBlendshapeCoefficients = _blendshapeCoefficients;
modelBlender->noteRequiresBlend(getThisPointer()); modelBlender->noteRequiresBlend(getThisPointer());
} }
@ -1552,7 +1553,7 @@ void Model::setBlendedVertices(int blendNumber, const Geometry::WeakPointer& geo
const auto vertexCount = mesh.vertices.size(); const auto vertexCount = mesh.vertices.size();
const auto verticesSize = vertexCount * sizeof(glm::vec3); const auto verticesSize = vertexCount * sizeof(glm::vec3);
const auto& buffer = _blendedVertexBuffers[i]; const auto& buffer = _blendedVertexBuffers[i];
assert(buffer); assert(buffer && _blendedVertexBuffersInitialized);
buffer->resize(mesh.vertices.size() * sizeof(glm::vec3) + mesh.normalsAndTangents.size() * sizeof(NormalType)); buffer->resize(mesh.vertices.size() * sizeof(glm::vec3) + mesh.normalsAndTangents.size() * sizeof(NormalType));
buffer->setSubData(0, verticesSize, (gpu::Byte*) vertices.constData() + index * sizeof(glm::vec3)); buffer->setSubData(0, verticesSize, (gpu::Byte*) vertices.constData() + index * sizeof(glm::vec3));
buffer->setSubData(verticesSize, mesh.normalsAndTangents.size() * sizeof(NormalType), (const gpu::Byte*) normalsAndTangents.data() + normalAndTangentIndex * sizeof(NormalType)); buffer->setSubData(verticesSize, mesh.normalsAndTangents.size() * sizeof(NormalType), (const gpu::Byte*) normalsAndTangents.data() + normalAndTangentIndex * sizeof(NormalType));
@ -1565,6 +1566,7 @@ void Model::setBlendedVertices(int blendNumber, const Geometry::WeakPointer& geo
void Model::deleteGeometry() { void Model::deleteGeometry() {
_deleteGeometryCounter++; _deleteGeometryCounter++;
_blendedVertexBuffers.clear(); _blendedVertexBuffers.clear();
_blendedVertexBuffersInitialized = false;
_meshStates.clear(); _meshStates.clear();
_rig.destroyAnimGraph(); _rig.destroyAnimGraph();
_blendedBlendshapeCoefficients.clear(); _blendedBlendshapeCoefficients.clear();
@ -1630,6 +1632,7 @@ void Model::initializeBlendshapes(const FBXMesh& mesh, int index) {
_blendedVertexBuffers[index]->setSubData(0, verticesSize, (const gpu::Byte*) mesh.vertices.constData()); _blendedVertexBuffers[index]->setSubData(0, verticesSize, (const gpu::Byte*) mesh.vertices.constData());
_blendedVertexBuffers[index]->setSubData(verticesSize, normalsAndTangents.size() * sizeof(NormalType), (const gpu::Byte*) normalsAndTangents.data()); _blendedVertexBuffers[index]->setSubData(verticesSize, normalsAndTangents.size() * sizeof(NormalType), (const gpu::Byte*) normalsAndTangents.data());
mesh.normalsAndTangents = normalsAndTangents; mesh.normalsAndTangents = normalsAndTangents;
_blendedVertexBuffersInitialized = true;
} }
void Model::createRenderItemSet() { void Model::createRenderItemSet() {

View file

@ -159,7 +159,7 @@ public:
bool getSnapModelToRegistrationPoint() { return _snapModelToRegistrationPoint; } bool getSnapModelToRegistrationPoint() { return _snapModelToRegistrationPoint; }
virtual void simulate(float deltaTime, bool fullUpdate = true); virtual void simulate(float deltaTime, bool fullUpdate = true);
virtual void updateClusterMatrices(bool triggerBlendshapes = true); virtual void updateClusterMatrices();
/// Returns a reference to the shared geometry. /// Returns a reference to the shared geometry.
const Geometry::Pointer& getGeometry() const { return _renderGeometry; } const Geometry::Pointer& getGeometry() const { return _renderGeometry; }
@ -345,6 +345,8 @@ public:
void addMaterial(graphics::MaterialLayer material, const std::string& parentMaterialName); void addMaterial(graphics::MaterialLayer material, const std::string& parentMaterialName);
void removeMaterial(graphics::MaterialPointer material, const std::string& parentMaterialName); void removeMaterial(graphics::MaterialPointer material, const std::string& parentMaterialName);
bool areBlendedVertexBuffersInitialized(int index) { return _blendedVertexBuffersInitialized; }
public slots: public slots:
void loadURLFinished(bool success); void loadURLFinished(bool success);
@ -424,8 +426,9 @@ protected:
QUrl _url; QUrl _url;
std::unordered_map<int, gpu::BufferPointer> _blendedVertexBuffers; std::unordered_map<int, gpu::BufferPointer> _blendedVertexBuffers;
bool _blendedVertexBuffersInitialized { false };
QVector<QVector<QSharedPointer<Texture> > > _dilatedTextures; QVector<QVector<QSharedPointer<Texture>>> _dilatedTextures;
QVector<float> _blendedBlendshapeCoefficients; QVector<float> _blendedBlendshapeCoefficients;
int _blendNumber; int _blendNumber;

View file

@ -31,7 +31,7 @@ int SoftAttachmentModel::getJointIndexOverride(int i) const {
// virtual // virtual
// use the _rigOverride matrices instead of the Model::_rig // use the _rigOverride matrices instead of the Model::_rig
void SoftAttachmentModel::updateClusterMatrices(bool triggerBlendshapes) { void SoftAttachmentModel::updateClusterMatrices() {
if (!_needsUpdateClusterMatrices) { if (!_needsUpdateClusterMatrices) {
return; return;
} }
@ -78,7 +78,7 @@ void SoftAttachmentModel::updateClusterMatrices(bool triggerBlendshapes) {
// post the blender if we're not currently waiting for one to finish // post the blender if we're not currently waiting for one to finish
auto modelBlender = DependencyManager::get<ModelBlender>(); auto modelBlender = DependencyManager::get<ModelBlender>();
if (triggerBlendshapes && modelBlender->shouldComputeBlendshapes() && geometry.hasBlendedMeshes() && _blendshapeCoefficients != _blendedBlendshapeCoefficients) { if (_blendedVertexBuffersInitialized && modelBlender->shouldComputeBlendshapes() && geometry.hasBlendedMeshes() && _blendshapeCoefficients != _blendedBlendshapeCoefficients) {
_blendedBlendshapeCoefficients = _blendshapeCoefficients; _blendedBlendshapeCoefficients = _blendshapeCoefficients;
modelBlender->noteRequiresBlend(getThisPointer()); modelBlender->noteRequiresBlend(getThisPointer());
} }

View file

@ -27,7 +27,7 @@ public:
~SoftAttachmentModel(); ~SoftAttachmentModel();
void updateRig(float deltaTime, glm::mat4 parentTransform) override; void updateRig(float deltaTime, glm::mat4 parentTransform) override;
void updateClusterMatrices(bool triggerBlendshapes = true) override; void updateClusterMatrices() override;
protected: protected:
int getJointIndexOverride(int i) const; int getJointIndexOverride(int i) const;

View file

@ -68,6 +68,7 @@ const glm::quat DEFAULT_AVATAR_RIGHTFOOT_ROT { -0.4016716778278351f, 0.915461599
const float DEFAULT_AVATAR_MAX_WALKING_SPEED = 2.6f; // meters / second const float DEFAULT_AVATAR_MAX_WALKING_SPEED = 2.6f; // meters / second
const float DEFAULT_AVATAR_MAX_WALKING_BACKWARD_SPEED = 2.2f; // meters / second const float DEFAULT_AVATAR_MAX_WALKING_BACKWARD_SPEED = 2.2f; // meters / second
const float DEFAULT_AVATAR_MAX_FLYING_SPEED = 30.0f; // meters / second const float DEFAULT_AVATAR_MAX_FLYING_SPEED = 30.0f; // meters / second
const float DEFAULT_AVATAR_WALK_SPEED_THRESHOLD = 0.15f;
const float DEFAULT_AVATAR_GRAVITY = -5.0f; // meters / second^2 const float DEFAULT_AVATAR_GRAVITY = -5.0f; // meters / second^2
const float DEFAULT_AVATAR_JUMP_SPEED = 3.5f; // meters / second const float DEFAULT_AVATAR_JUMP_SPEED = 3.5f; // meters / second

View file

@ -8,24 +8,7 @@
#include "NestableTransformNode.h" #include "NestableTransformNode.h"
NestableTransformNode::NestableTransformNode(SpatiallyNestableWeakPointer spatiallyNestable, int jointIndex) : template<>
_spatiallyNestable(spatiallyNestable), glm::vec3 BaseNestableTransformNode<SpatiallyNestable>::getActualScale(const std::shared_ptr<SpatiallyNestable>& nestablePointer) const {
_jointIndex(jointIndex) return nestablePointer->getAbsoluteJointScaleInObjectFrame(_jointIndex);
{
}
Transform NestableTransformNode::getTransform() {
auto nestable = _spatiallyNestable.lock();
if (!nestable) {
return Transform();
}
bool success;
Transform jointWorldTransform = nestable->getTransform(_jointIndex, success);
if (success) {
return jointWorldTransform;
} else {
return Transform();
}
} }

View file

@ -12,14 +12,48 @@
#include "SpatiallyNestable.h" #include "SpatiallyNestable.h"
class NestableTransformNode : public TransformNode { template <typename T>
class BaseNestableTransformNode : public TransformNode {
public: public:
NestableTransformNode(SpatiallyNestableWeakPointer spatiallyNestable, int jointIndex); BaseNestableTransformNode(std::weak_ptr<T> spatiallyNestable, int jointIndex) :
Transform getTransform() override; _spatiallyNestable(spatiallyNestable),
_jointIndex(jointIndex) {
auto nestablePointer = _spatiallyNestable.lock();
if (nestablePointer) {
glm::vec3 nestableDimensions = getActualScale(nestablePointer);
_baseScale = glm::max(glm::vec3(0.001f), nestableDimensions);
}
}
Transform getTransform() override {
std::shared_ptr<T> nestable = _spatiallyNestable.lock();
if (!nestable) {
return Transform();
}
bool success;
Transform jointWorldTransform = nestable->getTransform(_jointIndex, success);
if (!success) {
return Transform();
}
jointWorldTransform.setScale(getActualScale(nestable) / _baseScale);
return jointWorldTransform;
}
glm::vec3 getActualScale(const std::shared_ptr<T>& nestablePointer) const;
protected: protected:
SpatiallyNestableWeakPointer _spatiallyNestable; std::weak_ptr<T> _spatiallyNestable;
int _jointIndex; int _jointIndex;
glm::vec3 _baseScale { 1.0f };
};
class NestableTransformNode : public BaseNestableTransformNode<SpatiallyNestable> {
public:
NestableTransformNode(std::weak_ptr<SpatiallyNestable> spatiallyNestable, int jointIndex) : BaseNestableTransformNode(spatiallyNestable, jointIndex) {};
}; };
#endif // hifi_NestableTransformNode_h #endif // hifi_NestableTransformNode_h

View file

@ -259,10 +259,11 @@ public:
* A CollisionRegion defines a volume for checking collisions in the physics simulation. * A CollisionRegion defines a volume for checking collisions in the physics simulation.
* @typedef {object} CollisionRegion * @typedef {object} CollisionRegion
* @property {Shape} shape - The information about the collision region's size and shape. * @property {Shape} shape - The information about the collision region's size and shape. Dimensions are in world space, but will scale with the parent if defined.
* @property {Vec3} position - The position of the collision region, relative to a parent if defined. * @property {Vec3} position - The position of the collision region, relative to a parent if defined.
* @property {Quat} orientation - The orientation of the collision region, relative to a parent if defined. * @property {Quat} orientation - The orientation of the collision region, relative to a parent if defined.
* @property {float} threshold - The approximate minimum penetration depth for a test object to be considered in contact with the collision region. * @property {float} threshold - The approximate minimum penetration depth for a test object to be considered in contact with the collision region.
* The depth is measured in world space, but will scale with the parent if defined.
* @property {Uuid} parentID - The ID of the parent, either an avatar, an entity, or an overlay. * @property {Uuid} parentID - The ID of the parent, either an avatar, an entity, or an overlay.
* @property {number} parentJointIndex - The joint of the parent to parent to, for example, the joints on the model of an avatar. (default = 0, no joint) * @property {number} parentJointIndex - The joint of the parent to parent to, for example, the joints on the model of an avatar. (default = 0, no joint)
* @property {string} joint - If "Mouse," parents the pick to the mouse. If "Avatar," parents the pick to MyAvatar's head. Otherwise, parents to the joint of the given name on MyAvatar. * @property {string} joint - If "Mouse," parents the pick to the mouse. If "Avatar," parents the pick to MyAvatar's head. Otherwise, parents to the joint of the given name on MyAvatar.

View file

@ -13,12 +13,19 @@
#include <QtQml/QQmlContext> #include <QtQml/QQmlContext>
#include <QtCore/QThread> #include <QtCore/QThread>
#include <QtGui/QGuiApplication>
#include <QtQuick/QQuickWindow>
#include <DependencyManager.h> #include <DependencyManager.h>
#include <RegisteredMetaTypes.h> #include <RegisteredMetaTypes.h>
#include "OffscreenUi.h" #include "OffscreenUi.h"
#include "shared/QtHelpers.h" #include "shared/QtHelpers.h"
#include "MainWindow.h"
#ifdef Q_OS_WIN
#include <WinUser.h>
#endif
static auto CONTENT_WINDOW_QML = QUrl("InteractiveWindow.qml"); static auto CONTENT_WINDOW_QML = QUrl("InteractiveWindow.qml");
@ -87,6 +94,12 @@ InteractiveWindow::InteractiveWindow(const QString& sourceUrl, const QVariantMap
connect(object, SIGNAL(windowClosed()), this, SIGNAL(closed()), Qt::QueuedConnection); connect(object, SIGNAL(windowClosed()), this, SIGNAL(closed()), Qt::QueuedConnection);
connect(object, SIGNAL(selfDestruct()), this, SLOT(close()), Qt::QueuedConnection); connect(object, SIGNAL(selfDestruct()), this, SLOT(close()), Qt::QueuedConnection);
#ifdef Q_OS_WIN
connect(object, SIGNAL(nativeWindowChanged()), this, SLOT(parentNativeWindowToMainWindow()), Qt::QueuedConnection);
connect(object, SIGNAL(interactiveWindowVisibleChanged()), this, SLOT(parentNativeWindowToMainWindow()), Qt::QueuedConnection);
connect(object, SIGNAL(presentationModeChanged()), this, SLOT(parentNativeWindowToMainWindow()), Qt::QueuedConnection);
#endif
QUrl sourceURL{ sourceUrl }; QUrl sourceURL{ sourceUrl };
// If the passed URL doesn't correspond to a known scheme, assume it's a local file path // If the passed URL doesn't correspond to a known scheme, assume it's a local file path
if (!KNOWN_SCHEMES.contains(sourceURL.scheme(), Qt::CaseInsensitive)) { if (!KNOWN_SCHEMES.contains(sourceURL.scheme(), Qt::CaseInsensitive)) {
@ -279,6 +292,24 @@ int InteractiveWindow::getPresentationMode() const {
return _qmlWindow->property(PRESENTATION_MODE_PROPERTY).toInt(); return _qmlWindow->property(PRESENTATION_MODE_PROPERTY).toInt();
} }
#ifdef Q_OS_WIN
void InteractiveWindow::parentNativeWindowToMainWindow() {
if (QThread::currentThread() != thread()) {
QMetaObject::invokeMethod(this, "parentNativeWindowToMainWindow");
return;
}
if (_qmlWindow.isNull()) {
return;
}
const auto nativeWindowProperty = _qmlWindow->property("nativeWindow");
if (nativeWindowProperty.isNull() || !nativeWindowProperty.isValid()) {
return;
}
const auto nativeWindow = qvariant_cast<QQuickWindow*>(nativeWindowProperty);
SetWindowLongPtr((HWND)nativeWindow->winId(), GWLP_HWNDPARENT, (LONG)MainWindow::findMainWindow()->winId());
}
#endif
void InteractiveWindow::setPresentationMode(int presentationMode) { void InteractiveWindow::setPresentationMode(int presentationMode) {
if (QThread::currentThread() != thread()) { if (QThread::currentThread() != thread()) {
QMetaObject::invokeMethod(this, "setPresentationMode", Q_ARG(int, presentationMode)); QMetaObject::invokeMethod(this, "setPresentationMode", Q_ARG(int, presentationMode));

View file

@ -84,6 +84,10 @@ private:
Q_INVOKABLE void setPresentationMode(int presentationMode); Q_INVOKABLE void setPresentationMode(int presentationMode);
Q_INVOKABLE int getPresentationMode() const; Q_INVOKABLE int getPresentationMode() const;
#ifdef Q_OS_WIN
Q_INVOKABLE void parentNativeWindowToMainWindow();
#endif
public slots: public slots:
/**jsdoc /**jsdoc

View file

@ -22,6 +22,7 @@
#include <QDragEnterEvent> #include <QDragEnterEvent>
#include <QDropEvent> #include <QDropEvent>
#include <QMimeData> #include <QMimeData>
#include <QWindow>
#include <QDebug> #include <QDebug>
#include "ui/Logging.h" #include "ui/Logging.h"
@ -39,6 +40,18 @@ MainWindow::~MainWindow() {
qCDebug(uiLogging) << "Destroying main window"; qCDebug(uiLogging) << "Destroying main window";
} }
QWindow* MainWindow::findMainWindow() {
auto windows = qApp->topLevelWindows();
QWindow* result = nullptr;
for (const auto& window : windows) {
if (window->objectName().contains("MainWindow")) {
result = window;
break;
}
}
return result;
}
void MainWindow::restoreGeometry() { void MainWindow::restoreGeometry() {
// Did not use setGeometry() on purpose, // Did not use setGeometry() on purpose,
// see http://doc.qt.io/qt-5/qsettings.html#restoring-the-state-of-a-gui-application // see http://doc.qt.io/qt-5/qsettings.html#restoring-the-state-of-a-gui-application

View file

@ -22,6 +22,8 @@ public:
explicit MainWindow(QWidget* parent = NULL); explicit MainWindow(QWidget* parent = NULL);
~MainWindow(); ~MainWindow();
static QWindow* findMainWindow();
public slots: public slots:
void restoreGeometry(); void restoreGeometry();
void saveGeometry(); void saveGeometry();

View file

@ -29,6 +29,7 @@
#include "ui/Logging.h" #include "ui/Logging.h"
#include <PointerManager.h> #include <PointerManager.h>
#include "MainWindow.h"
/**jsdoc /**jsdoc
* @namespace OffscreenFlags * @namespace OffscreenFlags
@ -649,20 +650,7 @@ public:
} }
private: private:
QWindow* const _mainWindow { MainWindow::findMainWindow() };
static QWindow* findMainWindow() {
auto windows = qApp->topLevelWindows();
QWindow* result = nullptr;
for (auto window : windows) {
if (window->objectName().contains("MainWindow")) {
result = window;
break;
}
}
return result;
}
QWindow* const _mainWindow { findMainWindow() };
QWindow* _hackWindow { nullptr }; QWindow* _hackWindow { nullptr };
}; };

View file

@ -192,7 +192,7 @@ namespace workload {
struct Data { struct Data {
bool regulateViewRanges{ false }; // regulation is OFF by default bool regulateViewRanges{ true }; // regulation is ON by default
} data; } data;
struct DataExport { struct DataExport {

View file

@ -23,7 +23,7 @@ if (scripts.length >= 2) {
var qml = Script.resolvePath('debugWindow.qml'); var qml = Script.resolvePath('debugWindow.qml');
var HMD_DEBUG_WINDOW_GEOMETRY_KEY = 'hmdDebugWindowGeometry'; var HMD_DEBUG_WINDOW_GEOMETRY_KEY = 'hmdDebugWindowGeometry';
var hmdDebugWindowGeometryValue = Settings.getValue(HMD_DEBUG_WINDOW_GEOMETRY_KEY) var hmdDebugWindowGeometryValue = Settings.getValue(HMD_DEBUG_WINDOW_GEOMETRY_KEY);
var windowWidth = 400; var windowWidth = 400;
var windowHeight = 900; var windowHeight = 900;
@ -34,12 +34,13 @@ var windowY = 0;
if (hmdDebugWindowGeometryValue !== '') { if (hmdDebugWindowGeometryValue !== '') {
var geometry = JSON.parse(hmdDebugWindowGeometryValue); var geometry = JSON.parse(hmdDebugWindowGeometryValue);
if ((geometry.x !== 0) && (geometry.y !== 0)) {
windowWidth = geometry.width windowWidth = geometry.width;
windowHeight = geometry.height windowHeight = geometry.height;
windowX = geometry.x windowX = geometry.x;
windowY = geometry.y windowY = geometry.y;
hasPosition = true; hasPosition = true;
}
} }
var window = new OverlayWindow({ var window = new OverlayWindow({
@ -52,6 +53,12 @@ if (hasPosition) {
window.setPosition(windowX, windowY); window.setPosition(windowX, windowY);
} }
window.visibleChanged.connect(function() {
if (!window.visible) {
window.setVisible(true);
}
});
window.closed.connect(function () { Script.stop(); }); window.closed.connect(function () { Script.stop(); });
var getFormattedDate = function() { var getFormattedDate = function() {
@ -93,10 +100,10 @@ Script.scriptEnding.connect(function () {
y: window.position.y, y: window.position.y,
width: window.size.x, width: window.size.x,
height: window.size.y height: window.size.y
}) });
Settings.setValue(HMD_DEBUG_WINDOW_GEOMETRY_KEY, geometry); Settings.setValue(HMD_DEBUG_WINDOW_GEOMETRY_KEY, geometry);
window.close(); window.close();
}) });
}()); }());