570 lines
18 KiB
JavaScript
570 lines
18 KiB
JavaScript
|
/**
|
||
|
* Created by gaimeng on 15/3/9.
|
||
|
*/
|
||
|
|
||
|
IndoorMap3d = function(mapdiv){
|
||
|
var _this = this;
|
||
|
var _theme = null;
|
||
|
var _mapDiv = mapdiv,
|
||
|
_canvasWidth = _mapDiv.clientWidth,
|
||
|
_canvasWidthHalf = _canvasWidth / 2,
|
||
|
_canvasHeight = _mapDiv.clientHeight,
|
||
|
_canvasHeightHalf = _canvasHeight / 2;
|
||
|
|
||
|
var _scene, _controls, _projector, _rayCaster;
|
||
|
var _canvasDiv;
|
||
|
var _selected;
|
||
|
var _showNames = true, _showPubPoints = true;
|
||
|
var _curFloorId = 0;
|
||
|
var _selectionListener = null;
|
||
|
var _sceneOrtho, _cameraOrtho;//for 2d
|
||
|
var _spriteMaterials = [], _pubPointSprites=null, _nameSprites = null;
|
||
|
|
||
|
this.camera = null;
|
||
|
this.renderer = null;
|
||
|
this.mall = null;
|
||
|
this.is3d = true;
|
||
|
|
||
|
this.init = function(){
|
||
|
|
||
|
// perspective scene for normal 3d rendering
|
||
|
_scene = new THREE.Scene();
|
||
|
_this.camera = new THREE.PerspectiveCamera(20, _canvasWidth / _canvasHeight, 0.1, 2000);
|
||
|
|
||
|
//orthogonal scene for sprites 2d rendering
|
||
|
_sceneOrtho = new THREE.Scene();
|
||
|
_cameraOrtho = new THREE.OrthographicCamera(- _canvasWidthHalf, _canvasWidthHalf, _canvasHeightHalf, -_canvasHeightHalf, 1, 10);
|
||
|
_cameraOrtho.position.z = 10;
|
||
|
|
||
|
//controls
|
||
|
_controls = new THREE.OrbitControls(_this.camera);
|
||
|
|
||
|
//renderer
|
||
|
_this.renderer = new THREE.WebGLRenderer({ antialias: true, alpha: true });
|
||
|
_this.renderer.autoClear = false;
|
||
|
_this.renderer.setClearAlpha(0);
|
||
|
|
||
|
//set up the lights
|
||
|
var light = new THREE.DirectionalLight(0xffffff);
|
||
|
light.position.set(-500, 500, -500);
|
||
|
_scene.add(light);
|
||
|
|
||
|
var light = new THREE.DirectionalLight(0xffffff);
|
||
|
light.position.set(500, 500, 500);
|
||
|
_scene.add(light);
|
||
|
|
||
|
//canvas div
|
||
|
_this.renderer.setSize(_mapDiv.clientWidth, _mapDiv.clientHeight);
|
||
|
_canvasDiv = _this.renderer.domElement
|
||
|
_mapDiv.appendChild(_canvasDiv);
|
||
|
|
||
|
_mapDiv.style.overflow = "hidden";
|
||
|
_canvasDiv.style.width = "100%";
|
||
|
_canvasDiv.style.height = "100%";
|
||
|
}
|
||
|
|
||
|
this.setTheme = function(theme){
|
||
|
if(_theme == null){
|
||
|
_theme = theme
|
||
|
} else if(_theme != theme) {
|
||
|
_theme = theme;
|
||
|
_this.parse(_this.mall.jsonData); //parse
|
||
|
}
|
||
|
return _this;
|
||
|
}
|
||
|
|
||
|
this.theme = function(){
|
||
|
return _theme;
|
||
|
}
|
||
|
|
||
|
//load the map by the json file name
|
||
|
this.load = function (fileName, callback) {
|
||
|
var loader = new IndoorMapLoader(true);
|
||
|
_theme = default3dTheme;
|
||
|
loader.load(fileName, function(mall){
|
||
|
_this.mall = mall;
|
||
|
_scene.add(_this.mall.root);
|
||
|
_scene.mall = mall;
|
||
|
if(callback) {
|
||
|
callback();
|
||
|
}
|
||
|
// FixMe: 2020年7月7日00:41:14 透明色配置
|
||
|
// _this.renderer.setClearColor(_theme.background);
|
||
|
if(_curFloorId == 0){
|
||
|
_this.showAllFloors();
|
||
|
}else{
|
||
|
_this.showFloor(_curFloorId);
|
||
|
}
|
||
|
|
||
|
});
|
||
|
return _this;
|
||
|
}
|
||
|
|
||
|
//parse the json file
|
||
|
this.parse = function(json){
|
||
|
if(_theme == null) {
|
||
|
_theme = default3dTheme;
|
||
|
}
|
||
|
_this.mall = ParseModel(json, _this.is3d, _theme);
|
||
|
_scene.mall = _this.mall;
|
||
|
_this.showFloor(_this.mall.getDefaultFloorId());
|
||
|
_this.renderer.setClearColor(_theme.background);
|
||
|
_scene.add(_this.mall.root);
|
||
|
_mapDiv.style.background = _theme.background;
|
||
|
return _this;
|
||
|
}
|
||
|
|
||
|
//reset the camera to default configuration
|
||
|
this.setDefaultView = function () {
|
||
|
|
||
|
// var camAngle = _this.mall.FrontAngle + Math.PI/2;
|
||
|
var camAngle = 0;
|
||
|
var camDir = [Math.cos(camAngle), Math.sin(camAngle)];
|
||
|
var camLen = 230;
|
||
|
var tiltAngle = 75.0 * Math.PI/180.0;
|
||
|
_this.camera.position.set(camDir[1]*camLen, Math.sin(tiltAngle) * camLen, camDir[0]*camLen);//TODO: adjust the position automatically
|
||
|
_this.camera.lookAt(_scene.position);
|
||
|
|
||
|
_controls.reset();
|
||
|
_controls.viewChanged = true;
|
||
|
return _this;
|
||
|
}
|
||
|
|
||
|
//set top view
|
||
|
this.setTopView = function(){
|
||
|
_this.camera.position.set(0, 500, 0);
|
||
|
return _this;
|
||
|
}
|
||
|
|
||
|
//TODO:adjust camera to fit the building
|
||
|
this.adjustCamera = function() {
|
||
|
_this.setDefaultView();
|
||
|
|
||
|
}
|
||
|
|
||
|
this.zoomIn = function(zoomScale){
|
||
|
_controls.zoomOut(zoomScale);
|
||
|
redraw();
|
||
|
}
|
||
|
|
||
|
this.zoomOut = function(zoomScale){
|
||
|
_controls.zoomIn(zoomScale);
|
||
|
redraw();
|
||
|
}
|
||
|
|
||
|
//show floor by id
|
||
|
this.showFloor = function(floorid) {
|
||
|
_curFloorId = floorid;
|
||
|
if(_scene.mall == null){
|
||
|
return;
|
||
|
}
|
||
|
_scene.mall.showFloor(floorid);
|
||
|
_this.adjustCamera();
|
||
|
if(_showPubPoints) {
|
||
|
createPubPointSprites(floorid);
|
||
|
}
|
||
|
if(_showNames) {
|
||
|
createNameSprites(floorid);
|
||
|
}
|
||
|
redraw();
|
||
|
return _this;
|
||
|
}
|
||
|
|
||
|
//show all floors
|
||
|
this.showAllFloors = function(){
|
||
|
_curFloorId = 0; //0 for showing all
|
||
|
if(_this.mall == null){
|
||
|
return;
|
||
|
}
|
||
|
_this.mall.showAllFloors();
|
||
|
_this.adjustCamera();
|
||
|
clearPubPointSprites();
|
||
|
clearNameSprites();
|
||
|
return _this;
|
||
|
}
|
||
|
|
||
|
//set if the objects are selectable
|
||
|
this.setSelectable = function (selectable) {
|
||
|
if(selectable){
|
||
|
_projector = new THREE.Projector();
|
||
|
_rayCaster = new THREE.Raycaster();
|
||
|
_mapDiv.addEventListener('mousedown', onSelectObject, false);
|
||
|
_mapDiv.addEventListener('touchstart', onSelectObject, false);
|
||
|
}else{
|
||
|
_mapDiv.removeEventListener('mousedown', onSelectObject, false);
|
||
|
_mapDiv.removeEventListener('touchstart', onSelectObject, false);
|
||
|
}
|
||
|
return _this;
|
||
|
}
|
||
|
|
||
|
//set if the user can pan the camera
|
||
|
this.setMovable = function(movable){
|
||
|
_controls.enable = movable;
|
||
|
return _this;
|
||
|
}
|
||
|
|
||
|
//show the labels
|
||
|
this.showAreaNames = function(show) {
|
||
|
_showNames = show == undefined ? true : show;
|
||
|
return _this;
|
||
|
}
|
||
|
|
||
|
//show pubPoints(entries, ATM, escalator...)
|
||
|
this.showPubPoints = function(show){
|
||
|
_showPubPoints = show == undefined ? true: show;
|
||
|
return _this;
|
||
|
}
|
||
|
|
||
|
//get the selected object
|
||
|
this.getSelectedId = function(){
|
||
|
return _selected.id;
|
||
|
}
|
||
|
|
||
|
//the callback function when sth is selected
|
||
|
this.setSelectionListener = function(callback){
|
||
|
_selectionListener = callback;
|
||
|
return _this;
|
||
|
}
|
||
|
|
||
|
//select object by id
|
||
|
this.selectById = function(id){
|
||
|
var floor = _this.mall.getCurFloor();
|
||
|
for(var i = 0; i < floor.children.length; i++){
|
||
|
if(floor.children[i].id && floor.children[i].id == id) {
|
||
|
if (_selected) {
|
||
|
_selected.material.color.setHex(_selected.currentHex);
|
||
|
}
|
||
|
select(floor.children[i]);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//select object(just hight light it)
|
||
|
function select(obj){
|
||
|
obj.currentHex = _selected.material.color.getHex();
|
||
|
obj.material.color = new THREE.Color(_theme.selected);
|
||
|
obj.scale = new THREE.Vector3(2,2,2);
|
||
|
}
|
||
|
|
||
|
function onSelectObject(event) {
|
||
|
|
||
|
// find intersections
|
||
|
event.preventDefault();
|
||
|
var mouse = new THREE.Vector2();
|
||
|
if(event.type == "touchstart"){
|
||
|
mouse.x = ( event.touches[0].clientX / _canvasDiv.clientWidth ) * 2 - 1;
|
||
|
mouse.y = -( event.touches[0].clientY / _canvasDiv.clientHeight ) * 2 + 1;
|
||
|
}else {
|
||
|
mouse.x = ( event.clientX / _canvasDiv.clientWidth ) * 2 - 1;
|
||
|
mouse.y = -( event.clientY / _canvasDiv.clientHeight ) * 2 + 1;
|
||
|
}
|
||
|
var vector = new THREE.Vector3( mouse.x, mouse.y, 1 );
|
||
|
vector.unproject( _this.camera);
|
||
|
|
||
|
_rayCaster.set( _this.camera.position, vector.sub( _this.camera.position ).normalize() );
|
||
|
|
||
|
var intersects = _rayCaster.intersectObjects( _this.mall.root.children[0].children );
|
||
|
|
||
|
if ( intersects.length > 0 ) {
|
||
|
|
||
|
if ( _selected != intersects[ 0 ].object ) {
|
||
|
|
||
|
if ( _selected ) {
|
||
|
_selected.material.color.setHex( _selected.currentHex );
|
||
|
}
|
||
|
for(var i=0; i<intersects.length; i++) {
|
||
|
_selected = intersects[ i ].object;
|
||
|
if(_selected.type && _selected.type == "solidroom") {
|
||
|
select(_selected);
|
||
|
if(_selectionListener) {
|
||
|
_selectionListener(_selected.id); //notify the listener
|
||
|
}
|
||
|
break;
|
||
|
}else{
|
||
|
_selected = null;
|
||
|
}
|
||
|
if(_selected == null && _selectionListener != null){
|
||
|
_selectionListener(-1);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
} else {
|
||
|
|
||
|
if ( _selected ) {
|
||
|
_selected.material.color.setHex( _selected.currentHex );
|
||
|
}
|
||
|
|
||
|
_selected = null;
|
||
|
if(_selectionListener) {
|
||
|
_selectionListener(-1); //notify the listener
|
||
|
}
|
||
|
}
|
||
|
redraw();
|
||
|
|
||
|
}
|
||
|
|
||
|
function redraw(){
|
||
|
_controls.viewChanged = true;
|
||
|
}
|
||
|
|
||
|
function animate () {
|
||
|
requestAnimationFrame(animate);
|
||
|
_controls.update();
|
||
|
if(_controls.viewChanged) {
|
||
|
|
||
|
_this.renderer.clear();
|
||
|
_this.renderer.render(_scene, _this.camera);
|
||
|
|
||
|
if (_showNames || _showPubPoints) {
|
||
|
updateLabels();
|
||
|
}
|
||
|
_this.renderer.clearDepth();
|
||
|
_this.renderer.render(_sceneOrtho, _cameraOrtho);
|
||
|
|
||
|
}
|
||
|
|
||
|
_controls.viewChanged = false;
|
||
|
}
|
||
|
|
||
|
//load Sprites
|
||
|
function loadSprites(){
|
||
|
if(_this.mall != null && _spriteMaterials.length == 0){
|
||
|
var images = _theme.pubPointImg;
|
||
|
for(var key in images){
|
||
|
var texture = THREE.ImageUtils.loadTexture(images[key], undefined, redraw);
|
||
|
var material = new THREE.SpriteMaterial({map:texture});
|
||
|
_spriteMaterials[key] = material;
|
||
|
}
|
||
|
}
|
||
|
_spriteMaterials.isLoaded = true;
|
||
|
}
|
||
|
|
||
|
//labels includes pubPoints and shop names
|
||
|
function updateLabels() {
|
||
|
var mall = _this.mall;
|
||
|
if(mall == null || _controls == null || !_controls.viewChanged){
|
||
|
return;
|
||
|
}
|
||
|
var curFloor = mall.getCurFloor();
|
||
|
if(curFloor == null){
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
var projectMatrix = null;
|
||
|
|
||
|
if(_showNames) {
|
||
|
if(_nameSprites != undefined){
|
||
|
projectMatrix = new THREE.Matrix4();
|
||
|
projectMatrix.multiplyMatrices(_this.camera.projectionMatrix, _this.camera.matrixWorldInverse);
|
||
|
|
||
|
updateSprites(_nameSprites, projectMatrix);
|
||
|
}
|
||
|
|
||
|
}
|
||
|
|
||
|
if(_showPubPoints){
|
||
|
if(_pubPointSprites != undefined){
|
||
|
if(!projectMatrix){
|
||
|
projectMatrix = new THREE.Matrix4();
|
||
|
projectMatrix.multiplyMatrices(_this.camera.projectionMatrix, _this.camera.matrixWorldInverse);
|
||
|
}
|
||
|
updateSprites(_pubPointSprites, projectMatrix);
|
||
|
}
|
||
|
}
|
||
|
_controls.viewChanged = false;
|
||
|
};
|
||
|
|
||
|
//update sprites
|
||
|
function updateSprites(spritelist, projectMatrix){
|
||
|
for(var i = 0 ; i < spritelist.children.length; i++){
|
||
|
var sprite = spritelist.children[i];
|
||
|
var vec = new THREE.Vector3(sprite.oriX * 0.1, 0, -sprite.oriY * 0.1);
|
||
|
vec.applyProjection(projectMatrix);
|
||
|
|
||
|
var x = Math.round(vec.x * _canvasWidthHalf);
|
||
|
var y = Math.round(vec.y * _canvasHeightHalf);
|
||
|
sprite.position.set(x, y, 1);
|
||
|
|
||
|
//check collision with the former sprites
|
||
|
var visible = true;
|
||
|
var visibleMargin = 5;
|
||
|
for(var j = 0; j < i; j++){
|
||
|
var img = sprite.material.map.image;
|
||
|
if(!img){ //if img is undefined (the img has not loaded)
|
||
|
visible = false;
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
var imgWidthHalf1 = sprite.width / 2;
|
||
|
var imgHeightHalf1 = sprite.height / 2;
|
||
|
var rect1 = new Rect(sprite.position.x - imgWidthHalf1, sprite.position.y - imgHeightHalf1,
|
||
|
sprite.position.x + imgHeightHalf1, sprite.position.y + imgHeightHalf1 );
|
||
|
|
||
|
var sprite2 = spritelist.children[j];
|
||
|
var sprite2Pos = sprite2.position;
|
||
|
var imgWidthHalf2 = sprite2.width / 2;
|
||
|
var imgHeightHalf2 = sprite2.height / 2;
|
||
|
var rect2 = new Rect(sprite2Pos.x - imgWidthHalf2, sprite2Pos.y - imgHeightHalf2,
|
||
|
sprite2Pos.x + imgHeightHalf2, sprite2Pos.y + imgHeightHalf2 );
|
||
|
|
||
|
if(sprite2.visible && rect1.isCollide(rect2)){
|
||
|
visible = false;
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
rect1.tl[0] -= visibleMargin;
|
||
|
rect1.tl[1] -= visibleMargin;
|
||
|
rect2.tl[0] -= visibleMargin;
|
||
|
rect2.tl[1] -= visibleMargin;
|
||
|
|
||
|
|
||
|
if(sprite.visible == false && rect1.isCollide(rect2)){
|
||
|
visible = false;
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
sprite.visible = visible;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//creat the funcArea Name sprites of a floor
|
||
|
function createNameSprites(floorId){
|
||
|
if(!_nameSprites){
|
||
|
_nameSprites = new THREE.Object3D();
|
||
|
}else{
|
||
|
clearNameSprites();
|
||
|
}
|
||
|
var funcAreaJson = _this.mall.getFloorJson(_this.mall.getCurFloorId()).FuncAreas;
|
||
|
for(var i = 0 ; i < funcAreaJson.length; i++){
|
||
|
var sprite = makeTextSprite(funcAreaJson[i].Name_en, _theme.fontStyle);
|
||
|
sprite.oriX = funcAreaJson[i].Center[0];
|
||
|
sprite.oriY = funcAreaJson[i].Center[1];
|
||
|
_nameSprites.add(sprite);
|
||
|
}
|
||
|
_sceneOrtho.add(_nameSprites);
|
||
|
}
|
||
|
|
||
|
//create the pubpoint sprites in a floor by the floor id
|
||
|
function createPubPointSprites(floorId){
|
||
|
if(!_spriteMaterials.isLoaded){
|
||
|
loadSprites();
|
||
|
}
|
||
|
|
||
|
if(!_pubPointSprites) {
|
||
|
|
||
|
_pubPointSprites = new THREE.Object3D();
|
||
|
}else{
|
||
|
clearPubPointSprites();
|
||
|
}
|
||
|
|
||
|
var pubPointsJson = _this.mall.getFloorJson(_this.mall.getCurFloorId()).PubPoint;
|
||
|
var imgWidth, imgHeight;
|
||
|
for(var i = 0; i < pubPointsJson.length; i++){
|
||
|
var spriteMat = _spriteMaterials[pubPointsJson[i].Type];
|
||
|
if(spriteMat !== undefined) {
|
||
|
imgWidth = 30, imgHeight = 30;
|
||
|
var sprite = new THREE.Sprite(spriteMat);
|
||
|
sprite.scale.set(imgWidth, imgHeight, 1);
|
||
|
sprite.oriX = pubPointsJson[i].Outline[0][0][0];
|
||
|
sprite.oriY = pubPointsJson[i].Outline[0][0][1];
|
||
|
sprite.width = imgWidth;
|
||
|
sprite.height = imgHeight;
|
||
|
_pubPointSprites.add(sprite);
|
||
|
}
|
||
|
}
|
||
|
_sceneOrtho.add(_pubPointSprites);
|
||
|
}
|
||
|
|
||
|
function clearNameSprites(){
|
||
|
if(_nameSprites == null){
|
||
|
return;
|
||
|
}
|
||
|
_nameSprites.remove(_nameSprites.children);
|
||
|
_nameSprites.children.length = 0;
|
||
|
}
|
||
|
function clearPubPointSprites(){
|
||
|
if(_pubPointSprites == null){
|
||
|
return;
|
||
|
}
|
||
|
_pubPointSprites.remove(_pubPointSprites.children);
|
||
|
_pubPointSprites.children.length = 0;
|
||
|
}
|
||
|
|
||
|
function makeTextSprite( message, parameters )
|
||
|
{
|
||
|
if ( parameters === undefined ) parameters = {};
|
||
|
|
||
|
var fontface = parameters.hasOwnProperty("fontface") ?
|
||
|
parameters["fontface"] : "Arial";
|
||
|
|
||
|
var fontsize = parameters.hasOwnProperty("fontsize") ?
|
||
|
parameters["fontsize"] : 18;
|
||
|
|
||
|
var borderThickness = parameters.hasOwnProperty("borderThickness") ?
|
||
|
parameters["borderThickness"] : 2;
|
||
|
|
||
|
var borderColor = parameters.hasOwnProperty("borderColor") ?
|
||
|
parameters["borderColor"] : { r:0, g:0, b:0, a:1.0 };
|
||
|
|
||
|
var backgroundColor = parameters.hasOwnProperty("backgroundColor") ?
|
||
|
parameters["backgroundColor"] : { r:255, g:255, b:255, a:1.0 };
|
||
|
|
||
|
var fontColor = parameters.hasOwnProperty("color")?
|
||
|
parameters["color"] : "#000000";
|
||
|
|
||
|
//var spriteAlignment = parameters.hasOwnProperty("alignment") ?
|
||
|
// parameters["alignment"] : THREE.SpriteAlignment.topLeft;
|
||
|
|
||
|
var spriteAlignment = new THREE.Vector2( 0, 0 );
|
||
|
|
||
|
|
||
|
var canvas = document.createElement('canvas');
|
||
|
var context = canvas.getContext('2d');
|
||
|
context.font = "Bold " + fontsize + "px " + fontface;
|
||
|
|
||
|
// get size data (height depends only on font size)
|
||
|
var metrics = context.measureText( message );
|
||
|
//
|
||
|
// // background color
|
||
|
// context.fillStyle = "rgba(" + backgroundColor.r + "," + backgroundColor.g + ","
|
||
|
// + backgroundColor.b + "," + backgroundColor.a + ")";
|
||
|
// // border color
|
||
|
context.strokeStyle = "rgba(" + borderColor.r + "," + borderColor.g + ","
|
||
|
+ borderColor.b + "," + borderColor.a + ")";
|
||
|
//
|
||
|
// context.lineWidth = borderThickness;
|
||
|
// context.strokeRect(borderThickness/2, borderThickness/2, metrics.width + borderThickness, fontsize * 1.4 + borderThickness);
|
||
|
|
||
|
// text color
|
||
|
context.fillStyle = fontColor;
|
||
|
|
||
|
context.fillText( message, borderThickness, fontsize + borderThickness);
|
||
|
|
||
|
// canvas contents will be used for a texture
|
||
|
var texture = new THREE.Texture(canvas)
|
||
|
texture.needsUpdate = true;
|
||
|
|
||
|
|
||
|
var spriteMaterial = new THREE.SpriteMaterial(
|
||
|
{ map: texture, useScreenCoordinates: false } );
|
||
|
var sprite = new THREE.Sprite( spriteMaterial );
|
||
|
sprite.scale.set(100,50,1.0);
|
||
|
sprite.width = metrics.width;
|
||
|
sprite.height = fontsize * 1.4;
|
||
|
return sprite;
|
||
|
}
|
||
|
|
||
|
//resize the map
|
||
|
this.resize = function (width, height){
|
||
|
_this.camera.aspect = width / height;
|
||
|
_this.camera.updateProjectionMatrix();
|
||
|
|
||
|
_this.renderer.setSize( width, height );
|
||
|
_controls.viewChanged = true;
|
||
|
}
|
||
|
|
||
|
_this.init();
|
||
|
animate();
|
||
|
|
||
|
}
|