Integrated controller display from tutorial scripts + assets

This commit is contained in:
Anthony J. Thibault 2016-10-20 18:24:58 -07:00
parent 96445f4dee
commit 2193c5faad
21 changed files with 644 additions and 1 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 788 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 558 KiB

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 46 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 52 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

Binary file not shown.

View file

@ -37,6 +37,7 @@
#include <EntityScriptingInterface.h>
#include <MessagesClient.h>
#include <NetworkAccessManager.h>
#include <PathUtils.h>
#include <ResourceScriptingInterface.h>
#include <NodeList.h>
#include <udt/PacketHeaders.h>
@ -1136,6 +1137,10 @@ QUrl ScriptEngine::resolvePath(const QString& include) const {
return url;
}
QUrl ScriptEngine::resourcesPath() const {
return QUrl::fromLocalFile(PathUtils::resourcesPath());
}
void ScriptEngine::print(const QString& message) {
emit printedMessage(message);
}

View file

@ -133,6 +133,7 @@ public:
Q_INVOKABLE void clearTimeout(QObject* timer) { stopTimer(reinterpret_cast<QTimer*>(timer)); }
Q_INVOKABLE void print(const QString& message);
Q_INVOKABLE QUrl resolvePath(const QString& path) const;
Q_INVOKABLE QUrl resourcesPath() const;
// Entity Script Related methods
static void loadEntityScript(QWeakPointer<ScriptEngine> theEngine, const EntityItemID& entityID, const QString& entityScript, bool forceRedownload);

View file

@ -0,0 +1,186 @@
//
// controllerDisplay.js
//
// Created by Anthony J. Thibault on 10/20/16
// Originally created by Ryan Huffman on 9/21/2016
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
/* globals createControllerDisplay:true deleteControllerDisplay:true */
var PARENT_ID = "{00000000-0000-0000-0000-000000000001}";
function clamp(value, min, max) {
if (value < min) {
return min;
} else if (value > max) {
return max;
}
return value;
}
function resolveHardware(path) {
var parts = path.split(".");
function resolveInner(base, path, i) {
if (i >= path.length) {
return base;
}
return resolveInner(base[path[i]], path, ++i);
}
return resolveInner(Controller.Hardware, parts, 0);
}
createControllerDisplay = function(config) {
var controllerDisplay = {
overlays: [],
partOverlays: {},
parts: {},
mappingName: "mapping-display",
setVisible: function(visible) {
for (var i = 0; i < this.overlays.length; ++i) {
Overlays.editOverlay(this.overlays[i], {
visible: visible
});
}
},
setPartVisible: function(partName, visible) {
return;
if (partName in this.partOverlays) {
for (var i = 0; i < this.partOverlays[partName].length; ++i) {
Overlays.editOverlay(this.partOverlays[partName][i], {
//visible: visible
});
}
}
},
setLayerForPart: function(partName, layerName) {
if (partName in this.parts) {
var part = this.parts[partName];
if (part.textureLayers && layerName in part.textureLayers) {
var layer = part.textureLayers[layerName];
var textures = {};
if (layer.defaultTextureURL) {
textures[part.textureName] = layer.defaultTextureURL;
}
for (var i = 0; i < this.partOverlays[partName].length; ++i) {
Overlays.editOverlay(this.partOverlays[partName][i], {
textures: textures
});
}
}
}
}
};
var mapping = Controller.newMapping(controllerDisplay.mappingName);
for (var i = 0; i < config.controllers.length; ++i) {
var controller = config.controllers[i];
var position = controller.position;
if (controller.naturalPosition) {
position = Vec3.sum(Vec3.multiplyQbyV(controller.rotation, controller.naturalPosition), position);
}
var overlayID = Overlays.addOverlay("model", {
url: controller.modelURL,
dimensions: controller.dimensions,
localRotation: controller.rotation,
localPosition: position,
parentID: PARENT_ID,
parentJointIndex: controller.jointIndex,
ignoreRayIntersection: true
});
controllerDisplay.overlays.push(overlayID);
overlayID = null;
if (controller.parts) {
for (var partName in controller.parts) {
var part = controller.parts[partName];
var partPosition = Vec3.sum(controller.position, Vec3.multiplyQbyV(controller.rotation, part.naturalPosition));
var innerRotation = controller.rotation;
controllerDisplay.parts[partName] = controller.parts[partName];
var properties = {
url: part.modelURL,
localPosition: partPosition,
localRotation: innerRotation,
parentID: PARENT_ID,
parentJointIndex: controller.jointIndex,
ignoreRayIntersection: true
};
if (part.defaultTextureLayer) {
var textures = {};
textures[part.textureName] = part.textureLayers[part.defaultTextureLayer].defaultTextureURL;
properties['textures'] = textures;
}
overlayID = Overlays.addOverlay("model", properties);
if (part.type === "rotational") {
mapping.from([part.input]).peek().to(function(controller, overlayID, part) {
return function(value) {
value = clamp(value, part.minValue, part.maxValue);
var pct = (value - part.minValue) / part.maxValue;
var angle = pct * part.maxAngle;
var rotation = Quat.angleAxis(angle, part.axis);
var offset = { x: 0, y: 0, z: 0 };
if (part.origin) {
offset = Vec3.multiplyQbyV(rotation, part.origin);
offset = Vec3.subtract(offset, part.origin);
}
var partPosition = Vec3.sum(controller.position,
Vec3.multiplyQbyV(controller.rotation, Vec3.sum(offset, part.naturalPosition)));
Overlays.editOverlay(overlayID, {
localPosition: partPosition,
localRotation: Quat.multiply(controller.rotation, rotation)
});
};
}(controller, overlayID, part));
} else if (part.type === "touchpad") {
var visibleInput = resolveHardware(part.visibleInput);
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([yinput]).peek().invert().to(function(value) {
});
} else if (part.type === "static") {
// do nothing
} else {
print("TYPE NOT SUPPORTED: ", part.type);
}
controllerDisplay.overlays.push(overlayID);
if (!(partName in controllerDisplay.partOverlays)) {
controllerDisplay.partOverlays[partName] = [];
}
controllerDisplay.partOverlays[partName].push(overlayID);
}
}
}
Controller.enableMapping(controllerDisplay.mappingName);
return controllerDisplay;
};
deleteControllerDisplay = function(controllerDisplay) {
for (var i = 0; i < controllerDisplay.overlays.length; ++i) {
Overlays.deleteOverlay(controllerDisplay.overlays[i]);
}
Controller.disableMapping(controllerDisplay.mappingName);
};

View file

@ -0,0 +1,150 @@
//
// controllerDisplayManager.js
//
// Created by Anthony J. Thibault on 10/20/16
// Originally created by Ryan Huffman on 9/21/2016
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
/* globals ControllerDisplayManager:true createControllerDisplay deleteControllerDisplay
VIVE_CONTROLLER_CONFIGURATION_LEFT VIVE_CONTROLLER_CONFIGURATION_RIGHT */
Script.include("controllerDisplay.js");
Script.include("viveControllerConfiguration.js");
//
// Management of controller display
//
ControllerDisplayManager = function() {
var self = this;
var controllerLeft = null;
var controllerRight = null;
var controllerCheckerIntervalID = null;
this.setLeftVisible = function(visible) {
if (controllerLeft) {
controllerLeft.setVisible(visible);
}
};
this.setRightVisible = function(visible) {
if (controllerRight) {
controllerRight.setVisible(visible);
}
};
function updateControllers() {
if (HMD.active) {
if ("Vive" in Controller.Hardware) {
if (!controllerLeft) {
controllerLeft = createControllerDisplay(VIVE_CONTROLLER_CONFIGURATION_LEFT);
}
if (!controllerRight) {
controllerRight = createControllerDisplay(VIVE_CONTROLLER_CONFIGURATION_RIGHT);
}
// We've found the controllers, we no longer need to look for active controllers
if (controllerCheckerIntervalID) {
Script.clearInterval(controllerCheckerIntervalID);
controllerCheckerIntervalID = null;
}
} else {
self.deleteControllerDisplays();
if (!controllerCheckerIntervalID) {
controllerCheckerIntervalID = Script.setInterval(updateControllers, 1000);
}
}
} else {
// We aren't in HMD mode, we no longer need to look for active controllers
if (controllerCheckerIntervalID) {
Script.clearInterval(controllerCheckerIntervalID);
controllerCheckerIntervalID = null;
}
self.deleteControllerDisplays();
}
}
Messages.subscribe('Controller-Display');
var handleMessages = function(channel, message, sender) {
var i, data, name, visible;
if (!controllerLeft && !controllerRight) {
return;
}
if (sender === MyAvatar.sessionUUID) {
if (channel === 'Controller-Display') {
data = JSON.parse(message);
name = data.name;
visible = data.visible;
if (controllerLeft) {
if (name in controllerLeft.annotations) {
for (i = 0; i < controllerLeft.annotations[name].length; ++i) {
Overlays.editOverlay(controllerLeft.annotations[name][i], { visible: visible });
}
}
}
if (controllerRight) {
if (name in controllerRight.annotations) {
for (i = 0; i < controllerRight.annotations[name].length; ++i) {
Overlays.editOverlay(controllerRight.annotations[name][i], { visible: visible });
}
}
}
} else if (channel === 'Controller-Display-Parts') {
data = JSON.parse(message);
for (name in data) {
visible = data[name];
if (controllerLeft) {
controllerLeft.setPartVisible(name, visible);
}
if (controllerRight) {
controllerRight.setPartVisible(name, visible);
}
}
} else if (channel === 'Controller-Set-Part-Layer') {
data = JSON.parse(message);
for (name in data) {
var layer = data[name];
if (controllerLeft) {
controllerLeft.setLayerForPart(name, layer);
}
if (controllerRight) {
controllerRight.setLayerForPart(name, layer);
}
}
} else if (channel === 'Hifi-Object-Manipulation') {
data = JSON.parse(message);
visible = data.action !== 'equip';
if (data.joint === "LeftHand") {
self.setLeftVisible(visible);
} else if (data.joint === "RightHand") {
self.setRightVisible(visible);
}
}
}
};
Messages.messageReceived.connect(handleMessages);
this.deleteControllerDisplays = function() {
if (controllerLeft) {
deleteControllerDisplay(controllerLeft);
controllerLeft = null;
}
if (controllerRight) {
deleteControllerDisplay(controllerRight);
controllerRight = null;
}
};
this.destroy = function() {
Messages.messageReceived.disconnect(handleMessages);
self.deleteControllerDisplays();
};
HMD.displayModeChanged.connect(updateControllers);
updateControllers();
};
var controllerDisplayManager = new ControllerDisplayManager();

View file

@ -11,7 +11,10 @@
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
/* global setEntityCustomData, getEntityCustomData, flatten, Xform, Script, Quat, Vec3, MyAvatar, Entities, Overlays, Settings, Reticle, Controller, Camera, Messages, Mat4, getControllerWorldLocation, getGrabPointSphereOffset, setGrabCommunications */
/* global setEntityCustomData, getEntityCustomData, flatten, Xform, Script, Quat, Vec3, MyAvatar, Entities, Overlays, Settings,
Reticle, Controller, Camera, Messages, Mat4, getControllerWorldLocation, getGrabPointSphereOffset, setGrabCommunications */
/* eslint indent: ["error", 4, { "outerIIFEBody": 0 }] */
(function() { // BEGIN LOCAL_SCOPE

View file

@ -0,0 +1,298 @@
//
// viveControllerConfiguration.js
//
// Created by Anthony J. Thibault on 10/20/16
// Originally created by Ryan Huffman on 9/21/2016
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
/* globals VIVE_CONTROLLER_CONFIGURATION_LEFT:true VIVE_CONTROLLER_CONFIGURATION_RIGHT:true */
/* eslint camelcase: ["error", { "properties": "never" }] */
var LEFT_JOINT_INDEX = MyAvatar.getJointIndex("_CONTROLLER_LEFTHAND");
var RIGHT_JOINT_INDEX = MyAvatar.getJointIndex("_CONTROLLER_RIGHTHAND");
var leftBaseRotation = Quat.multiply(
Quat.fromPitchYawRollDegrees(0, 0, 45),
Quat.multiply(
Quat.fromPitchYawRollDegrees(90, 0, 0),
Quat.fromPitchYawRollDegrees(0, 0, 90)
)
);
var rightBaseRotation = Quat.multiply(
Quat.fromPitchYawRollDegrees(0, 0, -45),
Quat.multiply(
Quat.fromPitchYawRollDegrees(90, 0, 0),
Quat.fromPitchYawRollDegrees(0, 0, -90)
)
);
// keep these in sync with the values from plugins/openvr/src/OpenVrHelpers.cpp:303
var CONTROLLER_LATERAL_OFFSET = 0.0381;
var CONTROLLER_VERTICAL_OFFSET = 0.0495;
var CONTROLLER_FORWARD_OFFSET = 0.1371;
var leftBasePosition = {
x: CONTROLLER_VERTICAL_OFFSET,
y: CONTROLLER_FORWARD_OFFSET,
z: CONTROLLER_LATERAL_OFFSET
};
var rightBasePosition = {
x: -CONTROLLER_VERTICAL_OFFSET,
y: CONTROLLER_FORWARD_OFFSET,
z: CONTROLLER_LATERAL_OFFSET
};
var viveNaturalDimensions = {
x: 0.1174320001155138,
y: 0.08361100335605443,
z: 0.21942697931081057
};
var viveNaturalPosition = {
x: 0,
y: -0.034076502197422087,
z: 0.06380049744620919
};
var BASE_URL = Script.resourcesPath();
var viveModelURL = BASE_URL + "meshes/controller/vive_body.fbx";
var viveTipsModelURL = BASE_URL + "meshes/controller/vive_tips.fbx";
VIVE_CONTROLLER_CONFIGURATION_LEFT = {
name: "Vive",
controllers: [
{
modelURL: viveModelURL,
jointIndex: MyAvatar.getJointIndex("_CONTROLLER_LEFTHAND"),
naturalPosition: viveNaturalPosition,
rotation: leftBaseRotation,
position: Vec3.multiplyQbyV(Quat.fromPitchYawRollDegrees(0, 0, 45), leftBasePosition),
dimensions: viveNaturalDimensions,
parts: {
tips: {
type: "static",
modelURL: viveTipsModelURL,
naturalPosition: {"x":-0.004377640783786774,"y":-0.034371938556432724,"z":0.06769277155399323},
textureName: "Tex.Blank",
defaultTextureLayer: "blank",
textureLayers: {
blank: {
defaultTextureURL: viveTipsModelURL + "meshes/Controller-Tips.fbm/Blank.png"
},
trigger: {
defaultTextureURL: viveTipsModelURL + "meshes/Controller-Tips.fbm/Trigger.png"
},
arrows: {
defaultTextureURL: viveTipsModelURL + "meshes/Controller-Tips.fbm/Rotate.png"
},
grip: {
defaultTextureURL: viveTipsModelURL + "meshes/Controller-Tips.fbm/Grip.png"
},
teleport: {
defaultTextureURL: viveTipsModelURL + "meshes/Controller-Tips.fbm/Teleport.png"
}
}
},
// The touchpad type draws a dot indicating the current touch/thumb position
// and swaps in textures based on the thumb position.
touchpad: {
type: "touchpad",
modelURL: BASE_URL + "meshes/controller/vive_trackpad.fbx",
visibleInput: "Vive.RSTouch",
xInput: "Vive.LX",
yInput: "Vive.LY",
naturalPosition: {"x":0,"y":0.000979491975158453,"z":0.04872849956154823},
minValue: 0.0,
maxValue: 1.0,
minPosition: { x: -0.035, y: 0.004, z: -0.005 },
maxPosition: { x: -0.035, y: 0.004, z: -0.005 },
disable_textureName: "Tex.touchpad-blank",
disable_defaultTextureLayer: "blank",
disable_textureLayers: {
blank: {
defaultTextureURL: BASE_URL + "meshes/controller/vive_trackpad.fbx/Touchpad.fbm/touchpad-blank.jpg"
},
teleport: {
defaultTextureURL: BASE_URL + "meshes/controller/vive_trackpad.fbx/Touchpad.fbm/touchpad-teleport-active-LG.jpg"
},
arrows: {
defaultTextureURL: BASE_URL + "meshes/controller/vive_trackpad.fbx/Touchpad.fbm/touchpad-look-arrows.jpg"
}
}
},
trigger: {
type: "rotational",
modelURL: BASE_URL + "meshes/controller/vive_trigger.fbx",
input: Controller.Standard.LT,
naturalPosition: {"x":0.000004500150680541992,"y":-0.027690507471561432,"z":0.04830199480056763},
origin: { x: 0, y: -0.015, z: -0.00 },
minValue: 0.0,
maxValue: 1.0,
axis: { x: -1, y: 0, z: 0 },
maxAngle: 20
},
l_grip: {
type: "static",
modelURL: BASE_URL + "meshes/controller/vive_l_grip.fbx",
naturalPosition: {"x":-0.01720449887216091,"y":-0.014324013143777847,"z":0.08714400231838226}
},
r_grip: {
type: "static",
modelURL: BASE_URL + "meshes/controller/vive_r_grip.fbx",
naturalPosition: {"x":0.01720449887216091,"y":-0.014324013143777847,"z":0.08714400231838226}
},
sys_button: {
type: "static",
modelURL: BASE_URL + "meshes/controller/vive_sys_button.fbx",
naturalPosition: {"x":0,"y":0.0020399854984134436,"z":0.08825899660587311}
},
button: {
type: "static",
modelURL: BASE_URL + "meshes/controller/vive_button.fbx",
naturalPosition: {"x":0,"y":0.005480996798723936,"z":0.019918499514460564}
},
button2: {
type: "static",
modelURL: BASE_URL + "meshes/controller/vive_button.fbx",
naturalPosition: {"x":0,"y":0.005480996798723936,"z":0.019918499514460564}
}
}
}
]
};
VIVE_CONTROLLER_CONFIGURATION_RIGHT = {
name: "Vive Right",
controllers: [
{
modelURL: viveModelURL,
jointIndex: MyAvatar.getJointIndex("_CONTROLLER_RIGHTHAND"),
rotation: rightBaseRotation,
position: Vec3.multiplyQbyV(Quat.fromPitchYawRollDegrees(0, 0, -45), rightBasePosition),
dimensions: viveNaturalDimensions,
naturalPosition: {
x: 0,
y: -0.034076502197422087,
z: 0.06380049744620919
},
parts: {
tips: {
type: "static",
modelURL: viveTipsModelURL,
naturalPosition: {"x":-0.004377640783786774,"y":-0.034371938556432724,"z":0.06769277155399323},
textureName: "Tex.Blank",
defaultTextureLayer: "blank",
textureLayers: {
blank: {
defaultTextureURL: viveTipsModelURL + "meshes/Controller-Tips.fbm/Blank.png"
},
trigger: {
defaultTextureURL: viveTipsModelURL + "meshes/Controller-Tips.fbm/Trigger.png"
},
arrows: {
defaultTextureURL: viveTipsModelURL + "meshes/Controller-Tips.fbm/Rotate.png"
},
grip: {
defaultTextureURL: viveTipsModelURL + "meshes/Controller-Tips.fbm/Grip.png"
},
teleport: {
defaultTextureURL: viveTipsModelURL + "meshes/Controller-Tips.fbm/Teleport.png"
}
}
},
// The touchpad type draws a dot indicating the current touch/thumb position
// and swaps in textures based on the thumb position.
touchpad: {
type: "touchpad",
modelURL: BASE_URL + "meshes/controller/vive_trackpad.fbx",
visibleInput: "Vive.RSTouch",
xInput: "Vive.RX",
yInput: "Vive.RY",
naturalPosition: { x: 0, y: 0.000979491975158453, z: 0.04872849956154823 },
minValue: 0.0,
maxValue: 1.0,
minPosition: { x: -0.035, y: 0.004, z: -0.005 },
maxPosition: { x: -0.035, y: 0.004, z: -0.005 },
disable_textureName: "Tex.touchpad-blank",
disable_defaultTextureLayer: "blank",
disable_textureLayers: {
blank: {
defaultTextureURL: BASE_URL + "meshes/controller/vive_trackpad.fbx/Touchpad.fbm/touchpad-blank.jpg"
},
teleport: {
defaultTextureURL: BASE_URL + "meshes/controller/vive_trackpad.fbx/Touchpad.fbm/touchpad-teleport-active-LG.jpg"
},
arrows: {
defaultTextureURL: BASE_URL + "meshes/controller/vive_trackpad.fbx/Touchpad.fbm/touchpad-look-arrows-active.jpg"
}
}
},
trigger: {
type: "rotational",
modelURL: BASE_URL + "meshes/controller/vive_trigger.fbx",
input: Controller.Standard.RT,
naturalPosition: {"x":0.000004500150680541992,"y":-0.027690507471561432,"z":0.04830199480056763},
origin: { x: 0, y: -0.015, z: -0.00 },
minValue: 0.0,
maxValue: 1.0,
axis: { x: -1, y: 0, z: 0 },
maxAngle: 25
},
l_grip: {
type: "static",
modelURL: BASE_URL + "meshes/controller/vive_l_grip.fbx",
naturalPosition: {"x":-0.01720449887216091,"y":-0.014324013143777847,"z":0.08714400231838226}
},
r_grip: {
type: "static",
modelURL: BASE_URL + "meshes/controller/vive_r_grip.fbx",
naturalPosition: {"x":0.01720449887216091,"y":-0.014324013143777847,"z":0.08714400231838226}
},
sys_button: {
type: "static",
modelURL: BASE_URL + "meshes/controller/vive_sys_button.fbx",
naturalPosition: {"x":0,"y":0.0020399854984134436,"z":0.08825899660587311}
},
button: {
type: "static",
modelURL: BASE_URL + "meshes/controller/vive_button.fbx",
naturalPosition: {"x":0,"y":0.005480996798723936,"z":0.019918499514460564}
},
button2: {
type: "static",
modelURL: BASE_URL + "meshes/controller/vive_button.fbx",
naturalPosition: {"x":0,"y":0.005480996798723936,"z":0.019918499514460564}
}
}
}
]
};