300 lines
11 KiB
JavaScript
300 lines
11 KiB
JavaScript
//
|
|
// wearApp.js
|
|
//
|
|
// Created by Thijs Wenker on 11/17/17.
|
|
// 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
|
|
|
|
module.exports = (function() {
|
|
var APP_NAME = 'WEAR';
|
|
var WEAR_TUTORIAL_CHANNEL = 'com.highfidelity.wear.tutorialChannel';
|
|
var HTML_PATH = Script.resolvePath('html');
|
|
var APP_URL = HTML_PATH + '/wearApp.html';
|
|
var APP_ICON = HTML_PATH + '/img/WearAppIconWhite.svg';
|
|
var ATTACHMENT_SEARCH_RADIUS = 100; // meters (just in case)
|
|
var TUTORIAL_URLS = {
|
|
ADJUST: HTML_PATH + '/wearTutorialAdjust.html',
|
|
ADJUST_VR: HTML_PATH + '/wearTutorialAdjustVR.html',
|
|
ATTACH: HTML_PATH + '/wearTutorialAttach.html',
|
|
BUY: HTML_PATH + '/wearTutorialBuy.html'
|
|
};
|
|
|
|
var SETTING_RESET_FIRST_TIME_SETTINGS = 'com.highfidelity.wear.resetFirstTimeSettings';
|
|
var SETTING_STORE_ENTERED_FIRST_TIME = 'com.highfidelity.wear.storeEnteredFirstTime';
|
|
var SETTING_FIRST_TIME_USE_APP_DESKTOP = 'com.highfidelity.wear.firstTimeUseAppDesktop';
|
|
var SETTING_FIRST_TIME_USE_APP_VR = 'com.highfidelity.wear.firstTimeUseAppVR';
|
|
|
|
var isAppActive = false;
|
|
var isTutorialActive = false;
|
|
var activeTutorialURL = null;
|
|
var selectedAvatarEntity = null;
|
|
|
|
var tablet = Tablet.getTablet('com.highfidelity.interface.tablet.system');
|
|
var button = tablet.addButton({
|
|
text: APP_NAME,
|
|
icon: APP_ICON
|
|
});
|
|
|
|
// Check for first time settings being reset
|
|
// Console command for testing first time settings:
|
|
// Settings.setValue('com.highfidelity.wear.firstTimeUseApp', true);
|
|
if (Settings.getValue(SETTING_RESET_FIRST_TIME_SETTINGS, false)) {
|
|
Settings.setValue(SETTING_STORE_ENTERED_FIRST_TIME, false);
|
|
Settings.setValue(SETTING_FIRST_TIME_USE_APP_DESKTOP, true);
|
|
Settings.setValue(SETTING_FIRST_TIME_USE_APP_VR, true);
|
|
// Switch the reset option back off
|
|
Settings.setValue(SETTING_RESET_FIRST_TIME_SETTINGS, false);
|
|
}
|
|
|
|
var isEntityBeingWorn = function(entityID) {
|
|
return Entities.getEntityProperties(entityID, 'parentID').parentID === MyAvatar.sessionUUID;
|
|
};
|
|
|
|
var hasEnteredStoreForFirstTime = function() {
|
|
return Settings.getValue(SETTING_STORE_ENTERED_FIRST_TIME, false);
|
|
};
|
|
|
|
var isFirstTimeUseDesktop = function() {
|
|
return Settings.getValue(SETTING_FIRST_TIME_USE_APP_DESKTOP, true);
|
|
};
|
|
|
|
var isFirstTimeUseVR = function() {
|
|
return Settings.getValue(SETTING_FIRST_TIME_USE_APP_VR, true);
|
|
};
|
|
|
|
var getAttachedModelEntities = function() {
|
|
var resultEntities = [];
|
|
Entities.findEntitiesByType('Model', MyAvatar.position, ATTACHMENT_SEARCH_RADIUS).forEach(function(entityID) {
|
|
if (isEntityBeingWorn(entityID)) {
|
|
resultEntities.push(entityID);
|
|
}
|
|
});
|
|
return resultEntities;
|
|
};
|
|
|
|
var sendUpdate = function() {
|
|
var attachments = {};
|
|
getAttachedModelEntities().forEach(function(entityID) {
|
|
var properties = Entities.getEntityProperties(entityID, ['name', 'modelURL', 'parentJointIndex']);
|
|
|
|
var label;
|
|
if (properties.name.length > 0) {
|
|
label = properties.name;
|
|
} else {
|
|
label = properties.modelURL.split('/').pop();
|
|
}
|
|
|
|
attachments[entityID] = label + ' [' + MyAvatar.jointNames[properties.parentJointIndex] + ']';
|
|
});
|
|
|
|
var properties = null;
|
|
if (Object.keys(attachments).length > 0 && (selectedAvatarEntity === null || selectedAvatarEntity === '')) {
|
|
selectedAvatarEntity = Object.keys(attachments)[0];
|
|
}
|
|
|
|
if (selectedAvatarEntity) {
|
|
var entityProperties = Entities.getEntityProperties(selectedAvatarEntity, [
|
|
'localPosition', 'localRotation', 'dimensions', 'naturalDimensions']);
|
|
properties = {
|
|
position: entityProperties.localPosition,
|
|
rotation: Quat.safeEulerAngles(entityProperties.localRotation),
|
|
scale: entityProperties.dimensions.x / entityProperties.naturalDimensions.x
|
|
};
|
|
}
|
|
|
|
tablet.emitScriptEvent(JSON.stringify({
|
|
action: 'update',
|
|
attachments: attachments,
|
|
selectedAvatarEntity: selectedAvatarEntity,
|
|
properties: properties,
|
|
hmdActive: HMD.active
|
|
}));
|
|
};
|
|
|
|
var makeClientEntitiesGrabbable = function(grabbable) {
|
|
getAttachedModelEntities().forEach(function(entityID) {
|
|
var properties = Entities.getEntityProperties(entityID, ['clientOnly', 'userData']);
|
|
if (properties.clientOnly) {
|
|
var userData;
|
|
try {
|
|
userData = JSON.parse(properties.userData);
|
|
} catch (e) {
|
|
userData = {};
|
|
}
|
|
|
|
if (userData.grabbableKey === undefined) {
|
|
userData.grabbableKey = {};
|
|
}
|
|
userData.grabbableKey.grabbable = grabbable;
|
|
Entities.editEntity(entityID, {userData: JSON.stringify(userData)});
|
|
}
|
|
});
|
|
};
|
|
|
|
var onAddingEntity = function(entityID) {
|
|
if (isEntityBeingWorn(entityID)) {
|
|
sendUpdate();
|
|
}
|
|
};
|
|
|
|
var onWebEventReceived = function(data) {
|
|
if (!isAppActive && !isTutorialActive) {
|
|
// ignore web-events when the app and tutorial are inactive
|
|
return;
|
|
}
|
|
var dataObject = JSON.parse(data);
|
|
if (dataObject.action === 'gotIt') {
|
|
switch (activeTutorialURL) {
|
|
case TUTORIAL_URLS.ADJUST:
|
|
Settings.setValue(SETTING_FIRST_TIME_USE_APP_DESKTOP, false);
|
|
activateWearApp();
|
|
break;
|
|
case TUTORIAL_URLS.ADJUST_VR:
|
|
Settings.setValue(SETTING_FIRST_TIME_USE_APP_VR, false);
|
|
activateWearApp();
|
|
break;
|
|
case TUTORIAL_URLS.ATTACH:
|
|
Settings.setValue(SETTING_STORE_ENTERED_FIRST_TIME, true);
|
|
tablet.gotoHomeScreen();
|
|
HMD.closeTablet();
|
|
break;
|
|
case TUTORIAL_URLS.BUY:
|
|
tablet.gotoHomeScreen();
|
|
HMD.closeTablet();
|
|
break;
|
|
}
|
|
return;
|
|
}
|
|
if (dataObject.action === 'selectedAvatarEntityChanged') {
|
|
selectedAvatarEntity = dataObject.selectedAvatarEntity;
|
|
sendUpdate();
|
|
} else if (dataObject.action === 'appReady') {
|
|
sendUpdate();
|
|
} else if (dataObject.action === 'deleteEntity') {
|
|
Entities.deleteEntity(dataObject.selectedAvatarEntity);
|
|
sendUpdate();
|
|
} else if (dataObject.action === 'propertyUpdate') {
|
|
if (dataObject.position !== undefined) {
|
|
Entities.editEntity(selectedAvatarEntity, {
|
|
localPosition: dataObject.position
|
|
});
|
|
} else if (dataObject.rotation !== undefined) {
|
|
Entities.editEntity(selectedAvatarEntity, {
|
|
localRotation: Quat.fromVec3Degrees(dataObject.rotation)
|
|
});
|
|
} else if (dataObject.scale !== undefined) {
|
|
var naturalDimensions = Entities.getEntityProperties(selectedAvatarEntity,
|
|
'naturalDimensions').naturalDimensions;
|
|
Entities.editEntity(selectedAvatarEntity, {
|
|
dimensions: Vec3.multiply(naturalDimensions, dataObject.scale)
|
|
});
|
|
}
|
|
}
|
|
};
|
|
|
|
var activateWearApp = function() {
|
|
if (isAppActive) {
|
|
// skipping, app is already active
|
|
return;
|
|
}
|
|
tablet.gotoWebScreen(APP_URL);
|
|
Entities.addingEntity.connect(onAddingEntity);
|
|
Entities.clickReleaseOnEntity.connect(onClickReleaseOnEntity);
|
|
isAppActive = true;
|
|
if (HMD.active) {
|
|
makeClientEntitiesGrabbable(true);
|
|
}
|
|
};
|
|
|
|
var loadTutorial = function(tutorialURL) {
|
|
activeTutorialURL = tutorialURL;
|
|
tablet.gotoWebScreen(activeTutorialURL);
|
|
isTutorialActive = true;
|
|
};
|
|
|
|
var onTabletScreenChanged = function(type, url) {
|
|
if (isAppActive && url !== APP_URL) {
|
|
Entities.addingEntity.disconnect(onAddingEntity);
|
|
isAppActive = false;
|
|
if (HMD.active) {
|
|
makeClientEntitiesGrabbable(false);
|
|
}
|
|
}
|
|
if (isTutorialActive && url !== activeTutorialURL) {
|
|
isTutorialActive = false;
|
|
activeTutorialURL = null;
|
|
}
|
|
};
|
|
|
|
var onClickReleaseOnEntity = function(entityID) {
|
|
if (isEntityBeingWorn(entityID)) {
|
|
selectedAvatarEntity = entityID;
|
|
sendUpdate();
|
|
}
|
|
};
|
|
|
|
var onHmdChanged = function() {
|
|
if (isAppActive) {
|
|
sendUpdate();
|
|
makeClientEntitiesGrabbable(HMD.active);
|
|
}
|
|
};
|
|
|
|
var onMessageReceived = function(channel, message, sender) {
|
|
if (channel === WEAR_TUTORIAL_CHANNEL && sender === MyAvatar.sessionUUID) {
|
|
if (message === 'storeEnter' && !HMD.active && !hasEnteredStoreForFirstTime()) {
|
|
loadTutorial(TUTORIAL_URLS.ATTACH);
|
|
} else if (message === 'checkoutEnter' && !HMD.active) {
|
|
loadTutorial(TUTORIAL_URLS.BUY);
|
|
}
|
|
}
|
|
};
|
|
|
|
Messages.subscribe(WEAR_TUTORIAL_CHANNEL);
|
|
Messages.messageReceived.connect(onMessageReceived);
|
|
|
|
HMD.displayModeChanged.connect(onHmdChanged);
|
|
tablet.webEventReceived.connect(onWebEventReceived);
|
|
tablet.screenChanged.connect(onTabletScreenChanged);
|
|
|
|
button.clicked.connect(function() {
|
|
if (isAppActive) {
|
|
// skipping, app is already active
|
|
return;
|
|
}
|
|
|
|
if (HMD.active && isFirstTimeUseVR()) {
|
|
loadTutorial(TUTORIAL_URLS.ADJUST_VR);
|
|
return;
|
|
}
|
|
|
|
if (!HMD.active && isFirstTimeUseDesktop()) {
|
|
loadTutorial(TUTORIAL_URLS.ADJUST);
|
|
return;
|
|
}
|
|
|
|
activateWearApp();
|
|
});
|
|
|
|
var cleanUp = function() {
|
|
tablet.removeButton(button);
|
|
Messages.messageReceived.disconnect(onMessageReceived);
|
|
HMD.displayModeChanged.disconnect(onHmdChanged);
|
|
tablet.webEventReceived.disconnect(onWebEventReceived);
|
|
tablet.screenChanged.disconnect(onTabletScreenChanged);
|
|
Messages.unsubscribe(WEAR_TUTORIAL_CHANNEL);
|
|
if (isAppActive) {
|
|
if (HMD.active) {
|
|
makeClientEntitiesGrabbable(false);
|
|
}
|
|
Entities.addingEntity.disconnect(onAddingEntity);
|
|
Entities.clickReleaseOnEntity.disconnect(onClickReleaseOnEntity);
|
|
}
|
|
};
|
|
|
|
return {
|
|
cleanUp: cleanUp
|
|
};
|
|
});
|