428 lines
13 KiB
JavaScript
428 lines
13 KiB
JavaScript
|
/**
|
||
|
* dat.globe Javascript WebGL Globe Toolkit
|
||
|
* http://dataarts.github.com/dat.globe
|
||
|
*
|
||
|
* Copyright 2011 Data Arts Team, Google Creative Lab
|
||
|
*
|
||
|
* 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
|
||
|
*/
|
||
|
|
||
|
var DAT = DAT || {};
|
||
|
|
||
|
DAT.Globe = function(container, colorFn) {
|
||
|
|
||
|
colorFn = colorFn || function(x) {
|
||
|
var c = new THREE.Color();
|
||
|
c.setHSV( ( 0.6 - ( x * 0.5 ) ), 1.0, 1.0 );
|
||
|
return c;
|
||
|
};
|
||
|
|
||
|
var Shaders = {
|
||
|
'earth' : {
|
||
|
uniforms: {
|
||
|
'texture': { type: 't', value: 0, texture: null }
|
||
|
},
|
||
|
vertexShader: [
|
||
|
'varying vec3 vNormal;',
|
||
|
'varying vec2 vUv;',
|
||
|
'void main() {',
|
||
|
'gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );',
|
||
|
'vNormal = normalize( normalMatrix * normal );',
|
||
|
'vUv = uv;',
|
||
|
'}'
|
||
|
].join('\n'),
|
||
|
fragmentShader: [
|
||
|
'uniform sampler2D texture;',
|
||
|
'varying vec3 vNormal;',
|
||
|
'varying vec2 vUv;',
|
||
|
'void main() {',
|
||
|
'vec3 diffuse = texture2D( texture, vUv ).xyz;',
|
||
|
'float intensity = 1.05 - dot( vNormal, vec3( 0.0, 0.0, 1.0 ) );',
|
||
|
'vec3 atmosphere = vec3( 1.0, 1.0, 1.0 ) * pow( intensity, 3.0 );',
|
||
|
'gl_FragColor = vec4( diffuse + atmosphere, 1.0 );',
|
||
|
'}'
|
||
|
].join('\n')
|
||
|
},
|
||
|
'atmosphere' : {
|
||
|
uniforms: {},
|
||
|
vertexShader: [
|
||
|
'varying vec3 vNormal;',
|
||
|
'void main() {',
|
||
|
'vNormal = normalize( normalMatrix * normal );',
|
||
|
'gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );',
|
||
|
'}'
|
||
|
].join('\n'),
|
||
|
fragmentShader: [
|
||
|
'varying vec3 vNormal;',
|
||
|
'void main() {',
|
||
|
'float intensity = pow( 0.8 - dot( vNormal, vec3( 0, 0, 1.0 ) ), 12.0 ) * 0.7;',
|
||
|
'gl_FragColor = vec4( 0.55 + 0.36*intensity, 0.83 + 0.14*intensity, 0.95 + 0.04 * intensity, 1.0 * intensity ) ;',
|
||
|
'}'
|
||
|
].join('\n')
|
||
|
}
|
||
|
};
|
||
|
|
||
|
var camera, scene, sceneAtmosphere, renderer, w, h;
|
||
|
var vector, mesh, atmosphere, point;
|
||
|
|
||
|
var overRenderer;
|
||
|
|
||
|
/*custom change: the 'imgDir' attribute control the path of the globle's texture*/
|
||
|
var imgDir = './images/';
|
||
|
|
||
|
var curZoomSpeed = 0;
|
||
|
var zoomSpeed = 50;
|
||
|
|
||
|
var mouse = { x: 0, y: 0 }, mouseOnDown = { x: 0, y: 0 };
|
||
|
var rotation = { x: 0, y: 0 },
|
||
|
target = { x: Math.PI*3/2, y: Math.PI / 6.0 },
|
||
|
targetOnDown = { x: 0, y: 0 };
|
||
|
|
||
|
var distance = 100000, distanceTarget = 100000;
|
||
|
var padding = 40;
|
||
|
var PI_HALF = Math.PI / 2;
|
||
|
|
||
|
function init() {
|
||
|
|
||
|
container.style.color = '#ff0';
|
||
|
container.style.font = '13px/20px Arial, sans-serif';
|
||
|
|
||
|
var shader, uniforms, material;
|
||
|
w = container.offsetWidth || window.innerWidth;
|
||
|
h = container.offsetHeight || window.innerHeight;
|
||
|
|
||
|
camera = new THREE.Camera(
|
||
|
30, w / h, 1, 10000);
|
||
|
camera.position.z = distance;
|
||
|
|
||
|
vector = new THREE.Vector3();
|
||
|
|
||
|
scene = new THREE.Scene();
|
||
|
sceneAtmosphere = new THREE.Scene();
|
||
|
|
||
|
var geometry = new THREE.Sphere(200, 40, 30);
|
||
|
|
||
|
shader = Shaders['earth'];
|
||
|
uniforms = THREE.UniformsUtils.clone(shader.uniforms);
|
||
|
|
||
|
uniforms['texture'].texture = THREE.ImageUtils.loadTexture(imgDir+'world' +
|
||
|
'.jpg');
|
||
|
|
||
|
material = new THREE.MeshShaderMaterial({
|
||
|
|
||
|
uniforms: uniforms,
|
||
|
vertexShader: shader.vertexShader,
|
||
|
fragmentShader: shader.fragmentShader
|
||
|
|
||
|
});
|
||
|
|
||
|
mesh = new THREE.Mesh(geometry, material);
|
||
|
mesh.matrixAutoUpdate = false;
|
||
|
scene.addObject(mesh);
|
||
|
|
||
|
shader = Shaders['atmosphere'];
|
||
|
uniforms = THREE.UniformsUtils.clone(shader.uniforms);
|
||
|
|
||
|
material = new THREE.MeshShaderMaterial({
|
||
|
|
||
|
uniforms: uniforms,
|
||
|
vertexShader: shader.vertexShader,
|
||
|
fragmentShader: shader.fragmentShader
|
||
|
|
||
|
});
|
||
|
|
||
|
mesh = new THREE.Mesh(geometry, material);
|
||
|
mesh.scale.x = mesh.scale.y = mesh.scale.z = 1.1;
|
||
|
mesh.flipSided = true;
|
||
|
mesh.matrixAutoUpdate = false;
|
||
|
mesh.updateMatrix();
|
||
|
sceneAtmosphere.addObject(mesh);
|
||
|
|
||
|
geometry = new THREE.Cube(0.75, 0.75, 1, 1, 1, 1, null, false, { px: true,
|
||
|
nx: true, py: true, ny: true, pz: false, nz: true});
|
||
|
|
||
|
for (var i = 0; i < geometry.vertices.length; i++) {
|
||
|
|
||
|
var vertex = geometry.vertices[i];
|
||
|
vertex.position.z += 0.5;
|
||
|
|
||
|
}
|
||
|
|
||
|
point = new THREE.Mesh(geometry);
|
||
|
|
||
|
renderer = new THREE.WebGLRenderer({antialias: true});
|
||
|
renderer.autoClear = false;
|
||
|
renderer.setClearColorHex(0x000000, 0.0);
|
||
|
renderer.setSize(w, h);
|
||
|
|
||
|
renderer.domElement.style.position = 'absolute';
|
||
|
|
||
|
container.appendChild(renderer.domElement);
|
||
|
|
||
|
container.addEventListener('mousedown', onMouseDown, false);
|
||
|
|
||
|
container.addEventListener('mousewheel', onMouseWheel, false);
|
||
|
|
||
|
document.addEventListener('keydown', onDocumentKeyDown, false);
|
||
|
|
||
|
window.addEventListener('resize', onWindowResize, false);
|
||
|
|
||
|
container.addEventListener('mouseover', function() {
|
||
|
overRenderer = true;
|
||
|
}, false);
|
||
|
|
||
|
container.addEventListener('mouseout', function() {
|
||
|
overRenderer = false;
|
||
|
}, false);
|
||
|
}
|
||
|
|
||
|
addData = function(data, opts) {
|
||
|
var lat, lng, size, color, i, step, colorFnWrapper;
|
||
|
|
||
|
opts.animated = opts.animated || false;
|
||
|
this.is_animated = opts.animated;
|
||
|
opts.format = opts.format || 'magnitude'; // other option is 'legend'
|
||
|
console.log(opts.format);
|
||
|
if (opts.format === 'magnitude') {
|
||
|
step = 3;
|
||
|
colorFnWrapper = function(data, i) { return colorFn(data[i+2]); }
|
||
|
} else if (opts.format === 'legend') {
|
||
|
step = 4;
|
||
|
colorFnWrapper = function(data, i) { return colorFn(data[i+3]); }
|
||
|
} else {
|
||
|
throw('error: format not supported: '+opts.format);
|
||
|
}
|
||
|
|
||
|
if (opts.animated) {
|
||
|
if (this._baseGeometry === undefined) {
|
||
|
this._baseGeometry = new THREE.Geometry();
|
||
|
for (i = 0; i < data.length; i += step) {
|
||
|
lat = data[i];
|
||
|
lng = data[i + 1];
|
||
|
// size = data[i + 2];
|
||
|
color = colorFnWrapper(data,i);
|
||
|
size = 0;
|
||
|
addPoint(lat, lng, size, color, this._baseGeometry);
|
||
|
}
|
||
|
}
|
||
|
if(this._morphTargetId === undefined) {
|
||
|
this._morphTargetId = 0;
|
||
|
} else {
|
||
|
this._morphTargetId += 1;
|
||
|
}
|
||
|
opts.name = opts.name || 'morphTarget'+this._morphTargetId;
|
||
|
}
|
||
|
var subgeo = new THREE.Geometry();
|
||
|
for (i = 0; i < data.length; i += step) {
|
||
|
lat = data[i];
|
||
|
lng = data[i + 1];
|
||
|
color = colorFnWrapper(data,i);
|
||
|
size = data[i + 2];
|
||
|
size = size*200;
|
||
|
addPoint(lat, lng, size, color, subgeo);
|
||
|
}
|
||
|
if (opts.animated) {
|
||
|
this._baseGeometry.morphTargets.push({'name': opts.name, vertices: subgeo.vertices});
|
||
|
} else {
|
||
|
this._baseGeometry = subgeo;
|
||
|
}
|
||
|
|
||
|
};
|
||
|
|
||
|
function createPoints() {
|
||
|
if (this._baseGeometry !== undefined) {
|
||
|
if (this.is_animated === false) {
|
||
|
this.points = new THREE.Mesh(this._baseGeometry, new THREE.MeshBasicMaterial({
|
||
|
color: 0xffffff,
|
||
|
vertexColors: THREE.FaceColors,
|
||
|
morphTargets: false
|
||
|
}));
|
||
|
} else {
|
||
|
if (this._baseGeometry.morphTargets.length < 8) {
|
||
|
console.log('t l',this._baseGeometry.morphTargets.length);
|
||
|
var padding = 8-this._baseGeometry.morphTargets.length;
|
||
|
console.log('padding', padding);
|
||
|
for(var i=0; i<=padding; i++) {
|
||
|
console.log('padding',i);
|
||
|
this._baseGeometry.morphTargets.push({'name': 'morphPadding'+i, vertices: this._baseGeometry.vertices});
|
||
|
}
|
||
|
}
|
||
|
this.points = new THREE.Mesh(this._baseGeometry, new THREE.MeshBasicMaterial({
|
||
|
color: 0xffffff,
|
||
|
vertexColors: THREE.FaceColors,
|
||
|
morphTargets: true
|
||
|
}));
|
||
|
}
|
||
|
scene.addObject(this.points);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
function addPoint(lat, lng, size, color, subgeo) {
|
||
|
var phi = (90 - lat) * Math.PI / 180;
|
||
|
var theta = (180 - lng) * Math.PI / 180;
|
||
|
|
||
|
point.position.x = 200 * Math.sin(phi) * Math.cos(theta);
|
||
|
point.position.y = 200 * Math.cos(phi);
|
||
|
point.position.z = 200 * Math.sin(phi) * Math.sin(theta);
|
||
|
|
||
|
point.lookAt(mesh.position);
|
||
|
|
||
|
point.scale.z = -size;
|
||
|
point.updateMatrix();
|
||
|
|
||
|
var i;
|
||
|
for (i = 0; i < point.geometry.faces.length; i++) {
|
||
|
|
||
|
point.geometry.faces[i].color = color;
|
||
|
|
||
|
}
|
||
|
|
||
|
GeometryUtils.merge(subgeo, point);
|
||
|
}
|
||
|
|
||
|
function onMouseDown(event) {
|
||
|
event.preventDefault();
|
||
|
|
||
|
container.addEventListener('mousemove', onMouseMove, false);
|
||
|
container.addEventListener('mouseup', onMouseUp, false);
|
||
|
container.addEventListener('mouseout', onMouseOut, false);
|
||
|
|
||
|
mouseOnDown.x = - event.clientX;
|
||
|
mouseOnDown.y = event.clientY;
|
||
|
|
||
|
targetOnDown.x = target.x;
|
||
|
targetOnDown.y = target.y;
|
||
|
|
||
|
container.style.cursor = 'move';
|
||
|
}
|
||
|
|
||
|
function onMouseMove(event) {
|
||
|
mouse.x = - event.clientX;
|
||
|
mouse.y = event.clientY;
|
||
|
|
||
|
var zoomDamp = distance/1000;
|
||
|
|
||
|
target.x = targetOnDown.x + (mouse.x - mouseOnDown.x) * 0.005 * zoomDamp;
|
||
|
target.y = targetOnDown.y + (mouse.y - mouseOnDown.y) * 0.005 * zoomDamp;
|
||
|
|
||
|
target.y = target.y > PI_HALF ? PI_HALF : target.y;
|
||
|
target.y = target.y < - PI_HALF ? - PI_HALF : target.y;
|
||
|
}
|
||
|
|
||
|
function onMouseUp(event) {
|
||
|
container.removeEventListener('mousemove', onMouseMove, false);
|
||
|
container.removeEventListener('mouseup', onMouseUp, false);
|
||
|
container.removeEventListener('mouseout', onMouseOut, false);
|
||
|
container.style.cursor = 'auto';
|
||
|
}
|
||
|
|
||
|
function onMouseOut(event) {
|
||
|
container.removeEventListener('mousemove', onMouseMove, false);
|
||
|
container.removeEventListener('mouseup', onMouseUp, false);
|
||
|
container.removeEventListener('mouseout', onMouseOut, false);
|
||
|
}
|
||
|
|
||
|
function onMouseWheel(event) {
|
||
|
event.preventDefault();
|
||
|
if (overRenderer) {
|
||
|
zoom(event.wheelDeltaY * 0.3);
|
||
|
}
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
function onDocumentKeyDown(event) {
|
||
|
switch (event.keyCode) {
|
||
|
case 38:
|
||
|
zoom(100);
|
||
|
event.preventDefault();
|
||
|
break;
|
||
|
case 40:
|
||
|
zoom(-100);
|
||
|
event.preventDefault();
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
function onWindowResize( event ) {
|
||
|
console.log('resize');
|
||
|
camera.aspect = window.innerWidth / window.innerHeight;
|
||
|
camera.updateProjectionMatrix();
|
||
|
/*custom change: if there is a container, the width and height is the container's width and height*/
|
||
|
renderer.setSize( container.offsetWidth || window.innerWidth, container.offsetHeight || window.innerHeight );
|
||
|
}
|
||
|
|
||
|
function zoom(delta) {
|
||
|
distanceTarget -= delta;
|
||
|
distanceTarget = distanceTarget > 1000 ? 1000 : distanceTarget;
|
||
|
distanceTarget = distanceTarget < 350 ? 350 : distanceTarget;
|
||
|
}
|
||
|
|
||
|
function animate() {
|
||
|
requestAnimationFrame(animate);
|
||
|
render();
|
||
|
}
|
||
|
|
||
|
function render() {
|
||
|
zoom(curZoomSpeed);
|
||
|
|
||
|
rotation.x += (target.x - rotation.x) * 0.1;
|
||
|
rotation.y += (target.y - rotation.y) * 0.1;
|
||
|
distance += (distanceTarget - distance) * 0.3;
|
||
|
|
||
|
camera.position.x = distance * Math.sin(rotation.x) * Math.cos(rotation.y);
|
||
|
camera.position.y = distance * Math.sin(rotation.y);
|
||
|
camera.position.z = distance * Math.cos(rotation.x) * Math.cos(rotation.y);
|
||
|
|
||
|
vector.copy(camera.position);
|
||
|
|
||
|
renderer.clear();
|
||
|
renderer.render(scene, camera);
|
||
|
renderer.render(sceneAtmosphere, camera);
|
||
|
}
|
||
|
|
||
|
init();
|
||
|
this.animate = animate;
|
||
|
|
||
|
|
||
|
this.__defineGetter__('time', function() {
|
||
|
return this._time || 0;
|
||
|
});
|
||
|
|
||
|
this.__defineSetter__('time', function(t) {
|
||
|
var validMorphs = [];
|
||
|
var morphDict = this.points.morphTargetDictionary;
|
||
|
for(var k in morphDict) {
|
||
|
if(k.indexOf('morphPadding') < 0) {
|
||
|
validMorphs.push(morphDict[k]);
|
||
|
}
|
||
|
}
|
||
|
validMorphs.sort();
|
||
|
var l = validMorphs.length-1;
|
||
|
var scaledt = t*l+1;
|
||
|
var index = Math.floor(scaledt);
|
||
|
for (i=0;i<validMorphs.length;i++) {
|
||
|
this.points.morphTargetInfluences[validMorphs[i]] = 0;
|
||
|
}
|
||
|
var lastIndex = index - 1;
|
||
|
var leftover = scaledt - index;
|
||
|
if (lastIndex >= 0) {
|
||
|
this.points.morphTargetInfluences[lastIndex] = 1 - leftover;
|
||
|
}
|
||
|
this.points.morphTargetInfluences[index] = leftover;
|
||
|
this._time = t;
|
||
|
});
|
||
|
|
||
|
this.addData = addData;
|
||
|
this.createPoints = createPoints;
|
||
|
this.renderer = renderer;
|
||
|
this.scene = scene;
|
||
|
|
||
|
return this;
|
||
|
|
||
|
};
|
||
|
|