Created
April 1, 2015 14:53
-
-
Save robhawkes/fa172359144592b61819 to your computer and use it in GitHub Desktop.
ViziCities OBJ
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* globals window, _, VIZI, d3, THREE */ | |
(function() { | |
"use strict"; | |
/** | |
* Blueprint OBJ output | |
* @author Robin Hawkes - vizicities.com | |
*/ | |
// output: { | |
// type: "BlueprintOutputOBJ", | |
// options: { | |
// modelPathPrefix: "/data/kml-model/" | |
// } | |
// } | |
VIZI.BlueprintOutputOBJ = function(options) { | |
var self = this; | |
VIZI.BlueprintOutput.call(self, options); | |
_.defaults(self.options, { | |
infoUI: false, | |
name: "OBJ" | |
}); | |
// Triggers and actions reference | |
self.triggers = [ | |
{name: "initialised", arguments: []} | |
]; | |
self.actions = [ | |
{name: "outputOBJ", arguments: ["obj"]} | |
]; | |
self.name = self.options.name; | |
self.world; | |
self.infoUI; | |
}; | |
VIZI.BlueprintOutputOBJ.prototype = Object.create( VIZI.BlueprintOutput.prototype ); | |
// Initialise instance and start automated processes | |
VIZI.BlueprintOutputOBJ.prototype.init = function() { | |
var self = this; | |
// Set up info UI | |
if (self.options.infoUI) { | |
self.infoUI = new VIZI.InfoUI2D(self.world); | |
} | |
self.emit("initialised"); | |
}; | |
// TODO: Process collada import and mesh generation in a Web Worker | |
// TODO: Throttle requests for collada files | |
VIZI.BlueprintOutputOBJ.prototype.outputOBJ = function(data) { | |
var self = this; | |
// TODO: Remove this hack around THREE.Loader.Handlers | |
// THREE.Loader.Handlers = {get: function(){ return null; }}; | |
var loader = new THREE.OBJLoader(); | |
// Local pixels per meter - set once per tile | |
var pixelsPerMeter; | |
_.each(data, function(item) { | |
var path = (self.options.modelPathPrefix) ? self.options.modelPathPrefix + item.modelPath : item.modelPath; | |
loader.load(path, function (obj) { | |
var latLon = new VIZI.LatLon(item.coordinates[1], item.coordinates[0]); | |
var geoCoord = self.world.project(latLon); | |
// Set local pixels per meter if not set | |
if (pixelsPerMeter === undefined) { | |
pixelsPerMeter = self.world.pixelsPerMeter(latLon); | |
} | |
// Move to correct position | |
obj.position.x = geoCoord.x; | |
obj.position.z = geoCoord.y; | |
// Scale value below 1 indicates collada units are in metres | |
// https://github.com/mrdoob/three.js/blob/master/examples/js/loaders/ColladaLoader.js#L219 | |
// if (dae.scale.x < 1) { | |
// Scale up model from meters to pixels | |
obj.scale.x = obj.scale.y = obj.scale.z = obj.scale.x * pixelsPerMeter.y; | |
obj.updateMatrix(); | |
// } | |
self.add(obj); | |
// Create info panel | |
// TODO: Work out a way to pass in custom text for the info panel or | |
// make it obvcious that you can only use the data avaiable. | |
if (self.infoUI) { | |
self.infoUI.addPanel(obj, obj.id); | |
} | |
}); | |
}); | |
}; | |
VIZI.BlueprintOutputOBJ.prototype.onTick = function(delta) { | |
var self = this; | |
// Update panel positions | |
// TODO: Work out how to remove the visible lag between panel position | |
// and actual scene / camera position. | |
if (self.infoUI) { | |
self.infoUI.onChange(); | |
} | |
} | |
VIZI.BlueprintOutputOBJ.prototype.onHide = function() { | |
var self = this; | |
if (self.infoUI) { | |
self.infoUI.onHide(); | |
} | |
}; | |
VIZI.BlueprintOutputOBJ.prototype.onShow = function() { | |
var self = this; | |
if (self.infoUI) { | |
self.infoUI.onShow(); | |
} | |
}; | |
VIZI.BlueprintOutputOBJ.prototype.onAdd = function(world) { | |
var self = this; | |
self.world = world; | |
self.init(); | |
}; | |
}()); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* @author mrdoob / http://mrdoob.com/ | |
*/ | |
THREE.OBJLoader = function ( manager ) { | |
this.manager = ( manager !== undefined ) ? manager : THREE.DefaultLoadingManager; | |
}; | |
THREE.OBJLoader.prototype = { | |
constructor: THREE.OBJLoader, | |
load: function ( url, onLoad, onProgress, onError ) { | |
var scope = this; | |
var loader = new THREE.XHRLoader( scope.manager ); | |
loader.setCrossOrigin( this.crossOrigin ); | |
loader.load( url, function ( text ) { | |
onLoad( scope.parse( text ) ); | |
}, onProgress, onError ); | |
}, | |
parse: function ( text ) { | |
console.time( 'OBJLoader' ); | |
var object, objects = []; | |
var geometry, material; | |
function parseVertexIndex( value ) { | |
var index = parseInt( value ); | |
return ( index >= 0 ? index - 1 : index + vertices.length / 3 ) * 3; | |
} | |
function parseNormalIndex( value ) { | |
var index = parseInt( value ); | |
return ( index >= 0 ? index - 1 : index + normals.length / 3 ) * 3; | |
} | |
function parseUVIndex( value ) { | |
var index = parseInt( value ); | |
return ( index >= 0 ? index - 1 : index + uvs.length / 2 ) * 2; | |
} | |
function addVertex( a, b, c ) { | |
geometry.vertices.push( | |
vertices[ a ], vertices[ a + 1 ], vertices[ a + 2 ], | |
vertices[ b ], vertices[ b + 1 ], vertices[ b + 2 ], | |
vertices[ c ], vertices[ c + 1 ], vertices[ c + 2 ] | |
); | |
} | |
function addNormal( a, b, c ) { | |
geometry.normals.push( | |
normals[ a ], normals[ a + 1 ], normals[ a + 2 ], | |
normals[ b ], normals[ b + 1 ], normals[ b + 2 ], | |
normals[ c ], normals[ c + 1 ], normals[ c + 2 ] | |
); | |
} | |
function addUV( a, b, c ) { | |
geometry.uvs.push( | |
uvs[ a ], uvs[ a + 1 ], | |
uvs[ b ], uvs[ b + 1 ], | |
uvs[ c ], uvs[ c + 1 ] | |
); | |
} | |
function addFace( a, b, c, d, ua, ub, uc, ud, na, nb, nc, nd ) { | |
var ia = parseVertexIndex( a ); | |
var ib = parseVertexIndex( b ); | |
var ic = parseVertexIndex( c ); | |
if ( d === undefined ) { | |
addVertex( ia, ib, ic ); | |
} else { | |
var id = parseVertexIndex( d ); | |
addVertex( ia, ib, id ); | |
addVertex( ib, ic, id ); | |
} | |
if ( ua !== undefined ) { | |
var ia = parseUVIndex( ua ); | |
var ib = parseUVIndex( ub ); | |
var ic = parseUVIndex( uc ); | |
if ( d === undefined ) { | |
addUV( ia, ib, ic ); | |
} else { | |
var id = parseUVIndex( ud ); | |
addUV( ia, ib, id ); | |
addUV( ib, ic, id ); | |
} | |
} | |
if ( na !== undefined ) { | |
var ia = parseNormalIndex( na ); | |
var ib = parseNormalIndex( nb ); | |
var ic = parseNormalIndex( nc ); | |
if ( d === undefined ) { | |
addNormal( ia, ib, ic ); | |
} else { | |
var id = parseNormalIndex( nd ); | |
addNormal( ia, ib, id ); | |
addNormal( ib, ic, id ); | |
} | |
} | |
} | |
// create mesh if no objects in text | |
if ( /^o /gm.test( text ) === false ) { | |
geometry = { | |
vertices: [], | |
normals: [], | |
uvs: [] | |
}; | |
material = { | |
name: '' | |
}; | |
object = { | |
name: '', | |
geometry: geometry, | |
material: material | |
}; | |
objects.push( object ); | |
} | |
var vertices = []; | |
var normals = []; | |
var uvs = []; | |
// v float float float | |
var vertex_pattern = /v( +[\d|\.|\+|\-|e|E]+)( +[\d|\.|\+|\-|e|E]+)( +[\d|\.|\+|\-|e|E]+)/; | |
// vn float float float | |
var normal_pattern = /vn( +[\d|\.|\+|\-|e|E]+)( +[\d|\.|\+|\-|e|E]+)( +[\d|\.|\+|\-|e|E]+)/; | |
// vt float float | |
var uv_pattern = /vt( +[\d|\.|\+|\-|e|E]+)( +[\d|\.|\+|\-|e|E]+)/; | |
// f vertex vertex vertex ... | |
var face_pattern1 = /f( +-?\d+)( +-?\d+)( +-?\d+)( +-?\d+)?/; | |
// f vertex/uv vertex/uv vertex/uv ... | |
var face_pattern2 = /f( +(-?\d+)\/(-?\d+))( +(-?\d+)\/(-?\d+))( +(-?\d+)\/(-?\d+))( +(-?\d+)\/(-?\d+))?/; | |
// f vertex/uv/normal vertex/uv/normal vertex/uv/normal ... | |
var face_pattern3 = /f( +(-?\d+)\/(-?\d+)\/(-?\d+))( +(-?\d+)\/(-?\d+)\/(-?\d+))( +(-?\d+)\/(-?\d+)\/(-?\d+))( +(-?\d+)\/(-?\d+)\/(-?\d+))?/; | |
// f vertex//normal vertex//normal vertex//normal ... | |
var face_pattern4 = /f( +(-?\d+)\/\/(-?\d+))( +(-?\d+)\/\/(-?\d+))( +(-?\d+)\/\/(-?\d+))( +(-?\d+)\/\/(-?\d+))?/ | |
// | |
var lines = text.split( '\n' ); | |
for ( var i = 0; i < lines.length; i ++ ) { | |
var line = lines[ i ]; | |
line = line.trim(); | |
var result; | |
if ( line.length === 0 || line.charAt( 0 ) === '#' ) { | |
continue; | |
} else if ( ( result = vertex_pattern.exec( line ) ) !== null ) { | |
// ["v 1.0 2.0 3.0", "1.0", "2.0", "3.0"] | |
vertices.push( | |
parseFloat( result[ 1 ] ), | |
parseFloat( result[ 2 ] ), | |
parseFloat( result[ 3 ] ) | |
); | |
} else if ( ( result = normal_pattern.exec( line ) ) !== null ) { | |
// ["vn 1.0 2.0 3.0", "1.0", "2.0", "3.0"] | |
normals.push( | |
parseFloat( result[ 1 ] ), | |
parseFloat( result[ 2 ] ), | |
parseFloat( result[ 3 ] ) | |
); | |
} else if ( ( result = uv_pattern.exec( line ) ) !== null ) { | |
// ["vt 0.1 0.2", "0.1", "0.2"] | |
uvs.push( | |
parseFloat( result[ 1 ] ), | |
parseFloat( result[ 2 ] ) | |
); | |
} else if ( ( result = face_pattern1.exec( line ) ) !== null ) { | |
// ["f 1 2 3", "1", "2", "3", undefined] | |
addFace( | |
result[ 1 ], result[ 2 ], result[ 3 ], result[ 4 ] | |
); | |
} else if ( ( result = face_pattern2.exec( line ) ) !== null ) { | |
// ["f 1/1 2/2 3/3", " 1/1", "1", "1", " 2/2", "2", "2", " 3/3", "3", "3", undefined, undefined, undefined] | |
addFace( | |
result[ 2 ], result[ 5 ], result[ 8 ], result[ 11 ], | |
result[ 3 ], result[ 6 ], result[ 9 ], result[ 12 ] | |
); | |
} else if ( ( result = face_pattern3.exec( line ) ) !== null ) { | |
// ["f 1/1/1 2/2/2 3/3/3", " 1/1/1", "1", "1", "1", " 2/2/2", "2", "2", "2", " 3/3/3", "3", "3", "3", undefined, undefined, undefined, undefined] | |
addFace( | |
result[ 2 ], result[ 6 ], result[ 10 ], result[ 14 ], | |
result[ 3 ], result[ 7 ], result[ 11 ], result[ 15 ], | |
result[ 4 ], result[ 8 ], result[ 12 ], result[ 16 ] | |
); | |
} else if ( ( result = face_pattern4.exec( line ) ) !== null ) { | |
// ["f 1//1 2//2 3//3", " 1//1", "1", "1", " 2//2", "2", "2", " 3//3", "3", "3", undefined, undefined, undefined] | |
addFace( | |
result[ 2 ], result[ 5 ], result[ 8 ], result[ 11 ], | |
undefined, undefined, undefined, undefined, | |
result[ 3 ], result[ 6 ], result[ 9 ], result[ 12 ] | |
); | |
} else if ( /^o /.test( line ) ) { | |
geometry = { | |
vertices: [], | |
normals: [], | |
uvs: [] | |
}; | |
material = { | |
name: '' | |
}; | |
object = { | |
name: line.substring( 2 ).trim(), | |
geometry: geometry, | |
material: material | |
}; | |
objects.push( object ) | |
} else if ( /^g /.test( line ) ) { | |
// group | |
} else if ( /^usemtl /.test( line ) ) { | |
// material | |
material.name = line.substring( 7 ).trim(); | |
} else if ( /^mtllib /.test( line ) ) { | |
// mtl file | |
} else if ( /^s /.test( line ) ) { | |
// smooth shading | |
} else { | |
// console.log( "THREE.OBJLoader: Unhandled line " + line ); | |
} | |
} | |
var container = new THREE.Object3D(); | |
for ( var i = 0, l = objects.length; i < l; i ++ ) { | |
var object = objects[ i ]; | |
var geometry = object.geometry; | |
var buffergeometry = new THREE.BufferGeometry(); | |
buffergeometry.addAttribute( 'position', new THREE.BufferAttribute( new Float32Array( geometry.vertices ), 3 ) ); | |
if ( geometry.normals.length > 0 ) { | |
buffergeometry.addAttribute( 'normal', new THREE.BufferAttribute( new Float32Array( geometry.normals ), 3 ) ); | |
} | |
if ( geometry.uvs.length > 0 ) { | |
buffergeometry.addAttribute( 'uv', new THREE.BufferAttribute( new Float32Array( geometry.uvs ), 2 ) ); | |
} | |
var material = new THREE.MeshLambertMaterial(); | |
material.name = object.material.name; | |
var mesh = new THREE.Mesh( buffergeometry, material ); | |
mesh.name = object.name; | |
container.add( mesh ); | |
} | |
console.timeEnd( 'OBJLoader' ); | |
return container; | |
} | |
}; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment