var Arena ={ create:function(id, config, options){ var ar = {}; ///////// function init code here ///////// ar.init = function(id){ // public vars ar.units = []; ar.options = { showSky: true, dynamicSky: false, showShadow: true, dynamicShadow: true }; ar.id = id; ar.domId = "#" + id; ar.cruise = { target : new THREE.Vector3(), lon: 90, lat: 0, phi: 0, theta : 0 }; ar.temp = {}; ar.shape3Ds = []; ar.mouse = new THREE.Vector2(); function getViewSize(){ var SCREEN_WIDTH = window.innerWidth - $(ar.domId).offset().left; var SCREEN_HEIGHT = window.innerHeight - $(ar.domId).offset().top - 17; return { width: SCREEN_WIDTH, height: SCREEN_HEIGHT }; } function initScene(){ ar.raycaster = new THREE.Raycaster(); ar.scene = new THREE.Scene(); //ar.scene.overrideMaterial = new THREE.MeshDepthMaterial(); } function initRender(){ if(Detector.webgl){ ar.renderer = new THREE.WebGLRenderer({ antialias:true}); if (ar.options.showShadow){ ar.renderer.shadowMap.enabled = true; ar.renderer.shadowMapSoft = true; ar.renderer.shadowMap.type = THREE.PCFSoftShadowMap; } console.info("webGL render is working."); }else{ ar.renderer = new THREE.CanvasRenderer(); //WebGLRenderer console.info("canvas render is working. restart brower remote to enable WebGL"); } ar.renderer.sortObjects = false; ar.renderer.setClearColor( 0x0086b3 ,1); ar.renderer.setPixelRatio( window.devicePixelRatio ); ar.renderer.setSize(getViewSize().width,getViewSize().height); } function initCamera(){ // camera attributes var VIEW_ANGLE = 45, ASPECT = getViewSize().width / getViewSize().height, NEAR = 10, FAR = 50000; ar.camera = new THREE.PerspectiveCamera(VIEW_ANGLE, ASPECT, NEAR, FAR); ar.camera.position.set(-100, 1600, 1800); ar.camera.lookAt(ar.scene.position); ar.scene.add(ar.camera); //ar.scene.fog = new THREE.Fog( 0xffffff, 1, 5000 ); //ar.scene.fog.color.setHSL( 0.6, 0, 1 ); } function initSky(){ var texture_placeholder = document.createElement( 'canvas' ); texture_placeholder.width = 128; texture_placeholder.height = 128; var context = texture_placeholder.getContext( '2d' ); context.fillStyle = 'rgb( 200, 200, 200 )'; context.fillRect( 0, 0, texture_placeholder.width, texture_placeholder.height ); function loadTexture( path ) { var texture = new THREE.Texture( texture_placeholder ); var material = new THREE.MeshBasicMaterial( { map: texture, overdraw: 0.5 } ); var image = new Image(); image.onload = function () { texture.image = this; texture.needsUpdate = true; }; image.src = path; return material; } var materials = [ loadTexture('/img/3d/skybox/skybox_02.png'), loadTexture('/img/3d/skybox/skybox_04.png'), loadTexture('/img/3d/skybox/skybox_05.png'), loadTexture('/img/3d/skybox/skybox_06.png'), loadTexture('/img/3d/skybox/skybox_01.png'), loadTexture('/img/3d/skybox/skybox_03.png') ]; // var materials = [ // loadTexture( '/img/3d/skybox/px.jpg' ), // right // loadTexture( '/img/3d/skybox/nx.jpg' ), // left // loadTexture( '/img/3d/skybox/py.jpg' ), // top // loadTexture( '/img/3d/skybox/ny.jpg' ), // bottom // loadTexture( '/img/3d/skybox/pz.jpg' ), // back // loadTexture( '/img/3d/skybox/nz.jpg' ) // front // ]; var mesh = new THREE.Mesh( new THREE.BoxGeometry( 30000, 30000, 30000, 7, 7, 7 ), new THREE.MeshFaceMaterial( materials ) ); mesh.scale.x = - 1; ar.scene.add( mesh ); } function initResize(){ $(window).resize(function (){ ar.camera.aspect = getViewSize().width / getViewSize().height; ar.camera.updateProjectionMatrix(); ar.renderer.setSize( getViewSize().width, getViewSize().height); }); } function initControl(){ //ar.controls = new THREE.OrbitControls(ar.camera, ar.renderer.domElement); ar.controls = new THREE.OrbitControls( ar.camera ); ar.controls.addEventListener( 'change', ar.render ); ar.controls.minDistance = 500; ar.controls.maxDistance = 5000; ar.controls.maxPolarAngle = Math.PI * 0.5; //ar.controls.minPolarAngle = Math.PI * 0.1; ar.container.style.position = "relative"; var btnContainer = document.createElement('div'); btnContainer.id = "btnContainer"; btnContainer.style.cssText = "position:absolute;left:20px;bottom:20px;margin:10px;"; var vi = document.createElement('img'); vi.id = "veilimg"; vi.style.cssText = "padding:3px;cursor:pointer;"; vi.src = 'img/3d/veilView-h.png'; vi.onclick = function(){ if (this.src.indexOf('img/3d/veilView-h.png') >= 0) { this.src = 'img/3d/veilView-i.png'; document.getElementById("overimg").src="img/3d/overview-h.png"; ar.cartoon('veilview'); } }; var oi = document.createElement('img'); oi.id = "overimg"; oi.style.cssText = "padding:3px;cursor:pointer;"; oi.src = "img/3d/overview-h.png"; oi.onclick = function(){ if (this.src.indexOf('img/3d/overview-h.png') >= 0) { this.src = 'img/3d/overview-i.png'; document.getElementById("veilimg").src="img/3d/veilView-h.png"; ar.cartoon('overview'); } }; btnContainer.appendChild(vi); btnContainer.appendChild(oi); ar.container.appendChild(btnContainer); } function initDebugParts(){ // axes var axes = new THREE.AxisHelper(100); ar.scene.add( axes ); // displays current and past frames per second attained by scene ar.stats = new Stats(); ar.stats.domElement.style.position = 'absolute'; ar.stats.domElement.style.bottom = '0px'; ar.stats.domElement.style.zIndex = 100; ar.container.appendChild(ar.stats.domElement); // Grid var size = 800, step = 80; var geometry = new THREE.Geometry(); for (var i = -size; i <= size; i += step) { geometry.vertices.push(new THREE.Vector3(-size, 0, i)); geometry.vertices.push(new THREE.Vector3(size, 0, i)); geometry.vertices.push(new THREE.Vector3(i, 0, -size)); geometry.vertices.push(new THREE.Vector3(i, 0, size)); } var material = new THREE.LineBasicMaterial({color: 0xeeeeee, opacity: 0.7}); var line = new THREE.Line(geometry, material, THREE.LinePieces); ar.scene.add(line); //ArrowHelper var directionV3 = new THREE.Vector3(1, 0, 1); var originV3 = new THREE.Vector3(0, 200, 0); // 100 is length, 20 and 10 are head length and width var arrowHelper = new THREE.ArrowHelper(directionV3, originV3, 100, 0xff0000, 20, 10); ar.scene.add(arrowHelper); // 3. BoundingBoxHelper //bboxHelper = new THREE.BoundingBoxHelper(group, 0x999999); //ar.scene.add(bboxHelper); } function defineMouseBehavior(){ ar.container.addEventListener( 'mousemove', onDocumentMouseMove, false ); ar.container.addEventListener( 'mousedown', onDocumentMouseDown, false ); function onDocumentMouseMove( event ) { ar.mouse.x = ( (event.clientX - $(ar.domId).position().left)/ $(ar.domId).width()) * 2 - 1; ar.mouse.y = - ( (event.clientY - $(ar.domId).position().top) / $(ar.domId).height()) * 2 + 1; event.preventDefault(); } function onDocumentMouseDown( e ) { e.preventDefault(); if (ar.INTERSECTED){ var shp = ar.getShapeByMesh(ar.INTERSECTED); if (shp.config.bindings.length > 0) { var devId = shp.config.bindings[0].id; ar.options.shell.$location.url('/deviceInfo/' + devId); //alert(angular.toJson(shp.config)); } } } } function attachToDom(){ ar.container = document.getElementById(ar.id); ar.container.appendChild(ar.renderer.domElement); } initScene(); initRender(); initCamera(); attachToDom(); if (ar.options.showSky) initSky(); initResize(); initControl(); //initDebugParts(); defineMouseBehavior(); }; ar.clear = function(){ ar.shape3Ds = []; }; ar.load = function(config){ ar.clear(); if (config) { ar.loadConfig = config; } else { ar.loadConfig = bn.demoConfig; } ar.loadConfig.shapes.forEach(function (shp) { if (shp.param3D) ar.loadUnit(shp.param3D); }); ar.initLight(); }; ar.loadUnit = function(param) { var ut = _.find(ar.units, function (item) { return item.name === param.unit; }); if (_.isUndefined(ut)) return; if (_.isUndefined(ut.create)) return; var mesh = ut.create(param); mesh.unit = ut; var shp = _.extend({}, ar.shape3DClass); shp.mesh = mesh; shp.config = param; shp.id = _.uniqueId(); ar.shape3Ds.push(shp); ar.scene.add(mesh); }; ar.animate = function(){ ar.aniframeId = requestAnimationFrame(ar.animate); TWEEN.update(); ar.render(); ar.controls.update(); }; ar.render = function(){ if (!ar) return; ar.update(); ar.renderer.render(ar.scene, ar.camera); }; ar.update = function() { if (ar.options.dynamicSky){ ar.cruise.lon += 0.1; ar.cruise.lat = Math.max( - 85, Math.min( 85, ar.cruise.lat ) ); ar.cruise.phi = THREE.Math.degToRad( 90 - ar.cruise.lat ); ar.cruise.theta = THREE.Math.degToRad( ar.cruise.lon ); ar.cruise.target.x = 500 * Math.sin( ar.cruise.phi ) * Math.cos( ar.cruise.theta ); ar.cruise.target.y = 500 * Math.cos( ar.cruise.phi ); ar.cruise.target.z = 500 * Math.sin( ar.cruise.phi ) * Math.sin( ar.cruise.theta ); ar.camera.lookAt( ar.cruise.target ); } if (ar.options.dynamicShadow){ var timer = Date.now() * 0.0001; ar.light.position.x = Math.sin(timer) * 1000; ar.light.position.z = Math.cos(timer) * 1000; ar.light.target.position.set(0, 0, 0); } ar.shape3Ds.forEach(function(shp){ if (shp.mesh.unit.isFlat) shp.mesh.lookAt( ar.camera.position ); }); // high light mouse indicator ar.raycaster.setFromCamera( ar.mouse, ar.camera ); var intersects = ar.raycaster.intersectObjects( ar.scene.children, true ); if ( intersects.length > 0 ) { if ( ar.INTERSECTED != intersects[ 0 ].object ) { if ( ar.INTERSECTED ) ar.util.maskColor(ar.INTERSECTED, ar.INTERSECTED.currentHex); ar.INTERSECTED = intersects[ 0 ].object; ar.INTERSECTED.currentHex = ar.util.getMaskColor(ar.INTERSECTED); ar.util.maskColor(ar.INTERSECTED, 0x223366); } } else { if ( ar.INTERSECTED ) ar.util.maskColor(ar.INTERSECTED, ar.INTERSECTED.currentHex); ar.INTERSECTED = null; } }; ar.getUnit = function(name){ return _.find(ar.units, function (unit) { return unit.name === name; }); }; ar.initLight = function () { //泛光可调整整个房间的明暗度 //var ambient = new THREE.AmbientLight( 0x888888); //ar.scene.add( ambient ); //半球面环境光,模拟天空到大地的光线,可作为主环境光 var envlight = new THREE.HemisphereLight( 0xffffff, 0xA2C9ED, 0.8); //var envlight = new THREE.HemisphereLight( 0x888888, 0x111111, 0.8); //envlight.position.set( 0, 1500, 0 ); //envlight.shadowCameraFov = 60; ar.scene.add(envlight); // 5000 is sphere size, 3000 is arrow length //var hlightHelper = new THREE.HemisphereLightHelper(envlight, 10000, 1000); //ar.scene.add(hlightHelper); //平行光,用于产生阴影 var light = new THREE.DirectionalLight(0x222222); light.position.set(0, 1000, 2000); light.target.position.set(0, 0, 0); light.castShadow = true; light.shadowDarkness = 0.7; //light.shadowCameraNear = 2; //light.shadowCameraFar = 5000; light.shadowCameraLeft = -3000; light.shadowCameraRight = 3000; light.shadowCameraTop = 3000; light.shadowCameraBottom = -3000; //light.shadowCameraFov = 60000; light.shadowMapWidth = light.shadowMapHeight = 1024; ar.scene.add(light); ar.light = light; //ar.camera.updateMatrix(); //var cameraHelper = new THREE.CameraHelper( light.shadow ); //this.scene.add(cameraHelper); //// 50 is helper size //dlightHelper = new THREE.DirectionalLightHelper(light, 500); //ar.scene.add(dlightHelper); }; ar.dispose = function(){ var disposeObject = function(obj){ for(var b in obj){ if(typeof(obj[b]) === "function" && obj[b].name === "event"){ obj[b] = undefined; } else if(typeof(obj[b]) === "function" && obj[b].length > 0){ disposeObject(obj[b]); } else if(typeof(obj[b]) === "object" && b === "dispatch"){ disposeObject(obj[b]); } } }; var disposeNode = function(node) { if (node instanceof THREE.Camera) { node = undefined; } else if (node instanceof THREE.Light) { if (!node.dispose){ disposeObject(node); } else node.dispose (); node = undefined; } else if (node instanceof THREE.Mesh) { if (node.geometry) { node.geometry.dispose (); node.geometry = undefined; delete node.geometry; } if (node.material) { if (node.material instanceof THREE.MeshFaceMaterial) { $.each (node.material.materials, function (idx, mtrl) { if (!mtrl) return; mtrl.dispose(); if (mtrl.map) { mtrl.map.dispose(); mtrl.map = undefined; delete mtrl.map; } if (mtrl.lightMap) mtrl.lightMap.dispose(); if (mtrl.bumpMap) mtrl.bumpMap.dispose(); if (mtrl.normalMap) mtrl.normalMap.dispose(); if (mtrl.specularMap) mtrl.specularMap.dispose(); if (mtrl.envMap) mtrl.envMap.dispose(); mtrl.dispose(); // disposes any programs associated with the material mtrl = undefined; delete mtrl; }); } else { if (node.material.map) { node.material.map.dispose(); node.material.map = undefined; delete node.material.map; } if (node.material.lightMap) node.material.lightMap.dispose (); if (node.material.bumpMap) node.material.bumpMap.dispose (); if (node.material.normalMap) node.material.normalMap.dispose (); if (node.material.specularMap) node.material.specularMap.dispose (); if (node.material.envMap) node.material.envMap.dispose (); node.material.dispose (); // disposes any programs associated with the material node.material = undefined; delete node.material; } } node = undefined; } else if (node instanceof THREE.Object3D) { node = undefined; delete node; } }; var disposeHierarchy = function(node, callback) { for (var i = node.children.length - 1; i >= 0; i--) { var child = node.children[i]; disposeHierarchy (child, callback); callback (child); } }; //stop data pooling if (ar.provider) ar.provider.dispose(); //remove event for controller $(window).off(); $(window).unbind(); $(document).off(); $(document).unbind(); $(ar.domId).off(); $(ar.domId).unbind(); if (ar.renderer) $(ar.renderer.domElement).off(); //stop animation TWEEN.removeAll(); if (ar.aniframeId) window.cancelAnimationFrame(ar.aniframeId); //remove shapes // if (ar.shape3Ds){ // ar.shape3Ds.forEach(function(shp){ // if (shp.mesh){ // ar.scene.remove(shp.mesh); // disposeHierarchy(shp.mesh, disposeNode); // } // }); // disposeObject(ar.shape3Ds); // ar.shape3Ds = undefined; // delete ar.shape3Ds; // } //remove scene if (ar.controls) ar.controls.dispose(); if (ar.raycaster) disposeObject(ar.raycaster); // $.each(ar.scene.children, function(idx, obj) { // if (obj !== undefined) { // if (obj.geometry) { // obj.geometry.dispose(); // } // if (obj.material) { // if (obj.material instanceof THREE.MeshFaceMaterial) { // $.each(obj.material.materials, function(idx, obj) { // obj.dispose(); // }); // } else { // obj.material.dispose(); // } // } // if (obj.dispose) { // obj.dispose(); // } // } // }); //clear all resource if (ar.camera) disposeObject(ar.camera); //if (ar.scene) disposeHierarchy (ar.scene, disposeNode); if (ar.container) disposeObject(ar.container); if (ar.renderer) { ar.renderer.forceContextLoss(); ar.renderer.context = null; ar.renderer.domElement = null; disposeObject(ar.renderer); ar.renderer = null; } doDispose(ar.scene); function doDispose(obj) { if (obj !== null) { for (var i = 0; i < obj.children.length; i++) { doDispose(obj.children[i]); } if (obj.geometry) { obj.geometry.dispose(); obj.geometry = undefined; } if (obj.material) { if (obj.material instanceof THREE.MeshFaceMaterial) { $.each(obj.material.materials, function(idx, o) { if (!o) return; if (o.map) { o.map.dispose(); o.map = undefined; delete o.map; } o.dispose(); }); } else { obj.material.dispose(); } } if(obj.dispose) { obj.dispose(); } } obj = undefined; } ar.scene = null; disposeObject(ar); ar = undefined; }; ar.cartoon = function(type){ if (type == "overview"){ var position = {x:0,y:3000,z:0}; var target = {x:0,y:0,z:-1}; ar.tweenCamera(position, target); } if (type == "veilview"){ var position = {x:-3000,y:3000,z:-3000}; var target = {x:0,y:0,z:0}; ar.tweenCamera(position, target); } }; ar.tweenCamera = function tweenCamera(position, target){ //TWEEN.removeAll(); new TWEEN.Tween(ar.camera.position).to({ x: position.x, y: position.y, z: position.z },3000).easing(TWEEN.Easing.Linear.None).onUpdate(function () { ar.camera.lookAt(target); }).onComplete(function () { }).start(); }; ar.getShapeByMesh = function (mesh){ if (!mesh) return; var obj = mesh; while(!(obj.parent instanceof THREE.Scene)){ obj = obj.parent; } var selected; ar.shape3Ds.forEach(function(shp){ if (shp.mesh === obj){ selected = shp; } }); return selected; }; ar.loadUnits = function(){ ar.units = [ { name : "floor", buildFloor: function(param){ //因为SVG无法使用底图(只能使用UV贴图),作为地板非常难看。 //这里取消平面自动地板生成功能(可生成不规则地板)限制为仅能使用矩形地板(使用BOX模拟) //所以下面代码注释掉: //var floorG = ar.transformSVGPath(param.path); //var planeMesh = ar.createShape(floorG, 0xdddddd, 0, 0, 0, Math.PI/2, 0, 0, 1); //group.add(planeMesh); //planeMesh.geometry.center(); var tileSize = 64; var floorTexture = new THREE.TextureLoader().load(param.floorTexture); floorTexture.wrapS = floorTexture.wrapT = THREE.RepeatWrapping; floorTexture.repeat.set( param.floorWidth / tileSize, param.floorHeight / tileSize ); var floorMaterial = new THREE.MeshPhongMaterial( { map: floorTexture, side: THREE.DoubleSide} ); var floorGeometry = new THREE.PlaneGeometry(param.floorWidth, param.floorHeight,1,1); var floor = new THREE.Mesh(floorGeometry, floorMaterial); floor.position.y = -0.5; floor.rotation.x = Math.PI / 2; floor.receiveShadow = true; return floor; }, create: function (param){ //create an empty container var group = new THREE.Object3D(); var floor = this.buildFloor(param); group.add(floor); //get enclosure lines var lines = []; var lineX1 = {}; lineX1.start = {x: -(param.floorWidth / 2), y:0, z: -(param.floorHeight / 2)}; lineX1.end = {x: param.floorWidth / 2, y:0, z: -(param.floorHeight / 2)}; lines.push(lineX1); var lineX2 = {}; lineX2.start = {x: -(param.floorWidth / 2), y:0, z: param.floorHeight / 2}; lineX2.end = {x: param.floorWidth / 2, y:0, z: param.floorHeight / 2}; lines.push(lineX2); var lineX3 = {}; lineX3.start = {x: -(param.floorWidth / 2), y:0, z: -(param.floorHeight / 2)}; lineX3.end = {x: -(param.floorWidth / 2), y:0, z: param.floorHeight / 2}; lines.push(lineX3); var lineX4 = {}; lineX4.start = {x: param.floorWidth / 2, y:0, z: -(param.floorHeight / 2)}; lineX4.end = {x: param.floorWidth / 2, y:0, z: param.floorHeight / 2}; lines.push(lineX4); //build walls lines.forEach(function(line){ var mold = ar.util.getMold(param.moldId); group.add(ar.util.buildWall(mold, line)); }); return group; } },{ name : "door", create: function (param) { return ar.util.buildWallPart(param); } //},{ // name : "window", // create: function (param) { // return ar.util.buildWallPart(param); // } },{ name:"rack", create: function(param){ var mold = ar.util.getMold(param.moldId); var faces = []; // order of faces: x+,x-,y+,y-,z+,z- var rightFace = ar.util.newFace(mold.faces.rightFace); faces.push(rightFace); var leftFace = ar.util.newFace(mold.faces.leftFace); faces.push(leftFace); var topFace = ar.util.newFace(mold.faces.topFace); faces.push(topFace); var bottomFace = ar.util.newFace(mold.faces.bottomFace); faces.push(bottomFace); var frontFace = ar.util.newFace(mold.faces.frontFace); faces.push(frontFace); var backFace = ar.util.newFace(mold.faces.backFace); faces.push(backFace); var cubeMaterials = new THREE.MeshFaceMaterial(faces); var cubeGeometry = new THREE.CubeGeometry( mold.size.width, mold.size.high, mold.size.depth); var cube = new THREE.Mesh(cubeGeometry, cubeMaterials); cube.position.x = param.position.x; cube.position.y = mold.size.high / 2 + param.aboveGround; cube.position.z = param.position.z; //if (param.angle > 0 ) cube.rotateY( 0 - ar.util.radians(param.angle)); cube.castShadow = true; return cube; } },{ name:"line", create: function(param){ var geometry = new THREE.Geometry(); param.lines.forEach(function(line){ geometry.vertices.push(new THREE.Vector3( line.start.x, line.start.y, line.start.z )); geometry.vertices.push(new THREE.Vector3( line.end.x, line.end.y, line.end.z )); }); var m = new THREE.LineBasicMaterial({ color: param.color,fog:true}); var line = new THREE.Line(geometry, m); return line; } },{ name:"flat", isFlat:true, create: function(param){ var mold = ar.util.getMold(param.moldId); var obj = ar.util.flatObject(param, mold,param.position, parseFloat(param.aboveGround)); return obj.mesh; } },{ name:"wallPath", create: function(param){ var group = new THREE.Object3D(); var mold = ar.util.getMold(param.moldId); if (param.moldId !== "wallpath-null") { param.lines.forEach(function(line){ group.add(ar.util.buildWall(mold, line)); }); } //if (param.angle > 0 ) group.rotateY( 0 - ar.util.radians(param.angle)); return group; } },{ name:"camera", create: function(param){ //var transparentColor = 0xffffff; //var size = {x:64,y:64}; //var imgurl = "img/3d/CCTV-icon.png"; // //var obj = ar.util.flatObject(imgurl, transparentColor, // size,param.position); // //return obj.mesh; var geometry = new THREE.SphereGeometry( 30, 16, 16 ); var material = new THREE.MeshBasicMaterial( { map: THREE.ImageUtils.loadTexture('img/3d/global.jpg')}); var sphere = new THREE.Mesh( geometry, material ); sphere.position.x = param.position.x; sphere.position.y = param.position.y; sphere.position.z = param.position.z; return sphere; } } ]; }; ar.shape3DClass = { mesh: undefined, tws:[], defaultColors:[], update:function(status){ if (status.state === this.alarmLevel) return; this.alarmLevel = status.state; var needRemove=null; this.tws.forEach(function(fso){ if (fso.id === this.id) { fso.twarray.forEach(function(tw){ tw.stop(); }); needRemove = fso; } }); this.tws = _.without(this.tws, needRemove); this.resetColor(); if (this.alarmLevel == 0) return; this.shining(this.alarmLevel); }, resetColor:function(){ var that = this; if (that.mesh.material instanceof THREE.MultiMaterial) { var i=0; that.mesh.material.materials.forEach( function(m) { m.color = {r:1,g:1,b:1}; }); } }, shining: function(alarmLevel){ var that = this; var color = {r:1,g:1,b:1}; switch(alarmLevel) { case "1": color={r:0,g:0,b:2}; break; case "2": color={r:2,g:2,b:0}; break; case "3": color={r:2, g: 1, b:0}; break; case "4": color={r:2, g: 0, b:0}; break; default: } if (that.mesh.material instanceof THREE.MultiMaterial) { var kso = {id:that.id,twarray:[]}; that.mesh.material.materials.forEach(function(m){ var tw = new TWEEN.Tween(m.color) .to(color, 3000) .repeat(Infinity) .easing(TWEEN.Easing.Quartic.InOut) .start(); kso.twarray.push(tw); }); that.tws.push(kso); } } }; ar.util = {}; ar.util.createShape = function ( shape, color, x, y, z, rx, ry, rz, s ) { // flat shape like svg var geometry = new THREE.ShapeGeometry( shape ); var material = new THREE.MeshBasicMaterial({ color: color, side: THREE.DoubleSide, overdraw: true }); var mesh = new THREE.Mesh( geometry, material ); mesh.position.set( x, y, z ); mesh.rotation.set( rx, ry, rz ); mesh.scale.set( s, s, s ); return mesh; }; ar.util.transformSVGPath = function (pathStr) { const DIGIT_0 = 48, DIGIT_9 = 57, COMMA = 44, SPACE = 32, PERIOD = 46, MINUS = 45; var path = new THREE.Shape(); var idx = 1, len = pathStr.length, activeCmd, x = 0, y = 0, nx = 0, ny = 0, firstX = null, firstY = null, x1 = 0, x2 = 0, y1 = 0, y2 = 0, rx = 0, ry = 0, xar = 0, laf = 0, sf = 0, cx, cy; function eatNum() { var sidx, c, isFloat = false, s; // eat delims while (idx < len) { c = pathStr.charCodeAt(idx); if (c !== COMMA && c !== SPACE) break; idx++; } if (c === MINUS) sidx = idx++; else sidx = idx; // eat number while (idx < len) { c = pathStr.charCodeAt(idx); if (DIGIT_0 <= c && c <= DIGIT_9) { idx++; continue; } else if (c === PERIOD) { idx++; isFloat = true; continue; } s = pathStr.substring(sidx, idx); return isFloat ? parseFloat(s) : parseInt(s); } s = pathStr.substring(sidx); return isFloat ? parseFloat(s) : parseInt(s); } function nextIsNum() { var c; // do permanently eat any delims... while (idx < len) { c = pathStr.charCodeAt(idx); if (c !== COMMA && c !== SPACE) break; idx++; } c = pathStr.charCodeAt(idx); return (c === MINUS || (DIGIT_0 <= c && c <= DIGIT_9)); } var canRepeat; activeCmd = pathStr[0]; while (idx <= len) { canRepeat = true; switch (activeCmd) { // moveto commands, become lineto's if repeated case 'M': x = eatNum(); y = eatNum(); path.moveTo(x, y); activeCmd = 'L'; break; case 'm': x += eatNum(); y += eatNum(); path.moveTo(x, y); activeCmd = 'l'; break; case 'Z': case 'z': canRepeat = false; if (x !== firstX || y !== firstY) path.lineTo(firstX, firstY); break; // - lines! case 'L': case 'H': case 'V': nx = (activeCmd === 'V') ? x : eatNum(); ny = (activeCmd === 'H') ? y : eatNum(); path.lineTo(nx, ny); x = nx; y = ny; break; case 'l': case 'h': case 'v': nx = (activeCmd === 'v') ? x : (x + eatNum()); ny = (activeCmd === 'h') ? y : (y + eatNum()); path.lineTo(nx, ny); x = nx; y = ny; break; // - cubic bezier case 'C': x1 = eatNum(); y1 = eatNum(); case 'S': if (activeCmd === 'S') { x1 = 2 * x - x2; y1 = 2 * y - y2; } x2 = eatNum(); y2 = eatNum(); nx = eatNum(); ny = eatNum(); path.bezierCurveTo(x1, y1, x2, y2, nx, ny); x = nx; y = ny; break; case 'c': x1 = x + eatNum(); y1 = y + eatNum(); case 's': if (activeCmd === 's') { x1 = 2 * x - x2; y1 = 2 * y - y2; } x2 = x + eatNum(); y2 = y + eatNum(); nx = x + eatNum(); ny = y + eatNum(); path.bezierCurveTo(x1, y1, x2, y2, nx, ny); x = nx; y = ny; break; // - quadratic bezier case 'Q': x1 = eatNum(); y1 = eatNum(); case 'T': if (activeCmd === 'T') { x1 = 2 * x - x1; y1 = 2 * y - y1; } nx = eatNum(); ny = eatNum(); path.quadraticCurveTo(x1, y1, nx, ny); x = nx; y = ny; break; case 'q': x1 = x + eatNum(); y1 = y + eatNum(); case 't': if (activeCmd === 't') { x1 = 2 * x - x1; y1 = 2 * y - y1; } nx = x + eatNum(); ny = y + eatNum(); path.quadraticCurveTo(x1, y1, nx, ny); x = nx; y = ny; break; // - elliptical arc case 'A': rx = eatNum(); ry = eatNum(); xar = eatNum() * DEGS_TO_RADS; laf = eatNum(); sf = eatNum(); nx = eatNum(); ny = eatNum(); if (rx !== ry) { console.warn("Forcing elliptical arc to be a circular one :(", rx, ry); } // SVG implementation notes does all the math for us! woo! // http://www.w3.org/TR/SVG/implnote.html#ArcImplementationNotes // step1, using x1 as x1' x1 = Math.cos(xar) * (x - nx) / 2 + Math.sin(xar) * (y - ny) / 2; y1 = -Math.sin(xar) * (x - nx) / 2 + Math.cos(xar) * (y - ny) / 2; // step 2, using x2 as cx' var norm = Math.sqrt( (rx*rx * ry*ry - rx*rx * y1*y1 - ry*ry * x1*x1) / (rx*rx * y1*y1 + ry*ry * x1*x1)); if (laf === sf) norm = -norm; x2 = norm * rx * y1 / ry; y2 = norm * -ry * x1 / rx; // step 3 cx = Math.cos(xar) * x2 - Math.sin(xar) * y2 + (x + nx) / 2; cy = Math.sin(xar) * x2 + Math.cos(xar) * y2 + (y + ny) / 2; var u = new THREE.Vector2(1, 0), v = new THREE.Vector2((x1 - x2) / rx, (y1 - y2) / ry); var startAng = Math.acos(u.dot(v) / u.length() / v.length()); if (u.x * v.y - u.y * v.x < 0) startAng = -startAng; // we can reuse 'v' from start angle as our 'u' for delta angle u.x = (-x1 - x2) / rx; u.y = (-y1 - y2) / ry; var deltaAng = Math.acos(v.dot(u) / v.length() / u.length()); // This normalization ends up making our curves fail to triangulate... if (v.x * u.y - v.y * u.x < 0) deltaAng = -deltaAng; if (!sf && deltaAng > 0) deltaAng -= Math.PI * 2; if (sf && deltaAng < 0) deltaAng += Math.PI * 2; path.absarc(cx, cy, rx, startAng, startAng + deltaAng, sf); x = nx; y = ny; break; default: throw new Error("weird path command: " + activeCmd); } if (firstX === null) { firstX = x; firstY = y; } // just reissue the command if (canRepeat && nextIsNum()) continue; activeCmd = pathStr[idx++]; } return path; }; ar.util.gradientTexture = function (){ function generateMeterial() { var size = 512; // create canvas var canvas = document.createElement( 'canvas' ); canvas.width = size; canvas.height = size; // get context var context = canvas.getContext( '2d' ); // draw gradient context.rect( 0, 0, size, size ); var gradient = context.createLinearGradient( 0, 0, 0, size ); gradient.addColorStop(0, '#99ddff'); // light blue gradient.addColorStop(1, 'transparent'); // dark blue context.fillStyle = gradient; context.fill(); var texture = new THREE.Texture( canvas ); texture.needsUpdate = true; var material = new THREE.MeshBasicMaterial( { map: texture, transparent: true } ); return material; } return generateMeterial(); }; ar.util.flatObject = function(param, mold, position,aboveGround){ var material = ar.util.newFace(mold.face); var geometry = new THREE.PlaneGeometry( mold.size.width, mold.size.high, 1, 1 ); var flat = new THREE.Mesh(geometry, material); flat.position.x = position.x; flat.position.y = mold.size.high / 2 + aboveGround; flat.position.z = position.z; var shp = _.extend({}, ar.shape3DClass); shp.mesh = flat; shp.isFlat = true; shp.id = _.uniqueId(); ar.shape3Ds.push(shp); shp.config = param; return shp; }; ar.util.buildWall = function(mold, segment){ function updateLine(segment){ if (segment.start.x === segment.end.x){ segment.length = Math.abs(segment.end.z - segment.start.z); segment.direct = "Z"; } if (segment.start.z === segment.end.z){ segment.length = Math.abs(segment.end.x - segment.start.x); segment.direct = "X"; } return segment; } var line = updateLine(segment); var sideMaterial = ar.util.newFace(mold.faces.sideFace,line.length); var wallMaterial = sideMaterial; var topMaterial = ar.util.newFace(mold.faces.topFace, line.length); var xlen,ylen,zlen,xm,ym,zm,px,py,pz; ylen = mold.size.high; py = ylen/2; if (line.direct === "X") { xlen = line.length + mold.size.width - 0.1; zlen = mold.size.width; zm = wallMaterial; ym = topMaterial; xm = sideMaterial; if (line.start.x < line.end.x) px = line.length/ 2 + line.start.x; if (line.start.x > line.end.x) px = line.start.x - line.length/2; pz = line.start.z; } if (line.direct === "Z") { zlen = line.length + mold.size.width / 2 - 0.1; xlen = mold.size.width; xm = wallMaterial; zm = sideMaterial; ym = topMaterial; if (line.start.z > line.end.z) pz = line.start.z - line.length / 2; if (line.start.z < line.end.z) pz = line.length /2 + line.start.z; px = line.start.x; } var wall = new THREE.Mesh(new THREE.CubeGeometry(xlen,ylen,zlen), new THREE.MeshFaceMaterial( [ xm, // +x xm, // -x ym, // +y ym, // -y zm, // +z zm // -z ])); wall.position.x = px; wall.position.y = py; wall.position.z = pz; wall.castShadow = true; wall.receiveShadow = true; return wall; }; ar.util.buildWallPart = function(param){ var mold = ar.util.getMold(param.moldId); //var doorMaterial = ar.util.newFace(mold.faces.topFace); var frontMaterial = ar.util.newFace(mold.faces.frontFace); var backMaterial = ar.util.newFace(mold.faces.backFace); var sideMaterial = ar.util.newFace(mold.faces.sideFace); var xlen,ylen,zlen,xm1,xm2,ym1,ym2,zm1,zm2,px,py,pz; ylen = mold.size.high; py = mold.size.high /2 + param.position.y; if (param.direct === "X") { xlen = mold.size.width; zlen = mold.size.depth; zm1 = frontMaterial; zm2 = backMaterial; ym1 = sideMaterial; ym2 = sideMaterial; xm1 = sideMaterial; xm2 = sideMaterial; px = param.position.x; pz = param.position.z; } if (param.direct === "Z") { zlen = mold.size.width; xlen = mold.size.depth; xm1 = frontMaterial; xm2 = backMaterial; zm1 = sideMaterial; zm2 = sideMaterial; ym1 = sideMaterial; ym2 = sideMaterial; pz = param.position.z; px = param.position.x; } var door = new THREE.Mesh(new THREE.CubeGeometry(xlen,ylen,zlen), new THREE.MeshFaceMaterial( [ xm1, // +x xm2, // -x ym1, // +y ym2, // -y zm1, // +z zm2 // -z ])); door.position.x = px; door.position.y = py; door.position.z = pz; return door; }; ar.util.getMold = function(id){ var res; molds.forEach(function(m){ if (m.id === id) res = m; }); return res; }; ar.util.radians = function(degrees) { return degrees * Math.PI / 180; }; ar.util.newFace = function(cfg, faceSize){ var param = {}; if (cfg.texture) { param.map = new THREE.TextureLoader().load( cfg.texture ); if (cfg.tileWay){ param.map.wrapS = param.map.wrapT = THREE.RepeatWrapping; var walltileSize = cfg.textureSize; if (cfg.tileWay == "H") param.map.repeat.set( faceSize / walltileSize, 1); if (cfg.tileWay == "T") param.map.repeat.set( faceSize / walltileSize, faceSize / walltileSize); if (cfg.tileWay == "V") param.map.repeat.set(1, faceSize / walltileSize); } } if (cfg.side) param.side = cfg.side; if (cfg.color) param.color = new THREE.Color(cfg.color); if (cfg.transparent) param.transparent = cfg.transparent; var material = new THREE.MeshPhongMaterial(param); if (cfg.opacity) { material.opacity = cfg.opacity; } else{ material.opacity = 1; } if (material.opacity !== 1) material.depthWrite = false; return material; }; ar.util.maskColor = function(mesh, color){ if (!mesh) return; try { if (mesh.material instanceof THREE.MeshPhongMaterial) { mesh.material.emissive.setHex( color ); //mesh.material.color.setHex( color ); } if (mesh.material instanceof THREE.MultiMaterial) { mesh.material.materials.forEach(function(m){ //m.color.setHex( color ); m.emissive.setHex( color ); }); } } catch(err) { } }; ar.util.getMaskColor = function(mesh){ if (!mesh) return; if (mesh.material instanceof THREE.MeshPhongMaterial) { return mesh.material.emissive.getHex(); } if (mesh.material instanceof THREE.MultiMaterial) { return mesh.material.materials[0].emissive.getHex(); } }; ar.provider = { collectBinding : function(){ var res = []; ar.shape3Ds.forEach(function(shp){ if (shp.config.bindings.length > 0) { var pack = { id:shp.id, bs:shp.config.bindings}; res.push(pack); } }); return res; }, start : function(){ if (!ar.options.shell) return; var bindingSet = this.collectBinding(); //http ask for result by interval this.stop = ar.options.shell.$interval(function() { //get data and refresh shapes status ar.options.shell.$srv.getData(bindingSet).then(function(data){ ar.shape3Ds.forEach(function(shp){ var status = _.find(data, function(item){ return item.id === shp.id; }); if (status) shp.update(status); }); }); }, 5000); }, dispose :function(){ if (!ar.options.shell) return; if (this.stop) ar.options.shell.$interval.cancel(this.stop); ar.options.shell.$srv = undefined; ar.options.shell.$interval = undefined; ar.options.shell.$location = undefined; this.stop = undefined; } }; ///////// create main code here ///////// ar.init(id); ar.loadUnits(); if (config) ar.load(config); else ar.load(); if (options) ar.options = _.extend(ar.options, options); //start 3D time line ar.animate(); ar.provider.start(); return ar; } };