content/hifi-content/ben/GreeterApps/fsm_inspect.js
2022-02-13 21:50:01 +01:00

451 lines
No EOL
11 KiB
JavaScript

//
// inspect.js
// examples
//
// Created by Clément Brisset on March 20, 2014
// Copyright 2014 High Fidelity, Inc.
//
// Allows you to inspect non moving objects (Voxels or Avatars) using Atl, Control (Command on Mac) and Shift
//
// radial mode = hold ALT
// orbit mode = hold ALT + CONTROL
// pan mode = hold ALT + CONTROL + SHIFT
// Once you are in a mode left click on the object to inspect and hold the click
// Dragging the mouse will move your camera according to the mode you are in.
//
// CtrlAltStudio modifications:
// - Leave camera where it is when release Alt key.
// - Restore camera to default position when press Esc key or move avatar.
// - Make camera control start immediately rather than having a 0.5s delay at the start.
// - Add ability to orbit about a point in space if no object intersects mouse click.
//
// CtrlAltStudio information: http://ctrlaltstudio.com/hifi/inspect
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
// Wolfgang Modifications,
// - Hide cursor
// - Make camera move on first click
//
// Flame Soulis's Modifications,
// - Ignore Invisible
var PI = Math.PI;
var RAD_TO_DEG = 180.0 / PI;
var AZIMUTH_RATE = 90.0;
var ALTITUDE_RATE = 200.0;
var RADIUS_RATE = 1.0 / 100.0;
var PAN_RATE = 250.0;
var Y_AXIS = {
x: 0,
y: 1,
z: 0
};
var X_AXIS = {
x: 1,
y: 0,
z: 0
};
var LOOK_AT_TIME = 500;
// CAS...
var AVATAR_POSITION_SLOP = 0.1;
var AVATAR_ROTATION_SLOP = 0.09; // 5 degrees
// ... CAS
var alt = false;
var shift = false;
var control = false;
var isActive = false;
var overlayID = null;
var oldMode = Camera.mode;
var noMode = 0;
var orbitMode = 1;
var radialMode = 2;
var panningMode = 3;
var detachedMode = 4;
var mode = noMode;
var mouseLastX = 0;
var mouseLastY = 0;
var center = {
x: 0,
y: 0,
z: 0
};
var position = {
x: 0,
y: 0,
z: 0
};
var vector = {
x: 0,
y: 0,
z: 0
};
var radius = 0.0;
var azimuth = 0.0;
var altitude = 0.0;
var avatarPosition;
var avatarOrientation;
var rotatingTowardsTarget = false;
var targetCamOrientation;
var oldPosition, oldOrientation;
var startPosition, startOrientation, startTime, timeDelta, timer;
var timeDiv = 0.01;
function orientationOf(vector) {
var direction,
yaw,
pitch;
direction = Vec3.normalize(vector);
yaw = Quat.angleAxis(Math.atan2(direction.x, direction.z) * RAD_TO_DEG, Y_AXIS);
pitch = Quat.angleAxis(Math.asin(-direction.y) * RAD_TO_DEG, X_AXIS);
return Quat.multiply(yaw, pitch);
}
function handleRadialMode(dx, dy) {
azimuth += dx / AZIMUTH_RATE;
radius += radius * dy * RADIUS_RATE;
if (radius < 1) {
radius = 1;
}
vector = {
x: (Math.cos(altitude) * Math.cos(azimuth)) * radius,
y: Math.sin(altitude) * radius,
z: (Math.cos(altitude) * Math.sin(azimuth)) * radius
};
position = Vec3.sum(center, vector);
}
function handleOrbitMode(dx, dy) {
azimuth += dx / AZIMUTH_RATE;
altitude += dy / ALTITUDE_RATE;
if (altitude > PI / 2.0) {
altitude = PI / 2.0;
}
if (altitude < -PI / 2.0) {
altitude = -PI / 2.0;
}
vector = {
x: (Math.cos(altitude) * Math.cos(azimuth)) * radius,
y: Math.sin(altitude) * radius,
z: (Math.cos(altitude) * Math.sin(azimuth)) * radius
};
position = Vec3.sum(center, vector);
}
function handlePanMode(dx, dy) {
var up = Quat.getUp(Camera.getOrientation());
var right = Quat.getRight(Camera.getOrientation());
var distance = Vec3.length(vector);
var dv = Vec3.sum(Vec3.multiply(up, distance * dy / PAN_RATE), Vec3.multiply(right, -distance * dx / PAN_RATE));
center = Vec3.sum(center, dv);
position = Vec3.sum(position, dv);
}
function saveCameraState() {
oldMode = Camera.mode;
oldPosition = Camera.getPosition();
oldOrientation = Camera.getOrientation();
Camera.mode = "independent";
Camera.setPosition(oldPosition);
}
function restoreCameraState() {
Camera.mode = oldMode;
Camera.setPosition(oldPosition);
Camera.setOrientation(oldOrientation);
}
function handleModes() {
var newMode = (mode == noMode) ? noMode : detachedMode;
if (alt) {
if (control) {
if (shift) {
newMode = panningMode;
} else {
newMode = orbitMode;
}
} else {
newMode = radialMode;
}
}
// if entering detachMode
if (newMode == detachedMode && mode != detachedMode) {
avatarPosition = MyAvatar.position;
avatarOrientation = MyAvatar.orientation;
}
// if leaving detachMode
// CAS...
/*
if (mode == detachedMode && newMode == detachedMode &&
(avatarPosition.x != MyAvatar.position.x ||
avatarPosition.y != MyAvatar.position.y ||
avatarPosition.z != MyAvatar.position.z ||
avatarOrientation.x != MyAvatar.orientation.x ||
avatarOrientation.y != MyAvatar.orientation.y ||
avatarOrientation.z != MyAvatar.orientation.z ||
avatarOrientation.w != MyAvatar.orientation.w)) {
newMode = noMode;
}
*/
//... CAS
if (mode == detachedMode && newMode == detachedMode && (
Vec3.length(Vec3.subtract(avatarPosition, MyAvatar.position)) > AVATAR_POSITION_SLOP
|| Vec3.length(Vec3.subtract(Quat.getFront(avatarOrientation), Quat.getFront(MyAvatar.orientation))) > AVATAR_ROTATION_SLOP)) {
newMode = noMode;
}
if (mode == noMode && newMode != noMode && Camera.mode == "independent") {
newMode = noMode;
}
// if leaving noMode
if (mode == noMode && newMode != noMode) {
saveCameraState();
}
// if entering noMode
if (newMode == noMode && mode != noMode) {
restoreCameraState();
}
mode = newMode;
}
function keyPressEvent(event) {
var changed = false;
if (event.text == "ALT") {
alt = true;
changed = true;
}
if (event.text == "CONTROL") {
control = true;
changed = true;
}
if (event.text == "SHIFT") {
shift = true;
changed = true;
}
// CAS...
if (mode !== noMode && !alt && !control && !shift && /^ESC|LEFT|RIGHT|UP|DOWN|[wasdWASD]$/.test(event.text)) {
mode = noMode;
restoreCameraState();
changed = true;
}
// ...CAS
if (changed) {
handleModes();
}
}
function keyReleaseEvent(event) {
var changed = false;
if (event.text == "ALT") {
alt = false;
changed = true;
// CAS...
/*
mode = noMode;
restoreCameraState();
*/
// ...CAS
}
if (event.text == "CONTROL") {
control = false;
changed = true;
}
if (event.text == "SHIFT") {
shift = false;
changed = true;
}
if (changed) {
handleModes();
}
}
var retScale = 1;
function mousePressEvent(event) {
if (alt && !isActive) {
mouseLastX = event.x;
mouseLastY = event.y;
// Compute trajectories related values
var pickRay = Camera.computePickRay(mouseLastX, mouseLastY);
position = Camera.getPosition();
var avatarTarget = MyAvatar.getTargetAvatarPosition();
var distance = -1;
var string;
var modelIntersection = Entities.findRayIntersection(pickRay, true, [], [], true, true);
if (modelIntersection.intersects && modelIntersection.accurate) {
distance = modelIntersection.distance;
center = modelIntersection.intersection;
// CAS...
} else {
// Orbit about a point in the air.
var ORBIT_DISTANCE = 10;
center = Vec3.sum(Camera.position, Vec3.multiply(ORBIT_DISTANCE, pickRay.direction));
}
{
// ...CAS
string = "Inspecting model";
// CAS...
/*
//We've selected our target, now orbit towards it automatically
rotatingTowardsTarget = true;
//calculate our target cam rotation
Script.setTimeout(function() {
rotatingTowardsTarget = false;
}, LOOK_AT_TIME);
*/
// ... CAS
vector = Vec3.subtract(position, center);
targetCamOrientation = orientationOf(vector);
radius = Vec3.length(vector);
azimuth = Math.atan2(vector.z, vector.x);
altitude = Math.asin(vector.y / Vec3.length(vector));
startPosition = Camera.getPosition();
startOrientation = Camera.getOrientation();
startTime = Date.now();
timer = Script.setInterval(lerpCam,10);
isActive = true;
overlayID = Overlays.addOverlay("sphere",{position:center,dimensions:{x:0.1,y:0.1,z:0.1}});
scaleRet(0);
}
mouseMoveEvent(event);
}
}
function lerpCam(){
timeDelta = (Date.now() - startTime) * timeDiv;
if(timeDelta > 1 || !isActive){
Script.clearInterval(timer);
return;
}
Camera.setPosition(timeVLerp(startPosition,position));
Camera.setOrientation(timeQLerp(startOrientation,orientationOf(vector)));
}
function scaleRet(scale){
if(retScale !== scale){
retScale = scale;
Reticle.scale = retScale;
}
}
function mouseReleaseEvent(event) {
if (isActive) {
isActive = false;
Reticle.setPosition(Vec3.multiply(0.5,{x:Window.innerWidth,y:Window.innerHeight}));
Overlays.deleteOverlay(overlayID);
scaleRet(1);
}
}
function timeVLerp(vecA,vecB){
if(timeDelta > 1)return vecB;
return Vec3.mix(vecA,vecB,timeDelta);
}
function timeQLerp(quatA,quatB){
if(timeDelta > 1)return quatB;
return Quat.mix(quatA,quatB,timeDelta);
}
function mouseMoveEvent(event) {
if (isActive){
if(mode != noMode && !rotatingTowardsTarget) {
timeDelta = (Date.now() - startTime) * timeDiv;
if (mode == radialMode) {
handleRadialMode(event.x - mouseLastX, event.y - mouseLastY);
}
if (mode == orbitMode) {
handleOrbitMode(event.x - mouseLastX, event.y - mouseLastY);
}
if (mode == panningMode) {
handlePanMode(event.x - mouseLastX, event.y - mouseLastY);
}
Camera.setPosition(timeVLerp(startPosition,position));
Camera.setOrientation(timeQLerp(startOrientation,orientationOf(vector)));
var dist = Vec3.distance(center,position);
Overlays.editOverlay(overlayID,{dimensions:Vec3.multiply(dist,{x:0.01,y:0.01,z:0.01}),position:center});
}
}else {
scaleRet(1);
}
mouseLastX = event.x;
mouseLastY = event.y;
}
function update() {
handleModes();
if (rotatingTowardsTarget) {
rotateTowardsTarget();
}
}
function rotateTowardsTarget() {
var newOrientation = Quat.mix(Camera.getOrientation(), targetCamOrientation, .1);
Camera.setOrientation(newOrientation);
}
function scriptEnding() {
if (mode != noMode) {
restoreCameraState();
}
}
Controller.keyPressEvent.connect(keyPressEvent);
Controller.keyReleaseEvent.connect(keyReleaseEvent);
Controller.mousePressEvent.connect(mousePressEvent);
Controller.mouseReleaseEvent.connect(mouseReleaseEvent);
Controller.mouseMoveEvent.connect(mouseMoveEvent);
Script.update.connect(update);
Script.scriptEnding.connect(scriptEnding);