310 lines
No EOL
14 KiB
JavaScript
310 lines
No EOL
14 KiB
JavaScript
//
|
|
// Created by Luis Cuenca on 10/27/18
|
|
// Copyright 2018 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
|
|
//
|
|
|
|
/* jslint bitwise: true */
|
|
|
|
/* global Script, Overlays, Controller, Entities, MyAvatar, Quat, Vec3, AvatarList, Tablet, Render
|
|
*/
|
|
(function() {
|
|
|
|
var shown = false;
|
|
|
|
// ["{0b3f7741-9d91-4313-a01c-e857f68d71a0}", "{90143a7c-c83f-4ab7-b208-df4826ed03b1}", "{c15b9c1b-2d7a-4003-a23d-e10c74e47dfc}","{139ca1f2-7f7c-4238-a5ac-8b2f97a20853}"];
|
|
var fieldID = "{dbb98a91-749b-4681-a465-1767776d0313}";
|
|
var fieldProps = Entities.getEntityProperties(fieldID, ["position", "dimensions"]);
|
|
var fieldPosition = fieldProps.position;
|
|
var fieldDimensions = fieldProps.dimensions;
|
|
|
|
var monitorsIDs = Entities.findEntitiesByName("Monitor", fieldPosition, 1.5 * Math.max(fieldDimensions.x, fieldDimensions.z));
|
|
var ballIDs = Entities.findEntitiesByName("Soccer Ball", fieldPosition, 1.5 * Math.max(fieldDimensions.x, fieldDimensions.z));
|
|
|
|
var createMonitors = false;
|
|
|
|
var spectatorCameraConfig = Render.getConfig("SecondaryCamera");
|
|
var camera;
|
|
var cameraPosition;
|
|
var cameraRotation;
|
|
|
|
var CAMERA_FOLLOW_ENTITY = 0;
|
|
var CAMERA_FOLLOW_AVATAR = 1;
|
|
|
|
var CAMERA_CONSTRAINT_NONE = 0;
|
|
var CAMERA_CONSTRAINT_POINT = 1;
|
|
var CAMERA_CONSTRAINT_LINE = 2;
|
|
var CAMERA_CONSTRAINT_PLANE = 3;
|
|
|
|
var cameraRig;
|
|
|
|
var CameraFollow = function(type, id) {
|
|
this.type = type;
|
|
this.id = id;
|
|
}
|
|
|
|
var CameraConstraint = function(type, vector){
|
|
var self = this;
|
|
this.type = type;
|
|
this.vector = Vec3.normalize(vector);
|
|
this.uVector;
|
|
this.vVector;
|
|
this.calculatePlaneUV = function() {
|
|
if (vector.x === 0 && vector.y === 1 && vector.z === 0) {
|
|
self.uVector = {x: 1, y: 0, z: 0};
|
|
self.vVector = {x: 0, y: 0, z: 1};
|
|
} else {
|
|
self.vVector = Vec3.normalize(Vec3.cross(self.vector, {x: 1, y: 0, z: 0}));
|
|
self.uVector = Vec3.normalize(Vec3.cross(self.vVector, self.vector));
|
|
}
|
|
}
|
|
if (type === CAMERA_CONSTRAINT_PLANE) {
|
|
self.calculatePlaneUV();
|
|
}
|
|
}
|
|
|
|
var CameraNode = function(position, orientation, constraint, follows) {
|
|
var self = this;
|
|
this.follows = follows;
|
|
this.constraint = constraint;
|
|
this.followPoint;
|
|
this.position = position;
|
|
this.orientation = orientation;
|
|
this.distanceVector;
|
|
this.velocityVector;
|
|
this.computeFollowPoint = function() {
|
|
self.followPoint = Vec3.Zero;
|
|
for (var i = 0; i < self.follows.length; i++) {
|
|
var follow = self.follows[i];
|
|
if (follow.type === CAMERA_FOLLOW_AVATAR) {
|
|
self.followPoint = Vec3.sum(self.followPoint, AvatarList.getAvatar(follow.id).position);
|
|
} else if (follow.type === CAMERA_FOLLOW_ENTITY) {
|
|
self.followPoint = Vec3.sum(self.followPoint, Entities.getEntityProperties(follow.id, ["position"]).position);
|
|
}
|
|
self.followPoint = Vec3.multiply(self.followPoint, 1.0/self.follows.length);
|
|
}
|
|
}
|
|
|
|
this.computePosition = function() {
|
|
self.distanceVector = Vec3.subtract(self.followPoint, self.position);
|
|
if (self.constraint.type != CAMERA_CONSTRAINT_POINT) {
|
|
if (self.constraint.type === CAMERA_CONSTRAINT_NONE) {
|
|
self.velocityVector = 0.2 * self.distanceVector;
|
|
} else if (self.constraint.type === CAMERA_CONSTRAINT_LINE) {
|
|
self.velocityVector = Vec3.multiply(self.constraint.vector, 0.01 * Vec3.dot(self.distanceVector, self.constraint.vector));
|
|
} else if (self.constraint.type === CAMERA_CONSTRAINT_PLANE) {
|
|
var uvec = Vec3.multiply(self.constraint.uVector ,0.01 * Vec3.dot(self.distanceVector, self.constraint.uVector));
|
|
var vvec = Vec3.multiply(self.constraint.vVector ,0.01 * Vec3.dot(self.distanceVector, self.constraint.vVector));
|
|
self.velocityVector = Vec3.sum(uvec, vvec);
|
|
}
|
|
}
|
|
self.position = Vec3.sum(self.position, self.velocityVector);
|
|
}
|
|
|
|
this.computeOrientation = function() {
|
|
self.distanceVector = Vec3.subtract(self.followPoint, self.position);
|
|
var lookAtVector = Vec3.normalize(self.distanceVector);
|
|
self.orientation = Quat.lookAtSimple({x:0, y:0, z:0}, lookAtVector);
|
|
}
|
|
|
|
this.update = function() {
|
|
self.computeFollowPoint();
|
|
self.computePosition();
|
|
self.computeOrientation();
|
|
}
|
|
}
|
|
|
|
var Monitor = function(entityID, position, rotation, dimensions) {
|
|
var self = this;
|
|
this.position = position;
|
|
this.rotation = rotation;
|
|
this.dimensions = dimensions;
|
|
this.entityID = entityID;
|
|
this.overlayID;
|
|
this.created = false;
|
|
this.remove = function() {
|
|
if (self.entityID && self.created) {
|
|
Entities.deleteEntity(self.entityID);
|
|
}
|
|
if (self.overlayID) {
|
|
Overlays.deleteOverlay(self.overlayID);
|
|
}
|
|
}
|
|
this.create = function() {
|
|
self.remove();
|
|
if (!self.entityID) {
|
|
self.entityID = Entities.addEntity({
|
|
name: "Monitor",
|
|
type: "Box",
|
|
dimensions: self.dimensions,
|
|
position: self.position,
|
|
rotation: self.rotation
|
|
})
|
|
self.created = true;
|
|
} else {
|
|
var props = Entities.getEntityProperties(self.entityID, ["dimensions", "position", "rotation"]);
|
|
self.dimensions = props.dimensions;
|
|
self.position = props.position;
|
|
self.rotation = props.rotation;
|
|
}
|
|
var overlayDimensions = { x: self.dimensions.x, y: self.dimensions.x, z: 0.0};
|
|
self.overlayID = Overlays.addOverlay("image3d", {
|
|
url: "resource://spectatorCameraFrame",
|
|
emissive: true,
|
|
parentID: self.entityID,
|
|
localRotation: { w: 0, x: 0, y: 0, z: 1.0 },
|
|
localPosition: { x: 0.0, y: 0.0, z: -0.6 * self.dimensions.z },
|
|
alpha: 1,
|
|
dimensions: overlayDimensions
|
|
});
|
|
}
|
|
self.create();
|
|
}
|
|
|
|
var CameraRig = function(length, width, monitorLength, yOffset) {
|
|
var self = this;
|
|
this.length = length;
|
|
this.width = width;
|
|
this.cameraRotation;
|
|
this.cameraPosition;
|
|
this.cameraNodes = [];
|
|
this.monitors = [];
|
|
this.emitIndex = 0;
|
|
this.monitorDimensions = { x: monitorLength, y: 0.66 * monitorLength, z: 0.03 * monitorLength };
|
|
this.frontVector = {x: 1, y: 0, z: 0};
|
|
this.backVector = {x: -1, y: 0, z: 0};
|
|
this.rightVector = {x: 0, y: 0, z: 1};
|
|
this.leftVector = {x: 0, y: 0, z: -1};
|
|
this.offset = {x: 0, y: yOffset, z: 0};
|
|
/*
|
|
this.frontVector = Quat.getFront(MyAvatar.orientation);
|
|
this.backVector = Vec3.multiply(self.frontVector, -1);
|
|
this.rightVector = Quat.getRight(MyAvatar.orientation);
|
|
this.leftVector = Vec3.multiply(self.rightVector, -1);
|
|
*/
|
|
this.changeCount = 500;
|
|
this.changeTimer = 0;
|
|
|
|
this.createRig = function() {
|
|
var follow = new CameraFollow(CAMERA_FOLLOW_ENTITY, ballIDs[0]);
|
|
|
|
var constraint1 = new CameraConstraint(CAMERA_CONSTRAINT_LINE, self.rightVector);
|
|
var position1 = Vec3.sum(Vec3.sum(self.center, Vec3.multiply(self.frontVector, 10.0)), {x: 0, y: 2.0, z: 0});
|
|
var orientation1 = Quat.lookAtSimple({x:0, y:0, z:0}, self.leftVector);
|
|
var node1 = new CameraNode(position1, orientation1, constraint1, [follow]);
|
|
|
|
var constraint2 = new CameraConstraint(CAMERA_CONSTRAINT_PLANE, Vec3.sum(self.frontVector, {x: 0, y: 5, z: 0}));
|
|
var position2 = Vec3.sum(Vec3.sum(self.center, Vec3.multiply(self.frontVector, 10.0)), {x: 0, y: 5.0, z: 0});
|
|
var orientation2 = Quat.lookAtSimple({x:0, y:0, z:0}, self.backVector);
|
|
var node2 = new CameraNode(position2, orientation2, constraint2, [follow]);
|
|
|
|
var constraint3 = new CameraConstraint(CAMERA_CONSTRAINT_LINE, Vec3.sum(self.rightVector, self.frontVector));
|
|
var position3 = Vec3.sum(self.center, {x: 0, y: 0.5, z: 0});
|
|
var orientation3 = Quat.lookAtSimple({x:0, y:0, z:0}, self.leftVector);
|
|
var node3 = new CameraNode(position3, orientation3, constraint3, [follow]);
|
|
|
|
self.cameraNodes.push(node1);
|
|
self.cameraNodes.push(node2);
|
|
self.cameraNodes.push(node3);
|
|
}
|
|
|
|
this.createMonitors = function() {
|
|
if (createMonitors) {
|
|
var centerPosition = Vec3.sum(MyAvatar.position, self.offset);
|
|
centerPosition.y += 0.5 * (self.monitorDimensions.y - MyAvatar.getHeight());
|
|
var pos1 = Vec3.sum(centerPosition, Vec3.multiply(self.frontVector, 0.5 * self.length));
|
|
var pos2 = Vec3.sum(centerPosition, Vec3.multiply(self.rightVector, 0.5 * self.width));
|
|
var pos3 = Vec3.sum(centerPosition, Vec3.multiply(self.backVector, 0.5 * self.length));
|
|
var pos4 = Vec3.sum(centerPosition, Vec3.multiply(self.leftVector, 0.5 * self.width));
|
|
var ori1 = Quat.lookAtSimple({x:0, y:0, z:0}, Vec3.multiply(self.frontVector, -1));
|
|
var ori2 = Quat.lookAtSimple({x:0, y:0, z:0}, Vec3.multiply(self.rightVector, -1));
|
|
var ori3 = Quat.lookAtSimple({x:0, y:0, z:0}, Vec3.multiply(self.backVector, -1));
|
|
var ori4 = Quat.lookAtSimple({x:0, y:0, z:0}, Vec3.multiply(self.leftVector, -1));
|
|
self.monitors.push(new Monitor(undefined, pos1, ori1, self.monitorDimensions));
|
|
self.monitors.push(new Monitor(undefined, pos2, ori2, self.monitorDimensions));
|
|
self.monitors.push(new Monitor(undefined, pos3, ori3, self.monitorDimensions));
|
|
self.monitors.push(new Monitor(undefined, pos4, ori4, self.monitorDimensions));
|
|
} else {
|
|
for (var i = 0; i < monitorsIDs.length; i++) {
|
|
self.monitors.push(new Monitor(monitorsIDs[i]));
|
|
}
|
|
}
|
|
|
|
if (!fieldPosition) {
|
|
self.center = {x: 0, y: 0, z: 0};
|
|
for (var i = 0; i < self.monitors.length; i++) {
|
|
self.center = Vec3.sum(self.center, self.monitors[i].position);
|
|
}
|
|
self.center = Vec3.multiply(self.center, 1.0/self.monitors.length);
|
|
} else {
|
|
self.center = fieldPosition;
|
|
}
|
|
}
|
|
|
|
this.create = function() {
|
|
self.createMonitors();
|
|
self.createRig();
|
|
}
|
|
this.cleanup = function() {
|
|
for (var i = 0; i < self.monitors.length; i++) {
|
|
self.monitors[i].remove();
|
|
}
|
|
self.monitors = [];
|
|
}
|
|
this.emitFromCamera = function(index) {
|
|
if (index < self.cameraNodes.length) {
|
|
self.emitIndex = index;
|
|
}
|
|
}
|
|
this.update = function() {
|
|
self.changeTimer += 1;
|
|
if (self.emitIndex < self.cameraNodes.length) {
|
|
for (var i = 0; i < self.cameraNodes.length; i++) {
|
|
self.cameraNodes[i].update();
|
|
}
|
|
spectatorCameraConfig.setPosition(self.cameraNodes[self.emitIndex].position);
|
|
spectatorCameraConfig.setOrientation(self.cameraNodes[self.emitIndex].orientation);
|
|
}
|
|
if (self.changeTimer > self.changeCount) {
|
|
self.changeTimer = 0;
|
|
self.emitIndex = self.emitIndex + 1 >= self.cameraNodes.length ? 0 : self.emitIndex + 1;
|
|
ballIDs = Entities.findEntitiesByName("Soccer Ball", fieldPosition, 1.5 * Math.max(fieldDimensions.x, fieldDimensions.z));
|
|
}
|
|
self.emitFromCamera(self.emitIndex);
|
|
}
|
|
self.create();
|
|
}
|
|
|
|
function spectatorCameraOn() {
|
|
spectatorCameraConfig.enableSecondaryCameraRenderConfigs(true);
|
|
spectatorCameraConfig.resetSizeSpectatorCamera(Window.innerWidth, Window.innerHeight);
|
|
cameraRig = new CameraRig(45, 90, 20.0, 3.0);
|
|
}
|
|
|
|
function spectatorCameraOff() {
|
|
spectatorCameraConfig.attachedEntityId = false;
|
|
spectatorCameraConfig.enableSecondaryCameraRenderConfigs(false);
|
|
if (cameraRig) {
|
|
cameraRig.cleanup();
|
|
}
|
|
}
|
|
|
|
function shutdownTabletApp() {
|
|
spectatorCameraOff();
|
|
}
|
|
|
|
function onDomainChanged() {
|
|
spectatorCameraOff();
|
|
}
|
|
spectatorCameraOn();
|
|
Script.scriptEnding.connect(shutdownTabletApp);
|
|
Script.update.connect(function() {
|
|
if (cameraRig) {
|
|
cameraRig.update();
|
|
}
|
|
});
|
|
Window.domainChanged.connect(onDomainChanged);
|
|
})() |