Merge branch 'master' into Sitting_emote_variants

This commit is contained in:
dooglifeSF 2019-09-06 15:06:52 -07:00
commit 932093bead
28 changed files with 506 additions and 36 deletions

View file

@ -118,7 +118,7 @@ void AnimStats::updateStats(bool force) {
auto prevIter = _prevDebugAlphaMap.find(key);
if (prevIter != _prevDebugAlphaMap.end()) {
float prevAlpha = std::get<0>(iter.second);
float prevAlpha = std::get<0>(prevIter->second);
if (prevAlpha != alpha) {
// change detected: reset timer
_animAlphaValueChangedTimers[key] = now;

View file

@ -126,7 +126,10 @@ add_custom_command(TARGET ${PROJECT_NAME} PRE_BUILD
add_custom_command(TARGET ${PROJECT_NAME} POST_BUILD
COMMAND updater
COMMAND ${CMAKE_COMMAND} -E copy "${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_CFG_INTDIR}/updater" "${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_CFG_INTDIR}/${APP_NAME}.app/Contents/Resources/")
COMMAND ${CMAKE_COMMAND} -E copy "${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_CFG_INTDIR}/updater" "${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_CFG_INTDIR}/${APP_NAME}.app/Contents/MacOS/"
# Older versions of Launcher put updater in `/Contents/Resources/updater`.
COMMAND ${CMAKE_COMMAND} -E chdir "${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_CFG_INTDIR}/${APP_NAME}.app/Contents/Resources" ln -sf ../MacOS/updater updater
)
install(
TARGETS HQLauncher

View file

@ -59,7 +59,7 @@
NSString* defaultBuildTag = [json valueForKey:@"default_tag"];
NSString* launcherVersion = [launcherValues valueForKey:@"version"];
NSString* launcherUrl = [[launcherValues valueForKey:@"mac"] valueForKey:@"url"];
NSString* launcherUrl = [[[launcherValues valueForKey:@"mac"] valueForKey:@"url"] stringByRemovingPercentEncoding];
BOOL appDirectoryExist = [fileManager fileExistsAtPath:[[sharedLauncher getAppPath] stringByAppendingString:@"interface.app"]];

View file

@ -383,6 +383,13 @@ static BOOL const DELETE_ZIP_FILES = TRUE;
[self updateLatestBuildInfo];
NSString *kLauncherUrl = @"LAUNCHER_URL";
NSString *envLauncherUrl = [[NSProcessInfo processInfo] environment][kLauncherUrl];
if (envLauncherUrl != nil) {
NSLog(@"Using launcherUrl from environment: %@ = %@", kLauncherUrl, envLauncherUrl);
launcherUrl = envLauncherUrl;
}
NSDictionary* launcherArguments = [LauncherCommandlineArgs arguments];
if (newLauncherAvailable && ![launcherArguments valueForKey: @"--noUpdate"]) {
[self.downloadLauncher downloadLauncher: launcherUrl];
@ -460,11 +467,36 @@ static BOOL const DELETE_ZIP_FILES = TRUE;
-(void)runAutoupdater
{
NSTask* task = [[NSTask alloc] init];
NSException *exception;
bool launched = false;
NSString* newLauncher = [[[Launcher sharedLauncher] getDownloadPathForContentAndScripts] stringByAppendingPathComponent: @"HQ Launcher.app"];
task.launchPath = [newLauncher stringByAppendingString:@"/Contents/Resources/updater"];
task.arguments = @[[[NSBundle mainBundle] bundlePath], newLauncher];
[task launch];
// Older versions of Launcher put updater in `/Contents/Resources/updater`.
for (NSString *bundlePath in @[@"/Contents/MacOS/updater",
@"/Contents/Resources/updater",
]) {
NSTask* task = [[NSTask alloc] init];
task.launchPath = [newLauncher stringByAppendingString: bundlePath];
task.arguments = @[[[NSBundle mainBundle] bundlePath], newLauncher];
NSLog(@"launching updater: %@ %@", task.launchPath, task.arguments);
@try {
[task launch];
}
@catch (NSException *e) {
NSLog(@"couldn't launch updater: %@, %@", e.name, e.reason);
exception = e;
continue;
}
launched = true;
break;
}
if (!launched) {
@throw exception;
}
[NSApp terminate:self];
}

View file

@ -0,0 +1,148 @@
//
// AnimBlendDirectional.cpp
//
// Created by Anthony J. Thibault on Augest 30 2019.
// Copyright (c) 2019 High Fidelity, Inc. All rights reserved.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#include "AnimBlendDirectional.h"
#include "GLMHelpers.h"
#include "AnimationLogging.h"
#include "AnimUtil.h"
AnimBlendDirectional::AnimBlendDirectional(const QString& id, glm::vec3 alpha, const QString& centerId,
const QString& upId, const QString& downId, const QString& leftId, const QString& rightId,
const QString& upLeftId, const QString& upRightId, const QString& downLeftId, const QString& downRightId) :
AnimNode(AnimNode::Type::BlendDirectional, id),
_alpha(alpha),
_centerId(centerId),
_upId(upId),
_downId(downId),
_leftId(leftId),
_rightId(rightId),
_upLeftId(upLeftId),
_upRightId(upRightId),
_downLeftId(downLeftId),
_downRightId(downRightId) {
}
AnimBlendDirectional::~AnimBlendDirectional() {
}
const AnimPoseVec& AnimBlendDirectional::evaluate(const AnimVariantMap& animVars, const AnimContext& context, float dt, AnimVariantMap& triggersOut) {
// lookupRaw don't transform the vector.
_alpha = animVars.lookupRaw(_alphaVar, _alpha);
float parentDebugAlpha = context.getDebugAlpha(_id);
if (_children.size() == 9) {
// try to keep the order the same as quadrants, for _childIndices.
// +---+---+
// | 1 | 0 |
// +---+---+
// | 2 | 3 |
// +---+---+
std::array<int, 4> indices;
glm::vec2 alpha = _alpha;
if (_alpha.x > 0.0f) {
if (_alpha.y > 0.0f) {
// quadrant 0
indices = {{_childIndices[0][2], _childIndices[0][1], _childIndices[1][1], _childIndices[1][2]}};
} else {
// quadrant 3
indices = {{_childIndices[1][2], _childIndices[1][1], _childIndices[2][1], _childIndices[2][2]}};
// shift alpha up so both alpha.x and alpha.y are in the (0, 1) range.
alpha.y += 1.0f;
}
} else {
if (_alpha.y > 0.0f) {
// quadrant 1
indices = {{_childIndices[0][1], _childIndices[0][0], _childIndices[1][0], _childIndices[1][1]}};
// shift alpha right so both alpha.x and alpha.y are in the (0, 1) range.
alpha.x += 1.0f;
} else {
// quadrant 2
indices = {{_childIndices[1][1], _childIndices[1][0], _childIndices[2][0], _childIndices[2][1]}};
// shift alpha up and right so both alpha.x and alpha.y are in the (0, 1) range.
alpha.x += 1.0f;
alpha.y += 1.0f;
}
}
std::array<float, 4> alphas = {{
alpha.x * alpha.y,
(1.0f - alpha.x) * alpha.y,
(1.0f - alpha.x) * (1.0f - alpha.y),
alpha.x * (1.0f - alpha.y)
}};
// evaluate children
std::array<AnimPoseVec, 4> poseVecs;
for (int i = 0; i < 4; i++) {
poseVecs[i] = _children[indices[i]]->evaluate(animVars, context, dt, triggersOut);
}
// blend children
size_t minSize = INT_MAX;
for (int i = 0; i < 4; i++) {
if (poseVecs[i].size() < minSize) {
minSize = poseVecs[i].size();
}
}
_poses.resize(minSize);
blend4(minSize, &poseVecs[0][0], &poseVecs[1][0], &poseVecs[2][0], &poseVecs[3][0], &alphas[0], &_poses[0]);
// animation stack debug stats
for (int i = 0; i < 9; i++) {
context.setDebugAlpha(_children[i]->getID(), 0.0f, _children[i]->getType());
}
for (int i = 0; i < 4; i++) {
context.setDebugAlpha(_children[indices[i]]->getID(), alphas[i] * parentDebugAlpha, _children[indices[i]]->getType());
}
} else {
for (auto&& pose : _poses) {
pose = AnimPose::identity;
}
}
return _poses;
}
// for AnimDebugDraw rendering
const AnimPoseVec& AnimBlendDirectional::getPosesInternal() const {
return _poses;
}
bool AnimBlendDirectional::lookupChildIds() {
_childIndices[0][0] = findChildIndexByName(_upLeftId);
_childIndices[0][1] = findChildIndexByName(_upId);
_childIndices[0][2] = findChildIndexByName(_upRightId);
_childIndices[1][0] = findChildIndexByName(_leftId);
_childIndices[1][1] = findChildIndexByName(_centerId);
_childIndices[1][2] = findChildIndexByName(_rightId);
_childIndices[2][0] = findChildIndexByName(_downLeftId);
_childIndices[2][1] = findChildIndexByName(_downId);
_childIndices[2][2] = findChildIndexByName(_downRightId);
// manditory children
// TODO: currently all are manditory.
for (int i = 0; i < 3; i++) {
for (int j = 0; j < 3; j++) {
if (_childIndices[i][j] == -1) {
qDebug(animation) << "Error in AnimBlendDirectional::lookupChildIds() could not find child[" << i << "," << j << "]";
return false;
}
}
}
return true;
}

View file

@ -0,0 +1,59 @@
//
// AnimBlendDirectonal.h
//
// Created by Anthony J. Thibault on Augest 30 2019.
// Copyright (c) 2019 High Fidelity, Inc. All rights reserved.
//
// 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_AnimBlendDirectional_h
#define hifi_AnimBlendDirectional_h
#include "AnimNode.h"
// blend between up to nine AnimNodes.
class AnimBlendDirectional : public AnimNode {
public:
friend class AnimTests;
AnimBlendDirectional(const QString& id, glm::vec3 alpha, const QString& centerId,
const QString& upId, const QString& downId, const QString& leftId, const QString& rightId,
const QString& upLeftId, const QString& upRightId, const QString& downLeftId, const QString& downRightId);
virtual ~AnimBlendDirectional() override;
virtual const AnimPoseVec& evaluate(const AnimVariantMap& animVars, const AnimContext& context, float dt, AnimVariantMap& triggersOut) override;
void setAlphaVar(const QString& alphaVar) { _alphaVar = alphaVar; }
bool lookupChildIds();
protected:
// for AnimDebugDraw rendering
virtual const AnimPoseVec& getPosesInternal() const override;
AnimPoseVec _poses;
glm::vec3 _alpha;
QString _centerId;
QString _upId;
QString _downId;
QString _leftId;
QString _rightId;
QString _upLeftId;
QString _upRightId;
QString _downLeftId;
QString _downRightId;
QString _alphaVar;
int _childIndices[3][3];
// no copies
AnimBlendDirectional(const AnimBlendDirectional&) = delete;
AnimBlendDirectional& operator=(const AnimBlendDirectional&) = delete;
};
#endif // hifi_AnimBlendDirectional_h

View file

@ -110,11 +110,9 @@ void AnimBlendLinear::evaluateAndBlendChildren(const AnimVariantMap& animVars, c
// copy translation and scale from nextPoses
AnimPose pose = nextPoses[i];
int parentIndex = _skeleton->getParentIndex((int)i);
if (parentIndex >= 0) {
// but transform nextPoses rot into absPrev parent frame.
pose.rot() = glm::inverse(absPrev[parentIndex].rot()) * pose.rot() * absPrev[parentIndex].rot();
}
// convert from a rotation that happens in the absolute space of the joint
// into a rotation that happens in the relative space of the joint.
pose.rot() = glm::inverse(absPrev[i].rot()) * pose.rot() * absPrev[i].rot();
relOffsetPoses.push_back(pose);
}

View file

@ -56,7 +56,7 @@ static void bakeRelativeDeltaAnim(std::vector<AnimPoseVec>& anim, const AnimPose
// for each joint in animPoses
for (size_t i = 0; i < animPoses.size(); ++i) {
// convert this relative AnimPose into a delta animation.
animPoses[i] = animPoses[i] * invBasePoses[i];
animPoses[i] = invBasePoses[i] * animPoses[i];
}
}
}
@ -80,13 +80,11 @@ void bakeAbsoluteDeltaAnim(std::vector<AnimPoseVec>& anim, const AnimPoseVec& ba
for (size_t i = 0; i < animPoses.size(); ++i) {
// scale and translation are relative frame
animPoses[i] = animPoses[i] * invBasePoses[i];
animPoses[i] = invBasePoses[i] * animPoses[i];
// but transform the rotation delta into the absolute frame.
int parentIndex = skeleton->getParentIndex((int)i);
if (parentIndex >= 0) {
animPoses[i].rot() = absBasePoses[parentIndex].rot() * animPoses[i].rot() * glm::inverse(absBasePoses[parentIndex].rot());
}
// convert from a rotation that happens in the relative space of the joint
// into a rotation that happens in the absolute space of the joint.
animPoses[i].rot() = absBasePoses[i].rot() * animPoses[i].rot() * glm::inverse(absBasePoses[i].rot());
}
}
}

View file

@ -31,6 +31,7 @@ enum class AnimNodeType {
TwoBoneIK,
SplineIK,
PoleVectorConstraint,
BlendDirectional,
NumTypes
};

View file

@ -99,6 +99,15 @@ public:
return result;
}
int findChildIndexByName(const QString& id) {
for (size_t i = 0; i < _children.size(); ++i) {
if (_children[i]->getID() == id) {
return (int)i;
}
}
return -1;
}
const AnimPoseVec& getPoses() const { return getPosesInternal(); }
protected:

View file

@ -29,6 +29,7 @@
#include "AnimTwoBoneIK.h"
#include "AnimSplineIK.h"
#include "AnimPoleVectorConstraint.h"
#include "AnimBlendDirectional.h"
#include "AnimUtil.h"
using NodeLoaderFunc = AnimNode::Pointer (*)(const QJsonObject& jsonObj, const QString& id, const QUrl& jsonUrl);
@ -47,6 +48,7 @@ static AnimNode::Pointer loadDefaultPoseNode(const QJsonObject& jsonObj, const Q
static AnimNode::Pointer loadTwoBoneIKNode(const QJsonObject& jsonObj, const QString& id, const QUrl& jsonUrl);
static AnimNode::Pointer loadSplineIKNode(const QJsonObject& jsonObj, const QString& id, const QUrl& jsonUrl);
static AnimNode::Pointer loadPoleVectorConstraintNode(const QJsonObject& jsonObj, const QString& id, const QUrl& jsonUrl);
static AnimNode::Pointer loadBlendDirectionalNode(const QJsonObject& jsonObj, const QString& id, const QUrl& jsonUrl);
static const float ANIM_GRAPH_LOAD_PRIORITY = 10.0f;
@ -55,6 +57,7 @@ static const float ANIM_GRAPH_LOAD_PRIORITY = 10.0f;
static bool processDoNothing(AnimNode::Pointer node, const QJsonObject& jsonObj, const QString& id, const QUrl& jsonUrl) { return true; }
bool processStateMachineNode(AnimNode::Pointer node, const QJsonObject& jsonObj, const QString& id, const QUrl& jsonUrl);
bool processRandomSwitchStateMachineNode(AnimNode::Pointer node, const QJsonObject& jsonObj, const QString& id, const QUrl& jsonUrl);
bool processBlendDirectionalNode(AnimNode::Pointer node, const QJsonObject& jsonObj, const QString& id, const QUrl& jsonUrl);
static const char* animNodeTypeToString(AnimNode::Type type) {
switch (type) {
@ -70,6 +73,7 @@ static const char* animNodeTypeToString(AnimNode::Type type) {
case AnimNode::Type::TwoBoneIK: return "twoBoneIK";
case AnimNode::Type::SplineIK: return "splineIK";
case AnimNode::Type::PoleVectorConstraint: return "poleVectorConstraint";
case AnimNode::Type::BlendDirectional: return "blendDirectional";
case AnimNode::Type::NumTypes: return nullptr;
};
return nullptr;
@ -211,6 +215,7 @@ static NodeLoaderFunc animNodeTypeToLoaderFunc(AnimNode::Type type) {
case AnimNode::Type::TwoBoneIK: return loadTwoBoneIKNode;
case AnimNode::Type::SplineIK: return loadSplineIKNode;
case AnimNode::Type::PoleVectorConstraint: return loadPoleVectorConstraintNode;
case AnimNode::Type::BlendDirectional: return loadBlendDirectionalNode;
case AnimNode::Type::NumTypes: return nullptr;
};
return nullptr;
@ -230,6 +235,7 @@ static NodeProcessFunc animNodeTypeToProcessFunc(AnimNode::Type type) {
case AnimNode::Type::TwoBoneIK: return processDoNothing;
case AnimNode::Type::SplineIK: return processDoNothing;
case AnimNode::Type::PoleVectorConstraint: return processDoNothing;
case AnimNode::Type::BlendDirectional: return processBlendDirectionalNode;
case AnimNode::Type::NumTypes: return nullptr;
};
return nullptr;
@ -402,7 +408,6 @@ static AnimNode::Pointer loadClipNode(const QJsonObject& jsonObj, const QString&
auto tempUrl = QUrl(url);
tempUrl = jsonUrl.resolved(tempUrl);
// AJT:
AnimBlendType blendTypeEnum = AnimBlendType_Normal; // default value
if (!blendType.isEmpty()) {
blendTypeEnum = stringToAnimBlendType(blendType);
@ -772,6 +777,32 @@ static AnimNode::Pointer loadPoleVectorConstraintNode(const QJsonObject& jsonObj
return node;
}
static AnimNode::Pointer loadBlendDirectionalNode(const QJsonObject& jsonObj, const QString& id, const QUrl& jsonUrl) {
READ_VEC3(alpha, jsonObj, id, jsonUrl, nullptr);
READ_OPTIONAL_STRING(alphaVar, jsonObj);
READ_OPTIONAL_STRING(centerId, jsonObj);
READ_OPTIONAL_STRING(upId, jsonObj);
READ_OPTIONAL_STRING(downId, jsonObj);
READ_OPTIONAL_STRING(leftId, jsonObj);
READ_OPTIONAL_STRING(rightId, jsonObj);
READ_OPTIONAL_STRING(upLeftId, jsonObj);
READ_OPTIONAL_STRING(upRightId, jsonObj);
READ_OPTIONAL_STRING(downLeftId, jsonObj);
READ_OPTIONAL_STRING(downRightId, jsonObj);
auto node = std::make_shared<AnimBlendDirectional>(id, alpha, centerId,
upId, downId, leftId, rightId,
upLeftId, upRightId, downLeftId, downRightId);
if (!alphaVar.isEmpty()) {
node->setAlphaVar(alphaVar);
}
return node;
}
void buildChildMap(std::map<QString, int>& map, AnimNode::Pointer node) {
for (int i = 0; i < (int)node->getChildCount(); ++i) {
map.insert(std::pair<QString, int>(node->getChild(i)->getID(), i));
@ -1042,7 +1073,12 @@ bool processRandomSwitchStateMachineNode(AnimNode::Pointer node, const QJsonObje
return true;
}
bool processBlendDirectionalNode(AnimNode::Pointer node, const QJsonObject& jsonObj, const QString& nodeId, const QUrl& jsonUrl) {
auto blendNode = std::static_pointer_cast<AnimBlendDirectional>(node);
assert(blendNode);
return blendNode->lookupChildIds();
}
AnimNodeLoader::AnimNodeLoader(const QUrl& url) :
_url(url)

View file

@ -26,6 +26,31 @@ void blend(size_t numPoses, const AnimPose* a, const AnimPose* b, float alpha, A
}
}
void blend3(size_t numPoses, const AnimPose* a, const AnimPose* b, const AnimPose* c, float* alphas, AnimPose* result) {
for (size_t i = 0; i < numPoses; i++) {
const AnimPose& aPose = a[i];
const AnimPose& bPose = b[i];
const AnimPose& cPose = c[i];
result[i].scale() = alphas[0] * aPose.scale() + alphas[1] * bPose.scale() + alphas[2] * cPose.scale();
result[i].rot() = safeLinearCombine3(aPose.rot(), bPose.rot(), cPose.rot(), alphas);
result[i].trans() = alphas[0] * aPose.trans() + alphas[1] * bPose.trans() + alphas[2] * cPose.trans();
}
}
void blend4(size_t numPoses, const AnimPose* a, const AnimPose* b, const AnimPose* c, const AnimPose* d, float* alphas, AnimPose* result) {
for (size_t i = 0; i < numPoses; i++) {
const AnimPose& aPose = a[i];
const AnimPose& bPose = b[i];
const AnimPose& cPose = c[i];
const AnimPose& dPose = d[i];
result[i].scale() = alphas[0] * aPose.scale() + alphas[1] * bPose.scale() + alphas[2] * cPose.scale() + alphas[3] * dPose.scale();
result[i].rot() = safeLinearCombine4(aPose.rot(), bPose.rot(), cPose.rot(), dPose.rot(), alphas);
result[i].trans() = alphas[0] * aPose.trans() + alphas[1] * bPose.trans() + alphas[2] * cPose.trans() + alphas[3] * dPose.trans();
}
}
// additive blend
void blendAdd(size_t numPoses, const AnimPose* a, const AnimPose* b, float alpha, AnimPose* result) {
@ -43,8 +68,7 @@ void blendAdd(size_t numPoses, const AnimPose* a, const AnimPose* b, float alpha
delta = -delta;
}
delta = glm::lerp(identity, delta, alpha);
result[i].rot() = glm::normalize(delta * aPose.rot());
result[i].rot() = glm::normalize(aPose.rot() * delta);
result[i].trans() = aPose.trans() + (alpha * bPose.trans());
}
}

View file

@ -16,6 +16,12 @@
// this is where the magic happens
void blend(size_t numPoses, const AnimPose* a, const AnimPose* b, float alpha, AnimPose* result);
// blend between three sets of poses
void blend3(size_t numPoses, const AnimPose* a, const AnimPose* b, const AnimPose* c, float* alphas, AnimPose* result);
// blend between four sets of poses
void blend4(size_t numPoses, const AnimPose* a, const AnimPose* b, const AnimPose* c, const AnimPose* d, float* alphas, AnimPose* result);
// additive blending
void blendAdd(size_t numPoses, const AnimPose* a, const AnimPose* b, float alpha, AnimPose* result);
@ -34,6 +40,41 @@ inline glm::quat safeLerp(const glm::quat& a, const glm::quat& b, float alpha) {
return glm::normalize(glm::lerp(a, bTemp, alpha));
}
inline glm::quat safeLinearCombine3(const glm::quat& a, const glm::quat& b, const glm::quat& c, float* alphas) {
// adjust signs for b & c if necessary
glm::quat bTemp = b;
float dot = glm::dot(a, bTemp);
if (dot < 0.0f) {
bTemp = -bTemp;
}
glm::quat cTemp = c;
dot = glm::dot(a, cTemp);
if (dot < 0.0f) {
cTemp = -cTemp;
}
return glm::normalize(alphas[0] * a + alphas[1] * bTemp + alphas[2] * cTemp);
}
inline glm::quat safeLinearCombine4(const glm::quat& a, const glm::quat& b, const glm::quat& c, const glm::quat& d, float* alphas) {
// adjust signs for b, c & d if necessary
glm::quat bTemp = b;
float dot = glm::dot(a, bTemp);
if (dot < 0.0f) {
bTemp = -bTemp;
}
glm::quat cTemp = c;
dot = glm::dot(a, cTemp);
if (dot < 0.0f) {
cTemp = -cTemp;
}
glm::quat dTemp = d;
dot = glm::dot(a, dTemp);
if (dot < 0.0f) {
dTemp = -dTemp;
}
return glm::normalize(alphas[0] * a + alphas[1] * bTemp + alphas[2] * cTemp + alphas[3] * dTemp);
}
AnimPose boneLookAt(const glm::vec3& target, const AnimPose& bone);
// This will attempt to determine the proper body facing of a characters body

View file

@ -141,32 +141,26 @@ std::map<QString, QString> AnimVariantMap::toDebugMap() const {
break;
case AnimVariant::Type::Vec3: {
// To prevent filling up debug stats, don't show vec3 values
/*
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: {
// To prevent filling up the anim stats, don't show quat values
/*
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:
// To prevent filling up anim stats, don't show string values
/*
result[pair.first] = pair.second.getString();
*/
break;
default:
// invalid AnimVariant::Type

View file

@ -171,6 +171,8 @@ void ClientTraitsHandler::processTraitOverride(QSharedPointer<ReceivedMessage> m
return;
}
// AJT: DON'T CHECK THIS IN, disable model URL overrides.
/*
// only accept an override if this is for a trait type we override
// and the version matches what we last sent for skeleton
if (traitType == AvatarTraits::SkeletonModelURL
@ -192,6 +194,7 @@ void ClientTraitsHandler::processTraitOverride(QSharedPointer<ReceivedMessage> m
} else {
message->seek(message->getPosition() + traitBinarySize);
}
*/
}
}
}

View file

@ -111,7 +111,7 @@ vec3 fetchNormalMap(vec2 uv) {
// unpack normal, swizzle to get into hifi tangent space with Y axis pointing out
vec2 t = 2.0 * (texture(normalMap, uv, TAA_TEXTURE_LOD_BIAS).rg - vec2(0.5, 0.5));
vec2 t2 = t*t;
return vec3(t.x, sqrt(1.0 - t2.x - t2.y), t.y);
return vec3(t.x, sqrt(max(0.0, 1.0 - t2.x - t2.y)), t.y);
}
<@endif@>

View file

@ -291,6 +291,10 @@ void OffscreenSurface::setMaxFps(uint8_t maxFps) {
}
void OffscreenSurface::load(const QUrl& qmlSource, QQuickItem* parent, const QJSValue& callback) {
loadFromQml(qmlSource, parent, callback);
}
void OffscreenSurface::loadFromQml(const QUrl& qmlSource, QQuickItem* parent, const QJSValue& callback) {
loadInternal(qmlSource, false, parent, [callback](QQmlContext* context, QQuickItem* newItem) {
QJSValue(callback).call(QJSValueList() << context->engine()->newQObject(newItem));
});

View file

@ -111,6 +111,7 @@ signals:
void rootItemCreated(QQuickItem* rootContext);
protected:
virtual void loadFromQml(const QUrl& qmlSource, QQuickItem* parent, const QJSValue& callback);
bool eventFilter(QObject* originalDestination, QEvent* event) override;
bool filterEnabled(QObject* originalDestination, QEvent* event) const;

View file

@ -38,6 +38,7 @@
#include <QtScriptTools/QScriptEngineDebugger>
#include <shared/LocalFileAccessGate.h>
#include <shared/QtHelpers.h>
#include <AudioConstants.h>
#include <AudioEffectOptions.h>
@ -1123,6 +1124,12 @@ QScriptValue ScriptEngine::evaluate(const QString& sourceCode, const QString& fi
}
void ScriptEngine::run() {
if (QThread::currentThread() != qApp->thread() && _context == Context::CLIENT_SCRIPT) {
// Flag that we're allowed to access local HTML files on UI created from C++ calls on this thread
// (because we're a client script)
hifi::scripting::setLocalAccessSafeThread(true);
}
auto filenameParts = _fileNameString.split("/");
auto name = filenameParts.size() > 0 ? filenameParts[filenameParts.size() - 1] : "unknown";
PROFILE_SET_THREAD_NAME("Script: " + name);
@ -1300,6 +1307,9 @@ void ScriptEngine::run() {
emit finished(_fileNameString, qSharedPointerCast<ScriptEngine>(sharedFromThis()));
// Don't leave our local-file-access flag laying around, reset it to false when the scriptengine
// thread is finished
hifi::scripting::setLocalAccessSafeThread(false);
_isRunning = false;
emit runningStateChanged();
emit doneRunning();

View file

@ -0,0 +1,21 @@
//
// Created by Bradley Austin Davis on 2019/09/05
// Copyright 2013-2019 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 "LocalFileAccessGate.h"
#include <QtCore/QThreadStorage>
static QThreadStorage<bool> localAccessSafe;
void hifi::scripting::setLocalAccessSafeThread(bool safe) {
localAccessSafe.setLocalData(safe);
}
bool hifi::scripting::isLocalAccessSafeThread() {
return localAccessSafe.hasLocalData() && localAccessSafe.localData();
}

View file

@ -0,0 +1,21 @@
//
// Created by Bradley Austin Davis on 2019/09/05
// Copyright 2013-2019 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
//
#pragma once
#ifndef hifi_LocalFileAccessGate_h
#define hifi_LocalFileAccessGate_h
namespace hifi { namespace scripting {
void setLocalAccessSafeThread(bool safe = true);
bool isLocalAccessSafeThread();
}} // namespace hifi::scripting
#endif

View file

@ -42,6 +42,7 @@
#include <NetworkAccessManager.h>
#include <GLMHelpers.h>
#include <AudioClient.h>
#include <shared/LocalFileAccessGate.h>
#include <gl/OffscreenGLCanvas.h>
#include <gl/GLHelpers.h>
@ -814,4 +815,24 @@ void OffscreenQmlSurface::forceQmlAudioOutputDeviceUpdate() {
#endif
}
void OffscreenQmlSurface::loadFromQml(const QUrl& qmlSource, QQuickItem* parent, const QJSValue& callback) {
auto objectCallback = [callback](QQmlContext* context, QQuickItem* newItem) {
QJSValue(callback).call(QJSValueList() << context->engine()->newQObject(newItem));
};
if (hifi::scripting::isLocalAccessSafeThread()) {
// If this is a
auto contextCallback = [callback](QQmlContext* context) {
ContextAwareProfile::restrictContext(context, false);
#if !defined(Q_OS_ANDROID)
FileTypeProfile::registerWithContext(context);
HFWebEngineProfile::registerWithContext(context);
#endif
};
loadInternal(qmlSource, true, parent, objectCallback, contextCallback);
} else {
loadInternal(qmlSource, false, parent, objectCallback);
}
}
#include "OffscreenQmlSurface.moc"

View file

@ -37,8 +37,8 @@ public:
Q_INVOKABLE void lowerKeyboard();
PointerEvent::EventType choosePointerEventType(QEvent::Type type);
Q_INVOKABLE unsigned int deviceIdByTouchPoint(qreal x, qreal y);
signals:
void focusObjectChanged(QObject* newFocus);
void focusTextChanged(bool focusText);
@ -61,6 +61,7 @@ public slots:
void sendToQml(const QVariant& message);
protected:
void loadFromQml(const QUrl& qmlSource, QQuickItem* parent, const QJSValue& callback) override;
void clearFocusItem();
void setFocusText(bool newFocusText);
void initializeEngine(QQmlEngine* engine) override;

View file

@ -12,6 +12,7 @@
#include <QtQml/QQmlProperty>
#include <shared/QtHelpers.h>
#include <shared/LocalFileAccessGate.h>
#include <PathUtils.h>
#include <DependencyManager.h>
#include <AccountManager.h>
@ -635,13 +636,24 @@ void TabletProxy::returnToPreviousApp() {
qCDebug(uiLogging) << "tablet cannot load QML because _qmlTabletRoot is null";
}
}
void TabletProxy::loadQMLSource(const QVariant& path, bool resizable) {
// Capture whether the current script thread is allowed to load local HTML content,
// pass the information along to the real function
bool localSafeContext = hifi::scripting::isLocalAccessSafeThread();
if (QThread::currentThread() != thread()) {
QMetaObject::invokeMethod(this, "loadQMLSource", Q_ARG(QVariant, path), Q_ARG(bool, resizable));
QMetaObject::invokeMethod(this, "loadQMLSourceImpl", Q_ARG(QVariant, path), Q_ARG(bool, resizable), Q_ARG(bool, localSafeContext));
return;
}
loadQMLSourceImpl(path, resizable, localSafeContext);
}
void TabletProxy::loadQMLSourceImpl(const QVariant& path, bool resizable, bool localSafeContext) {
if (QThread::currentThread() != thread()) {
qCWarning(uiLogging) << __FUNCTION__ << "may not be called directly by scripts";
return;
}
QObject* root = nullptr;
if (!_toolbarMode && _qmlTabletRoot) {
root = _qmlTabletRoot;
@ -650,7 +662,14 @@ void TabletProxy::loadQMLSource(const QVariant& path, bool resizable) {
}
if (root) {
// BUGZ-1398: tablet access to local HTML files from client scripts
// Here we TEMPORARILY mark the main thread as allowed to load local file content,
// because the thread that originally made the call is so marked.
if (localSafeContext) {
hifi::scripting::setLocalAccessSafeThread(true);
}
QMetaObject::invokeMethod(root, "loadSource", Q_ARG(const QVariant&, path));
hifi::scripting::setLocalAccessSafeThread(false);
_state = State::QML;
_currentPathLoaded = path;
QMetaObject::invokeMethod(root, "setShown", Q_ARG(const QVariant&, QVariant(true)));

View file

@ -291,6 +291,13 @@ public:
* to have it not resizable.
*/
Q_INVOKABLE void loadQMLSource(const QVariant& path, bool resizable = false);
/**jsdoc
* Internal function, do not call from scripts
* @function TabletProxy#loadQMLSourceImpl
*/
Q_INVOKABLE void loadQMLSourceImpl(const QVariant& path, bool resizable, bool localSafeContext);
// FIXME: This currently relies on a script initializing the tablet (hence the bool denoting success);
// it should be initialized internally so it cannot fail

View file

@ -79,7 +79,6 @@ function pruneOldAvimojis() {
});
}
function maybeClearTimeoutDelete() {
if (defaultTimeout) {
Script.clearTimeout(defaultTimeout);
@ -157,6 +156,7 @@ function handleSelectedEmoji(emojiFilename) {
}
}
function onDomainChanged() {
resetEmojis();
}
@ -167,6 +167,14 @@ function onScaleChanged() {
}
function onAddingWearable(id) {
var props = Entities.getEntityProperties(id, ["name"]);
if (props.name.toLowerCase().indexOf("avimoji") > -1) {
Entities.deleteEntity(id);
}
}
// #endregion
// *************************************
// END ui_handlers
@ -179,7 +187,13 @@ function onScaleChanged() {
// what happens when we need to add an emoji over a user
var firstEmojiMadeOnStartup = false;
function addEmoji(emojiFilename) {
if (!firstEmojiMadeOnStartup) {
firstEmojiMadeOnStartup = true;
Entities.addingWearable.disconnect(onAddingWearable);
}
if (currentEmoji) {
resetEmojis();
}
@ -418,6 +432,7 @@ function startup() {
Script.scriptEnding.connect(unload);
Window.domainChanged.connect(onDomainChanged);
MyAvatar.scaleChanged.connect(onScaleChanged);
Entities.addingWearable.connect(onAddingWearable);
signalsConnected = true;
function AviMoji() {
@ -457,6 +472,10 @@ function unload() {
if (signalsConnected) {
Window.domainChanged.disconnect(onDomainChanged);
MyAvatar.scaleChanged.disconnect(onScaleChanged);
if (!firstEmojiMadeOnStartup) {
Entities.addingWearable.disconnect(onAddingWearable);
}
signalsConnected = false;
}
}

View file

@ -543,7 +543,7 @@ function onDisplayModeChanged(isHMDMode) {
}
var emojiAPI = Script.require("./emojiApp/simplifiedEmoji.js");
var emojiAPI = Script.require("./emojiApp/simplifiedEmoji.js?" + Date.now());
var keyPressSignalsConnected = false;
var emojiCodeMap;
var customEmojiCodeMap;

View file

@ -584,7 +584,7 @@ function restoreLODSettings() {
var nametag = Script.require("./simplifiedNametag/simplifiedNametag.js?" + Date.now());
var si = Script.require("./simplifiedStatusIndicator/simplifiedStatusIndicator.js?" + Date.now())
var si = Script.require("./simplifiedStatusIndicator/simplifiedStatusIndicator.js?" + Date.now());
var emote = Script.require("../simplifiedEmote/simplifiedEmote.js?" + Date.now());
var oldShowAudioTools;
var oldShowBubbleTools;