779 lines
No EOL
35 KiB
JavaScript
779 lines
No EOL
35 KiB
JavaScript
//
|
|
// drawSphereClient.js
|
|
//
|
|
// Created by Rebecca Stankus 3/28/2019
|
|
// Copyright 2019 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
|
|
|
|
(function() {
|
|
var _this;
|
|
|
|
var WHITEBOARD_ZONE_SEARCH_RADIUS_M = 100;
|
|
var MINIMUM_MOVEMENT_TO_DRAW_M = 0.0005;
|
|
var MAXIMUM_MOVEMENT_TO_DRAW_M = 0.1;
|
|
var MAXIMUM_DISTANCE_TO_SEARCH_M = 1;
|
|
var MAXIMUM_DISTANCE_TO_DELETE_M = 0.03;
|
|
var DISTANCE_TO_DRAW_IN_FRONT_OF_CAMERA_DESKTOP_M = 1.5;
|
|
var STROKE_FORWARD_OFFSET_M = 0.01;
|
|
|
|
var WAIT_TO_CLEAN_UP_MS = 2000;
|
|
var DELETE_AGAIN_MS = 100;
|
|
var WAIT_FOR_ENTITIES_TO_LOAD_MS = 300;
|
|
var REPEAT_DISTANCE_CHECK_MS = 60;
|
|
var LASER_LIFETIME_S = 1;
|
|
var DECAY_TIME_S = 60;
|
|
|
|
var DRAW_SOUND = SoundCache.getSound(Script.resolvePath('../resources/sounds/draw.mp3'));
|
|
var OPEN_SOUND = SoundCache.getSound(Script.resolvePath('../resources/sounds/open.mp3'));
|
|
var CLOSE_SOUND = SoundCache.getSound(Script.resolvePath('../resources/sounds/close.mp3'));
|
|
var DRAW_SOUND_VOLUME = 0.08;
|
|
var OPEN_SOUND_VOLUME = 0.02;
|
|
var CLOSE_SOUND_VOLUME = 0.02;
|
|
|
|
var MINIMUM_TRIGGER_PRESS_VALUE = 0.97;
|
|
|
|
var HALF = 0.5;
|
|
|
|
var DEFAULT_NORMAL = { x: 0, y: 0, z: 1 };
|
|
var MAX_LINE_POINTS = 100;
|
|
var DEFAULT_LINE_PROPERTIES = {
|
|
type: "PolyLine",
|
|
name: "Whiteboard Polyline",
|
|
isUVModeStretch: true,
|
|
lifetime: DECAY_TIME_S,
|
|
collisionless: true,
|
|
grab: { grabbable: false }
|
|
};
|
|
|
|
var tablet = Tablet.getTablet('com.highfidelity.interface.tablet.system');
|
|
|
|
var dominantHandJoint;
|
|
var dominantHand;
|
|
var parentJointIndex;
|
|
|
|
var whiteboard = null;
|
|
var whiteboardZone = null;
|
|
var whiteboardParts = [];
|
|
var whiteboardProperties;
|
|
|
|
var mouseEventsConnected = false;
|
|
var controllerMappingName = 'Hifi-DrawApp';
|
|
var controllerMapping;
|
|
|
|
var drawInterval = null;
|
|
var deletingInterval;
|
|
|
|
|
|
var activeTriggerPress = false;
|
|
var activeGripPress = false;
|
|
var drawingInDesktop;
|
|
|
|
var animationData = {};
|
|
var animationHandlerID;
|
|
|
|
var injector;
|
|
|
|
var pickRay;
|
|
|
|
var polyLine = null;
|
|
var lineStartPosition;
|
|
var previousLinePoint;
|
|
var previousNormal;
|
|
var previousStrokeWidth;
|
|
var currentPoint;
|
|
var currentNormal;
|
|
var currentStrokeWidth;
|
|
var wasLastPointOnBoard;
|
|
var displacementFromStart;
|
|
|
|
var laser = null;
|
|
|
|
var readyToDraw = false;
|
|
var initialLineStartDataReady = false;
|
|
|
|
var PaintSphere = function() {
|
|
_this = this;
|
|
};
|
|
|
|
/* When a paint sphere is created, collect data for later use. Find the correct joint to use parent the sphere
|
|
to and connect signals. Set up mapping, and enter enter correct view mode. Play a sound to signal getting a new
|
|
paint sphere. */
|
|
PaintSphere.prototype = {
|
|
preload: function(entityID) {
|
|
_this.entityID = entityID;
|
|
Script.setTimeout(function() {
|
|
var properties = Entities.getEntityProperties(_this.entityID, ['userData','color']);
|
|
_this.color = properties.color;
|
|
_this.texture = JSON.parse(properties.userData).textureURL;
|
|
_this.findWhiteboard();
|
|
readyToDraw = true;
|
|
}, WAIT_FOR_ENTITIES_TO_LOAD_MS);
|
|
dominantHand = MyAvatar.getDominantHand();
|
|
dominantHandJoint = (dominantHand === "right") ? "RightHand" : "LeftHand";
|
|
MyAvatar.dominantHandChanged.connect(_this.handChanged);
|
|
|
|
parentJointIndex = MyAvatar.getJointIndex(dominantHandJoint + "Index4");
|
|
if (parentJointIndex === -1) {
|
|
parentJointIndex = MyAvatar.getJointIndex(dominantHandJoint + "Index3");
|
|
}
|
|
if (parentJointIndex === -1) {
|
|
parentJointIndex =MyAvatar.getJointIndex(dominantHandJoint);
|
|
print("ERROR: Falling back to dominant hand joint as index finger tip could not be found");
|
|
}
|
|
tablet.tabletShownChanged.connect(_this.tabletShownChanged);
|
|
HMD.displayModeChanged.connect(_this.displayModeChanged);
|
|
Window.domainChanged.connect(_this.domainChanged);
|
|
_this.registerControllerMapping();
|
|
if (HMD.active) {
|
|
_this.setUpHMDMode();
|
|
} else {
|
|
_this.setUpDesktopMode();
|
|
}
|
|
_this.playSound(OPEN_SOUND, OPEN_SOUND_VOLUME, MyAvatar.position, true, false);
|
|
},
|
|
|
|
/* PLAY A SOUND: Plays a sound at the specified position, volume, local mode, and playback
|
|
mode requested. */
|
|
playSound: function(sound, volume, position, localOnly, loop){
|
|
if (sound.downloaded) {
|
|
if (injector) {
|
|
injector.stop();
|
|
injector = null;
|
|
}
|
|
injector = Audio.playSound(sound, {
|
|
position: position,
|
|
volume: volume,
|
|
localOnly: localOnly,
|
|
loop: loop
|
|
});
|
|
}
|
|
},
|
|
|
|
/* Determine is my avatar is inside the boundaries of a zone */
|
|
isUserInZone: function(zoneID) {
|
|
var zoneProperties = Entities.getEntityProperties(zoneID, ['position', 'rotation', 'dimensions']);
|
|
var localPosition = Vec3.multiplyQbyV(Quat.inverse(zoneProperties.rotation),
|
|
Vec3.subtract(MyAvatar.position, zoneProperties.position));
|
|
var halfDimensions = Vec3.multiply(zoneProperties.dimensions, HALF);
|
|
return -halfDimensions.x <= localPosition.x &&
|
|
halfDimensions.x >= localPosition.x &&
|
|
-halfDimensions.y <= localPosition.y &&
|
|
halfDimensions.y >= localPosition.y &&
|
|
-halfDimensions.z <= localPosition.z &&
|
|
halfDimensions.z >= localPosition.z;
|
|
},
|
|
|
|
/* Scroll through whiteboard zones to find the one whose zone you are in. If not found, delete this sphere,
|
|
ending this script */
|
|
findWhiteboard: function() {
|
|
Entities.findEntitiesByName("Whiteboard Zone", MyAvatar.position, WHITEBOARD_ZONE_SEARCH_RADIUS_M).
|
|
forEach(function(foundWhiteboardZone) {
|
|
if (_this.isUserInZone(foundWhiteboardZone)) {
|
|
whiteboardZone = foundWhiteboardZone;
|
|
whiteboard = Entities.getEntityProperties(whiteboardZone, 'parentID').parentID;
|
|
}
|
|
});
|
|
if (whiteboard) {
|
|
whiteboardParts = Entities.getChildrenIDs(whiteboard);
|
|
whiteboardParts.push(whiteboard);
|
|
} else {
|
|
Entities.deleteEntity(_this.entityID);
|
|
}
|
|
},
|
|
|
|
/* If current point is in range, and on same surface as last point, get ready to draw. If this is not a new
|
|
line, check that the current line has room to add more points and start a new line if necessary. Otherwise,
|
|
update the position of the draw sound and edit the current line to add the current point. If not editing the
|
|
current line, draw a new one. */
|
|
draw: function(onBoard) {
|
|
if (!readyToDraw) {
|
|
return;
|
|
}
|
|
if (Vec3.distance(previousLinePoint, currentPoint) < MINIMUM_MOVEMENT_TO_DRAW_M ||
|
|
Vec3.distance(previousLinePoint, currentPoint) > MAXIMUM_MOVEMENT_TO_DRAW_M) {
|
|
return;
|
|
}
|
|
if (onBoard !== wasLastPointOnBoard) { // toggle between on board and air, stop drawing
|
|
_this.stopDrawing();
|
|
wasLastPointOnBoard = null;
|
|
return;
|
|
}
|
|
wasLastPointOnBoard = onBoard;
|
|
var newLine = !polyLine;
|
|
var lineProperties = DEFAULT_LINE_PROPERTIES;
|
|
var linePointsCount;
|
|
if (!newLine) { // maybe editing existing line
|
|
var previousLineProperties = Entities.getEntityProperties(polyLine, ['linePoints', 'normals',
|
|
'strokeWidths', 'age']);
|
|
if (!(previousLineProperties.linePoints && previousLineProperties.normals &&
|
|
previousLineProperties.strokeWidths && previousLineProperties.age)) {
|
|
_this.stopDrawing();
|
|
return;
|
|
}
|
|
linePointsCount = previousLineProperties.linePoints.length;
|
|
if (linePointsCount > MAX_LINE_POINTS) { // too many line points, start new line connected to previous point
|
|
newLine = true;
|
|
lineStartPosition = previousLinePoint;
|
|
displacementFromStart = Vec3.subtract(currentPoint, lineStartPosition);
|
|
} else { // actually editing the previous line
|
|
if (injector) {
|
|
injector.options = {
|
|
position: currentPoint,
|
|
volume: DRAW_SOUND_VOLUME
|
|
};
|
|
}
|
|
lineProperties.linePoints.push(displacementFromStart);
|
|
lineProperties.normals.push(currentNormal);
|
|
lineProperties.strokeWidths.push(currentStrokeWidth);
|
|
if (!onBoard) {
|
|
lineProperties.lifetime = previousLineProperties.age + DECAY_TIME_S;
|
|
}
|
|
Entities.editEntity(polyLine, {
|
|
linePoints: lineProperties.linePoints,
|
|
normals: lineProperties.normals,
|
|
strokeWidths: lineProperties.strokeWidths,
|
|
lifetime: lineProperties.lifetime,
|
|
faceCamera: !onBoard
|
|
});
|
|
}
|
|
}
|
|
// new line due to just beginning to draw or starting new to continue line with too many points.
|
|
// All lines have some previous data saved from the initial point, actual new lines have no line points yet
|
|
if (newLine) {
|
|
lineProperties.position = lineStartPosition;
|
|
lineProperties.linePoints = [{x: 0, y: 0, z: 0 }, displacementFromStart];
|
|
lineProperties.normals = [previousNormal, currentNormal];
|
|
lineProperties.strokeWidths = [previousStrokeWidth, currentStrokeWidth];
|
|
lineProperties.color = _this.color;
|
|
lineProperties.textures = _this.texture;
|
|
lineProperties.faceCamera = !onBoard;
|
|
if (onBoard) {
|
|
lineProperties.lifetime = -1;
|
|
polyLine = Entities.addEntity(lineProperties);
|
|
} else {
|
|
polyLine = Entities.addEntity(lineProperties, 'avatar');
|
|
}
|
|
}
|
|
},
|
|
|
|
/* Since polylines don't intersect, we delete by finding the line with a point closest to the current
|
|
position where we want to delete. */
|
|
deleteFromPoint: function(point) {
|
|
var lineToDelete = null;
|
|
Entities.findEntitiesByName("Whiteboard Polyline", point,
|
|
MAXIMUM_DISTANCE_TO_SEARCH_M).forEach(function(nearbyWhiteboardLine) {
|
|
var lineProperties = Entities.getEntityProperties(nearbyWhiteboardLine,
|
|
['position', 'linePoints']);
|
|
if (!(lineProperties.linePoints && lineProperties.position)) {
|
|
return;
|
|
}
|
|
var lineBoundingBoxCenter = lineProperties.position;
|
|
var numberLinePoints = lineProperties.linePoints.length;
|
|
var shortestDistance = MAXIMUM_DISTANCE_TO_DELETE_M;
|
|
for (var i = 0; i < numberLinePoints; i++) {
|
|
var distanceFromPoint = Vec3.distance(point,
|
|
Vec3.sum(lineBoundingBoxCenter, lineProperties.linePoints[i]));
|
|
if (distanceFromPoint <= shortestDistance) {
|
|
lineToDelete = nearbyWhiteboardLine;
|
|
shortestDistance = distanceFromPoint;
|
|
}
|
|
}
|
|
});
|
|
if (lineToDelete) {
|
|
Entities.deleteEntity(lineToDelete);
|
|
}
|
|
},
|
|
|
|
/* Use the dimensions of the paint sphere in order to handle avatar resizing */
|
|
getCurrentStrokeWidth: function() {
|
|
var paintSphereDimensions = Entities.getEntityProperties(_this.entityID, 'dimensions').dimensions;
|
|
return paintSphereDimensions.x;
|
|
},
|
|
|
|
/* Create a ray from the mouse position to the white board to get intersection data. If it intersects
|
|
a selection button, we are not drawing */
|
|
getDesktopIntersectionData: function(event) {
|
|
pickRay = Camera.computePickRay(event.x, event.y);
|
|
var whiteBoardIntersectionData = Entities.findRayIntersection(pickRay, true, whiteboardParts);
|
|
if (whiteBoardIntersectionData.intersects) {
|
|
var intersectedWhiteboardPartName = Entities.getEntityProperties(whiteBoardIntersectionData.entityID,
|
|
'name').name;
|
|
if (intersectedWhiteboardPartName !== "Whiteboard") {
|
|
if (drawingInDesktop) {
|
|
_this.stopDrawing();
|
|
}
|
|
return -1;
|
|
}
|
|
}
|
|
return whiteBoardIntersectionData;
|
|
},
|
|
|
|
/* On mouse press, if the user is not in the whiteboard zone or is using tablet or create, ignore. Check for
|
|
an intersection, and project point onto board if necessary. If drawing in air, project point forward 1M in
|
|
front of camera. Begin drawing sound and store initial data. If deleting, begin at current point. */
|
|
mousePressed: function(event) {
|
|
if (!whiteboardZone) {
|
|
return;
|
|
}
|
|
if (!_this.isUserInZone(whiteboardZone)) {
|
|
Entities.deleteEntity(_this.entityID);
|
|
}
|
|
if (Settings.getValue("io.highfidelity.isEditing", false) || tablet.tabletShown) {
|
|
return;
|
|
}
|
|
var whiteBoardIntersectionData = _this.getDesktopIntersectionData(event);
|
|
if (whiteBoardIntersectionData === -1) {
|
|
return;
|
|
}
|
|
var isCurrentPointOnBoard = _this.maybeProjectPointOntoBoard(whiteBoardIntersectionData, true);
|
|
wasLastPointOnBoard = isCurrentPointOnBoard;
|
|
if (event.isLeftButton) {
|
|
drawingInDesktop = true;
|
|
if (!isCurrentPointOnBoard) {
|
|
currentPoint = Vec3.sum(pickRay.origin, Vec3.multiply(pickRay.direction,
|
|
DISTANCE_TO_DRAW_IN_FRONT_OF_CAMERA_DESKTOP_M));
|
|
currentNormal = DEFAULT_NORMAL;
|
|
}
|
|
currentStrokeWidth = _this.getCurrentStrokeWidth();
|
|
_this.playSound(DRAW_SOUND, DRAW_SOUND_VOLUME, currentPoint, true, true);
|
|
lineStartPosition = currentPoint;
|
|
} else if (event.isMiddleButton) {
|
|
_this.deleteFromPoint(currentPoint);
|
|
}
|
|
},
|
|
|
|
/* While holding mouse button, continue getting new intersection data, and updating line data to draw
|
|
or delete with. */
|
|
mouseContinueLine: function(event) {
|
|
var whiteBoardIntersectionData = _this.getDesktopIntersectionData(event);
|
|
if (whiteBoardIntersectionData === -1) {
|
|
return;
|
|
}
|
|
previousLinePoint = currentPoint;
|
|
previousNormal = currentNormal;
|
|
previousStrokeWidth = currentStrokeWidth;
|
|
currentStrokeWidth = _this.getCurrentStrokeWidth();
|
|
var isCurrentPointOnBoard = _this.maybeProjectPointOntoBoard(whiteBoardIntersectionData, true);
|
|
if (event.isLeftButton) {
|
|
if (!isCurrentPointOnBoard) {
|
|
currentPoint = Vec3.sum(pickRay.origin, Vec3.multiply(pickRay.direction,
|
|
DISTANCE_TO_DRAW_IN_FRONT_OF_CAMERA_DESKTOP_M));
|
|
currentNormal = DEFAULT_NORMAL;
|
|
}
|
|
displacementFromStart = Vec3.subtract(currentPoint, lineStartPosition);
|
|
_this.draw(isCurrentPointOnBoard);
|
|
} else if (event.isMiddleButton) {
|
|
_this.deleteFromPoint(currentPoint);
|
|
}
|
|
},
|
|
|
|
/* On mouse release stop drawing if necessary */
|
|
mouseReleased: function(event) {
|
|
if (event.isLeftButton) {
|
|
if (drawingInDesktop) {
|
|
drawingInDesktop = false;
|
|
_this.stopDrawing();
|
|
}
|
|
}
|
|
},
|
|
|
|
/* If there is an intersection with the whiteboard, project the point onto the surface, set its normals to match,
|
|
and then move it slightly in front of the board. */
|
|
maybeProjectPointOntoBoard: function(whiteBoardIntersectionData, desktop) {
|
|
if (whiteBoardIntersectionData.intersects) {
|
|
currentPoint = whiteBoardIntersectionData.intersection;
|
|
whiteboardProperties = Entities.getEntityProperties(whiteboard, ['position', 'rotation']);
|
|
var isCurrentPointOnBoard = true;
|
|
currentNormal = Vec3.multiply(-1, Quat.getFront(whiteboardProperties.rotation));
|
|
var distanceWhiteboardPlane = Vec3.dot(currentNormal, whiteboardProperties.position);
|
|
var distanceLocal = Vec3.dot(currentNormal, currentPoint) - distanceWhiteboardPlane;
|
|
currentPoint = Vec3.subtract(currentPoint, Vec3.multiply(distanceLocal, currentNormal));
|
|
currentPoint = Vec3.subtract(currentPoint, Vec3.multiply(currentNormal, STROKE_FORWARD_OFFSET_M));
|
|
} else {
|
|
isCurrentPointOnBoard = false;
|
|
}
|
|
return isCurrentPointOnBoard;
|
|
},
|
|
|
|
/* Create a laser to show where the user is drawing or deleting */
|
|
beginLaser: function() {
|
|
laser = Entities.addEntity({
|
|
type: "Shape",
|
|
shape: "Cylinder",
|
|
registrationPoint: {x: 0.5, y: 0, z: 0.5 },
|
|
dimensions: {x: 0.01, y: 1, z: 0.01 },
|
|
lifetime: LASER_LIFETIME_S,
|
|
collisionless: true,
|
|
grab: { grabbable: false },
|
|
localPosition: {x: 0, y: 0, z: 0 },
|
|
color: _this.color,
|
|
name: "Whiteboard HMD Beam",
|
|
parentID: _this.entityID
|
|
}, 'avatar');
|
|
},
|
|
|
|
/* Set the length of the laser to match the distance from the user's paint sphere to the whiteboard intersection */
|
|
updateLaser: function(whiteBoardIntersectionData) {
|
|
var currentAge = Entities.getEntityProperties(laser, 'age').age;
|
|
Entities.editEntity(laser, {
|
|
lifetime: currentAge + LASER_LIFETIME_S,
|
|
dimensions: {x: 0.005, y: whiteBoardIntersectionData.distance, z: 0.005 }
|
|
});
|
|
},
|
|
|
|
/* Save previous point data, get intersection data, and project point if necessary. Calculate displacement
|
|
of point from line start and if the point is in air, set the normal to default. If collecting initial line data,
|
|
set start position. Store and return onBoard data for comparison with next point. */
|
|
getHMDLinePointData: function(force) {
|
|
// forced on initial run through to collect line start data
|
|
if (!initialLineStartDataReady && !force) {
|
|
isCurrentPointOnBoard = -1;
|
|
}
|
|
if (initialLineStartDataReady) {
|
|
previousLinePoint = currentPoint;
|
|
previousNormal = currentNormal;
|
|
previousStrokeWidth = currentStrokeWidth;
|
|
}
|
|
var sphereProperties = Entities.getEntityProperties(_this.entityID, ['position', 'rotation', 'dimensions']);
|
|
currentPoint = sphereProperties.position;
|
|
currentStrokeWidth = sphereProperties.dimensions.x;
|
|
var whiteBoardIntersectionData = _this.getHMDIntersectionData(currentPoint);
|
|
if (whiteBoardIntersectionData === -1) {
|
|
return;
|
|
}
|
|
var isCurrentPointOnBoard = _this.maybeProjectPointOntoBoard(whiteBoardIntersectionData, false);
|
|
displacementFromStart = Vec3.subtract(currentPoint, lineStartPosition);
|
|
if (!isCurrentPointOnBoard) {
|
|
currentNormal = DEFAULT_NORMAL;
|
|
}
|
|
if (!initialLineStartDataReady) {
|
|
lineStartPosition = currentPoint;
|
|
initialLineStartDataReady = true;
|
|
}
|
|
wasLastPointOnBoard = isCurrentPointOnBoard;
|
|
return isCurrentPointOnBoard;
|
|
},
|
|
|
|
/* Create ray from paint sphere away from hand along outstretched finger. If it intersects the whiteboard,
|
|
check if this is only a selection and return if so. Draw laser if none exists yet. */
|
|
getHMDIntersectionData: function(origin) {
|
|
var pickRay = {
|
|
origin: origin,
|
|
direction: Vec3.multiplyQbyV(Quat.multiply(MyAvatar.orientation,
|
|
MyAvatar.getAbsoluteJointRotationInObjectFrame(parentJointIndex)), [0, 1, 0])
|
|
};
|
|
var whiteBoardIntersectionData = Entities.findRayIntersection(pickRay, true, whiteboardParts);
|
|
if (whiteBoardIntersectionData.intersects) {
|
|
var intersectedWhiteboardPartName = Entities.getEntityProperties(whiteBoardIntersectionData.entityID,
|
|
'name').name;
|
|
if (intersectedWhiteboardPartName !== "Whiteboard") {
|
|
if (initialLineStartDataReady) {
|
|
_this.stopDrawing();
|
|
}
|
|
return -1;
|
|
}
|
|
if (!laser) {
|
|
_this.beginLaser();
|
|
} else {
|
|
_this.updateLaser(whiteBoardIntersectionData);
|
|
}
|
|
}
|
|
return whiteBoardIntersectionData;
|
|
},
|
|
|
|
/* On trigger press, if the user is not in whiteboard zone, delete this sphere. If the user is using tablet or grip
|
|
button, ignore. Get line point data and begin draw sound then start an interval to continue collecting data
|
|
and drawing */
|
|
triggerPressed: function() {
|
|
if (!readyToDraw) {
|
|
return;
|
|
}
|
|
if (!_this.isUserInZone(whiteboardZone)) {
|
|
Entities.deleteEntity(_this.entityID);
|
|
}
|
|
if (tablet.tabletShown || activeGripPress) {
|
|
return;
|
|
}
|
|
var isCurrentPointOnBoard = _this.getHMDLinePointData(true);
|
|
if (isCurrentPointOnBoard === -1) {
|
|
return;
|
|
}
|
|
_this.playSound(DRAW_SOUND, DRAW_SOUND_VOLUME, lineStartPosition, true, true);
|
|
drawInterval = Script.setInterval(function() { // for trigger presses, check the position on an interval to draw
|
|
if (initialLineStartDataReady) {
|
|
isCurrentPointOnBoard = _this.getHMDLinePointData(false);
|
|
if (isCurrentPointOnBoard === -1) {
|
|
return;
|
|
}
|
|
_this.draw(isCurrentPointOnBoard);
|
|
}
|
|
}, REPEAT_DISTANCE_CHECK_MS);
|
|
},
|
|
|
|
/* On releasing trigger, if the user is not in whiteboard zone, delete this sphere. If laser or drawing interval
|
|
exists, delete it.*/
|
|
triggerReleased: function() {
|
|
if (!_this.isUserInZone(whiteboardZone)) {
|
|
Entities.deleteEntity(_this.entityID);
|
|
}
|
|
_this.stopDrawing();
|
|
if (laser) {
|
|
Entities.deleteEntity(laser);
|
|
laser = null;
|
|
}
|
|
if (drawInterval) {
|
|
Script.clearInterval(drawInterval);
|
|
drawInterval = null;
|
|
}
|
|
},
|
|
|
|
/* On grip press, if the user is not in whiteboard zone, delete this sphere. If the user is using tablet or trigger
|
|
button, ignore. Begin a deleting interval that gets intersection data, updates laser, and deletes from the
|
|
current point. */
|
|
gripPressed: function() {
|
|
if (!_this.isUserInZone(whiteboardZone)) {
|
|
Entities.deleteEntity(_this.entityID);
|
|
}
|
|
if (tablet.tabletShown || activeTriggerPress) {
|
|
return;
|
|
}
|
|
deletingInterval = Script.setInterval(function() {
|
|
var sphereProperties = Entities.getEntityProperties(_this.entityID, ['position', 'rotation', 'dimensions']);
|
|
currentPoint = sphereProperties.position;
|
|
var whiteBoardIntersectionData = _this.getHMDIntersectionData(currentPoint);
|
|
var isCurrentPointOnBoard = _this.maybeProjectPointOntoBoard(whiteBoardIntersectionData, false);
|
|
if (isCurrentPointOnBoard) {
|
|
// delete on board
|
|
if (!laser) {
|
|
_this.beginLaser();
|
|
} else {
|
|
_this.updateLaser(whiteBoardIntersectionData);
|
|
}
|
|
}
|
|
_this.deleteFromPoint(currentPoint);
|
|
}, DELETE_AGAIN_MS);
|
|
},
|
|
|
|
/* On releasing grip, stop the interval that is searching for lines to delete */
|
|
gripReleased: function() {
|
|
if (!_this.isUserInZone(whiteboardZone)) {
|
|
Entities.deleteEntity(_this.entityID);
|
|
}
|
|
if (deletingInterval) {
|
|
Script.clearInterval(deletingInterval);
|
|
deletingInterval = null;
|
|
}
|
|
if (laser) {
|
|
Entities.deleteEntity(laser);
|
|
laser = null;
|
|
}
|
|
},
|
|
|
|
/* Stop drawing sound, reset line data */
|
|
stopDrawing: function() {
|
|
if (injector) {
|
|
injector.stop();
|
|
injector = null;
|
|
}
|
|
if (!polyLine) {
|
|
return;
|
|
}
|
|
initialLineStartDataReady = false;
|
|
polyLine = null;
|
|
currentPoint = null;
|
|
previousLinePoint = null;
|
|
},
|
|
|
|
/* Get correct animation overrides depending on dominant hand */
|
|
getAnimationData: function() {
|
|
if (dominantHand === "right") {
|
|
animationData.rightHandType = 0;
|
|
animationData.isRightHandGrasp = false;
|
|
animationData.isRightIndexPoint = true;
|
|
animationData.isRightThumbRaise = false;
|
|
animationData.isRightIndexPointAndThumbRaise = false;
|
|
} else {
|
|
animationData.leftHandType = 0;
|
|
animationData.isLeftHandGrasp = false;
|
|
animationData.isLeftIndexPoint = true;
|
|
animationData.isLeftThumbRaise = false;
|
|
animationData.isLeftIndexPointAndThumbRaise = false;
|
|
}
|
|
return animationData;
|
|
},
|
|
|
|
/* Create controller mapping to listen for trigger presses */
|
|
setUpHMDMode: function() {
|
|
if (controllerMapping) {
|
|
controllerMapping.enable();
|
|
}
|
|
animationHandlerID = MyAvatar.addAnimationStateHandler(_this.getAnimationData, []);
|
|
Messages.sendLocalMessage("Hifi-Hand-Disabler", dominantHand);
|
|
},
|
|
|
|
/* Listen for mouse presses */
|
|
setUpDesktopMode: function() {
|
|
if (!mouseEventsConnected) {
|
|
mouseEventsConnected = true;
|
|
Controller.mousePressEvent.connect(_this.mousePressed);
|
|
Controller.mouseMoveEvent.connect(_this.mouseContinueLine);
|
|
Controller.mouseReleaseEvent.connect(_this.mouseReleased);
|
|
}
|
|
},
|
|
|
|
/* Remove controller mapping */
|
|
closeHMDMode: function() {
|
|
if (controllerMapping) {
|
|
controllerMapping.disable();
|
|
}
|
|
Messages.sendLocalMessage("Hifi-Hand-Disabler", "none");
|
|
if (animationHandlerID) {
|
|
animationHandlerID = MyAvatar.removeAnimationStateHandler(animationHandlerID);
|
|
}
|
|
},
|
|
|
|
/* Stop listening for mouse presses */
|
|
closeDesktopMode: function() {
|
|
if (mouseEventsConnected) {
|
|
mouseEventsConnected = false;
|
|
Controller.mousePressEvent.disconnect(_this.mousePressed);
|
|
Controller.mouseMoveEvent.disconnect(_this.mouseContinueLine);
|
|
Controller.mouseReleaseEvent.disconnect(_this.mouseReleased);
|
|
}
|
|
},
|
|
|
|
/* Listen for controller trigger movements and act when the trigger is pressed or
|
|
released */
|
|
registerControllerMapping: function() {
|
|
controllerMapping = Controller.newMapping(controllerMappingName);
|
|
controllerMapping.from(Controller.Standard.RT).to(function (value) {
|
|
if (dominantHand === "right") {
|
|
if (value >= MINIMUM_TRIGGER_PRESS_VALUE && !activeTriggerPress) {
|
|
activeTriggerPress = true;
|
|
_this.triggerPressed();
|
|
} else if (value <= MINIMUM_TRIGGER_PRESS_VALUE && activeTriggerPress) {
|
|
activeTriggerPress = false;
|
|
_this.triggerReleased();
|
|
}
|
|
}
|
|
});
|
|
controllerMapping.from(Controller.Standard.RightGrip).to(function (value) {
|
|
if (dominantHand === "right") {
|
|
if (value >= MINIMUM_TRIGGER_PRESS_VALUE && !activeGripPress) {
|
|
activeGripPress = true;
|
|
_this.gripPressed();
|
|
} else if (value <= MINIMUM_TRIGGER_PRESS_VALUE && activeGripPress) {
|
|
activeGripPress = false;
|
|
_this.gripReleased();
|
|
}
|
|
}
|
|
});
|
|
controllerMapping.from(Controller.Standard.LT).to(function (value) {
|
|
if (dominantHand === "left") {
|
|
if (value >= MINIMUM_TRIGGER_PRESS_VALUE && !activeTriggerPress ) {
|
|
activeTriggerPress = true;
|
|
_this.triggerPressed();
|
|
} else if (value <= MINIMUM_TRIGGER_PRESS_VALUE && activeTriggerPress) {
|
|
activeTriggerPress = false;
|
|
_this.triggerReleased();
|
|
}
|
|
}
|
|
});
|
|
controllerMapping.from(Controller.Standard.LeftGrip).to(function (value) {
|
|
if (dominantHand === "left") {
|
|
if (value >= MINIMUM_TRIGGER_PRESS_VALUE && !activeGripPress) {
|
|
activeGripPress = true;
|
|
_this.gripPressed();
|
|
} else if (value <= MINIMUM_TRIGGER_PRESS_VALUE && activeGripPress) {
|
|
activeGripPress = false;
|
|
_this.gripReleased();
|
|
}
|
|
}
|
|
});
|
|
},
|
|
|
|
/* Delete paint sphere in hand when leaving the domain */
|
|
domainChanged: function() {
|
|
Script.setTimeout(function() {
|
|
Entities.deleteEntity(_this.entityID);
|
|
}, WAIT_TO_CLEAN_UP_MS);
|
|
},
|
|
|
|
/* Delete paint sphere in hand when switching dominant hand */
|
|
handChanged: function() {
|
|
Entities.deleteEntity(_this.entityID);
|
|
},
|
|
|
|
/* If opening tablet, stop action and close drawing view mode. When the tablet closes, set up correct drawing mode. */
|
|
tabletShownChanged: function() {
|
|
if (tablet.tabletShown) {
|
|
if (HMD.active) {
|
|
if (activeTriggerPress) {
|
|
_this.triggerReleased();
|
|
} else if (activeGripPress) {
|
|
_this.gripReleased();
|
|
}
|
|
_this.closeHMDMode();
|
|
} else {
|
|
_this.mouseReleased();
|
|
_this.closeDesktopMode();
|
|
}
|
|
} else {
|
|
if (HMD.active) {
|
|
_this.setUpHMDMode();
|
|
} else {
|
|
_this.setUpDesktopMode();
|
|
}
|
|
}
|
|
},
|
|
|
|
/* Set variable to track which mode to use to draw lines */
|
|
displayModeChanged: function() {
|
|
if (HMD.active) {
|
|
_this.closeDesktopMode();
|
|
_this.setUpHMDMode();
|
|
} else {
|
|
_this.closeHMDMode();
|
|
_this.setUpDesktopMode();
|
|
}
|
|
},
|
|
|
|
/* Close drawing view mode, stop injector, play closing sound, disable hand mapping and overrides. Remove
|
|
animation handlers, delete all intervals, and disconnect signals. */
|
|
unload: function() {
|
|
if (HMD.active) {
|
|
_this.closeHMDMode();
|
|
} else {
|
|
_this.closeDesktopMode();
|
|
}
|
|
if (injector) {
|
|
injector.stop();
|
|
injector = null;
|
|
}
|
|
_this.playSound(CLOSE_SOUND, CLOSE_SOUND_VOLUME, MyAvatar.position, true, false);
|
|
if (controllerMapping) {
|
|
controllerMapping.disable();
|
|
}
|
|
Messages.sendLocalMessage("Hifi-Hand-Disabler", "none");
|
|
if (animationHandlerID) {
|
|
animationHandlerID = MyAvatar.removeAnimationStateHandler(animationHandlerID);
|
|
}
|
|
if (drawInterval) {
|
|
Script.clearInterval(drawInterval);
|
|
drawInterval = null;
|
|
}
|
|
if (deletingInterval) {
|
|
Script.clearInterval(deletingInterval);
|
|
deletingInterval = null;
|
|
}
|
|
tablet.tabletShownChanged.disconnect(_this.tabletShownChanged);
|
|
MyAvatar.dominantHandChanged.disconnect(_this.handChanged);
|
|
HMD.displayModeChanged.disconnect(_this.displayModeChanged);
|
|
Window.domainChanged.disconnect(_this.domainChanged);
|
|
}
|
|
};
|
|
|
|
return new PaintSphere();
|
|
}); |