content/hifi-content/dante/scripts/mClient.js
2022-02-13 22:49:05 +01:00

349 lines
18 KiB
JavaScript

"use strict";
(function () { // BEGIN LOCAL SCOPE
// VARIABLES
var _this = this;
var debug = false;
var mobileSpectatorCamera = true;
var resolution = 1024;
var defaultDimLength = 0.025;
var mirrorOverlayID;
var mirrorOverlayRunning;
var mirrorScalerGrabbed;
var mirrorOverlayOffset = 0.01;
var mirrorScalerID;
var mirrorToggleID;
var mirrorToggleOverlayID;
var spectatorCameraConfig = Render.getConfig("SecondaryCamera");
var debugSpectatorCameraID;
var debugNearClipPlaneID;
var mirrorToggleOverlayModelInactiveURL = "https://hifi-content.s3.amazonaws.com/patrickmanalich/mirrorFolder/models/mirrorToggleOverlayInactive.fbx";
var mirrorToggleOverlayModelActiveURL = "https://hifi-content.s3.amazonaws.com/patrickmanalich/mirrorFolder/models/mirrorToggleOverlayActive.fbx";
// LOCAL FUNCTIONS
// Takes in the spectator camera position and creates an array of the front four vertices of the mirror. It then calculates
// the distance between each vertex and the spectator camera position and returns the farthest distance.
function findNearClipPlaneDistance(spectatorCameraPos) {
var mirrorProps = Entities.getEntityProperties(_this.entityID, ["position", "dimensions", "rotation"]);
var mirrorFrontVertices = [];
// Upper Right, 1 is the Lower Right, 2 is the Upper Left, and 3 is Lower Left
var vertexPosX;
var vertexPosY;
var vertexPosZ;
for (var i = 0; i < 2; i++) {
if (i === 0){
vertexPosX = 0.5 * mirrorProps.dimensions.x;
} else {
vertexPosX = -0.5 * mirrorProps.dimensions.x;
}
for (var j = 0; j < 2; j++) {
if (j === 0){
vertexPosY = 0.5 * mirrorProps.dimensions.y;
} else {
vertexPosY = -0.5 * mirrorProps.dimensions.y;
}
vertexPosZ = 0.5 * mirrorProps.dimensions.z;
var localPos = { x: vertexPosX, y: vertexPosY, z: vertexPosZ };
mirrorFrontVertices[j+i*2] = Vec3.sum(Vec3.multiplyQbyV(mirrorProps.rotation, localPos), mirrorProps.position);
}
}
var maxDistance = 0;
for (var i = 0; i < 4; i++) {
var distance = Math.abs(Vec3.distance(mirrorFrontVertices[i], spectatorCameraPos));
if (distance > maxDistance) {
maxDistance = distance;
}
}
return maxDistance;
}
// Updates the spectator camera configuration to orient the view frustrum in such a way that it mimics
// the way a mirror reflects what you percieve based on where you are standing relative to the mirror if 'mobileSpectatorCamera'
// is true. Else it will place the spectator camera at the center of the mirror and will be immobile
function updateSpectatorCamera() {
if (mirrorOverlayRunning) {
var mirrorProps = Entities.getEntityProperties(_this.entityID, ["dimensions", "position", "rotation"]);
var headPos = Camera.getPosition();
var adjustedPos;
if (!mirrorScalerGrabbed) {
adjustedPos = { x: 0, y: 0, z: 0};
} else {
adjustedPos = { x: (mirrorProps.dimensions.x * 0.5), y: (mirrorProps.dimensions.y * -0.5), z: 0 };
}
var rotatedAdjustedPos = Vec3.multiplyQbyV(mirrorProps.rotation, adjustedPos);
var rotatedAdjustedMirrorPos = Vec3.sum(mirrorProps.position, rotatedAdjustedPos);
if (mobileSpectatorCamera) { // mobile
var mirrorToHeadVec = Vec3.subtract(headPos, rotatedAdjustedMirrorPos);
var zLocalVecNormalized = Vec3.multiplyQbyV(mirrorProps.rotation, Vec3.UNIT_Z);
var distanceFromMirror = (Vec3.dot(zLocalVecNormalized, mirrorToHeadVec));
var oppositeSideMirrorPos = Vec3.subtract(headPos, Vec3.multiply(2 * distanceFromMirror, zLocalVecNormalized));
spectatorCameraConfig.orientation = Quat.lookAt(oppositeSideMirrorPos, rotatedAdjustedMirrorPos, Vec3.multiplyQbyV(mirrorProps.rotation, Vec3.UP));
spectatorCameraConfig.position = oppositeSideMirrorPos;
spectatorCameraConfig.nearClipPlaneDistance = findNearClipPlaneDistance(spectatorCameraConfig.position);
var distanceAway = Vec3.distance(rotatedAdjustedMirrorPos, headPos);
var halfHeight = mirrorProps.dimensions.y / 2;
var halfAngle = Math.atan(halfHeight/distanceAway) / (Math.PI / 180);
spectatorCameraConfig.vFoV = halfAngle * 2;
} else {
spectatorCameraConfig.orientation = Quat.multiply(mirrorProps.rotation, Quat.fromPitchYawRollDegrees(0,180,0));
spectatorCameraConfig.position = rotatedAdjustedMirrorPos;
spectatorCameraConfig.nearClipPlaneDistance = (mirrorProps.dimensions.z / 2) + mirrorOverlayOffset;
spectatorCameraConfig.vFoV = 45;
}
if (debug) {
Entities.editEntity(debugSpectatorCameraID, {position: oppositeSideMirrorPos});
Entities.editEntity(debugSpectatorCameraID, {rotation: spectatorCameraConfig.orientation});
var offsetVector = Vec3.multiply(Vec3.FRONT, findNearClipPlaneDistance(spectatorCameraConfig.position));
var relativeOffset = Vec3.multiplyQbyV(spectatorCameraConfig.orientation, offsetVector);
var worldPosition = Vec3.sum(spectatorCameraConfig.position, relativeOffset);
Entities.editEntity(debugNearClipPlaneID, {position: worldPosition});
Entities.editEntity(debugNearClipPlaneID, {rotation: spectatorCameraConfig.orientation});
}
}
}
// Calls 'updateMirrorOverlay' once to set up mirror overlay, then connects 'updateSpectatorCamera' and starts rendering
function mirrorOverlayOn() {
mirrorOverlayRunning = true;
if (!spectatorCameraConfig.attachedEntityId) {
Overlays.editOverlay(mirrorToggleOverlayID, { url: mirrorToggleOverlayModelActiveURL });
Entities.callEntityMethod(_this.entityID, 'updateMirrorOverlay', []);
Script.update.connect(updateSpectatorCamera);
spectatorCameraConfig.enableSecondaryCameraRenderConfigs(true);
if (debug) {
Entities.editEntity(debugSpectatorCameraID, { visible: true });
Entities.editEntity(debugNearClipPlaneID, { visible: true });
}
} else {
print("Cannot turn on mirror if spectator camera is already in use");
}
}
// Deletes the mirror overlay and disconnects 'updateSpectatorCamera' and rendering
function mirrorOverlayOff() {
if (!spectatorCameraConfig.attachedEntityId) {
spectatorCameraConfig.enableSecondaryCameraRenderConfigs(false);
if (mirrorOverlayRunning) {
Overlays.editOverlay(mirrorToggleOverlayID, { url: mirrorToggleOverlayModelInactiveURL });
Overlays.deleteOverlay(mirrorOverlayID);
Script.update.disconnect(updateSpectatorCamera);
if (debug) {
Entities.editEntity(debugSpectatorCameraID, { visible: false });
Entities.editEntity(debugNearClipPlaneID, { visible: false });
}
}
} else {
print("Cannot turn off mirror if spectator camera is already in use");
}
mirrorOverlayRunning = false;
}
// ENTITY FUNCTIONS
// Called only once when the script is loaded in. Creates mirror scalers and sets their names, IDs, and positions
_this.preload = function(entityID) {
Script.setTimeout(function() {
print("preload mirror client");
_this.entityID = entityID;
var mirrorProps = Entities.getEntityProperties(_this.entityID, ["position", "rotation", "dimensions"]);
var foundEntitiesArray = Entities.findEntities(mirrorProps.position, 0.01);
foundEntitiesArray.forEach(function(foundEntityID) {
var foundEntityName = Entities.getEntityProperties(foundEntityID, ["name"]).name;
if (foundEntityName === "mirrorToggle") {
mirrorToggleID = foundEntityID;
} else if (foundEntityName === "mirrorScaler") {
mirrorScalerID = foundEntityID;
}
});
var localAdjustedPos = {
x: (mirrorProps.dimensions.x * 0.5),
y: (mirrorProps.dimensions.y * -0.5),
z: 0
};
var localRotatedAdjustedPos = Vec3.multiplyQbyV(mirrorProps.rotation, localAdjustedPos);
var worldRotatedAdjustedPos = Vec3.sum(mirrorProps.position, localRotatedAdjustedPos);
Entities.editEntity(mirrorScalerID, { position: worldRotatedAdjustedPos } );
Entities.editEntity(mirrorScalerID, { visible: true } );
localAdjustedPos = {
x: (mirrorProps.dimensions.x * 0.4),
y: (mirrorProps.dimensions.y * 0.4),
z: mirrorOverlayOffset
};
localRotatedAdjustedPos = Vec3.multiplyQbyV(mirrorProps.rotation, localAdjustedPos);
worldRotatedAdjustedPos = Vec3.sum(mirrorProps.position, localRotatedAdjustedPos);
Entities.editEntity(mirrorToggleID, { position: worldRotatedAdjustedPos } );
var mirrorToggleProps = Entities.getEntityProperties(mirrorToggleID, ["position","rotation"]);
mirrorToggleOverlayID = Overlays.addOverlay("model", {
name: "mirrorToggleOverlay",
url: mirrorToggleOverlayModelInactiveURL,
position: mirrorToggleProps.position,
localRotation: Quat.multiply(mirrorToggleProps.rotation, { w: 0, x: -0.707, y: 0, z: -0.707}),
dimensions: { x: defaultDimLength / 3, y: defaultDimLength, z: defaultDimLength },
visible: true
});
mirrorOverlayRunning = false;
mirrorScalerGrabbed = false;
if (debug) {
debugSpectatorCameraID = Entities.addEntity({
name: "debugSpectatorCamera",
dimensions: {
x: 0.1,
y: 0.1,
z: 0.1
},
collisionless: true,
visible: false,
type: "Model",
modelURL: "https://hifi-content.s3.amazonaws.com/patrickmanalich/mirrorFolder/models/spectatorCamera.fbx"
});
debugNearClipPlaneID = Entities.addEntity({
name: "debugNearClipPlane",
color: {
red: 0,
blue: 255,
green: 255
},
dimensions: {
x: 0.1,
y: 0.2,
z: 0.005
},
collisionless: true,
visible: false,
type: "Box"
});
}
}, 1500);
};
// Takes in an mirror scaler number which is used for the index of "halfDimSigns" that is needed to adjust the mirror
// overlay's position. Deletes and re-adds the mirror overlay so the url and position is updated, and resets the
// resolution of the spectator camera
_this.updateMirrorOverlay = function (entityID, data) {
if (mirrorOverlayRunning) {
var mirrorProps = Entities.getEntityProperties(_this.entityID, ["rotation", "dimensions", "position"]);
var dimX = mirrorProps.dimensions.x;
var dimY = mirrorProps.dimensions.y;
Overlays.deleteOverlay(mirrorOverlayID);
mirrorOverlayID = Overlays.addOverlay("image3d", {
name: "mirrorOverlay",
url: "resource://spectatorCameraFrame",
emissive: true,
parentID: _this.entityID,
alpha: 1,
rotation: mirrorProps.rotation,
dimensions: {
x: -(dimY > dimX ? dimY : dimX),
y: -(dimY > dimX ? dimY : dimX),
z: 0
}
});
if (!mirrorScalerGrabbed) {
Overlays.editOverlay(mirrorOverlayID, {localPosition: { x: 0, y: 0, z: mirrorOverlayOffset }});
} else {
Overlays.editOverlay(mirrorOverlayID, {localPosition: { x: (dimX * 0.5), y: (dimY * -0.5), z: mirrorOverlayOffset} });
}
spectatorCameraConfig.resetSizeSpectatorCamera(dimX * resolution, dimY * resolution);
}
};
// Toggle the mirror overlay on and off
_this.toggleMirrorOverlay = function (entityID, data) { // TODO: convert this into one function like adjustMirrorToggle
if (!mirrorOverlayRunning) {
mirrorOverlayOn();
} else {
mirrorOverlayOff();
}
};
// Toggle the mirror overlay on and off
_this.adjustMirrorToggle = function (entityID, data) {
if (!mirrorScalerGrabbed) {
mirrorScalerGrabbed = true;
Overlays.editOverlay(mirrorToggleOverlayID, {visible: false});
Entities.editEntity(mirrorToggleID, {userData: "{\"grabbableKey\":{\"wantsTrigger\":false}}"});
} else {
var mirrorProps = Entities.getEntityProperties(_this.entityID, ["position", "rotation", "dimensions"]);
var localAdjustedPos = {
x: (mirrorProps.dimensions.x * 0.4),
y: (mirrorProps.dimensions.y * 0.4),
z: mirrorOverlayOffset
};
var localRotatedAdjustedPos = Vec3.multiplyQbyV(mirrorProps.rotation, localAdjustedPos);
var worldRotatedAdjustedPos = Vec3.sum(mirrorProps.position, localRotatedAdjustedPos);
Entities.editEntity(mirrorToggleID, { position: worldRotatedAdjustedPos } );
Entities.editEntity(mirrorToggleID, { rotation: mirrorProps.rotation } );
Entities.editEntity(mirrorToggleID, { dimensions: {x: JSON.parse(data), y: JSON.parse(data), z: JSON.parse(data)} } );
Entities.editEntity(mirrorToggleID, {userData: "{\"grabbableKey\":{\"wantsTrigger\":true}}"});
var mirrorToggleProps = Entities.getEntityProperties(mirrorToggleID, ["position", "rotation", "dimensions"]);
Overlays.editOverlay(mirrorToggleOverlayID, { position: mirrorToggleProps.position } );
Overlays.editOverlay(mirrorToggleOverlayID, { rotation: Quat.multiply(mirrorToggleProps.rotation, { w: 0, x: -0.707, y: 0, z: -0.707}) } );
Overlays.editOverlay(mirrorToggleOverlayID, { dimensions: { x: JSON.parse(data) / 3, y: JSON.parse(data), z: JSON.parse(data) } } );
Overlays.editOverlay(mirrorToggleOverlayID, { visible: true});
mirrorScalerGrabbed = false;
}
};
_this.startNearGrab = function(entityID, data) {
Entities.editEntity(mirrorScalerID, { userData: "{\"grabbableKey\":{\"grabbable\":false}}" });
Entities.editEntity(mirrorScalerID, { visible: false } );
Entities.editEntity(mirrorToggleID, {userData: "{\"grabbableKey\":{\"wantsTrigger\":false}}"});
Overlays.editOverlay(mirrorToggleOverlayID, { visible: false});
};
_this.releaseGrab = function(entityID, data) {
var mirrorProps = Entities.getEntityProperties(_this.entityID, ["position", "rotation", "dimensions"]);
// adjust mirror scaler
var localAdjustedPos = {
x: (mirrorProps.dimensions.x * 0.5),
y: (mirrorProps.dimensions.y * -0.5),
z: 0
};
var localRotatedAdjustedPos = Vec3.multiplyQbyV(mirrorProps.rotation, localAdjustedPos);
var worldRotatedAdjustedPos = Vec3.sum(mirrorProps.position, localRotatedAdjustedPos);
Entities.editEntity(mirrorScalerID, { position: worldRotatedAdjustedPos } );
Entities.editEntity(mirrorScalerID, { rotation: mirrorProps.rotation } );
Entities.editEntity(mirrorScalerID, { userData: "{\"grabbableKey\":{\"grabbable\":true}}" });
Entities.editEntity(mirrorScalerID, { visible: true } );
// adjust mirror toggle
localAdjustedPos = {
x: (mirrorProps.dimensions.x * 0.4),
y: (mirrorProps.dimensions.y * 0.4),
z: mirrorOverlayOffset
};
localRotatedAdjustedPos = Vec3.multiplyQbyV(mirrorProps.rotation, localAdjustedPos);
worldRotatedAdjustedPos = Vec3.sum(mirrorProps.position, localRotatedAdjustedPos);
Entities.editEntity(mirrorToggleID, { position: worldRotatedAdjustedPos } );
Entities.editEntity(mirrorToggleID, { rotation: mirrorProps.rotation } );
Entities.editEntity(mirrorToggleID, {userData: "{\"grabbableKey\":{\"wantsTrigger\":true}}"});
// adjust mirror toggle overlay
var mirrorToggleProps = Entities.getEntityProperties(mirrorToggleID, ["position", "rotation", "dimensions"]);
Overlays.editOverlay(mirrorToggleOverlayID, { position: mirrorToggleProps.position } );
Overlays.editOverlay(mirrorToggleOverlayID, { rotation: Quat.multiply(mirrorToggleProps.rotation, { w: 0, x: -0.707, y: 0, z: -0.707}) } );
Overlays.editOverlay(mirrorToggleOverlayID, { visible: true});
};
// Turns off mirror and deletes all mirror editors
_this.unload = function(entityID) {
print("unload mirror client");
mirrorOverlayOff();
Overlays.deleteOverlay(mirrorToggleOverlayID);
Entities.deleteEntity(mirrorToggleID);
if (debug) {
Entities.deleteEntity(debugSpectatorCameraID);
Entities.deleteEntity(debugNearClipPlaneID);
}
};
});