goog.module('gep.world.Visitor');
const {WorldProvider} = goog.require('gep.provider.WorldProvider');
const {EventTarget} = goog.require('goog.events');
/**
* @extends {EventTarget}
*/
class Visitor extends EventTarget
{
/**
* This class represents the virtual visitor object (creates the THREE.Camera). The camera is nested in a container which is used for the movement through the exhibition.
*/
constructor()
{
super();
/**
* Reference to the WorldProvider for monitoring actions, loading processes and status changes of the created THREE.Scene.
* @type {WorldProvider}
* @private
*/
this.worldProvider_ = WorldProvider.getInstance();
/**
* Main 3d container used for the application of the movement in the exhibition. All 3D objects within the Visitor class are added to this container.
* @type {THREE.Object3D}
* @private
*/
this.object3D_ = new THREE.Group();
/**
* Main 3d camera object (view from visitor)
* @type {THREE.Camera}
* @protected
*/
this.camera_ = null;
/**
* Default distance of camera to the ground (default visitor height)
* @type {number}
*/
this.cameraNormalY = 1.5;
/**
* Range that determines the Y-rotation of the camera (visitor head tilt to the left and right).
* @type {{start:number,end:number}}
*/
this.cameraRangeY = {start: this.cameraNormalY, end: this.worldProvider_.getMoveSettings('cameraYMax')};
/**
* Defines the maximum distance the visitor object can move away from an exhibit. This is to prevent the user from moving too far away from the actual area of the exhibition in the open world 3d scene.
* @type {number}
*/
this.maxDistanceToExhibit = 50;
/**
* Determines whether the user can control the visitor.
* @type {boolean}
*/
this.controlsAreEnabled = false;
/**
* Cannon mass object which is used for calculating the physic behaviour of the visitor.
* @type {CANNON.Body}
* @private
*/
this.physicBody_ = null;
/**
* Cannon sensor object which is used for physic engine hit test check.
* @type {CANNON.Body}
* @private
*/
this.physicSensor_ = null;
/**
* THREE sensor object which is used for raycast detection.
* @type {THREE.Mesh}
* @private
*/
this.sensor_ = null;
/**
* Radius of the cannon sphere (mass object)
* @type {number}
* @private
*/
this.radius_ = 0;
/**
* Range that determines the polar rotation of the camera (visitor head tilt to the bottom and top).
* @type {{start:number, end:number}}
*/
this.cameraPolarAngleRange = {start: this.worldProvider_.getMoveSettings('verticalRotationLimitDown') * Math.PI / 180, end: this.worldProvider_.getMoveSettings('verticalRotationLimitUp') * Math.PI / 180};
/**
* THREE object for the graphical representation of the visitor from the top view
* @type {THREE.Mesh}
* @private
*/
this.topViewPlane_ = null;
}
/**
* Method creates the camera and sensors that make up the visitor in the 3D scene.
* @param {THREE.Vector3} position start position in the 3d scene
* @param {number} radius Radius of the cannon sphere (mass object)
* @param {THREE.Texture=} topViewTexture Optional texture which is used for the visitor display (THREE.Plane) in the top view
*/
build(position, radius, topViewTexture = null)
{
this.radius_ = radius;
this.camera_ = new THREE.PerspectiveCamera(30.0, window.innerWidth / window.innerHeight, .1, 1000);
this.camera_.position.set(0, this.cameraNormalY, 0);
this.camera_.name = 'visitorCamera';
this.object3D_.add(this.camera_);
let boxSize = new THREE.Vector3(.2, .2, 1);
let geometry = new THREE.BoxGeometry(boxSize.x, boxSize.y, boxSize.z);
let material = new THREE.MeshBasicMaterial({
'color': 0x0000ff,
'opacity': window['PHYSIC_MODE'] === 'on' ? .3 : 0,
'transparent': true,
'side': THREE.FrontSide
});
this.sensor_ = new THREE.Mesh(geometry, material);
this.sensor_.name = 'sensor';
this.sensor_.position.set(0, 0, -3 -(boxSize.z * .5));
this.sensor_.castShadow = false;
this.sensor_.receiveShadow = false; //true;
this.sensor_.renderOrder = 999998;
this.object3D_.add(this.sensor_);
let shape = new CANNON.Box(new CANNON.Vec3(
boxSize.x * .5 * this.worldProvider_.settings.physic['worldScale'],
boxSize.y * .5 * this.worldProvider_.settings.physic['worldScale'],
boxSize.z * .5 * this.worldProvider_.settings.physic['worldScale']
));
this.physicSensor_ = new CANNON.Body({
'isTrigger': true,
'mass': 1,
'shape': shape,
'collisionFilterGroup': 2,
'collisionFilterMask': 2
});
shape = new CANNON.Sphere(this.radius_ * this.worldProvider_.settings.physic['worldScale']);
this.physicBody_ = new CANNON.Body({
'mass': 1,
'shape': shape,
'collisionFilterGroup': 2,
'collisionFilterMask': 2
});
this.physicBody_.angularFactor = new CANNON.Vec3(0,1,0);
this.physicBody_.position.set(
position.x * this.worldProvider_.settings.physic['worldScale'],
this.radius_ * this.worldProvider_.settings.physic['worldScale'],
position.z * this.worldProvider_.settings.physic['worldScale']
);
this.physicBody_.linearDamping = this.worldProvider_.settings.physic['linearDamping'];
if(topViewTexture)
{
const geometry = new THREE.PlaneGeometry(3, 3);
const material = new THREE.MeshBasicMaterial({'map': topViewTexture, 'side': THREE.FrontSide, 'transparent': true});
this.topViewPlane_ = new THREE.Mesh(geometry, material);
this.topViewPlane_.position.y = 10;
this.topViewPlane_.rotation.x = -Math.PI / 2;
this.topViewPlane_.renderOrder = 999999;
this.topViewPlane_.material.opacity = 0;
this.topViewPlane_.visibe = false;
this.object3D_.add(this.topViewPlane_);
}
}
/**
* Method enables or disables the visitor top view display object
* @param {boolean} value Action value for activation or deactivation
* @param {number} delay Delay for the activation or deactivation
*/
enableTopViewPlane(value, delay)
{
if(!this.topViewPlane_)
return;
if(value === true)
this.topViewPlane_.visibe = true;
gsap.to(this.topViewPlane_.material,{'opacity': value === true ? 1 : 0, 'duration': .5, 'delay': delay, 'onComplete': () => {this.topViewPlane_.visibe = value;}});
}
/**
* Method is called by the main WebGLRenderer render process.
* Transfers the position and rotation of the THREE sensor object to the Cannon physical body (mass).
*/
update()
{
let worldPosition = this.sensor_.getWorldPosition(new THREE.Vector3());
let worldQuaternion = this.sensor_.getWorldQuaternion(new THREE.Quaternion());
this.physicSensor_.position.x = worldPosition.x;
this.physicSensor_.position.y = worldPosition.y;
this.physicSensor_.position.z = worldPosition.z;
this.physicSensor_.quaternion.set(worldQuaternion.x, worldQuaternion.y, worldQuaternion.z, worldQuaternion.w);
}
/**
* Getter
* @return {THREE.Object3D}
*/
get object3D()
{
return this.object3D_;
}
/**
* Getter
* @return {number}
*/
get radius()
{
return this.radius_;
}
/**
* Getter
* @return {THREE.Camera}
*/
get camera()
{
return this.camera_;
}
/**
* Getter
* @return {CANNON.Body}
*/
get physicBody()
{
return this.physicBody_;
}
/**
* Getter
* @return {CANNON.Body}
*/
get physicSensor()
{
return this.physicSensor_;
}
}
exports = Visitor;