diff --git a/interface/resources/meshes/controller/vive_body.fbm/onepointfive_spec.png b/interface/resources/meshes/controller/vive_body.fbm/onepointfive_spec.png new file mode 100644 index 0000000000..eedf0470a5 Binary files /dev/null and b/interface/resources/meshes/controller/vive_body.fbm/onepointfive_spec.png differ diff --git a/interface/resources/meshes/controller/vive_body.fbm/onepointfive_texture.png b/interface/resources/meshes/controller/vive_body.fbm/onepointfive_texture.png new file mode 100644 index 0000000000..5deca5c7bc Binary files /dev/null and b/interface/resources/meshes/controller/vive_body.fbm/onepointfive_texture.png differ diff --git a/interface/resources/meshes/controller/vive_body.fbx b/interface/resources/meshes/controller/vive_body.fbx new file mode 100644 index 0000000000..91fc1a05f4 Binary files /dev/null and b/interface/resources/meshes/controller/vive_body.fbx differ diff --git a/interface/resources/meshes/controller/vive_button.fbx b/interface/resources/meshes/controller/vive_button.fbx new file mode 100644 index 0000000000..fb2f108ed1 Binary files /dev/null and b/interface/resources/meshes/controller/vive_button.fbx differ diff --git a/interface/resources/meshes/controller/vive_l_grip.fbx b/interface/resources/meshes/controller/vive_l_grip.fbx new file mode 100644 index 0000000000..833fdae663 Binary files /dev/null and b/interface/resources/meshes/controller/vive_l_grip.fbx differ diff --git a/interface/resources/meshes/controller/vive_r_grip.fbx b/interface/resources/meshes/controller/vive_r_grip.fbx new file mode 100644 index 0000000000..ae3957b830 Binary files /dev/null and b/interface/resources/meshes/controller/vive_r_grip.fbx differ diff --git a/interface/resources/meshes/controller/vive_sys_button.fbx b/interface/resources/meshes/controller/vive_sys_button.fbx new file mode 100644 index 0000000000..c72bb4346c Binary files /dev/null and b/interface/resources/meshes/controller/vive_sys_button.fbx differ diff --git a/interface/resources/meshes/controller/vive_tips.fbm/Blank.png b/interface/resources/meshes/controller/vive_tips.fbm/Blank.png new file mode 100644 index 0000000000..ff20ac638e Binary files /dev/null and b/interface/resources/meshes/controller/vive_tips.fbm/Blank.png differ diff --git a/interface/resources/meshes/controller/vive_tips.fbm/Grip.png b/interface/resources/meshes/controller/vive_tips.fbm/Grip.png new file mode 100644 index 0000000000..f23c74c686 Binary files /dev/null and b/interface/resources/meshes/controller/vive_tips.fbm/Grip.png differ diff --git a/interface/resources/meshes/controller/vive_tips.fbm/Rotate.png b/interface/resources/meshes/controller/vive_tips.fbm/Rotate.png new file mode 100644 index 0000000000..7fe7dcc3f7 Binary files /dev/null and b/interface/resources/meshes/controller/vive_tips.fbm/Rotate.png differ diff --git a/interface/resources/meshes/controller/vive_tips.fbm/Teleport.png b/interface/resources/meshes/controller/vive_tips.fbm/Teleport.png new file mode 100644 index 0000000000..fbffd7eea4 Binary files /dev/null and b/interface/resources/meshes/controller/vive_tips.fbm/Teleport.png differ diff --git a/interface/resources/meshes/controller/vive_tips.fbm/Trigger.png b/interface/resources/meshes/controller/vive_tips.fbm/Trigger.png new file mode 100644 index 0000000000..d66c6da3bf Binary files /dev/null and b/interface/resources/meshes/controller/vive_tips.fbm/Trigger.png differ diff --git a/interface/resources/meshes/controller/vive_tips.fbx b/interface/resources/meshes/controller/vive_tips.fbx new file mode 100644 index 0000000000..f363f442f2 Binary files /dev/null and b/interface/resources/meshes/controller/vive_tips.fbx differ diff --git a/interface/resources/meshes/controller/vive_trackpad.fbx b/interface/resources/meshes/controller/vive_trackpad.fbx new file mode 100644 index 0000000000..8202fc2a19 Binary files /dev/null and b/interface/resources/meshes/controller/vive_trackpad.fbx differ diff --git a/interface/resources/meshes/controller/vive_trigger.fbx b/interface/resources/meshes/controller/vive_trigger.fbx new file mode 100644 index 0000000000..f6178bcaa2 Binary files /dev/null and b/interface/resources/meshes/controller/vive_trigger.fbx differ diff --git a/libraries/script-engine/src/ScriptEngine.cpp b/libraries/script-engine/src/ScriptEngine.cpp index a9e1defabe..776c7cfec6 100644 --- a/libraries/script-engine/src/ScriptEngine.cpp +++ b/libraries/script-engine/src/ScriptEngine.cpp @@ -37,6 +37,7 @@ #include #include #include +#include #include #include #include @@ -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); } diff --git a/libraries/script-engine/src/ScriptEngine.h b/libraries/script-engine/src/ScriptEngine.h index 62db99a431..9fc3de1c9e 100644 --- a/libraries/script-engine/src/ScriptEngine.h +++ b/libraries/script-engine/src/ScriptEngine.h @@ -133,6 +133,7 @@ public: Q_INVOKABLE void clearTimeout(QObject* timer) { stopTimer(reinterpret_cast(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 theEngine, const EntityItemID& entityID, const QString& entityScript, bool forceRedownload); diff --git a/scripts/system/controllers/controllerDisplay.js b/scripts/system/controllers/controllerDisplay.js new file mode 100644 index 0000000000..c25dfa25e0 --- /dev/null +++ b/scripts/system/controllers/controllerDisplay.js @@ -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); +}; diff --git a/scripts/system/controllers/controllerDisplayManager.js b/scripts/system/controllers/controllerDisplayManager.js new file mode 100644 index 0000000000..999b6b3257 --- /dev/null +++ b/scripts/system/controllers/controllerDisplayManager.js @@ -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(); diff --git a/scripts/system/controllers/handControllerGrab.js b/scripts/system/controllers/handControllerGrab.js index db70c01d12..1ffa993d01 100644 --- a/scripts/system/controllers/handControllerGrab.js +++ b/scripts/system/controllers/handControllerGrab.js @@ -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 diff --git a/scripts/system/controllers/viveControllerConfiguration.js b/scripts/system/controllers/viveControllerConfiguration.js new file mode 100644 index 0000000000..ed61bb1be6 --- /dev/null +++ b/scripts/system/controllers/viveControllerConfiguration.js @@ -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} + } + } + } + ] +}; +