diff --git a/CHANGELOG.md b/CHANGELOG.md index 761625a5..2d9f58bd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,24 @@ +## 2.0.7 + +Minor version by [@jscastro76](https://github.com/jscastro76), some enhancements and bugs. + +#### :sparkles: Enhancements + +- [**#54**](https://github.com/jscastro76/threebox/issues/54) vue and threebox,map not defined. +- [**#63**](https://github.com/jscastro76/threebox/issues/63) `CSS2DObject` is not disposing properly. +- Update example [15-performance.html](https://github.com/jscastro76/threebox/blob/master/examples/15-performance.html) to avoid dupplicated calls when dragging the count GUI control. + +#### :beetle: Bug fixes + +- [**#61**](https://github.com/jscastro76/threebox/issues/61) .userData not refreshed properly on obj.duplicate. +- [**#62**](https://github.com/jscastro76/threebox/issues/62) Memory Leak on tb.remove(). Tested +- [**#64**](https://github.com/jscastro76/threebox/issues/64) Cache instance of a loaded model is disposed when the world children is. + +
+ +- - - + + ## 2.0.6 Minor version by [@jscastro76](https://github.com/jscastro76), some enhancements and bugs. diff --git a/LICENSE.txt b/LICENSE.txt index b4e1fff7..c7b3fd0a 100755 --- a/LICENSE.txt +++ b/LICENSE.txt @@ -2,7 +2,7 @@ v.0.3.0 MIT License Copyright (c) 2017 Peter Liu -v.2.0.1 - v.2.0.6 +v.2.0.1 - v.2.0.7 MIT License Copyright (c) 2020 Jesus Serrano diff --git a/ThreeboxSolution.njsproj b/ThreeboxSolution.njsproj index 1991376f..e78e8588 100644 --- a/ThreeboxSolution.njsproj +++ b/ThreeboxSolution.njsproj @@ -14,7 +14,7 @@ $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion) Debug|Any CPU True - /examples/01-basic.html + /examples/15-performance.html True 8080 @@ -64,6 +64,7 @@ + diff --git a/dist/threebox.js b/dist/threebox.js index bc1530b1..3bec4f49 100755 --- a/dist/threebox.js +++ b/dist/threebox.js @@ -53,7 +53,7 @@ Threebox.prototype = { this.map = map; this.map.tb = this; //[jscastro] needed if we want to queryRenderedFeatures from map.onload - this.objects = new Objects(this.map); + this.objects = new Objects(); // Set up a THREE.js scene this.renderer = new THREE.WebGLRenderer({ @@ -86,8 +86,7 @@ Threebox.prototype = { this.scene.add(this.world); this.objectsCache = new Map(); - this.primises = new Map(); - + this.cameraSync = new CameraSync(this.map, this.camera, this.world); //raycaster for mouse events @@ -491,7 +490,7 @@ Threebox.prototype = { cache.promise .then(obj => { //console.log("Cloning " + options.obj); - cb(obj.duplicate()); + cb(obj.duplicate(options)); }) .catch(err => { this.objectsCache.delete(options.obj); @@ -504,7 +503,7 @@ Threebox.prototype = { loader(options, cb, function (obj) { //console.log("Loading " + options.obj); if (obj.duplicate) { - resolve(obj); + resolve(obj.duplicate()); } else { reject(obj); } @@ -681,43 +680,9 @@ Threebox.prototype = { }, remove: function (obj) { - //[jscastro] remove also the label if exists dispatching the event removed to fire CSS2DRenderer "removed" listener - if (obj.label) { obj.label.remove() }; - if (obj.tooltip) { obj.tooltip.remove() }; - obj.traverse(function (o) { - if (o.isMesh) { - o.geometry.dispose(); - if (o.material) { - if (o.material instanceof THREE.MeshFaceMaterial) { - o.material.materials.forEach(function (m) { - m.dispose(); - if (m.map) { - m.map.dispose(); - } - }); - } else { - o.material.dispose(); - } - let m = o.material; - let md = (m.map || m.alphaMap || m.aoMap || m.bumpMap || m.displacementMap || m.emissiveMap || m.envMap || m.lightMap || m.metalnessMap || m.normalMap || m.roughnessMap) - if (md) { - if (m.map) m.map.dispose(); - if (m.alphaMap) m.alphaMap.dispose(); - if (m.aoMap) m.aoMap.dispose(); - if (m.bumpMap) m.bumpMap.dispose(); - if (m.displacementMap) m.displacementMap.dispose(); - if (m.emissiveMap) m.emissiveMap.dispose(); - if (m.envMap) m.envMap.dispose(); - if (m.lightMap) m.lightMap.dispose(); - if (m.metalnessMap) m.metalnessMap.dispose(); - if (m.normalMap) m.normalMap.dispose(); - if (m.roughnessMap) m.roughnessMap.dispose(); - } - } - } - if (o.dispose) o.dispose(); - }) + obj.dispose() this.world.remove(obj); + obj = null; }, //[jscastro] this clears tb.world in order to dispose properly the resources @@ -731,7 +696,6 @@ Threebox.prototype = { let obj = objects[i]; //if layerId, check the layer to remove, otherwise always remove if ((layerId && obj.userData.feature.layer === layerId) || !layerId) { - if (dispose) obj.dispose(); this.remove(obj); } } @@ -894,7 +858,7 @@ Threebox.prototype = { programs: function () { return this.renderer.info.programs.length }, - version: '2.0.6', + version: '2.0.7', } @@ -927,6 +891,10 @@ function AnimationManager(map) { AnimationManager.prototype = { + unenroll: function (obj) { + this.enrolledObjects.splice(this.enrolledObjects.indexOf(obj), 1); + }, + enroll: function (obj) { //[jscastro] add the object default animations @@ -985,7 +953,6 @@ AnimationManager.prototype = { }) /* Extend the provided object with animation-specific properties and track in the animation manager */ - this.enrolledObjects.push(obj); // Give this object its own internal animation queue @@ -1042,7 +1009,7 @@ AnimationManager.prototype = { this.animationQueue .push(entry); - map.repaint = true; + tb.map.repaint = true; } //if no duration set, stop object's existing animations and go to that state immediately @@ -1092,7 +1059,7 @@ AnimationManager.prototype = { this.animationQueue .push(entry); - map.repaint = true; + tb.map.repaint = true; return this; }; @@ -1129,7 +1096,7 @@ AnimationManager.prototype = { if (w) this.position.copy(w); this.updateMatrixWorld(); - map.repaint = true + tb.map.repaint = true }; //[jscastro] play default animation @@ -1152,7 +1119,7 @@ AnimationManager.prototype = { this.animationQueue .push(entry); - map.repaint = true + tb.map.repaint = true return this; } } @@ -1212,7 +1179,7 @@ AnimationManager.prototype = { // Update the animation mixer and render this frame obj.mixer.update(0.01); } - map.repaint = true; + tb.map.repaint = true; return this; } @@ -1327,7 +1294,7 @@ AnimationManager.prototype = { object.isPlaying = true; object.animationMethod = requestAnimationFrame(this.update); object.mixer.update(object.clock.getDelta()); - map.repaint = true; + tb.map.repaint = true; } } @@ -1491,6 +1458,8 @@ THREE.CSS2DObject = function (element) { this.dispose = function () { this.remove(); + this.element = null; + if (this.parent) this.parent.remove(this); } this.remove = function () { @@ -1811,8 +1780,8 @@ function Object3D(options) { var projScaleGroup = new THREE.Group(); projScaleGroup.add(obj); var userScaleGroup = Objects.prototype._makeGroup(projScaleGroup, options); - - userScaleGroup.model = options.obj; + options.obj.name = "model"; + //userScaleGroup.model = options.obj; Objects.prototype._addMethods(userScaleGroup); //[jscastro] calculate automatically the pivotal center of the object @@ -1990,12 +1959,12 @@ function Label(obj) { let div = Objects.prototype.drawLabelHTML(obj.htmlElement, obj.cssClass); let label = new THREE.CSS2DObject(div); + label.name = "label"; label.visible = obj.alwaysVisible; label.alwaysVisible = obj.alwaysVisible; var userScaleGroup = Objects.prototype._makeGroup(label, obj); Objects.prototype._addMethods(userScaleGroup); - userScaleGroup.label = label; userScaleGroup.visibility = obj.alwaysVisible; return userScaleGroup; @@ -3060,7 +3029,7 @@ function loadObj(options, cb, promise) { loader.load(options.obj, obj => { //[jscastro] MTL/GLTF/FBX models have a different structure - let animations; + let animations = []; switch (options.type) { case "mtl": obj = obj.children[0]; @@ -3074,7 +3043,7 @@ function loadObj(options, cb, promise) { animations = obj.animations; break; } - + obj.animations = animations; // [jscastro] options.rotation was wrongly used var r = utils.types.rotation(options.rotation, [0, 0, 0]); var s = utils.types.scale(options.scale, [1, 1, 1]); @@ -3082,11 +3051,12 @@ function loadObj(options, cb, promise) { obj.scale.set(s[0], s[1], s[2]); // [jscastro] normalize specular/metalness/shininess from meshes in FBX and GLB model as it would need 5 lights to illuminate them properly if (options.normalize) { normalizeSpecular(obj); } - + obj.name = "model"; var projScaleGroup = new THREE.Group(); + projScaleGroup.name = "group"; projScaleGroup.add(obj) var userScaleGroup = Objects.prototype._makeGroup(projScaleGroup, options); - userScaleGroup.model = obj; + userScaleGroup.name = "object"; //[jscastro] assign the animations to the userScaleGroup before enrolling it in AnimationsManager through _addMethods userScaleGroup.animations = animations; @@ -3096,6 +3066,8 @@ function loadObj(options, cb, promise) { //[jscastro] override the center calculated if the object has adjustments userScaleGroup.setCenter(options.adjustment); + let anim = userScaleGroup.animations; + // [jscastro] after adding methods create the bounding box at userScaleGroup but add it to its children for positioning let boxGrid = userScaleGroup.drawBoundingBox(); projScaleGroup.add(boxGrid); @@ -16199,6 +16171,21 @@ Objects.prototype = { }, + unenroll: function (obj, isStatic) { + var root = this; + + if (isStatic) { + + } + + else { + // Bestow this mesh with animation superpowers and keeps track of its movements in the global animation queue + root.animationManager.unenroll(obj); + + } + + }, + _addMethods: function (obj, isStatic) { var root = this; @@ -16309,24 +16296,22 @@ Objects.prototype = { model.position.add(point); // re-add the offset model.rotateOnAxis(axis, theta) - map.repaint = true; + tb.map.repaint = true; } let _boundingBox; //[jscastro] added property for boundingBox helper Object.defineProperty(obj, 'boundingBox', { - get() { return _boundingBox; }, - set(value) { - _boundingBox = value; + get() { + return obj.getObjectByName("BoxModel"); } }) let _boundingBoxShadow; //[jscastro] added property for boundingBox helper Object.defineProperty(obj, 'boundingBoxShadow', { - get() { return _boundingBoxShadow; }, - set(value) { - _boundingBoxShadow = value; + get() { + return obj.getObjectByName("BoxShadow"); } }) @@ -16342,7 +16327,7 @@ Objects.prototype = { boxModel.name = "BoxModel"; boxGrid.add(boxModel); boxModel.layers.disable(0); // it makes the object invisible for the raycaster - obj.boundingBox = boxModel; + //obj.boundingBox = boxModel; //it needs to clone, to avoid changing the object by reference let bb2 = bb.clone(); @@ -16353,7 +16338,7 @@ Objects.prototype = { boxGrid.add(boxShadow); boxShadow.layers.disable(0); // it makes the object invisible for the raycaster - obj.boundingBoxShadow = boxShadow; + //obj.boundingBoxShadow = boxShadow; boxGrid.visible = false; // visibility is managed from the parent return boxGrid; @@ -16432,21 +16417,27 @@ Objects.prototype = { } let _label; - //[jscastro] added property for wireframes state + //[jscastro] added property for simulated label Object.defineProperty(obj, 'label', { - get() { return _label; }, - set(value) { - _label = value; - } + get() { return obj.getObjectByName("label"); } }); let _tooltip; //[jscastro] added property for simulated tooltip Object.defineProperty(obj, 'tooltip', { - get() { return _tooltip; }, - set(value) { - _tooltip = value; - } + get() { return obj.getObjectByName("tooltip"); } + }); + + //[jscastro] added property for the internal 3D model + Object.defineProperty(obj, 'model', { + get() { return obj.getObjectByName("model"); } + }); + + let _animations; + //[jscastro] added property for the internal 3D model + Object.defineProperty(obj, 'animations', { + get() { return _animations}, + set(value) { _animations = value} }); //[jscastro] added property to redefine visible, including the label and tooltip @@ -16500,13 +16491,14 @@ Objects.prototype = { const box = obj.box3(); const size = box.getSize(new THREE.Vector3()); let bottomLeft = { x: box.max.x, y: box.max.y, z: box.min.z }; - if (obj.label) { obj.label.remove; obj.label = null; } - obj.label = new CSS2D.CSS2DObject(div); - obj.label.position.set(((-size.x * 0.5) - obj.model.position.x - center.x + bottomLeft.x), ((-size.y * 0.5) - obj.model.position.y - center.y + bottomLeft.y), size.z * 0.5); //middle-centered - obj.label.visible = visible; - obj.label.alwaysVisible = visible; + if (obj.label) { obj.label.remove; } + let label = new CSS2D.CSS2DObject(div); + label.name = "label"; + label.position.set(((-size.x * 0.5) - obj.model.position.x - center.x + bottomLeft.x), ((-size.y * 0.5) - obj.model.position.y - center.y + bottomLeft.y), size.z * 0.5); //middle-centered + label.visible = visible; + label.alwaysVisible = visible; - return obj.label; + return label; } //[jscastro] add tooltip method @@ -16516,12 +16508,13 @@ Objects.prototype = { const box = obj.box3(); const size = box.getSize(new THREE.Vector3()); let bottomLeft = { x: box.max.x, y: box.max.y, z: box.min.z }; - if (obj.tooltip) { obj.tooltip.remove; obj.tooltip = null; } - obj.tooltip = new CSS2D.CSS2DObject(divToolTip); - obj.tooltip.position.set(((-size.x * 0.5) - obj.model.position.x - center.x + bottomLeft.x), ((-size.y * 0.5) - obj.model.position.y - center.y + bottomLeft.y), size.z); //top-centered - obj.tooltip.visible = false; //only visible on mouseover or selected + if (obj.tooltip) { obj.tooltip.remove; } + let tooltip = new CSS2D.CSS2DObject(divToolTip); + tooltip.name = "tooltip"; + tooltip.position.set(((-size.x * 0.5) - obj.model.position.x - center.x + bottomLeft.x), ((-size.y * 0.5) - obj.model.position.y - center.y + bottomLeft.y), size.z); //top-centered + tooltip.visible = false; //only visible on mouseover or selected //we add it to the first children to get same boxing and position - obj.children[0].add(obj.tooltip); + obj.children[0].add(tooltip); } } @@ -16687,9 +16680,9 @@ Objects.prototype = { if (obj.model) { //let's clone the object before manipulate it let dup = obj.clone(true); - dup.model = obj.model.clone(); + let model = obj.model.clone(); //get the size of the model because the object is translated and has boundingBoxShadow - bounds = new THREE.Box3().setFromObject(dup.model); + bounds = new THREE.Box3().setFromObject(model); //if the object has parent it's already in the added to world so it's scaled and it could be rotated if (obj.parent) { //first, we return the object to it's original position of rotation, extract rotation and apply inversed @@ -16699,7 +16692,7 @@ Objects.prototype = { rm.getInverse(rmi); dup.setRotationFromMatrix(rmi); //now the object inside will give us a NAABB Non-Axes Aligned Bounding Box - bounds = new THREE.Box3().setFromObject(dup.model); + bounds = new THREE.Box3().setFromObject(model); } } return bounds; @@ -16747,11 +16740,9 @@ Objects.prototype = { } //[jscastro] clone + assigning all the attributes - obj.duplicate = function () { - var dupe = obj.clone(true); - dupe.userData = obj.userData; - dupe.model = dupe.children[0].children[0]; - dupe.animations = dupe.model.animations; + obj.duplicate = function (options) { + let dupe = obj.clone(true); + dupe.userData = options || obj.userData; root._addMethods(dupe); dupe.deepCopy(obj); @@ -16761,40 +16752,44 @@ Objects.prototype = { obj.deepCopy = function (o) { obj.anchor = o.anchor; + obj.none = { x: 0, y: 0, z: 0 }; + obj.center = o.center; obj.bottom = o.bottom; obj.bottomLeft = o.bottomLeft; obj.bottomRight = o.bottomRight; - obj.center = o.center; - obj.left = o.left; - obj.right = o.right; obj.top = o.top; obj.topLeft = o.topLeft; obj.topRight = o.topRight; - obj.boundingBox = obj.children[0].children[1].children[0]; - obj.boundingBoxShadow = obj.children[0].children[1].children[1]; - obj.tooltip = obj.children[0].children[2]; + obj.left = o.left; + obj.right = o.right; return obj; } obj.dispose = function () { - obj.traverse(object => { - if (!object.isMesh) return - //console.log('dispose geometry!') - object.geometry.dispose() + Objects.prototype.unenroll(obj); - if (object.material.isMaterial) { - cleanMaterial(object.material) - } else { - // an array of materials - for (const material of object.material) cleanMaterial(material) + obj.traverse(o => { + //don't dispose th object itself as it will be recursive + if (o.parent && o.parent.name == "world") return; + if (o.isMesh) { + //console.log('dispose geometry!') + o.geometry.dispose(); + + if (o.material.isMaterial) { + cleanMaterial(o.material) + } else { + // an array of materials + for (const material of o.material) cleanMaterial(material) + } } + if (o.dispose) o.dispose(); + }) - if (obj.label) { obj.label.dispose() }; - if (obj.tooltip) { obj.tooltip.dispose() }; - if (obj.model) { obj.model = {} }; + obj.children = []; + } const cleanMaterial = material => { @@ -17017,10 +17012,9 @@ function Tooltip(obj) { let tooltip = new CSS2D.CSS2DObject(divToolTip); tooltip.visible = false; + tooltip.name = "tooltip"; var userScaleGroup = Objects.prototype._makeGroup(tooltip, obj); Objects.prototype._addMethods(userScaleGroup); - userScaleGroup.tooltip = tooltip; - return userScaleGroup; } diff --git a/dist/threebox.min.js b/dist/threebox.min.js index 42f4741d..8dd33b60 100644 --- a/dist/threebox.min.js +++ b/dist/threebox.min.js @@ -2,22 +2,22 @@ window.Threebox=require("./src/Threebox.js"),window.THREE=require("./src/three.js"); },{"./src/Threebox.js":2,"./src/three.js":22}],2:[function(require,module,exports){ -var THREE=require("./three.js"),CameraSync=require("./camera/CameraSync.js"),utils=require("./utils/utils.js"),SunCalc=require("./utils/suncalc.js"),AnimationManager=require("./animation/AnimationManager.js"),ThreeboxConstants=require("./utils/constants.js"),Objects=require("./objects/objects.js"),material=require("./utils/material.js"),sphere=require("./objects/sphere.js"),label=require("./objects/label.js"),tooltip=require("./objects/tooltip.js"),loader=require("./objects/loadObj.js"),Object3D=require("./objects/Object3D.js"),line=require("./objects/line.js"),tube=require("./objects/tube.js"),LabelRenderer=require("./objects/LabelRenderer.js"),BuildingShadows=require("./objects/effects/BuildingShadows.js");function Threebox(e,t,i){this.init(e,t,i)}Threebox.prototype={repaint:function(){this.map.repaint=!0},init:function(e,t,i){this.options=utils._validate(i||{},defaultOptions),this.map=e,this.map.tb=this,this.objects=new Objects(this.map),this.renderer=new THREE.WebGLRenderer({alpha:!0,antialias:!0,canvas:e.getCanvas(),context:t}),this.renderer.setPixelRatio(window.devicePixelRatio),this.renderer.setSize(this.map.getCanvas().clientWidth,this.map.getCanvas().clientHeight),this.renderer.outputEncoding=THREE.sRGBEncoding,this.renderer.autoClear=!1,this.labelRenderer=new LabelRenderer(this.map),this.scene=new THREE.Scene,this.camera=new THREE.PerspectiveCamera(ThreeboxConstants.FOV_DEGREES,this.map.getCanvas().clientWidth/this.map.getCanvas().clientHeight,1,1e21),this.camera.layers.enable(0),this.camera.layers.enable(1),this.world=new THREE.Group,this.world.name="world",this.scene.add(this.world),this.objectsCache=new Map,this.primises=new Map,this.cameraSync=new CameraSync(this.map,this.camera,this.world),this.raycaster=new THREE.Raycaster,this.raycaster.layers.set(0),this.mapCenter=this.map.getCenter(),this.mapCenterUnits=utils.projectToWorld([this.mapCenter.lng,this.mapCenter.lat]),this.lightDateTime=new Date,this.lightLng=this.mapCenter.lng,this.lightLat=this.mapCenter.lat,this.sunPosition,this.lights=this.initLights,this.options.defaultLights&&this.defaultLights(),this.options.realSunlight&&this.realSunlight(),this.options.enableSelectingFeatures&&(this.enableSelectingFeatures=this.options.enableSelectingFeatures),this.options.enableSelectingObjects&&(this.enableSelectingObjects=this.options.enableSelectingObjects),this.options.enableDraggingObjects&&(this.enableDraggingObjects=this.options.enableDraggingObjects),this.options.enableRotatingObjects&&(this.enableRotatingObjects=this.options.enableRotatingObjects),this.options.enableTooltips&&(this.enableTooltips=this.options.enableTooltips),this.map.on("load",function(){let t,i,s,n,a,r,o,l=this.getCanvasContainer();this.getCanvasContainer().style.cursor="default";let h,u,d=[];function c(e){var t=l.getBoundingClientRect();return new mapboxgl.Point(e.originalEvent.clientX-t.left-l.clientLeft,e.originalEvent.clientY-t.top-l.clientTop)}function p(e,t){void 0!==e.id&&(t.setFeatureState({source:e.source,sourceLayer:e.sourceLayer,id:e.id},{select:!1}),y(e,t),(e=t.queryRenderedFeatures({layers:[e.layer.id],filter:["==",["id"],e.id]})[0])&&t.fire("SelectedFeatureChange",{detail:e}),r=null)}function g(e,t){r=e,t.setFeatureState({source:r.source,sourceLayer:r.sourceLayer,id:r.id},{select:!0}),f(r=t.queryRenderedFeatures({layers:[r.layer.id],filter:["==",["id"],r.id]})[0],t),t.fire("SelectedFeatureChange",{detail:r})}function m(e,t){a&&void 0!==a&&a.id!=e&&(t.setFeatureState({source:a.source,sourceLayer:a.sourceLayer,id:a.id},{hover:!1}),y(a,t),a=null)}function b(e){e.selected=!1,t=null}function f(e,t){if(!t.tb.enableTooltips)return;let i=t.tb.getFeatureCenter(e),s=t.tb.tooltip({text:e.properties.name||e.id||e.type,mapboxStyle:!0,feature:e});s.setCoords(i),t.tb.add(s),e.tooltip=s,e.tooltip.tooltip.visible=!0}function y(e,t){e.tooltip&&(e.tooltip.visibility=!1,t.tb.remove(e.tooltip),e.tooltip=null)}e.onContextMenu=function(e){alert("contextMenu")},e.onClick=function(i){let s,n=[];if(e.tb.enableSelectingObjects&&(n=this.tb.queryRenderedFeatures(i.point)),s="object"==typeof n[0]){let e=Threebox.prototype.findParent3DObject(n[0]);if(e){if(r&&p(r,this),t){if(t.uuid!=e.uuid)t.selected=!1,e.selected=!0,t=e;else if(t.uuid==e.uuid)return void b(t)}else(t=e).selected=!0;t.dispatchEvent(new CustomEvent("Wireframed",{detail:t,bubbles:!0,cancelable:!0})),t.dispatchEvent(new CustomEvent("IsPlayingChanged",{detail:t,bubbles:!0,cancelable:!0})),this.repaint=!0,i.preventDefault()}}else{let s=[];if(e.tb.enableSelectingFeatures&&(s=this.queryRenderedFeatures(i.point)),s.length>0&&"fill-extrusion"==s[0].layer.type&&void 0!==s[0].id)if(t&&b(t),r){if(r.id!=s[0].id)p(r,this),g(s[0],this);else if(r.id==s[0].id)return void p(r,this)}else g(s[0],this)}},e.onMouseMove=function(l){let d,p=c(l);if(this.getCanvasContainer().style.cursor="default",l.originalEvent.altKey&&i){if(!e.tb.enableRotatingObjects)return;s="rotate",this.getCanvasContainer().style.cursor="move";Math.min(o.x,p.x),Math.max(o.x,p.x),Math.min(o.y,p.y),Math.max(o.y,p.y);let t={x:0,y:0,z:360+~~((p.x-o.x)/10)%360*10%360};return void i.setRotation(t)}if(l.originalEvent.shiftKey&&i){if(!e.tb.enableDraggingObjects)return;s="translate",this.getCanvasContainer().style.cursor="move";let t=l.lngLat,n=[Number((t.lng+h).toFixed(6)),Number((t.lat+u).toFixed(6)),i.modelHeight];return void i.setCoords(n)}let g=[];if(e.tb.enableSelectingObjects&&(g=this.tb.queryRenderedFeatures(l.point)),d="object"==typeof g[0]){let e=Threebox.prototype.findParent3DObject(g[0]);e&&(m(a,this),this.getCanvasContainer().style.cursor="pointer",t&&e.uuid==t.uuid||(n&&(n.over=!1,n=null),e.over=!0,n=e),this.repaint=!0,l.preventDefault())}else{n&&(n.over=!1,n=null);let t=[];e.tb.enableSelectingFeatures&&(t=this.queryRenderedFeatures(l.point)),t.length>0&&(m(t[0],this),"fill-extrusion"==t[0].layer.type&&void 0!==t[0].id&&(r&&r.id==t[0].id||(this.getCanvasContainer().style.cursor="pointer",a=t[0],this.setFeatureState({source:a.source,sourceLayer:a.sourceLayer,id:a.id},{hover:!0}),f(a=e.queryRenderedFeatures({layers:[a.layer.id],filter:["==",["id"],a.id]})[0],this))))}},e.onMouseDown=function(s){(s.originalEvent.shiftKey||s.originalEvent.altKey)&&0===s.originalEvent.button&&t&&(s.preventDefault(),e.getCanvasContainer().style.cursor="move",e.once("mouseup",e.onMouseUp),e.once("mouseout",e.onMouseUp),i=t,o=c(s),d=i.coordinates,h=d[0]-s.lngLat.lng,u=d[1]-s.lngLat.lat)},e.onMouseUp=function(t){this.getCanvasContainer().style.cursor="default",this.off("mouseup",e.onMouseUp),this.off("mouseout",e.onMouseUp),this.dragPan.enable(),i&&(i.dispatchEvent(new CustomEvent("ObjectDragged",{detail:{draggedObject:i,draggedAction:s},bubbles:!0,cancelable:!0})),i=null,s=null)},e.onMouseOut=function(e){if(a){let t=this.queryRenderedFeatures(e.point);t.length>0&&a.id!=t[0].id&&(this.getCanvasContainer().style.cursor="default",m(t[0],this))}},this.on("click",e.onClick),this.on("mousemove",e.onMouseMove),this.on("mouseout",e.onMouseOut),this.on("mousedown",e.onMouseDown)})},sphere:sphere,line:line,label:label,tooltip:tooltip,tube:function(e){return tube(e,this.world)},Object3D:function(e,t){return Object3D(e,t)},loadObj:async function(e,t){let i=this.objectsCache.get(e.obj);i?i.promise.then(e=>{t(e.duplicate())}).catch(t=>{this.objectsCache.delete(e.obj),console.error("Could not load model file: "+e.obj)}):this.objectsCache.set(e.obj,{promise:new Promise(function(i,s){loader(e,t,function(e){e.duplicate?i(e):s(e)})})})},material:function(e){return material(e)},initLights:{ambientLight:null,dirLight:null,dirLightBack:null,dirLightHelper:null,hemiLight:null,pointLight:null},utils:utils,SunCalc:SunCalc,Constants:ThreeboxConstants,projectToWorld:function(e){return this.utils.projectToWorld(e)},unprojectFromWorld:function(e){return this.utils.unprojectFromWorld(e)},projectedUnitsPerMeter:function(e){return this.utils.projectedUnitsPerMeter(e)},getFeatureCenter:function(e,t,i){return utils.getFeatureCenter(e,t,i)},getObjectHeightOnFloor:function(e,t,i){return utils.getObjectHeightOnFloor(e,t,i)},queryRenderedFeatures:function(e){let t=new THREE.Vector2;return t.x=e.x/this.map.transform.width*2-1,t.y=1-e.y/this.map.transform.height*2,this.raycaster.setFromCamera(t,this.camera),this.raycaster.intersectObjects(this.world.children,!0)},findParent3DObject:function(e){var t;return e.object.traverseAncestors(function(e){e.parent&&"Group"==e.parent.type&&e.userData.obj&&(t=e)}),t},setLabelZoomRange:function(e,t){this.labelRenderer.setZoomRange(e,t)},setLayoutProperty:function(e,t,i){this.map.setLayoutProperty(e,t,i),null==i||"visibility"!==t||this.world.children.forEach(function(t){t.userData.feature&&t.userData.feature.layer===e&&(t.visibility=i)})},setLayerZoomRange:function(e,t,i){this.map.getLayer(e)&&(this.map.setLayerZoomRange(e,t,i),this.setLabelZoomRange(t,i))},setLayerHeigthProperty:function(e,t){let i=this.map.getLayer(e);if(i)if("fill-extrusion"==i.type){let e=this.map.getStyle().sources[i.source].data;e.features.forEach(function(e){e.properties.level=t}),this.map.getSource(i.source).setData(e)}else"custom"==i.type&&this.world.children.forEach(function(i){let s=i.userData.feature;if(s&&s.layer===e){let e=this.tb.getFeatureCenter(s,i,t);i.setCoords(e)}})},setStyle:function(e,t){this.map.setStyle(e,t),this.clear(null,!0)},toggleLayer:function(e,t){this.map.getLayer(e)&&this.setLayoutProperty(e,"visibility",t?"visible":"none")},update:function(){this.map.repaint&&(this.map.repaint=!1);var e=Date.now();this.objects.animationManager.update(e),this.updateLightHelper(),this.renderer.state.reset(),this.renderer.render(this.scene,this.camera),this.labelRenderer.render(this.scene,this.camera),!1===this.options.passiveRendering&&this.map.triggerRepaint()},add:function(e){!this.enableTooltips&&e.tooltip&&(e.tooltip.visibility=!1),this.world.add(e)},remove:function(e){e.label&&e.label.remove(),e.tooltip&&e.tooltip.remove(),e.traverse(function(e){if(e.isMesh&&(e.geometry.dispose(),e.material)){e.material instanceof THREE.MeshFaceMaterial?e.material.materials.forEach(function(e){e.dispose(),e.map&&e.map.dispose()}):e.material.dispose();let t=e.material;(t.map||t.alphaMap||t.aoMap||t.bumpMap||t.displacementMap||t.emissiveMap||t.envMap||t.lightMap||t.metalnessMap||t.normalMap||t.roughnessMap)&&(t.map&&t.map.dispose(),t.alphaMap&&t.alphaMap.dispose(),t.aoMap&&t.aoMap.dispose(),t.bumpMap&&t.bumpMap.dispose(),t.displacementMap&&t.displacementMap.dispose(),t.emissiveMap&&t.emissiveMap.dispose(),t.envMap&&t.envMap.dispose(),t.lightMap&&t.lightMap.dispose(),t.metalnessMap&&t.metalnessMap.dispose(),t.normalMap&&t.normalMap.dispose(),t.roughnessMap&&t.roughnessMap.dispose())}e.dispose&&e.dispose()}),this.world.remove(e)},clear:async function(e=null,t=!1){return new Promise((i,s)=>{let n=[];this.world.children.forEach(function(e){n.push(e)});for(let i=0;i{this.map.removeLayer(e)})},getSunPosition:function(e,t,i){return SunCalc.getPosition(e,i,t)},setBuildingShadows:function(e){if(this.map.getLayer(e.buildingsLayerId)){let t=new BuildingShadows(e,this);this.map.addLayer(t,e.buildingsLayerId)}else console.warn("The layer '"+e.buildingsLayerId+"' does not exist in the map.")},setSunlight:function(e=new Date,t){if(!this.lights.dirLight||!this.options.realSunlight)return void console.warn("To use setSunlight it's required to set realSunlight : true in Threebox initial options.");var i=new Date(e.getTime());if(this.mapCenter=t?{lng:t[0],lat:t[1]}:this.map.getCenter(),this.lightDateTime&&this.lightDateTime.getTime()===i.getTime()&&this.lightLng===this.mapCenter.lng&&this.lightLat===this.mapCenter.lat)return;this.lightDateTime=i,this.lightLng=this.mapCenter.lng,this.lightLat=this.mapCenter.lat,this.sunPosition=this.getSunPosition(i,this.mapCenter.lng,this.mapCenter.lat);let s=this.sunPosition.altitude,n=Math.PI+this.sunPosition.azimuth,a=ThreeboxConstants.WORLD_SIZE/2,r=Math.sin(s),o=Math.cos(s),l=Math.cos(n)*o,h=Math.sin(n)*o;this.lights.dirLight.position.set(h,l,r),this.lights.dirLight.position.multiplyScalar(a),this.lights.dirLight.intensity=Math.max(r,-.15),this.lights.dirLight.updateMatrixWorld(),this.updateLightHelper(),this.map.loaded()&&this.map.setLight({anchor:"map",position:[1.5,180+180*this.sunPosition.azimuth/Math.PI,90-180*this.sunPosition.altitude/Math.PI],"position-transition":{duration:0},color:`hsl(40, ${50*Math.cos(this.sunPosition.altitude)}%, ${96*Math.sin(this.sunPosition.altitude)}%)`},{duration:0})},updateLightHelper:function(){this.lights.dirLightHelper&&(this.lights.dirLightHelper.position.setFromMatrixPosition(this.lights.dirLight.matrixWorld),this.lights.dirLightHelper.updateMatrix(),this.lights.dirLightHelper.update())},dispose:async function(){return console.log(window.tb.memory()),new Promise(e=>{this.clear(null,!0),this.map.remove(),this.map={},this.scene.remove(this.world),this.scene.dispose(),this.world.children=[],this.world=null,this.labelRenderer.dispose(),console.log(window.tb.memory()),this.renderer.dispose(),e("dispose finished")})},defaultLights:function(){this.lights.ambientLight=new THREE.AmbientLight(new THREE.Color("hsl(0, 0%, 100%)"),.75),this.scene.add(this.lights.ambientLight),this.lights.dirLightBack=new THREE.DirectionalLight(new THREE.Color("hsl(0, 0%, 100%)"),.25),this.lights.dirLightBack.position.set(30,100,100),this.scene.add(this.lights.dirLightBack),this.lights.dirLight=new THREE.DirectionalLight(new THREE.Color("hsl(0, 0%, 100%)"),.25),this.lights.dirLight.position.set(-30,100,-100),this.scene.add(this.lights.dirLight)},realSunlight:function(){this.renderer.shadowMap.enabled=!0,this.lights.dirLight=new THREE.DirectionalLight(16777215,1),this.scene.add(this.lights.dirLight),this.lights.dirLightHelper=new THREE.DirectionalLightHelper(this.lights.dirLight,5),this.scene.add(this.lights.dirLightHelper);this.lights.dirLight.castShadow=!0,this.lights.dirLight.shadow.radius=2,this.lights.dirLight.shadow.mapSize.width=8192,this.lights.dirLight.shadow.mapSize.height=8192,this.lights.dirLight.shadow.camera.top=this.lights.dirLight.shadow.camera.right=1e3,this.lights.dirLight.shadow.camera.bottom=this.lights.dirLight.shadow.camera.left=-1e3,this.lights.dirLight.shadow.camera.near=1,this.lights.dirLight.shadow.camera.visible=!0,this.lights.dirLight.shadow.camera.far=4e8,this.lights.hemiLight=new THREE.HemisphereLight(new THREE.Color(16777215),new THREE.Color(16777215),.6),this.lights.hemiLight.color.setHSL(.661,.96,.12),this.lights.hemiLight.groundColor.setHSL(.11,.96,.14),this.lights.hemiLight.position.set(0,0,50),this.scene.add(this.lights.hemiLight),this.setSunlight()},memory:function(){return this.renderer.info.memory},programs:function(){return this.renderer.info.programs.length},version:"2.0.6"};var defaultOptions={defaultLights:!1,realSunlight:!1,passiveRendering:!0,enableSelectingFeatures:!1,enableSelectingObjects:!1,enableDraggingObjects:!1,enableRotatingObjects:!1,enableTooltips:!1};module.exports=exports=Threebox; +var THREE=require("./three.js"),CameraSync=require("./camera/CameraSync.js"),utils=require("./utils/utils.js"),SunCalc=require("./utils/suncalc.js"),AnimationManager=require("./animation/AnimationManager.js"),ThreeboxConstants=require("./utils/constants.js"),Objects=require("./objects/objects.js"),material=require("./utils/material.js"),sphere=require("./objects/sphere.js"),label=require("./objects/label.js"),tooltip=require("./objects/tooltip.js"),loader=require("./objects/loadObj.js"),Object3D=require("./objects/Object3D.js"),line=require("./objects/line.js"),tube=require("./objects/tube.js"),LabelRenderer=require("./objects/LabelRenderer.js"),BuildingShadows=require("./objects/effects/BuildingShadows.js");function Threebox(e,t,i){this.init(e,t,i)}Threebox.prototype={repaint:function(){this.map.repaint=!0},init:function(e,t,i){this.options=utils._validate(i||{},defaultOptions),this.map=e,this.map.tb=this,this.objects=new Objects,this.renderer=new THREE.WebGLRenderer({alpha:!0,antialias:!0,canvas:e.getCanvas(),context:t}),this.renderer.setPixelRatio(window.devicePixelRatio),this.renderer.setSize(this.map.getCanvas().clientWidth,this.map.getCanvas().clientHeight),this.renderer.outputEncoding=THREE.sRGBEncoding,this.renderer.autoClear=!1,this.labelRenderer=new LabelRenderer(this.map),this.scene=new THREE.Scene,this.camera=new THREE.PerspectiveCamera(ThreeboxConstants.FOV_DEGREES,this.map.getCanvas().clientWidth/this.map.getCanvas().clientHeight,1,1e21),this.camera.layers.enable(0),this.camera.layers.enable(1),this.world=new THREE.Group,this.world.name="world",this.scene.add(this.world),this.objectsCache=new Map,this.cameraSync=new CameraSync(this.map,this.camera,this.world),this.raycaster=new THREE.Raycaster,this.raycaster.layers.set(0),this.mapCenter=this.map.getCenter(),this.mapCenterUnits=utils.projectToWorld([this.mapCenter.lng,this.mapCenter.lat]),this.lightDateTime=new Date,this.lightLng=this.mapCenter.lng,this.lightLat=this.mapCenter.lat,this.sunPosition,this.lights=this.initLights,this.options.defaultLights&&this.defaultLights(),this.options.realSunlight&&this.realSunlight(),this.options.enableSelectingFeatures&&(this.enableSelectingFeatures=this.options.enableSelectingFeatures),this.options.enableSelectingObjects&&(this.enableSelectingObjects=this.options.enableSelectingObjects),this.options.enableDraggingObjects&&(this.enableDraggingObjects=this.options.enableDraggingObjects),this.options.enableRotatingObjects&&(this.enableRotatingObjects=this.options.enableRotatingObjects),this.options.enableTooltips&&(this.enableTooltips=this.options.enableTooltips),this.map.on("load",function(){let t,i,s,n,r,o,a,l=this.getCanvasContainer();this.getCanvasContainer().style.cursor="default";let h,u,d=[];function c(e){var t=l.getBoundingClientRect();return new mapboxgl.Point(e.originalEvent.clientX-t.left-l.clientLeft,e.originalEvent.clientY-t.top-l.clientTop)}function g(e,t){void 0!==e.id&&(t.setFeatureState({source:e.source,sourceLayer:e.sourceLayer,id:e.id},{select:!1}),y(e,t),(e=t.queryRenderedFeatures({layers:[e.layer.id],filter:["==",["id"],e.id]})[0])&&t.fire("SelectedFeatureChange",{detail:e}),o=null)}function p(e,t){o=e,t.setFeatureState({source:o.source,sourceLayer:o.sourceLayer,id:o.id},{select:!0}),f(o=t.queryRenderedFeatures({layers:[o.layer.id],filter:["==",["id"],o.id]})[0],t),t.fire("SelectedFeatureChange",{detail:o})}function b(e,t){r&&void 0!==r&&r.id!=e&&(t.setFeatureState({source:r.source,sourceLayer:r.sourceLayer,id:r.id},{hover:!1}),y(r,t),r=null)}function m(e){e.selected=!1,t=null}function f(e,t){if(!t.tb.enableTooltips)return;let i=t.tb.getFeatureCenter(e),s=t.tb.tooltip({text:e.properties.name||e.id||e.type,mapboxStyle:!0,feature:e});s.setCoords(i),t.tb.add(s),e.tooltip=s,e.tooltip.tooltip.visible=!0}function y(e,t){e.tooltip&&(e.tooltip.visibility=!1,t.tb.remove(e.tooltip),e.tooltip=null)}e.onContextMenu=function(e){alert("contextMenu")},e.onClick=function(i){let s,n=[];if(e.tb.enableSelectingObjects&&(n=this.tb.queryRenderedFeatures(i.point)),s="object"==typeof n[0]){let e=Threebox.prototype.findParent3DObject(n[0]);if(e){if(o&&g(o,this),t){if(t.uuid!=e.uuid)t.selected=!1,e.selected=!0,t=e;else if(t.uuid==e.uuid)return void m(t)}else(t=e).selected=!0;t.dispatchEvent(new CustomEvent("Wireframed",{detail:t,bubbles:!0,cancelable:!0})),t.dispatchEvent(new CustomEvent("IsPlayingChanged",{detail:t,bubbles:!0,cancelable:!0})),this.repaint=!0,i.preventDefault()}}else{let s=[];if(e.tb.enableSelectingFeatures&&(s=this.queryRenderedFeatures(i.point)),s.length>0&&"fill-extrusion"==s[0].layer.type&&void 0!==s[0].id)if(t&&m(t),o){if(o.id!=s[0].id)g(o,this),p(s[0],this);else if(o.id==s[0].id)return void g(o,this)}else p(s[0],this)}},e.onMouseMove=function(l){let d,g=c(l);if(this.getCanvasContainer().style.cursor="default",l.originalEvent.altKey&&i){if(!e.tb.enableRotatingObjects)return;s="rotate",this.getCanvasContainer().style.cursor="move";Math.min(a.x,g.x),Math.max(a.x,g.x),Math.min(a.y,g.y),Math.max(a.y,g.y);let t={x:0,y:0,z:360+~~((g.x-a.x)/10)%360*10%360};return void i.setRotation(t)}if(l.originalEvent.shiftKey&&i){if(!e.tb.enableDraggingObjects)return;s="translate",this.getCanvasContainer().style.cursor="move";let t=l.lngLat,n=[Number((t.lng+h).toFixed(6)),Number((t.lat+u).toFixed(6)),i.modelHeight];return void i.setCoords(n)}let p=[];if(e.tb.enableSelectingObjects&&(p=this.tb.queryRenderedFeatures(l.point)),d="object"==typeof p[0]){let e=Threebox.prototype.findParent3DObject(p[0]);e&&(b(r,this),this.getCanvasContainer().style.cursor="pointer",t&&e.uuid==t.uuid||(n&&(n.over=!1,n=null),e.over=!0,n=e),this.repaint=!0,l.preventDefault())}else{n&&(n.over=!1,n=null);let t=[];e.tb.enableSelectingFeatures&&(t=this.queryRenderedFeatures(l.point)),t.length>0&&(b(t[0],this),"fill-extrusion"==t[0].layer.type&&void 0!==t[0].id&&(o&&o.id==t[0].id||(this.getCanvasContainer().style.cursor="pointer",r=t[0],this.setFeatureState({source:r.source,sourceLayer:r.sourceLayer,id:r.id},{hover:!0}),f(r=e.queryRenderedFeatures({layers:[r.layer.id],filter:["==",["id"],r.id]})[0],this))))}},e.onMouseDown=function(s){(s.originalEvent.shiftKey||s.originalEvent.altKey)&&0===s.originalEvent.button&&t&&(s.preventDefault(),e.getCanvasContainer().style.cursor="move",e.once("mouseup",e.onMouseUp),e.once("mouseout",e.onMouseUp),i=t,a=c(s),d=i.coordinates,h=d[0]-s.lngLat.lng,u=d[1]-s.lngLat.lat)},e.onMouseUp=function(t){this.getCanvasContainer().style.cursor="default",this.off("mouseup",e.onMouseUp),this.off("mouseout",e.onMouseUp),this.dragPan.enable(),i&&(i.dispatchEvent(new CustomEvent("ObjectDragged",{detail:{draggedObject:i,draggedAction:s},bubbles:!0,cancelable:!0})),i=null,s=null)},e.onMouseOut=function(e){if(r){let t=this.queryRenderedFeatures(e.point);t.length>0&&r.id!=t[0].id&&(this.getCanvasContainer().style.cursor="default",b(t[0],this))}},this.on("click",e.onClick),this.on("mousemove",e.onMouseMove),this.on("mouseout",e.onMouseOut),this.on("mousedown",e.onMouseDown)})},sphere:sphere,line:line,label:label,tooltip:tooltip,tube:function(e){return tube(e,this.world)},Object3D:function(e,t){return Object3D(e,t)},loadObj:async function(e,t){let i=this.objectsCache.get(e.obj);i?i.promise.then(i=>{t(i.duplicate(e))}).catch(t=>{this.objectsCache.delete(e.obj),console.error("Could not load model file: "+e.obj)}):this.objectsCache.set(e.obj,{promise:new Promise(function(i,s){loader(e,t,function(e){e.duplicate?i(e.duplicate()):s(e)})})})},material:function(e){return material(e)},initLights:{ambientLight:null,dirLight:null,dirLightBack:null,dirLightHelper:null,hemiLight:null,pointLight:null},utils:utils,SunCalc:SunCalc,Constants:ThreeboxConstants,projectToWorld:function(e){return this.utils.projectToWorld(e)},unprojectFromWorld:function(e){return this.utils.unprojectFromWorld(e)},projectedUnitsPerMeter:function(e){return this.utils.projectedUnitsPerMeter(e)},getFeatureCenter:function(e,t,i){return utils.getFeatureCenter(e,t,i)},getObjectHeightOnFloor:function(e,t,i){return utils.getObjectHeightOnFloor(e,t,i)},queryRenderedFeatures:function(e){let t=new THREE.Vector2;return t.x=e.x/this.map.transform.width*2-1,t.y=1-e.y/this.map.transform.height*2,this.raycaster.setFromCamera(t,this.camera),this.raycaster.intersectObjects(this.world.children,!0)},findParent3DObject:function(e){var t;return e.object.traverseAncestors(function(e){e.parent&&"Group"==e.parent.type&&e.userData.obj&&(t=e)}),t},setLabelZoomRange:function(e,t){this.labelRenderer.setZoomRange(e,t)},setLayoutProperty:function(e,t,i){this.map.setLayoutProperty(e,t,i),null==i||"visibility"!==t||this.world.children.forEach(function(t){t.userData.feature&&t.userData.feature.layer===e&&(t.visibility=i)})},setLayerZoomRange:function(e,t,i){this.map.getLayer(e)&&(this.map.setLayerZoomRange(e,t,i),this.setLabelZoomRange(t,i))},setLayerHeigthProperty:function(e,t){let i=this.map.getLayer(e);if(i)if("fill-extrusion"==i.type){let e=this.map.getStyle().sources[i.source].data;e.features.forEach(function(e){e.properties.level=t}),this.map.getSource(i.source).setData(e)}else"custom"==i.type&&this.world.children.forEach(function(i){let s=i.userData.feature;if(s&&s.layer===e){let e=this.tb.getFeatureCenter(s,i,t);i.setCoords(e)}})},setStyle:function(e,t){this.map.setStyle(e,t),this.clear(null,!0)},toggleLayer:function(e,t){this.map.getLayer(e)&&this.setLayoutProperty(e,"visibility",t?"visible":"none")},update:function(){this.map.repaint&&(this.map.repaint=!1);var e=Date.now();this.objects.animationManager.update(e),this.updateLightHelper(),this.renderer.state.reset(),this.renderer.render(this.scene,this.camera),this.labelRenderer.render(this.scene,this.camera),!1===this.options.passiveRendering&&this.map.triggerRepaint()},add:function(e){!this.enableTooltips&&e.tooltip&&(e.tooltip.visibility=!1),this.world.add(e)},remove:function(e){e.dispose(),this.world.remove(e),e=null},clear:async function(e=null,t=!1){return new Promise((t,i)=>{let s=[];this.world.children.forEach(function(e){s.push(e)});for(let t=0;t{this.map.removeLayer(e)})},getSunPosition:function(e,t,i){return SunCalc.getPosition(e,i,t)},setBuildingShadows:function(e){if(this.map.getLayer(e.buildingsLayerId)){let t=new BuildingShadows(e,this);this.map.addLayer(t,e.buildingsLayerId)}else console.warn("The layer '"+e.buildingsLayerId+"' does not exist in the map.")},setSunlight:function(e=new Date,t){if(!this.lights.dirLight||!this.options.realSunlight)return void console.warn("To use setSunlight it's required to set realSunlight : true in Threebox initial options.");var i=new Date(e.getTime());if(this.mapCenter=t?{lng:t[0],lat:t[1]}:this.map.getCenter(),this.lightDateTime&&this.lightDateTime.getTime()===i.getTime()&&this.lightLng===this.mapCenter.lng&&this.lightLat===this.mapCenter.lat)return;this.lightDateTime=i,this.lightLng=this.mapCenter.lng,this.lightLat=this.mapCenter.lat,this.sunPosition=this.getSunPosition(i,this.mapCenter.lng,this.mapCenter.lat);let s=this.sunPosition.altitude,n=Math.PI+this.sunPosition.azimuth,r=ThreeboxConstants.WORLD_SIZE/2,o=Math.sin(s),a=Math.cos(s),l=Math.cos(n)*a,h=Math.sin(n)*a;this.lights.dirLight.position.set(h,l,o),this.lights.dirLight.position.multiplyScalar(r),this.lights.dirLight.intensity=Math.max(o,-.15),this.lights.dirLight.updateMatrixWorld(),this.updateLightHelper(),this.map.loaded()&&this.map.setLight({anchor:"map",position:[1.5,180+180*this.sunPosition.azimuth/Math.PI,90-180*this.sunPosition.altitude/Math.PI],"position-transition":{duration:0},color:`hsl(40, ${50*Math.cos(this.sunPosition.altitude)}%, ${96*Math.sin(this.sunPosition.altitude)}%)`},{duration:0})},updateLightHelper:function(){this.lights.dirLightHelper&&(this.lights.dirLightHelper.position.setFromMatrixPosition(this.lights.dirLight.matrixWorld),this.lights.dirLightHelper.updateMatrix(),this.lights.dirLightHelper.update())},dispose:async function(){return console.log(window.tb.memory()),new Promise(e=>{this.clear(null,!0),this.map.remove(),this.map={},this.scene.remove(this.world),this.scene.dispose(),this.world.children=[],this.world=null,this.labelRenderer.dispose(),console.log(window.tb.memory()),this.renderer.dispose(),e("dispose finished")})},defaultLights:function(){this.lights.ambientLight=new THREE.AmbientLight(new THREE.Color("hsl(0, 0%, 100%)"),.75),this.scene.add(this.lights.ambientLight),this.lights.dirLightBack=new THREE.DirectionalLight(new THREE.Color("hsl(0, 0%, 100%)"),.25),this.lights.dirLightBack.position.set(30,100,100),this.scene.add(this.lights.dirLightBack),this.lights.dirLight=new THREE.DirectionalLight(new THREE.Color("hsl(0, 0%, 100%)"),.25),this.lights.dirLight.position.set(-30,100,-100),this.scene.add(this.lights.dirLight)},realSunlight:function(){this.renderer.shadowMap.enabled=!0,this.lights.dirLight=new THREE.DirectionalLight(16777215,1),this.scene.add(this.lights.dirLight),this.lights.dirLightHelper=new THREE.DirectionalLightHelper(this.lights.dirLight,5),this.scene.add(this.lights.dirLightHelper);this.lights.dirLight.castShadow=!0,this.lights.dirLight.shadow.radius=2,this.lights.dirLight.shadow.mapSize.width=8192,this.lights.dirLight.shadow.mapSize.height=8192,this.lights.dirLight.shadow.camera.top=this.lights.dirLight.shadow.camera.right=1e3,this.lights.dirLight.shadow.camera.bottom=this.lights.dirLight.shadow.camera.left=-1e3,this.lights.dirLight.shadow.camera.near=1,this.lights.dirLight.shadow.camera.visible=!0,this.lights.dirLight.shadow.camera.far=4e8,this.lights.hemiLight=new THREE.HemisphereLight(new THREE.Color(16777215),new THREE.Color(16777215),.6),this.lights.hemiLight.color.setHSL(.661,.96,.12),this.lights.hemiLight.groundColor.setHSL(.11,.96,.14),this.lights.hemiLight.position.set(0,0,50),this.scene.add(this.lights.hemiLight),this.setSunlight()},memory:function(){return this.renderer.info.memory},programs:function(){return this.renderer.info.programs.length},version:"2.0.7"};var defaultOptions={defaultLights:!1,realSunlight:!1,passiveRendering:!0,enableSelectingFeatures:!1,enableSelectingObjects:!1,enableDraggingObjects:!1,enableRotatingObjects:!1,enableTooltips:!1};module.exports=exports=Threebox; },{"./animation/AnimationManager.js":3,"./camera/CameraSync.js":4,"./objects/LabelRenderer.js":6,"./objects/Object3D.js":7,"./objects/effects/BuildingShadows.js":9,"./objects/label.js":10,"./objects/line.js":11,"./objects/loadObj.js":12,"./objects/objects.js":18,"./objects/sphere.js":19,"./objects/tooltip.js":20,"./objects/tube.js":21,"./three.js":22,"./utils/constants.js":23,"./utils/material.js":24,"./utils/suncalc.js":25,"./utils/utils.js":26}],3:[function(require,module,exports){ -const THREE=require("../three.js");var threebox=require("../Threebox.js"),utils=require("../utils/utils.js"),validate=require("../utils/validate.js");function AnimationManager(t){this.map=t,this.enrolledObjects=[],this.previousFrameTime}AnimationManager.prototype={enroll:function(t){if(t.clock=new THREE.Clock,t.hasDefaultAnimation=!1,t.defaultAction,t.actions=[],t.mixer,t.animations&&t.animations.length>0){t.hasDefaultAnimation=!0;let a=t.userData.defaultAnimation?t.userData.defaultAnimation:0;t.mixer=new THREE.AnimationMixer(t),e(a)}function e(e){for(let a=0;at.animations.length&&console.log("The animation index "+e+" doesn't exist for this object");let i=t.animations[a],n=t.mixer.clipAction(i);t.actions.push(n),e===a?(t.defaultAction=n,n.setEffectiveWeight(1)):n.setEffectiveWeight(0),n.play()}}let a=!1;Object.defineProperty(t,"isPlaying",{get:()=>a,set(e){a!=e&&(a=e,t.dispatchEvent(new CustomEvent("IsPlayingChanged",{detail:t,bubbles:!0,cancelable:!0})))}}),this.enrolledObjects.push(t),t.animationQueue=[],t.set=function(e){if(e.duration>0){var a={start:Date.now(),expiration:Date.now()+e.duration,endState:{}};utils.extend(e,a);var i=e.coords,n=e.rotation,o=e.scale||e.scaleX||e.scaleY||e.scaleZ;if(n){var r=t.rotation;e.startRotation=[r.x,r.y,r.z],e.endState.rotation=utils.types.rotation(e.rotation,e.startRotation),e.rotationPerMs=e.endState.rotation.map(function(t,a){return(t-e.startRotation[a])/e.duration})}if(o){var s=t.scale;e.startScale=[s.x,s.y,s.z],e.endState.scale=utils.types.scale(e.scale,e.startScale),e.scalePerMs=e.endState.scale.map(function(t,a){return(t-e.startScale[a])/e.duration})}i&&(e.pathCurve=new THREE.CatmullRomCurve3(utils.lnglatsToWorld([t.coordinates,e.coords])));var l={type:"set",parameters:e};this.animationQueue.push(l),map.repaint=!0}else this.stop(),e.rotation=utils.radify(e.rotation),this._setObject(e);return this},t.animationMethod=null,t.stop=function(e){return t.mixer&&(t.isPlaying=!1,cancelAnimationFrame(t.animationMethod)),this.animationQueue=[],this},t.followPath=function(t,e){var a={type:"followPath",parameters:utils._validate(t,defaults.followPath)};return utils.extend(a.parameters,{pathCurve:new THREE.CatmullRomCurve3(utils.lnglatsToWorld(t.path)),start:Date.now(),expiration:Date.now()+a.parameters.duration,cb:e}),this.animationQueue.push(a),map.repaint=!0,this},t._setObject=function(t){var e=t.position,a=t.rotation,i=t.scale,n=t.worldCoordinates,o=t.quaternion,r=t.translate;if(e){this.coordinates=e;var s=utils.projectToWorld(e);this.position.copy(s)}if(r){this.coordinates=[this.coordinates[0]+r[0],this.coordinates[1]+r[1],this.coordinates[2]+r[2]];s=utils.projectToWorld(r);this.translateX(s.x),this.translateY(s.y),this.translateZ(s.z)}a&&this.rotation.set(a[0],a[1],a[2]),i&&this.scale.set(i[0],i[1],i[2]),o&&this.quaternion.setFromAxisAngle(o[0],o[1]),n&&this.position.copy(n),this.updateMatrixWorld(),map.repaint=!0},t.playDefault=function(e){if(t.mixer&&t.hasDefaultAnimation){var a={start:Date.now(),expiration:Date.now()+e.duration,endState:{}};utils.extend(e,a);var i={type:"playDefault",parameters:e};return this.animationQueue.push(i),map.repaint=!0,this}},t.playAnimation=function(a){t.mixer&&(a.animation&&e(a.animation),t.playDefault(a))},t.pauseAllActions=function(){t.mixer&&t.actions.forEach(function(t){t.paused=!0})},t.unPauseAllActions=function(){t.mixer&&t.actions.forEach(function(t){t.paused=!1})},t.deactivateAllActions=function(){t.mixer&&t.actions.forEach(function(t){t.stop()})},t.activateAllActions=function(){t.mixer&&t.actions.forEach(function(t){t.play()})},t.idle=function(){return t.mixer&&t.mixer.update(.01),map.repaint=!0,this}},update:function(t){void 0===this.previousFrameTime&&(this.previousFrameTime=t);if(!this.enrolledObjects)return!1;for(var e=this.enrolledObjects.length-1;e>=0;e--){var a=this.enrolledObjects[e];if(a.animationQueue&&0!==a.animationQueue.length)for(var i=a.animationQueue.length-1;i>=0;i--){var n=a.animationQueue[i];if(n){var o=n.parameters;if(!o.expiration)return a.animationQueue.splice(i,1),void(a.animationQueue[i]&&(a.animationQueue[i].parameters.start=t));if(t>=o.expiration)o.expiration=!1,"playDefault"===n.type?a.stop():(o.endState&&a._setObject(o.endState),void 0!==o.cb&&o.cb());else{var r=(t-o.start)/o.duration;if("set"===n.type){var s={};o.pathCurve&&(s.worldCoordinates=o.pathCurve.getPoint(r)),o.rotationPerMs&&(s.rotation=o.startRotation.map(function(t,e){return t+o.rotationPerMs[e]*r*o.duration})),o.scalePerMs&&(s.scale=o.startScale.map(function(t,e){return t+o.scalePerMs[e]*r*o.duration})),a._setObject(s)}if("followPath"===n.type){if(s={worldCoordinates:o.pathCurve.getPointAt(r)},o.trackHeading){var l=o.pathCurve.getTangentAt(r).normalize(),u=new THREE.Vector3(0,0,0),c=new THREE.Vector3(0,1,0);u.crossVectors(c,l).normalize();var p=Math.acos(c.dot(l));s.quaternion=[u,p]}a._setObject(s)}"playDefault"===n.type&&(a.activateAllActions(),a.isPlaying=!0,a.animationMethod=requestAnimationFrame(this.update),a.mixer.update(a.clock.getDelta()),map.repaint=!0)}}}}this.previousFrameTime=t}};const defaults={followPath:{path:null,duration:1e3,trackHeading:!0}};module.exports=exports=AnimationManager; +const THREE=require("../three.js");var threebox=require("../Threebox.js"),utils=require("../utils/utils.js"),validate=require("../utils/validate.js");function AnimationManager(t){this.map=t,this.enrolledObjects=[],this.previousFrameTime}AnimationManager.prototype={unenroll:function(t){this.enrolledObjects.splice(this.enrolledObjects.indexOf(t),1)},enroll:function(t){if(t.clock=new THREE.Clock,t.hasDefaultAnimation=!1,t.defaultAction,t.actions=[],t.mixer,t.animations&&t.animations.length>0){t.hasDefaultAnimation=!0;let a=t.userData.defaultAnimation?t.userData.defaultAnimation:0;t.mixer=new THREE.AnimationMixer(t),e(a)}function e(e){for(let a=0;at.animations.length&&console.log("The animation index "+e+" doesn't exist for this object");let i=t.animations[a],n=t.mixer.clipAction(i);t.actions.push(n),e===a?(t.defaultAction=n,n.setEffectiveWeight(1)):n.setEffectiveWeight(0),n.play()}}let a=!1;Object.defineProperty(t,"isPlaying",{get:()=>a,set(e){a!=e&&(a=e,t.dispatchEvent(new CustomEvent("IsPlayingChanged",{detail:t,bubbles:!0,cancelable:!0})))}}),this.enrolledObjects.push(t),t.animationQueue=[],t.set=function(e){if(e.duration>0){var a={start:Date.now(),expiration:Date.now()+e.duration,endState:{}};utils.extend(e,a);var i=e.coords,n=e.rotation,o=e.scale||e.scaleX||e.scaleY||e.scaleZ;if(n){var r=t.rotation;e.startRotation=[r.x,r.y,r.z],e.endState.rotation=utils.types.rotation(e.rotation,e.startRotation),e.rotationPerMs=e.endState.rotation.map(function(t,a){return(t-e.startRotation[a])/e.duration})}if(o){var s=t.scale;e.startScale=[s.x,s.y,s.z],e.endState.scale=utils.types.scale(e.scale,e.startScale),e.scalePerMs=e.endState.scale.map(function(t,a){return(t-e.startScale[a])/e.duration})}i&&(e.pathCurve=new THREE.CatmullRomCurve3(utils.lnglatsToWorld([t.coordinates,e.coords])));var l={type:"set",parameters:e};this.animationQueue.push(l),tb.map.repaint=!0}else this.stop(),e.rotation=utils.radify(e.rotation),this._setObject(e);return this},t.animationMethod=null,t.stop=function(e){return t.mixer&&(t.isPlaying=!1,cancelAnimationFrame(t.animationMethod)),this.animationQueue=[],this},t.followPath=function(t,e){var a={type:"followPath",parameters:utils._validate(t,defaults.followPath)};return utils.extend(a.parameters,{pathCurve:new THREE.CatmullRomCurve3(utils.lnglatsToWorld(t.path)),start:Date.now(),expiration:Date.now()+a.parameters.duration,cb:e}),this.animationQueue.push(a),tb.map.repaint=!0,this},t._setObject=function(t){var e=t.position,a=t.rotation,i=t.scale,n=t.worldCoordinates,o=t.quaternion,r=t.translate;if(e){this.coordinates=e;var s=utils.projectToWorld(e);this.position.copy(s)}if(r){this.coordinates=[this.coordinates[0]+r[0],this.coordinates[1]+r[1],this.coordinates[2]+r[2]];s=utils.projectToWorld(r);this.translateX(s.x),this.translateY(s.y),this.translateZ(s.z)}a&&this.rotation.set(a[0],a[1],a[2]),i&&this.scale.set(i[0],i[1],i[2]),o&&this.quaternion.setFromAxisAngle(o[0],o[1]),n&&this.position.copy(n),this.updateMatrixWorld(),tb.map.repaint=!0},t.playDefault=function(e){if(t.mixer&&t.hasDefaultAnimation){var a={start:Date.now(),expiration:Date.now()+e.duration,endState:{}};utils.extend(e,a);var i={type:"playDefault",parameters:e};return this.animationQueue.push(i),tb.map.repaint=!0,this}},t.playAnimation=function(a){t.mixer&&(a.animation&&e(a.animation),t.playDefault(a))},t.pauseAllActions=function(){t.mixer&&t.actions.forEach(function(t){t.paused=!0})},t.unPauseAllActions=function(){t.mixer&&t.actions.forEach(function(t){t.paused=!1})},t.deactivateAllActions=function(){t.mixer&&t.actions.forEach(function(t){t.stop()})},t.activateAllActions=function(){t.mixer&&t.actions.forEach(function(t){t.play()})},t.idle=function(){return t.mixer&&t.mixer.update(.01),tb.map.repaint=!0,this}},update:function(t){void 0===this.previousFrameTime&&(this.previousFrameTime=t);if(!this.enrolledObjects)return!1;for(var e=this.enrolledObjects.length-1;e>=0;e--){var a=this.enrolledObjects[e];if(a.animationQueue&&0!==a.animationQueue.length)for(var i=a.animationQueue.length-1;i>=0;i--){var n=a.animationQueue[i];if(n){var o=n.parameters;if(!o.expiration)return a.animationQueue.splice(i,1),void(a.animationQueue[i]&&(a.animationQueue[i].parameters.start=t));if(t>=o.expiration)o.expiration=!1,"playDefault"===n.type?a.stop():(o.endState&&a._setObject(o.endState),void 0!==o.cb&&o.cb());else{var r=(t-o.start)/o.duration;if("set"===n.type){var s={};o.pathCurve&&(s.worldCoordinates=o.pathCurve.getPoint(r)),o.rotationPerMs&&(s.rotation=o.startRotation.map(function(t,e){return t+o.rotationPerMs[e]*r*o.duration})),o.scalePerMs&&(s.scale=o.startScale.map(function(t,e){return t+o.scalePerMs[e]*r*o.duration})),a._setObject(s)}if("followPath"===n.type){if(s={worldCoordinates:o.pathCurve.getPointAt(r)},o.trackHeading){var l=o.pathCurve.getTangentAt(r).normalize(),u=new THREE.Vector3(0,0,0),c=new THREE.Vector3(0,1,0);u.crossVectors(c,l).normalize();var p=Math.acos(c.dot(l));s.quaternion=[u,p]}a._setObject(s)}"playDefault"===n.type&&(a.activateAllActions(),a.isPlaying=!0,a.animationMethod=requestAnimationFrame(this.update),a.mixer.update(a.clock.getDelta()),tb.map.repaint=!0)}}}}this.previousFrameTime=t}};const defaults={followPath:{path:null,duration:1e3,trackHeading:!0}};module.exports=exports=AnimationManager; },{"../Threebox.js":2,"../three.js":22,"../utils/utils.js":26,"../utils/validate.js":27}],4:[function(require,module,exports){ var THREE=require("../three.js"),utils=require("../utils/utils.js"),ThreeboxConstants=require("../utils/constants.js");function CameraSync(t,a,e){this.map=t,this.camera=a,this.active=!0,this.camera.matrixAutoUpdate=!1,this.world=e||new THREE.Group,this.world.position.x=this.world.position.y=ThreeboxConstants.WORLD_SIZE/2,this.world.matrixAutoUpdate=!1,this.state={fov:ThreeboxConstants.FOV,translateCenter:(new THREE.Matrix4).makeTranslation(ThreeboxConstants.WORLD_SIZE/2,-ThreeboxConstants.WORLD_SIZE/2,0),worldSizeRatio:ThreeboxConstants.TILE_SIZE/ThreeboxConstants.WORLD_SIZE};var i=this;this.map.on("move",function(){i.updateCamera()}).on("resize",function(){i.setupCamera()}),this.setupCamera()}CameraSync.prototype={setupCamera:function(){const t=this.map.transform;this.camera.aspect=t.width/t.height,this.camera.updateProjectionMatrix();const a=this.state.fov/2,e={x:t.width/2,y:t.height/2},i=.5/Math.tan(a)*t.height,s=t._maxPitch*Math.PI/180,r=Math.PI/2-s;this.state.cameraToCenterDistance=i,this.state.offset=e,this.state.cameraTranslateZ=(new THREE.Matrix4).makeTranslation(0,0,i),this.state.maxFurthestDistance=.95*i*(Math.cos(r)*Math.sin(a)/Math.sin(Math.max(.01,Math.min(Math.PI-.01,r-a)))+1),this.updateCamera()},updateCamera:function(t){if(!this.camera)return void console.log("nocamera");const a=this.map.transform,e=Math.PI/2+a._pitch,i=this.state.fov*(.5+this.state.offset.y/a.height),s=Math.sin(i)*this.state.cameraToCenterDistance/Math.sin(utils.clamp(Math.PI-e-i,.01,Math.PI-.01)),r=Math.cos(Math.PI/2-a._pitch)*s+this.state.cameraToCenterDistance,n=Math.min(1.01*r,this.state.maxFurthestDistance),o=a.height/50;this.camera.projectionMatrix=utils.makePerspectiveMatrix(this.state.fov,a.width/a.height,o,n);var h=this.calcCameraMatrix(a._pitch,a.angle);this.camera.matrixWorld.copy(h);var m=a.scale*this.state.worldSizeRatio,c=new THREE.Matrix4,p=new THREE.Matrix4,l=new THREE.Matrix4;c.makeScale(m,m,m);var u=a.x||a.point.x,x=a.y||a.point.y;p.makeTranslation(-u,x,0),l.makeRotationZ(Math.PI),this.world.matrix=(new THREE.Matrix4).premultiply(l).premultiply(this.state.translateCenter).premultiply(c).premultiply(p)},calcCameraMatrix(t,a,e){const i=this.map.transform,s=void 0===t?i._pitch:t,r=void 0===a?i.angle:a,n=void 0===e?this.state.cameraTranslateZ:e;return(new THREE.Matrix4).premultiply(n).premultiply((new THREE.Matrix4).makeRotationX(s)).premultiply((new THREE.Matrix4).makeRotationZ(r))}},module.exports=exports=CameraSync; },{"../three.js":22,"../utils/constants.js":23,"../utils/utils.js":26}],5:[function(require,module,exports){ -const THREE=require("../three.js");THREE.CSS2DObject=function(e){THREE.Object3D.call(this),this.element=e||document.createElement("div"),this.element.style.position="absolute",this.alwaysVisible=!1,this.dispose=function(){this.remove()},this.remove=function(){this.traverse(function(e){e.element instanceof Element&&null!==e.element.parentNode&&e.element.parentNode.removeChild(e.element)})},this.addEventListener("removed",function(){this.remove()})},THREE.CSS2DObject.prototype=Object.assign(Object.create(THREE.Object3D.prototype),{constructor:THREE.CSS2DObject,copy:function(e,t){return THREE.Object3D.prototype.copy.call(this,e,t),this.element=e.element.cloneNode(!0),this}}),THREE.CSS2DRenderer=function(){var e,t,n,r,i=this,o=new THREE.Vector3,s=new THREE.Matrix4,a=new THREE.Matrix4,c={objects:new WeakMap,list:new Map};this.cacheList=c.list;var l=document.createElement("div");l.style.overflow="hidden",this.domElement=l,this.getSize=function(){return{width:e,height:t}},this.setSize=function(i,o){n=(e=i)/2,r=(t=o)/2,l.style.width=i+"px",l.style.height=o+"px"},this.renderObject=function(e,t,s){if(e instanceof THREE.CSS2DObject)if(e.visible){e.onBeforeRender(i,t,s),o.setFromMatrixPosition(e.matrixWorld),o.applyMatrix4(a);var d=e.element,h="translate(-50%,-50%) translate("+(o.x*n+n)+"px,"+(-o.y*r+r)+"px)";d.style.WebkitTransform=h,d.style.MozTransform=h,d.style.oTransform=h,d.style.transform=h,d.style.display=e.visible&&o.z>=-1&&o.z<=1?"":"none";var E={distanceToCameraSquared:u(s,e)};c.objects.set(e,E),c.list.set(e,e),d.parentNode!==l&&l.appendChild(d),e.onAfterRender(i,t,s)}else e.remove();for(var m=0,f=e.children.length;m=-1&&o.z<=1?"":"none";var u={distanceToCameraSquared:m(s,e)};c.objects.set(e,u),c.list.set(e,e),d.parentNode!==l&&l.appendChild(d),e.onAfterRender(i,t,s)}else e.remove();for(var E=0,p=e.children.length;Ethis.maxzoom?this.toggleLabels(!1):this.toggleLabels(!0)},this.toggleLabels=function(e){i!=e&&(this.setVisibility(e,this.scene,this.camera,this.renderer),i=e)},this.setVisibility=function(e,t,i,s){this.renderer.cacheList.forEach(function(n){n.visible!=e&&(e&&n.alwaysVisible||!e)&&(n.visible=e,s.renderObject(n,t,i))})}}module.exports=exports=LabelRenderer; },{"./CSS2DRenderer.js":5}],7:[function(require,module,exports){ -var Objects=require("./objects.js"),utils=require("../utils/utils.js");function Object3D(t){var e=(t=utils._validate(t,Objects.prototype._defaults.Object3D)).obj,o=new THREE.Group;o.add(e);var r=Objects.prototype._makeGroup(o,t);r.model=t.obj,Objects.prototype._addMethods(r),r.setAnchor(t.anchor),r.setCenter(t.adjustment);let s=r.drawBoundingBox();return o.add(s),r.addTooltip(r.uuid,!0,r.anchor),r.visibility=!0,r}module.exports=exports=Object3D; +var Objects=require("./objects.js"),utils=require("../utils/utils.js");function Object3D(t){var e=(t=utils._validate(t,Objects.prototype._defaults.Object3D)).obj,o=new THREE.Group;o.add(e);var r=Objects.prototype._makeGroup(o,t);t.obj.name="model",Objects.prototype._addMethods(r),r.setAnchor(t.anchor),r.setCenter(t.adjustment);let s=r.drawBoundingBox();return o.add(s),r.addTooltip(r.uuid,!0,r.anchor),r.visibility=!0,r}module.exports=exports=Object3D; },{"../utils/utils.js":26,"./objects.js":18}],8:[function(require,module,exports){ var mod={},l=void 0,aa=mod;function r(t,r){var i,e=t.split("."),h=aa;!(e[0]in h)&&h.execScript&&h.execScript("var "+e[0]);for(;e.length&&(i=e.shift());)e.length||r===l?h=h[i]?h[i]:h[i]={}:h[i]=r}var t="undefined"!=typeof Uint8Array&&"undefined"!=typeof Uint16Array&&"undefined"!=typeof Uint32Array&&"undefined"!=typeof DataView;function v(r){var i,e,h,s,a,n,f,o,l,u,c=r.length,b=0,y=Number.POSITIVE_INFINITY;for(o=0;ob&&(b=r[o]),r[o]>=1;for(u=h<<16|o,l=n;l>>=1){case 0:var i=this.input,e=this.a,h=this.c,s=this.b,a=i.length,n=l,f=h.length,o=l;if(this.d=this.f=0,e+1>=a)throw Error("invalid uncompressed block header: LEN");if(n=i[e++]|i[e++]<<8,e+1>=a)throw Error("invalid uncompressed block header: NLEN");if(n===~(i[e++]|i[e++]<<8))throw Error("invalid uncompressed block header: length verify");if(e+n>i.length)throw Error("input buffer is broken");switch(this.i){case A:for(;s+n>h.length;){if(n-=o=f-s,t)h.set(i.subarray(e,e+o),s),s+=o,e+=o;else for(;o--;)h[s++]=i[e++];this.b=s,h=this.e(),s=this.b}break;case y:for(;s+n>h.length;)h=this.e({p:2});break;default:throw Error("invalid inflate mode")}if(t)h.set(i.subarray(e,e+n),s),s+=n,e+=n;else for(;n--;)h[s++]=i[e++];this.a=e,this.b=s,this.c=h;break;case 1:this.j(ba,ca);break;case 2:var u,c,b,d,p=C(this,5)+257,w=C(this,5)+1,g=C(this,4)+4,k=new(t?Uint8Array:Array)(D.length),m=l,U=l,I=l,N=l,P=l;for(P=0;P=P?8:255>=P?9:279>=P?7:8;var R,ga,ba=v(O),Q=new(t?Uint8Array:Array)(30);for(R=0,ga=Q.length;R=n)throw Error("input buffer is broken");e|=s[a++]<>>r,t.d=h-r,t.a=a,i}function E(t,r){for(var i,e,h=t.f,s=t.d,a=t.input,n=t.a,f=a.length,o=r[0],l=r[1];s=f);)h|=a[n++]<>>16)>s)throw Error("invalid code length: "+e);return t.f=h>>e,t.d=s-e,t.a=n,65535&i}function W(t,r){var i,e;switch(this.input=t,this.a=0,!r&&(r={})||(r.index&&(this.a=r.index),r.verify&&(this.A=r.verify)),i=t[this.a++],e=t[this.a++],15&i){case ha:this.method=ha;break;default:throw Error("unsupported compression method")}if(0!=((i<<8)+e)%31)throw Error("invalid fcheck flag:"+((i<<8)+e)%31);if(32&e)throw Error("fdict flag is not supported");this.q=new w(t,{index:this.a,bufferSize:r.bufferSize,bufferType:r.bufferType,resize:r.resize})}w.prototype.j=function(t,r){var i=this.c,e=this.b;this.o=t;for(var h,s,a,n,f=i.length-258;256!==(h=E(this,t));)if(256>h)e>=f&&(this.b=e,i=this.e(),e=this.b),i[e++]=h;else for(n=I[s=h-257],0=f&&(this.b=e,i=this.e(),e=this.b);n--;)i[e]=i[e++-a];for(;8<=this.d;)this.d-=8,this.a--;this.b=e},w.prototype.w=function(t,r){var i=this.c,e=this.b;this.o=t;for(var h,s,a,n,f=i.length;256!==(h=E(this,t));)if(256>h)e>=f&&(f=(i=this.e()).length),i[e++]=h;else for(n=I[s=h-257],0f&&(f=(i=this.e()).length);n--;)i[e]=i[e++-a];for(;8<=this.d;)this.d-=8,this.a--;this.b=e},w.prototype.e=function(){var r,i,e=new(t?Uint8Array:Array)(this.b-32768),h=this.b-32768,s=this.c;if(t)e.set(s.subarray(32768,e.length));else for(r=0,i=e.length;rr;++r)s[r]=s[h+r];return this.b=32768,s},w.prototype.z=function(r){var i,e,h,s=this.input.length/this.a+1|0,a=this.input,n=this.c;return r&&("number"==typeof r.p&&(s=r.p),"number"==typeof r.u&&(s+=r.u)),2>s?e=(h=(a.length-this.a)/this.o[2]/2*258|0)i&&(this.c.length=i),r=this.c),this.buffer=r},W.prototype.k=function(){var t,r,i=this.input;if(t=this.q.k(),this.a=this.q.a,this.A){r=(i[this.a++]<<24|i[this.a++]<<16|i[this.a++]<<8|i[this.a++])>>>0;var e=t;if("string"==typeof e){var h,s,a=e.split("");for(h=0,s=a.length;h>>0;e=a}for(var n,f=1,o=0,l=e.length,u=0;0>>0)throw Error("invalid adler-32 checksum")}return t};var ha=8;r("Zlib.Inflate",W),r("Zlib.Inflate.prototype.decompress",W.prototype.k);var Y,Z,$,ia,X={ADAPTIVE:B.s,BLOCK:B.t};if(Object.keys)Y=Object.keys(X);else for(Z in Y=[],$=0,X)Y[$++]=Z;for($=0,ia=Y.length;$ 0.0 ? height : base, 1);\n\t\t\t\tfloat len = pos.z * u_height_factor / tan(u_altitude);\n\t\t\t\tpos.x += cos(u_azimuth) * len;\n\t\t\t\tpos.y += sin(u_azimuth) * len;\n\t\t\t\tpos.z = 0.0;\n\t\t\t\tgl_Position = u_matrix * pos;\n\t\t\t}\n\t\t\t"),i.compileShader(e);const r=i.createShader(i.FRAGMENT_SHADER);i.shaderSource(r,"\n\t\t\tvoid main() {\n\t\t\t\tgl_FragColor = vec4(0.0, 0.0, 0.0, 0.7);\n\t\t\t}\n\t\t\t"),i.compileShader(r),this.program=i.createProgram(),i.attachShader(this.program,e),i.attachShader(this.program,r),i.linkProgram(this.program),i.validateProgram(this.program),this.uMatrix=i.getUniformLocation(this.program,"u_matrix"),this.uHeightFactor=i.getUniformLocation(this.program,"u_height_factor"),this.uAltitude=i.getUniformLocation(this.program,"u_altitude"),this.uAzimuth=i.getUniformLocation(this.program,"u_azimuth"),this.aPos=i.getAttribLocation(this.program,"a_pos"),this.aNormal=i.getAttribLocation(this.program,"a_normal_ed"),this.aBase=i.getAttribLocation(this.program,"a_base"),this.aHeight=i.getAttribLocation(this.program,"a_height")}render(t,i){t.useProgram(this.program);const e=this.map.style.sourceCaches.composite,r=e.getVisibleCoordinates().reverse(),a=map.getLayer(this.buildingsLayerId),o=this.map.painter.context,{lng:s,lat:n}=this.map.getCenter(),h=this.tb.getSunPosition(this.tb.lightDateTime,s,n);t.uniform1f(this.uAltitude,h.altitude>this.minAltitude?h.altitude:0),t.uniform1f(this.uAzimuth,h.azimuth+3*Math.PI/2),t.enable(t.BLEND),t.blendFunc(t.SRC_ALPHA,t.ONE_MINUS_SRC_ALPHA);t.getExtension("EXT_blend_minmax");t.disable(t.DEPTH_TEST);for(const i of r){const r=e.getTile(i),s=r.getBucket(a);if(!s)continue;const[n,h]=s.programConfigurations.programConfigurations[this.buildingsLayerId]._buffers;t.uniformMatrix4fv(this.uMatrix,!1,i.posMatrix),t.uniform1f(this.uHeightFactor,Math.pow(2,i.overscaledZ)/r.tileSize/8);for(const i of s.segments.get()){const e=o.currentNumAttributes||0,r=2;for(let i=r;i\n\t\t#include \n\t\t#include \n\t\t#include \n\t\t#include \n\n\t\tuniform float linewidth;\n\t\tuniform vec2 resolution;\n\n\t\tattribute vec3 instanceStart;\n\t\tattribute vec3 instanceEnd;\n\n\t\tattribute vec3 instanceColorStart;\n\t\tattribute vec3 instanceColorEnd;\n\n\t\tvarying vec2 vUv;\n\n\t\t#ifdef USE_DASH\n\n\t\t\tuniform float dashScale;\n\t\t\tattribute float instanceDistanceStart;\n\t\t\tattribute float instanceDistanceEnd;\n\t\t\tvarying float vLineDistance;\n\n\t\t#endif\n\n\t\tvoid trimSegment( const in vec4 start, inout vec4 end ) {\n\n\t\t\t// trim end segment so it terminates between the camera plane and the near plane\n\n\t\t\t// conservative estimate of the near plane\n\t\t\tfloat a = projectionMatrix[ 2 ][ 2 ]; // 3nd entry in 3th column\n\t\t\tfloat b = projectionMatrix[ 3 ][ 2 ]; // 3nd entry in 4th column\n\t\t\tfloat nearEstimate = - 0.5 * b / a;\n\n\t\t\tfloat alpha = ( nearEstimate - start.z ) / ( end.z - start.z );\n\n\t\t\tend.xyz = mix( start.xyz, end.xyz, alpha );\n\n\t\t}\n\n\t\tvoid main() {\n\n\t\t\t#ifdef USE_COLOR\n\n\t\t\t\tvColor.xyz = ( position.y < 0.5 ) ? instanceColorStart : instanceColorEnd;\n\n\t\t\t#endif\n\n\t\t\t#ifdef USE_DASH\n\n\t\t\t\tvLineDistance = ( position.y < 0.5 ) ? dashScale * instanceDistanceStart : dashScale * instanceDistanceEnd;\n\n\t\t\t#endif\n\n\t\t\tfloat aspect = resolution.x / resolution.y;\n\n\t\t\tvUv = uv;\n\n\t\t\t// camera space\n\t\t\tvec4 start = modelViewMatrix * vec4( instanceStart, 1.0 );\n\t\t\tvec4 end = modelViewMatrix * vec4( instanceEnd, 1.0 );\n\n\t\t\t// special case for perspective projection, and segments that terminate either in, or behind, the camera plane\n\t\t\t// clearly the gpu firmware has a way of addressing this issue when projecting into ndc space\n\t\t\t// but we need to perform ndc-space calculations in the shader, so we must address this issue directly\n\t\t\t// perhaps there is a more elegant solution -- WestLangley\n\n\t\t\tbool perspective = ( projectionMatrix[ 2 ][ 3 ] == - 1.0 ); // 4th entry in the 3rd column\n\n\t\t\tif ( perspective ) {\n\n\t\t\t\tif ( start.z < 0.0 && end.z >= 0.0 ) {\n\n\t\t\t\t\ttrimSegment( start, end );\n\n\t\t\t\t} else if ( end.z < 0.0 && start.z >= 0.0 ) {\n\n\t\t\t\t\ttrimSegment( end, start );\n\n\t\t\t\t}\n\n\t\t\t}\n\n\t\t\t// clip space\n\t\t\tvec4 clipStart = projectionMatrix * start;\n\t\t\tvec4 clipEnd = projectionMatrix * end;\n\n\t\t\t// ndc space\n\t\t\tvec2 ndcStart = clipStart.xy / clipStart.w;\n\t\t\tvec2 ndcEnd = clipEnd.xy / clipEnd.w;\n\n\t\t\t// direction\n\t\t\tvec2 dir = ndcEnd - ndcStart;\n\n\t\t\t// account for clip-space aspect ratio\n\t\t\tdir.x *= aspect;\n\t\t\tdir = normalize( dir );\n\n\t\t\t// perpendicular to dir\n\t\t\tvec2 offset = vec2( dir.y, - dir.x );\n\n\t\t\t// undo aspect ratio adjustment\n\t\t\tdir.x /= aspect;\n\t\t\toffset.x /= aspect;\n\n\t\t\t// sign flip\n\t\t\tif ( position.x < 0.0 ) offset *= - 1.0;\n\n\t\t\t// endcaps\n\t\t\tif ( position.y < 0.0 ) {\n\n\t\t\t\toffset += - dir;\n\n\t\t\t} else if ( position.y > 1.0 ) {\n\n\t\t\t\toffset += dir;\n\n\t\t\t}\n\n\t\t\t// adjust for linewidth\n\t\t\toffset *= linewidth;\n\n\t\t\t// adjust for clip-space to screen-space conversion // maybe resolution should be based on viewport ...\n\t\t\toffset /= resolution.y;\n\n\t\t\t// select end\n\t\t\tvec4 clip = ( position.y < 0.5 ) ? clipStart : clipEnd;\n\n\t\t\t// back to clip space\n\t\t\toffset *= clip.w;\n\n\t\t\tclip.xy += offset;\n\n\t\t\tgl_Position = clip;\n\n\t\t\t#include \n\n\t\t\t#include \n\t\t\t#include \n\t\t\t#include \n\n\t\t}\n\t\t",fragmentShader:"\n\t\tuniform vec3 diffuse;\n\t\tuniform float opacity;\n\n\t\t#ifdef USE_DASH\n\n\t\t\tuniform float dashSize;\n\t\t\tuniform float gapSize;\n\n\t\t#endif\n\n\t\tvarying float vLineDistance;\n\n\t\t#include \n\t\t#include \n\t\t#include \n\t\t#include \n\t\t#include \n\n\t\tvarying vec2 vUv;\n\n\t\tvoid main() {\n\n\t\t\t#include \n\n\t\t\t#ifdef USE_DASH\n\n\t\t\t\tif ( vUv.y < 0.5 || vUv.y > 0.5 ) discard; // discard endcaps\n\n\t\t\t\tif ( mod( vLineDistance, dashSize + gapSize ) > dashSize ) discard; // todo - FIX\n\n\t\t\t#endif\n\n\t\t\tif ( vUv.y < 0.5 || vUv.y > 0.5 ) {\n\n\t\t\t\tfloat a = vUv.x - 0.5;\n\t\t\t\tfloat b = vUv.y - 0.5;\n\t\t\t\tfloat len2 = a * a + b * b;\n\n\t\t\t\tif ( len2 > 0.25 ) discard;\n\n\t\t\t}\n\n\t\t\tvec4 diffuseColor = vec4( diffuse, opacity );\n\n\t\t\t#include \n\t\t\t#include \n\n\t\t\tgl_FragColor = vec4( diffuseColor.rgb, opacity );\n\n\t\t\t#include \n\t\t\t#include \n\t\t\t#include \n\t\t\t#include \n\n\t\t}\n\t\t"},THREE.LineMaterial=function(t){var e=THREE.UniformsUtils.clone(THREE.ShaderLib.line.uniforms);e.opacity.value=t.opacity,THREE.ShaderMaterial.call(this,{type:"LineMaterial",uniforms:e,vertexShader:THREE.ShaderLib.line.vertexShader,fragmentShader:THREE.ShaderLib.line.fragmentShader}),this.dashed=!1,Object.defineProperties(this,{color:{enumerable:!0,get:function(){return this.uniforms.diffuse.value},set:function(t){this.uniforms.diffuse.value=t}},linewidth:{enumerable:!0,get:function(){return this.uniforms.linewidth.value},set:function(t){this.uniforms.linewidth.value=t}},dashScale:{enumerable:!0,get:function(){return this.uniforms.dashScale.value},set:function(t){this.uniforms.dashScale.value=t}},dashSize:{enumerable:!0,get:function(){return this.uniforms.dashSize.value},set:function(t){this.uniforms.dashSize.value=t}},gapSize:{enumerable:!0,get:function(){return this.uniforms.gapSize.value},set:function(t){this.uniforms.gapSize.value=t}},resolution:{enumerable:!0,get:function(){return this.uniforms.resolution.value},set:function(t){this.uniforms.resolution.value.copy(t)}}}),this.setValues(t)},THREE.LineMaterial.prototype=Object.create(THREE.ShaderMaterial.prototype),THREE.LineMaterial.prototype.constructor=THREE.LineMaterial,THREE.LineMaterial.prototype.isLineMaterial=!0,THREE.LineMaterial.prototype.copy=function(t){return THREE.ShaderMaterial.prototype.copy.call(this,t),this.color.copy(t.color),this.linewidth=t.linewidth,this.resolution=t.resolution,this},THREE.LineSegments2=function(t,e){THREE.Mesh.call(this),this.type="LineSegments2",this.geometry=void 0!==t?t:new THREE.LineSegmentsGeometry,this.material=void 0!==e?e:new THREE.LineMaterial({color:16777215*Math.random()})},THREE.LineSegments2.prototype=Object.assign(Object.create(THREE.Mesh.prototype),{constructor:THREE.LineSegments2,isLineSegments2:!0,computeLineDistances:function(){var t=new THREE.Vector3,e=new THREE.Vector3;return function(){for(var n=this.geometry,i=n.attributes.instanceStart,r=n.attributes.instanceEnd,o=new Float32Array(2*i.data.count),a=0,s=0,c=i.data.count;a{let t;switch(e.type){case"mtl":r=r.children[0];break;case"gltf":case"dae":t=r.animations,r=r.scene;break;case"fbx":t=r.animations}var l=utils.types.rotation(e.rotation,[0,0,0]),s=utils.types.scale(e.scale,[1,1,1]);r.rotation.set(l[0],l[1],l[2]),r.scale.set(s[0],s[1],s[2]),e.normalize&&function(e){e.traverse(function(e){if(e.isMesh){let a;"MeshStandardMaterial"==e.material.type?(e.material.metalness&&(e.material.metalness*=.1),e.material.glossiness&&(e.material.glossiness*=.25),a=new THREE.Color(12,12,12)):"MeshPhongMaterial"==e.material.type&&(e.material.shininess=.1,a=new THREE.Color(20,20,20)),e.material.specular&&e.material.specular.isColor&&(e.material.specular=a)}})}(r);var d=new THREE.Group;d.add(r);var i=Objects.prototype._makeGroup(d,e);i.model=r,i.animations=t,Objects.prototype._addMethods(i),i.setAnchor(e.anchor),i.setCenter(e.adjustment);let n=i.drawBoundingBox();d.add(n),i.addTooltip(i.uuid,!0,i.anchor),a(i),o(i),i.idle()},()=>null,a=>{console.error("Could not load model file: "+e.obj+" \n "+a.stack),o("Error loading the model")})},()=>null,e=>{console.warn("No material file found for SymbolLayer3D model "+m)})}module.exports=exports=loadObj; +var utils=require("../utils/utils.js"),Objects=require("./objects.js");const OBJLoader=require("./loaders/OBJLoader.js"),MTLLoader=require("./loaders/MTLLoader.js"),FBXLoader=require("./loaders/FBXLoader.js"),GLTFLoader=require("./loaders/GLTFLoader.js"),ColladaLoader=require("./loaders/ColladaLoader.js"),objLoader=new OBJLoader,materialLoader=new MTLLoader,gltfLoader=new GLTFLoader,fbxLoader=new FBXLoader,daeLoader=new ColladaLoader;function loadObj(e,a,o){if(void 0===e)return console.error("Invalid options provided to loadObj()");e=utils._validate(e,Objects.prototype._defaults.loadObj),this.loaded=!1;var r;switch(e.type||(e.type="mtl"),e.type){case"mtl":r=objLoader;break;case"gltf":r=gltfLoader;break;case"fbx":r=fbxLoader;break;case"dae":r=daeLoader}materialLoader.load(e.mtl,function(t){t&&"mtl"==e.type&&(t.preload(),r.setMaterials(t));r.load(e.obj,r=>{let t=[];switch(e.type){case"mtl":r=r.children[0];break;case"gltf":case"dae":t=r.animations,r=r.scene;break;case"fbx":t=r.animations}r.animations=t;var l=utils.types.rotation(e.rotation,[0,0,0]),s=utils.types.scale(e.scale,[1,1,1]);r.rotation.set(l[0],l[1],l[2]),r.scale.set(s[0],s[1],s[2]),e.normalize&&function(e){e.traverse(function(e){if(e.isMesh){let a;"MeshStandardMaterial"==e.material.type?(e.material.metalness&&(e.material.metalness*=.1),e.material.glossiness&&(e.material.glossiness*=.25),a=new THREE.Color(12,12,12)):"MeshPhongMaterial"==e.material.type&&(e.material.shininess=.1,a=new THREE.Color(20,20,20)),e.material.specular&&e.material.specular.isColor&&(e.material.specular=a)}})}(r),r.name="model";var d=new THREE.Group;d.name="group",d.add(r);var i=Objects.prototype._makeGroup(d,e);i.name="object",i.animations=t,Objects.prototype._addMethods(i),i.setAnchor(e.anchor),i.setCenter(e.adjustment);i.animations;let n=i.drawBoundingBox();d.add(n),i.addTooltip(i.uuid,!0,i.anchor),a(i),o(i),i.idle()},()=>null,a=>{console.error("Could not load model file: "+e.obj+" \n "+a.stack),o("Error loading the model")})},()=>null,e=>{console.warn("No material file found for SymbolLayer3D model "+m)})}module.exports=exports=loadObj; },{"../utils/utils.js":26,"./loaders/ColladaLoader.js":13,"./loaders/FBXLoader.js":14,"./loaders/GLTFLoader.js":15,"./loaders/MTLLoader.js":16,"./loaders/OBJLoader.js":17,"./objects.js":18}],13:[function(require,module,exports){ const THREE=require("../../three.js");THREE.ColladaLoader=function(e){THREE.Loader.call(this,e)},THREE.ColladaLoader.prototype=Object.assign(Object.create(THREE.Loader.prototype),{constructor:THREE.ColladaLoader,load:function(e,t,r,a){var n=this,i=""===n.path?THREE.LoaderUtils.extractUrlBase(e):n.path,s=new THREE.FileLoader(n.manager);s.setPath(n.path),s.load(e,function(r){try{t(n.parse(r,i))}catch(t){a?a(t):console.error(t),n.manager.itemError(e)}},r,a)},options:{set convertUpAxis(e){console.warn("THREE.ColladaLoader: options.convertUpAxis() has been removed. Up axis is converted automatically.")}},parse:function(e,t){function r(e,t){for(var r=[],a=e.childNodes,n=0,i=a.length;n0&&t.push(new THREE.VectorKeyframeTrack(a+".position",n,i)),s.length>0&&t.push(new THREE.QuaternionKeyframeTrack(a+".quaternion",n,s)),o.length>0&&t.push(new THREE.VectorKeyframeTrack(a+".scale",n,o)),t}function T(e,t,r){var a,n,i,s=!0;for(n=0,i=e.length;n=0;){var a=e[t];if(null!==a.value[r])return a;t--}return null}function x(e,t,r){for(;t>>0));switch(r=r.toLowerCase()){case"tga":t=De;break;default:t=We}return t}(r);if(void 0!==n){var i=n.load(r),s=e.extra;if(void 0!==s&&void 0!==s.technique&&!1===o(s.technique)){var c=s.technique;i.wrapS=c.wrapU?THREE.RepeatWrapping:THREE.ClampToEdgeWrapping,i.wrapT=c.wrapV?THREE.RepeatWrapping:THREE.ClampToEdgeWrapping,i.offset.set(c.offsetU||0,c.offsetV||0),i.repeat.set(c.repeatU||1,c.repeatV||1)}else i.wrapS=THREE.RepeatWrapping,i.wrapT=THREE.RepeatWrapping;return i}return console.warn("THREE.ColladaLoader: Loader for texture %s not found.",r),null}return console.warn("THREE.ColladaLoader: Couldn't create texture with ID:",e.id),null}r.name=e.name||"";var c=n.parameters;for(var l in c){var d=c[l];switch(l){case"diffuse":d.color&&r.color.fromArray(d.color),d.texture&&(r.map=s(d.texture));break;case"specular":d.color&&r.specular&&r.specular.fromArray(d.color),d.texture&&(r.specularMap=s(d.texture));break;case"bump":d.texture&&(r.normalMap=s(d.texture));break;case"ambient":d.texture&&(r.lightMap=s(d.texture));break;case"shininess":d.float&&r.shininess&&(r.shininess=d.float);break;case"emission":d.color&&r.emissive&&r.emissive.fromArray(d.color),d.texture&&(r.emissiveMap=s(d.texture))}}var u=c.transparent,h=c.transparency;if(void 0===h&&u&&(h={float:1}),void 0===u&&h&&(u={opaque:"A_ONE",data:{color:[1,1,1,1]}}),u&&h)if(u.data.texture)r.transparent=!0;else{var m=u.data.color;switch(u.opaque){case"A_ONE":r.opacity=m[3]*h.float;break;case"RGB_ZERO":r.opacity=1-m[0]*h.float;break;case"A_ZERO":r.opacity=1-m[3]*h.float;break;case"RGB_ONE":r.opacity=m[0]*h.float;break;default:console.warn('THREE.ColladaLoader: Invalid opaque type "%s" of transparent tag.',u.opaque)}r.opacity<1&&(r.transparent=!0)}return void 0!==i&&void 0!==i.technique&&1===i.technique.double_sided&&(r.side=THREE.DoubleSide),r}function X(e){return f(Ke.materials[e],J)}function K(e){for(var t=0;t0?c+d:c;t.inputs[u]={id:o,offset:l},t.stride=Math.max(t.stride,l+1),"TEXCOORD"===c&&(t.hasUV=!0);break;case"vcount":t.vcount=i(n.textContent);break;case"p":t.p=i(n.textContent)}}return t}function oe(e){for(var t=0,r=0,a=e.length;r0&&t0&&h.setAttribute("position",new THREE.Float32BufferAttribute(n.array,n.stride)),i.array.length>0&&h.setAttribute("normal",new THREE.Float32BufferAttribute(i.array,i.stride)),c.array.length>0&&h.setAttribute("color",new THREE.Float32BufferAttribute(c.array,c.stride)),s.array.length>0&&h.setAttribute("uv",new THREE.Float32BufferAttribute(s.array,s.stride)),o.array.length>0&&h.setAttribute("uv2",new THREE.Float32BufferAttribute(o.array,o.stride)),l.length>0&&h.setAttribute("skinIndex",new THREE.Float32BufferAttribute(l,d)),u.length>0&&h.setAttribute("skinWeight",new THREE.Float32BufferAttribute(u,f)),a.data=h,a.type=e[0].type,a.materialKeys=m,a}function de(e,t,r,a){var n=e.p,i=e.stride,s=e.vcount;function o(e){for(var t=n[e+r]*l,i=t+l;t4)for(var g=1,b=h-2;g<=b;g++){m=d+i*g,p=d+i*(g+1);o(d+0*i),o(m),o(p)}d+=i*h}else for(u=0,f=n.length;u=t.limits.max&&(t.static=!0),t.middlePosition=(t.limits.min+t.limits.max)/2,t}function ve(e){for(var t={sid:e.getAttribute("sid"),name:e.getAttribute("name")||"",attachments:[],transforms:[]},r=0;ra.limits.max||t0?t[t.length-1]:"",smooth:void 0!==r?r.smooth:this.smooth,groupStart:void 0!==r?r.groupEnd:0,groupEnd:-1,groupCount:-1,inherited:!1,clone:function(e){var t={index:"number"==typeof e?e:this.index,name:this.name,mtllib:this.mtllib,smooth:this.smooth,groupStart:0,groupEnd:-1,groupCount:-1,inherited:!1};return t.clone=this.clone.bind(t),t}};return this.materials.push(s),s},currentMaterial:function(){if(this.materials.length>0)return this.materials[this.materials.length-1]},_finalize:function(e){var t=this.currentMaterial();if(t&&-1===t.groupEnd&&(t.groupEnd=this.geometry.vertices.length/3,t.groupCount=t.groupEnd-t.groupStart,t.inherited=!1),e&&this.materials.length>1)for(var r=this.materials.length-1;r>=0;r--)this.materials[r].groupCount<=0&&this.materials.splice(r,1);return e&&0===this.materials.length&&this.materials.push({name:"",smooth:this.smooth}),t}},r&&r.name&&"function"==typeof r.clone){var s=r.clone(0);s.inherited=!0,this.object.materials.push(s)}this.objects.push(this.object)},finalize:function(){this.object&&"function"==typeof this.object._finalize&&this.object._finalize(!0)},parseVertexIndex:function(e,t){var r=parseInt(e,10);return 3*(r>=0?r-1:r+t/3)},parseNormalIndex:function(e,t){var r=parseInt(e,10);return 3*(r>=0?r-1:r+t/3)},parseUVIndex:function(e,t){var r=parseInt(e,10);return 2*(r>=0?r-1:r+t/2)},addVertex:function(e,t,r){var s=this.vertices,i=this.object.geometry.vertices;i.push(s[e+0],s[e+1],s[e+2]),i.push(s[t+0],s[t+1],s[t+2]),i.push(s[r+0],s[r+1],s[r+2])},addVertexPoint:function(e){var t=this.vertices;this.object.geometry.vertices.push(t[e+0],t[e+1],t[e+2])},addVertexLine:function(e){var t=this.vertices;this.object.geometry.vertices.push(t[e+0],t[e+1],t[e+2])},addNormal:function(e,t,r){var s=this.normals,i=this.object.geometry.normals;i.push(s[e+0],s[e+1],s[e+2]),i.push(s[t+0],s[t+1],s[t+2]),i.push(s[r+0],s[r+1],s[r+2])},addFaceNormal:function(e,t,r){var s=this.vertices,h=this.object.geometry.normals;i.fromArray(s,e),a.fromArray(s,t),o.fromArray(s,r),l.subVectors(o,a),n.subVectors(i,a),l.cross(n),l.normalize(),h.push(l.x,l.y,l.z),h.push(l.x,l.y,l.z),h.push(l.x,l.y,l.z)},addColor:function(e,t,r){var s=this.colors,i=this.object.geometry.colors;void 0!==s[e]&&i.push(s[e+0],s[e+1],s[e+2]),void 0!==s[t]&&i.push(s[t+0],s[t+1],s[t+2]),void 0!==s[r]&&i.push(s[r+0],s[r+1],s[r+2])},addUV:function(e,t,r){var s=this.uvs,i=this.object.geometry.uvs;i.push(s[e+0],s[e+1]),i.push(s[t+0],s[t+1]),i.push(s[r+0],s[r+1])},addDefaultUV:function(){var e=this.object.geometry.uvs;e.push(0,0),e.push(0,0),e.push(0,0)},addUVLine:function(e){var t=this.uvs;this.object.geometry.uvs.push(t[e+0],t[e+1])},addFace:function(e,t,r,s,i,a,o,n,l){var h=this.vertices.length,c=this.parseVertexIndex(e,h),u=this.parseVertexIndex(t,h),p=this.parseVertexIndex(r,h);if(this.addVertex(c,u,p),this.addColor(c,u,p),void 0!==o&&""!==o){var m=this.normals.length;c=this.parseNormalIndex(o,m),u=this.parseNormalIndex(n,m),p=this.parseNormalIndex(l,m),this.addNormal(c,u,p),this.object.geometry.hasNormalIndices=!0}else this.addFaceNormal(c,u,p);if(void 0!==s&&""!==s){var d=this.uvs.length;c=this.parseUVIndex(s,d),u=this.parseUVIndex(i,d),p=this.parseUVIndex(a,d),this.addUV(c,u,p),this.object.geometry.hasUVIndices=!0}else this.addDefaultUV()},addPointGeometry:function(e){this.object.geometry.type="Points";for(var t=this.vertices.length,r=0,s=e.length;r=7?a.colors.push(parseFloat(d[4]),parseFloat(d[5]),parseFloat(d[6])):a.colors.push(void 0,void 0,void 0);break;case"vn":a.normals.push(parseFloat(d[1]),parseFloat(d[2]),parseFloat(d[3]));break;case"vt":a.uvs.push(parseFloat(d[1]),parseFloat(d[2]))}}else if("f"===l){for(var f=n.substr(1).trim().split(/\s+/),v=[],g=0,b=f.length;g0){var y=E.split("/");v.push(y)}}var j=v[0];for(g=1,b=v.length-1;g1){var z=c[1].trim().toLowerCase();a.object.smooth="0"!==z&&"off"!==z}else a.object.smooth=!0;(q=a.object.currentMaterial())&&(q.smooth=a.object.smooth)}else{if("\0"===n)continue;console.warn('THREE.OBJLoader: Unexpected line: "'+n+'"')}a.finalize();var A=new THREE.Group;A.materialLibraries=[].concat(a.materialLibraries);for(p=0,m=a.objects.length;p0&&(C=!0,_.setAttribute("color",new THREE.Float32BufferAttribute(P.colors,3))),!0===P.hasUVIndices&&_.setAttribute("uv",new THREE.Float32BufferAttribute(P.uvs,2));for(var G,S=[],D=0,J=B.length;D1){for(D=0,J=B.length;Dt,set(e){t=e}}),Object.defineProperty(e,"boundingBoxShadow",{get:()=>i,set(e){i=e}}),e.drawBoundingBox=function(){let t=e.box3(),o=new THREE.Group;o.name="BoxGrid",o.updateMatrixWorld(!0);let a=new THREE.Box3Helper(t,Objects.prototype._defaults.colors.yellow);a.name="BoxModel",o.add(a),a.layers.disable(0),e.boundingBox=a;let i=t.clone();i.max.z=i.min.z;let n=new THREE.Box3Helper(i,Objects.prototype._defaults.colors.black);return n.name="BoxShadow",o.add(n),n.layers.disable(0),e.boundingBoxShadow=n,o.visible=!1,o},e.setBoundingBoxShadowFloor=function(){e.boundingBox&&(e.boundingBoxShadow.box.max.z=-e.modelHeight,e.boundingBoxShadow.box.min.z=-e.modelHeight)},e.setAnchor=function(t){const o=e.box3(),a=(o.getSize(new THREE.Vector3),o.getCenter(new THREE.Vector3));switch(e.none={x:0,y:0,z:0},e.center={x:a.x,y:a.y,z:o.min.z},e.bottom={x:a.x,y:o.max.y,z:o.min.z},e.bottomLeft={x:o.max.x,y:o.max.y,z:o.min.z},e.bottomRight={x:o.min.x,y:o.max.y,z:o.min.z},e.top={x:a.x,y:o.min.y,z:o.min.z},e.topLeft={x:o.max.x,y:o.min.y,z:o.min.z},e.topRight={x:o.min.x,y:o.min.y,z:o.min.z},e.left={x:o.max.x,y:a.y,z:o.min.z},e.right={x:o.min.x,y:a.y,z:o.min.z},t){case"center":e.anchor=e.center;break;case"top":e.anchor=e.top;break;case"top-left":e.anchor=e.topLeft;break;case"top-right":e.anchor=e.topRight;break;case"left":e.anchor=e.left;break;case"right":e.anchor=e.right;break;case"bottom":e.anchor=e.bottom;break;case"bottom-left":default:e.anchor=e.bottomLeft;break;case"bottom-right":e.anchor=e.bottomRight;break;case"auto":case"none":e.anchor=e.none}e.model.position.set(-e.anchor.x,-e.anchor.y,-e.anchor.z)},e.setCenter=function(t){if(t&&(0!=t.x||0!=t.y||0!=t.z)){let o=e.getSize();e.anchor={x:-o.x*t.x,y:-o.y*t.y,z:-o.z*t.z},e.model.position.set(-e.anchor.x,-e.anchor.y,-e.anchor.z)}},Object.defineProperty(e,"label",{get:()=>n,set(e){n=e}}),Object.defineProperty(e,"tooltip",{get:()=>l,set(e){l=e}}),Object.defineProperty(e,"visibility",{get:()=>e.visible,set(t){let o=t;if("visible"==t||1==t)o=!0,e.label&&(e.label.visible=o);else{if("none"!=t&&0!=t)return;o=!1,e.label&&e.label.alwaysVisible&&(e.label.visible=o),e.tooltip&&(e.tooltip.visible=o)}e.visible!=o&&(e.visible=o,e.model&&e.model.traverse(function(e){"Mesh"!=e.type&&"SkinnedMesh"!=e.type||(o?e.layers.enable(0):e.layers.disable(0)),"LineSegments"==e.type&&e.layers.disableAll()}))}}),e.addLabel=function(t,o=!1,a=e.anchor){t&&e.children[0].add(e.drawLabelHTML(t,o,a))},e.drawLabelHTML=function(t,a=!1,i=e.anchor){let n=o.drawLabelHTML(t,Objects.prototype._defaults.label.cssClass);const l=e.box3(),r=l.getSize(new THREE.Vector3);let s=l.max.x,d=l.max.y;l.min.z;return e.label&&(e.label.remove,e.label=null),e.label=new CSS2D.CSS2DObject(n),e.label.position.set(.5*-r.x-e.model.position.x-i.x+s,.5*-r.y-e.model.position.y-i.y+d,.5*r.z),e.label.visible=a,e.label.alwaysVisible=a,e.label},e.addTooltip=function(t,a=!1,i=e.anchor){if(t){let n=o.drawTooltip(t,a);const l=e.box3(),r=l.getSize(new THREE.Vector3);let s={x:l.max.x,y:l.max.y,z:l.min.z};e.tooltip&&(e.tooltip.remove,e.tooltip=null),e.tooltip=new CSS2D.CSS2DObject(n),e.tooltip.position.set(.5*-r.x-e.model.position.x-i.x+s.x,.5*-r.y-e.model.position.y-i.y+s.y,r.z),e.tooltip.visible=!1,e.children[0].add(e.tooltip)}};let r=!1;Object.defineProperty(e,"castShadow",{get:()=>r,set(t){if(r!=t){if(e.model.traverse(function(e){e.isMesh&&(e.castShadow=!0)}),t){const o=e.modelSize,a=[o.x,o.y,o.z],i=10*Math.max(...a),n=new THREE.PlaneBufferGeometry(i,i),l=new THREE.ShadowMaterial;l.opacity=.5;let r=new THREE.Mesh(n,l);r.layers.enable(1),r.layers.disable(0),r.receiveShadow=t,e.add(r)}else e.traverse(function(t){t.isMesh&&t.material instanceof THREE.ShadowMaterial&&e.remove(t)});r=t}}});let s=!1;Object.defineProperty(e,"receiveShadow",{get:()=>s,set(t){s!=t&&(e.model.traverse(function(e){e.isMesh&&(e.receiveShadow=!0)}),s=t)}});let d=!1;Object.defineProperty(e,"wireframe",{get:()=>d,set(t){d!=t&&(e.model.traverse(function(e){if("Mesh"==e.type||"SkinnedMesh"==e.type){let o=[];Array.isArray(e.material)?o=e.material:o.push(e.material),o.forEach(function(e){e.opacity=t?.5:1,e.wireframe=t}),t?(e.layers.disable(0),e.layers.enable(1)):(e.layers.disable(1),e.layers.enable(0))}"LineSegments"==e.type&&e.layers.disableAll()}),d=t,e.dispatchEvent(new CustomEvent("Wireframed",{detail:e,bubbles:!0,cancelable:!0})))}});let c=!1;Object.defineProperty(e,"selected",{get:()=>c,set(t){t?(e.boundingBox&&(e.boundingBox.material=Objects.prototype._defaults.materials.boxSelectedMaterial,e.boundingBox.parent.visible=!0,e.boundingBox.layers.enable(1),e.boundingBoxShadow.layers.enable(1)),e.label&&!e.label.alwaysVisible&&(e.label.visible=!0)):(e.boundingBox&&(e.boundingBox.parent.visible=!1,e.boundingBox.layers.disable(1),e.boundingBoxShadow.layers.disable(1),e.boundingBox.material=Objects.prototype._defaults.materials.boxNormalMaterial),e.label&&!e.label.alwaysVisible&&(e.label.visible=!1)),e.tooltip&&(e.tooltip.visible=t),c!=t&&(c=t,e.dispatchEvent(new CustomEvent("SelectedChange",{detail:e,bubbles:!0,cancelable:!0})))}});let p=!1;Object.defineProperty(e,"over",{get:()=>p,set(t){t?(e.selected||e.boundingBox&&(e.boundingBox.material=Objects.prototype._defaults.materials.boxOverMaterial,e.boundingBox.parent.visible=!0,e.boundingBox.layers.enable(1),e.boundingBoxShadow.layers.enable(1)),e.label&&!e.label.alwaysVisible&&(e.label.visible=!0),e.dispatchEvent(new CustomEvent("ObjectMouseOver",{detail:e,bubbles:!0,cancelable:!0}))):(e.selected||(e.boundingBox&&(e.boundingBox.parent.visible=!1,e.boundingBox.layers.disable(1),e.boundingBoxShadow.layers.disable(1),e.boundingBox.material=Objects.prototype._defaults.materials.boxNormalMaterial),e.label&&!e.label.alwaysVisible&&(e.label.visible=!1)),e.dispatchEvent(new CustomEvent("ObjectMouseOut",{detail:e,bubbles:!0,cancelable:!0}))),e.tooltip&&(e.tooltip.visible=t||e.selected),p=t}}),e.box3=function(){let t;if(e.updateMatrix(),e.updateMatrixWorld(!0,!0),e.model){let o=e.clone(!0);if(o.model=e.model.clone(),t=(new THREE.Box3).setFromObject(o.model),e.parent){let a=new THREE.Matrix4,i=new THREE.Matrix4;e.matrix.extractRotation(a),a.getInverse(i),o.setRotationFromMatrix(i),t=(new THREE.Box3).setFromObject(o.model)}}return t},e.modelBox=function(){return e.box3()},e.getSize=function(){return e.box3().getSize(new THREE.Vector3(0,0,0))};let b=!1;Object.defineProperty(e,"modelSize",{get:()=>b=e.getSize(),set(e){b!=e&&(b=e)}}),e.modelHeight=0}e.add=function(t){return e.children[0].add(t),t.position.z=e.coordinates[2]?-e.coordinates[2]:0,t},e.remove=function(t){e.children[0].remove(t),tb.map.repaint=!0},e.duplicate=function(){var t=e.clone(!0);return t.userData=e.userData,t.model=t.children[0].children[0],t.animations=t.model.animations,o._addMethods(t),t.deepCopy(e),t},e.deepCopy=function(t){return e.anchor=t.anchor,e.bottom=t.bottom,e.bottomLeft=t.bottomLeft,e.bottomRight=t.bottomRight,e.center=t.center,e.left=t.left,e.right=t.right,e.top=t.top,e.topLeft=t.topLeft,e.topRight=t.topRight,e.boundingBox=e.children[0].children[1].children[0],e.boundingBoxShadow=e.children[0].children[1].children[1],e.tooltip=e.children[0].children[2],e},e.dispose=function(){e.traverse(e=>{if(e.isMesh)if(e.geometry.dispose(),e.material.isMaterial)i(e.material);else for(const t of e.material)i(t)}),e.label&&e.label.dispose(),e.tooltip&&e.tooltip.dispose(),e.model&&(e.model={})};const i=e=>{e.dispose();for(const t of Object.keys(e)){const o=e[t];o&&"object"==typeof o&&"minFilter"in o&&o.dispose()}let t=e;(t.map||t.alphaMap||t.aoMap||t.bumpMap||t.displacementMap||t.emissiveMap||t.envMap||t.lightMap||t.metalnessMap||t.normalMap||t.roughnessMap)&&(t.map&&t.map.dispose(),t.alphaMap&&t.alphaMap.dispose(),t.aoMap&&t.aoMap.dispose(),t.bumpMap&&t.bumpMap.dispose(),t.displacementMap&&t.displacementMap.dispose(),t.emissiveMap&&t.emissiveMap.dispose(),t.envMap&&t.envMap.dispose(),t.lightMap&&t.lightMap.dispose(),t.metalnessMap&&t.metalnessMap.dispose(),t.normalMap&&t.normalMap.dispose(),t.roughnessMap&&t.roughnessMap.dispose())};return e},_makeGroup:function(e,t){var a=new THREE.Group;if(a.userData=t||{},a.userData.isGeoGroup=!0,a.userData.feature&&(a.userData.feature.properties.uuid=a.uuid),e.length)for(o of e)a.add(o);else a.add(e);return utils._flipMaterialSides(e),a},animationManager:new AnimationManager,drawTooltip:function(e,t=!1){if(e){let o;if(t){let t=document.createElement("div");t.className="mapboxgl-popup-content";let a=document.createElement("strong");a.innerHTML=e,t.appendChild(a);let i=document.createElement("div");i.className="mapboxgl-popup-tip";let n=document.createElement("div");n.className="marker mapboxgl-popup-anchor-bottom",n.appendChild(i),n.appendChild(t),(o=document.createElement("div")).className+="label3D",o.appendChild(n)}else(o=document.createElement("span")).className=this._defaults.tooltip.cssClass,o.innerHTML=e;return o}},drawLabelHTML:function(e,t){let o=document.createElement("div");return o.className+=t,o.innerHTML="string"==typeof e?e:e.outerHTML,o},_defaults:{colors:{red:new THREE.Color(16711680),yellow:new THREE.Color(16776960),green:new THREE.Color(65280),black:new THREE.Color(0)},materials:{boxNormalMaterial:new THREE.LineBasicMaterial({color:new THREE.Color(16711680)}),boxOverMaterial:new THREE.LineBasicMaterial({color:new THREE.Color(16776960)}),boxSelectedMaterial:new THREE.LineBasicMaterial({color:new THREE.Color(65280)})},line:{geometry:null,color:"black",width:1,opacity:1},sphere:{position:[0,0,0],radius:1,sides:20,units:"scene",material:"MeshBasicMaterial",anchor:"bottom-left"},label:{htmlElement:null,cssClass:" label3D",alwaysVisible:!1,topMargin:-.5,feature:null},tooltip:{text:"",cssClass:"toolTip text-xs",mapboxStyle:!1,topMargin:0,feature:null},tube:{geometry:null,radius:1,sides:6,units:"scene",material:"MeshBasicMaterial",anchor:"center"},extrusion:{footprint:null,base:0,top:100,color:"black",material:"MeshBasicMaterial",scaleToLatitude:!1},loadObj:{type:null,obj:null,units:"scene",scale:1,rotation:0,defaultAnimation:0,anchor:"bottom-left"},Object3D:{obj:null,units:"scene",anchor:"bottom-left"}},geometries:{line:["LineString"],tube:["LineString"],sphere:["Point"]}},module.exports=exports=Objects; +var utils=require("../utils/utils.js"),material=require("../utils/material.js");const THREE=require("../three.js"),AnimationManager=require("../animation/AnimationManager.js"),CSS2D=require("./CSS2DRenderer.js");function Objects(){}Objects.prototype={line:function(e){e=utils._validate(e,this._defaults.line);var t=utils.lnglatsToWorld(e.geometry),o=utils.normalizeVertices(t),a=utils.flattenVectors(o.vertices),i=new Float32Array(a),n=new THREE.BufferGeometry;n.setAttribute("position",new THREE.BufferAttribute(i,3));var l=new THREE.LineBasicMaterial({color:16711680,linewidth:21}),r=new THREE.Line(n,l);return r.options=options||{},r.position.copy(o.position),r},extrusion:function(e){},unenroll:function(e,t){t||this.animationManager.unenroll(e)},_addMethods:function(e,t){var o=this;if(t);else{function a(e,t,o,a){let i=utils.radify(a);e.position.sub(t),e.position.applyAxisAngle(o,i),e.position.add(t),e.rotateOnAxis(o,i),tb.map.repaint=!0}let t;e.coordinates||(e.coordinates=[0,0,0]),o.animationManager.enroll(e),e.setCoords=function(t){if("meters"===e.userData.units){var o=utils.projectedUnitsPerMeter(t[1]);o||(o=1),"number"==typeof(o=Number(o.toFixed(7)))?e.scale.set(o,o,o):e.scale.set(o.x,o.y,o.z)}return e.userData.topMargin&&e.userData.feature&&(t[2]+=(e.userData.feature.properties.height-(e.userData.feature.properties.base_height||e.userData.feature.properties.min_height||0))*e.userData.topMargin),e.coordinates=t,e.set({position:t}),e.modelHeight=e.coordinates[2],e.setBoundingBoxShadowFloor(),e},e.setTranslate=function(t){return e.set({translate:t}),e},e.setRotation=function(t){"number"==typeof t&&(t={z:t});var o={x:utils.radify(t.x)||e.rotation.x,y:utils.radify(t.y)||e.rotation.y,z:utils.radify(t.z)||e.rotation.z};e._setObject({rotation:[o.x,o.y,o.z]})},e.calculateAdjustedPosition=function(t,o,a){let i=t.slice(),n=utils.unprojectFromWorld(e.modelSize);return a?(i[0]-=0!=o.x?n[0]/o.x:0,i[1]-=0!=o.y?n[1]/o.y:0,i[2]-=0!=o.z?n[2]/o.z:0):(i[0]+=0!=o.x?n[0]/o.x:0,i[1]+=0!=o.y?n[1]/o.y:0,i[2]+=0!=o.z?n[2]/o.z:0),i},e.setRotationAxis=function(t){"number"==typeof t&&(t={z:t});let o=e.modelBox(),i=new THREE.Vector3(o.max.x,o.max.y,o.min.z);0!=t.x&&a(e,i,new THREE.Vector3(0,0,1),t.x),0!=t.y&&a(e,i,new THREE.Vector3(0,0,1),t.y),0!=t.z&&a(e,i,new THREE.Vector3(0,0,1),t.z)},Object.defineProperty(e,"boundingBox",{get:()=>e.getObjectByName("BoxModel")}),Object.defineProperty(e,"boundingBoxShadow",{get:()=>e.getObjectByName("BoxShadow")}),e.drawBoundingBox=function(){let t=e.box3(),o=new THREE.Group;o.name="BoxGrid",o.updateMatrixWorld(!0);let a=new THREE.Box3Helper(t,Objects.prototype._defaults.colors.yellow);a.name="BoxModel",o.add(a),a.layers.disable(0);let i=t.clone();i.max.z=i.min.z;let n=new THREE.Box3Helper(i,Objects.prototype._defaults.colors.black);return n.name="BoxShadow",o.add(n),n.layers.disable(0),o.visible=!1,o},e.setBoundingBoxShadowFloor=function(){e.boundingBox&&(e.boundingBoxShadow.box.max.z=-e.modelHeight,e.boundingBoxShadow.box.min.z=-e.modelHeight)},e.setAnchor=function(t){const o=e.box3(),a=(o.getSize(new THREE.Vector3),o.getCenter(new THREE.Vector3));switch(e.none={x:0,y:0,z:0},e.center={x:a.x,y:a.y,z:o.min.z},e.bottom={x:a.x,y:o.max.y,z:o.min.z},e.bottomLeft={x:o.max.x,y:o.max.y,z:o.min.z},e.bottomRight={x:o.min.x,y:o.max.y,z:o.min.z},e.top={x:a.x,y:o.min.y,z:o.min.z},e.topLeft={x:o.max.x,y:o.min.y,z:o.min.z},e.topRight={x:o.min.x,y:o.min.y,z:o.min.z},e.left={x:o.max.x,y:a.y,z:o.min.z},e.right={x:o.min.x,y:a.y,z:o.min.z},t){case"center":e.anchor=e.center;break;case"top":e.anchor=e.top;break;case"top-left":e.anchor=e.topLeft;break;case"top-right":e.anchor=e.topRight;break;case"left":e.anchor=e.left;break;case"right":e.anchor=e.right;break;case"bottom":e.anchor=e.bottom;break;case"bottom-left":default:e.anchor=e.bottomLeft;break;case"bottom-right":e.anchor=e.bottomRight;break;case"auto":case"none":e.anchor=e.none}e.model.position.set(-e.anchor.x,-e.anchor.y,-e.anchor.z)},e.setCenter=function(t){if(t&&(0!=t.x||0!=t.y||0!=t.z)){let o=e.getSize();e.anchor={x:-o.x*t.x,y:-o.y*t.y,z:-o.z*t.z},e.model.position.set(-e.anchor.x,-e.anchor.y,-e.anchor.z)}},Object.defineProperty(e,"label",{get:()=>e.getObjectByName("label")}),Object.defineProperty(e,"tooltip",{get:()=>e.getObjectByName("tooltip")}),Object.defineProperty(e,"model",{get:()=>e.getObjectByName("model")}),Object.defineProperty(e,"animations",{get:()=>t,set(e){t=e}}),Object.defineProperty(e,"visibility",{get:()=>e.visible,set(t){let o=t;if("visible"==t||1==t)o=!0,e.label&&(e.label.visible=o);else{if("none"!=t&&0!=t)return;o=!1,e.label&&e.label.alwaysVisible&&(e.label.visible=o),e.tooltip&&(e.tooltip.visible=o)}e.visible!=o&&(e.visible=o,e.model&&e.model.traverse(function(e){"Mesh"!=e.type&&"SkinnedMesh"!=e.type||(o?e.layers.enable(0):e.layers.disable(0)),"LineSegments"==e.type&&e.layers.disableAll()}))}}),e.addLabel=function(t,o=!1,a=e.anchor){t&&e.children[0].add(e.drawLabelHTML(t,o,a))},e.drawLabelHTML=function(t,a=!1,i=e.anchor){let n=o.drawLabelHTML(t,Objects.prototype._defaults.label.cssClass);const l=e.box3(),r=l.getSize(new THREE.Vector3);let s=l.max.x,d=l.max.y;l.min.z;e.label&&e.label.remove;let c=new CSS2D.CSS2DObject(n);return c.name="label",c.position.set(.5*-r.x-e.model.position.x-i.x+s,.5*-r.y-e.model.position.y-i.y+d,.5*r.z),c.visible=a,c.alwaysVisible=a,c},e.addTooltip=function(t,a=!1,i=e.anchor){if(t){let n=o.drawTooltip(t,a);const l=e.box3(),r=l.getSize(new THREE.Vector3);let s={x:l.max.x,y:l.max.y,z:l.min.z};e.tooltip&&e.tooltip.remove;let d=new CSS2D.CSS2DObject(n);d.name="tooltip",d.position.set(.5*-r.x-e.model.position.x-i.x+s.x,.5*-r.y-e.model.position.y-i.y+s.y,r.z),d.visible=!1,e.children[0].add(d)}};let i=!1;Object.defineProperty(e,"castShadow",{get:()=>i,set(t){if(i!=t){if(e.model.traverse(function(e){e.isMesh&&(e.castShadow=!0)}),t){const o=e.modelSize,a=[o.x,o.y,o.z],i=10*Math.max(...a),n=new THREE.PlaneBufferGeometry(i,i),l=new THREE.ShadowMaterial;l.opacity=.5;let r=new THREE.Mesh(n,l);r.layers.enable(1),r.layers.disable(0),r.receiveShadow=t,e.add(r)}else e.traverse(function(t){t.isMesh&&t.material instanceof THREE.ShadowMaterial&&e.remove(t)});i=t}}});let n=!1;Object.defineProperty(e,"receiveShadow",{get:()=>n,set(t){n!=t&&(e.model.traverse(function(e){e.isMesh&&(e.receiveShadow=!0)}),n=t)}});let l=!1;Object.defineProperty(e,"wireframe",{get:()=>l,set(t){l!=t&&(e.model.traverse(function(e){if("Mesh"==e.type||"SkinnedMesh"==e.type){let o=[];Array.isArray(e.material)?o=e.material:o.push(e.material),o.forEach(function(e){e.opacity=t?.5:1,e.wireframe=t}),t?(e.layers.disable(0),e.layers.enable(1)):(e.layers.disable(1),e.layers.enable(0))}"LineSegments"==e.type&&e.layers.disableAll()}),l=t,e.dispatchEvent(new CustomEvent("Wireframed",{detail:e,bubbles:!0,cancelable:!0})))}});let r=!1;Object.defineProperty(e,"selected",{get:()=>r,set(t){t?(e.boundingBox&&(e.boundingBox.material=Objects.prototype._defaults.materials.boxSelectedMaterial,e.boundingBox.parent.visible=!0,e.boundingBox.layers.enable(1),e.boundingBoxShadow.layers.enable(1)),e.label&&!e.label.alwaysVisible&&(e.label.visible=!0)):(e.boundingBox&&(e.boundingBox.parent.visible=!1,e.boundingBox.layers.disable(1),e.boundingBoxShadow.layers.disable(1),e.boundingBox.material=Objects.prototype._defaults.materials.boxNormalMaterial),e.label&&!e.label.alwaysVisible&&(e.label.visible=!1)),e.tooltip&&(e.tooltip.visible=t),r!=t&&(r=t,e.dispatchEvent(new CustomEvent("SelectedChange",{detail:e,bubbles:!0,cancelable:!0})))}});let s=!1;Object.defineProperty(e,"over",{get:()=>s,set(t){t?(e.selected||e.boundingBox&&(e.boundingBox.material=Objects.prototype._defaults.materials.boxOverMaterial,e.boundingBox.parent.visible=!0,e.boundingBox.layers.enable(1),e.boundingBoxShadow.layers.enable(1)),e.label&&!e.label.alwaysVisible&&(e.label.visible=!0),e.dispatchEvent(new CustomEvent("ObjectMouseOver",{detail:e,bubbles:!0,cancelable:!0}))):(e.selected||(e.boundingBox&&(e.boundingBox.parent.visible=!1,e.boundingBox.layers.disable(1),e.boundingBoxShadow.layers.disable(1),e.boundingBox.material=Objects.prototype._defaults.materials.boxNormalMaterial),e.label&&!e.label.alwaysVisible&&(e.label.visible=!1)),e.dispatchEvent(new CustomEvent("ObjectMouseOut",{detail:e,bubbles:!0,cancelable:!0}))),e.tooltip&&(e.tooltip.visible=t||e.selected),s=t}}),e.box3=function(){let t;if(e.updateMatrix(),e.updateMatrixWorld(!0,!0),e.model){let o=e.clone(!0),a=e.model.clone();if(t=(new THREE.Box3).setFromObject(a),e.parent){let i=new THREE.Matrix4,n=new THREE.Matrix4;e.matrix.extractRotation(i),i.getInverse(n),o.setRotationFromMatrix(n),t=(new THREE.Box3).setFromObject(a)}}return t},e.modelBox=function(){return e.box3()},e.getSize=function(){return e.box3().getSize(new THREE.Vector3(0,0,0))};let d=!1;Object.defineProperty(e,"modelSize",{get:()=>d=e.getSize(),set(e){d!=e&&(d=e)}}),e.modelHeight=0}e.add=function(t){return e.children[0].add(t),t.position.z=e.coordinates[2]?-e.coordinates[2]:0,t},e.remove=function(t){e.children[0].remove(t),tb.map.repaint=!0},e.duplicate=function(t){let a=e.clone(!0);return a.userData=t||e.userData,o._addMethods(a),a.deepCopy(e),a},e.deepCopy=function(t){return e.anchor=t.anchor,e.none={x:0,y:0,z:0},e.center=t.center,e.bottom=t.bottom,e.bottomLeft=t.bottomLeft,e.bottomRight=t.bottomRight,e.top=t.top,e.topLeft=t.topLeft,e.topRight=t.topRight,e.left=t.left,e.right=t.right,e},e.dispose=function(){Objects.prototype.unenroll(e),e.traverse(e=>{if(!e.parent||"world"!=e.parent.name){if(e.isMesh)if(e.geometry.dispose(),e.material.isMaterial)i(e.material);else for(const t of e.material)i(t);e.dispose&&e.dispose()}}),e.children=[]};const i=e=>{e.dispose();for(const t of Object.keys(e)){const o=e[t];o&&"object"==typeof o&&"minFilter"in o&&o.dispose()}let t=e;(t.map||t.alphaMap||t.aoMap||t.bumpMap||t.displacementMap||t.emissiveMap||t.envMap||t.lightMap||t.metalnessMap||t.normalMap||t.roughnessMap)&&(t.map&&t.map.dispose(),t.alphaMap&&t.alphaMap.dispose(),t.aoMap&&t.aoMap.dispose(),t.bumpMap&&t.bumpMap.dispose(),t.displacementMap&&t.displacementMap.dispose(),t.emissiveMap&&t.emissiveMap.dispose(),t.envMap&&t.envMap.dispose(),t.lightMap&&t.lightMap.dispose(),t.metalnessMap&&t.metalnessMap.dispose(),t.normalMap&&t.normalMap.dispose(),t.roughnessMap&&t.roughnessMap.dispose())};return e},_makeGroup:function(e,t){var a=new THREE.Group;if(a.userData=t||{},a.userData.isGeoGroup=!0,a.userData.feature&&(a.userData.feature.properties.uuid=a.uuid),e.length)for(o of e)a.add(o);else a.add(e);return utils._flipMaterialSides(e),a},animationManager:new AnimationManager,drawTooltip:function(e,t=!1){if(e){let o;if(t){let t=document.createElement("div");t.className="mapboxgl-popup-content";let a=document.createElement("strong");a.innerHTML=e,t.appendChild(a);let i=document.createElement("div");i.className="mapboxgl-popup-tip";let n=document.createElement("div");n.className="marker mapboxgl-popup-anchor-bottom",n.appendChild(i),n.appendChild(t),(o=document.createElement("div")).className+="label3D",o.appendChild(n)}else(o=document.createElement("span")).className=this._defaults.tooltip.cssClass,o.innerHTML=e;return o}},drawLabelHTML:function(e,t){let o=document.createElement("div");return o.className+=t,o.innerHTML="string"==typeof e?e:e.outerHTML,o},_defaults:{colors:{red:new THREE.Color(16711680),yellow:new THREE.Color(16776960),green:new THREE.Color(65280),black:new THREE.Color(0)},materials:{boxNormalMaterial:new THREE.LineBasicMaterial({color:new THREE.Color(16711680)}),boxOverMaterial:new THREE.LineBasicMaterial({color:new THREE.Color(16776960)}),boxSelectedMaterial:new THREE.LineBasicMaterial({color:new THREE.Color(65280)})},line:{geometry:null,color:"black",width:1,opacity:1},sphere:{position:[0,0,0],radius:1,sides:20,units:"scene",material:"MeshBasicMaterial",anchor:"bottom-left"},label:{htmlElement:null,cssClass:" label3D",alwaysVisible:!1,topMargin:-.5,feature:null},tooltip:{text:"",cssClass:"toolTip text-xs",mapboxStyle:!1,topMargin:0,feature:null},tube:{geometry:null,radius:1,sides:6,units:"scene",material:"MeshBasicMaterial",anchor:"center"},extrusion:{footprint:null,base:0,top:100,color:"black",material:"MeshBasicMaterial",scaleToLatitude:!1},loadObj:{type:null,obj:null,units:"scene",scale:1,rotation:0,defaultAnimation:0,anchor:"bottom-left"},Object3D:{obj:null,units:"scene",anchor:"bottom-left"}},geometries:{line:["LineString"],tube:["LineString"],sphere:["Point"]}},module.exports=exports=Objects; },{"../animation/AnimationManager.js":3,"../three.js":22,"../utils/material.js":24,"../utils/utils.js":26,"./CSS2DRenderer.js":5}],19:[function(require,module,exports){ var utils=require("../utils/utils.js"),material=require("../utils/material.js"),Objects=require("./objects.js"),Object3D=require("./Object3D.js");function Sphere(e){e=utils._validate(e,Objects.prototype._defaults.sphere);var t=new THREE.SphereBufferGeometry(e.radius,e.sides,e.sides),r=material(e),s=new THREE.Mesh(t,r);return new Object3D({obj:s,units:e.units,anchor:e.anchor,adjustment:e.adjustment})}module.exports=exports=Sphere; },{"../utils/material.js":24,"../utils/utils.js":26,"./Object3D.js":7,"./objects.js":18}],20:[function(require,module,exports){ -const utils=require("../utils/utils.js"),Objects=require("./objects.js"),CSS2D=require("./CSS2DRenderer.js");function Tooltip(t){if((t=utils._validate(t,Objects.prototype._defaults.tooltip)).text){let o=Objects.prototype.drawTooltip(t.text,t.mapboxStyle),r=new CSS2D.CSS2DObject(o);r.visible=!1;var e=Objects.prototype._makeGroup(r,t);return Objects.prototype._addMethods(e),e.tooltip=r,e}}module.exports=exports=Tooltip; +const utils=require("../utils/utils.js"),Objects=require("./objects.js"),CSS2D=require("./CSS2DRenderer.js");function Tooltip(t){if((t=utils._validate(t,Objects.prototype._defaults.tooltip)).text){let o=Objects.prototype.drawTooltip(t.text,t.mapboxStyle),r=new CSS2D.CSS2DObject(o);r.visible=!1,r.name="tooltip";var e=Objects.prototype._makeGroup(r,t);return Objects.prototype._addMethods(e),e}}module.exports=exports=Tooltip; },{"../utils/utils.js":26,"./CSS2DRenderer.js":5,"./objects.js":18}],21:[function(require,module,exports){ var utils=require("../utils/utils.js"),material=require("../utils/material.js"),Objects=require("./objects.js"),THREE=require("../three.js"),Object3D=require("./Object3D.js");function tube(e,t){e=utils._validate(e,Objects.prototype._defaults.tube);var r=utils.lnglatsToWorld(e.geometry),i=utils.normalizeVertices(r),s=tube.prototype.defineCrossSection(e),o=tube.prototype.buildVertices(s,i.vertices,t),a=tube.prototype.buildFaces(o,i.vertices,e),n=material(e),u=new THREE.Mesh(a,n);return new Object3D({obj:u,units:e.units,anchor:e.anchor,adjustment:e.adjustment})}tube.prototype={buildVertices:function(e,t,r){var s=new THREE.PlaneBufferGeometry(99999999999,9999999999),o=new THREE.MeshBasicMaterial({color:16777215,side:THREE.DoubleSide});o.opacity=0;var a=new THREE.Mesh(s,o),n=new THREE.Geometry,u=!1,c=[t[0].clone().normalize()];for(i in t){var l;i=parseFloat(i),t[i+1]&&(l=(new THREE.Vector3).subVectors(t[i+1],t[i]).normalize()),c.push(l)}for(i in c.push(new THREE.Vector3),t){i=parseFloat(i);var E=t[i],d=c[i],p=c[i+1],f=d.clone().add(p).normalize();if(0===i?f=p:i===t.length-1&&(f=d),u){v=[];a.position.copy(E),a.lookAt(f.clone().add(E)),a.updateMatrixWorld(),u.forEach(function(e){var t=new THREE.Raycaster(e,d).intersectObject(a)[0];t?(n.vertices.push(t.point),v.push(t.point)):console.error("Tube geometry failed at vertex "+i+". Consider reducing tube radius, or smoothening out the sharp angle at this vertex")}),u=v}else{var v;(v=e.clone()).lookAt(f),v.vertices.forEach(function(e){n.vertices.push(e.add(E))}),u=v.vertices}}return r.remove(a),n},defineCrossSection:function(e){for(var t=new THREE.Geometry,r=e.sides,i=0;i + Threebox performance test @@ -64,7 +64,7 @@ } ); - + import Stats from 'https://threejs.org/examples/jsm/libs/stats.module.js'; map.on('style.load', function () { @@ -106,6 +106,8 @@ count: 100 }; + var model = { obj: 'models/windmill_a/windmill_a.json', type: 'gltf', scale: 0.1 }; + function initMesh() { let diff = api.count - tb.world.children.length; @@ -116,7 +118,6 @@ console.time("(clear)"); for (let j = tb.world.children.length - 1; j >= api.count; j--) { var obj = tb.world.children[j]; - if (obj.dispose) obj.dispose(); tb.remove(obj); } getGeometryTotalLength(); @@ -126,22 +127,23 @@ } else { var options = { - obj: 'models/windmill_a/windmill_a.json', - type: 'gltf', - scale: 0.1, + obj: model.obj, + type: model.type, + scale: model.scale, units: 'meters', rotation: { x: 90, y: 90, z: 0 }, anchor: 'center' } - makeNaive(options, diff); + if (!processing) makeNaive(options, diff); } } + let processing = false; function makeNaive(options, diff) { - + processing = true; let j = 0; for (var i = 0; i < diff; i++) { @@ -158,14 +160,16 @@ let lng = origin[0] + Math.random() * 0.4 - 0.2; let lat = origin[1] + Math.random() * 0.4 - 0.2; let alt = origin[2] + Math.random() * 0.4 - 0.2; - let pdu = model.setCoords([lng, lat, alt]); - tb.add(pdu); + let obj = model.setCoords([lng, lat, alt]); + tb.add(obj); getGeometryTotalLength(); - if (j == diff) + if (j == diff) { console.timeEnd(api.method + ' (build)'); - + console.log("Items: " + tb.world.children.length); + processing = false; + } }) } @@ -178,7 +182,7 @@ gui = new GUI(); gui.add(api, 'method', Method).onChange(initMesh); - gui.add(api, 'count', 1, 1000).step(10).onChange(initMesh); + gui.add(api, 'count', 0, 1000).step(10).onChange(initMesh); var perfFolder = gui.addFolder('Performance'); diff --git a/src/Animation/AnimationManager.js b/src/Animation/AnimationManager.js index 8351b0de..356f2c3a 100755 --- a/src/Animation/AnimationManager.js +++ b/src/Animation/AnimationManager.js @@ -13,6 +13,10 @@ function AnimationManager(map) { AnimationManager.prototype = { + unenroll: function (obj) { + this.enrolledObjects.splice(this.enrolledObjects.indexOf(obj), 1); + }, + enroll: function (obj) { //[jscastro] add the object default animations @@ -71,7 +75,6 @@ AnimationManager.prototype = { }) /* Extend the provided object with animation-specific properties and track in the animation manager */ - this.enrolledObjects.push(obj); // Give this object its own internal animation queue @@ -128,7 +131,7 @@ AnimationManager.prototype = { this.animationQueue .push(entry); - map.repaint = true; + tb.map.repaint = true; } //if no duration set, stop object's existing animations and go to that state immediately @@ -178,7 +181,7 @@ AnimationManager.prototype = { this.animationQueue .push(entry); - map.repaint = true; + tb.map.repaint = true; return this; }; @@ -215,7 +218,7 @@ AnimationManager.prototype = { if (w) this.position.copy(w); this.updateMatrixWorld(); - map.repaint = true + tb.map.repaint = true }; //[jscastro] play default animation @@ -238,7 +241,7 @@ AnimationManager.prototype = { this.animationQueue .push(entry); - map.repaint = true + tb.map.repaint = true return this; } } @@ -298,7 +301,7 @@ AnimationManager.prototype = { // Update the animation mixer and render this frame obj.mixer.update(0.01); } - map.repaint = true; + tb.map.repaint = true; return this; } @@ -413,7 +416,7 @@ AnimationManager.prototype = { object.isPlaying = true; object.animationMethod = requestAnimationFrame(this.update); object.mixer.update(object.clock.getDelta()); - map.repaint = true; + tb.map.repaint = true; } } diff --git a/src/Threebox.js b/src/Threebox.js index 781115ca..f601eb0b 100644 --- a/src/Threebox.js +++ b/src/Threebox.js @@ -48,7 +48,7 @@ Threebox.prototype = { this.map = map; this.map.tb = this; //[jscastro] needed if we want to queryRenderedFeatures from map.onload - this.objects = new Objects(this.map); + this.objects = new Objects(); // Set up a THREE.js scene this.renderer = new THREE.WebGLRenderer({ @@ -81,7 +81,7 @@ Threebox.prototype = { this.scene.add(this.world); this.objectsCache = new Map(); - + this.cameraSync = new CameraSync(this.map, this.camera, this.world); //raycaster for mouse events @@ -485,7 +485,7 @@ Threebox.prototype = { cache.promise .then(obj => { //console.log("Cloning " + options.obj); - cb(obj.duplicate()); + cb(obj.duplicate(options)); }) .catch(err => { this.objectsCache.delete(options.obj); @@ -498,7 +498,7 @@ Threebox.prototype = { loader(options, cb, function (obj) { //console.log("Loading " + options.obj); if (obj.duplicate) { - resolve(obj); + resolve(obj.duplicate()); } else { reject(obj); } @@ -675,43 +675,9 @@ Threebox.prototype = { }, remove: function (obj) { - //[jscastro] remove also the label if exists dispatching the event removed to fire CSS2DRenderer "removed" listener - if (obj.label) { obj.label.remove() }; - if (obj.tooltip) { obj.tooltip.remove() }; - obj.traverse(function (o) { - if (o.isMesh) { - o.geometry.dispose(); - if (o.material) { - if (o.material instanceof THREE.MeshFaceMaterial) { - o.material.materials.forEach(function (m) { - m.dispose(); - if (m.map) { - m.map.dispose(); - } - }); - } else { - o.material.dispose(); - } - let m = o.material; - let md = (m.map || m.alphaMap || m.aoMap || m.bumpMap || m.displacementMap || m.emissiveMap || m.envMap || m.lightMap || m.metalnessMap || m.normalMap || m.roughnessMap) - if (md) { - if (m.map) m.map.dispose(); - if (m.alphaMap) m.alphaMap.dispose(); - if (m.aoMap) m.aoMap.dispose(); - if (m.bumpMap) m.bumpMap.dispose(); - if (m.displacementMap) m.displacementMap.dispose(); - if (m.emissiveMap) m.emissiveMap.dispose(); - if (m.envMap) m.envMap.dispose(); - if (m.lightMap) m.lightMap.dispose(); - if (m.metalnessMap) m.metalnessMap.dispose(); - if (m.normalMap) m.normalMap.dispose(); - if (m.roughnessMap) m.roughnessMap.dispose(); - } - } - } - if (o.dispose) o.dispose(); - }) + obj.dispose() this.world.remove(obj); + obj = null; }, //[jscastro] this clears tb.world in order to dispose properly the resources @@ -725,7 +691,6 @@ Threebox.prototype = { let obj = objects[i]; //if layerId, check the layer to remove, otherwise always remove if ((layerId && obj.userData.feature.layer === layerId) || !layerId) { - if (dispose) obj.dispose(); this.remove(obj); } } @@ -888,7 +853,7 @@ Threebox.prototype = { programs: function () { return this.renderer.info.programs.length }, - version: '2.0.6', + version: '2.0.7', } diff --git a/src/objects/CSS2DRenderer.js b/src/objects/CSS2DRenderer.js index f81bddb2..25e8ce13 100644 --- a/src/objects/CSS2DRenderer.js +++ b/src/objects/CSS2DRenderer.js @@ -16,6 +16,8 @@ THREE.CSS2DObject = function (element) { this.dispose = function () { this.remove(); + this.element = null; + if (this.parent) this.parent.remove(this); } this.remove = function () { diff --git a/src/objects/Object3D.js b/src/objects/Object3D.js index 3610076d..06dfc104 100644 --- a/src/objects/Object3D.js +++ b/src/objects/Object3D.js @@ -9,8 +9,8 @@ function Object3D(options) { var projScaleGroup = new THREE.Group(); projScaleGroup.add(obj); var userScaleGroup = Objects.prototype._makeGroup(projScaleGroup, options); - - userScaleGroup.model = options.obj; + options.obj.name = "model"; + //userScaleGroup.model = options.obj; Objects.prototype._addMethods(userScaleGroup); //[jscastro] calculate automatically the pivotal center of the object diff --git a/src/objects/label.js b/src/objects/label.js index e2e269e3..19416f94 100644 --- a/src/objects/label.js +++ b/src/objects/label.js @@ -9,12 +9,12 @@ function Label(obj) { let div = Objects.prototype.drawLabelHTML(obj.htmlElement, obj.cssClass); let label = new THREE.CSS2DObject(div); + label.name = "label"; label.visible = obj.alwaysVisible; label.alwaysVisible = obj.alwaysVisible; var userScaleGroup = Objects.prototype._makeGroup(label, obj); Objects.prototype._addMethods(userScaleGroup); - userScaleGroup.label = label; userScaleGroup.visibility = obj.alwaysVisible; return userScaleGroup; diff --git a/src/objects/loadObj.js b/src/objects/loadObj.js index 02f8fa41..2814fa6c 100644 --- a/src/objects/loadObj.js +++ b/src/objects/loadObj.js @@ -59,7 +59,7 @@ function loadObj(options, cb, promise) { loader.load(options.obj, obj => { //[jscastro] MTL/GLTF/FBX models have a different structure - let animations; + let animations = []; switch (options.type) { case "mtl": obj = obj.children[0]; @@ -73,7 +73,7 @@ function loadObj(options, cb, promise) { animations = obj.animations; break; } - + obj.animations = animations; // [jscastro] options.rotation was wrongly used var r = utils.types.rotation(options.rotation, [0, 0, 0]); var s = utils.types.scale(options.scale, [1, 1, 1]); @@ -81,11 +81,12 @@ function loadObj(options, cb, promise) { obj.scale.set(s[0], s[1], s[2]); // [jscastro] normalize specular/metalness/shininess from meshes in FBX and GLB model as it would need 5 lights to illuminate them properly if (options.normalize) { normalizeSpecular(obj); } - + obj.name = "model"; var projScaleGroup = new THREE.Group(); + projScaleGroup.name = "group"; projScaleGroup.add(obj) var userScaleGroup = Objects.prototype._makeGroup(projScaleGroup, options); - userScaleGroup.model = obj; + userScaleGroup.name = "object"; //[jscastro] assign the animations to the userScaleGroup before enrolling it in AnimationsManager through _addMethods userScaleGroup.animations = animations; @@ -95,6 +96,8 @@ function loadObj(options, cb, promise) { //[jscastro] override the center calculated if the object has adjustments userScaleGroup.setCenter(options.adjustment); + let anim = userScaleGroup.animations; + // [jscastro] after adding methods create the bounding box at userScaleGroup but add it to its children for positioning let boxGrid = userScaleGroup.drawBoundingBox(); projScaleGroup.add(boxGrid); diff --git a/src/objects/objects.js b/src/objects/objects.js index ac0794f1..488b4bda 100644 --- a/src/objects/objects.js +++ b/src/objects/objects.js @@ -47,6 +47,21 @@ Objects.prototype = { }, + unenroll: function (obj, isStatic) { + var root = this; + + if (isStatic) { + + } + + else { + // Bestow this mesh with animation superpowers and keeps track of its movements in the global animation queue + root.animationManager.unenroll(obj); + + } + + }, + _addMethods: function (obj, isStatic) { var root = this; @@ -157,24 +172,22 @@ Objects.prototype = { model.position.add(point); // re-add the offset model.rotateOnAxis(axis, theta) - map.repaint = true; + tb.map.repaint = true; } let _boundingBox; //[jscastro] added property for boundingBox helper Object.defineProperty(obj, 'boundingBox', { - get() { return _boundingBox; }, - set(value) { - _boundingBox = value; + get() { + return obj.getObjectByName("BoxModel"); } }) let _boundingBoxShadow; //[jscastro] added property for boundingBox helper Object.defineProperty(obj, 'boundingBoxShadow', { - get() { return _boundingBoxShadow; }, - set(value) { - _boundingBoxShadow = value; + get() { + return obj.getObjectByName("BoxShadow"); } }) @@ -190,7 +203,7 @@ Objects.prototype = { boxModel.name = "BoxModel"; boxGrid.add(boxModel); boxModel.layers.disable(0); // it makes the object invisible for the raycaster - obj.boundingBox = boxModel; + //obj.boundingBox = boxModel; //it needs to clone, to avoid changing the object by reference let bb2 = bb.clone(); @@ -201,7 +214,7 @@ Objects.prototype = { boxGrid.add(boxShadow); boxShadow.layers.disable(0); // it makes the object invisible for the raycaster - obj.boundingBoxShadow = boxShadow; + //obj.boundingBoxShadow = boxShadow; boxGrid.visible = false; // visibility is managed from the parent return boxGrid; @@ -280,21 +293,27 @@ Objects.prototype = { } let _label; - //[jscastro] added property for wireframes state + //[jscastro] added property for simulated label Object.defineProperty(obj, 'label', { - get() { return _label; }, - set(value) { - _label = value; - } + get() { return obj.getObjectByName("label"); } }); let _tooltip; //[jscastro] added property for simulated tooltip Object.defineProperty(obj, 'tooltip', { - get() { return _tooltip; }, - set(value) { - _tooltip = value; - } + get() { return obj.getObjectByName("tooltip"); } + }); + + //[jscastro] added property for the internal 3D model + Object.defineProperty(obj, 'model', { + get() { return obj.getObjectByName("model"); } + }); + + let _animations; + //[jscastro] added property for the internal 3D model + Object.defineProperty(obj, 'animations', { + get() { return _animations}, + set(value) { _animations = value} }); //[jscastro] added property to redefine visible, including the label and tooltip @@ -348,13 +367,14 @@ Objects.prototype = { const box = obj.box3(); const size = box.getSize(new THREE.Vector3()); let bottomLeft = { x: box.max.x, y: box.max.y, z: box.min.z }; - if (obj.label) { obj.label.remove; obj.label = null; } - obj.label = new CSS2D.CSS2DObject(div); - obj.label.position.set(((-size.x * 0.5) - obj.model.position.x - center.x + bottomLeft.x), ((-size.y * 0.5) - obj.model.position.y - center.y + bottomLeft.y), size.z * 0.5); //middle-centered - obj.label.visible = visible; - obj.label.alwaysVisible = visible; - - return obj.label; + if (obj.label) { obj.label.remove; } + let label = new CSS2D.CSS2DObject(div); + label.name = "label"; + label.position.set(((-size.x * 0.5) - obj.model.position.x - center.x + bottomLeft.x), ((-size.y * 0.5) - obj.model.position.y - center.y + bottomLeft.y), size.z * 0.5); //middle-centered + label.visible = visible; + label.alwaysVisible = visible; + + return label; } //[jscastro] add tooltip method @@ -364,12 +384,13 @@ Objects.prototype = { const box = obj.box3(); const size = box.getSize(new THREE.Vector3()); let bottomLeft = { x: box.max.x, y: box.max.y, z: box.min.z }; - if (obj.tooltip) { obj.tooltip.remove; obj.tooltip = null; } - obj.tooltip = new CSS2D.CSS2DObject(divToolTip); - obj.tooltip.position.set(((-size.x * 0.5) - obj.model.position.x - center.x + bottomLeft.x), ((-size.y * 0.5) - obj.model.position.y - center.y + bottomLeft.y), size.z); //top-centered - obj.tooltip.visible = false; //only visible on mouseover or selected + if (obj.tooltip) { obj.tooltip.remove; } + let tooltip = new CSS2D.CSS2DObject(divToolTip); + tooltip.name = "tooltip"; + tooltip.position.set(((-size.x * 0.5) - obj.model.position.x - center.x + bottomLeft.x), ((-size.y * 0.5) - obj.model.position.y - center.y + bottomLeft.y), size.z); //top-centered + tooltip.visible = false; //only visible on mouseover or selected //we add it to the first children to get same boxing and position - obj.children[0].add(obj.tooltip); + obj.children[0].add(tooltip); } } @@ -535,9 +556,9 @@ Objects.prototype = { if (obj.model) { //let's clone the object before manipulate it let dup = obj.clone(true); - dup.model = obj.model.clone(); + let model = obj.model.clone(); //get the size of the model because the object is translated and has boundingBoxShadow - bounds = new THREE.Box3().setFromObject(dup.model); + bounds = new THREE.Box3().setFromObject(model); //if the object has parent it's already in the added to world so it's scaled and it could be rotated if (obj.parent) { //first, we return the object to it's original position of rotation, extract rotation and apply inversed @@ -547,7 +568,7 @@ Objects.prototype = { rm.getInverse(rmi); dup.setRotationFromMatrix(rmi); //now the object inside will give us a NAABB Non-Axes Aligned Bounding Box - bounds = new THREE.Box3().setFromObject(dup.model); + bounds = new THREE.Box3().setFromObject(model); } } return bounds; @@ -595,11 +616,9 @@ Objects.prototype = { } //[jscastro] clone + assigning all the attributes - obj.duplicate = function () { - var dupe = obj.clone(true); - dupe.userData = obj.userData; - dupe.model = dupe.children[0].children[0]; - dupe.animations = dupe.model.animations; + obj.duplicate = function (options) { + let dupe = obj.clone(true); + dupe.userData = options || obj.userData; root._addMethods(dupe); dupe.deepCopy(obj); @@ -609,40 +628,44 @@ Objects.prototype = { obj.deepCopy = function (o) { obj.anchor = o.anchor; + obj.none = { x: 0, y: 0, z: 0 }; + obj.center = o.center; obj.bottom = o.bottom; obj.bottomLeft = o.bottomLeft; obj.bottomRight = o.bottomRight; - obj.center = o.center; - obj.left = o.left; - obj.right = o.right; obj.top = o.top; obj.topLeft = o.topLeft; obj.topRight = o.topRight; - obj.boundingBox = obj.children[0].children[1].children[0]; - obj.boundingBoxShadow = obj.children[0].children[1].children[1]; - obj.tooltip = obj.children[0].children[2]; + obj.left = o.left; + obj.right = o.right; return obj; } obj.dispose = function () { - obj.traverse(object => { - if (!object.isMesh) return - //console.log('dispose geometry!') - object.geometry.dispose() + Objects.prototype.unenroll(obj); - if (object.material.isMaterial) { - cleanMaterial(object.material) - } else { - // an array of materials - for (const material of object.material) cleanMaterial(material) + obj.traverse(o => { + //don't dispose th object itself as it will be recursive + if (o.parent && o.parent.name == "world") return; + if (o.isMesh) { + //console.log('dispose geometry!') + o.geometry.dispose(); + + if (o.material.isMaterial) { + cleanMaterial(o.material) + } else { + // an array of materials + for (const material of o.material) cleanMaterial(material) + } } + if (o.dispose) o.dispose(); + }) - if (obj.label) { obj.label.dispose() }; - if (obj.tooltip) { obj.tooltip.dispose() }; - if (obj.model) { obj.model = {} }; + obj.children = []; + } const cleanMaterial = material => { diff --git a/src/objects/tooltip.js b/src/objects/tooltip.js index e14c4094..4d0b3c02 100644 --- a/src/objects/tooltip.js +++ b/src/objects/tooltip.js @@ -12,10 +12,9 @@ function Tooltip(obj) { let tooltip = new CSS2D.CSS2DObject(divToolTip); tooltip.visible = false; + tooltip.name = "tooltip"; var userScaleGroup = Objects.prototype._makeGroup(tooltip, obj); Objects.prototype._addMethods(userScaleGroup); - userScaleGroup.tooltip = tooltip; - return userScaleGroup; } diff --git a/tests/threebox-tests-bundle.js b/tests/threebox-tests-bundle.js index c5ffd7b0..f116e104 100644 --- a/tests/threebox-tests-bundle.js +++ b/tests/threebox-tests-bundle.js @@ -8992,7 +8992,7 @@ Threebox.prototype = { this.map = map; this.map.tb = this; //[jscastro] needed if we want to queryRenderedFeatures from map.onload - this.objects = new Objects(this.map); + this.objects = new Objects(); // Set up a THREE.js scene this.renderer = new THREE.WebGLRenderer({ @@ -9025,8 +9025,7 @@ Threebox.prototype = { this.scene.add(this.world); this.objectsCache = new Map(); - this.primises = new Map(); - + this.cameraSync = new CameraSync(this.map, this.camera, this.world); //raycaster for mouse events @@ -9430,7 +9429,7 @@ Threebox.prototype = { cache.promise .then(obj => { //console.log("Cloning " + options.obj); - cb(obj.duplicate()); + cb(obj.duplicate(options)); }) .catch(err => { this.objectsCache.delete(options.obj); @@ -9443,7 +9442,7 @@ Threebox.prototype = { loader(options, cb, function (obj) { //console.log("Loading " + options.obj); if (obj.duplicate) { - resolve(obj); + resolve(obj.duplicate()); } else { reject(obj); } @@ -9620,43 +9619,9 @@ Threebox.prototype = { }, remove: function (obj) { - //[jscastro] remove also the label if exists dispatching the event removed to fire CSS2DRenderer "removed" listener - if (obj.label) { obj.label.remove() }; - if (obj.tooltip) { obj.tooltip.remove() }; - obj.traverse(function (o) { - if (o.isMesh) { - o.geometry.dispose(); - if (o.material) { - if (o.material instanceof THREE.MeshFaceMaterial) { - o.material.materials.forEach(function (m) { - m.dispose(); - if (m.map) { - m.map.dispose(); - } - }); - } else { - o.material.dispose(); - } - let m = o.material; - let md = (m.map || m.alphaMap || m.aoMap || m.bumpMap || m.displacementMap || m.emissiveMap || m.envMap || m.lightMap || m.metalnessMap || m.normalMap || m.roughnessMap) - if (md) { - if (m.map) m.map.dispose(); - if (m.alphaMap) m.alphaMap.dispose(); - if (m.aoMap) m.aoMap.dispose(); - if (m.bumpMap) m.bumpMap.dispose(); - if (m.displacementMap) m.displacementMap.dispose(); - if (m.emissiveMap) m.emissiveMap.dispose(); - if (m.envMap) m.envMap.dispose(); - if (m.lightMap) m.lightMap.dispose(); - if (m.metalnessMap) m.metalnessMap.dispose(); - if (m.normalMap) m.normalMap.dispose(); - if (m.roughnessMap) m.roughnessMap.dispose(); - } - } - } - if (o.dispose) o.dispose(); - }) + obj.dispose() this.world.remove(obj); + obj = null; }, //[jscastro] this clears tb.world in order to dispose properly the resources @@ -9670,7 +9635,6 @@ Threebox.prototype = { let obj = objects[i]; //if layerId, check the layer to remove, otherwise always remove if ((layerId && obj.userData.feature.layer === layerId) || !layerId) { - if (dispose) obj.dispose(); this.remove(obj); } } @@ -9833,7 +9797,7 @@ Threebox.prototype = { programs: function () { return this.renderer.info.programs.length }, - version: '2.0.6', + version: '2.0.7', } @@ -9866,6 +9830,10 @@ function AnimationManager(map) { AnimationManager.prototype = { + unenroll: function (obj) { + this.enrolledObjects.splice(this.enrolledObjects.indexOf(obj), 1); + }, + enroll: function (obj) { //[jscastro] add the object default animations @@ -9924,7 +9892,6 @@ AnimationManager.prototype = { }) /* Extend the provided object with animation-specific properties and track in the animation manager */ - this.enrolledObjects.push(obj); // Give this object its own internal animation queue @@ -9981,7 +9948,7 @@ AnimationManager.prototype = { this.animationQueue .push(entry); - map.repaint = true; + tb.map.repaint = true; } //if no duration set, stop object's existing animations and go to that state immediately @@ -10031,7 +9998,7 @@ AnimationManager.prototype = { this.animationQueue .push(entry); - map.repaint = true; + tb.map.repaint = true; return this; }; @@ -10068,7 +10035,7 @@ AnimationManager.prototype = { if (w) this.position.copy(w); this.updateMatrixWorld(); - map.repaint = true + tb.map.repaint = true }; //[jscastro] play default animation @@ -10091,7 +10058,7 @@ AnimationManager.prototype = { this.animationQueue .push(entry); - map.repaint = true + tb.map.repaint = true return this; } } @@ -10151,7 +10118,7 @@ AnimationManager.prototype = { // Update the animation mixer and render this frame obj.mixer.update(0.01); } - map.repaint = true; + tb.map.repaint = true; return this; } @@ -10266,7 +10233,7 @@ AnimationManager.prototype = { object.isPlaying = true; object.animationMethod = requestAnimationFrame(this.update); object.mixer.update(object.clock.getDelta()); - map.repaint = true; + tb.map.repaint = true; } } @@ -10430,6 +10397,8 @@ THREE.CSS2DObject = function (element) { this.dispose = function () { this.remove(); + this.element = null; + if (this.parent) this.parent.remove(this); } this.remove = function () { @@ -10750,8 +10719,8 @@ function Object3D(options) { var projScaleGroup = new THREE.Group(); projScaleGroup.add(obj); var userScaleGroup = Objects.prototype._makeGroup(projScaleGroup, options); - - userScaleGroup.model = options.obj; + options.obj.name = "model"; + //userScaleGroup.model = options.obj; Objects.prototype._addMethods(userScaleGroup); //[jscastro] calculate automatically the pivotal center of the object @@ -10929,12 +10898,12 @@ function Label(obj) { let div = Objects.prototype.drawLabelHTML(obj.htmlElement, obj.cssClass); let label = new THREE.CSS2DObject(div); + label.name = "label"; label.visible = obj.alwaysVisible; label.alwaysVisible = obj.alwaysVisible; var userScaleGroup = Objects.prototype._makeGroup(label, obj); Objects.prototype._addMethods(userScaleGroup); - userScaleGroup.label = label; userScaleGroup.visibility = obj.alwaysVisible; return userScaleGroup; @@ -11999,7 +11968,7 @@ function loadObj(options, cb, promise) { loader.load(options.obj, obj => { //[jscastro] MTL/GLTF/FBX models have a different structure - let animations; + let animations = []; switch (options.type) { case "mtl": obj = obj.children[0]; @@ -12013,7 +11982,7 @@ function loadObj(options, cb, promise) { animations = obj.animations; break; } - + obj.animations = animations; // [jscastro] options.rotation was wrongly used var r = utils.types.rotation(options.rotation, [0, 0, 0]); var s = utils.types.scale(options.scale, [1, 1, 1]); @@ -12021,11 +11990,12 @@ function loadObj(options, cb, promise) { obj.scale.set(s[0], s[1], s[2]); // [jscastro] normalize specular/metalness/shininess from meshes in FBX and GLB model as it would need 5 lights to illuminate them properly if (options.normalize) { normalizeSpecular(obj); } - + obj.name = "model"; var projScaleGroup = new THREE.Group(); + projScaleGroup.name = "group"; projScaleGroup.add(obj) var userScaleGroup = Objects.prototype._makeGroup(projScaleGroup, options); - userScaleGroup.model = obj; + userScaleGroup.name = "object"; //[jscastro] assign the animations to the userScaleGroup before enrolling it in AnimationsManager through _addMethods userScaleGroup.animations = animations; @@ -12035,6 +12005,8 @@ function loadObj(options, cb, promise) { //[jscastro] override the center calculated if the object has adjustments userScaleGroup.setCenter(options.adjustment); + let anim = userScaleGroup.animations; + // [jscastro] after adding methods create the bounding box at userScaleGroup but add it to its children for positioning let boxGrid = userScaleGroup.drawBoundingBox(); projScaleGroup.add(boxGrid); @@ -25138,6 +25110,21 @@ Objects.prototype = { }, + unenroll: function (obj, isStatic) { + var root = this; + + if (isStatic) { + + } + + else { + // Bestow this mesh with animation superpowers and keeps track of its movements in the global animation queue + root.animationManager.unenroll(obj); + + } + + }, + _addMethods: function (obj, isStatic) { var root = this; @@ -25248,24 +25235,22 @@ Objects.prototype = { model.position.add(point); // re-add the offset model.rotateOnAxis(axis, theta) - map.repaint = true; + tb.map.repaint = true; } let _boundingBox; //[jscastro] added property for boundingBox helper Object.defineProperty(obj, 'boundingBox', { - get() { return _boundingBox; }, - set(value) { - _boundingBox = value; + get() { + return obj.getObjectByName("BoxModel"); } }) let _boundingBoxShadow; //[jscastro] added property for boundingBox helper Object.defineProperty(obj, 'boundingBoxShadow', { - get() { return _boundingBoxShadow; }, - set(value) { - _boundingBoxShadow = value; + get() { + return obj.getObjectByName("BoxShadow"); } }) @@ -25281,7 +25266,7 @@ Objects.prototype = { boxModel.name = "BoxModel"; boxGrid.add(boxModel); boxModel.layers.disable(0); // it makes the object invisible for the raycaster - obj.boundingBox = boxModel; + //obj.boundingBox = boxModel; //it needs to clone, to avoid changing the object by reference let bb2 = bb.clone(); @@ -25292,7 +25277,7 @@ Objects.prototype = { boxGrid.add(boxShadow); boxShadow.layers.disable(0); // it makes the object invisible for the raycaster - obj.boundingBoxShadow = boxShadow; + //obj.boundingBoxShadow = boxShadow; boxGrid.visible = false; // visibility is managed from the parent return boxGrid; @@ -25371,21 +25356,27 @@ Objects.prototype = { } let _label; - //[jscastro] added property for wireframes state + //[jscastro] added property for simulated label Object.defineProperty(obj, 'label', { - get() { return _label; }, - set(value) { - _label = value; - } + get() { return obj.getObjectByName("label"); } }); let _tooltip; //[jscastro] added property for simulated tooltip Object.defineProperty(obj, 'tooltip', { - get() { return _tooltip; }, - set(value) { - _tooltip = value; - } + get() { return obj.getObjectByName("tooltip"); } + }); + + //[jscastro] added property for the internal 3D model + Object.defineProperty(obj, 'model', { + get() { return obj.getObjectByName("model"); } + }); + + let _animations; + //[jscastro] added property for the internal 3D model + Object.defineProperty(obj, 'animations', { + get() { return _animations}, + set(value) { _animations = value} }); //[jscastro] added property to redefine visible, including the label and tooltip @@ -25439,13 +25430,14 @@ Objects.prototype = { const box = obj.box3(); const size = box.getSize(new THREE.Vector3()); let bottomLeft = { x: box.max.x, y: box.max.y, z: box.min.z }; - if (obj.label) { obj.label.remove; obj.label = null; } - obj.label = new CSS2D.CSS2DObject(div); - obj.label.position.set(((-size.x * 0.5) - obj.model.position.x - center.x + bottomLeft.x), ((-size.y * 0.5) - obj.model.position.y - center.y + bottomLeft.y), size.z * 0.5); //middle-centered - obj.label.visible = visible; - obj.label.alwaysVisible = visible; + if (obj.label) { obj.label.remove; } + let label = new CSS2D.CSS2DObject(div); + label.name = "label"; + label.position.set(((-size.x * 0.5) - obj.model.position.x - center.x + bottomLeft.x), ((-size.y * 0.5) - obj.model.position.y - center.y + bottomLeft.y), size.z * 0.5); //middle-centered + label.visible = visible; + label.alwaysVisible = visible; - return obj.label; + return label; } //[jscastro] add tooltip method @@ -25455,12 +25447,13 @@ Objects.prototype = { const box = obj.box3(); const size = box.getSize(new THREE.Vector3()); let bottomLeft = { x: box.max.x, y: box.max.y, z: box.min.z }; - if (obj.tooltip) { obj.tooltip.remove; obj.tooltip = null; } - obj.tooltip = new CSS2D.CSS2DObject(divToolTip); - obj.tooltip.position.set(((-size.x * 0.5) - obj.model.position.x - center.x + bottomLeft.x), ((-size.y * 0.5) - obj.model.position.y - center.y + bottomLeft.y), size.z); //top-centered - obj.tooltip.visible = false; //only visible on mouseover or selected + if (obj.tooltip) { obj.tooltip.remove; } + let tooltip = new CSS2D.CSS2DObject(divToolTip); + tooltip.name = "tooltip"; + tooltip.position.set(((-size.x * 0.5) - obj.model.position.x - center.x + bottomLeft.x), ((-size.y * 0.5) - obj.model.position.y - center.y + bottomLeft.y), size.z); //top-centered + tooltip.visible = false; //only visible on mouseover or selected //we add it to the first children to get same boxing and position - obj.children[0].add(obj.tooltip); + obj.children[0].add(tooltip); } } @@ -25626,9 +25619,9 @@ Objects.prototype = { if (obj.model) { //let's clone the object before manipulate it let dup = obj.clone(true); - dup.model = obj.model.clone(); + let model = obj.model.clone(); //get the size of the model because the object is translated and has boundingBoxShadow - bounds = new THREE.Box3().setFromObject(dup.model); + bounds = new THREE.Box3().setFromObject(model); //if the object has parent it's already in the added to world so it's scaled and it could be rotated if (obj.parent) { //first, we return the object to it's original position of rotation, extract rotation and apply inversed @@ -25638,7 +25631,7 @@ Objects.prototype = { rm.getInverse(rmi); dup.setRotationFromMatrix(rmi); //now the object inside will give us a NAABB Non-Axes Aligned Bounding Box - bounds = new THREE.Box3().setFromObject(dup.model); + bounds = new THREE.Box3().setFromObject(model); } } return bounds; @@ -25686,11 +25679,9 @@ Objects.prototype = { } //[jscastro] clone + assigning all the attributes - obj.duplicate = function () { - var dupe = obj.clone(true); - dupe.userData = obj.userData; - dupe.model = dupe.children[0].children[0]; - dupe.animations = dupe.model.animations; + obj.duplicate = function (options) { + let dupe = obj.clone(true); + dupe.userData = options || obj.userData; root._addMethods(dupe); dupe.deepCopy(obj); @@ -25700,40 +25691,44 @@ Objects.prototype = { obj.deepCopy = function (o) { obj.anchor = o.anchor; + obj.none = { x: 0, y: 0, z: 0 }; + obj.center = o.center; obj.bottom = o.bottom; obj.bottomLeft = o.bottomLeft; obj.bottomRight = o.bottomRight; - obj.center = o.center; - obj.left = o.left; - obj.right = o.right; obj.top = o.top; obj.topLeft = o.topLeft; obj.topRight = o.topRight; - obj.boundingBox = obj.children[0].children[1].children[0]; - obj.boundingBoxShadow = obj.children[0].children[1].children[1]; - obj.tooltip = obj.children[0].children[2]; + obj.left = o.left; + obj.right = o.right; return obj; } obj.dispose = function () { - obj.traverse(object => { - if (!object.isMesh) return - //console.log('dispose geometry!') - object.geometry.dispose() + Objects.prototype.unenroll(obj); - if (object.material.isMaterial) { - cleanMaterial(object.material) - } else { - // an array of materials - for (const material of object.material) cleanMaterial(material) + obj.traverse(o => { + //don't dispose th object itself as it will be recursive + if (o.parent && o.parent.name == "world") return; + if (o.isMesh) { + //console.log('dispose geometry!') + o.geometry.dispose(); + + if (o.material.isMaterial) { + cleanMaterial(o.material) + } else { + // an array of materials + for (const material of o.material) cleanMaterial(material) + } } + if (o.dispose) o.dispose(); + }) - if (obj.label) { obj.label.dispose() }; - if (obj.tooltip) { obj.tooltip.dispose() }; - if (obj.model) { obj.model = {} }; + obj.children = []; + } const cleanMaterial = material => { @@ -25956,10 +25951,9 @@ function Tooltip(obj) { let tooltip = new CSS2D.CSS2DObject(divToolTip); tooltip.visible = false; + tooltip.name = "tooltip"; var userScaleGroup = Objects.prototype._makeGroup(tooltip, obj); Objects.prototype._addMethods(userScaleGroup); - userScaleGroup.tooltip = tooltip; - return userScaleGroup; }