goog.module('gep.provider.WorldProvider');
const {Event,EventTarget} = goog.require('goog.events');
const userAgent = goog.require('goog.userAgent');
/**
* This provider (singleton) takes care of settings, status changes (e.g. also change of camera view) of the 3D scene.
* @extends {EventTarget}
*/
class WorldProvider extends EventTarget
{
constructor()
{
super();
/**
* Checks if the device is a mobile device.
* @type {boolean}
*/
this.isMobileDevice = userAgent.MOBILE || userAgent.IOS || userAgent.ANDROID;
/**
* Current {@Link WorldState}
* @type {string}
* @private
*/
this.state_ = '';
/**
* Main loading progress of the 3D scene
* @type {number}
* @private
*/
this.loadProgress_ = 0;
/**
* Is Cannon physics calculation enabled for the 3D Scene
* @type {boolean}
*/
this.physicIsEnabled = false;
/**
* If the scene is currently viewed from the top view
* @type {boolean}
* @private
*/
this.isTopView_ = false;
/**
* If the scene is currently being viewed from an exhibit view (e.g. from an exhibit orbit view)
* @type {boolean}
* @private
*/
this.isExhibitView_ = false;
/**
* If the viewer is currently in an activation zone of an exhibit,
* its model id and the corresponding artwork model id are stored
* here for later assignment.
* @type {?{id:number,elementId:number}}
*/
this.activeExhibit_ = null;
/**
* List of THREE objects that could interact with the THREE raycaster (e.g. for collision detection).
* @type {Array<THREE.Object3D>}
*/
this.intersectables = [];
/**
* Indicates whether the transition of a view change (e.g. animation from the visitor view to the top view) takes place at the moment.
* @type {boolean}
*/
this.isSwitchingView = false;
/**
* General settings object to set properties in the 3D and address them globally. So these values are also more easily updateable via a GUI {@Link https://github.com/dataarts/dat.gui/blob/master/API.md}.
* @type {{lights:Object,background:Object,fog:Object,artwork:Object,ground:Object,physic:Object,moving:Object,profile:Object,sound:Object}}
*/
this.settings = {
lights: {
'hemiLightColor': 0xf2f2f2,
'hemiLightGroundColor': 0xeed69a,
'hemiLightIntensity': 1,
'hemiLightIntensityTopview': 1,
'dirLightColor': 0xffffff,
'dirLightIntensity': .5,
'dirLightOffsetX': 30,
'dirLightOffsetY': 200,
'dirLightOffsetZ': 50,
},
background: {
'color': 0xfffbf6,
},
fog: {
'color': 0xfffbf6,
'density': 0.015,
},
ground: {
'color': 0xfff2dc,
},
artwork: {
'envMap': true,
},
physic: {
'fixedTimeStep': 1 / 60,
'maxSubSteps': 3,
'worldScale': 1,
'attractionSpeed': 15,
'mass': 3,
'linearDamping': .95,
'scaleSpeed': 0.002
},
moving: {
'accelerationMoveDirect': this.getMoveSettings('accelerationMoveDirect'),
'frictionMoveDirect': this.getMoveSettings('frictionMoveDirect'),
'accelerationMoveSide': 25,
'frictionMoveSide': this.getMoveSettings('frictionMoveSide'),
'accelerationRotateHorizontal': this.getMoveSettings('accelerationRotateHorizontal'),
'frictionRotateHorizontal': this.getMoveSettings('frictionRotateHorizontal'),
'accelerationRotateVertical': 20,
'frictionRotateVertical': 8,
'chairRotationIncreaseFactor': this.getMoveSettings('chairRotationIncreaseFactor'),
'chairRotationSpeed': 30,
'chairRotationStaticAngleLimit': 20,
'chairRotationStaticTriggerInterval': 0.05,
'chairRotationStaticSpeed': 0.1,
'verticalRotationLimitDown': this.getMoveSettings('verticalRotationLimitDown'),
'verticalRotationLimitUp': this.getMoveSettings('verticalRotationLimitUp'),
'useStaticAngleLimit': false,
},
profile: {
'changeMultiplier': .1
},
sound: {
'influenceRadius': 10
}
};
}
/**
* Returns individual settings values that are adjusted taking into account the currently used control.
* @param {string} key
* @return {number}
*/
getMoveSettings(key)
{
switch(key) {
case 'accelerationMoveDirect': return 40;
case 'frictionMoveDirect': return window['GAMEPAD_MODE'] == 'on' ? 3 : 6;
case 'frictionMoveSide': return window['GAMEPAD_MODE'] == 'on' ? 5 : 6;
case 'accelerationRotateHorizontal': return window['GAMEPAD_MODE'] == 'on'? 90 : 50;
case 'frictionRotateHorizontal': return window['GAMEPAD_MODE'] == 'on' ? 3 : 5;
case 'chairRotationIncreaseFactor': return window['GAMEPAD_MODE'] == 'on' ? 3 : 1.8;
case 'verticalRotationLimitDown': return window['GAMEPAD_MODE'] == 'on' ? -10 : 0;
case 'verticalRotationLimitUp': return window['GAMEPAD_MODE'] == 'on' ? 90 : -10;
case 'cameraYMax': return window['GAMEPAD_MODE'] == 'on' ? 2 : 3;
default: return this.settings.moving[key];
}
}
/**
* Fires the {@Link WorldEventType} `START_BUILDING` event
*/
build()
{
this.dispatchEvent(new Event(WorldEventType.START_BUILDING));
}
/**
* Changes the state to {@Link WorldState} `READY` and fires the {@Link WorldEventType} `LOADED` event
*/
loaded()
{
this.state_ = WorldState.READY;
this.dispatchEvent(new Event(WorldEventType.LOADED));
}
/**
* Updates the loading progress and fires the {@Link WorldEventType} `LOAD_PROGRESS` event
* @param {number} progress
*/
updateLoadProgress(progress)
{
this.loadProgress_ = progress;
this.dispatchEvent(new Event(WorldEventType.LOAD_PROGRESS));
}
/**
* Changes the state to {@Link WorldState} `ACTIVE` and fires the {@Link WorldEventType} `ACTIVATE` event
*/
activate()
{
this.state_ = WorldState.ACTIVE;
this.dispatchEvent(new Event(WorldEventType.ACTIVATE));
}
/**
* Changes the state to {@Link WorldState} `ERROR` and fires the {@Link WorldEventType} `ERROR` event
*/
showNoSupportMessage()
{
this.state_ = WorldState.ERROR;
this.dispatchEvent(new Event(WorldEventType.ERROR));
}
/**
* Fires the {@Link WorldEventType} `PAUSE_RENDERING` event
*/
pauseRendering()
{
this.dispatchEvent(new Event(WorldEventType.PAUSE_RENDERING));
}
/**
* Fires the {@Link WorldEventType} `START_RENDERING` event
*/
startRendering()
{
this.dispatchEvent(new Event(WorldEventType.START_RENDERING));
}
/**
* Fires the {@Link WorldEventType} `CHANGE_MUTING` event
*/
changeMuting()
{
this.dispatchEvent(new Event(WorldEventType.CHANGE_MUTING));
}
/**
* Toggles the normal and top view state value and fires the {@Link WorldEventType} `SWITCH_WORLD_VIEW` event
*/
switchWorldView()
{
if(this.isSwitchingView)
return;
this.isTopView_ = !this.isTopView_;
this.dispatchEvent(new Event(WorldEventType.SWITCH_WORLD_VIEW));
}
/**
* Toggles the normal and exhibit/orbit view state value and fires the {@Link WorldEventType} `SWITCH_EXHIBIT_VIEW` event
*/
switchExhibitView()
{
if(this.isSwitchingView)
return;
this.isExhibitView_ = !this.isExhibitView_;
this.dispatchEvent(new Event(WorldEventType.SWITCH_EXHIBIT_VIEW));
}
/**
* Sets the current active exhibit and fires the {@Link WorldEventType} `ACTIVATE_EXHIBIT` event
* @param {number} exhibitId Exhibit model id
* @param {number} elementId Artwork model id
*/
activateExhibit(exhibitId, elementId)
{
this.activeExhibit_ = {id: exhibitId, elementId: elementId};
this.dispatchEvent(new Event(WorldEventType.ACTIVATE_EXHIBIT));
}
/**
* Sets the active exhibit to null and fires the {@Link WorldEventType} `DEACTIVATE_EXHIBIT` event
*/
deactivateExhibit()
{
this.activeExhibit_ = null;
this.dispatchEvent(new Event(WorldEventType.DEACTIVATE_EXHIBIT));
}
/**
* Fires the {@Link WorldEventType}UPDATE_CONTROLLING} event
*/
updateControlling()
{
this.dispatchEvent(new Event(WorldEventType.UPDATE_CONTROLLING));
}
/**
* Getter
* @return {string}
*/
get state()
{
return this.state_;
}
/**
* Getter
* @return {number}
*/
get loadProgress()
{
return this.loadProgress_;
}
/**
* Getter
* @return {boolean}
*/
get isTopView()
{
return this.isTopView_;
}
/**
* Getter
* @return {boolean}
*/
get isExhibitView()
{
return this.isExhibitView_;
}
/**
* Getter
* @return {?{id:number,elementId:number}}
*/
get activeExhibit()
{
return this.activeExhibit_;
}
}
goog.addSingletonGetter(WorldProvider);
/**
* @enum {string}
*/
const WorldState = {
/** ready */ READY: 'ready',
/** active */ ACTIVE: 'active',
/** error */ ERROR: 'error'
};
/**
* @enum {string}
*/
const WorldEventType = {
/** error-world */ ERROR: 'error-world',
/** start-building-world */ START_BUILDING: 'start-building-world',
/** loaded-world */ LOADED: 'loaded-world',
/** loading-world-progress */ LOAD_PROGRESS: 'loading-world-progress',
/** activate-world */ ACTIVATE: 'activate-world',
/** show-world-guiding */ SHOW_GUIDING: 'show-world-guiding',
/** hide-world-guiding */ HIDE_GUIDING: 'hide-world-guiding',
/** complete-world-guiding */ COMPLETE_GUIDING: 'complete-world-guiding',
/** pause-rendering */ PAUSE_RENDERING: 'pause-rendering',
/** start-rendering */ START_RENDERING: 'start-rendering',
/** change-muting */ CHANGE_MUTING: 'change-muting',
/** switch-world-view */ SWITCH_WORLD_VIEW: 'switch-world-view',
/** switch-exhibit-view */ SWITCH_EXHIBIT_VIEW: 'switch-exhibit-view',
/** activate-view */ ACTIVATE_EXHIBIT: 'activate-view',
/** deactivate-view */ DEACTIVATE_EXHIBIT: 'deactivate-view',
/** update-controlling */ UPDATE_CONTROLLING: 'update-controlling'
};
exports = {WorldProvider,WorldState,WorldEventType};