123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355 |
- /* ------------------------------------------------------------
- Copyright 2016 Esri
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at:
- http://www.apache.org/licenses/LICENSE-2.0
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
- --------------------------------------------------------------- */
- define([
- 'esri/core/declare',
- 'esri/geometry/Point',
- 'esri/geometry/SpatialReference',
- 'esri/views/3d/externalRenderers',
- 'esri/geometry/support/webMercatorUtils',
- ], function(declare, Point, SpatialReference, externalRenderers, webMercatorUtils) {
- // Enforce strict mode
- 'use strict';
- // Constants
- var THREE = window.THREE;
- var RADIUS = 6378137;
- var OFFSET = 5000;
- // var COLOR = 0xffffff;
- var COLOR = 0xef6c00; //0x00ffff;
- var REST = 75; //ms
- // var REST = 200;
- const RandomColors = [
- // '#1A93D3',
- // '#B5C334',
- // '#C1232B',
- // '#E87C25',
- // '#27727B',
- // '#FE8463',
- // '#9BCA63',
- // '#FAD860',
- // '#F3A43B',
- // '#60C0DD',
- // '#D7504B',
- // '#C6E579',
- // '#F4E001',
- // '#F0805A',
- // '#26C0C0',
- // '#c23531',
- '#03f0ff',
- '#03f0ff',
- // '#2f4554',
- // '#61a0a8',
- // '#d48265',
- // '#91c7ae',
- // '#749f83',
- // '#ca8622',
- // '#bda29a',
- // '#6e7074',
- // '#546570',
- // '#c4ccd3',
- ];
- var colorindex = 0;
- function getColor() {
- if (colorindex === 0) {
- colorindex = 1;
- } else {
- colorindex = 0;
- }
- return RandomColors[colorindex].replace('#', '0x');
- }
- return declare([], {
- constructor: function(view, tracks) {
- this.view = view;
- this.tracks = tracks;
- this.index = 0;
- this.max = 0;
- this.meshLines = [];
- this.refresh = Date.now();
- },
- setup: function(context) {
- // Create the THREE.js webgl renderer
- this.renderer = new THREE.WebGLRenderer({
- context: context.gl,
- premultipliedAlpha: false,
- });
- this.renderer.setPixelRatio(window.devicePixelRatio);
- this.renderer.setViewport(0, 0, this.view.width, this.view.height);
- // Make sure it does not clear anything before rendering
- this.renderer.autoClear = false;
- this.renderer.autoClearDepth = false;
- this.renderer.autoClearColor = false;
- this.renderer.autoClearStencil = false;
- // The ArcGIS JS API renders to custom offscreen buffers, and not to the default framebuffers.
- // We have to inject this bit of code into the three.js runtime in order for it to bind those
- // buffers instead of the default ones.
- var originalSetRenderTarget = this.renderer.setRenderTarget.bind(this.renderer);
- this.renderer.setRenderTarget = function(target) {
- originalSetRenderTarget(target);
- if (target == null) {
- context.bindRenderTarget();
- }
- };
- this.scene = new THREE.Scene();
- // setup the camera
- this.camera = new THREE.PerspectiveCamera();
- // Create lights that will resemble the sun light lighting that we do internally
- this._createLights();
- // Create objects and add them to the scene
- this._createObjects();
- // Set starting geometries
- this._updateObjects();
- // cleanup after ourselfs
- context.resetWebGLState();
- },
- render: function(context) {
- // Make sure to reset the internal THREE.js state
- this.renderer.resetGLState();
- // Update the THREE.js camera so it's synchronized to our camera
- this._updateCamera(context);
- // Update the THREE.js lights so it's synchronized to our light
- this._updateLights(context);
- // Advance current geometry
- this._updateObjects(context);
- // Render the scene
- this.renderer.render(this.scene, this.camera);
- // Immediately request a new redraw
- externalRenderers.requestRender(this.view);
- // cleanup
- context.resetWebGLState();
- },
- dispose: function(content) {},
- _createObjects: function() {
- //
- var scope = this;
- var transform = new THREE.Matrix4();
- scope.tracks.forEach(function(track) {
- // Smooth and densify line
- var curve = new THREE.SplineCurve(
- track.map(function(e) {
- const lonlat = webMercatorUtils.xyToLngLat(e[0], e[1]);
- return new THREE.Vector2(lonlat[0], lonlat[1]);
- })
- );
- // var smooth = curve.getSpacedPoints(curve.points.length * 2);
- var smooth = curve.getSpacedPoints(35);
- // Convert vectors to Esri webgl cartesian
- var smooth3d = smooth.map(function(e) {
- // // Convert lat/long to radians
- // var lon = (e.x * Math.PI) / 180 - Math.PI;
- // var lat = (e.y * Math.PI) / 180 - Math.PI / 2;
- // // Create vector to current
- // var q = new THREE.Vector3(RADIUS + OFFSET, 0, 0);
- // q.applyAxisAngle(new THREE.Vector3(0, 1, 0), lat);
- // q.applyAxisAngle(new THREE.Vector3(0, 0, 1), lon);
- // // Return vector
- // return new THREE.Vector3(q.x, q.y, q.z);
- const mercatorPoint = webMercatorUtils.geographicToWebMercator(new Point({
- x: e.x,
- y: e.y,
- z: 10
- }));
- transform.fromArray(
- externalRenderers.renderCoordinateTransformAt(
- scope.view,
- [mercatorPoint.x, mercatorPoint.y, mercatorPoint.z],
- SpatialReference.WebMercator,
- new Array(16)
- )
- );
- return new THREE.Vector3(
- transform.elements[12],
- transform.elements[13],
- transform.elements[14]
- );
- });
- // Get the length of the longest track
- scope.max = Math.max(scope.max, smooth3d.length);
- // Create a random offset. Used to stagger animations.
- var offset = Math.floor(Math.random() * (smooth3d.length - 1));
- var color1 = Number(getColor());
- var resolution = new THREE.Vector2(window.innerWidth, window.innerHeight);
- for (var i = 0; i < smooth3d.length - 1; i++) {
- // Create line geometry
- var geometry = new THREE.Geometry();
- geometry.vertices = [smooth3d[i], smooth3d[i + 1]];
- // Create line material
- // var material = new THREE.LineBasicMaterial({
- // color: COLOR,
- // opacity: 0,
- // transparent: true
- // });
- // Create line.
- // var line = new THREE.Line(geometry, material);
- // line.visible = false;
- // line.flag = i;
- // line.offset = offset;
- var g = new MeshLine();
- g.setGeometry(geometry);
- var line = new window.THREE.Mesh(
- g.geometry,
- new MeshLineMaterial({
- useMap: false,
- color: new THREE.Color(color1),
- opacity: 0,
- transparent: true,
- dashArray: 0,
- resolution: resolution,
- sizeAttenuation: false,
- lineWidth: 15, //10
- near: scope.camera.near,
- far: scope.camera.far,
- })
- );
- line.visible = false;
- line.flag = i;
- line.offset = offset;
- // Add line
- scope.scene.add(line);
- scope.meshLines.push(line);
- }
- });
- // Clear tracks array
- this.tracks = null;
- },
- _createLights: function() {
- // setup scene lighting
- this.ambient = new THREE.AmbientLight(0xffffff, 0.5);
- this.scene.add(this.ambient);
- this.sun = new THREE.DirectionalLight(0xffffff, 0.5);
- this.scene.add(this.sun);
- },
- _updateCamera: function(context) {
- // update camera parameters
- ///////////////////////////////////////////////////////////////////////////////////
- var cam = context.camera;
- this.camera.position.set(cam.eye[0], cam.eye[1], cam.eye[2]);
- this.camera.up.set(cam.up[0], cam.up[1], cam.up[2]);
- this.camera.lookAt(new THREE.Vector3(cam.center[0], cam.center[1], cam.center[2]));
- // Projection matrix can be copied directly
- this.camera.projectionMatrix.fromArray(cam.projectionMatrix);
- },
- _updateLights: function(context) {
- var l = context.sunLight;
- this.sun.position.set(l.direction[0], l.direction[1], l.direction[2]);
- this.sun.intensity = l.diffuse.intensity;
- this.sun.color = new THREE.Color(l.diffuse.color[0], l.diffuse.color[1], l.diffuse.color[2]);
- this.ambient.intensity = l.ambient.intensity;
- this.ambient.color = new THREE.Color(
- l.ambient.color[0],
- l.ambient.color[1],
- l.ambient.color[2]
- );
- },
- _updateObjects: function(context) {
- // Only update shapes every ~75ms
- var now = Date.now();
- if (now - this.refresh < REST) {
- return;
- }
- this.refresh = now;
- // Loop for every shape
- var scope = this;
- this.meshLines.forEach(function(e) {
- // Ignore other shapes
- // Create a new offset index
- var index = scope.index + e.offset;
- if (index > scope.max) {
- index -= scope.max;
- }
- // Show or hide a line segment
- if (e.flag - index >= 0 && e.flag - index <= 10) {
- // Slowly fade in a new line.
- var fade = 1;
- switch (index) {
- case 0:
- fade = 0.1;
- break;
- case 1:
- fade = 0.2;
- break;
- case 2:
- fade = 0.5;
- break;
- }
- // if (index > 0 && index < 1) {
- // fade = 0.1;
- // } else if (index > 1 && index < 2) {
- // fade = 0.2;
- // } else if (index > 2 && index < 3) {
- // fade = 0.5;
- // }
- // Show the line segment.
- e.material.opacity = (fade * (e.flag - index)) / 10;
- e.visible = true;
- } else {
- // Hide the line segment.
- e.visible = false;
- e.material.opacity = 0;
- }
- });
- // Increment the drawing index
- this.index++;
- // this.index += 0.2;
- if (this.index >= this.max) {
- this.index = 0;
- }
- },
- });
- });
|