//// controllerDisplay.js//// Created by Anthony J. Thibault on 10/20/16// Originally created by Ryan Huffman on 9/21/2016// Copyright 2016 High Fidelity, Inc.// Copyright 2023, Overte e.V.//// 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, Controller, Entities, Vec3, MyAvatar, Quat */ function clamp(value, min, max) { if (value < min) { return min; } else if (value > max) { return max; } return value;} function resolveHardware(path) { 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(Controller.Hardware, parts, 0); } return path;} var DEBUG = true;function debug() { if (DEBUG) { var args = Array.prototype.slice.call(arguments); args.unshift("controllerDisplay.js | "); print.apply(this, args); }} createControllerDisplay = function(config) { var controllerDisplay = { overlays: [], partOverlays: {}, parts: {}, mappingName: "mapping-display-" + Math.random(), partValues: {}, setVisible: function(visible) { for (var i = 0; i < this.overlays.length; ++i) { Entities.editEntity(this.overlays[i], { "visible": visible }); } }, setPartVisible: function(partName, visible) { // Disabled /* if (partName in this.partOverlays) { for (var i = 0; i < this.partOverlays[partName].length; ++i) { Entities.editEntity(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) { Entities.editEntity(this.partOverlays[partName][i], { "textures": textures }); } } } }, resize: function(sensorScaleFactor) { if (this.overlays.length >= 0) { var controller = config.controllers[0]; var position = controller.position; // first overlay/Local Entity is main body. var overlayID = this.overlays[0]; var localPosition = Vec3.multiply(sensorScaleFactor, Vec3.sum(Vec3.multiplyQbyV(controller.rotation, controller.naturalPosition), position)); var dimensions = Vec3.multiply(sensorScaleFactor, controller.dimensions); Entities.editEntity(overlayID, { "dimensions": dimensions, "localPosition": localPosition }); if (controller.parts) { var i = 1; for (var partName in controller.parts) { overlayID = this.overlays[i++]; var part = controller.parts[partName]; localPosition = Vec3.subtract(part.naturalPosition, controller.naturalPosition); var localRotation; var value = this.partValues[partName]; var offset, rotation; if (value !== undefined) { if (part.type === "linear") { offset = Vec3.multiply(part.maxTranslation * value, part.axis); localPosition = Vec3.sum(localPosition, offset); localRotation = undefined; } else if (part.type === "joystick") { rotation = Quat.fromPitchYawRollDegrees(value.y * part.xHalfAngle, 0, value.x * part.yHalfAngle); if (part.originOffset) { offset = Vec3.multiplyQbyV(rotation, part.originOffset); offset = Vec3.subtract(part.originOffset, offset); } else { offset = { x: 0, y: 0, z: 0 }; } localPosition = Vec3.sum(offset, localPosition); localRotation = rotation; } else if (part.type === "rotational") { value = clamp(value, part.minValue, part.maxValue); var pct = (value - part.minValue) / part.maxValue; var angle = pct * part.maxAngle; rotation = Quat.angleAxis(angle, part.axis); if (part.origin) { offset = Vec3.multiplyQbyV(rotation, part.origin); offset = Vec3.subtract(offset, part.origin); } else { offset = { x: 0, y: 0, z: 0 }; } localPosition = Vec3.sum(offset, localPosition); localRotation = rotation; } } if (localRotation !== undefined) { Entities.editEntity(overlayID, { "dimensions": Vec3.multiply(sensorScaleFactor, part.naturalDimensions), "localPosition": Vec3.multiply(sensorScaleFactor, localPosition), "localRotation": localRotation }); } else { Entities.editEntity(overlayID, { "dimensions": Vec3.multiply(sensorScaleFactor, part.naturalDimensions), "localPosition": Vec3.multiply(sensorScaleFactor, localPosition) }); } } } } } }; var mapping = Controller.newMapping(controllerDisplay.mappingName); for (var i = 0; i < config.controllers.length; ++i) { var controller = config.controllers[i]; var position = controller.position; var sensorScaleFactor = MyAvatar.sensorToWorldScale; if (controller.naturalPosition) { position = Vec3.sum(Vec3.multiplyQbyV(controller.rotation, controller.naturalPosition), position); } else { controller.naturalPosition = { x: 0, y: 0, z: 0 }; } var baseOverlayID = Entities.addEntity({ "type": "Model", "modelURL": controller.modelURL, "dimensions": Vec3.multiply(sensorScaleFactor, controller.dimensions), "localRotation": controller.rotation, "localPosition": Vec3.multiply(sensorScaleFactor, position), "parentID": MyAvatar.SELF_ID, "parentJointIndex": controller.jointIndex, "ignorePickIntersection": true }, "local"); controllerDisplay.overlays.push(baseOverlayID); if (controller.parts) { for (var partName in controller.parts) { var part = controller.parts[partName]; var localPosition = Vec3.subtract(part.naturalPosition, controller.naturalPosition); var localRotation = { x: 0, y: 0, z: 0, w: 1 }; controllerDisplay.parts[partName] = controller.parts[partName]; var properties = { "type": "Model", "modelURL": part.modelURL, "localPosition": localPosition, "localRotation": localRotation, "parentID": baseOverlayID, "ignorePickIntersection": true }; if (part.defaultTextureLayer) { var textures = {}; textures[part.textureName] = part.textureLayers[part.defaultTextureLayer].defaultTextureURL; properties.textures = textures; } var overlayID = Entities.addEntity(properties, "local"); if (part.type === "rotational") { var input = resolveHardware(part.input); mapping.from([input]).peek().to(function(partName) { return function(value) { // insert the most recent controller value into controllerDisplay.partValues. controllerDisplay.partValues[partName] = value; controllerDisplay.resize(MyAvatar.sensorToWorldScale); }; }(partName)); } 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 === "joystick") { (function(part, partName) { var xInput = resolveHardware(part.xInput); var yInput = resolveHardware(part.yInput); mapping.from([xInput]).peek().to(function(value) { // insert the most recent controller value into controllerDisplay.partValues. if (controllerDisplay.partValues[partName]) { controllerDisplay.partValues[partName].x = value; } else { controllerDisplay.partValues[partName] = {x: value, y: 0}; } controllerDisplay.resize(MyAvatar.sensorToWorldScale); }); mapping.from([yInput]).peek().to(function(value) { // insert the most recent controller value into controllerDisplay.partValues. if (controllerDisplay.partValues[partName]) { controllerDisplay.partValues[partName].y = value; } else { controllerDisplay.partValues[partName] = {x: 0, y: value}; } controllerDisplay.resize(MyAvatar.sensorToWorldScale); }); })(part, partName); } else if (part.type === "linear") { (function(part, partName) { var input = resolveHardware(part.input); mapping.from([input]).peek().to(function(value) { // insert the most recent controller value into controllerDisplay.partValues. controllerDisplay.partValues[partName] = value; controllerDisplay.resize(MyAvatar.sensorToWorldScale); }); })(part, partName); } else if (part.type === "static") { // do nothing } else { debug("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); controllerDisplay.resize(MyAvatar.sensorToWorldScale); return controllerDisplay;}; deleteControllerDisplay = function(controllerDisplay) { for (var i = 0; i < controllerDisplay.overlays.length; ++i) { Entities.deleteEntity(controllerDisplay.overlays[i]); } Controller.disableMapping(controllerDisplay.mappingName);};
//
// controllerDisplay.js
//
// Created by Anthony J. Thibault on 10/20/16
// Originally created by Ryan Huffman on 9/21/2016
// Copyright 2016 High Fidelity, Inc.
// Copyright 2023, Overte e.V.
//
// 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, Controller, Entities, Vec3, MyAvatar, Quat */
function clamp(value, min, max) {
if (value < min) {
return min;
} else if (value > max) {
return max;
}
return value;
}
function resolveHardware(path) {
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(Controller.Hardware, parts, 0);
}
return path;
}
var DEBUG = true;
function debug() {
if (DEBUG) {
var args = Array.prototype.slice.call(arguments);
args.unshift("controllerDisplay.js | ");
print.apply(this, args);
}
}
createControllerDisplay = function(config) {
var controllerDisplay = {
overlays: [],
partOverlays: {},
parts: {},
mappingName: "mapping-display-" + Math.random(),
partValues: {},
setVisible: function(visible) {
for (var i = 0; i < this.overlays.length; ++i) {
Entities.editEntity(this.overlays[i], {
"visible": visible
});
}
},
setPartVisible: function(partName, visible) {
// Disabled
/*
if (partName in this.partOverlays) {
for (var i = 0; i < this.partOverlays[partName].length; ++i) {
Entities.editEntity(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) {
Entities.editEntity(this.partOverlays[partName][i], {
"textures": textures
});
}
}
}
},
resize: function(sensorScaleFactor) {
if (this.overlays.length >= 0) {
var controller = config.controllers[0];
var position = controller.position;
// first overlay/Local Entity is main body.
var overlayID = this.overlays[0];
var localPosition = Vec3.multiply(sensorScaleFactor, Vec3.sum(Vec3.multiplyQbyV(controller.rotation, controller.naturalPosition), position));
var dimensions = Vec3.multiply(sensorScaleFactor, controller.dimensions);
Entities.editEntity(overlayID, {
"dimensions": dimensions,
"localPosition": localPosition
});
if (controller.parts) {
var i = 1;
for (var partName in controller.parts) {
overlayID = this.overlays[i++];
var part = controller.parts[partName];
localPosition = Vec3.subtract(part.naturalPosition, controller.naturalPosition);
var localRotation;
var value = this.partValues[partName];
var offset, rotation;
if (value !== undefined) {
if (part.type === "linear") {
offset = Vec3.multiply(part.maxTranslation * value, part.axis);
localPosition = Vec3.sum(localPosition, offset);
localRotation = undefined;
} else if (part.type === "joystick") {
rotation = Quat.fromPitchYawRollDegrees(value.y * part.xHalfAngle, 0, value.x * part.yHalfAngle);
if (part.originOffset) {
offset = Vec3.multiplyQbyV(rotation, part.originOffset);
offset = Vec3.subtract(part.originOffset, offset);
} else {
offset = { x: 0, y: 0, z: 0 };
}
localPosition = Vec3.sum(offset, localPosition);
localRotation = rotation;
} else if (part.type === "rotational") {
value = clamp(value, part.minValue, part.maxValue);
var pct = (value - part.minValue) / part.maxValue;
var angle = pct * part.maxAngle;
rotation = Quat.angleAxis(angle, part.axis);
if (part.origin) {
offset = Vec3.multiplyQbyV(rotation, part.origin);
offset = Vec3.subtract(offset, part.origin);
} else {
offset = { x: 0, y: 0, z: 0 };
}
localPosition = Vec3.sum(offset, localPosition);
localRotation = rotation;
}
}
if (localRotation !== undefined) {
Entities.editEntity(overlayID, {
"dimensions": Vec3.multiply(sensorScaleFactor, part.naturalDimensions),
"localPosition": Vec3.multiply(sensorScaleFactor, localPosition),
"localRotation": localRotation
});
} else {
Entities.editEntity(overlayID, {
"dimensions": Vec3.multiply(sensorScaleFactor, part.naturalDimensions),
"localPosition": Vec3.multiply(sensorScaleFactor, localPosition)
});
}
}
}
}
}
};
var mapping = Controller.newMapping(controllerDisplay.mappingName);
for (var i = 0; i < config.controllers.length; ++i) {
var controller = config.controllers[i];
var position = controller.position;
var sensorScaleFactor = MyAvatar.sensorToWorldScale;
if (controller.naturalPosition) {
position = Vec3.sum(Vec3.multiplyQbyV(controller.rotation, controller.naturalPosition), position);
} else {
controller.naturalPosition = { x: 0, y: 0, z: 0 };
}
var baseOverlayID = Entities.addEntity({
"type": "Model",
"modelURL": controller.modelURL,
"dimensions": Vec3.multiply(sensorScaleFactor, controller.dimensions),
"localRotation": controller.rotation,
"localPosition": Vec3.multiply(sensorScaleFactor, position),
"parentID": MyAvatar.SELF_ID,
"parentJointIndex": controller.jointIndex,
"ignorePickIntersection": true
}, "local");
controllerDisplay.overlays.push(baseOverlayID);
if (controller.parts) {
for (var partName in controller.parts) {
var part = controller.parts[partName];
var localPosition = Vec3.subtract(part.naturalPosition, controller.naturalPosition);
var localRotation = { x: 0, y: 0, z: 0, w: 1 };
controllerDisplay.parts[partName] = controller.parts[partName];
var properties = {
"type": "Model",
"modelURL": part.modelURL,
"localPosition": localPosition,
"localRotation": localRotation,
"parentID": baseOverlayID,
"ignorePickIntersection": true
};
if (part.defaultTextureLayer) {
var textures = {};
textures[part.textureName] = part.textureLayers[part.defaultTextureLayer].defaultTextureURL;
properties.textures = textures;
}
var overlayID = Entities.addEntity(properties, "local");
if (part.type === "rotational") {
var input = resolveHardware(part.input);
mapping.from([input]).peek().to(function(partName) {
return function(value) {
// insert the most recent controller value into controllerDisplay.partValues.
controllerDisplay.partValues[partName] = value;
controllerDisplay.resize(MyAvatar.sensorToWorldScale);
};
}(partName));
} 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 === "joystick") {
(function(part, partName) {
var xInput = resolveHardware(part.xInput);
var yInput = resolveHardware(part.yInput);
mapping.from([xInput]).peek().to(function(value) {
// insert the most recent controller value into controllerDisplay.partValues.
if (controllerDisplay.partValues[partName]) {
controllerDisplay.partValues[partName].x = value;
} else {
controllerDisplay.partValues[partName] = {x: value, y: 0};
}
controllerDisplay.resize(MyAvatar.sensorToWorldScale);
});
mapping.from([yInput]).peek().to(function(value) {
// insert the most recent controller value into controllerDisplay.partValues.
if (controllerDisplay.partValues[partName]) {
controllerDisplay.partValues[partName].y = value;
} else {
controllerDisplay.partValues[partName] = {x: 0, y: value};
}
controllerDisplay.resize(MyAvatar.sensorToWorldScale);
});
})(part, partName);
} else if (part.type === "linear") {
(function(part, partName) {
var input = resolveHardware(part.input);
mapping.from([input]).peek().to(function(value) {
// insert the most recent controller value into controllerDisplay.partValues.
controllerDisplay.partValues[partName] = value;
controllerDisplay.resize(MyAvatar.sensorToWorldScale);
});
})(part, partName);
} else if (part.type === "static") {
// do nothing
} else {
debug("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);
controllerDisplay.resize(MyAvatar.sensorToWorldScale);
return controllerDisplay;
};
deleteControllerDisplay = function(controllerDisplay) {
for (var i = 0; i < controllerDisplay.overlays.length; ++i) {
Entities.deleteEntity(controllerDisplay.overlays[i]);
}
Controller.disableMapping(controllerDisplay.mappingName);
};