globe.js 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427
  1. /**
  2. * dat.globe Javascript WebGL Globe Toolkit
  3. * http://dataarts.github.com/dat.globe
  4. *
  5. * Copyright 2011 Data Arts Team, Google Creative Lab
  6. *
  7. * Licensed under the Apache License, Version 2.0 (the 'License');
  8. * you may not use this file except in compliance with the License.
  9. * You may obtain a copy of the License at
  10. *
  11. * http://www.apache.org/licenses/LICENSE-2.0
  12. */
  13. var DAT = DAT || {};
  14. DAT.Globe = function(container, colorFn) {
  15. colorFn = colorFn || function(x) {
  16. var c = new THREE.Color();
  17. c.setHSV( ( 0.6 - ( x * 0.5 ) ), 1.0, 1.0 );
  18. return c;
  19. };
  20. var Shaders = {
  21. 'earth' : {
  22. uniforms: {
  23. 'texture': { type: 't', value: 0, texture: null }
  24. },
  25. vertexShader: [
  26. 'varying vec3 vNormal;',
  27. 'varying vec2 vUv;',
  28. 'void main() {',
  29. 'gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );',
  30. 'vNormal = normalize( normalMatrix * normal );',
  31. 'vUv = uv;',
  32. '}'
  33. ].join('\n'),
  34. fragmentShader: [
  35. 'uniform sampler2D texture;',
  36. 'varying vec3 vNormal;',
  37. 'varying vec2 vUv;',
  38. 'void main() {',
  39. 'vec3 diffuse = texture2D( texture, vUv ).xyz;',
  40. 'float intensity = 1.05 - dot( vNormal, vec3( 0.0, 0.0, 1.0 ) );',
  41. 'vec3 atmosphere = vec3( 1.0, 1.0, 1.0 ) * pow( intensity, 3.0 );',
  42. 'gl_FragColor = vec4( diffuse + atmosphere, 1.0 );',
  43. '}'
  44. ].join('\n')
  45. },
  46. 'atmosphere' : {
  47. uniforms: {},
  48. vertexShader: [
  49. 'varying vec3 vNormal;',
  50. 'void main() {',
  51. 'vNormal = normalize( normalMatrix * normal );',
  52. 'gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );',
  53. '}'
  54. ].join('\n'),
  55. fragmentShader: [
  56. 'varying vec3 vNormal;',
  57. 'void main() {',
  58. 'float intensity = pow( 0.8 - dot( vNormal, vec3( 0, 0, 1.0 ) ), 12.0 ) * 0.7;',
  59. 'gl_FragColor = vec4( 0.55 + 0.36*intensity, 0.83 + 0.14*intensity, 0.95 + 0.04 * intensity, 1.0 * intensity ) ;',
  60. '}'
  61. ].join('\n')
  62. }
  63. };
  64. var camera, scene, sceneAtmosphere, renderer, w, h;
  65. var vector, mesh, atmosphere, point;
  66. var overRenderer;
  67. /*custom change: the 'imgDir' attribute control the path of the globle's texture*/
  68. var imgDir = './images/';
  69. var curZoomSpeed = 0;
  70. var zoomSpeed = 50;
  71. var mouse = { x: 0, y: 0 }, mouseOnDown = { x: 0, y: 0 };
  72. var rotation = { x: 0, y: 0 },
  73. target = { x: Math.PI*3/2, y: Math.PI / 6.0 },
  74. targetOnDown = { x: 0, y: 0 };
  75. var distance = 100000, distanceTarget = 100000;
  76. var padding = 40;
  77. var PI_HALF = Math.PI / 2;
  78. function init() {
  79. container.style.color = '#ff0';
  80. container.style.font = '13px/20px Arial, sans-serif';
  81. var shader, uniforms, material;
  82. w = container.offsetWidth || window.innerWidth;
  83. h = container.offsetHeight || window.innerHeight;
  84. camera = new THREE.Camera(
  85. 30, w / h, 1, 10000);
  86. camera.position.z = distance;
  87. vector = new THREE.Vector3();
  88. scene = new THREE.Scene();
  89. sceneAtmosphere = new THREE.Scene();
  90. var geometry = new THREE.Sphere(200, 40, 30);
  91. shader = Shaders['earth'];
  92. uniforms = THREE.UniformsUtils.clone(shader.uniforms);
  93. uniforms['texture'].texture = THREE.ImageUtils.loadTexture(imgDir+'world' +
  94. '.jpg');
  95. material = new THREE.MeshShaderMaterial({
  96. uniforms: uniforms,
  97. vertexShader: shader.vertexShader,
  98. fragmentShader: shader.fragmentShader
  99. });
  100. mesh = new THREE.Mesh(geometry, material);
  101. mesh.matrixAutoUpdate = false;
  102. scene.addObject(mesh);
  103. shader = Shaders['atmosphere'];
  104. uniforms = THREE.UniformsUtils.clone(shader.uniforms);
  105. material = new THREE.MeshShaderMaterial({
  106. uniforms: uniforms,
  107. vertexShader: shader.vertexShader,
  108. fragmentShader: shader.fragmentShader
  109. });
  110. mesh = new THREE.Mesh(geometry, material);
  111. mesh.scale.x = mesh.scale.y = mesh.scale.z = 1.1;
  112. mesh.flipSided = true;
  113. mesh.matrixAutoUpdate = false;
  114. mesh.updateMatrix();
  115. sceneAtmosphere.addObject(mesh);
  116. geometry = new THREE.Cube(0.75, 0.75, 1, 1, 1, 1, null, false, { px: true,
  117. nx: true, py: true, ny: true, pz: false, nz: true});
  118. for (var i = 0; i < geometry.vertices.length; i++) {
  119. var vertex = geometry.vertices[i];
  120. vertex.position.z += 0.5;
  121. }
  122. point = new THREE.Mesh(geometry);
  123. renderer = new THREE.WebGLRenderer({antialias: true});
  124. renderer.autoClear = false;
  125. renderer.setClearColorHex(0x000000, 0.0);
  126. renderer.setSize(w, h);
  127. renderer.domElement.style.position = 'absolute';
  128. container.appendChild(renderer.domElement);
  129. container.addEventListener('mousedown', onMouseDown, false);
  130. container.addEventListener('mousewheel', onMouseWheel, false);
  131. document.addEventListener('keydown', onDocumentKeyDown, false);
  132. window.addEventListener('resize', onWindowResize, false);
  133. container.addEventListener('mouseover', function() {
  134. overRenderer = true;
  135. }, false);
  136. container.addEventListener('mouseout', function() {
  137. overRenderer = false;
  138. }, false);
  139. }
  140. addData = function(data, opts) {
  141. var lat, lng, size, color, i, step, colorFnWrapper;
  142. opts.animated = opts.animated || false;
  143. this.is_animated = opts.animated;
  144. opts.format = opts.format || 'magnitude'; // other option is 'legend'
  145. console.log(opts.format);
  146. if (opts.format === 'magnitude') {
  147. step = 3;
  148. colorFnWrapper = function(data, i) { return colorFn(data[i+2]); }
  149. } else if (opts.format === 'legend') {
  150. step = 4;
  151. colorFnWrapper = function(data, i) { return colorFn(data[i+3]); }
  152. } else {
  153. throw('error: format not supported: '+opts.format);
  154. }
  155. if (opts.animated) {
  156. if (this._baseGeometry === undefined) {
  157. this._baseGeometry = new THREE.Geometry();
  158. for (i = 0; i < data.length; i += step) {
  159. lat = data[i];
  160. lng = data[i + 1];
  161. // size = data[i + 2];
  162. color = colorFnWrapper(data,i);
  163. size = 0;
  164. addPoint(lat, lng, size, color, this._baseGeometry);
  165. }
  166. }
  167. if(this._morphTargetId === undefined) {
  168. this._morphTargetId = 0;
  169. } else {
  170. this._morphTargetId += 1;
  171. }
  172. opts.name = opts.name || 'morphTarget'+this._morphTargetId;
  173. }
  174. var subgeo = new THREE.Geometry();
  175. for (i = 0; i < data.length; i += step) {
  176. lat = data[i];
  177. lng = data[i + 1];
  178. color = colorFnWrapper(data,i);
  179. size = data[i + 2];
  180. size = size*200;
  181. addPoint(lat, lng, size, color, subgeo);
  182. }
  183. if (opts.animated) {
  184. this._baseGeometry.morphTargets.push({'name': opts.name, vertices: subgeo.vertices});
  185. } else {
  186. this._baseGeometry = subgeo;
  187. }
  188. };
  189. function createPoints() {
  190. if (this._baseGeometry !== undefined) {
  191. if (this.is_animated === false) {
  192. this.points = new THREE.Mesh(this._baseGeometry, new THREE.MeshBasicMaterial({
  193. color: 0xffffff,
  194. vertexColors: THREE.FaceColors,
  195. morphTargets: false
  196. }));
  197. } else {
  198. if (this._baseGeometry.morphTargets.length < 8) {
  199. console.log('t l',this._baseGeometry.morphTargets.length);
  200. var padding = 8-this._baseGeometry.morphTargets.length;
  201. console.log('padding', padding);
  202. for(var i=0; i<=padding; i++) {
  203. console.log('padding',i);
  204. this._baseGeometry.morphTargets.push({'name': 'morphPadding'+i, vertices: this._baseGeometry.vertices});
  205. }
  206. }
  207. this.points = new THREE.Mesh(this._baseGeometry, new THREE.MeshBasicMaterial({
  208. color: 0xffffff,
  209. vertexColors: THREE.FaceColors,
  210. morphTargets: true
  211. }));
  212. }
  213. scene.addObject(this.points);
  214. }
  215. }
  216. function addPoint(lat, lng, size, color, subgeo) {
  217. var phi = (90 - lat) * Math.PI / 180;
  218. var theta = (180 - lng) * Math.PI / 180;
  219. point.position.x = 200 * Math.sin(phi) * Math.cos(theta);
  220. point.position.y = 200 * Math.cos(phi);
  221. point.position.z = 200 * Math.sin(phi) * Math.sin(theta);
  222. point.lookAt(mesh.position);
  223. point.scale.z = -size;
  224. point.updateMatrix();
  225. var i;
  226. for (i = 0; i < point.geometry.faces.length; i++) {
  227. point.geometry.faces[i].color = color;
  228. }
  229. GeometryUtils.merge(subgeo, point);
  230. }
  231. function onMouseDown(event) {
  232. event.preventDefault();
  233. container.addEventListener('mousemove', onMouseMove, false);
  234. container.addEventListener('mouseup', onMouseUp, false);
  235. container.addEventListener('mouseout', onMouseOut, false);
  236. mouseOnDown.x = - event.clientX;
  237. mouseOnDown.y = event.clientY;
  238. targetOnDown.x = target.x;
  239. targetOnDown.y = target.y;
  240. container.style.cursor = 'move';
  241. }
  242. function onMouseMove(event) {
  243. mouse.x = - event.clientX;
  244. mouse.y = event.clientY;
  245. var zoomDamp = distance/1000;
  246. target.x = targetOnDown.x + (mouse.x - mouseOnDown.x) * 0.005 * zoomDamp;
  247. target.y = targetOnDown.y + (mouse.y - mouseOnDown.y) * 0.005 * zoomDamp;
  248. target.y = target.y > PI_HALF ? PI_HALF : target.y;
  249. target.y = target.y < - PI_HALF ? - PI_HALF : target.y;
  250. }
  251. function onMouseUp(event) {
  252. container.removeEventListener('mousemove', onMouseMove, false);
  253. container.removeEventListener('mouseup', onMouseUp, false);
  254. container.removeEventListener('mouseout', onMouseOut, false);
  255. container.style.cursor = 'auto';
  256. }
  257. function onMouseOut(event) {
  258. container.removeEventListener('mousemove', onMouseMove, false);
  259. container.removeEventListener('mouseup', onMouseUp, false);
  260. container.removeEventListener('mouseout', onMouseOut, false);
  261. }
  262. function onMouseWheel(event) {
  263. event.preventDefault();
  264. if (overRenderer) {
  265. zoom(event.wheelDeltaY * 0.3);
  266. }
  267. return false;
  268. }
  269. function onDocumentKeyDown(event) {
  270. switch (event.keyCode) {
  271. case 38:
  272. zoom(100);
  273. event.preventDefault();
  274. break;
  275. case 40:
  276. zoom(-100);
  277. event.preventDefault();
  278. break;
  279. }
  280. }
  281. function onWindowResize( event ) {
  282. console.log('resize');
  283. camera.aspect = window.innerWidth / window.innerHeight;
  284. camera.updateProjectionMatrix();
  285. /*custom change: if there is a container, the width and height is the container's width and height*/
  286. renderer.setSize( container.offsetWidth || window.innerWidth, container.offsetHeight || window.innerHeight );
  287. }
  288. function zoom(delta) {
  289. distanceTarget -= delta;
  290. distanceTarget = distanceTarget > 1000 ? 1000 : distanceTarget;
  291. distanceTarget = distanceTarget < 350 ? 350 : distanceTarget;
  292. }
  293. function animate() {
  294. requestAnimationFrame(animate);
  295. render();
  296. }
  297. function render() {
  298. zoom(curZoomSpeed);
  299. rotation.x += (target.x - rotation.x) * 0.1;
  300. rotation.y += (target.y - rotation.y) * 0.1;
  301. distance += (distanceTarget - distance) * 0.3;
  302. camera.position.x = distance * Math.sin(rotation.x) * Math.cos(rotation.y);
  303. camera.position.y = distance * Math.sin(rotation.y);
  304. camera.position.z = distance * Math.cos(rotation.x) * Math.cos(rotation.y);
  305. vector.copy(camera.position);
  306. renderer.clear();
  307. renderer.render(scene, camera);
  308. renderer.render(sceneAtmosphere, camera);
  309. }
  310. init();
  311. this.animate = animate;
  312. this.__defineGetter__('time', function() {
  313. return this._time || 0;
  314. });
  315. this.__defineSetter__('time', function(t) {
  316. var validMorphs = [];
  317. var morphDict = this.points.morphTargetDictionary;
  318. for(var k in morphDict) {
  319. if(k.indexOf('morphPadding') < 0) {
  320. validMorphs.push(morphDict[k]);
  321. }
  322. }
  323. validMorphs.sort();
  324. var l = validMorphs.length-1;
  325. var scaledt = t*l+1;
  326. var index = Math.floor(scaledt);
  327. for (i=0;i<validMorphs.length;i++) {
  328. this.points.morphTargetInfluences[validMorphs[i]] = 0;
  329. }
  330. var lastIndex = index - 1;
  331. var leftover = scaledt - index;
  332. if (lastIndex >= 0) {
  333. this.points.morphTargetInfluences[lastIndex] = 1 - leftover;
  334. }
  335. this.points.morphTargetInfluences[index] = leftover;
  336. this._time = t;
  337. });
  338. this.addData = addData;
  339. this.createPoints = createPoints;
  340. this.renderer = renderer;
  341. this.scene = scene;
  342. return this;
  343. };