Merge pull request from huffman/feat/tutorial-touch

Add touch support to tutorial
This commit is contained in:
Stephen Birarda 2016-12-16 17:12:22 -08:00 committed by GitHub
commit 59f6c8857d
31 changed files with 932 additions and 354 deletions

View file

@ -1389,16 +1389,31 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo
}
qCDebug(interfaceapp) << "Server content version: " << contentVersion;
bool hasTutorialContent = contentVersion >= 1;
static const int MIN_VIVE_CONTENT_VERSION = 1;
static const int MIN_OCULUS_TOUCH_CONTENT_VERSION = 27;
bool hasSufficientTutorialContent = false;
bool hasHandControllers = false;
// Only specific hand controllers are currently supported, so only send users to the tutorial
// if they have one of those hand controllers.
if (PluginUtils::isViveControllerAvailable()) {
hasHandControllers = true;
hasSufficientTutorialContent = contentVersion >= MIN_VIVE_CONTENT_VERSION;
} else if (PluginUtils::isOculusTouchControllerAvailable()) {
hasHandControllers = true;
hasSufficientTutorialContent = contentVersion >= MIN_OCULUS_TOUCH_CONTENT_VERSION;
}
Setting::Handle<bool> firstRun { Settings::firstRun, true };
bool hasHMDAndHandControllers = PluginUtils::isHMDAvailable("OpenVR (Vive)") && PluginUtils::isHandControllerAvailable();
bool hasHMDAndHandControllers = PluginUtils::isHMDAvailable() && hasHandControllers;
Setting::Handle<bool> tutorialComplete { "tutorialComplete", false };
bool shouldGoToTutorial = hasHMDAndHandControllers && hasTutorialContent && !tutorialComplete.get();
bool shouldGoToTutorial = hasHMDAndHandControllers && hasSufficientTutorialContent && !tutorialComplete.get();
qCDebug(interfaceapp) << "Has HMD + Hand Controllers: " << hasHMDAndHandControllers << ", current plugin: " << _displayPlugin->getName();
qCDebug(interfaceapp) << "Has tutorial content: " << hasTutorialContent;
qCDebug(interfaceapp) << "Has sufficient tutorial content (" << contentVersion << ") : " << hasSufficientTutorialContent;
qCDebug(interfaceapp) << "Tutorial complete: " << tutorialComplete.get();
qCDebug(interfaceapp) << "Should go to tutorial: " << shouldGoToTutorial;

View file

@ -49,12 +49,17 @@ glm::vec2 HMDScriptingInterface::overlayToSpherical(const glm::vec2 & position)
return qApp->getApplicationCompositor().overlayToSpherical(position);
}
bool HMDScriptingInterface::isHMDAvailable() {
return PluginUtils::isHMDAvailable();
bool HMDScriptingInterface::isHMDAvailable(const QString& name) {
return PluginUtils::isHMDAvailable(name);
}
bool HMDScriptingInterface::isHandControllerAvailable() {
return PluginUtils::isHandControllerAvailable();
bool HMDScriptingInterface::isHandControllerAvailable(const QString& name) {
return PluginUtils::isHandControllerAvailable(name);
}
bool HMDScriptingInterface::isSubdeviceContainingNameAvailable(const QString& name) {
return PluginUtils::isSubdeviceContainingNameAvailable(name);
}
void HMDScriptingInterface::requestShowHandControllers() {

View file

@ -38,8 +38,9 @@ public:
Q_INVOKABLE QString preferredAudioInput() const;
Q_INVOKABLE QString preferredAudioOutput() const;
Q_INVOKABLE bool isHMDAvailable();
Q_INVOKABLE bool isHandControllerAvailable();
Q_INVOKABLE bool isHMDAvailable(const QString& name = "");
Q_INVOKABLE bool isHandControllerAvailable(const QString& name = "");
Q_INVOKABLE bool isSubdeviceContainingNameAvailable(const QString& name);
Q_INVOKABLE void requestShowHandControllers();
Q_INVOKABLE void requestHideHandControllers();

View file

@ -25,14 +25,15 @@ void UserActivityLoggerScriptingInterface::toggledAway(bool isAway) {
}
void UserActivityLoggerScriptingInterface::tutorialProgress( QString stepName, int stepNumber, float secondsToComplete,
float tutorialElapsedTime, QString tutorialRunID, int tutorialVersion) {
float tutorialElapsedTime, QString tutorialRunID, int tutorialVersion, QString controllerType) {
logAction("tutorial_progress", {
{ "tutorial_run_id", tutorialRunID },
{ "tutorial_version", tutorialVersion },
{ "step", stepName },
{ "step_number", stepNumber },
{ "seconds_to_complete", secondsToComplete },
{ "tutorial_elapsed_seconds", tutorialElapsedTime }
{ "tutorial_elapsed_seconds", tutorialElapsedTime },
{ "controller_type", controllerType }
});
}

View file

@ -24,7 +24,7 @@ public:
Q_INVOKABLE void openedMarketplace();
Q_INVOKABLE void toggledAway(bool isAway);
Q_INVOKABLE void tutorialProgress(QString stepName, int stepNumber, float secondsToComplete,
float tutorialElapsedTime, QString tutorialRunID = "", int tutorialVersion = 0);
float tutorialElapsedTime, QString tutorialRunID = "", int tutorialVersion = 0, QString controllerType = "");
private:
void logAction(QString action, QJsonObject details = {});

View file

@ -24,16 +24,16 @@ bool PluginUtils::isHMDAvailable(const QString& pluginName) {
return false;
}
bool PluginUtils::isHandControllerAvailable() {
bool PluginUtils::isHandControllerAvailable(const QString& pluginName) {
for (auto& inputPlugin : PluginManager::getInstance()->getInputPlugins()) {
if (inputPlugin->isHandController()) {
if (inputPlugin->isHandController() && (pluginName.isEmpty() || inputPlugin->getName() == pluginName)) {
return true;
}
}
return false;
};
bool isSubdeviceContainingNameAvailable(QString name) {
bool PluginUtils::isSubdeviceContainingNameAvailable(QString name) {
for (auto& inputPlugin : PluginManager::getInstance()->getInputPlugins()) {
if (inputPlugin->isActive()) {
auto subdeviceNames = inputPlugin->getSubdeviceNames();

View file

@ -15,7 +15,8 @@
class PluginUtils {
public:
static bool isHMDAvailable(const QString& pluginName = "");
static bool isHandControllerAvailable();
static bool isHandControllerAvailable(const QString& pluginName = "");
static bool isSubdeviceContainingNameAvailable(QString name);
static bool isViveControllerAvailable();
static bool isOculusTouchControllerAvailable();
static bool isXboxControllerAvailable();

View file

@ -850,6 +850,12 @@ void Model::setTextures(const QVariantMap& textures) {
_needsUpdateTextures = true;
_needsFixupInScene = true;
_renderGeometry->setTextures(textures);
} else {
// FIXME(Huffman): Disconnect previously connected lambdas so we don't set textures multiple
// after the geometry has finished loading.
connect(&_renderWatcher, &GeometryResourceWatcher::finished, this, [this, textures]() {
_renderGeometry->setTextures(textures);
});
}
}

View file

@ -21,14 +21,17 @@ function clamp(value, min, max) {
}
function resolveHardware(path) {
var parts = path.split(".");
function resolveInner(base, path, i) {
if (i >= path.length) {
return base;
if (typeof path === 'string') {
var parts = path.split(".");
function resolveInner(base, path, i) {
if (i >= path.length) {
return base;
}
return resolveInner(base[path[i]], path, ++i);
}
return resolveInner(base[path[i]], path, ++i);
return resolveInner(Controller.Hardware, parts, 0);
}
return resolveInner(Controller.Hardware, parts, 0);
return path;
}
var DEBUG = true;
@ -132,7 +135,9 @@ createControllerDisplay = function(config) {
overlayID = Overlays.addOverlay("model", properties);
if (part.type === "rotational") {
mapping.from([part.input]).peek().to(function(controller, overlayID, part) {
var input = resolveHardware(part.input);
print("Mapping to: ", part.input, input);
mapping.from([input]).peek().to(function(controller, overlayID, part) {
return function(value) {
value = clamp(value, part.minValue, part.maxValue);
@ -157,18 +162,85 @@ createControllerDisplay = function(config) {
}(controller, overlayID, part));
} else if (part.type === "touchpad") {
var visibleInput = resolveHardware(part.visibleInput);
var xinput = resolveHardware(part.xInput);
var yinput = resolveHardware(part.yInput);
var xInput = resolveHardware(part.xInput);
var yInput = resolveHardware(part.yInput);
// TODO: Touchpad inputs are currently only working for half
// of the touchpad. When that is fixed, it would be useful
// to update these to display the current finger position.
mapping.from([visibleInput]).peek().to(function(value) {
});
mapping.from([xinput]).peek().to(function(value) {
mapping.from([xInput]).peek().to(function(value) {
});
mapping.from([yinput]).peek().invert().to(function(value) {
mapping.from([yInput]).peek().invert().to(function(value) {
});
} else if (part.type === "joystick") {
(function(controller, overlayID, part) {
const xInput = resolveHardware(part.xInput);
const yInput = resolveHardware(part.yInput);
var xvalue = 0;
var yvalue = 0;
function calculatePositionAndRotation(xValue, yValue) {
var rotation = Quat.fromPitchYawRollDegrees(yValue * part.xHalfAngle, 0, xValue * part.yHalfAngle);
var offset = { x: 0, y: 0, z: 0 };
if (part.originOffset) {
offset = Vec3.multiplyQbyV(rotation, part.originOffset);
offset = Vec3.subtract(part.originOffset, offset);
}
var partPosition = Vec3.sum(controller.position,
Vec3.multiplyQbyV(controller.rotation, Vec3.sum(offset, part.naturalPosition)));
var partRotation = Quat.multiply(controller.rotation, rotation)
return {
position: partPosition,
rotation: partRotation
}
}
mapping.from([xInput]).peek().to(function(value) {
xvalue = value;
//print(overlayID, xvalue.toFixed(3), yvalue.toFixed(3));
var posRot = calculatePositionAndRotation(xvalue, yvalue);
Overlays.editOverlay(overlayID, {
localPosition: posRot.position,
localRotation: posRot.rotation
});
});
mapping.from([yInput]).peek().to(function(value) {
yvalue = value;
var posRot = calculatePositionAndRotation(xvalue, yvalue);
Overlays.editOverlay(overlayID, {
localPosition: posRot.position,
localRotation: posRot.rotation
});
});
})(controller, overlayID, part);
} else if (part.type === "linear") {
(function(controller, overlayID, part) {
const input = resolveHardware(part.input);
mapping.from([input]).peek().to(function(value) {
//print(value);
var axis = Vec3.multiplyQbyV(controller.rotation, part.axis);
var offset = Vec3.multiply(part.maxTranslation * value, axis);
var partPosition = Vec3.sum(controller.position, Vec3.multiplyQbyV(controller.rotation, part.naturalPosition));
var position = Vec3.sum(partPosition, offset);
Overlays.editOverlay(overlayID, {
localPosition: position
});
});
})(controller, overlayID, part);
} else if (part.type === "static") {
// do nothing
} else {

View file

@ -15,6 +15,7 @@
Script.include("controllerDisplay.js");
Script.include("viveControllerConfiguration.js");
Script.include("touchControllerConfiguration.js");
var HIDE_CONTROLLERS_ON_EQUIP = false;
@ -41,12 +42,28 @@ ControllerDisplayManager = function() {
function updateControllers() {
if (HMD.active && HMD.shouldShowHandControllers()) {
var leftConfig = null;
var rightConfig = null;
if ("Vive" in Controller.Hardware) {
if (!controllerLeft) {
controllerLeft = createControllerDisplay(VIVE_CONTROLLER_CONFIGURATION_LEFT);
leftConfig = VIVE_CONTROLLER_CONFIGURATION_LEFT;
rightConfig = VIVE_CONTROLLER_CONFIGURATION_RIGHT;
}
if ("OculusTouch" in Controller.Hardware) {
leftConfig = TOUCH_CONTROLLER_CONFIGURATION_LEFT;
rightConfig = TOUCH_CONTROLLER_CONFIGURATION_RIGHT;
}
if (leftConfig !== null && rightConfig !== null) {
print("Loading controllers");
if (controllerLeft === null) {
controllerLeft = createControllerDisplay(leftConfig);
controllerLeft.setVisible(true);
}
if (!controllerRight) {
controllerRight = createControllerDisplay(VIVE_CONTROLLER_CONFIGURATION_RIGHT);
if (controllerRight === null) {
controllerRight = createControllerDisplay(rightConfig);
controllerRight.setVisible(true);
}
// We've found the controllers, we no longer need to look for active controllers
if (controllerCheckerIntervalID) {

View file

@ -0,0 +1,353 @@
//
// touchControllerConfiguration.js
//
// Created by Ryan Huffman on 12/06/16
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
/* globals TOUCH_CONTROLLER_CONFIGURATION_LEFT:true TOUCH_CONTROLLER_CONFIGURATION_RIGHT:true */
/* eslint camelcase: ["error", { "properties": "never" }] */
var leftBaseRotation = Quat.multiply(
Quat.fromPitchYawRollDegrees(-90, 0, 0),
Quat.fromPitchYawRollDegrees(0, 0, 90)
);
var rightBaseRotation = Quat.multiply(
Quat.fromPitchYawRollDegrees(-90, 0, 0),
Quat.fromPitchYawRollDegrees(0, 0, -90)
);
// keep these in sync with the values from OculusHelpers.cpp
var CONTROLLER_LENGTH_OFFSET = 0.0762;
var CONTROLLER_LATERAL_OFFSET = 0.0381;
var CONTROLLER_VERTICAL_OFFSET = 0.0381;
var CONTROLLER_FORWARD_OFFSET = 0.1524;
var leftBasePosition = Vec3.multiplyQbyV(leftBaseRotation, {
x: -CONTROLLER_LENGTH_OFFSET / 2.0,
y: CONTROLLER_LENGTH_OFFSET / 2.0,
z: CONTROLLER_LENGTH_OFFSET * 1.5
});
var rightBasePosition = Vec3.multiplyQbyV(rightBaseRotation, {
x: CONTROLLER_LENGTH_OFFSET / 2.0,
y: CONTROLLER_LENGTH_OFFSET / 2.0,
z: CONTROLLER_LENGTH_OFFSET * 1.5
});
var BASE_URL = Script.resourcesPath() + "meshes/controller/touch/";
TOUCH_CONTROLLER_CONFIGURATION_LEFT = {
name: "Touch",
controllers: [
{
modelURL: BASE_URL + "touch_l_body.fbx",
jointIndex: MyAvatar.getJointIndex("_CONTROLLER_LEFTHAND"),
naturalPosition: { x: 0.01648625358939171, y: -0.03551870584487915, z: -0.018527675420045853 },
dimensions: { x: 0.11053799837827682, y: 0.0995776429772377, z: 0.10139888525009155 },
rotation: leftBaseRotation,
position: leftBasePosition,
parts: {
tips: {
type: "static",
modelURL: BASE_URL + "Oculus-Labels-L.fbx",
naturalPosition: { x: -0.022335469722747803, y: 0.00022516027092933655, z: 0.020340695977211 },
textureName: "blank",
defaultTextureLayer: "blank",
textureLayers: {
blank: {
defaultTextureURL: BASE_URL + "Oculus-Labels-L.fbx/Oculus-Labels-L.fbm/Blank.png"
},
trigger: {
defaultTextureURL: BASE_URL + "Oculus-Labels-L.fbx/Oculus-Labels-L.fbm/Trigger.png"
},
arrows: {
defaultTextureURL: BASE_URL + "Oculus-Labels-L.fbx/Oculus-Labels-L.fbm/Rotate.png"
},
grip: {
defaultTextureURL: BASE_URL + "Oculus-Labels-L.fbx/Oculus-Labels-L.fbm/Grip-oculus.png"
},
teleport: {
defaultTextureURL: BASE_URL + "Oculus-Labels-L.fbx/Oculus-Labels-L.fbm/Teleport.png"
},
}
},
trigger: {
type: "rotational",
modelURL: BASE_URL + "touch_l_trigger.fbx",
naturalPosition: { x: 0.0008544912561774254, y: -0.019867943599820137, z: 0.018800459802150726 },
// rotational
input: Controller.Standard.LT,
origin: { x: 0, y: -0.015, z: -0.00 },
minValue: 0.0,
maxValue: 1.0,
axis: { x: 1, y: 0, z: 0 },
maxAngle: 17,
textureName: "tex-highlight",
defaultTextureLayer: "normal",
textureLayers: {
normal: {
defaultTextureURL: BASE_URL + "touch_l_trigger.fbx/touch_l_trigger.fbm/L_controller_DIF.jpg",
},
highlight: {
defaultTextureURL: BASE_URL + "touch_l_trigger.fbx/touch_l_trigger.fbm/L_controller-highlight_DIF.jpg",
}
}
},
grip: {
type: "linear",
modelURL: BASE_URL + "touch_l_bumper.fbx",
naturalPosition: { x: 0.00008066371083259583, y: -0.02715788595378399, z: -0.02448512241244316 },
// linear properties
// Offset from origin = 0.36470, 0.11048, 0.11066
input: "OculusTouch.LeftGrip",
axis: { x: 1, y: 0.302933918, z: 0.302933918 },
maxTranslation: 0.003967,
textureName: "tex-highlight",
defaultTextureLayer: "normal",
textureLayers: {
normal: {
defaultTextureURL: BASE_URL + "touch_l_bumper.fbx/touch_l_bumper.fbm/L_controller_DIF.jpg",
},
highlight: {
defaultTextureURL: BASE_URL + "touch_l_bumper.fbx/touch_l_bumper.fbm/L_controller-highlight_DIF.jpg",
}
}
},
joystick: {
type: "joystick",
modelURL: BASE_URL + "touch_l_joystick.fbx",
naturalPosition: { x: 0.0075613949447870255, y: -0.008225866593420506, z: 0.004792703315615654 },
// joystick
xInput: "OculusTouch.LX",
yInput: "OculusTouch.LY",
originOffset: { x: 0, y: -0.0028564, z: -0.00 },
xHalfAngle: 20,
yHalfAngle: 20,
textureName: "tex-highlight",
defaultTextureLayer: "normal",
textureLayers: {
normal: {
defaultTextureURL: BASE_URL + "touch_l_joystick.fbx/touch_l_joystick.fbm/L_controller_DIF.jpg",
},
highlight: {
defaultTextureURL: BASE_URL + "touch_l_joystick.fbx/touch_l_joystick.fbm/L_controller-highlight_DIF.jpg",
}
}
},
button_a: {
type: "linear",
modelURL: BASE_URL + "touch_l_button_x.fbx",
naturalPosition: { x: -0.009307309985160828, y: -0.00005015172064304352, z: -0.012594521045684814 },
input: "OculusTouch.X",
axis: { x: 0, y: -1, z: 0 },
maxTranslation: 0.001,
textureName: "tex-highlight",
defaultTextureLayer: "normal",
textureLayers: {
normal: {
defaultTextureURL: BASE_URL + "touch_l_button_x.fbx/touch_l_button_x.fbm/L_controller_DIF.jpg",
},
highlight: {
defaultTextureURL: BASE_URL + "touch_l_button_x.fbx/touch_l_button_x.fbm/L_controller-highlight_DIF.jpg",
}
}
},
button_b: {
type: "linear",
modelURL: BASE_URL + "touch_l_button_y.fbx",
naturalPosition: { x: -0.01616849936544895, y: -0.000050364527851343155, z: 0.0017703399062156677 },
input: "OculusTouch.Y",
axis: { x: 0, y: -1, z: 0 },
maxTranslation: 0.001,
textureName: "tex-highlight",
defaultTextureLayer: "normal",
textureLayers: {
normal: {
defaultTextureURL: BASE_URL + "touch_l_button_y.fbx/touch_l_button_y.fbm/L_controller_DIF.jpg",
},
highlight: {
defaultTextureURL: BASE_URL + "touch_l_button_y.fbx/touch_l_button_y.fbm/L_controller-highlight_DIF.jpg",
}
}
},
}
}
]
};
TOUCH_CONTROLLER_CONFIGURATION_RIGHT = {
name: "Touch",
controllers: [
{
modelURL: BASE_URL + "touch_r_body.fbx",
jointIndex: MyAvatar.getJointIndex("_CONTROLLER_RIGHTHAND"),
naturalPosition: { x: -0.016486231237649918, y: -0.03551865369081497, z: -0.018527653068304062 },
dimensions: { x: 0.11053784191608429, y: 0.09957750141620636, z: 0.10139875113964081 },
rotation: rightBaseRotation,
position: rightBasePosition,
parts: {
tips: {
type: "static",
modelURL: BASE_URL + "Oculus-Labels-R.fbx",
naturalPosition: { x: 0.009739525616168976, y: -0.0017818436026573181, z: 0.016794726252555847 },
textureName: "Texture",
defaultTextureLayer: "blank",
textureLayers: {
blank: {
defaultTextureURL: BASE_URL + "Oculus-Labels-R.fbx/Oculus-Labels-R.fbm/Blank.png"
},
trigger: {
defaultTextureURL: BASE_URL + "Oculus-Labels-R.fbx/Oculus-Labels-R.fbm/Trigger.png"
},
arrows: {
defaultTextureURL: BASE_URL + "Oculus-Labels-R.fbx/Oculus-Labels-R.fbm/Rotate.png"
},
grip: {
defaultTextureURL: BASE_URL + "Oculus-Labels-R.fbx/Oculus-Labels-R.fbm/Grip-oculus.png"
},
teleport: {
defaultTextureURL: BASE_URL + "Oculus-Labels-R.fbx/Oculus-Labels-R.fbm/Teleport.png"
},
}
},
trigger: {
type: "rotational",
modelURL: BASE_URL + "touch_r_trigger.fbx",
naturalPosition: { x: -0.0008544912561774254, y: -0.019867943599820137, z: 0.018800459802150726 },
// rotational
input: "OculusTouch.RT",
origin: { x: 0, y: -0.015, z: 0 },
minValue: 0.0,
maxValue: 1.0,
axis: { x: 1, y: 0, z: 0 },
maxAngle: 17,
textureName: "tex-highlight",
defaultTextureLayer: "normal",
textureLayers: {
normal: {
defaultTextureURL: BASE_URL + "touch_r_trigger.fbx/touch_r_trigger.fbm/R_controller_DIF.jpg",
},
highlight: {
defaultTextureURL: BASE_URL + "touch_r_trigger.fbx/touch_r_trigger.fbm/R_controller-highlight_DIF.jpg",
}
}
},
grip: {
type: "linear",
modelURL: BASE_URL + "touch_r_bumper.fbx",
naturalPosition: { x: -0.0000806618481874466, y: -0.027157839387655258, z: -0.024485092610120773 },
// linear properties
// Offset from origin = 0.36470, 0.11048, 0.11066
input: "OculusTouch.RightGrip",
axis: { x: -1, y: 0.302933918, z: 0.302933918 },
maxTranslation: 0.003967,
textureName: "tex-highlight",
defaultTextureLayer: "normal",
textureLayers: {
normal: {
defaultTextureURL: BASE_URL + "touch_r_bumper.fbx/touch_r_bumper.fbm/R_controller_DIF.jpg",
},
highlight: {
defaultTextureURL: BASE_URL + "touch_r_bumper.fbx/touch_r_bumper.fbm/R_controller-highlight_DIF.jpg",
}
}
},
joystick: {
type: "joystick",
modelURL: BASE_URL + "touch_r_joystick.fbx",
naturalPosition: { x: -0.007561382371932268, y: -0.008225853554904461, z: 0.00479268841445446 },
// joystick
xInput: "OculusTouch.RX",
yInput: "OculusTouch.RY",
originOffset: { x: 0, y: -0.0028564, z: 0 },
xHalfAngle: 20,
yHalfAngle: 20,
textureName: "tex-highlight",
defaultTextureLayer: "normal",
textureLayers: {
normal: {
defaultTextureURL: BASE_URL + "touch_r_joystick.fbx/touch_r_joystick.fbm/R_controller_DIF.jpg",
},
highlight: {
defaultTextureURL: BASE_URL + "touch_r_joystick.fbx/touch_r_joystick.fbm/R_controller-highlight_DIF.jpg",
}
}
},
button_a: {
type: "linear",
modelURL: BASE_URL + "touch_r_button_a.fbx",
naturalPosition: { x: 0.009307296946644783, y: -0.00005015172064304352, z: -0.012594504281878471 },
input: "OculusTouch.A",
axis: { x: 0, y: -1, z: 0 },
maxTranslation: 0.001,
textureName: "tex-highlight",
defaultTextureLayer: "normal",
textureLayers: {
normal: {
defaultTextureURL: BASE_URL + "touch_r_button_a.fbx/touch_r_button_a.fbm/R_controller_DIF.jpg",
},
highlight: {
defaultTextureURL: BASE_URL + "touch_r_button_a.fbx/touch_r_button_a.fbm/R_controller-highlight_DIF.jpg",
}
}
},
button_b: {
type: "linear",
modelURL: BASE_URL + "touch_r_button_b.fbx",
naturalPosition: { x: 0.01616847701370716, y: -0.000050364527851343155, z: 0.0017703361809253693 },
input: "OculusTouch.B",
axis: { x: 0, y: -1, z: 0 },
maxTranslation: 0.001,
textureName: "tex-highlight",
defaultTextureLayer: "normal",
textureLayers: {
normal: {
defaultTextureURL: BASE_URL + "touch_r_button_b.fbx/touch_r_button_b.fbm/R_controller_DIF.jpg",
},
highlight: {
defaultTextureURL: BASE_URL + "touch_r_button_b.fbx/touch_r_button_b.fbm/R_controller-highlight_DIF.jpg",
}
}
},
}
}
]
};

View file

@ -83,7 +83,6 @@ VIVE_CONTROLLER_CONFIGURATION_LEFT = {
naturalPosition: {"x":-0.004377640783786774,"y":-0.034371938556432724,"z":0.06769277155399323},
textureName: "Tex.Blank",
defaultTextureLayer: "blank",
textureLayers: {
blank: {

View file

@ -58,11 +58,6 @@ function info() {
}
}
// Return a number between min (inclusive) and max (exclusive)
function randomInt(min, max) {
return min + Math.floor(Math.random() * (max - min))
}
var NEAR_BOX_SPAWN_NAME = "tutorial/nearGrab/box_spawn";
var FAR_BOX_SPAWN_NAME = "tutorial/farGrab/box_spawn";
var GUN_SPAWN_NAME = "tutorial/gun_spawn";
@ -78,10 +73,6 @@ function setAwayEnabled(value) {
Messages.sendLocalMessage(CHANNEL_AWAY_ENABLE, message);
}
function beginsWithFilter(value, key) {
return value.indexOf(properties[key]) == 0;
}
findEntity = function(properties, searchRadius, filterFn) {
var entities = findEntities(properties, searchRadius, filterFn);
return entities.length > 0 ? entities[0] : null;
@ -116,32 +107,30 @@ findEntities = function(properties, searchRadius, filterFn) {
return matchedEntities;
}
function setControllerPartsVisible(parts) {
Messages.sendLocalMessage('Controller-Display-Parts', JSON.stringify(parts));
function findEntitiesWithTag(tag) {
return findEntities({ userData: "" }, 10000, function(properties, key, value) {
data = parseJSON(value);
return data.tag === tag;
});
}
/**
* A controller in made up of parts, and each part can have multiple "layers,"
* which are really just different texures. For example, the "trigger" part
* has "normal" and "highlight" layers.
*/
function setControllerPartLayer(part, layer) {
data = {};
data[part] = layer;
Messages.sendLocalMessage('Controller-Set-Part-Layer', JSON.stringify(data));
}
function triggerHapticPulse() {
function scheduleHaptics(delay, strength, duration) {
Script.setTimeout(function() {
Controller.triggerHapticPulse(strength, duration, 0);
Controller.triggerHapticPulse(strength, duration, 1);
}, delay);
}
scheduleHaptics(0, 0.8, 100);
scheduleHaptics(300, 0.5, 100);
scheduleHaptics(600, 0.3, 100);
scheduleHaptics(900, 0.2, 100);
scheduleHaptics(1200, 0.1, 100);
}
function spawn(entityData, transform, modifyFn) {
debug("Creating: ", entityData);
/**
* Spawn entities and return the newly created entity's ids.
* @param {object[]} entityPropertiesList A list of properties of the entities
* to spawn.
*/
function spawn(entityPropertiesList, transform, modifyFn) {
if (!transform) {
transform = {
position: { x: 0, y: 0, z: 0 },
@ -149,9 +138,8 @@ function spawn(entityData, transform, modifyFn) {
}
}
var ids = [];
for (var i = 0; i < entityData.length; ++i) {
var data = entityData[i];
debug("Creating: ", data.name);
for (var i = 0; i < entityPropertiesList.length; ++i) {
var data = entityPropertiesList[i];
data.position = Vec3.sum(transform.position, data.position);
data.rotation = Quat.multiply(data.rotation, transform.rotation);
if (modifyFn) {
@ -159,11 +147,16 @@ function spawn(entityData, transform, modifyFn) {
}
var id = Entities.addEntity(data);
ids.push(id);
debug(id, "data:", JSON.stringify(data));
}
return ids;
}
/**
* @function parseJSON
* @param {string} jsonString The string to parse.
* @return {object} Return an empty if the string was not valid JSON, otherwise
* the parsed object is returned.
*/
function parseJSON(jsonString) {
var data;
try {
@ -174,6 +167,10 @@ function parseJSON(jsonString) {
return data;
}
/**
* Spawn entities with `tag` in the userData.
* @function spawnWithTag
*/
function spawnWithTag(entityData, transform, tag) {
function modifyFn(data) {
var userData = parseJSON(data.userData);
@ -185,6 +182,10 @@ function spawnWithTag(entityData, transform, tag) {
return spawn(entityData, transform, modifyFn);
}
/**
* Delete all entities with the tag `tag` in their userData.
* @function deleteEntitiesWithTag
*/
function deleteEntitiesWithTag(tag) {
debug("searching for...:", tag);
var entityIDs = findEntitiesWithTag(tag);
@ -192,6 +193,7 @@ function deleteEntitiesWithTag(tag) {
Entities.deleteEntity(entityIDs[i]);
}
}
function editEntitiesWithTag(tag, propertiesOrFn) {
var entities = TUTORIAL_TAG_TO_ENTITY_IDS_MAP[tag];
@ -208,19 +210,130 @@ function editEntitiesWithTag(tag, propertiesOrFn) {
}
}
function findEntitiesWithTag(tag) {
return findEntities({ userData: "" }, 10000, function(properties, key, value) {
data = parseJSON(value);
return data.tag == tag;
});
}
// From http://stackoverflow.com/questions/5999998/how-can-i-check-if-a-javascript-variable-is-function-type
function isFunction(functionToCheck) {
var getType = {};
return functionToCheck && getType.toString.call(functionToCheck) === '[object Function]';
}
/**
* Return `true` if `entityID` can be found in the local entity tree, otherwise `false`.
*/
function isEntityInLocalTree(entityID) {
return Entities.getEntityProperties(entityID, 'visible').visible !== undefined;
}
/**
*
*/
function showEntitiesWithTags(tags) {
for (var i = 0; i < tags.length; ++i) {
showEntitiesWithTag(tags[i]);
}
}
function showEntitiesWithTag(tag) {
var entities = TUTORIAL_TAG_TO_ENTITY_IDS_MAP[tag];
if (entities) {
for (entityID in entities) {
var data = entities[entityID];
var collisionless = data.visible === false ? true : false;
if (data.collidable !== undefined) {
collisionless = data.collidable === true ? false : true;
}
if (data.soundKey) {
data.soundKey.playing = true;
}
var newProperties = {
visible: data.visible == false ? false : true,
collisionless: collisionless,
userData: JSON.stringify(data),
};
debug("Showing: ", entityID, ", Is in local tree: ", isEntityInLocalTree(entityID));
Entities.editEntity(entityID, newProperties);
}
} else {
debug("ERROR | No entities for tag: ", tag);
}
return;
// Dynamic method, suppressed for now
//editEntitiesWithTag(tag, function(entityID) {
// var userData = Entities.getEntityProperties(entityID, "userData").userData;
// var data = parseJSON(userData);
// var collisionless = data.visible === false ? true : false;
// if (data.collidable !== undefined) {
// collisionless = data.collidable === true ? false : true;
// }
// if (data.soundKey) {
// data.soundKey.playing = true;
// }
// var newProperties = {
// visible: data.visible == false ? false : true,
// collisionless: collisionless,
// userData: JSON.stringify(data),
// };
// Entities.editEntity(entityID, newProperties);
//});
}
function hideEntitiesWithTags(tags) {
for (var i = 0; i < tags.length; ++i) {
hideEntitiesWithTag(tags[i]);
}
}
function hideEntitiesWithTag(tag) {
var entities = TUTORIAL_TAG_TO_ENTITY_IDS_MAP[tag];
if (entities) {
for (entityID in entities) {
var data = entities[entityID];
if (data.soundKey) {
data.soundKey.playing = false;
}
var newProperties = {
visible: false,
collisionless: 1,
ignoreForCollisions: 1,
userData: JSON.stringify(data),
};
debug("Hiding: ", entityID, ", Is in local tree: ", isEntityInLocalTree(entityID));
Entities.editEntity(entityID, newProperties);
}
}
return;
// Dynamic method, suppressed for now
//editEntitiesWithTag(tag, function(entityID) {
// var userData = Entities.getEntityProperties(entityID, "userData").userData;
// var data = parseJSON(userData);
// if (data.soundKey) {
// data.soundKey.playing = false;
// }
// var newProperties = {
// visible: false,
// collisionless: 1,
// ignoreForCollisions: 1,
// userData: JSON.stringify(data),
// };
// Entities.editEntity(entityID, newProperties);
//});
}
/**
* Return the entity properties for an entity with a given name if it is in our
* cached list of entities. Otherwise, return undefined.
*/
function getEntityWithName(name) {
debug("Getting entity with name:", name);
var entityID = TUTORIAL_NAME_TO_ENTITY_PROPERTIES_MAP[name];
debug("Entity id: ", entityID, ", Is in local tree: ", isEntityInLocalTree(entityID));
return entityID;
}
function playSuccessSound() {
Audio.playSound(successSound, {
position: MyAvatar.position,
@ -229,7 +342,6 @@ function playSuccessSound() {
});
}
function playFirecrackerSound(position) {
Audio.playSound(firecrackerSound, {
position: position,
@ -238,27 +350,17 @@ function playFirecrackerSound(position) {
});
}
///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
// //
// STEP: DISABLE CONTROLLERS //
// //
///////////////////////////////////////////////////////////////////////////////
var stepStart = function(name) {
this.tag = name;
}
stepStart.prototype = {
start: function(onFinish) {
disableEverything();
HMD.requestShowHandControllers();
onFinish();
},
cleanup: function() {
}
};
/**
* This disables everything, including:
*
* - The door to leave the tutorial
* - Overlays
* - Hand controlelrs
* - Teleportation
* - Advanced movement
* - Equip and far grab
* - Away mode
*/
function disableEverything() {
editEntitiesWithTag('door', { visible: true, collisionless: false });
Menu.setIsOptionChecked("Overlays", false);
@ -271,6 +373,11 @@ function disableEverything() {
farGrabEnabled: false,
}));
setControllerPartLayer('touchpad', 'blank');
setControllerPartLayer('trigger', 'blank');
setControllerPartLayer('joystick', 'blank');
setControllerPartLayer('grip', 'blank');
setControllerPartLayer('button_a', 'blank');
setControllerPartLayer('button_b', 'blank');
setControllerPartLayer('tips', 'blank');
hideEntitiesWithTag('finish');
@ -278,6 +385,10 @@ function disableEverything() {
setAwayEnabled(false);
}
/**
* This reenables everything that disableEverything() disables. This can be
* used when leaving the tutorial to ensure that nothing is left disabled.
*/
function reenableEverything() {
editEntitiesWithTag('door', { visible: false, collisionless: true });
Menu.setIsOptionChecked("Overlays", true);
@ -290,11 +401,39 @@ function reenableEverything() {
farGrabEnabled: true,
}));
setControllerPartLayer('touchpad', 'blank');
setControllerPartLayer('trigger', 'blank');
setControllerPartLayer('joystick', 'blank');
setControllerPartLayer('grip', 'blank');
setControllerPartLayer('button_a', 'blank');
setControllerPartLayer('button_b', 'blank');
setControllerPartLayer('tips', 'blank');
MyAvatar.shouldRenderLocally = true;
setAwayEnabled(true);
}
///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
// //
// STEP: DISABLE CONTROLLERS //
// //
///////////////////////////////////////////////////////////////////////////////
var stepStart = function() {
this.name = 'start';
};
stepStart.prototype = {
start: function(onFinish) {
disableEverything();
HMD.requestShowHandControllers();
onFinish();
},
cleanup: function() {
}
};
///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
// //
@ -302,10 +441,9 @@ function reenableEverything() {
// //
///////////////////////////////////////////////////////////////////////////////
var stepEnableControllers = function(name) {
this.tag = name;
var stepEnableControllers = function() {
this.shouldLog = false;
}
};
stepEnableControllers.prototype = {
start: function(onFinish) {
reenableEverything();
@ -316,28 +454,6 @@ stepEnableControllers.prototype = {
}
};
///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
// //
// STEP: Welcome //
// //
///////////////////////////////////////////////////////////////////////////////
var stepWelcome = function(name) {
this.tag = name;
}
stepWelcome.prototype = {
start: function(onFinish) {
this.timerID = Script.setTimeout(onFinish, 8000);
showEntitiesWithTag(this.tag);
},
cleanup: function() {
if (this.timerID) {
Script.clearTimeout(this.timerID);
this.timerID = null;
}
hideEntitiesWithTag(this.tag);
}
};
///////////////////////////////////////////////////////////////////////////////
@ -346,9 +462,9 @@ stepWelcome.prototype = {
// STEP: Orient and raise hands above head //
// //
///////////////////////////////////////////////////////////////////////////////
var stepOrient = function(name) {
this.tag = name;
this.tempTag = name + "-temporary";
var stepOrient = function(tutorialManager) {
this.name = 'orient';
this.tags = ["orient", "orient-" + tutorialManager.controllerName];
}
stepOrient.prototype = {
start: function(onFinish) {
@ -357,7 +473,8 @@ stepOrient.prototype = {
var tag = this.tag;
// Spawn content set
editEntitiesWithTag(this.tag, { visible: true });
//editEntitiesWithTag(this.tag, { visible: true });
showEntitiesWithTags(this.tags);
this.checkIntervalID = null;
function checkForHandsAboveHead() {
@ -386,8 +503,8 @@ stepOrient.prototype = {
Script.clearInterval(this.checkIntervalID);
this.checkIntervalID = null;
}
editEntitiesWithTag(this.tag, { visible: false, collisionless: 1 });
deleteEntitiesWithTag(this.tempTag);
//editEntitiesWithTag(this.tag, { visible: false, collisionless: 1 });
hideEntitiesWithTags(this.tags);
}
};
@ -399,9 +516,10 @@ stepOrient.prototype = {
// STEP: Near Grab //
// //
///////////////////////////////////////////////////////////////////////////////
var stepNearGrab = function(name) {
this.tag = name;
this.tempTag = name + "-temporary";
var stepNearGrab = function() {
this.name = 'nearGrab';
this.tag = "nearGrab";
this.tempTag = "nearGrab-temporary";
this.birdIDs = [];
Messages.subscribe("Entity-Exploded");
@ -414,7 +532,6 @@ stepNearGrab.prototype = {
setControllerPartLayer('tips', 'trigger');
setControllerPartLayer('trigger', 'highlight');
var tag = this.tag;
// Spawn content set
showEntitiesWithTag(this.tag, { visible: true });
@ -457,6 +574,7 @@ stepNearGrab.prototype = {
setControllerPartLayer('tips', 'blank');
setControllerPartLayer('trigger', 'normal');
hideEntitiesWithTag(this.tag, { visible: false});
hideEntitiesWithTag('bothGrab', { visible: false});
deleteEntitiesWithTag(this.tempTag);
if (this.positionWatcher) {
this.positionWatcher.destroy();
@ -473,9 +591,10 @@ stepNearGrab.prototype = {
// STEP: Far Grab //
// //
///////////////////////////////////////////////////////////////////////////////
var stepFarGrab = function(name) {
this.tag = name;
this.tempTag = name + "-temporary";
var stepFarGrab = function() {
this.name = 'farGrab';
this.tag = "farGrab";
this.tempTag = "farGrab-temporary";
this.finished = true;
this.birdIDs = [];
@ -535,7 +654,6 @@ stepFarGrab.prototype = {
setControllerPartLayer('tips', 'blank');
setControllerPartLayer('trigger', 'normal');
hideEntitiesWithTag(this.tag, { visible: false});
hideEntitiesWithTag('bothGrab', { visible: false});
deleteEntitiesWithTag(this.tempTag);
if (this.positionWatcher) {
this.positionWatcher.destroy();
@ -576,11 +694,16 @@ PositionWatcher.prototype = {
// STEP: Equip //
// //
///////////////////////////////////////////////////////////////////////////////
var stepEquip = function(name) {
this.tag = name;
this.tagPart1 = name + "-part1";
this.tagPart2 = name + "-part2";
this.tempTag = name + "-temporary";
var stepEquip = function(tutorialManager) {
const controllerName = tutorialManager.controllerName;
this.name = 'equip';
this.tags = ["equip", "equip-" + controllerName];
this.tagsPart1 = ["equip-part1", "equip-part1-" + controllerName];
this.tagsPart2 = ["equip-part2", "equip-part2-" + controllerName];
this.tempTag = "equip-temporary";
this.PART1 = 0;
this.PART2 = 1;
this.PART3 = 2;
@ -593,6 +716,7 @@ stepEquip.prototype = {
start: function(onFinish) {
setControllerPartLayer('tips', 'trigger');
setControllerPartLayer('trigger', 'highlight');
Messages.sendLocalMessage('Hifi-Grab-Disable', JSON.stringify({
holdEnabled: true,
}));
@ -600,8 +724,8 @@ stepEquip.prototype = {
var tag = this.tag;
// Spawn content set
showEntitiesWithTag(this.tag);
showEntitiesWithTag(this.tagPart1);
showEntitiesWithTags(this.tags);
showEntitiesWithTags(this.tagsPart1);
this.currentPart = this.PART1;
@ -658,9 +782,10 @@ stepEquip.prototype = {
Script.setTimeout(function() {
debug("Equip | Starting part 3");
this.currentPart = this.PART3;
hideEntitiesWithTag(this.tagPart1);
showEntitiesWithTag(this.tagPart2);
hideEntitiesWithTags(this.tagsPart1);
showEntitiesWithTags(this.tagsPart2);
setControllerPartLayer('trigger', 'normal');
setControllerPartLayer('grip', 'highlight');
setControllerPartLayer('tips', 'grip');
Messages.subscribe('Hifi-Object-Manipulation');
debug("Equip | Finished starting part 3");
@ -687,16 +812,19 @@ stepEquip.prototype = {
}
setControllerPartLayer('tips', 'blank');
setControllerPartLayer('grip', 'normal');
setControllerPartLayer('trigger', 'normal');
this.stopWatchingGun();
this.currentPart = this.COMPLETE;
if (this.checkCollidesTimer) {
Script.clearInterval(this.checkCollidesTimer);
this.checkColllidesTimer = null;
}
hideEntitiesWithTag(this.tagPart1);
hideEntitiesWithTag(this.tagPart2);
hideEntitiesWithTag(this.tag);
hideEntitiesWithTags(this.tagsPart1);
hideEntitiesWithTags(this.tagsPart2);
hideEntitiesWithTags(this.tags);
deleteEntitiesWithTag(this.tempTag);
}
};
@ -710,9 +838,11 @@ stepEquip.prototype = {
// STEP: Turn Around //
// //
///////////////////////////////////////////////////////////////////////////////
var stepTurnAround = function(name) {
this.tag = name;
this.tempTag = name + "-temporary";
var stepTurnAround = function(tutorialManager) {
this.name = 'turnAround';
this.tags = ["turnAround", "turnAround-" + tutorialManager.controllerName];
this.tempTag = "turnAround-temporary";
this.onActionBound = this.onAction.bind(this);
this.numTimesSnapTurnPressed = 0;
@ -720,10 +850,11 @@ var stepTurnAround = function(name) {
}
stepTurnAround.prototype = {
start: function(onFinish) {
setControllerPartLayer('joystick', 'highlight');
setControllerPartLayer('touchpad', 'arrows');
setControllerPartLayer('tips', 'arrows');
showEntitiesWithTag(this.tag);
showEntitiesWithTags(this.tags);
this.numTimesSnapTurnPressed = 0;
this.numTimesSmoothTurnPressed = 0;
@ -776,13 +907,14 @@ stepTurnAround.prototype = {
} catch (e) {
}
setControllerPartLayer('joystick', 'normal');
setControllerPartLayer('touchpad', 'blank');
setControllerPartLayer('tips', 'blank');
if (this.interval) {
Script.clearInterval(this.interval);
}
hideEntitiesWithTag(this.tag);
hideEntitiesWithTags(this.tags);
deleteEntitiesWithTag(this.tempTag);
}
};
@ -796,12 +928,15 @@ stepTurnAround.prototype = {
// STEP: Teleport //
// //
///////////////////////////////////////////////////////////////////////////////
var stepTeleport = function(name) {
this.tag = name;
this.tempTag = name + "-temporary";
var stepTeleport = function(tutorialManager) {
this.name = 'teleport';
this.tags = ["teleport", "teleport-" + tutorialManager.controllerName];
this.tempTag = "teleport-temporary";
}
stepTeleport.prototype = {
start: function(onFinish) {
setControllerPartLayer('button_a', 'highlight');
setControllerPartLayer('touchpad', 'teleport');
setControllerPartLayer('tips', 'teleport');
@ -831,17 +966,18 @@ stepTeleport.prototype = {
}
this.checkCollidesTimer = Script.setInterval(checkCollides.bind(this), 500);
showEntitiesWithTag(this.tag);
showEntitiesWithTags(this.tags);
},
cleanup: function() {
debug("Teleport | Cleanup");
setControllerPartLayer('button_a', 'normal');
setControllerPartLayer('touchpad', 'blank');
setControllerPartLayer('tips', 'blank');
if (this.checkCollidesTimer) {
Script.clearInterval(this.checkCollidesTimer);
}
hideEntitiesWithTag(this.tag);
hideEntitiesWithTags(this.tags);
deleteEntitiesWithTag(this.tempTag);
}
};
@ -856,9 +992,11 @@ stepTeleport.prototype = {
// STEP: Finish //
// //
///////////////////////////////////////////////////////////////////////////////
var stepFinish = function(name) {
this.tag = name;
this.tempTag = name + "-temporary";
var stepFinish = function() {
this.name = 'finish';
this.tag = "finish";
this.tempTag = "finish-temporary";
}
stepFinish.prototype = {
start: function(onFinish) {
@ -872,6 +1010,8 @@ stepFinish.prototype = {
};
var stepCleanupFinish = function() {
this.name = 'cleanup';
this.shouldLog = false;
}
stepCleanupFinish.prototype = {
@ -885,101 +1025,6 @@ stepCleanupFinish.prototype = {
function isEntityInLocalTree(entityID) {
return Entities.getEntityProperties(entityID, 'visible').visible !== undefined;
}
function showEntitiesWithTag(tag) {
var entities = TUTORIAL_TAG_TO_ENTITY_IDS_MAP[tag];
if (entities) {
for (entityID in entities) {
var data = entities[entityID];
var collisionless = data.visible === false ? true : false;
if (data.collidable !== undefined) {
collisionless = data.collidable === true ? false : true;
}
if (data.soundKey) {
data.soundKey.playing = true;
}
var newProperties = {
visible: data.visible == false ? false : true,
collisionless: collisionless,
userData: JSON.stringify(data),
};
debug("Showing: ", entityID, ", Is in local tree: ", isEntityInLocalTree(entityID));
Entities.editEntity(entityID, newProperties);
}
}
// Dynamic method, suppressed for now
return;
editEntitiesWithTag(tag, function(entityID) {
var userData = Entities.getEntityProperties(entityID, "userData").userData;
var data = parseJSON(userData);
var collisionless = data.visible === false ? true : false;
if (data.collidable !== undefined) {
collisionless = data.collidable === true ? false : true;
}
if (data.soundKey) {
data.soundKey.playing = true;
}
var newProperties = {
visible: data.visible == false ? false : true,
collisionless: collisionless,
userData: JSON.stringify(data),
};
Entities.editEntity(entityID, newProperties);
});
}
function hideEntitiesWithTag(tag) {
var entities = TUTORIAL_TAG_TO_ENTITY_IDS_MAP[tag];
if (entities) {
for (entityID in entities) {
var data = entities[entityID];
if (data.soundKey) {
data.soundKey.playing = false;
}
var newProperties = {
visible: false,
collisionless: 1,
ignoreForCollisions: 1,
userData: JSON.stringify(data),
};
debug("Hiding: ", entityID, ", Is in local tree: ", isEntityInLocalTree(entityID));
Entities.editEntity(entityID, newProperties);
}
}
// Dynamic method, suppressed for now
return;
editEntitiesWithTag(tag, function(entityID) {
var userData = Entities.getEntityProperties(entityID, "userData").userData;
var data = parseJSON(userData);
if (data.soundKey) {
data.soundKey.playing = false;
}
var newProperties = {
visible: false,
collisionless: 1,
ignoreForCollisions: 1,
userData: JSON.stringify(data),
};
Entities.editEntity(entityID, newProperties);
});
}
// Return the entity properties for an entity with a given name if it is in our
// cached list of entities. Otherwise, return undefined.
function getEntityWithName(name) {
debug("Getting entity with name:", name);
var entityID = TUTORIAL_NAME_TO_ENTITY_PROPERTIES_MAP[name];
debug("Entity id: ", entityID, ", Is in local tree: ", isEntityInLocalTree(entityID));
return entityID;
}
TutorialManager = function() {
@ -992,11 +1037,25 @@ TutorialManager = function() {
var didFinishTutorial = false;
var wentToEntryStepNum;
var VERSION = 1;
var VERSION = 2;
var tutorialID;
var self = this;
// The real controller name is the actual detected controller name, or 'unknown'
// if one is not found.
if (HMD.isSubdeviceContainingNameAvailable("OculusTouch")) {
this.controllerName = "touch";
this.realControllerName = "touch";
} else if (HMD.isHandControllerAvailable("OpenVR")) {
this.controllerName = "vive";
this.realControllerName = "vive";
} else {
info("ERROR, no known hand controller found, defaulting to Vive");
this.controllerName = "vive";
this.realControllerName = "unknown";
}
this.startTutorial = function() {
currentStepNum = -1;
currentStep = null;
@ -1006,15 +1065,15 @@ TutorialManager = function() {
// If Script.generateUUID is not available, default to an empty string.
tutorialID = Script.generateUUID ? Script.generateUUID() : "";
STEPS = [
new stepStart("start"),
new stepOrient("orient"),
new stepNearGrab("nearGrab"),
new stepFarGrab("farGrab"),
new stepEquip("equip"),
new stepTurnAround("turnAround"),
new stepTeleport("teleport"),
new stepFinish("finish"),
new stepEnableControllers("enableControllers"),
new stepStart(this),
new stepOrient(this),
new stepFarGrab(this),
new stepNearGrab(this),
new stepEquip(this),
new stepTurnAround(this),
new stepTeleport(this),
new stepFinish(this),
new stepEnableControllers(this),
];
wentToEntryStepNum = STEPS.length;
for (var i = 0; i < STEPS.length; ++i) {
@ -1027,7 +1086,7 @@ TutorialManager = function() {
this.onFinish = function() {
debug("onFinish", currentStepNum);
if (currentStep && currentStep.shouldLog !== false) {
self.trackStep(currentStep.tag, currentStepNum);
self.trackStep(currentStep.name, currentStepNum);
}
self.startNextStep();
@ -1083,7 +1142,7 @@ TutorialManager = function() {
var tutorialTimeElapsed = (Date.now() - startedTutorialAt) / 1000;
UserActivityLogger.tutorialProgress(
name, stepNum, timeToFinishStep, tutorialTimeElapsed,
tutorialID, VERSION);
tutorialID, VERSION, this.realControllerName);
}
// This is a message sent from the "entry" portal in the courtyard,
@ -1099,6 +1158,25 @@ TutorialManager = function() {
// To run the tutorial:
//
// var tutorialManager = new TutorialManager();
// tutorialManager.startTutorial();
//var tutorialManager = new TutorialManager();
//tutorialManager.startTutorial();
//
//
//var keyReleaseHandler = function(event) {
// if (event.isShifted && event.isAlt) {
// print('here', event.text);
// if (event.text == "F12") {
// if (!tutorialManager.startNextStep()) {
// tutorialManager.startTutorial();
// }
// } else if (event.text == "F11") {
// tutorialManager.restartStep();
// } else if (event.text == "F10") {
// MyAvatar.shouldRenderLocally = !MyAvatar.shouldRenderLocally;
// } else if (event.text == "r") {
// tutorialManager.stopTutorial();
// tutorialManager.startTutorial();
// }
// }
//};
//Controller.keyReleaseEvent.connect(keyReleaseHandler);

View file

@ -1,8 +1,25 @@
TUTORIAL_TAG_TO_ENTITY_IDS_MAP = {
"teleport": {
"teleport-vive": {
"{7df1abc4-1b7c-4352-985c-f3f6ad8d65b7}": {
"tag": "teleport-vive"
}
},
"teleport-touch": {
"{ff064b9e-7fa4-4693-a386-a67b9f92a948}": {
"tag": "teleport"
},
"tag": "teleport-touch"
}
},
"turnAround-vive": {
"{9b14f224-b2f6-447f-bb86-f5d875cf4c33}": {
"tag": "turnAround-vive"
}
},
"turnAround-touch": {
"{ce74b3ca-d1c7-4980-bd98-2d488095a39e}": {
"tag": "turnAround-touch"
}
},
"teleport": {
"{4478f7b5-d3ac-4213-9a7b-ad8cd69575b8}": {
"tag": "teleport"
}
@ -82,17 +99,19 @@ TUTORIAL_TAG_TO_ENTITY_IDS_MAP = {
"tag": "equip-part1"
}
},
"equip-part2": {
"equip-part2-vive": {
"{b5d17eda-90ab-40cf-b973-efcecb2e992e}": {
"tag": "equip-part2"
},
"{6307cd16-dd1d-4988-a339-578178436b45}": {
"tag": "equip-part2"
"tag": "equip-part2-vive"
}
},
"turnAround": {
"{ce74b3ca-d1c7-4980-bd98-2d488095a39e}": {
"tag": "turnAround"
"equip-part2-touch": {
"{69195139-e020-4739-bb2c-50faebc6860a}": {
"tag": "equip-part2-touch"
}
},
"equip-part2": {
"{6307cd16-dd1d-4988-a339-578178436b45}": {
"tag": "equip-part2"
}
},
"bothGrab": {
@ -143,133 +162,144 @@ TUTORIAL_TAG_TO_ENTITY_IDS_MAP = {
"tag": "equip"
}
},
"orient": {
"orient-vive": {
"{95d233ab-ed0a-46e1-b047-1c542688ef3f}": {
"tag": "orient"
"tag": "orient-vive"
}
},
"orient-touch": {
"{1c95f945-ec46-4aac-b0f1-e64e073dbfaa}": {
"tag": "orient-touch"
}
}
};
TUTORIAL_NAME_TO_ENTITY_PROPERTIES_MAP = {
"tutorial/gun_spawn": {
"userData": "{\"tag\":\"equip\",\"visible\":false}",
"dimensions": {
"y": 0.0649842768907547,
"x": 0.0649842768907547,
"z": 0.0649842768907547
},
"clientOnly": 0,
"collisionless": 1,
"created": "2016-09-08T18:38:24Z",
"color": {
"blue": 0,
"green": 0,
"red": 255
},
"queryAACube": {
"y": 0.6283726096153259,
"x": 0.6865367293357849,
"scale": 0.11255607008934021,
"z": 0.3359576463699341
"created": "2016-09-08T18:38:24Z",
"dimensions": {
"x": 0.0649842768907547,
"y": 0.0649842768907547,
"z": 0.0649842768907547
},
"visible": 0,
"shape": "Cube",
"clientOnly": 0,
"id": "{9df518da-9e65-4b76-8a79-eeefdb0b7310}",
"ignoreForCollisions": 1,
"lastEdited": 1481926907366120,
"lastEditedBy": "{b80185ea-0936-4397-a5a4-3a64004f545f}",
"name": "tutorial/gun_spawn",
"owningAvatarID": "{00000000-0000-0000-0000-000000000000}",
"position": {
"y": 0.6846506595611572,
"x": 0.7428147792816162,
"z": 0.3922356963157654
"x": 0.60231781005859375,
"y": 0.68465065956115723,
"z": 0.39223569631576538
},
"queryAACube": {
"scale": 0.11255607008934021,
"x": 0.54603976011276245,
"y": 0.62837260961532593,
"z": 0.33595764636993408
},
"rotation": {
"y": 0.7066605091094971,
"x": 0.7066605091094971,
"z": -0.025131583213806152,
"w": -0.025101065635681152
"w": -0.025101065635681152,
"x": 0.70666050910949707,
"y": 0.70666050910949707,
"z": -0.025131583213806152
},
"ignoreForCollisions": 1,
"shape": "Cube",
"type": "Box",
"id": "{9df518da-9e65-4b76-8a79-eeefdb0b7310}",
"name": "tutorial/gun_spawn"
"userData": "{\"visible\":false,\"tag\":\"equip\"}",
"visible": 0
},
"tutorial/nearGrab/box_spawn": {
"userData": "{\"tag\":\"nearGrab\",\"visible\":false}",
"dimensions": {
"y": 0.08225371688604355,
"x": 0.08225371688604355,
"z": 0.08225371688604355
},
"clientOnly": 0,
"collisionless": 1,
"created": "2016-09-08T18:38:24Z",
"color": {
"blue": 255,
"green": 0,
"red": 255
},
"queryAACube": {
"y": 0.738319456577301,
"x": 0.8985498547554016,
"scale": 0.14246761798858643,
"z": 0.29067665338516235
"created": "2016-09-08T18:38:24Z",
"dimensions": {
"x": 0.082253716886043549,
"y": 0.082253716886043549,
"z": 0.082253716886043549
},
"visible": 0,
"shape": "Cube",
"clientOnly": 0,
"id": "{5cf22b9c-fb22-4854-8821-554422980b24}",
"ignoreForCollisions": 1,
"lastEdited": 1481926907334206,
"lastEditedBy": "{b80185ea-0936-4397-a5a4-3a64004f545f}",
"name": "tutorial/nearGrab/box_spawn",
"owningAvatarID": "{00000000-0000-0000-0000-000000000000}",
"position": {
"y": 0.8095532655715942,
"x": 0.9697836637496948,
"x": 0.61857688426971436,
"y": 0.80955326557159424,
"z": 0.36191046237945557
},
"rotation": {
"y": -1.52587890625e-05,
"x": -1.52587890625e-05,
"z": -1.52587890625e-05,
"w": 1
"queryAACube": {
"scale": 0.14246761798858643,
"x": 0.54734307527542114,
"y": 0.73831945657730103,
"z": 0.29067665338516235
},
"ignoreForCollisions": 1,
"rotation": {
"w": 1,
"x": -1.52587890625e-05,
"y": -1.52587890625e-05,
"z": -1.52587890625e-05
},
"shape": "Cube",
"type": "Box",
"id": "{5cf22b9c-fb22-4854-8821-554422980b24}",
"name": "tutorial/nearGrab/box_spawn"
"userData": "{\"visible\":false,\"tag\":\"nearGrab\"}",
"visible": 0
},
"tutorial/farGrab/box_spawn": {
"userData": "{\"tag\":\"farGrab\",\"visible\":false}",
"dimensions": {
"y": 0.37358683347702026,
"x": 0.37358683347702026,
"z": 0.37358683347702026
},
"clientOnly": 0,
"collisionless": 1,
"created": "2016-09-08T18:38:24Z",
"color": {
"blue": 255,
"green": 0,
"red": 255
},
"queryAACube": {
"y": 0.3304251432418823,
"x": 3.0951309204101562,
"scale": 0.647071361541748,
"z": 0.18027013540267944
"created": "2016-09-08T18:38:24Z",
"dimensions": {
"x": 0.16850528120994568,
"y": 0.16850528120994568,
"z": 0.16850528120994568
},
"visible": 0,
"shape": "Cube",
"clientOnly": 0,
"id": "{70fcd96c-cd59-4f23-9ca5-a167f2f85680}",
"ignoreForCollisions": 1,
"lastEdited": 1481926908795578,
"lastEditedBy": "{b80185ea-0936-4397-a5a4-3a64004f545f}",
"name": "tutorial/farGrab/box_spawn",
"owningAvatarID": "{00000000-0000-0000-0000-000000000000}",
"position": {
x: 3.4866,
y: 0.6716,
z: 0.4789
"x": 3.4866282939910889,
"y": 0.67159509658813477,
"z": 0.47892442345619202
},
"queryAACube": {
"scale": 0.64707136154174805,
"x": 3.2037394046783447,
"y": 0.33042514324188232,
"z": 0.14542555809020996
},
"rotation": {
"y": -1.52587890625e-05,
"w": 1,
"x": -1.52587890625e-05,
"z": -1.52587890625e-05,
"w": 1
"y": -1.52587890625e-05,
"z": -1.52587890625e-05
},
"ignoreForCollisions": 1,
"shape": "Cube",
"type": "Box",
"id": "{70fcd96c-cd59-4f23-9ca5-a167f2f85680}",
"name": "tutorial/farGrab/box_spawn"
"userData": "{\"visible\":false,\"tag\":\"farGrab\"}",
"visible": 0
},
"tutorial/teleport/pad": {
"userData": "{\"tag\":\"teleport\"}",

View file

@ -45,7 +45,7 @@ if (!Function.prototype.bind) {
keyReleaseHandler: function(event) {
print(event.text);
if (event.isShifted && event.isAlt) {
if (event.text == ",") {
if (event.text == "F12") {
if (!this.tutorialManager.startNextStep()) {
this.tutorialManager.startTutorial();
}