master
Robin Hawkes 2016-03-11 14:16:35 +00:00
rodzic 979c6b5725
commit 165f9d446a
3 zmienionych plików z 24 dodań i 310 usunięć

Wyświetl plik

@ -1,310 +0,0 @@
import EventEmitter from 'eventemitter3';
import extend from 'lodash.assign';
import CRS from './geo/crs/index';
import {point as Point} from './geo/Point';
import {latLon as LatLon} from './geo/LatLon';
import Engine from './engine/Engine';
import EnvironmentLayer from './layer/environment/EnvironmentLayer';
// TODO: Make sure nothing is left behind in the heap after calling destroy()
// Pretty much any event someone using ViziCities would need will be emitted or
// proxied by World (eg. render events, etc)
class World extends EventEmitter {
constructor(domId, options) {
super();
var defaults = {
crs: CRS.EPSG3857,
skybox: false
};
this.options = extend({}, defaults, options);
this._layers = [];
this._controls = [];
this._initContainer(domId);
this._initEngine();
this._initEnvironment();
this._initEvents();
this._pause = false;
// Kick off the update and render loop
this._update();
}
_initContainer(domId) {
this._container = document.getElementById(domId);
}
_initEngine() {
this._engine = new Engine(this._container, this);
// Engine events
//
// Consider proxying these through events on World for public access
// this._engine.on('preRender', () => {});
// this._engine.on('postRender', () => {});
}
_initEnvironment() {
// Not sure if I want to keep this as a private API
//
// Makes sense to allow others to customise their environment so perhaps
// add some method of disable / overriding the environment settings
this._environment = new EnvironmentLayer({
skybox: this.options.skybox
}).addTo(this);
}
_initEvents() {
this.on('controlsMoveEnd', this._onControlsMoveEnd);
}
_onControlsMoveEnd(point) {
var _point = Point(point.x, point.z);
this._resetView(this.pointToLatLon(_point), _point);
}
// Reset world view
_resetView(latlon, point) {
this.emit('preResetView');
this._moveStart();
this._move(latlon, point);
this._moveEnd();
this.emit('postResetView');
}
_moveStart() {
this.emit('moveStart');
}
_move(latlon, point) {
this._lastPosition = latlon;
this.emit('move', latlon, point);
}
_moveEnd() {
this.emit('moveEnd');
}
_update() {
if (this._pause) {
return;
}
var delta = this._engine.clock.getDelta();
// Once _update is called it will run forever, for now
window.requestAnimationFrame(this._update.bind(this));
// Update controls
this._controls.forEach(controls => {
controls.update();
});
this.emit('preUpdate', delta);
this._engine.update(delta);
this.emit('postUpdate', delta);
}
// Set world view
setView(latlon) {
// Store initial geographic coordinate for the [0,0,0] world position
//
// The origin point doesn't move in three.js / 3D space so only set it once
// here instead of every time _resetView is called
//
// If it was updated every time then coorindates would shift over time and
// would be out of place / context with previously-placed points (0,0 would
// refer to a different point each time)
this._originLatlon = latlon;
this._originPoint = this.project(latlon);
this._resetView(latlon);
return this;
}
// Return world geographic position
getPosition() {
return this._lastPosition;
}
// Transform geographic coordinate to world point
//
// This doesn't take into account the origin offset
//
// For example, this takes a geographic coordinate and returns a point
// relative to the origin point of the projection (not the world)
project(latlon) {
return this.options.crs.latLonToPoint(LatLon(latlon));
}
// Transform world point to geographic coordinate
//
// This doesn't take into account the origin offset
//
// For example, this takes a point relative to the origin point of the
// projection (not the world) and returns a geographic coordinate
unproject(point) {
return this.options.crs.pointToLatLon(Point(point));
}
// Takes into account the origin offset
//
// For example, this takes a geographic coordinate and returns a point
// relative to the three.js / 3D origin (0,0)
latLonToPoint(latlon) {
var projectedPoint = this.project(LatLon(latlon));
return projectedPoint._subtract(this._originPoint);
}
// Takes into account the origin offset
//
// For example, this takes a point relative to the three.js / 3D origin (0,0)
// and returns the exact geographic coordinate at that point
pointToLatLon(point) {
var projectedPoint = Point(point).add(this._originPoint);
return this.unproject(projectedPoint);
}
// Return pointscale for a given geographic coordinate
pointScale(latlon, accurate) {
return this.options.crs.pointScale(latlon, accurate);
}
// Convert from real meters to world units
//
// TODO: Would be nice not to have to pass in a pointscale here
metresToWorld(metres, pointScale, zoom) {
return this.options.crs.metresToWorld(metres, pointScale, zoom);
}
// Convert from real meters to world units
//
// TODO: Would be nice not to have to pass in a pointscale here
worldToMetres(worldUnits, pointScale, zoom) {
return this.options.crs.worldToMetres(worldUnits, pointScale, zoom);
}
// Unsure if it's a good idea to expose this here for components like
// GridLayer to use (eg. to keep track of a frustum)
getCamera() {
return this._engine._camera;
}
addLayer(layer) {
layer._addToWorld(this);
this._layers.push(layer);
if (layer.isOutput()) {
// Could move this into Layer but it'll do here for now
this._engine._scene.add(layer._object3D);
this._engine._domScene3D.add(layer._domObject3D);
this._engine._domScene2D.add(layer._domObject2D);
}
this.emit('layerAdded', layer);
return this;
}
// Remove layer from world and scene but don't destroy it entirely
removeLayer(layer) {
var layerIndex = this._layers.indexOf(layer);
if (layerIndex > -1) {
// Remove from this._layers
this._layers.splice(layerIndex, 1);
};
if (layer.isOutput()) {
this._engine._scene.remove(layer._object3D);
this._engine._domScene3D.remove(layer._domObject3D);
this._engine._domScene2D.remove(layer._domObject2D);
}
this.emit('layerRemoved');
return this;
}
addControls(controls) {
controls._addToWorld(this);
this._controls.push(controls);
this.emit('controlsAdded', controls);
return this;
}
// Remove controls from world but don't destroy them entirely
removeControls(controls) {
var controlsIndex = this._controls.indexOf(controlsIndex);
if (controlsIndex > -1) {
this._controls.splice(controlsIndex, 1);
};
this.emit('controlsRemoved', controls);
return this;
}
stop() {
this._pause = true;
}
start() {
this._pause = false;
this._update();
}
// Destroys the world(!) and removes it from the scene and memory
//
// TODO: World out why so much three.js stuff is left in the heap after this
destroy() {
this.stop();
// Remove listeners
this.off('controlsMoveEnd', this._onControlsMoveEnd);
var i;
// Remove all controls
var controls;
for (i = this._controls.length - 1; i >= 0; i--) {
controls = this._controls[0];
this.removeControls(controls);
controls.destroy();
};
// Remove all layers
var layer;
for (i = this._layers.length - 1; i >= 0; i--) {
layer = this._layers[0];
this.removeLayer(layer);
layer.destroy();
};
// Environment layer is removed with the other layers
this._environment = null;
this._engine.destroy();
this._engine = null;
// TODO: Probably should clean the container too / remove the canvas
this._container = null;
}
}
export default World;
var noNew = function(domId, options) {
return new World(domId, options);
};
// Initialise without requiring new keyword
export {noNew as world};

Wyświetl plik

@ -0,0 +1,23 @@
import THREE from 'three';
// This can only be accessed from Engine.camera if you want to reference the
// same scene in multiple places
// TODO: Ensure that FOV looks natural on all aspect ratios
// http://stackoverflow.com/q/26655930/997339
export default function(container) {
var camera = new THREE.PerspectiveCamera(45, 1, 1, 200000);
camera.position.y = 400;
camera.position.z = 400;
var updateSize = function() {
camera.aspect = container.clientWidth / container.clientHeight;
camera.updateProjectionMatrix();
};
window.addEventListener('resize', updateSize, false);
updateSize();
return camera;
};

Wyświetl plik

@ -46,6 +46,7 @@ class Engine extends EventEmitter {
// TODO: Set up composer to automatically resize on viewport change
// TODO: Update passes that rely on width / height on resize
// TODO: Merge default passes into a single shader / pass for performance
_initPostProcessing() {
var renderPass = new RenderPass(this._scene, this._camera);