Merge branch 'master' of github.com:highfidelity/hifi into config-pucks

This commit is contained in:
Dante Ruiz 2017-05-02 21:34:27 +01:00
commit cf2507cc63
45 changed files with 815 additions and 397 deletions

View file

@ -23,11 +23,13 @@ Item {
property bool keyboardRaised: false property bool keyboardRaised: false
property bool punctuationMode: false property bool punctuationMode: false
property bool isDesktop: false property bool isDesktop: false
property bool removingPage: false property string initialPage: ""
property bool loadingPage: false property bool startingUp: true
property alias webView: webview property alias webView: webview
property alias profile: webview.profile property alias profile: webview.profile
property bool remove: false property bool remove: false
property var urlList: []
property var forwardList: []
property int currentPage: -1 // used as a model for repeater property int currentPage: -1 // used as a model for repeater
@ -79,9 +81,12 @@ Item {
id: displayUrl id: displayUrl
color: hifi.colors.baseGray color: hifi.colors.baseGray
font.pixelSize: 12 font.pixelSize: 12
verticalAlignment: Text.AlignLeft
anchors { anchors {
top: nav.bottom top: nav.bottom
horizontalCenter: parent.horizontalCenter; horizontalCenter: parent.horizontalCenter;
left: parent.left
leftMargin: 20
} }
} }
@ -106,19 +111,19 @@ Item {
} }
function goBack() { function goBack() {
if (webview.canGoBack && !isUrlLoaded(webview.url)) { if (webview.canGoBack) {
if (currentPage > 0) { forwardList.push(webview.url);
removingPage = true;
pagesModel.remove(currentPage);
}
webview.goBack(); webview.goBack();
} else if (currentPage > 0) { } else if (web.urlList.length > 0) {
removingPage = true; var url = web.urlList.pop();
pagesModel.remove(currentPage); loadUrl(url);
} else if (web.forwardList.length > 0) {
var url = web.forwardList.pop();
loadUrl(url);
web.forwardList = [];
} }
} }
function closeWebEngine() { function closeWebEngine() {
if (remove) { if (remove) {
web.destroy(); web.destroy();
@ -151,32 +156,42 @@ Item {
view.setEnabled(true); view.setEnabled(true);
} }
function loadUrl(url) {
webview.url = url
web.url = webview.url;
web.address = webview.url;
}
function onInitialPage(url) {
return (url === webview.url);
}
function urlAppend(url) { function urlAppend(url) {
if (removingPage) {
removingPage = false;
return;
}
var lurl = decodeURIComponent(url) var lurl = decodeURIComponent(url)
if (lurl[lurl.length - 1] !== "/") { if (lurl[lurl.length - 1] !== "/") {
lurl = lurl + "/" lurl = lurl + "/"
} }
if (currentPage === -1 || (pagesModel.get(currentPage).webUrl !== lurl && !timer.running)) { web.urlList.push(url);
timer.start(); setBackButtonStatus();
pagesModel.append({webUrl: lurl});
}
} }
onCurrentPageChanged: { function setBackButtonStatus() {
if (currentPage >= 0 && currentPage < pagesModel.count) { if (web.urlList.length > 0 || webview.canGoBack) {
timer.start(); back.enabledColor = hifi.colors.darkGray;
webview.url = pagesModel.get(currentPage).webUrl; back.enabled = true;
web.url = webview.url; } else {
web.address = webview.url; back.enabledColor = hifi.colors.baseGray;
back.enabled = false;
} }
} }
onUrlChanged: { onUrlChanged: {
gotoPage(url) loadUrl(url);
if (startingUp) {
web.initialPage = webview.url;
startingUp = false;
}
} }
QtObject { QtObject {
@ -184,18 +199,7 @@ Item {
WebChannel.id: "eventBridgeWrapper" WebChannel.id: "eventBridgeWrapper"
property var eventBridge; property var eventBridge;
} }
Timer {
id: timer
interval: 200
running: false
repeat: false
onTriggered: timer.stop();
}
WebEngineView { WebEngineView {
id: webview id: webview
objectName: "webEngineView" objectName: "webEngineView"
@ -235,6 +239,7 @@ Item {
worldId: WebEngineScript.MainWorld worldId: WebEngineScript.MainWorld
} }
property string urlTag: "noDownload=false";
userScripts: [ createGlobalEventBridge, raiseAndLowerKeyboard, userScript ] userScripts: [ createGlobalEventBridge, raiseAndLowerKeyboard, userScript ]
property string newUrl: "" property string newUrl: ""
@ -261,9 +266,7 @@ Item {
keyboard.resetShiftMode(false); keyboard.resetShiftMode(false);
// Required to support clicking on "hifi://" links // Required to support clicking on "hifi://" links
if (WebEngineView.LoadStartedStatus == loadRequest.status) { if (WebEngineView.LoadStartedStatus == loadRequest.status) {
urlAppend(loadRequest.url.toString()); var url = loadRequest.url.toString();
loadingPage = true;
var url = loadRequest.url.toString();
if (urlHandler.canHandleUrl(url)) { if (urlHandler.canHandleUrl(url)) {
if (urlHandler.handleUrl(url)) { if (urlHandler.handleUrl(url)) {
root.stop(); root.stop();
@ -274,9 +277,19 @@ Item {
if (WebEngineView.LoadFailedStatus == loadRequest.status) { if (WebEngineView.LoadFailedStatus == loadRequest.status) {
console.log(" Tablet WebEngineView failed to laod url: " + loadRequest.url.toString()); console.log(" Tablet WebEngineView failed to laod url: " + loadRequest.url.toString());
} }
}
if (WebEngineView.LoadSucceededStatus == loadRequest.status) {
web.address = webview.url;
if (startingUp) {
web.initialPage = webview.url;
startingUp = false;
}
}
}
onNewViewRequested: { onNewViewRequested: {
var currentUrl = webview.url;
urlAppend(currentUrl);
request.openIn(webview); request.openIn(webview);
} }
} }

View file

@ -31,7 +31,7 @@ Item {
property bool drillDownToPlace: false; property bool drillDownToPlace: false;
property bool showPlace: isConcurrency; property bool showPlace: isConcurrency;
property string messageColor: hifi.colors.blueAccent; property string messageColor: isAnnouncement ? "white" : hifi.colors.blueAccent;
property string timePhrase: pastTime(timestamp); property string timePhrase: pastTime(timestamp);
property int onlineUsers: 0; property int onlineUsers: 0;
property bool isConcurrency: action === 'concurrency'; property bool isConcurrency: action === 'concurrency';
@ -71,6 +71,10 @@ Item {
property bool hasGif: imageUrl.indexOf('.gif') === (imageUrl.length - 4); property bool hasGif: imageUrl.indexOf('.gif') === (imageUrl.length - 4);
function pluralize(count, singular, optionalPlural) {
return (count === 1) ? singular : (optionalPlural || (singular + "s"));
}
DropShadow { DropShadow {
visible: isStacked; visible: isStacked;
anchors.fill: shadow1; anchors.fill: shadow1;
@ -115,7 +119,7 @@ Item {
id: lobby; id: lobby;
visible: !hasGif || (animation.status !== Image.Ready); visible: !hasGif || (animation.status !== Image.Ready);
width: parent.width - (isConcurrency ? 0 : (2 * smallMargin)); width: parent.width - (isConcurrency ? 0 : (2 * smallMargin));
height: parent.height - messageHeight - (isConcurrency ? 0 : smallMargin); height: parent.height -(isAnnouncement ? smallMargin : messageHeight) - (isConcurrency ? 0 : smallMargin);
source: thumbnail || defaultThumbnail; source: thumbnail || defaultThumbnail;
fillMode: Image.PreserveAspectCrop; fillMode: Image.PreserveAspectCrop;
anchors { anchors {
@ -160,7 +164,24 @@ Item {
margins: textPadding; margins: textPadding;
} }
} }
Rectangle {
id: lozenge;
visible: isAnnouncement;
color: hifi.colors.redHighlight;
anchors.fill: infoRow;
radius: lozenge.height / 2.0;
border.width: lozengeHot.containsMouse ? 4 : 0;
border.color: "white";
}
Row { Row {
id: infoRow;
Image {
id: icon;
source: isAnnouncement ? "../../images/Announce-Blast.svg" : "../../images/snap-icon.svg";
width: 40;
height: 40;
visible: ((action === 'snapshot') || isAnnouncement) && (messageHeight >= 40);
}
FiraSansRegular { FiraSansRegular {
id: users; id: users;
visible: isConcurrency || isAnnouncement; visible: isConcurrency || isAnnouncement;
@ -169,34 +190,42 @@ Item {
color: messageColor; color: messageColor;
anchors.verticalCenter: message.verticalCenter; anchors.verticalCenter: message.verticalCenter;
} }
Image {
id: icon;
source: "../../images/snap-icon.svg"
width: 40;
height: 40;
visible: (action === 'snapshot') && (messageHeight >= 40);
}
RalewayRegular { RalewayRegular {
id: message; id: message;
text: isConcurrency ? ((onlineUsers === 1) ? "person" : "people") : (isAnnouncement ? "connections" : (drillDownToPlace ? "snapshots" : ("by " + userName))); visible: !isAnnouncement;
text: isConcurrency ? pluralize(onlineUsers, "person", "people") : (drillDownToPlace ? "snapshots" : ("by " + userName));
size: textSizeSmall; size: textSizeSmall;
color: messageColor; color: messageColor;
elide: Text.ElideRight; // requires a width to be specified` elide: Text.ElideRight; // requires a width to be specified`
width: root.width - textPadding width: root.width - textPadding
- (users.visible ? users.width + parent.spacing : 0)
- (icon.visible ? icon.width + parent.spacing : 0) - (icon.visible ? icon.width + parent.spacing : 0)
- (users.visible ? users.width + parent.spacing : 0)
- (actionIcon.width + (2 * smallMargin)); - (actionIcon.width + (2 * smallMargin));
anchors { anchors {
bottom: parent.bottom; bottom: parent.bottom;
bottomMargin: parent.spacing; bottomMargin: parent.spacing;
} }
} }
Column {
visible: isAnnouncement;
RalewayRegular {
text: pluralize(onlineUsers, "connection") + " "; // hack padding
size: textSizeSmall;
color: messageColor;
}
RalewayRegular {
text: pluralize(onlineUsers, "is here now", "are here now");
size: textSizeSmall * 0.7;
color: messageColor;
}
}
spacing: textPadding; spacing: textPadding;
height: messageHeight; height: messageHeight;
anchors { anchors {
bottom: parent.bottom; bottom: parent.bottom;
left: parent.left; left: parent.left;
leftMargin: textPadding; leftMargin: textPadding;
bottomMargin: isAnnouncement ? textPadding : 0;
} }
} }
// These two can be supplied to provide hover behavior. // These two can be supplied to provide hover behavior.
@ -214,6 +243,7 @@ Item {
} }
StateImage { StateImage {
id: actionIcon; id: actionIcon;
visible: !isAnnouncement;
imageURL: "../../images/info-icon-2-state.svg"; imageURL: "../../images/info-icon-2-state.svg";
size: 30; size: 30;
buttonState: messageArea.containsMouse ? 1 : 0; buttonState: messageArea.containsMouse ? 1 : 0;
@ -223,13 +253,25 @@ Item {
margins: smallMargin; margins: smallMargin;
} }
} }
function go() {
goFunction(drillDownToPlace ? ("/places/" + placeName) : ("/user_stories/" + storyId));
}
MouseArea { MouseArea {
id: messageArea; id: messageArea;
visible: !isAnnouncement;
width: parent.width; width: parent.width;
height: messageHeight; height: messageHeight;
anchors.top: lobby.bottom; anchors.top: lobby.bottom;
acceptedButtons: Qt.LeftButton; acceptedButtons: Qt.LeftButton;
onClicked: goFunction(drillDownToPlace ? ("/places/" + placeName) : ("/user_stories/" + storyId)); onClicked: go();
hoverEnabled: true;
}
MouseArea {
id: lozengeHot;
visible: lozenge.visible;
anchors.fill: lozenge;
acceptedButtons: Qt.LeftButton;
onClicked: go();
hoverEnabled: true; hoverEnabled: true;
} }
} }

View file

@ -280,7 +280,6 @@ StackView {
cardHeight: 163 + (2 * 4); cardHeight: 163 + (2 * 4);
metaverseServerUrl: addressBarDialog.metaverseServerUrl; metaverseServerUrl: addressBarDialog.metaverseServerUrl;
labelText: 'HAPPENING NOW'; labelText: 'HAPPENING NOW';
//actions: 'concurrency,snapshot'; // uncomment this line instead of next to produce fake announcement data for testing.
actions: 'announcement'; actions: 'announcement';
filter: addressLine.text; filter: addressLine.text;
goFunction: goCard; goFunction: goCard;

View file

@ -5423,9 +5423,6 @@ void Application::registerScriptEngineWithApplicationServices(ScriptEngine* scri
entityScriptingInterface->setPacketSender(&_entityEditSender); entityScriptingInterface->setPacketSender(&_entityEditSender);
entityScriptingInterface->setEntityTree(getEntities()->getTree()); entityScriptingInterface->setEntityTree(getEntities()->getTree());
// AvatarManager has some custom types
AvatarManager::registerMetaTypes(scriptEngine);
// give the script engine to the RecordingScriptingInterface for its callbacks // give the script engine to the RecordingScriptingInterface for its callbacks
DependencyManager::get<RecordingScriptingInterface>()->setScriptEngine(scriptEngine); DependencyManager::get<RecordingScriptingInterface>()->setScriptEngine(scriptEngine);

View file

@ -13,9 +13,8 @@
#include "Camera.h" #include "Camera.h"
#include <EntityItem.h> #include <EntityTypes.h>
// TODO: come up with a better name than "FancyCamera"
class FancyCamera : public Camera { class FancyCamera : public Camera {
Q_OBJECT Q_OBJECT

View file

@ -495,12 +495,15 @@ Menu::Menu() {
qApp, SLOT(setActiveEyeTracker())); qApp, SLOT(setActiveEyeTracker()));
#endif #endif
addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::AvatarReceiveStats, 0, false, action = addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::AvatarReceiveStats, 0, false);
avatarManager.data(), SLOT(setShouldShowReceiveStats(bool))); connect(action, &QAction::triggered, [this]{ Avatar::setShowReceiveStats(isOptionChecked(MenuOption::AvatarReceiveStats)); });
action = addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::ShowBoundingCollisionShapes, 0, false);
connect(action, &QAction::triggered, [this]{ Avatar::setShowCollisionShapes(isOptionChecked(MenuOption::ShowBoundingCollisionShapes)); });
action = addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::ShowMyLookAtVectors, 0, false);
connect(action, &QAction::triggered, [this]{ Avatar::setShowMyLookAtVectors(isOptionChecked(MenuOption::ShowMyLookAtVectors)); });
action = addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::ShowOtherLookAtVectors, 0, false);
connect(action, &QAction::triggered, [this]{ Avatar::setShowOtherLookAtVectors(isOptionChecked(MenuOption::ShowOtherLookAtVectors)); });
addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::RenderBoundingCollisionShapes);
addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::RenderMyLookAtVectors, 0, false);
addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::RenderOtherLookAtVectors, 0, false);
addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::FixGaze, 0, false); addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::FixGaze, 0, false);
addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::AnimDebugDrawDefaultPose, 0, false, addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::AnimDebugDrawDefaultPose, 0, false,
avatar.get(), SLOT(setEnableDebugDrawDefaultPose(bool))); avatar.get(), SLOT(setEnableDebugDrawDefaultPose(bool)));

View file

@ -145,9 +145,6 @@ namespace MenuOption {
const QString Quit = "Quit"; const QString Quit = "Quit";
const QString ReloadAllScripts = "Reload All Scripts"; const QString ReloadAllScripts = "Reload All Scripts";
const QString ReloadContent = "Reload Content (Clears all caches)"; const QString ReloadContent = "Reload Content (Clears all caches)";
const QString RenderBoundingCollisionShapes = "Show Bounding Collision Shapes";
const QString RenderMyLookAtVectors = "Show My Eye Vectors";
const QString RenderOtherLookAtVectors = "Show Other Eye Vectors";
const QString RenderMaxTextureMemory = "Maximum Texture Memory"; const QString RenderMaxTextureMemory = "Maximum Texture Memory";
const QString RenderMaxTextureAutomatic = "Automatic Texture Memory"; const QString RenderMaxTextureAutomatic = "Automatic Texture Memory";
const QString RenderMaxTexture4MB = "4 MB"; const QString RenderMaxTexture4MB = "4 MB";
@ -173,8 +170,11 @@ namespace MenuOption {
const QString SendWrongDSConnectVersion = "Send wrong DS connect version"; const QString SendWrongDSConnectVersion = "Send wrong DS connect version";
const QString SendWrongProtocolVersion = "Send wrong protocol version"; const QString SendWrongProtocolVersion = "Send wrong protocol version";
const QString SetHomeLocation = "Set Home Location"; const QString SetHomeLocation = "Set Home Location";
const QString ShowDSConnectTable = "Show Domain Connection Timing";
const QString ShowBordersEntityNodes = "Show Entity Nodes"; const QString ShowBordersEntityNodes = "Show Entity Nodes";
const QString ShowBoundingCollisionShapes = "Show Bounding Collision Shapes";
const QString ShowDSConnectTable = "Show Domain Connection Timing";
const QString ShowMyLookAtVectors = "Show My Eye Vectors";
const QString ShowOtherLookAtVectors = "Show Other Eye Vectors";
const QString ShowRealtimeEntityStats = "Show Realtime Entity Stats"; const QString ShowRealtimeEntityStats = "Show Realtime Entity Stats";
const QString StandingHMDSensorMode = "Standing HMD Sensor Mode"; const QString StandingHMDSensorMode = "Standing HMD Sensor Mode";
const QString SimulateEyeTracking = "Simulate"; const QString SimulateEyeTracking = "Simulate";

View file

@ -28,10 +28,8 @@
#include <VariantMapToScriptValue.h> #include <VariantMapToScriptValue.h>
#include <DebugDraw.h> #include <DebugDraw.h>
#include "AvatarManager.h"
#include "AvatarMotionState.h" #include "AvatarMotionState.h"
#include "Camera.h" #include "Camera.h"
#include "Menu.h"
#include "InterfaceLogging.h" #include "InterfaceLogging.h"
#include "SceneScriptingInterface.h" #include "SceneScriptingInterface.h"
#include "SoftAttachmentModel.h" #include "SoftAttachmentModel.h"
@ -59,7 +57,6 @@ namespace render {
auto avatarPtr = static_pointer_cast<Avatar>(avatar); auto avatarPtr = static_pointer_cast<Avatar>(avatar);
if (avatarPtr->isInitialized() && args) { if (avatarPtr->isInitialized() && args) {
PROFILE_RANGE_BATCH(*args->_batch, "renderAvatarPayload"); PROFILE_RANGE_BATCH(*args->_batch, "renderAvatarPayload");
// TODO AVATARS_RENDERER: remove need for qApp
avatarPtr->render(args); avatarPtr->render(args);
} }
} }
@ -74,6 +71,31 @@ namespace render {
} }
} }
static bool showReceiveStats = false;
void Avatar::setShowReceiveStats(bool receiveStats) {
showReceiveStats = receiveStats;
}
static bool showMyLookAtVectors = false;
void Avatar::setShowMyLookAtVectors(bool showMine) {
showMyLookAtVectors = showMine;
}
static bool showOtherLookAtVectors = false;
void Avatar::setShowOtherLookAtVectors(bool showOthers) {
showOtherLookAtVectors = showOthers;
}
static bool showCollisionShapes = false;
void Avatar::setShowCollisionShapes(bool render) {
showCollisionShapes = render;
}
static bool showNamesAboveHeads = false;
void Avatar::setShowNamesAboveHeads(bool show) {
showNamesAboveHeads = show;
}
Avatar::Avatar(QThread* thread, RigPointer rig) : Avatar::Avatar(QThread* thread, RigPointer rig) :
AvatarData(), AvatarData(),
_skeletonOffset(0.0f), _skeletonOffset(0.0f),
@ -126,11 +148,6 @@ Avatar::~Avatar() {
}); });
} }
if (_motionState) {
delete _motionState;
_motionState = nullptr;
}
auto geometryCache = DependencyManager::get<GeometryCache>(); auto geometryCache = DependencyManager::get<GeometryCache>();
if (geometryCache) { if (geometryCache) {
geometryCache->releaseID(_nameRectGeometryID); geometryCache->releaseID(_nameRectGeometryID);
@ -350,7 +367,7 @@ void Avatar::simulate(float deltaTime, bool inView) {
_smoothPositionTimer += deltaTime; _smoothPositionTimer += deltaTime;
if (_smoothPositionTimer < _smoothPositionTime) { if (_smoothPositionTimer < _smoothPositionTime) {
AvatarData::setPosition( AvatarData::setPosition(
lerp(_smoothPositionInitial, lerp(_smoothPositionInitial,
_smoothPositionTarget, _smoothPositionTarget,
easeInOutQuad(glm::clamp(_smoothPositionTimer / _smoothPositionTime, 0.0f, 1.0f))) easeInOutQuad(glm::clamp(_smoothPositionTimer / _smoothPositionTime, 0.0f, 1.0f)))
); );
@ -363,7 +380,7 @@ void Avatar::simulate(float deltaTime, bool inView) {
_smoothOrientationTimer += deltaTime; _smoothOrientationTimer += deltaTime;
if (_smoothOrientationTimer < _smoothOrientationTime) { if (_smoothOrientationTimer < _smoothOrientationTime) {
AvatarData::setOrientation( AvatarData::setOrientation(
slerp(_smoothOrientationInitial, slerp(_smoothOrientationInitial,
_smoothOrientationTarget, _smoothOrientationTarget,
easeInOutQuad(glm::clamp(_smoothOrientationTimer / _smoothOrientationTime, 0.0f, 1.0f))) easeInOutQuad(glm::clamp(_smoothOrientationTimer / _smoothOrientationTime, 0.0f, 1.0f)))
); );
@ -535,14 +552,7 @@ void Avatar::updateRenderItem(render::Transaction& transaction) {
void Avatar::postUpdate(float deltaTime) { void Avatar::postUpdate(float deltaTime) {
bool renderLookAtVectors; if (isMyAvatar() ? showMyLookAtVectors : showOtherLookAtVectors) {
if (isMyAvatar()) {
renderLookAtVectors = Menu::getInstance()->isOptionChecked(MenuOption::RenderMyLookAtVectors);
} else {
renderLookAtVectors = Menu::getInstance()->isOptionChecked(MenuOption::RenderOtherLookAtVectors);
}
if (renderLookAtVectors) {
const float EYE_RAY_LENGTH = 10.0; const float EYE_RAY_LENGTH = 10.0;
const glm::vec4 BLUE(0.0f, 0.0f, 1.0f, 1.0f); const glm::vec4 BLUE(0.0f, 0.0f, 1.0f, 1.0f);
const glm::vec4 RED(1.0f, 0.0f, 0.0f, 1.0f); const glm::vec4 RED(1.0f, 0.0f, 0.0f, 1.0f);
@ -583,7 +593,6 @@ void Avatar::render(RenderArgs* renderArgs) {
bool havePosition, haveRotation; bool havePosition, haveRotation;
if (_handState & LEFT_HAND_POINTING_FLAG) { if (_handState & LEFT_HAND_POINTING_FLAG) {
if (_handState & IS_FINGER_POINTING_FLAG) { if (_handState & IS_FINGER_POINTING_FLAG) {
int leftIndexTip = getJointIndex("LeftHandIndex4"); int leftIndexTip = getJointIndex("LeftHandIndex4");
int leftIndexTipJoint = getJointIndex("LeftHandIndex3"); int leftIndexTipJoint = getJointIndex("LeftHandIndex3");
@ -636,44 +645,25 @@ void Avatar::render(RenderArgs* renderArgs) {
return; return;
} }
glm::vec3 toTarget = frustum.getPosition() - getPosition(); fixupModelsInScene(renderArgs->_scene);
float distanceToTarget = glm::length(toTarget);
{ if (showCollisionShapes && shouldRenderHead(renderArgs) && _skeletonModel->isRenderable()) {
fixupModelsInScene(renderArgs->_scene); PROFILE_RANGE_BATCH(batch, __FUNCTION__":skeletonBoundingCollisionShapes");
const float BOUNDING_SHAPE_ALPHA = 0.7f;
if (renderArgs->_renderMode != RenderArgs::SHADOW_RENDER_MODE) { _skeletonModel->renderBoundingCollisionShapes(*renderArgs->_batch, getUniformScale(), BOUNDING_SHAPE_ALPHA);
// add local lights
const float BASE_LIGHT_DISTANCE = 2.0f;
const float LIGHT_FALLOFF_RADIUS = 0.01f;
const float LIGHT_EXPONENT = 1.0f;
const float LIGHT_CUTOFF = glm::radians(80.0f);
float distance = BASE_LIGHT_DISTANCE * getUniformScale();
glm::vec3 position = _skeletonModel->getTranslation();
glm::quat orientation = getOrientation();
foreach (const AvatarManager::LocalLight& light, DependencyManager::get<AvatarManager>()->getLocalLights()) {
glm::vec3 direction = orientation * light.direction;
DependencyManager::get<DeferredLightingEffect>()->addSpotLight(position - direction * distance,
distance * 2.0f, light.color, 0.5f, LIGHT_FALLOFF_RADIUS, orientation, LIGHT_EXPONENT, LIGHT_CUTOFF);
}
}
bool renderBounding = Menu::getInstance()->isOptionChecked(MenuOption::RenderBoundingCollisionShapes);
if (renderBounding && shouldRenderHead(renderArgs) && _skeletonModel->isRenderable()) {
PROFILE_RANGE_BATCH(batch, __FUNCTION__":skeletonBoundingCollisionShapes");
const float BOUNDING_SHAPE_ALPHA = 0.7f;
_skeletonModel->renderBoundingCollisionShapes(*renderArgs->_batch, getUniformScale(), BOUNDING_SHAPE_ALPHA);
}
} }
const float DISPLAYNAME_DISTANCE = 20.0f; if (showReceiveStats || showNamesAboveHeads) {
setShowDisplayName(distanceToTarget < DISPLAYNAME_DISTANCE); glm::vec3 toTarget = frustum.getPosition() - getPosition();
float distanceToTarget = glm::length(toTarget);
if (!isMyAvatar() || renderArgs->_cameraMode != (int8_t)CAMERA_MODE_FIRST_PERSON) { const float DISPLAYNAME_DISTANCE = 20.0f;
auto& frustum = renderArgs->getViewFrustum(); updateDisplayNameAlpha(distanceToTarget < DISPLAYNAME_DISTANCE);
auto textPosition = getDisplayNamePosition(); if (!isMyAvatar() || renderArgs->_cameraMode != (int8_t)CAMERA_MODE_FIRST_PERSON) {
if (frustum.pointIntersectsFrustum(textPosition)) { auto& frustum = renderArgs->getViewFrustum();
renderDisplayName(batch, frustum, textPosition); auto textPosition = getDisplayNamePosition();
if (frustum.pointIntersectsFrustum(textPosition)) {
renderDisplayName(batch, frustum, textPosition);
}
} }
} }
} }
@ -829,7 +819,7 @@ Transform Avatar::calculateDisplayNameTransform(const ViewFrustum& view, const g
void Avatar::renderDisplayName(gpu::Batch& batch, const ViewFrustum& view, const glm::vec3& textPosition) const { void Avatar::renderDisplayName(gpu::Batch& batch, const ViewFrustum& view, const glm::vec3& textPosition) const {
PROFILE_RANGE_BATCH(batch, __FUNCTION__); PROFILE_RANGE_BATCH(batch, __FUNCTION__);
bool shouldShowReceiveStats = DependencyManager::get<AvatarManager>()->shouldShowReceiveStats() && !isMyAvatar(); bool shouldShowReceiveStats = showReceiveStats && !isMyAvatar();
// If we have nothing to draw, or it's totally transparent, or it's too close or behind the camera, return // If we have nothing to draw, or it's totally transparent, or it's too close or behind the camera, return
static const float CLIP_DISTANCE = 0.2f; static const float CLIP_DISTANCE = 0.2f;
@ -1200,8 +1190,8 @@ int Avatar::parseDataFromBuffer(const QByteArray& buffer) {
const float MOVE_DISTANCE_THRESHOLD = 0.001f; const float MOVE_DISTANCE_THRESHOLD = 0.001f;
_moving = glm::distance(oldPosition, getPosition()) > MOVE_DISTANCE_THRESHOLD; _moving = glm::distance(oldPosition, getPosition()) > MOVE_DISTANCE_THRESHOLD;
if (_moving && _motionState) { if (_moving) {
_motionState->addDirtyFlags(Simulation::DIRTY_POSITION); addPhysicsFlags(Simulation::DIRTY_POSITION);
} }
if (_moving || _hasNewJointData) { if (_moving || _hasNewJointData) {
locationChanged(); locationChanged();
@ -1284,8 +1274,8 @@ float Avatar::getPelvisFloatingHeight() const {
return -_skeletonModel->getBindExtents().minimum.y; return -_skeletonModel->getBindExtents().minimum.y;
} }
void Avatar::setShowDisplayName(bool showDisplayName) { void Avatar::updateDisplayNameAlpha(bool showDisplayName) {
if (!Menu::getInstance()->isOptionChecked(MenuOption::NamesAboveHeads)) { if (!(showNamesAboveHeads || showReceiveStats)) {
_displayNameAlpha = 0.0f; _displayNameAlpha = 0.0f;
return; return;
} }
@ -1323,14 +1313,18 @@ void Avatar::getCapsule(glm::vec3& start, glm::vec3& end, float& radius) {
radius = halfExtents.x; radius = halfExtents.x;
} }
void Avatar::setMotionState(AvatarMotionState* motionState) {
_motionState = motionState;
}
// virtual // virtual
void Avatar::rebuildCollisionShape() { void Avatar::rebuildCollisionShape() {
if (_motionState) { addPhysicsFlags(Simulation::DIRTY_SHAPE);
_motionState->addDirtyFlags(Simulation::DIRTY_SHAPE); }
void Avatar::setPhysicsCallback(AvatarPhysicsCallback cb) {
_physicsCallback = cb;
}
void Avatar::addPhysicsFlags(uint32_t flags) {
if (_physicsCallback) {
_physicsCallback(flags);
} }
} }
@ -1493,16 +1487,16 @@ QList<QVariant> Avatar::getSkeleton() {
void Avatar::addToScene(AvatarSharedPointer myHandle, const render::ScenePointer& scene) { void Avatar::addToScene(AvatarSharedPointer myHandle, const render::ScenePointer& scene) {
if (scene) { if (scene) {
render::Transaction transaction;
auto nodelist = DependencyManager::get<NodeList>(); auto nodelist = DependencyManager::get<NodeList>();
if (DependencyManager::get<SceneScriptingInterface>()->shouldRenderAvatars() if (DependencyManager::get<SceneScriptingInterface>()->shouldRenderAvatars()
&& !nodelist->isIgnoringNode(getSessionUUID()) && !nodelist->isIgnoringNode(getSessionUUID())
&& !nodelist->isRadiusIgnoringNode(getSessionUUID())) { && !nodelist->isRadiusIgnoringNode(getSessionUUID())) {
render::Transaction transaction;
addToScene(myHandle, scene, transaction); addToScene(myHandle, scene, transaction);
scene->enqueueTransaction(transaction);
} }
scene->enqueueTransaction(transaction);
} else { } else {
qCWarning(interfaceapp) << "AvatarManager::addAvatar() : Unexpected null scene, possibly during application shutdown"; qCWarning(interfaceapp) << "Avatar::addAvatar() : Unexpected null scene, possibly during application shutdown";
} }
} }

View file

@ -11,6 +11,7 @@
#ifndef hifi_Avatar_h #ifndef hifi_Avatar_h
#define hifi_Avatar_h #define hifi_Avatar_h
#include <functional>
#include <glm/glm.hpp> #include <glm/glm.hpp>
#include <glm/gtc/quaternion.hpp> #include <glm/gtc/quaternion.hpp>
@ -48,9 +49,10 @@ enum ScreenTintLayer {
NUM_SCREEN_TINT_LAYERS NUM_SCREEN_TINT_LAYERS
}; };
class AvatarMotionState;
class Texture; class Texture;
using AvatarPhysicsCallback = std::function<void(uint32_t)>;
class Avatar : public AvatarData { class Avatar : public AvatarData {
Q_OBJECT Q_OBJECT
@ -66,6 +68,12 @@ class Avatar : public AvatarData {
Q_PROPERTY(glm::vec3 skeletonOffset READ getSkeletonOffset WRITE setSkeletonOffset) Q_PROPERTY(glm::vec3 skeletonOffset READ getSkeletonOffset WRITE setSkeletonOffset)
public: public:
static void setShowReceiveStats(bool receiveStats);
static void setShowMyLookAtVectors(bool showMine);
static void setShowOtherLookAtVectors(bool showOthers);
static void setShowCollisionShapes(bool render);
static void setShowNamesAboveHeads(bool show);
explicit Avatar(QThread* thread, RigPointer rig = nullptr); explicit Avatar(QThread* thread, RigPointer rig = nullptr);
~Avatar(); ~Avatar();
@ -148,7 +156,7 @@ public:
virtual void setSkeletonModelURL(const QUrl& skeletonModelURL) override; virtual void setSkeletonModelURL(const QUrl& skeletonModelURL) override;
virtual void setAttachmentData(const QVector<AttachmentData>& attachmentData) override; virtual void setAttachmentData(const QVector<AttachmentData>& attachmentData) override;
void setShowDisplayName(bool showDisplayName); void updateDisplayNameAlpha(bool showDisplayName);
virtual void setSessionDisplayName(const QString& sessionDisplayName) override { }; // no-op virtual void setSessionDisplayName(const QString& sessionDisplayName) override { }; // no-op
virtual int parseDataFromBuffer(const QByteArray& buffer) override; virtual int parseDataFromBuffer(const QByteArray& buffer) override;
@ -185,8 +193,6 @@ public:
virtual void computeShapeInfo(ShapeInfo& shapeInfo); virtual void computeShapeInfo(ShapeInfo& shapeInfo);
void getCapsule(glm::vec3& start, glm::vec3& end, float& radius); void getCapsule(glm::vec3& start, glm::vec3& end, float& radius);
AvatarMotionState* getMotionState() { return _motionState; }
using SpatiallyNestable::setPosition; using SpatiallyNestable::setPosition;
virtual void setPosition(const glm::vec3& position) override; virtual void setPosition(const glm::vec3& position) override;
using SpatiallyNestable::setOrientation; using SpatiallyNestable::setOrientation;
@ -238,6 +244,17 @@ public:
return (lerpValue*(4.0f - 2.0f * lerpValue) - 1.0f); return (lerpValue*(4.0f - 2.0f * lerpValue) - 1.0f);
} }
float getBoundingRadius() const;
void addToScene(AvatarSharedPointer self, const render::ScenePointer& scene);
void ensureInScene(AvatarSharedPointer self, const render::ScenePointer& scene);
bool isInScene() const { return render::Item::isValidID(_renderItemID); }
bool isMoving() const { return _moving; }
//void setMotionState(AvatarMotionState* motionState);
void setPhysicsCallback(AvatarPhysicsCallback cb);
void addPhysicsFlags(uint32_t flags);
bool isInPhysicsSimulation() const { return _physicsCallback != nullptr; }
public slots: public slots:
@ -251,7 +268,6 @@ public slots:
void setModelURLFinished(bool success); void setModelURLFinished(bool success);
protected: protected:
friend class AvatarManager;
const float SMOOTH_TIME_POSITION = 0.125f; const float SMOOTH_TIME_POSITION = 0.125f;
const float SMOOTH_TIME_ORIENTATION = 0.075f; const float SMOOTH_TIME_ORIENTATION = 0.075f;
@ -260,8 +276,6 @@ protected:
QString _empty{}; QString _empty{};
virtual void maybeUpdateSessionDisplayNameFromTransport(const QString& sessionDisplayName) override { _sessionDisplayName = sessionDisplayName; } // don't use no-op setter! virtual void maybeUpdateSessionDisplayNameFromTransport(const QString& sessionDisplayName) override { _sessionDisplayName = sessionDisplayName; } // don't use no-op setter!
void setMotionState(AvatarMotionState* motionState);
SkeletonModelPointer _skeletonModel; SkeletonModelPointer _skeletonModel;
glm::vec3 _skeletonOffset; glm::vec3 _skeletonOffset;
std::vector<std::shared_ptr<Model>> _attachmentModels; std::vector<std::shared_ptr<Model>> _attachmentModels;
@ -315,10 +329,6 @@ protected:
ThreadSafeValueCache<glm::vec3> _rightPalmPositionCache { glm::vec3() }; ThreadSafeValueCache<glm::vec3> _rightPalmPositionCache { glm::vec3() };
ThreadSafeValueCache<glm::quat> _rightPalmRotationCache { glm::quat() }; ThreadSafeValueCache<glm::quat> _rightPalmRotationCache { glm::quat() };
void addToScene(AvatarSharedPointer self, const render::ScenePointer& scene);
void ensureInScene(AvatarSharedPointer self, const render::ScenePointer& scene);
bool isInScene() const { return render::Item::isValidID(_renderItemID); }
// Some rate tracking support // Some rate tracking support
RateCounter<> _simulationRate; RateCounter<> _simulationRate;
RateCounter<> _simulationInViewRate; RateCounter<> _simulationInViewRate;
@ -354,13 +364,15 @@ private:
bool _isLookAtTarget { false }; bool _isLookAtTarget { false };
bool _isAnimatingScale { false }; bool _isAnimatingScale { false };
float getBoundingRadius() const;
static int _jointConesID; static int _jointConesID;
int _voiceSphereID; int _voiceSphereID;
AvatarMotionState* _motionState = nullptr; AvatarPhysicsCallback _physicsCallback { nullptr };
float _displayNameTargetAlpha { 1.0f };
float _displayNameAlpha { 1.0f };
}; };
#endif // hifi_Avatar_h #endif // hifi_Avatar_h

View file

@ -50,23 +50,6 @@ static const quint64 MIN_TIME_BETWEEN_MY_AVATAR_DATA_SENDS = USECS_PER_SECOND /
// We add _myAvatar into the hash with all the other AvatarData, and we use the default NULL QUid as the key. // We add _myAvatar into the hash with all the other AvatarData, and we use the default NULL QUid as the key.
const QUuid MY_AVATAR_KEY; // NULL key const QUuid MY_AVATAR_KEY; // NULL key
static QScriptValue localLightToScriptValue(QScriptEngine* engine, const AvatarManager::LocalLight& light) {
QScriptValue object = engine->newObject();
object.setProperty("direction", vec3toScriptValue(engine, light.direction));
object.setProperty("color", vec3toScriptValue(engine, light.color));
return object;
}
static void localLightFromScriptValue(const QScriptValue& value, AvatarManager::LocalLight& light) {
vec3FromScriptValue(value.property("direction"), light.direction);
vec3FromScriptValue(value.property("color"), light.color);
}
void AvatarManager::registerMetaTypes(QScriptEngine* engine) {
qScriptRegisterMetaType(engine, localLightToScriptValue, localLightFromScriptValue);
qScriptRegisterSequenceMetaType<QVector<AvatarManager::LocalLight> >(engine);
}
AvatarManager::AvatarManager(QObject* parent) : AvatarManager::AvatarManager(QObject* parent) :
_avatarsToFade(), _avatarsToFade(),
_myAvatar(std::make_shared<MyAvatar>(qApp->thread(), std::make_shared<Rig>())) _myAvatar(std::make_shared<MyAvatar>(qApp->thread(), std::make_shared<Rig>()))
@ -91,6 +74,7 @@ AvatarManager::AvatarManager(QObject* parent) :
} }
AvatarManager::~AvatarManager() { AvatarManager::~AvatarManager() {
assert(_motionStates.empty());
} }
void AvatarManager::init() { void AvatarManager::init() {
@ -145,10 +129,9 @@ float AvatarManager::getAvatarUpdateRate(const QUuid& sessionID, const QString&
float AvatarManager::getAvatarSimulationRate(const QUuid& sessionID, const QString& rateName) const { float AvatarManager::getAvatarSimulationRate(const QUuid& sessionID, const QString& rateName) const {
auto avatar = std::static_pointer_cast<Avatar>(getAvatarBySessionID(sessionID)); auto avatar = std::static_pointer_cast<Avatar>(getAvatarBySessionID(sessionID));
return avatar ? avatar->getSimulationRate(rateName) : 0.0f; return avatar ? avatar->getSimulationRate(rateName) : 0.0f;
} }
void AvatarManager::updateOtherAvatars(float deltaTime) { void AvatarManager::updateOtherAvatars(float deltaTime) {
// lock the hash for read to check the size // lock the hash for read to check the size
QReadLocker lock(&_hashLock); QReadLocker lock(&_hashLock);
@ -200,16 +183,15 @@ void AvatarManager::updateOtherAvatars(float deltaTime) {
if (_shouldRender) { if (_shouldRender) {
avatar->ensureInScene(avatar, qApp->getMain3DScene()); avatar->ensureInScene(avatar, qApp->getMain3DScene());
} }
if (!avatar->getMotionState()) { if (!avatar->isInPhysicsSimulation()) {
ShapeInfo shapeInfo; ShapeInfo shapeInfo;
avatar->computeShapeInfo(shapeInfo); avatar->computeShapeInfo(shapeInfo);
btCollisionShape* shape = const_cast<btCollisionShape*>(ObjectMotionState::getShapeManager()->getShape(shapeInfo)); btCollisionShape* shape = const_cast<btCollisionShape*>(ObjectMotionState::getShapeManager()->getShape(shapeInfo));
if (shape) { if (shape) {
// don't add to the simulation now, instead put it on a list to be added later AvatarMotionState* motionState = new AvatarMotionState(avatar, shape);
AvatarMotionState* motionState = new AvatarMotionState(avatar.get(), shape); avatar->setPhysicsCallback([=] (uint32_t flags) { motionState->addDirtyFlags(flags); });
avatar->setMotionState(motionState); _motionStates.insert(avatar.get(), motionState);
_motionStatesToAddToPhysics.insert(motionState); _motionStatesToAddToPhysics.insert(motionState);
_motionStatesThatMightUpdate.insert(motionState);
} }
} }
avatar->animateScaleChanges(deltaTime); avatar->animateScaleChanges(deltaTime);
@ -294,30 +276,24 @@ void AvatarManager::simulateAvatarFades(float deltaTime) {
const float MIN_FADE_SCALE = MIN_AVATAR_SCALE; const float MIN_FADE_SCALE = MIN_AVATAR_SCALE;
QReadLocker locker(&_hashLock); QReadLocker locker(&_hashLock);
QVector<AvatarSharedPointer>::iterator itr = _avatarsToFade.begin(); QVector<AvatarSharedPointer>::iterator avatarItr = _avatarsToFade.begin();
while (itr != _avatarsToFade.end()) { while (avatarItr != _avatarsToFade.end()) {
auto avatar = std::static_pointer_cast<Avatar>(*itr); auto avatar = std::static_pointer_cast<Avatar>(*avatarItr);
avatar->setTargetScale(avatar->getUniformScale() * SHRINK_RATE); avatar->setTargetScale(avatar->getUniformScale() * SHRINK_RATE);
avatar->animateScaleChanges(deltaTime); avatar->animateScaleChanges(deltaTime);
if (avatar->getTargetScale() <= MIN_FADE_SCALE) { if (avatar->getTargetScale() <= MIN_FADE_SCALE) {
// fading to zero is such a rare event we push unique transaction for each one // fading to zero is such a rare event we push a unique transaction for each
if (avatar->isInScene()) { if (avatar->isInScene()) {
const render::ScenePointer& scene = qApp->getMain3DScene(); const render::ScenePointer& scene = qApp->getMain3DScene();
render::Transaction transaction; render::Transaction transaction;
avatar->removeFromScene(*itr, scene, transaction); avatar->removeFromScene(*avatarItr, scene, transaction);
scene->enqueueTransaction(transaction); scene->enqueueTransaction(transaction);
} }
avatarItr = _avatarsToFade.erase(avatarItr);
// only remove from _avatarsToFade if we're sure its motionState has been removed from PhysicsEngine
if (_motionStatesToRemoveFromPhysics.empty()) {
itr = _avatarsToFade.erase(itr);
} else {
++itr;
}
} else { } else {
const bool inView = true; // HACK const bool inView = true; // HACK
avatar->simulate(deltaTime, inView); avatar->simulate(deltaTime, inView);
++itr; ++avatarItr;
} }
} }
} }
@ -329,15 +305,15 @@ AvatarSharedPointer AvatarManager::newSharedAvatar() {
void AvatarManager::handleRemovedAvatar(const AvatarSharedPointer& removedAvatar, KillAvatarReason removalReason) { void AvatarManager::handleRemovedAvatar(const AvatarSharedPointer& removedAvatar, KillAvatarReason removalReason) {
AvatarHashMap::handleRemovedAvatar(removedAvatar, removalReason); AvatarHashMap::handleRemovedAvatar(removedAvatar, removalReason);
// removedAvatar is a shared pointer to an AvatarData but we need to get to the derived Avatar // remove from physics
// class in this context so we can call methods that don't exist at the base class.
auto avatar = std::static_pointer_cast<Avatar>(removedAvatar); auto avatar = std::static_pointer_cast<Avatar>(removedAvatar);
avatar->setPhysicsCallback(nullptr);
AvatarMotionState* motionState = avatar->getMotionState(); AvatarMotionStateMap::iterator itr = _motionStates.find(avatar.get());
if (motionState) { if (itr != _motionStates.end()) {
_motionStatesThatMightUpdate.remove(motionState); AvatarMotionState* motionState = *itr;
_motionStatesToAddToPhysics.remove(motionState); _motionStatesToAddToPhysics.remove(motionState);
_motionStatesToRemoveFromPhysics.push_back(motionState); _motionStatesToRemoveFromPhysics.push_back(motionState);
_motionStates.erase(itr);
} }
if (removalReason == KillAvatarReason::TheirAvatarEnteredYourBubble) { if (removalReason == KillAvatarReason::TheirAvatarEnteredYourBubble) {
@ -373,11 +349,15 @@ void AvatarManager::clearOtherAvatars() {
++avatarIterator; ++avatarIterator;
} }
} }
assert(scene);
scene->enqueueTransaction(transaction); scene->enqueueTransaction(transaction);
_myAvatar->clearLookAtTargetAvatar(); _myAvatar->clearLookAtTargetAvatar();
} }
void AvatarManager::deleteAllAvatars() { void AvatarManager::deleteAllAvatars() {
assert(_motionStates.empty()); // should have called clearOtherAvatars() before getting here
deleteMotionStates();
QReadLocker locker(&_hashLock); QReadLocker locker(&_hashLock);
AvatarHash::iterator avatarIterator = _avatarHash.begin(); AvatarHash::iterator avatarIterator = _avatarHash.begin();
while (avatarIterator != _avatarHash.end()) { while (avatarIterator != _avatarHash.end()) {
@ -387,27 +367,18 @@ void AvatarManager::deleteAllAvatars() {
} }
} }
void AvatarManager::setLocalLights(const QVector<AvatarManager::LocalLight>& localLights) { void AvatarManager::deleteMotionStates() {
if (QThread::currentThread() != thread()) { // delete motionstates that were removed from physics last frame
QMetaObject::invokeMethod(this, "setLocalLights", Q_ARG(const QVector<AvatarManager::LocalLight>&, localLights)); for (auto state : _motionStatesToDelete) {
return; delete state;
} }
_localLights = localLights; _motionStatesToDelete.clear();
}
QVector<AvatarManager::LocalLight> AvatarManager::getLocalLights() const {
if (QThread::currentThread() != thread()) {
QVector<AvatarManager::LocalLight> result;
QMetaObject::invokeMethod(const_cast<AvatarManager*>(this), "getLocalLights", Qt::BlockingQueuedConnection,
Q_RETURN_ARG(QVector<AvatarManager::LocalLight>, result));
return result;
}
return _localLights;
} }
void AvatarManager::getObjectsToRemoveFromPhysics(VectorOfMotionStates& result) { void AvatarManager::getObjectsToRemoveFromPhysics(VectorOfMotionStates& result) {
result.clear(); deleteMotionStates();
result.swap(_motionStatesToRemoveFromPhysics); result = _motionStatesToRemoveFromPhysics;
_motionStatesToDelete.swap(_motionStatesToRemoveFromPhysics);
} }
void AvatarManager::getObjectsToAddToPhysics(VectorOfMotionStates& result) { void AvatarManager::getObjectsToAddToPhysics(VectorOfMotionStates& result) {
@ -420,10 +391,12 @@ void AvatarManager::getObjectsToAddToPhysics(VectorOfMotionStates& result) {
void AvatarManager::getObjectsToChange(VectorOfMotionStates& result) { void AvatarManager::getObjectsToChange(VectorOfMotionStates& result) {
result.clear(); result.clear();
for (auto state : _motionStatesThatMightUpdate) { AvatarMotionStateMap::iterator motionStateItr = _motionStates.begin();
if (state->_dirtyFlags > 0) { while (motionStateItr != _motionStates.end()) {
result.push_back(state); if ((*motionStateItr)->getIncomingDirtyFlags() != 0) {
result.push_back(*motionStateItr);
} }
++motionStateItr;
} }
} }

View file

@ -62,18 +62,6 @@ public:
void clearOtherAvatars(); void clearOtherAvatars();
void deleteAllAvatars(); void deleteAllAvatars();
bool shouldShowReceiveStats() const { return _shouldShowReceiveStats; }
class LocalLight {
public:
glm::vec3 color;
glm::vec3 direction;
};
Q_INVOKABLE void setLocalLights(const QVector<AvatarManager::LocalLight>& localLights);
Q_INVOKABLE QVector<AvatarManager::LocalLight> getLocalLights() const;
void getObjectsToRemoveFromPhysics(VectorOfMotionStates& motionStates); void getObjectsToRemoveFromPhysics(VectorOfMotionStates& motionStates);
void getObjectsToAddToPhysics(VectorOfMotionStates& motionStates); void getObjectsToAddToPhysics(VectorOfMotionStates& motionStates);
void getObjectsToChange(VectorOfMotionStates& motionStates); void getObjectsToChange(VectorOfMotionStates& motionStates);
@ -95,7 +83,6 @@ public:
float getMyAvatarSendRate() const { return _myAvatarSendRate.rate(); } float getMyAvatarSendRate() const { return _myAvatarSendRate.rate(); }
public slots: public slots:
void setShouldShowReceiveStats(bool shouldShowReceiveStats) { _shouldShowReceiveStats = shouldShowReceiveStats; }
void updateAvatarRenderStatus(bool shouldRenderAvatars); void updateAvatarRenderStatus(bool shouldRenderAvatars);
private: private:
@ -105,21 +92,20 @@ private:
void simulateAvatarFades(float deltaTime); void simulateAvatarFades(float deltaTime);
AvatarSharedPointer newSharedAvatar() override; AvatarSharedPointer newSharedAvatar() override;
void deleteMotionStates();
void handleRemovedAvatar(const AvatarSharedPointer& removedAvatar, KillAvatarReason removalReason = KillAvatarReason::NoReason) override; void handleRemovedAvatar(const AvatarSharedPointer& removedAvatar, KillAvatarReason removalReason = KillAvatarReason::NoReason) override;
QVector<AvatarSharedPointer> _avatarsToFade; QVector<AvatarSharedPointer> _avatarsToFade;
QSet<AvatarMotionState*> _motionStatesThatMightUpdate; using AvatarMotionStateMap = QMap<Avatar*, AvatarMotionState*>;
AvatarMotionStateMap _motionStates;
VectorOfMotionStates _motionStatesToRemoveFromPhysics; VectorOfMotionStates _motionStatesToRemoveFromPhysics;
VectorOfMotionStates _motionStatesToDelete;
SetOfMotionStates _motionStatesToAddToPhysics; SetOfMotionStates _motionStatesToAddToPhysics;
std::shared_ptr<MyAvatar> _myAvatar; std::shared_ptr<MyAvatar> _myAvatar;
quint64 _lastSendAvatarDataTime = 0; // Controls MyAvatar send data rate. quint64 _lastSendAvatarDataTime = 0; // Controls MyAvatar send data rate.
QVector<AvatarManager::LocalLight> _localLights;
bool _shouldShowReceiveStats = false;
std::list<QPointer<AudioInjector>> _collisionInjectors; std::list<QPointer<AudioInjector>> _collisionInjectors;
RateCounter<> _myAvatarSendRate; RateCounter<> _myAvatarSendRate;
@ -129,7 +115,4 @@ private:
bool _shouldRender { true }; bool _shouldRender { true };
}; };
Q_DECLARE_METATYPE(AvatarManager::LocalLight)
Q_DECLARE_METATYPE(QVector<AvatarManager::LocalLight>)
#endif // hifi_AvatarManager_h #endif // hifi_AvatarManager_h

View file

@ -17,7 +17,7 @@
#include "AvatarMotionState.h" #include "AvatarMotionState.h"
#include "BulletUtil.h" #include "BulletUtil.h"
AvatarMotionState::AvatarMotionState(Avatar* avatar, const btCollisionShape* shape) : ObjectMotionState(shape), _avatar(avatar) { AvatarMotionState::AvatarMotionState(AvatarSharedPointer avatar, const btCollisionShape* shape) : ObjectMotionState(shape), _avatar(avatar) {
assert(_avatar); assert(_avatar);
_type = MOTIONSTATE_TYPE_AVATAR; _type = MOTIONSTATE_TYPE_AVATAR;
if (_shape) { if (_shape) {
@ -49,7 +49,7 @@ PhysicsMotionType AvatarMotionState::computePhysicsMotionType() const {
// virtual and protected // virtual and protected
const btCollisionShape* AvatarMotionState::computeNewShape() { const btCollisionShape* AvatarMotionState::computeNewShape() {
ShapeInfo shapeInfo; ShapeInfo shapeInfo;
_avatar->computeShapeInfo(shapeInfo); std::static_pointer_cast<Avatar>(_avatar)->computeShapeInfo(shapeInfo);
return getShapeManager()->getShape(shapeInfo); return getShapeManager()->getShape(shapeInfo);
} }
@ -130,7 +130,7 @@ glm::vec3 AvatarMotionState::getObjectAngularVelocity() const {
// virtual // virtual
glm::vec3 AvatarMotionState::getObjectGravity() const { glm::vec3 AvatarMotionState::getObjectGravity() const {
return _avatar->getAcceleration(); return std::static_pointer_cast<Avatar>(_avatar)->getAcceleration();
} }
// virtual // virtual

View file

@ -20,7 +20,7 @@ class Avatar;
class AvatarMotionState : public ObjectMotionState { class AvatarMotionState : public ObjectMotionState {
public: public:
AvatarMotionState(Avatar* avatar, const btCollisionShape* shape); AvatarMotionState(AvatarSharedPointer avatar, const btCollisionShape* shape);
virtual PhysicsMotionType getMotionType() const override { return _motionType; } virtual PhysicsMotionType getMotionType() const override { return _motionType; }
@ -74,11 +74,7 @@ protected:
virtual bool isReadyToComputeShape() const override { return true; } virtual bool isReadyToComputeShape() const override { return true; }
virtual const btCollisionShape* computeNewShape() override; virtual const btCollisionShape* computeNewShape() override;
// The AvatarMotionState keeps a RAW backpointer to its Avatar because all AvatarMotionState AvatarSharedPointer _avatar;
// instances are "owned" by their corresponding Avatar instance and are deleted in the Avatar dtor.
// In other words, it is impossible for the Avatar to be deleted out from under its MotionState.
// In conclusion: weak pointer shennanigans would be pure overhead.
Avatar* _avatar; // do NOT use smartpointer here, no need for weakpointer
uint32_t _dirtyFlags; uint32_t _dirtyFlags;
}; };

View file

@ -111,7 +111,7 @@ void setupPreferences() {
static const QString SNAPSHOTS { "Snapshots" }; static const QString SNAPSHOTS { "Snapshots" };
{ {
auto getter = []()->QString { return Snapshot::snapshotsLocation.get(); }; auto getter = []()->QString { return Snapshot::snapshotsLocation.get(); };
auto setter = [](const QString& value) { Snapshot::snapshotsLocation.set(value); }; auto setter = [](const QString& value) { Snapshot::snapshotsLocation.set(value); emit DependencyManager::get<Snapshot>()->snapshotLocationSet(value); };
auto preference = new BrowsePreference(SNAPSHOTS, "Put my snapshots here", getter, setter); auto preference = new BrowsePreference(SNAPSHOTS, "Put my snapshots here", getter, setter);
preferences->addPreference(preference); preferences->addPreference(preference);
} }

View file

@ -44,6 +44,9 @@ public:
static Setting::Handle<QString> snapshotsLocation; static Setting::Handle<QString> snapshotsLocation;
static void uploadSnapshot(const QString& filename, const QUrl& href = QUrl("")); static void uploadSnapshot(const QString& filename, const QUrl& href = QUrl(""));
signals:
void snapshotLocationSet(const QString& value);
public slots: public slots:
Q_INVOKABLE QString getSnapshotsLocation(); Q_INVOKABLE QString getSnapshotsLocation();
Q_INVOKABLE void setSnapshotsLocation(const QString& location); Q_INVOKABLE void setSnapshotsLocation(const QString& location);

View file

@ -63,8 +63,6 @@ AvatarData::AvatarData() :
_keyState(NO_KEY_DOWN), _keyState(NO_KEY_DOWN),
_forceFaceTrackerConnected(false), _forceFaceTrackerConnected(false),
_headData(NULL), _headData(NULL),
_displayNameTargetAlpha(1.0f),
_displayNameAlpha(1.0f),
_errorLogExpiry(0), _errorLogExpiry(0),
_owningAvatarMixer(), _owningAvatarMixer(),
_targetVelocity(0.0f) _targetVelocity(0.0f)

View file

@ -692,9 +692,6 @@ protected:
QString _sessionDisplayName { }; QString _sessionDisplayName { };
QUrl cannonicalSkeletonModelURL(const QUrl& empty) const; QUrl cannonicalSkeletonModelURL(const QUrl& empty) const;
float _displayNameTargetAlpha;
float _displayNameAlpha;
QHash<QString, int> _jointIndices; ///< 1-based, since zero is returned for missing keys QHash<QString, int> _jointIndices; ///< 1-based, since zero is returned for missing keys
QStringList _jointNames; ///< in order of depth-first traversal QStringList _jointNames; ///< in order of depth-first traversal

View file

@ -69,5 +69,23 @@ namespace controller {
pose.valid = valid; pose.valid = valid;
return pose; return pose;
} }
Pose Pose::postTransform(const glm::mat4& mat) const {
glm::mat4 original = ::createMatFromQuatAndPos(rotation, translation);
glm::mat4 result = original * mat;
auto translationOut = ::extractTranslation(result);
auto rotationOut = ::glmExtractRotation(result);
auto velocityOut = velocity + glm::cross(angularVelocity, translationOut - translation); // warning: this may be completely wrong
auto angularVelocityOut = angularVelocity;
Pose pose(translationOut,
rotationOut,
velocityOut,
angularVelocityOut);
pose.valid = valid;
return pose;
}
} }

View file

@ -41,6 +41,7 @@ namespace controller {
vec3 getAngularVelocity() const { return angularVelocity; } vec3 getAngularVelocity() const { return angularVelocity; }
Pose transform(const glm::mat4& mat) const; Pose transform(const glm::mat4& mat) const;
Pose postTransform(const glm::mat4& mat) const;
static QScriptValue toScriptValue(QScriptEngine* engine, const Pose& event); static QScriptValue toScriptValue(QScriptEngine* engine, const Pose& event);
static void fromScriptValue(const QScriptValue& object, Pose& event); static void fromScriptValue(const QScriptValue& object, Pose& event);

View file

@ -20,6 +20,8 @@
#include <PathUtils.h> #include <PathUtils.h>
#include <NumericalConstants.h> #include <NumericalConstants.h>
#include <StreamUtils.h>
#include "StandardController.h" #include "StandardController.h"
#include "StateController.h" #include "StateController.h"
#include "InputRecorder.h" #include "InputRecorder.h"
@ -563,7 +565,18 @@ bool UserInputMapper::applyRoute(const Route::Pointer& route, bool force) {
if (source->isPose()) { if (source->isPose()) {
Pose value = getPose(source, route->peek); Pose value = getPose(source, route->peek);
static const Pose IDENTITY_POSE { vec3(), quat() }; static const Pose IDENTITY_POSE { vec3(), quat() };
if (debugRoutes && route->debug) { if (debugRoutes && route->debug) {
qCDebug(controllers) << "Value was t:" << value.translation << "r:" << value.rotation;
}
// Apply each of the filters.
for (const auto& filter : route->filters) {
value = filter->apply(value);
}
if (debugRoutes && route->debug) {
qCDebug(controllers) << "Filtered value was t:" << value.translation << "r:" << value.rotation;
if (!value.valid) { if (!value.valid) {
qCDebug(controllers) << "Applying invalid pose"; qCDebug(controllers) << "Applying invalid pose";
} else if (value == IDENTITY_POSE) { } else if (value == IDENTITY_POSE) {

View file

@ -24,6 +24,10 @@
#include "filters/InvertFilter.h" #include "filters/InvertFilter.h"
#include "filters/PulseFilter.h" #include "filters/PulseFilter.h"
#include "filters/ScaleFilter.h" #include "filters/ScaleFilter.h"
#include "filters/TranslateFilter.h"
#include "filters/TransformFilter.h"
#include "filters/PostTransformFilter.h"
#include "filters/RotateFilter.h"
using namespace controller; using namespace controller;
@ -37,6 +41,10 @@ REGISTER_FILTER_CLASS_INSTANCE(HysteresisFilter, "hysteresis")
REGISTER_FILTER_CLASS_INSTANCE(InvertFilter, "invert") REGISTER_FILTER_CLASS_INSTANCE(InvertFilter, "invert")
REGISTER_FILTER_CLASS_INSTANCE(ScaleFilter, "scale") REGISTER_FILTER_CLASS_INSTANCE(ScaleFilter, "scale")
REGISTER_FILTER_CLASS_INSTANCE(PulseFilter, "pulse") REGISTER_FILTER_CLASS_INSTANCE(PulseFilter, "pulse")
REGISTER_FILTER_CLASS_INSTANCE(TranslateFilter, "translate")
REGISTER_FILTER_CLASS_INSTANCE(TransformFilter, "transform")
REGISTER_FILTER_CLASS_INSTANCE(PostTransformFilter, "postTransform")
REGISTER_FILTER_CLASS_INSTANCE(RotateFilter, "rotate")
const QString JSON_FILTER_TYPE = QStringLiteral("type"); const QString JSON_FILTER_TYPE = QStringLiteral("type");
const QString JSON_FILTER_PARAMS = QStringLiteral("params"); const QString JSON_FILTER_PARAMS = QStringLiteral("params");
@ -76,7 +84,6 @@ bool Filter::parseSingleFloatParameter(const QJsonValue& parameters, const QStri
return true; return true;
} }
} else if (parameters.isObject()) { } else if (parameters.isObject()) {
static const QString JSON_MIN = QStringLiteral("interval");
auto objectParameters = parameters.toObject(); auto objectParameters = parameters.toObject();
if (objectParameters.contains(name)) { if (objectParameters.contains(name)) {
output = objectParameters[name].toDouble(); output = objectParameters[name].toDouble();
@ -86,6 +93,92 @@ bool Filter::parseSingleFloatParameter(const QJsonValue& parameters, const QStri
return false; return false;
} }
bool Filter::parseVec3Parameter(const QJsonValue& parameters, glm::vec3& output) {
if (parameters.isDouble()) {
output = glm::vec3(parameters.toDouble());
return true;
} else if (parameters.isArray()) {
auto arrayParameters = parameters.toArray();
if (arrayParameters.size() == 3) {
output = glm::vec3(arrayParameters[0].toDouble(),
arrayParameters[1].toDouble(),
arrayParameters[2].toDouble());
return true;
}
} else if (parameters.isObject()) {
auto objectParameters = parameters.toObject();
if (objectParameters.contains("x") && objectParameters.contains("y") && objectParameters.contains("z")) {
output = glm::vec3(objectParameters["x"].toDouble(),
objectParameters["y"].toDouble(),
objectParameters["z"].toDouble());
return true;
}
}
return false;
}
bool Filter::parseMat4Parameter(const QJsonValue& parameters, glm::mat4& output) {
if (parameters.isObject()) {
auto objectParameters = parameters.toObject();
if (objectParameters.contains("r0c0") &&
objectParameters.contains("r1c0") &&
objectParameters.contains("r2c0") &&
objectParameters.contains("r3c0") &&
objectParameters.contains("r0c1") &&
objectParameters.contains("r1c1") &&
objectParameters.contains("r2c1") &&
objectParameters.contains("r3c1") &&
objectParameters.contains("r0c2") &&
objectParameters.contains("r1c2") &&
objectParameters.contains("r2c2") &&
objectParameters.contains("r3c2") &&
objectParameters.contains("r0c3") &&
objectParameters.contains("r1c3") &&
objectParameters.contains("r2c3") &&
objectParameters.contains("r3c3")) {
output[0][0] = objectParameters["r0c0"].toDouble();
output[0][1] = objectParameters["r1c0"].toDouble();
output[0][2] = objectParameters["r2c0"].toDouble();
output[0][3] = objectParameters["r3c0"].toDouble();
output[1][0] = objectParameters["r0c1"].toDouble();
output[1][1] = objectParameters["r1c1"].toDouble();
output[1][2] = objectParameters["r2c1"].toDouble();
output[1][3] = objectParameters["r3c1"].toDouble();
output[2][0] = objectParameters["r0c2"].toDouble();
output[2][1] = objectParameters["r1c2"].toDouble();
output[2][2] = objectParameters["r2c2"].toDouble();
output[2][3] = objectParameters["r3c2"].toDouble();
output[3][0] = objectParameters["r0c3"].toDouble();
output[3][1] = objectParameters["r1c3"].toDouble();
output[3][2] = objectParameters["r2c3"].toDouble();
output[3][3] = objectParameters["r3c3"].toDouble();
return true;
}
}
return false;
}
bool Filter::parseQuatParameter(const QJsonValue& parameters, glm::quat& output) {
if (parameters.isObject()) {
auto objectParameters = parameters.toObject();
if (objectParameters.contains("w") &&
objectParameters.contains("x") &&
objectParameters.contains("y") &&
objectParameters.contains("z")) {
output = glm::quat(objectParameters["w"].toDouble(),
objectParameters["x"].toDouble(),
objectParameters["y"].toDouble(),
objectParameters["z"].toDouble());
return true;
}
}
return false;
}
#if 0 #if 0

View file

@ -21,6 +21,8 @@
#include <QtCore/QEasingCurve> #include <QtCore/QEasingCurve>
#include "../Pose.h"
class QJsonValue; class QJsonValue;
namespace controller { namespace controller {
@ -34,6 +36,8 @@ namespace controller {
using Factory = hifi::SimpleFactory<Filter, QString>; using Factory = hifi::SimpleFactory<Filter, QString>;
virtual float apply(float value) const = 0; virtual float apply(float value) const = 0;
virtual Pose apply(Pose value) const = 0;
// Factory features // Factory features
virtual bool parseParameters(const QJsonValue& parameters) { return true; } virtual bool parseParameters(const QJsonValue& parameters) { return true; }
@ -42,6 +46,9 @@ namespace controller {
static Factory& getFactory() { return _factory; } static Factory& getFactory() { return _factory; }
static bool parseSingleFloatParameter(const QJsonValue& parameters, const QString& name, float& output); static bool parseSingleFloatParameter(const QJsonValue& parameters, const QString& name, float& output);
static bool parseVec3Parameter(const QJsonValue& parameters, glm::vec3& output);
static bool parseQuatParameter(const QJsonValue& parameters, glm::quat& output);
static bool parseMat4Parameter(const QJsonValue& parameters, glm::mat4& output);
protected: protected:
static Factory _factory; static Factory _factory;
}; };

View file

@ -26,6 +26,10 @@
#include "filters/InvertFilter.h" #include "filters/InvertFilter.h"
#include "filters/PulseFilter.h" #include "filters/PulseFilter.h"
#include "filters/ScaleFilter.h" #include "filters/ScaleFilter.h"
#include "filters/TranslateFilter.h"
#include "filters/TransformFilter.h"
#include "filters/PostTransformFilter.h"
#include "filters/RotateFilter.h"
#include "conditionals/AndConditional.h" #include "conditionals/AndConditional.h"
using namespace controller; using namespace controller;
@ -103,6 +107,26 @@ QObject* RouteBuilderProxy::deadZone(float min) {
return this; return this;
} }
QObject* RouteBuilderProxy::translate(glm::vec3 translate) {
addFilter(std::make_shared<TranslateFilter>(translate));
return this;
}
QObject* RouteBuilderProxy::transform(glm::mat4 transform) {
addFilter(std::make_shared<TransformFilter>(transform));
return this;
}
QObject* RouteBuilderProxy::postTransform(glm::mat4 transform) {
addFilter(std::make_shared<PostTransformFilter>(transform));
return this;
}
QObject* RouteBuilderProxy::rotate(glm::quat rotation) {
addFilter(std::make_shared<RotateFilter>(rotation));
return this;
}
QObject* RouteBuilderProxy::constrainToInteger() { QObject* RouteBuilderProxy::constrainToInteger() {
addFilter(std::make_shared<ConstrainToIntegerFilter>()); addFilter(std::make_shared<ConstrainToIntegerFilter>());
return this; return this;

View file

@ -48,6 +48,10 @@ class RouteBuilderProxy : public QObject {
Q_INVOKABLE QObject* deadZone(float min); Q_INVOKABLE QObject* deadZone(float min);
Q_INVOKABLE QObject* constrainToInteger(); Q_INVOKABLE QObject* constrainToInteger();
Q_INVOKABLE QObject* constrainToPositiveInteger(); Q_INVOKABLE QObject* constrainToPositiveInteger();
Q_INVOKABLE QObject* translate(glm::vec3 translate);
Q_INVOKABLE QObject* transform(glm::mat4 transform);
Q_INVOKABLE QObject* postTransform(glm::mat4 transform);
Q_INVOKABLE QObject* rotate(glm::quat rotation);
private: private:
void to(const Endpoint::Pointer& destination); void to(const Endpoint::Pointer& destination);

View file

@ -21,6 +21,9 @@ public:
virtual float apply(float value) const override { virtual float apply(float value) const override {
return glm::clamp(value, _min, _max); return glm::clamp(value, _min, _max);
} }
virtual Pose apply(Pose value) const override { return value; }
virtual bool parseParameters(const QJsonValue& parameters) override; virtual bool parseParameters(const QJsonValue& parameters) override;
protected: protected:
float _min = 0.0f; float _min = 0.0f;

View file

@ -22,6 +22,9 @@ public:
virtual float apply(float value) const override { virtual float apply(float value) const override {
return glm::sign(value); return glm::sign(value);
} }
virtual Pose apply(Pose value) const override { return value; }
protected: protected:
}; };

View file

@ -22,6 +22,9 @@ public:
virtual float apply(float value) const override { virtual float apply(float value) const override {
return (value <= 0.0f) ? 0.0f : 1.0f; return (value <= 0.0f) ? 0.0f : 1.0f;
} }
virtual Pose apply(Pose value) const override { return value; }
protected: protected:
}; };

View file

@ -20,6 +20,9 @@ public:
DeadZoneFilter(float min = 0.0) : _min(min) {}; DeadZoneFilter(float min = 0.0) : _min(min) {};
virtual float apply(float value) const override; virtual float apply(float value) const override;
virtual Pose apply(Pose value) const override { return value; }
virtual bool parseParameters(const QJsonValue& parameters) override; virtual bool parseParameters(const QJsonValue& parameters) override;
protected: protected:
float _min = 0.0f; float _min = 0.0f;

View file

@ -19,6 +19,9 @@ class HysteresisFilter : public Filter {
public: public:
HysteresisFilter(float min = 0.25, float max = 0.75); HysteresisFilter(float min = 0.25, float max = 0.75);
virtual float apply(float value) const override; virtual float apply(float value) const override;
virtual Pose apply(Pose value) const override { return value; }
virtual bool parseParameters(const QJsonValue& parameters) override; virtual bool parseParameters(const QJsonValue& parameters) override;
protected: protected:
float _min; float _min;

View file

@ -0,0 +1,33 @@
//
// Created by Brad Hefta-Gaub 2017/04/11
// Copyright 2017 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_Controllers_Filters_PostTransform_h
#define hifi_Controllers_Filters_PostTransform_h
#include <glm/gtx/transform.hpp>
#include "../Filter.h"
namespace controller {
class PostTransformFilter : public Filter {
REGISTER_FILTER_CLASS(PostTransformFilter);
public:
PostTransformFilter() { }
PostTransformFilter(glm::mat4 transform) : _transform(transform) {}
virtual float apply(float value) const override { return value; }
virtual Pose apply(Pose value) const override { return value.postTransform(_transform); }
virtual bool parseParameters(const QJsonValue& parameters) override { return parseMat4Parameter(parameters, _transform); }
private:
glm::mat4 _transform;
};
}
#endif

View file

@ -23,6 +23,8 @@ public:
virtual float apply(float value) const override; virtual float apply(float value) const override;
virtual Pose apply(Pose value) const override { return value; }
virtual bool parseParameters(const QJsonValue& parameters) override; virtual bool parseParameters(const QJsonValue& parameters) override;
private: private:

View file

@ -0,0 +1,39 @@
//
// Created by Brad Hefta-Gaub 2017/04/11
// Copyright 2017 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_Controllers_Filters_Rotate_h
#define hifi_Controllers_Filters_Rotate_h
#include <glm/gtx/transform.hpp>
#include "../Filter.h"
namespace controller {
class RotateFilter : public Filter {
REGISTER_FILTER_CLASS(RotateFilter);
public:
RotateFilter() { }
RotateFilter(glm::quat rotation) : _rotation(rotation) {}
virtual float apply(float value) const override { return value; }
virtual Pose apply(Pose value) const override {
return value.transform(glm::mat4(glm::quat(_rotation)));
}
virtual bool parseParameters(const QJsonValue& parameters) override { return parseQuatParameter(parameters, _rotation); }
private:
glm::quat _rotation;
};
}
#endif

View file

@ -10,6 +10,8 @@
#ifndef hifi_Controllers_Filters_Scale_h #ifndef hifi_Controllers_Filters_Scale_h
#define hifi_Controllers_Filters_Scale_h #define hifi_Controllers_Filters_Scale_h
#include <glm/gtc/matrix_transform.hpp>
#include "../Filter.h" #include "../Filter.h"
namespace controller { namespace controller {
@ -23,6 +25,11 @@ public:
virtual float apply(float value) const override { virtual float apply(float value) const override {
return value * _scale; return value * _scale;
} }
virtual Pose apply(Pose value) const override {
return value.transform(glm::scale(glm::mat4(), glm::vec3(_scale)));
}
virtual bool parseParameters(const QJsonValue& parameters) override; virtual bool parseParameters(const QJsonValue& parameters) override;
private: private:

View file

@ -0,0 +1,35 @@
//
// Created by Brad Hefta-Gaub 2017/04/11
// Copyright 2017 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_Controllers_Filters_Transform_h
#define hifi_Controllers_Filters_Transform_h
#include <glm/gtx/transform.hpp>
#include "../Filter.h"
namespace controller {
class TransformFilter : public Filter {
REGISTER_FILTER_CLASS(TransformFilter);
public:
TransformFilter() { }
TransformFilter(glm::mat4 transform) : _transform(transform) {}
virtual float apply(float value) const override { return value; }
virtual Pose apply(Pose value) const override { return value.transform(_transform); }
virtual bool parseParameters(const QJsonValue& parameters) override { return parseMat4Parameter(parameters, _transform); }
private:
glm::mat4 _transform;
};
}
#endif

View file

@ -0,0 +1,35 @@
//
// Created by Brad Hefta-Gaub 2017/04/11
// Copyright 2017 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_Controllers_Filters_Translate_h
#define hifi_Controllers_Filters_Translate_h
#include <glm/gtx/transform.hpp>
#include "../Filter.h"
namespace controller {
class TranslateFilter : public Filter {
REGISTER_FILTER_CLASS(TranslateFilter);
public:
TranslateFilter() { }
TranslateFilter(glm::vec3 translate) : _translate(translate) {}
virtual float apply(float value) const override { return value; }
virtual Pose apply(Pose value) const override { return value.transform(glm::translate(_translate)); }
virtual bool parseParameters(const QJsonValue& parameters) override { return parseVec3Parameter(parameters, _translate); }
private:
glm::vec3 _translate { 0.0f };
};
}
#endif

View file

@ -170,7 +170,7 @@ protected:
bool _hasInternalKinematicChanges { false }; bool _hasInternalKinematicChanges { false };
}; };
typedef QSet<ObjectMotionState*> SetOfMotionStates; using SetOfMotionStates = QSet<ObjectMotionState*>;
typedef QVector<ObjectMotionState*> VectorOfMotionStates; using VectorOfMotionStates = QVector<ObjectMotionState*>;
#endif // hifi_ObjectMotionState_h #endif // hifi_ObjectMotionState_h

View file

@ -220,6 +220,7 @@ void PhysicsEngine::removeObjects(const SetOfMotionStates& objects) {
body->setMotionState(nullptr); body->setMotionState(nullptr);
delete body; delete body;
} }
object->clearIncomingDirtyFlags();
} }
} }

View file

@ -41,8 +41,8 @@ public:
void* _b; // ObjectMotionState pointer void* _b; // ObjectMotionState pointer
}; };
typedef std::map<ContactKey, ContactInfo> ContactMap; using ContactMap = std::map<ContactKey, ContactInfo>;
typedef std::vector<Collision> CollisionEvents; using CollisionEvents = std::vector<Collision>;
class PhysicsEngine { class PhysicsEngine {
public: public:

View file

@ -130,8 +130,8 @@ public:
bool _enableTexturing { true }; bool _enableTexturing { true };
RenderDetails _details; RenderDetails _details;
render::ScenePointer _scene; // HACK render::ScenePointer _scene;
int8_t _cameraMode { -1 }; // HACK int8_t _cameraMode { -1 };
}; };
#endif // hifi_RenderArgs_h #endif // hifi_RenderArgs_h

View file

@ -0,0 +1,34 @@
//
// puppetFeet3.js
// examples/controllers
//
// Created by Brad Hefta-Gaub on 2017/04/11
// Copyright 2017 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
//
var MAPPING_NAME = "com.highfidelity.examples.puppetFeet3";
var mapping = Controller.newMapping(MAPPING_NAME);
var puppetOffset = { x: 0, y: -1, z: 0 };
var rotation = Quat.fromPitchYawRollDegrees(0, 0, -90);
var noTranslation = { x: 0, y: 0, z: 0 };
var transformMatrix = Mat4.createFromRotAndTrans(rotation, noTranslation);
var rotateAndTranslate = Mat4.createFromRotAndTrans(rotation, puppetOffset);
mapping.from(Controller.Standard.LeftHand).peek().rotate(rotation).translate(puppetOffset).to(Controller.Standard.LeftFoot);
//mapping.from(Controller.Standard.LeftHand).peek().translate(puppetOffset).to(Controller.Standard.LeftFoot);
//mapping.from(Controller.Standard.LeftHand).peek().transform(transformMatrix).translate(puppetOffset).to(Controller.Standard.LeftFoot);
//mapping.from(Controller.Standard.LeftHand).peek().transform(rotateAndTranslate).to(Controller.Standard.LeftFoot);
Controller.enableMapping(MAPPING_NAME);
Script.scriptEnding.connect(function(){
mapping.disable();
});

View file

@ -80,6 +80,9 @@ input[type=button].naked:active {
#snapshot-images { #snapshot-images {
width: 100%; width: 100%;
display: flex;
justify-content: center;
flex-direction: column;
} }
#snapshot-images img { #snapshot-images img {
@ -108,7 +111,7 @@ input[type=button].naked:active {
justify-content: space-between; justify-content: space-between;
flex-direction: row; flex-direction: row;
align-items: center; align-items: center;
height: 50px; height: 45px;
line-height: 60px; line-height: 60px;
width: calc(100% - 8px); width: calc(100% - 8px);
position: absolute; position: absolute;
@ -119,13 +122,13 @@ input[type=button].naked:active {
.shareButtons { .shareButtons {
display: flex; display: flex;
align-items: center; align-items: center;
margin-left: 30px; margin-left: 15px;
height: 100%; height: 100%;
width: 80%; width: 75%;
} }
.blastToConnections { .blastToConnections {
text-align: left; text-align: left;
margin-right: 25px; margin-right: 20px;
height: 29px; height: 29px;
} }
.shareWithEveryone { .shareWithEveryone {
@ -158,10 +161,11 @@ input[type=button].naked:active {
font-family: Raleway-SemiBold; font-family: Raleway-SemiBold;
font-size: 14px; font-size: 14px;
color: white; color: white;
text-shadow: 2px 2px 3px #000000;
height: 100%; height: 100%;
margin-right: 10px; margin-right: 10px;
width: 20%; }
.showShareButtonsButtonDiv > label {
text-shadow: 2px 2px 3px #000000;
} }
.showShareButton { .showShareButton {
width: 40px; width: 40px;
@ -193,23 +197,17 @@ input[type=button].naked:active {
background-color: white; background-color: white;
} }
.showShareButtonDots { .showShareButtonDots {
display: flex; display: block;
width: 32px; width: 40px;
height: 40px; height: 40px;
font-family: HiFi-Glyphs;
font-size: 60px;
position: absolute; position: absolute;
top: 5px; right: 20px;
right: 14px; bottom: 12px;
color: #00b4ef;
pointer-events: none; pointer-events: none;
} }
.showShareButtonDots > span {
width: 10px;
height: 10px;
margin: auto;
background-color: #0093C5;
border-radius: 50%;
border-width: 0;
display: inline;
}
/* /*
// END styling of share overlay // END styling of share overlay
*/ */

View file

@ -101,9 +101,11 @@ input[type=radio] {
opacity: 0; opacity: 0;
} }
input[type=radio] + label{ input[type=radio] + label{
display: inline-block; display: inline-block;
margin-left: -2em; margin-left: -2em;
line-height: 2em; line-height: 2em;
font-family: Raleway-SemiBold;
font-size: 14px;
} }
input[type=radio] + label > span{ input[type=radio] + label > span{
display: inline-block; display: inline-block;
@ -157,7 +159,6 @@ input[type=radio]:active + label > span > span{
border-width: 0px; border-width: 0px;
background-image: linear-gradient(#00B4EF, #1080B8); background-image: linear-gradient(#00B4EF, #1080B8);
min-height: 30px; min-height: 30px;
} }
.blueButton:hover { .blueButton:hover {
background-image: linear-gradient(#00B4EF, #00B4EF); background-image: linear-gradient(#00B4EF, #00B4EF);

View file

@ -52,7 +52,7 @@ function clearImages() {
imageCount = 0; imageCount = 0;
idCounter = 0; idCounter = 0;
} }
function addImage(image_data, isGifLoading, isShowingPreviousImages, canSharePreviousImages, hifiShareButtonsDisabled) { function addImage(image_data, isGifLoading, canShare, isShowingPreviousImages, blastButtonDisabled, hifiButtonDisabled) {
if (!image_data.localPath) { if (!image_data.localPath) {
return; return;
} }
@ -60,8 +60,9 @@ function addImage(image_data, isGifLoading, isShowingPreviousImages, canSharePre
// imageContainer setup // imageContainer setup
var imageContainer = document.createElement("DIV"); var imageContainer = document.createElement("DIV");
imageContainer.id = id; imageContainer.id = id;
imageContainer.style.width = "100%"; imageContainer.style.width = "95%";
imageContainer.style.height = "251px"; imageContainer.style.height = "240px";
imageContainer.style.margin = "5px auto";
imageContainer.style.display = "flex"; imageContainer.style.display = "flex";
imageContainer.style.justifyContent = "center"; imageContainer.style.justifyContent = "center";
imageContainer.style.alignItems = "center"; imageContainer.style.alignItems = "center";
@ -80,22 +81,22 @@ function addImage(image_data, isGifLoading, isShowingPreviousImages, canSharePre
if (isGif) { if (isGif) {
imageContainer.innerHTML += '<span class="gifLabel">GIF</span>'; imageContainer.innerHTML += '<span class="gifLabel">GIF</span>';
} }
if (!isGifLoading && !isShowingPreviousImages) { if (!isGifLoading && !isShowingPreviousImages && canShare) {
shareForUrl(id); shareForUrl(id);
} else if (isShowingPreviousImages && canSharePreviousImages) { } else if (isShowingPreviousImages && canShare && image_data.story_id) {
appendShareBar(id, image_data.story_id, isGif, hifiShareButtonsDisabled) appendShareBar(id, image_data.story_id, isGif, blastButtonDisabled, hifiButtonDisabled)
} }
} }
function appendShareBar(divID, story_id, isGif, hifiShareButtonsDisabled) { function appendShareBar(divID, story_id, isGif, blastButtonDisabled, hifiButtonDisabled) {
var story_url = "https://highfidelity.com/user_stories/" + story_id; var story_url = "https://highfidelity.com/user_stories/" + story_id;
var parentDiv = document.getElementById(divID); var parentDiv = document.getElementById(divID);
parentDiv.setAttribute('data-story-id', story_id); parentDiv.setAttribute('data-story-id', story_id);
document.getElementById(divID).appendChild(createShareBar(divID, isGif, story_url, hifiShareButtonsDisabled)); document.getElementById(divID).appendChild(createShareBar(divID, isGif, story_url, blastButtonDisabled, hifiButtonDisabled));
if (divID === "p0") { if (divID === "p0") {
selectImageToShare(divID, true); selectImageToShare(divID, true);
} }
} }
function createShareBar(parentID, isGif, shareURL, hifiShareButtonsDisabled) { function createShareBar(parentID, isGif, shareURL, blastButtonDisabled, hifiButtonDisabled) {
var shareBar = document.createElement("div"); var shareBar = document.createElement("div");
shareBar.id = parentID + "shareBar"; shareBar.id = parentID + "shareBar";
shareBar.className = "shareControls"; shareBar.className = "shareControls";
@ -109,8 +110,8 @@ function createShareBar(parentID, isGif, shareURL, hifiShareButtonsDisabled) {
var twitterButtonID = parentID + "twitterButton"; var twitterButtonID = parentID + "twitterButton";
shareBar.innerHTML += '' + shareBar.innerHTML += '' +
'<div class="shareButtons" id="' + shareButtonsDivID + '" style="visibility:hidden">' + '<div class="shareButtons" id="' + shareButtonsDivID + '" style="visibility:hidden">' +
'<input type="button"' + (hifiShareButtonsDisabled ? ' disabled' : '') + ' class="blastToConnections blueButton" id="' + blastToConnectionsButtonID + '" value="BLAST TO MY CONNECTIONS" onclick="blastToConnections(' + parentID + ', ' + isGif + ')" />' + '<input type="button"' + (blastButtonDisabled ? ' disabled' : '') + ' class="blastToConnections blueButton" id="' + blastToConnectionsButtonID + '" value="BLAST TO MY CONNECTIONS" onclick="blastToConnections(' + parentID + ', ' + isGif + ')" />' +
'<input type="button"' + (hifiShareButtonsDisabled ? ' disabled' : '') + ' class="shareWithEveryone" id="' + shareWithEveryoneButtonID + '" onclick="shareWithEveryone(' + parentID + ', ' + isGif + ')" />' + '<input type="button"' + (hifiButtonDisabled ? ' disabled' : '') + ' class="shareWithEveryone" id="' + shareWithEveryoneButtonID + '" onclick="shareWithEveryone(' + parentID + ', ' + isGif + ')" />' +
'<a class="facebookButton" id="' + facebookButtonID + '" onclick="shareButtonClicked(' + parentID + ')" target="_blank" href="https://www.facebook.com/dialog/feed?app_id=1585088821786423&link=' + shareURL + '"></a>' + '<a class="facebookButton" id="' + facebookButtonID + '" onclick="shareButtonClicked(' + parentID + ')" target="_blank" href="https://www.facebook.com/dialog/feed?app_id=1585088821786423&link=' + shareURL + '"></a>' +
'<a class="twitterButton" id="' + twitterButtonID + '" onclick="shareButtonClicked(' + parentID + ')" target="_blank" href="https://twitter.com/intent/tweet?text=I%20just%20took%20a%20snapshot!&url=' + shareURL + '&via=highfidelity&hashtags=VR,HiFi"></a>' + '<a class="twitterButton" id="' + twitterButtonID + '" onclick="shareButtonClicked(' + parentID + ')" target="_blank" href="https://twitter.com/intent/tweet?text=I%20just%20took%20a%20snapshot!&url=' + shareURL + '&via=highfidelity&hashtags=VR,HiFi"></a>' +
'</div>' + '</div>' +
@ -118,7 +119,7 @@ function createShareBar(parentID, isGif, shareURL, hifiShareButtonsDisabled) {
'<label id="' + showShareButtonsLabelID + '" for="' + showShareButtonsButtonID + '">SHARE</label>' + '<label id="' + showShareButtonsLabelID + '" for="' + showShareButtonsButtonID + '">SHARE</label>' +
'<input type="button" class="showShareButton inactive" id="' + showShareButtonsButtonID + '" onclick="selectImageToShare(' + parentID + ', true)" />' + '<input type="button" class="showShareButton inactive" id="' + showShareButtonsButtonID + '" onclick="selectImageToShare(' + parentID + ', true)" />' +
'<div class="showShareButtonDots">' + '<div class="showShareButtonDots">' +
'<span></span><span></span><span></span>' + '&#xe019;' +
'</div>' + '</div>' +
'</div>'; '</div>';
@ -149,6 +150,15 @@ function selectImageToShare(selectedID, isSelected) {
shareBar.style.backgroundColor = "rgba(0, 0, 0, 0.5)"; shareBar.style.backgroundColor = "rgba(0, 0, 0, 0.5)";
shareButtonsDiv.style.visibility = "visible"; shareButtonsDiv.style.visibility = "visible";
var containers = document.getElementsByClassName("shareControls");
var parentID;
for (var i = 0; i < containers.length; ++i) {
parentID = containers[i].id.slice(0, 2);
if (parentID !== selectedID) {
selectImageToShare(parentID, false);
}
}
} else { } else {
showShareButtonsButton.onclick = function () { selectImageToShare(selectedID, true) }; showShareButtonsButton.onclick = function () { selectImageToShare(selectedID, true) };
showShareButtonsButton.classList.remove("active"); showShareButtonsButton.classList.remove("active");
@ -173,7 +183,6 @@ function blastToConnections(selectedID, isGif) {
selectedID = selectedID.id; // `selectedID` is passed as an HTML object to these functions; we just want the ID selectedID = selectedID.id; // `selectedID` is passed as an HTML object to these functions; we just want the ID
document.getElementById(selectedID + "blastToConnectionsButton").disabled = true; document.getElementById(selectedID + "blastToConnectionsButton").disabled = true;
document.getElementById(selectedID + "shareWithEveryoneButton").disabled = true;
EventBridge.emitWebEvent(JSON.stringify({ EventBridge.emitWebEvent(JSON.stringify({
type: "snapshot", type: "snapshot",
@ -185,7 +194,6 @@ function blastToConnections(selectedID, isGif) {
function shareWithEveryone(selectedID, isGif) { function shareWithEveryone(selectedID, isGif) {
selectedID = selectedID.id; // `selectedID` is passed as an HTML object to these functions; we just want the ID selectedID = selectedID.id; // `selectedID` is passed as an HTML object to these functions; we just want the ID
document.getElementById(selectedID + "blastToConnectionsButton").disabled = true;
document.getElementById(selectedID + "shareWithEveryoneButton").disabled = true; document.getElementById(selectedID + "shareWithEveryoneButton").disabled = true;
EventBridge.emitWebEvent(JSON.stringify({ EventBridge.emitWebEvent(JSON.stringify({
@ -233,7 +241,7 @@ function handleCaptureSetting(setting) {
window.onload = function () { window.onload = function () {
// Uncomment the line below to test functionality in a browser. // Uncomment the line below to test functionality in a browser.
// See definition of "testInBrowser()" to modify tests. // See definition of "testInBrowser()" to modify tests.
//testInBrowser(true); //testInBrowser(false);
openEventBridge(function () { openEventBridge(function () {
// Set up a handler for receiving the data, and tell the .js we are ready to receive it. // Set up a handler for receiving the data, and tell the .js we are ready to receive it.
EventBridge.scriptEventReceived.connect(function (message) { EventBridge.scriptEventReceived.connect(function (message) {
@ -260,7 +268,7 @@ window.onload = function () {
var messageOptions = message.options; var messageOptions = message.options;
imageCount = message.image_data.length; imageCount = message.image_data.length;
message.image_data.forEach(function (element, idx, array) { message.image_data.forEach(function (element, idx, array) {
addImage(element, true, true, message.canShare, message.image_data[idx].buttonDisabled); addImage(element, true, message.canShare, true, message.image_data[idx].blastButtonDisabled, message.image_data[idx].hifiButtonDisabled);
}); });
break; break;
case 'addImages': case 'addImages':
@ -272,22 +280,24 @@ window.onload = function () {
if (messageOptions.containsGif) { if (messageOptions.containsGif) {
if (messageOptions.processingGif) { if (messageOptions.processingGif) {
imageCount = message.image_data.length + 1; // "+1" for the GIF that'll finish processing soon imageCount = message.image_data.length + 1; // "+1" for the GIF that'll finish processing soon
message.image_data.unshift({ localPath: messageOptions.loadingGifPath }); message.image_data.push({ localPath: messageOptions.loadingGifPath });
message.image_data.forEach(function (element, idx, array) { message.image_data.forEach(function (element, idx, array) {
addImage(element, idx === 0, false, false); addImage(element, idx === 1, idx === 0 && messageOptions.canShare, false);
}); });
} else { } else {
var gifPath = message.image_data[0].localPath; var gifPath = message.image_data[0].localPath;
var p0img = document.getElementById('p0img'); var p1img = document.getElementById('p1img');
p0img.src = gifPath; p1img.src = gifPath;
paths[0] = gifPath; paths[1] = gifPath;
shareForUrl("p0"); if (messageOptions.canShare) {
shareForUrl("p1");
}
} }
} else { } else {
imageCount = message.image_data.length; imageCount = message.image_data.length;
message.image_data.forEach(function (element, idx, array) { message.image_data.forEach(function (element, idx, array) {
addImage(element, false, false, false); addImage(element, false, messageOptions.canShare, false);
}); });
} }
break; break;
@ -296,7 +306,7 @@ window.onload = function () {
break; break;
case 'snapshotUploadComplete': case 'snapshotUploadComplete':
var isGif = message.image_url.split('.').pop().toLowerCase() === "gif"; var isGif = message.image_url.split('.').pop().toLowerCase() === "gif";
appendShareBar(isGif || imageCount === 1 ? "p0" : "p1", message.story_id, isGif); appendShareBar(isGif ? "p1" : "p0", message.story_id, isGif);
break; break;
default: default:
console.log("Unknown message action received in SnapshotReview.js."); console.log("Unknown message action received in SnapshotReview.js.");
@ -329,6 +339,7 @@ function testInBrowser(isTestingSetupInstructions) {
} else { } else {
imageCount = 1; imageCount = 1;
//addImage({ localPath: 'http://lorempixel.com/553/255' }); //addImage({ localPath: 'http://lorempixel.com/553/255' });
addImage({ localPath: 'C:/Users/valef/Desktop/hifi-snap-by-zfox-on-2017-04-26_10-26-53.gif' }, false, true, true, false); addImage({ localPath: 'C:/Users/valef/Desktop/hifi-snap-by-zfox-on-2017-05-01_15-48-15.gif' }, false, true, true, false, false);
addImage({ localPath: 'C:/Users/valef/Desktop/hifi-snap-by-zfox-on-2017-05-01_15-48-15.jpg' }, false, true, true, false, false);
} }
} }

View file

@ -519,11 +519,9 @@
function updateTriggers(value, fromKeyboard, hand) { function updateTriggers(value, fromKeyboard, hand) {
if (currentHand && hand !== currentHand) { if (currentHand && hand !== currentHand) {
debug("currentHand", currentHand, "ignoring messages from", hand); debug("currentHand", currentHand, "ignoring messages from", hand); // this can be a lot of spam on Touch. Should guard that someday.
return; return;
} }
currentHand = hand;
currentHandJointIndex = getIdealHandJointIndex(MyAvatar, handToString(currentHand)); // Always, in case of changed skeleton.
// ok now, we are either initiating or quitting... // ok now, we are either initiating or quitting...
var isGripping = value > GRIP_MIN; var isGripping = value > GRIP_MIN;
if (isGripping) { if (isGripping) {
@ -531,6 +529,8 @@
if (state !== STATES.INACTIVE) { if (state !== STATES.INACTIVE) {
return; return;
} }
currentHand = hand;
currentHandJointIndex = getIdealHandJointIndex(MyAvatar, handToString(currentHand)); // Always, in case of changed skeleton.
startHandshake(fromKeyboard); startHandshake(fromKeyboard);
} else { } else {
// TODO: should we end handshake even when inactive? Ponder // TODO: should we end handshake even when inactive? Ponder

View file

@ -117,13 +117,15 @@ function onMessage(message) {
setting: Settings.getValue("alsoTakeAnimatedSnapshot", true) setting: Settings.getValue("alsoTakeAnimatedSnapshot", true)
})); }));
if (Snapshot.getSnapshotsLocation() !== "") { if (Snapshot.getSnapshotsLocation() !== "") {
tablet.emitScriptEvent(JSON.stringify({ isDomainOpen(Settings.getValue("previousSnapshotDomainID"), function (canShare) {
type: "snapshot", tablet.emitScriptEvent(JSON.stringify({
action: "showPreviousImages", type: "snapshot",
options: snapshotOptions, action: "showPreviousImages",
image_data: imageData, options: snapshotOptions,
canShare: !isDomainOpen(Settings.getValue("previousSnapshotDomainID")) image_data: imageData,
})); canShare: canShare
}));
});
} else { } else {
tablet.emitScriptEvent(JSON.stringify({ tablet.emitScriptEvent(JSON.stringify({
type: "snapshot", type: "snapshot",
@ -131,10 +133,12 @@ function onMessage(message) {
})); }));
Settings.setValue("previousStillSnapPath", ""); Settings.setValue("previousStillSnapPath", "");
Settings.setValue("previousStillSnapStoryID", ""); Settings.setValue("previousStillSnapStoryID", "");
Settings.setValue("previousStillSnapSharingDisabled", false); Settings.setValue("previousStillSnapBlastingDisabled", false);
Settings.setValue("previousStillSnapHifiSharingDisabled", false);
Settings.setValue("previousAnimatedSnapPath", ""); Settings.setValue("previousAnimatedSnapPath", "");
Settings.setValue("previousAnimatedSnapStoryID", ""); Settings.setValue("previousAnimatedSnapStoryID", "");
Settings.setValue("previousAnimatedSnapSharingDisabled", false); Settings.setValue("previousAnimatedSnapBlastingDisabled", false);
Settings.setValue("previousAnimatedSnapHifiSharingDisabled", false);
} }
break; break;
case 'chooseSnapshotLocation': case 'chooseSnapshotLocation':
@ -180,9 +184,9 @@ function onMessage(message) {
isLoggedIn = Account.isLoggedIn(); isLoggedIn = Account.isLoggedIn();
storyIDsToMaybeDelete.splice(storyIDsToMaybeDelete.indexOf(message.story_id), 1); storyIDsToMaybeDelete.splice(storyIDsToMaybeDelete.indexOf(message.story_id), 1);
if (message.isGif) { if (message.isGif) {
Settings.setValue("previousAnimatedSnapSharingDisabled", true); Settings.setValue("previousAnimatedSnapBlastingDisabled", true);
} else { } else {
Settings.setValue("previousStillSnapSharingDisabled", true); Settings.setValue("previousStillSnapBlastingDisabled", true);
} }
if (isLoggedIn) { if (isLoggedIn) {
@ -220,9 +224,9 @@ function onMessage(message) {
if (error || (response.status !== 'success')) { if (error || (response.status !== 'success')) {
print("ERROR uploading announcement story: ", error || response.status); print("ERROR uploading announcement story: ", error || response.status);
if (message.isGif) { if (message.isGif) {
Settings.setValue("previousAnimatedSnapSharingDisabled", false); Settings.setValue("previousAnimatedSnapBlastingDisabled", false);
} else { } else {
Settings.setValue("previousStillSnapSharingDisabled", false); Settings.setValue("previousStillSnapBlastingDisabled", false);
} }
return; return;
} else { } else {
@ -240,9 +244,9 @@ function onMessage(message) {
isLoggedIn = Account.isLoggedIn(); isLoggedIn = Account.isLoggedIn();
storyIDsToMaybeDelete.splice(storyIDsToMaybeDelete.indexOf(message.story_id), 1); storyIDsToMaybeDelete.splice(storyIDsToMaybeDelete.indexOf(message.story_id), 1);
if (message.isGif) { if (message.isGif) {
Settings.setValue("previousAnimatedSnapSharingDisabled", true); Settings.setValue("previousAnimatedSnapHifiSharingDisabled", true);
} else { } else {
Settings.setValue("previousStillSnapSharingDisabled", true); Settings.setValue("previousStillSnapHifiSharingDisabled", true);
} }
if (isLoggedIn) { if (isLoggedIn) {
@ -264,9 +268,9 @@ function onMessage(message) {
if (error || (response.status !== 'success')) { if (error || (response.status !== 'success')) {
print("ERROR changing audience: ", error || response.status); print("ERROR changing audience: ", error || response.status);
if (message.isGif) { if (message.isGif) {
Settings.setValue("previousAnimatedSnapSharingDisabled", false); Settings.setValue("previousAnimatedSnapHifiSharingDisabled", false);
} else { } else {
Settings.setValue("previousStillSnapSharingDisabled", false); Settings.setValue("previousStillSnapHifiSharingDisabled", false);
} }
return; return;
} else { } else {
@ -301,21 +305,33 @@ function onButtonClicked() {
shouldActivateButton = true; shouldActivateButton = true;
var previousStillSnapPath = Settings.getValue("previousStillSnapPath"); var previousStillSnapPath = Settings.getValue("previousStillSnapPath");
var previousStillSnapStoryID = Settings.getValue("previousStillSnapStoryID"); var previousStillSnapStoryID = Settings.getValue("previousStillSnapStoryID");
var previousStillSnapSharingDisabled = Settings.getValue("previousStillSnapSharingDisabled"); var previousStillSnapBlastingDisabled = Settings.getValue("previousStillSnapBlastingDisabled");
var previousStillSnapHifiSharingDisabled = Settings.getValue("previousStillSnapHifiSharingDisabled");
var previousAnimatedSnapPath = Settings.getValue("previousAnimatedSnapPath"); var previousAnimatedSnapPath = Settings.getValue("previousAnimatedSnapPath");
var previousAnimatedSnapStoryID = Settings.getValue("previousAnimatedSnapStoryID"); var previousAnimatedSnapStoryID = Settings.getValue("previousAnimatedSnapStoryID");
var previousAnimatedSnapSharingDisabled = Settings.getValue("previousAnimatedSnapSharingDisabled"); var previousAnimatedSnapBlastingDisabled = Settings.getValue("previousAnimatedSnapBlastingDisabled");
var previousAnimatedSnapHifiSharingDisabled = Settings.getValue("previousAnimatedSnapHifiSharingDisabled");
snapshotOptions = { snapshotOptions = {
containsGif: previousAnimatedSnapPath !== "", containsGif: previousAnimatedSnapPath !== "",
processingGif: false, processingGif: false,
shouldUpload: false shouldUpload: false
} }
imageData = []; imageData = [];
if (previousAnimatedSnapPath !== "") {
imageData.push({ localPath: previousAnimatedSnapPath, story_id: previousAnimatedSnapStoryID, buttonDisabled: previousAnimatedSnapSharingDisabled });
}
if (previousStillSnapPath !== "") { if (previousStillSnapPath !== "") {
imageData.push({ localPath: previousStillSnapPath, story_id: previousStillSnapStoryID, buttonDisabled: previousStillSnapSharingDisabled }); imageData.push({
localPath: previousStillSnapPath,
story_id: previousStillSnapStoryID,
blastButtonDisabled: previousStillSnapBlastingDisabled,
hifiButtonDisabled: previousStillSnapHifiSharingDisabled
});
}
if (previousAnimatedSnapPath !== "") {
imageData.push({
localPath: previousAnimatedSnapPath,
story_id: previousAnimatedSnapStoryID,
blastButtonDisabled: previousAnimatedSnapBlastingDisabled,
hifiButtonDisabled: previousAnimatedSnapHifiSharingDisabled
});
} }
tablet.gotoWebScreen(SNAPSHOT_REVIEW_URL); tablet.gotoWebScreen(SNAPSHOT_REVIEW_URL);
tablet.webEventReceived.connect(onMessage); tablet.webEventReceived.connect(onMessage);
@ -355,10 +371,12 @@ function takeSnapshot() {
})); }));
Settings.setValue("previousStillSnapPath", ""); Settings.setValue("previousStillSnapPath", "");
Settings.setValue("previousStillSnapStoryID", ""); Settings.setValue("previousStillSnapStoryID", "");
Settings.setValue("previousStillSnapSharingDisabled", false); Settings.setValue("previousStillSnapBlastingDisabled", false);
Settings.setValue("previousStillSnapHifiSharingDisabled", false);
Settings.setValue("previousAnimatedSnapPath", ""); Settings.setValue("previousAnimatedSnapPath", "");
Settings.setValue("previousAnimatedSnapStoryID", ""); Settings.setValue("previousAnimatedSnapStoryID", "");
Settings.setValue("previousAnimatedSnapSharingDisabled", false); Settings.setValue("previousAnimatedSnapBlastingDisabled", false);
Settings.setValue("previousAnimatedSnapHifiSharingDisabled", false);
// Raising the desktop for the share dialog at end will interact badly with clearOverlayWhenMoving. // Raising the desktop for the share dialog at end will interact badly with clearOverlayWhenMoving.
// Turn it off now, before we start futzing with things (and possibly moving). // Turn it off now, before we start futzing with things (and possibly moving).
@ -377,6 +395,7 @@ function takeSnapshot() {
resetOverlays = Menu.isOptionChecked("Overlays"); // For completeness. Certainly true if the button is visible to be clicked. resetOverlays = Menu.isOptionChecked("Overlays"); // For completeness. Certainly true if the button is visible to be clicked.
reticleVisible = Reticle.visible; reticleVisible = Reticle.visible;
Reticle.visible = false; Reticle.visible = false;
Reticle.allowMouseCapture = false;
var includeAnimated = Settings.getValue("alsoTakeAnimatedSnapshot", true); var includeAnimated = Settings.getValue("alsoTakeAnimatedSnapshot", true);
if (includeAnimated) { if (includeAnimated) {
@ -403,37 +422,40 @@ function takeSnapshot() {
}, FINISH_SOUND_DELAY); }, FINISH_SOUND_DELAY);
} }
function isDomainOpen(id) { function isDomainOpen(id, callback) {
print("Checking open status of domain with ID:", id); print("Checking open status of domain with ID:", id);
if (!id) { var status = false;
return false; if (id) {
var options = [
'now=' + new Date().toISOString(),
'include_actions=concurrency',
'domain_id=' + id.slice(1, -1),
'restriction=open,hifi' // If we're sharing, we're logged in
// If we're here, protocol matches, and it is online
];
var url = METAVERSE_BASE + "/api/v1/user_stories?" + options.join('&');
request({
uri: url,
method: 'GET'
}, function (error, response) {
if (error || (response.status !== 'success')) {
print("ERROR getting open status of domain: ", error || response.status);
} else {
status = response.total_entries ? true : false;
}
print("Domain open status:", status);
callback(status);
});
} else {
callback(status);
} }
var options = [
'now=' + new Date().toISOString(),
'include_actions=concurrency',
'domain_id=' + id.slice(1, -1),
'restriction=open,hifi' // If we're sharing, we're logged in
// If we're here, protocol matches, and it is online
];
var url = METAVERSE_BASE + "/api/v1/user_stories?" + options.join('&');
return request({
uri: url,
method: 'GET'
}, function (error, response) {
if (error || (response.status !== 'success')) {
print("ERROR getting open status of domain: ", error || response.status);
return false;
} else {
return response.total_entries;
}
});
} }
function stillSnapshotTaken(pathStillSnapshot, notify) { function stillSnapshotTaken(pathStillSnapshot, notify) {
// show hud // show hud
Reticle.visible = reticleVisible; Reticle.visible = reticleVisible;
Reticle.allowMouseCapture = true;
// show overlays if they were on // show overlays if they were on
if (resetOverlays) { if (resetOverlays) {
Menu.setIsOptionChecked("Overlays", true); Menu.setIsOptionChecked("Overlays", true);
@ -448,25 +470,27 @@ function stillSnapshotTaken(pathStillSnapshot, notify) {
// during which time the user may have moved. So stash that info in the dialog so that // during which time the user may have moved. So stash that info in the dialog so that
// it records the correct href. (We can also stash in .jpegs, but not .gifs.) // it records the correct href. (We can also stash in .jpegs, but not .gifs.)
// last element in data array tells dialog whether we can share or not // last element in data array tells dialog whether we can share or not
snapshotOptions = {
containsGif: false,
processingGif: false,
canShare: !isDomainOpen(domainId)
};
imageData = [{ localPath: pathStillSnapshot, href: href }];
Settings.setValue("previousStillSnapPath", pathStillSnapshot); Settings.setValue("previousStillSnapPath", pathStillSnapshot);
tablet.emitScriptEvent(JSON.stringify({
type: "snapshot",
action: "addImages",
options: snapshotOptions,
image_data: imageData
}));
if (clearOverlayWhenMoving) { if (clearOverlayWhenMoving) {
MyAvatar.setClearOverlayWhenMoving(true); // not until after the share dialog MyAvatar.setClearOverlayWhenMoving(true); // not until after the share dialog
} }
HMD.openTablet(); HMD.openTablet();
isDomainOpen(domainId, function (canShare) {
snapshotOptions = {
containsGif: false,
processingGif: false,
canShare: canShare
};
imageData = [{ localPath: pathStillSnapshot, href: href }];
tablet.emitScriptEvent(JSON.stringify({
type: "snapshot",
action: "addImages",
options: snapshotOptions,
image_data: imageData
}));
});
} }
function processingGifStarted(pathStillSnapshot) { function processingGifStarted(pathStillSnapshot) {
@ -474,31 +498,33 @@ function processingGifStarted(pathStillSnapshot) {
Window.processingGifCompleted.connect(processingGifCompleted); Window.processingGifCompleted.connect(processingGifCompleted);
// show hud // show hud
Reticle.visible = reticleVisible; Reticle.visible = reticleVisible;
Reticle.allowMouseCapture = true;
// show overlays if they were on // show overlays if they were on
if (resetOverlays) { if (resetOverlays) {
Menu.setIsOptionChecked("Overlays", true); Menu.setIsOptionChecked("Overlays", true);
} }
snapshotOptions = {
containsGif: true,
processingGif: true,
loadingGifPath: Script.resolvePath(Script.resourcesPath() + 'icons/loadingDark.gif'),
canShare: !isDomainOpen(domainId)
};
imageData = [{ localPath: pathStillSnapshot, href: href }];
Settings.setValue("previousStillSnapPath", pathStillSnapshot); Settings.setValue("previousStillSnapPath", pathStillSnapshot);
tablet.emitScriptEvent(JSON.stringify({
type: "snapshot",
action: "addImages",
options: snapshotOptions,
image_data: imageData
}));
if (clearOverlayWhenMoving) { if (clearOverlayWhenMoving) {
MyAvatar.setClearOverlayWhenMoving(true); // not until after the share dialog MyAvatar.setClearOverlayWhenMoving(true); // not until after the share dialog
} }
HMD.openTablet(); HMD.openTablet();
isDomainOpen(domainId, function (canShare) {
snapshotOptions = {
containsGif: true,
processingGif: true,
loadingGifPath: Script.resolvePath(Script.resourcesPath() + 'icons/loadingDark.gif'),
canShare: canShare
};
imageData = [{ localPath: pathStillSnapshot, href: href }];
tablet.emitScriptEvent(JSON.stringify({
type: "snapshot",
action: "addImages",
options: snapshotOptions,
image_data: imageData
}));
});
} }
function processingGifCompleted(pathAnimatedSnapshot) { function processingGifCompleted(pathAnimatedSnapshot) {
@ -508,20 +534,22 @@ function processingGifCompleted(pathAnimatedSnapshot) {
buttonConnected = true; buttonConnected = true;
} }
snapshotOptions = {
containsGif: true,
processingGif: false,
canShare: !isDomainOpen(domainId)
}
imageData = [{ localPath: pathAnimatedSnapshot, href: href }];
Settings.setValue("previousAnimatedSnapPath", pathAnimatedSnapshot); Settings.setValue("previousAnimatedSnapPath", pathAnimatedSnapshot);
tablet.emitScriptEvent(JSON.stringify({ isDomainOpen(domainId, function (canShare) {
type: "snapshot", snapshotOptions = {
action: "addImages", containsGif: true,
options: snapshotOptions, processingGif: false,
image_data: imageData canShare: canShare
})); };
imageData = [{ localPath: pathAnimatedSnapshot, href: href }];
tablet.emitScriptEvent(JSON.stringify({
type: "snapshot",
action: "addImages",
options: snapshotOptions,
image_data: imageData
}));
});
} }
function maybeDeleteSnapshotStories() { function maybeDeleteSnapshotStories() {
storyIDsToMaybeDelete.forEach(function (element, idx, array) { storyIDsToMaybeDelete.forEach(function (element, idx, array) {
@ -554,12 +582,21 @@ function onUsernameChanged() {
shareAfterLogin = false; shareAfterLogin = false;
} }
} }
function snapshotLocationSet(location) {
if (location !== "") {
tablet.emitScriptEvent(JSON.stringify({
type: "snapshot",
action: "snapshotLocationChosen"
}));
}
}
button.clicked.connect(onButtonClicked); button.clicked.connect(onButtonClicked);
buttonConnected = true; buttonConnected = true;
Window.snapshotShared.connect(snapshotUploaded); Window.snapshotShared.connect(snapshotUploaded);
tablet.screenChanged.connect(onTabletScreenChanged); tablet.screenChanged.connect(onTabletScreenChanged);
Account.usernameChanged.connect(onUsernameChanged); Account.usernameChanged.connect(onUsernameChanged);
Snapshot.snapshotLocationSet.connect(snapshotLocationSet);
Script.scriptEnding.connect(function () { Script.scriptEnding.connect(function () {
if (buttonConnected) { if (buttonConnected) {
button.clicked.disconnect(onButtonClicked); button.clicked.disconnect(onButtonClicked);
@ -570,6 +607,7 @@ Script.scriptEnding.connect(function () {
} }
Window.snapshotShared.disconnect(snapshotUploaded); Window.snapshotShared.disconnect(snapshotUploaded);
tablet.screenChanged.disconnect(onTabletScreenChanged); tablet.screenChanged.disconnect(onTabletScreenChanged);
Snapshot.snapshotLocationSet.disconnect(snapshotLocationSet);
}); });
}()); // END LOCAL_SCOPE }()); // END LOCAL_SCOPE