OmniTool first pass

This commit is contained in:
Brad Davis 2015-08-31 22:54:23 -07:00
parent b199e9e593
commit ea415071b4
8 changed files with 461 additions and 27 deletions

View file

@ -53,6 +53,12 @@ Highlighter.prototype.setSize = function(newSize) {
});
}
Highlighter.prototype.setRotation = function(newRotation) {
Overlays.editOverlay(this.highlightCube, {
rotation: newRotation
});
}
Highlighter.prototype.updateHighlight = function() {
if (this.hightlighted) {
var properties = Entities.getEntityProperties(this.hightlighted);

View file

@ -0,0 +1,300 @@
//
// Created by Bradley Austin Davis on 2015/09/01
// Copyright 2015 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
Script.include("constants.js");
Script.include("utils.js");
Script.include("highlighter.js");
Script.include("omniTool/models/modelBase.js");
Script.include("omniTool/models/wand.js");
OmniToolModules = {};
OmniToolModuleType = null;
OmniTool = function(side) {
this.OMNI_KEY = "OmniTool";
this.MAX_FRAMERATE = 30;
this.UPDATE_INTERVAL = 1.0 / this.MAX_FRAMERATE
this.SIDE = side;
this.PALM = 2 * side;
this.ACTION = findAction(side ? "ACTION2" : "ACTION1");
this.ALT_ACTION = findAction(side ? "ACTION1" : "ACTION2");
this.highlighter = new Highlighter();
this.ignoreEntities = {};
this.nearestOmniEntity = {
id: null,
inside: false,
position: null,
distance: Infinity,
radius: 0,
omniProperties: {},
boundingBox: null,
};
this.activeOmniEntityId = null;
this.lastUpdateInterval = 0;
this.tipLength = 0.4;
this.active = false;
this.module = null;
this.moduleEntityId = null;
this.lastScanPosition = ZERO_VECTOR;
this.model = new Wand();
this.model.setLength(this.tipLength);
// Connect to desired events
var _this = this;
Controller.actionEvent.connect(function(action, state) {
_this.onActionEvent(action, state);
});
Script.update.connect(function(deltaTime) {
_this.lastUpdateInterval += deltaTime;
if (_this.lastUpdateInterval >= _this.UPDATE_INTERVAL) {
_this.onUpdate(_this.lastUpdateInterval);
_this.lastUpdateInterval = 0;
}
});
Script.scriptEnding.connect(function() {
_this.onCleanup();
});
}
OmniTool.prototype.onActionEvent = function(action, state) {
// FIXME figure out the issues when only one spatial controller is active
// logDebug("Action: " + action + " " + state);
if (this.module && this.module.onActionEvent) {
this.module.onActionEvent(action, state);
}
if (action == this.ACTION) {
if (state) {
this.onClick();
} else {
this.onRelease();
}
}
// FIXME Does not work
//// with only one controller active (listed as 2 here because 'tip' + 'palm')
//// then treat the alt action button as the action button
}
OmniTool.prototype.getOmniToolData = function(entityId) {
return getEntityCustomData(this.OMNI_KEY, entityId, null);
}
OmniTool.prototype.setOmniToolData = function(entityId, data) {
setEntityCustomData(this.OMNI_KEY, entityId, data);
}
OmniTool.prototype.updateOmniToolData = function(entityId, data) {
var currentData = this.getOmniToolData(entityId) || {};
for (var key in data) {
currentData[key] = data[key];
}
setEntityCustomData(this.OMNI_KEY, entityId, currentData);
}
OmniTool.prototype.setActive = function(active) {
if (active === this.active) {
return;
}
logDebug("omnitool changing active state: " + active);
this.active = active;
this.model.setVisible(this.active);
if (this.module && this.module.onActiveChanged) {
this.module.onActiveChanged(this.side);
}
}
OmniTool.prototype.onUpdate = function(deltaTime) {
// FIXME this returns data if either the left or right controller is not on the base
this.position = Controller.getSpatialControlPosition(this.PALM);
// When on the base, hydras report a position of 0
this.setActive(Vec3.length(this.position) > 0.001);
var rawRotation = Controller.getSpatialControlRawRotation(this.PALM);
this.rotation = Quat.multiply(MyAvatar.orientation, rawRotation);
this.model.setTransform({
rotation: this.rotation,
position: this.position,
});
this.scan();
if (this.module && this.module.onUpdate) {
this.module.onUpdate(deltaTime);
}
}
OmniTool.prototype.onClick = function() {
// First check to see if the user is switching to a new omni module
if (this.nearestOmniEntity.inside && this.nearestOmniEntity.omniProperties.script) {
this.activateNewOmniModule();
return;
}
// Next check if there is an active module and if so propagate the click
// FIXME how to I switch to a new module?
if (this.module && this.module.onClick) {
this.module.onClick();
return;
}
}
OmniTool.prototype.onRelease = function() {
// FIXME how to I switch to a new module?
if (this.module && this.module.onRelease) {
this.module.onRelease();
return;
}
logDebug("Base omnitool does nothing on release");
}
// FIXME resturn a structure of all nearby entities to distances
OmniTool.prototype.findNearestOmniEntity = function(maxDistance, selector) {
if (!maxDistance) {
maxDistance = 2.0;
}
var resultDistance = Infinity;
var resultId = null;
var resultProperties = null;
var resultOmniData = null;
var ids = Entities.findEntities(this.model.tipPosition, maxDistance);
for (var i in ids) {
var entityId = ids[i];
if (this.ignoreEntities[entityId]) {
continue;
}
var omniData = this.getOmniToolData(entityId);
if (!omniData) {
// FIXME find a place to flush this information
this.ignoreEntities[entityId] = true;
continue;
}
// Let searchers query specifically
if (selector && !selector(entityId, omniData)) {
continue;
}
var properties = Entities.getEntityProperties(entityId);
var distance = Vec3.distance(this.model.tipPosition, properties.position);
if (distance < resultDistance) {
resultDistance = distance;
resultId = entityId;
}
}
return resultId;
}
OmniTool.prototype.onEnterNearestOmniEntity = function() {
this.nearestOmniEntity.inside = true;
this.highlighter.highlight(this.nearestOmniEntity.id);
logDebug("On enter omniEntity " + this.nearestOmniEntity.id);
}
OmniTool.prototype.onLeaveNearestOmniEntity = function() {
this.nearestOmniEntity.inside = false;
this.highlighter.highlight(null);
logDebug("On leave omniEntity " + this.nearestOmniEntity.id);
}
OmniTool.prototype.setNearestOmniEntity = function(entityId) {
if (entityId && entityId !== this.nearestOmniEntity.id) {
if (this.nearestOmniEntity.id && this.nearestOmniEntity.inside) {
this.onLeaveNearestOmniEntity();
}
this.nearestOmniEntity.id = entityId;
this.nearestOmniEntity.omniProperties = this.getOmniToolData(entityId);
var properties = Entities.getEntityProperties(entityId);
this.nearestOmniEntity.position = properties.position;
// FIXME use a real bounding box, not a sphere
var bbox = properties.boundingBox;
this.nearestOmniEntity.radius = Vec3.length(Vec3.subtract(bbox.center, bbox.brn));
this.highlighter.setRotation(properties.rotation);
this.highlighter.setSize(Vec3.multiply(1.05, bbox.dimensions));
}
}
OmniTool.prototype.scan = function() {
var scanDistance = Vec3.distance(this.model.tipPosition, this.lastScanPosition);
if (scanDistance < 0.005) {
return;
}
this.lastScanPosition = this.model.tipPosition;
this.setNearestOmniEntity(this.findNearestOmniEntity());
if (this.nearestOmniEntity.id) {
var distance = Vec3.distance(this.model.tipPosition, this.nearestOmniEntity.position);
// track distance on a half centimeter basis
if (Math.abs(this.nearestOmniEntity.distance - distance) > 0.005) {
this.nearestOmniEntity.distance = distance;
if (!this.nearestOmniEntity.inside && distance < this.nearestOmniEntity.radius) {
this.onEnterNearestOmniEntity();
}
if (this.nearestOmniEntity.inside && distance > this.nearestOmniEntity.radius + 0.01) {
this.onLeaveNearestOmniEntity();
}
}
}
}
OmniTool.prototype.unloadModule = function() {
if (this.module && this.module.onUnload) {
this.module.onUnload();
}
this.module = null;
this.moduleEntityId = null;
}
OmniTool.prototype.activateNewOmniModule = function() {
// Support the ability for scripts to just run without replacing the current module
var script = this.nearestOmniEntity.omniProperties.script;
if (script.indexOf("/") < 0) {
script = "omniTool/modules/" + script;
}
// Reset the tool type
OmniToolModuleType = null;
logDebug("Including script path: " + script);
try {
Script.include(script);
} catch(err) {
logWarn("Failed to include script: " + script + "\n" + err);
return;
}
// If we're building a new module, unload the old one
if (OmniToolModuleType) {
logDebug("New OmniToolModule: " + OmniToolModuleType);
this.unloadModule();
try {
this.module = new OmniToolModules[OmniToolModuleType](this, this.nearestOmniEntity.id);
this.moduleEntityId = this.nearestOmniEntity.id;
if (this.module.onLoad) {
this.module.onLoad();
}
} catch(err) {
logWarn("Failed to instantiate new module: " + err);
}
}
}
// FIXME find a good way to sync the two omni tools
OMNI_TOOLS = [ new OmniTool(0), new OmniTool(1) ];

View file

@ -0,0 +1,19 @@
ModelBase = function() {
this.length = 0.2;
}
ModelBase.prototype.setVisible = function(visible) {
this.visible = visible;
}
ModelBase.prototype.setLength = function(length) {
this.length = length;
}
ModelBase.prototype.setTransform = function(transform) {
this.rotation = transform.rotation;
this.position = transform.position;
this.tipVector = Vec3.multiplyQbyV(this.rotation, { x: 0, y: this.length, z: 0 });
this.tipPosition = Vec3.sum(this.position, this.tipVector);
}

View file

@ -0,0 +1,120 @@
Wand = function() {
// Max updates fps
this.MAX_FRAMERATE = 30
this.UPDATE_INTERVAL = 1.0 / this.MAX_FRAMERATE
this.DEFAULT_TIP_COLORS = [ {
red: 128,
green: 128,
blue: 128,
}, {
red: 64,
green: 64,
blue: 64,
}];
this.POINTER_ROTATION = Quat.fromPitchYawRollDegrees(45, 0, 45);
// FIXME does this need to be a member of this?
this.lastUpdateInterval = 0;
this.pointers = [
Overlays.addOverlay("cube", {
position: ZERO_VECTOR,
color: this.DEFAULT_TIP_COLORS[0],
alpha: 1.0,
solid: true,
visible: false,
}),
Overlays.addOverlay("cube", {
position: ZERO_VECTOR,
color: this.DEFAULT_TIP_COLORS[1],
alpha: 1.0,
solid: true,
visible: false,
})
];
this.wand = Overlays.addOverlay("cube", {
position: ZERO_VECTOR,
color: COLORS.WHITE,
dimensions: { x: 0.01, y: 0.01, z: 0.2 },
alpha: 1.0,
solid: true,
visible: false
});
var _this = this;
Script.scriptEnding.connect(function() {
Overlays.deleteOverlay(_this.pointers[0]);
Overlays.deleteOverlay(_this.pointers[1]);
Overlays.deleteOverlay(_this.wand);
});
Script.update.connect(function(deltaTime) {
_this.lastUpdateInterval += deltaTime;
if (_this.lastUpdateInterval >= _this.UPDATE_INTERVAL) {
_this.onUpdate(_this.lastUpdateInterval);
_this.lastUpdateInterval = 0;
}
});
}
Wand.prototype = Object.create( ModelBase.prototype );
Wand.prototype.setVisible = function(visible) {
ModelBase.prototype.setVisible.call(this, visible);
Overlays.editOverlay(this.pointers[0], {
visible: this.visible
});
Overlays.editOverlay(this.pointers[1], {
visible: this.visible
});
Overlays.editOverlay(this.wand, {
visible: this.visible
});
}
Wand.prototype.setTransform = function(transform) {
ModelBase.prototype.setTransform.call(this, transform);
var wandPosition = Vec3.sum(this.position, Vec3.multiply(0.5, this.tipVector));
Overlays.editOverlay(this.pointers[0], {
position: this.tipPosition,
rotation: this.rotation,
visible: true,
});
Overlays.editOverlay(this.pointers[1], {
position: this.tipPosition,
rotation: Quat.multiply(this.POINTER_ROTATION, this.rotation),
visible: true,
});
Overlays.editOverlay(this.wand, {
dimensions: { x: 0.01, y: this.length * 0.9, z: 0.01 },
position: wandPosition,
rotation: this.rotation,
visible: true,
});
}
Wand.prototype.setTipColors = function(color1, color2) {
Overlays.editOverlay(this.pointers[0], {
color: color1 || this.DEFAULT_TIP_COLORS[0],
});
Overlays.editOverlay(this.pointers[1], {
color: color2 || this.DEFAULT_TIP_COLORS[1],
});
}
Wand.prototype.onUpdate = function(deltaTime) {
if (this.visible) {
var time = new Date().getTime() / 250;
var scale1 = Math.abs(Math.sin(time));
var scale2 = Math.abs(Math.cos(time));
Overlays.editOverlay(this.pointers[0], {
scale: scale1 * 0.01,
});
Overlays.editOverlay(this.pointers[1], {
scale: scale2 * 0.01,
});
}
}

View file

@ -0,0 +1,9 @@
OmniToolModules.Test = function() {
}
OmniToolModules.Test.prototype.onClick = function() {
logDebug("Test module onClick");
}
OmniToolModuleType = "Test"

View file

@ -53,11 +53,17 @@ getEntityUserData = function(id) {
var results = null;
var properties = Entities.getEntityProperties(id);
if (properties.userData) {
results = JSON.parse(properties.userData);
try {
results = JSON.parse(properties.userData);
} catch(err) {
logDebug(err);
logDebug(properties.userData);
}
}
return results ? results : {};
}
// Non-destructively modify the user data of an entity.
setEntityCustomData = function(customKey, id, data) {
var userData = getEntityUserData(id);

View file

@ -1,26 +0,0 @@
//
// Created by Bradley Austin Davis on 2015/08/25
// Copyright 2015 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
Script.include("../../libraries/htmlColors.js");
Script.include("constants.js");
Script.include("utils.js");
Script.include("magBalls.js");
Script.include("ballController.js");
var magBalls = new MagBalls();
// Clear any previous balls
// magBalls.clear();
MenuController = function(side) {
HandController.call(this, side);
}
// FIXME resolve some of the issues with dual controllers before allowing both controllers active
var handControllers = [new BallController(LEFT_CONTROLLER, magBalls)]; //, new HandController(RIGHT) ];