From python CGI to jekyll stuff
This commit is contained in:
237
static/js/three/DDSLoader.js
Normal file
237
static/js/three/DDSLoader.js
Normal file
@@ -0,0 +1,237 @@
|
||||
/*
|
||||
* @author mrdoob / http://mrdoob.com/
|
||||
*/
|
||||
|
||||
THREE.DDSLoader = function () {
|
||||
this._parser = THREE.DDSLoader.parse;
|
||||
};
|
||||
|
||||
THREE.DDSLoader.prototype = Object.create( THREE.CompressedTextureLoader.prototype );
|
||||
THREE.DDSLoader.prototype.constructor = THREE.DDSLoader;
|
||||
|
||||
THREE.DDSLoader.parse = function ( buffer, loadMipmaps ) {
|
||||
|
||||
var dds = { mipmaps: [], width: 0, height: 0, format: null, mipmapCount: 1 };
|
||||
|
||||
// Adapted from @toji's DDS utils
|
||||
// https://github.com/toji/webgl-texture-utils/blob/master/texture-util/dds.js
|
||||
|
||||
// All values and structures referenced from:
|
||||
// http://msdn.microsoft.com/en-us/library/bb943991.aspx/
|
||||
|
||||
var DDS_MAGIC = 0x20534444;
|
||||
|
||||
var DDSD_CAPS = 0x1,
|
||||
DDSD_HEIGHT = 0x2,
|
||||
DDSD_WIDTH = 0x4,
|
||||
DDSD_PITCH = 0x8,
|
||||
DDSD_PIXELFORMAT = 0x1000,
|
||||
DDSD_MIPMAPCOUNT = 0x20000,
|
||||
DDSD_LINEARSIZE = 0x80000,
|
||||
DDSD_DEPTH = 0x800000;
|
||||
|
||||
var DDSCAPS_COMPLEX = 0x8,
|
||||
DDSCAPS_MIPMAP = 0x400000,
|
||||
DDSCAPS_TEXTURE = 0x1000;
|
||||
|
||||
var DDSCAPS2_CUBEMAP = 0x200,
|
||||
DDSCAPS2_CUBEMAP_POSITIVEX = 0x400,
|
||||
DDSCAPS2_CUBEMAP_NEGATIVEX = 0x800,
|
||||
DDSCAPS2_CUBEMAP_POSITIVEY = 0x1000,
|
||||
DDSCAPS2_CUBEMAP_NEGATIVEY = 0x2000,
|
||||
DDSCAPS2_CUBEMAP_POSITIVEZ = 0x4000,
|
||||
DDSCAPS2_CUBEMAP_NEGATIVEZ = 0x8000,
|
||||
DDSCAPS2_VOLUME = 0x200000;
|
||||
|
||||
var DDPF_ALPHAPIXELS = 0x1,
|
||||
DDPF_ALPHA = 0x2,
|
||||
DDPF_FOURCC = 0x4,
|
||||
DDPF_RGB = 0x40,
|
||||
DDPF_YUV = 0x200,
|
||||
DDPF_LUMINANCE = 0x20000;
|
||||
|
||||
function fourCCToInt32( value ) {
|
||||
|
||||
return value.charCodeAt(0) +
|
||||
(value.charCodeAt(1) << 8) +
|
||||
(value.charCodeAt(2) << 16) +
|
||||
(value.charCodeAt(3) << 24);
|
||||
|
||||
}
|
||||
|
||||
function int32ToFourCC( value ) {
|
||||
|
||||
return String.fromCharCode(
|
||||
value & 0xff,
|
||||
(value >> 8) & 0xff,
|
||||
(value >> 16) & 0xff,
|
||||
(value >> 24) & 0xff
|
||||
);
|
||||
}
|
||||
|
||||
function loadARGBMip( buffer, dataOffset, width, height ) {
|
||||
var dataLength = width * height * 4;
|
||||
var srcBuffer = new Uint8Array( buffer, dataOffset, dataLength );
|
||||
var byteArray = new Uint8Array( dataLength );
|
||||
var dst = 0;
|
||||
var src = 0;
|
||||
for ( var y = 0; y < height; y ++ ) {
|
||||
for ( var x = 0; x < width; x ++ ) {
|
||||
var b = srcBuffer[src]; src ++;
|
||||
var g = srcBuffer[src]; src ++;
|
||||
var r = srcBuffer[src]; src ++;
|
||||
var a = srcBuffer[src]; src ++;
|
||||
byteArray[dst] = r; dst ++; //r
|
||||
byteArray[dst] = g; dst ++; //g
|
||||
byteArray[dst] = b; dst ++; //b
|
||||
byteArray[dst] = a; dst ++; //a
|
||||
}
|
||||
}
|
||||
return byteArray;
|
||||
}
|
||||
|
||||
var FOURCC_DXT1 = fourCCToInt32("DXT1");
|
||||
var FOURCC_DXT3 = fourCCToInt32("DXT3");
|
||||
var FOURCC_DXT5 = fourCCToInt32("DXT5");
|
||||
|
||||
var headerLengthInt = 31; // The header length in 32 bit ints
|
||||
|
||||
// Offsets into the header array
|
||||
|
||||
var off_magic = 0;
|
||||
|
||||
var off_size = 1;
|
||||
var off_flags = 2;
|
||||
var off_height = 3;
|
||||
var off_width = 4;
|
||||
|
||||
var off_mipmapCount = 7;
|
||||
|
||||
var off_pfFlags = 20;
|
||||
var off_pfFourCC = 21;
|
||||
var off_RGBBitCount = 22;
|
||||
var off_RBitMask = 23;
|
||||
var off_GBitMask = 24;
|
||||
var off_BBitMask = 25;
|
||||
var off_ABitMask = 26;
|
||||
|
||||
var off_caps = 27;
|
||||
var off_caps2 = 28;
|
||||
var off_caps3 = 29;
|
||||
var off_caps4 = 30;
|
||||
|
||||
// Parse header
|
||||
|
||||
var header = new Int32Array( buffer, 0, headerLengthInt );
|
||||
|
||||
if ( header[ off_magic ] !== DDS_MAGIC ) {
|
||||
|
||||
console.error( 'THREE.DDSLoader.parse: Invalid magic number in DDS header.' );
|
||||
return dds;
|
||||
|
||||
}
|
||||
|
||||
if ( ! header[ off_pfFlags ] & DDPF_FOURCC ) {
|
||||
|
||||
console.error( 'THREE.DDSLoader.parse: Unsupported format, must contain a FourCC code.' );
|
||||
return dds;
|
||||
|
||||
}
|
||||
|
||||
var blockBytes;
|
||||
|
||||
var fourCC = header[ off_pfFourCC ];
|
||||
|
||||
var isRGBAUncompressed = false;
|
||||
|
||||
switch ( fourCC ) {
|
||||
|
||||
case FOURCC_DXT1:
|
||||
|
||||
blockBytes = 8;
|
||||
dds.format = THREE.RGB_S3TC_DXT1_Format;
|
||||
break;
|
||||
|
||||
case FOURCC_DXT3:
|
||||
|
||||
blockBytes = 16;
|
||||
dds.format = THREE.RGBA_S3TC_DXT3_Format;
|
||||
break;
|
||||
|
||||
case FOURCC_DXT5:
|
||||
|
||||
blockBytes = 16;
|
||||
dds.format = THREE.RGBA_S3TC_DXT5_Format;
|
||||
break;
|
||||
|
||||
default:
|
||||
|
||||
if ( header[off_RGBBitCount] == 32
|
||||
&& header[off_RBitMask]&0xff0000
|
||||
&& header[off_GBitMask]&0xff00
|
||||
&& header[off_BBitMask]&0xff
|
||||
&& header[off_ABitMask]&0xff000000 ) {
|
||||
isRGBAUncompressed = true;
|
||||
blockBytes = 64;
|
||||
dds.format = THREE.RGBAFormat;
|
||||
} else {
|
||||
console.error( 'THREE.DDSLoader.parse: Unsupported FourCC code ', int32ToFourCC( fourCC ) );
|
||||
return dds;
|
||||
}
|
||||
}
|
||||
|
||||
dds.mipmapCount = 1;
|
||||
|
||||
if ( header[ off_flags ] & DDSD_MIPMAPCOUNT && loadMipmaps !== false ) {
|
||||
|
||||
dds.mipmapCount = Math.max( 1, header[ off_mipmapCount ] );
|
||||
|
||||
}
|
||||
|
||||
//TODO: Verify that all faces of the cubemap are present with DDSCAPS2_CUBEMAP_POSITIVEX, etc.
|
||||
|
||||
dds.isCubemap = header[ off_caps2 ] & DDSCAPS2_CUBEMAP ? true : false;
|
||||
|
||||
dds.width = header[ off_width ];
|
||||
dds.height = header[ off_height ];
|
||||
|
||||
var dataOffset = header[ off_size ] + 4;
|
||||
|
||||
// Extract mipmaps buffers
|
||||
|
||||
var width = dds.width;
|
||||
var height = dds.height;
|
||||
|
||||
var faces = dds.isCubemap ? 6 : 1;
|
||||
|
||||
for ( var face = 0; face < faces; face ++ ) {
|
||||
|
||||
for ( var i = 0; i < dds.mipmapCount; i ++ ) {
|
||||
|
||||
if ( isRGBAUncompressed ) {
|
||||
var byteArray = loadARGBMip( buffer, dataOffset, width, height );
|
||||
var dataLength = byteArray.length;
|
||||
} else {
|
||||
var dataLength = Math.max( 4, width ) / 4 * Math.max( 4, height ) / 4 * blockBytes;
|
||||
var byteArray = new Uint8Array( buffer, dataOffset, dataLength );
|
||||
}
|
||||
|
||||
var mipmap = { "data": byteArray, "width": width, "height": height };
|
||||
dds.mipmaps.push( mipmap );
|
||||
|
||||
dataOffset += dataLength;
|
||||
|
||||
width = Math.max( width * 0.5, 1 );
|
||||
height = Math.max( height * 0.5, 1 );
|
||||
|
||||
}
|
||||
|
||||
width = dds.width;
|
||||
height = dds.height;
|
||||
|
||||
}
|
||||
|
||||
return dds;
|
||||
|
||||
};
|
||||
|
||||
441
static/js/three/MTLLoader.js
Normal file
441
static/js/three/MTLLoader.js
Normal file
@@ -0,0 +1,441 @@
|
||||
/**
|
||||
* Loads a Wavefront .mtl file specifying materials
|
||||
*
|
||||
* @author angelxuanchang
|
||||
*/
|
||||
|
||||
THREE.MTLLoader = function( baseUrl, options, crossOrigin ) {
|
||||
|
||||
this.baseUrl = baseUrl;
|
||||
this.options = options;
|
||||
this.crossOrigin = crossOrigin;
|
||||
|
||||
};
|
||||
|
||||
THREE.MTLLoader.prototype = {
|
||||
|
||||
constructor: THREE.MTLLoader,
|
||||
|
||||
load: function ( url, onLoad, onProgress, onError ) {
|
||||
|
||||
var scope = this;
|
||||
|
||||
var loader = new THREE.XHRLoader();
|
||||
loader.setCrossOrigin( this.crossOrigin );
|
||||
loader.load( url, function ( text ) {
|
||||
|
||||
onLoad( scope.parse( text ) );
|
||||
|
||||
}, onProgress, onError );
|
||||
|
||||
},
|
||||
|
||||
/**
|
||||
* Parses loaded MTL file
|
||||
* @param text - Content of MTL file
|
||||
* @return {THREE.MTLLoader.MaterialCreator}
|
||||
*/
|
||||
parse: function ( text ) {
|
||||
|
||||
var lines = text.split( "\n" );
|
||||
var info = {};
|
||||
var delimiter_pattern = /\s+/;
|
||||
var materialsInfo = {};
|
||||
|
||||
for ( var i = 0; i < lines.length; i ++ ) {
|
||||
|
||||
var line = lines[ i ];
|
||||
line = line.trim();
|
||||
|
||||
if ( line.length === 0 || line.charAt( 0 ) === '#' ) {
|
||||
|
||||
// Blank line or comment ignore
|
||||
continue;
|
||||
|
||||
}
|
||||
|
||||
var pos = line.indexOf( ' ' );
|
||||
|
||||
var key = ( pos >= 0 ) ? line.substring( 0, pos ) : line;
|
||||
key = key.toLowerCase();
|
||||
|
||||
var value = ( pos >= 0 ) ? line.substring( pos + 1 ) : "";
|
||||
value = value.trim();
|
||||
|
||||
if ( key === "newmtl" ) {
|
||||
|
||||
// New material
|
||||
|
||||
info = { name: value };
|
||||
materialsInfo[ value ] = info;
|
||||
|
||||
} else if ( info ) {
|
||||
|
||||
if ( key === "ka" || key === "kd" || key === "ks" ) {
|
||||
|
||||
var ss = value.split( delimiter_pattern, 3 );
|
||||
info[ key ] = [ parseFloat( ss[0] ), parseFloat( ss[1] ), parseFloat( ss[2] ) ];
|
||||
|
||||
} else {
|
||||
|
||||
info[ key ] = value;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
var materialCreator = new THREE.MTLLoader.MaterialCreator( this.baseUrl, this.options );
|
||||
materialCreator.crossOrigin = this.crossOrigin
|
||||
materialCreator.setMaterials( materialsInfo );
|
||||
return materialCreator;
|
||||
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
* Create a new THREE-MTLLoader.MaterialCreator
|
||||
* @param baseUrl - Url relative to which textures are loaded
|
||||
* @param options - Set of options on how to construct the materials
|
||||
* side: Which side to apply the material
|
||||
* THREE.FrontSide (default), THREE.BackSide, THREE.DoubleSide
|
||||
* wrap: What type of wrapping to apply for textures
|
||||
* THREE.RepeatWrapping (default), THREE.ClampToEdgeWrapping, THREE.MirroredRepeatWrapping
|
||||
* normalizeRGB: RGBs need to be normalized to 0-1 from 0-255
|
||||
* Default: false, assumed to be already normalized
|
||||
* ignoreZeroRGBs: Ignore values of RGBs (Ka,Kd,Ks) that are all 0's
|
||||
* Default: false
|
||||
* invertTransparency: If transparency need to be inverted (inversion is needed if d = 0 is fully opaque)
|
||||
* Default: false (d = 1 is fully opaque)
|
||||
* @constructor
|
||||
*/
|
||||
|
||||
THREE.MTLLoader.MaterialCreator = function( baseUrl, options ) {
|
||||
|
||||
this.baseUrl = baseUrl;
|
||||
this.options = options;
|
||||
this.materialsInfo = {};
|
||||
this.materials = {};
|
||||
this.materialsArray = [];
|
||||
this.nameLookup = {};
|
||||
|
||||
this.side = ( this.options && this.options.side ) ? this.options.side : THREE.FrontSide;
|
||||
this.wrap = ( this.options && this.options.wrap ) ? this.options.wrap : THREE.RepeatWrapping;
|
||||
|
||||
};
|
||||
|
||||
THREE.MTLLoader.MaterialCreator.prototype = {
|
||||
|
||||
constructor: THREE.MTLLoader.MaterialCreator,
|
||||
|
||||
setMaterials: function( materialsInfo ) {
|
||||
|
||||
this.materialsInfo = this.convert( materialsInfo );
|
||||
this.materials = {};
|
||||
this.materialsArray = [];
|
||||
this.nameLookup = {};
|
||||
|
||||
},
|
||||
|
||||
convert: function( materialsInfo ) {
|
||||
|
||||
if ( !this.options ) return materialsInfo;
|
||||
|
||||
var converted = {};
|
||||
|
||||
for ( var mn in materialsInfo ) {
|
||||
|
||||
// Convert materials info into normalized form based on options
|
||||
|
||||
var mat = materialsInfo[ mn ];
|
||||
|
||||
var covmat = {};
|
||||
|
||||
converted[ mn ] = covmat;
|
||||
|
||||
for ( var prop in mat ) {
|
||||
|
||||
var save = true;
|
||||
var value = mat[ prop ];
|
||||
var lprop = prop.toLowerCase();
|
||||
|
||||
switch ( lprop ) {
|
||||
|
||||
case 'kd':
|
||||
case 'ka':
|
||||
case 'ks':
|
||||
|
||||
// Diffuse color (color under white light) using RGB values
|
||||
|
||||
if ( this.options && this.options.normalizeRGB ) {
|
||||
|
||||
value = [ value[ 0 ] / 255, value[ 1 ] / 255, value[ 2 ] / 255 ];
|
||||
|
||||
}
|
||||
|
||||
if ( this.options && this.options.ignoreZeroRGBs ) {
|
||||
|
||||
if ( value[ 0 ] === 0 && value[ 1 ] === 0 && value[ 1 ] === 0 ) {
|
||||
|
||||
// ignore
|
||||
|
||||
save = false;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case 'd':
|
||||
|
||||
// According to MTL format (http://paulbourke.net/dataformats/mtl/):
|
||||
// d is dissolve for current material
|
||||
// factor of 1.0 is fully opaque, a factor of 0 is fully dissolved (completely transparent)
|
||||
|
||||
if ( this.options && this.options.invertTransparency ) {
|
||||
|
||||
value = 1 - value;
|
||||
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
default:
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
if ( save ) {
|
||||
|
||||
covmat[ lprop ] = value;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return converted;
|
||||
|
||||
},
|
||||
|
||||
preload: function () {
|
||||
|
||||
for ( var mn in this.materialsInfo ) {
|
||||
|
||||
this.create( mn );
|
||||
|
||||
}
|
||||
|
||||
},
|
||||
|
||||
getIndex: function( materialName ) {
|
||||
|
||||
return this.nameLookup[ materialName ];
|
||||
|
||||
},
|
||||
|
||||
getAsArray: function() {
|
||||
|
||||
var index = 0;
|
||||
|
||||
for ( var mn in this.materialsInfo ) {
|
||||
|
||||
this.materialsArray[ index ] = this.create( mn );
|
||||
this.nameLookup[ mn ] = index;
|
||||
index ++;
|
||||
|
||||
}
|
||||
|
||||
return this.materialsArray;
|
||||
|
||||
},
|
||||
|
||||
create: function ( materialName ) {
|
||||
|
||||
if ( this.materials[ materialName ] === undefined ) {
|
||||
|
||||
this.createMaterial_( materialName );
|
||||
|
||||
}
|
||||
|
||||
return this.materials[ materialName ];
|
||||
|
||||
},
|
||||
|
||||
createMaterial_: function ( materialName ) {
|
||||
|
||||
// Create material
|
||||
|
||||
var mat = this.materialsInfo[ materialName ];
|
||||
var params = {
|
||||
|
||||
name: materialName,
|
||||
side: this.side
|
||||
|
||||
};
|
||||
|
||||
for ( var prop in mat ) {
|
||||
|
||||
var value = mat[ prop ];
|
||||
|
||||
switch ( prop.toLowerCase() ) {
|
||||
|
||||
// Ns is material specular exponent
|
||||
|
||||
case 'kd':
|
||||
|
||||
// Diffuse color (color under white light) using RGB values
|
||||
|
||||
params[ 'diffuse' ] = new THREE.Color().fromArray( value );
|
||||
|
||||
break;
|
||||
|
||||
case 'ka':
|
||||
|
||||
// Ambient color (color under shadow) using RGB values
|
||||
|
||||
break;
|
||||
|
||||
case 'ks':
|
||||
|
||||
// Specular color (color when light is reflected from shiny surface) using RGB values
|
||||
params[ 'specular' ] = new THREE.Color().fromArray( value );
|
||||
|
||||
break;
|
||||
|
||||
case 'map_kd':
|
||||
|
||||
// Diffuse texture map
|
||||
|
||||
params[ 'map' ] = this.loadTexture( this.baseUrl + value );
|
||||
params[ 'map' ].wrapS = this.wrap;
|
||||
params[ 'map' ].wrapT = this.wrap;
|
||||
|
||||
break;
|
||||
|
||||
case 'ns':
|
||||
|
||||
// The specular exponent (defines the focus of the specular highlight)
|
||||
// A high exponent results in a tight, concentrated highlight. Ns values normally range from 0 to 1000.
|
||||
|
||||
params['shininess'] = value;
|
||||
|
||||
break;
|
||||
|
||||
case 'd':
|
||||
|
||||
// According to MTL format (http://paulbourke.net/dataformats/mtl/):
|
||||
// d is dissolve for current material
|
||||
// factor of 1.0 is fully opaque, a factor of 0 is fully dissolved (completely transparent)
|
||||
|
||||
if ( value < 1 ) {
|
||||
|
||||
params['transparent'] = true;
|
||||
params['opacity'] = value;
|
||||
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case 'map_bump':
|
||||
case 'bump':
|
||||
|
||||
// Bump texture map
|
||||
|
||||
if ( params[ 'bumpMap' ] ) break; // Avoid loading twice.
|
||||
|
||||
params[ 'bumpMap' ] = this.loadTexture( this.baseUrl + value );
|
||||
params[ 'bumpMap' ].wrapS = this.wrap;
|
||||
params[ 'bumpMap' ].wrapT = this.wrap;
|
||||
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if ( params[ 'diffuse' ] ) {
|
||||
|
||||
params[ 'color' ] = params[ 'diffuse' ];
|
||||
|
||||
}
|
||||
|
||||
this.materials[ materialName ] = new THREE.MeshPhongMaterial( params );
|
||||
return this.materials[ materialName ];
|
||||
|
||||
},
|
||||
|
||||
|
||||
loadTexture: function ( url, mapping, onLoad, onError ) {
|
||||
|
||||
var texture;
|
||||
var loader = THREE.Loader.Handlers.get( url );
|
||||
|
||||
if ( loader !== null ) {
|
||||
|
||||
texture = loader.load( url, onLoad );
|
||||
|
||||
} else {
|
||||
|
||||
texture = new THREE.Texture();
|
||||
|
||||
loader = new THREE.ImageLoader();
|
||||
loader.crossOrigin = this.crossOrigin;
|
||||
loader.load( url, function ( image ) {
|
||||
|
||||
texture.image = THREE.MTLLoader.ensurePowerOfTwo_( image );
|
||||
texture.needsUpdate = true;
|
||||
|
||||
if ( onLoad ) onLoad( texture );
|
||||
|
||||
} );
|
||||
|
||||
}
|
||||
|
||||
if ( mapping !== undefined ) texture.mapping = mapping;
|
||||
|
||||
return texture;
|
||||
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
THREE.MTLLoader.ensurePowerOfTwo_ = function ( image ) {
|
||||
|
||||
if ( ! THREE.Math.isPowerOfTwo( image.width ) || ! THREE.Math.isPowerOfTwo( image.height ) ) {
|
||||
|
||||
var canvas = document.createElement( "canvas" );
|
||||
canvas.width = THREE.MTLLoader.nextHighestPowerOfTwo_( image.width );
|
||||
canvas.height = THREE.MTLLoader.nextHighestPowerOfTwo_( image.height );
|
||||
|
||||
var ctx = canvas.getContext("2d");
|
||||
ctx.drawImage( image, 0, 0, image.width, image.height, 0, 0, canvas.width, canvas.height );
|
||||
return canvas;
|
||||
|
||||
}
|
||||
|
||||
return image;
|
||||
|
||||
};
|
||||
|
||||
THREE.MTLLoader.nextHighestPowerOfTwo_ = function( x ) {
|
||||
|
||||
-- x;
|
||||
|
||||
for ( var i = 1; i < 32; i <<= 1 ) {
|
||||
|
||||
x = x | x >> i;
|
||||
|
||||
}
|
||||
|
||||
return x + 1;
|
||||
|
||||
};
|
||||
|
||||
THREE.EventDispatcher.prototype.apply( THREE.MTLLoader.prototype );
|
||||
372
static/js/three/OBJLoader.js
Normal file
372
static/js/three/OBJLoader.js
Normal file
@@ -0,0 +1,372 @@
|
||||
/**
|
||||
* @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 );
|
||||
var id;
|
||||
|
||||
if ( d === undefined ) {
|
||||
|
||||
addVertex( ia, ib, ic );
|
||||
|
||||
} else {
|
||||
|
||||
id = parseVertexIndex( d );
|
||||
|
||||
addVertex( ia, ib, id );
|
||||
addVertex( ib, ic, id );
|
||||
|
||||
}
|
||||
|
||||
if ( ua !== undefined ) {
|
||||
|
||||
ia = parseUVIndex( ua );
|
||||
ib = parseUVIndex( ub );
|
||||
ic = parseUVIndex( uc );
|
||||
|
||||
if ( d === undefined ) {
|
||||
|
||||
addUV( ia, ib, ic );
|
||||
|
||||
} else {
|
||||
|
||||
id = parseUVIndex( ud );
|
||||
|
||||
addUV( ia, ib, id );
|
||||
addUV( ib, ic, id );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if ( na !== undefined ) {
|
||||
|
||||
ia = parseNormalIndex( na );
|
||||
ib = parseNormalIndex( nb );
|
||||
ic = parseNormalIndex( nc );
|
||||
|
||||
if ( d === undefined ) {
|
||||
|
||||
addNormal( ia, ib, ic );
|
||||
|
||||
} else {
|
||||
|
||||
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 ++ ) {
|
||||
|
||||
object = objects[ i ];
|
||||
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 ) );
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
}
|
||||
|
||||
};
|
||||
365
static/js/three/OBJMTLLoader.js
Normal file
365
static/js/three/OBJMTLLoader.js
Normal file
@@ -0,0 +1,365 @@
|
||||
/**
|
||||
* Loads a Wavefront .obj file with materials
|
||||
*
|
||||
* @author mrdoob / http://mrdoob.com/
|
||||
* @author angelxuanchang
|
||||
*/
|
||||
|
||||
THREE.OBJMTLLoader = function ( manager ) {
|
||||
|
||||
this.manager = ( manager !== undefined ) ? manager : THREE.DefaultLoadingManager;
|
||||
|
||||
};
|
||||
|
||||
THREE.OBJMTLLoader.prototype = {
|
||||
|
||||
constructor: THREE.OBJMTLLoader,
|
||||
|
||||
load: function ( url, mtlurl, onLoad, onProgress, onError ) {
|
||||
|
||||
var scope = this;
|
||||
|
||||
var mtlLoader = new THREE.MTLLoader( url.substr( 0, url.lastIndexOf( "/" ) + 1 ) );
|
||||
mtlLoader.crossOrigin = scope.crossOrigin;
|
||||
mtlLoader.load( mtlurl, function ( materials ) {
|
||||
|
||||
var materialsCreator = materials;
|
||||
materialsCreator.preload();
|
||||
|
||||
var loader = new THREE.XHRLoader( scope.manager );
|
||||
loader.setCrossOrigin( scope.crossOrigin );
|
||||
loader.load( url, function ( text ) {
|
||||
|
||||
var object = scope.parse( text );
|
||||
|
||||
object.traverse( function ( object ) {
|
||||
|
||||
if ( object instanceof THREE.Mesh ) {
|
||||
|
||||
if ( object.material.name ) {
|
||||
|
||||
var material = materialsCreator.create( object.material.name );
|
||||
|
||||
if ( material ) object.material = material;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
} );
|
||||
|
||||
onLoad( object );
|
||||
|
||||
}, onProgress, onError );
|
||||
|
||||
}, onProgress, onError );
|
||||
|
||||
},
|
||||
|
||||
/**
|
||||
* Parses loaded .obj file
|
||||
* @param data - content of .obj file
|
||||
* @param mtllibCallback - callback to handle mtllib declaration (optional)
|
||||
* @return {THREE.Object3D} - Object3D (with default material)
|
||||
*/
|
||||
|
||||
parse: function ( data, mtllibCallback ) {
|
||||
|
||||
function vector( x, y, z ) {
|
||||
|
||||
return new THREE.Vector3( x, y, z );
|
||||
|
||||
}
|
||||
|
||||
function uv( u, v ) {
|
||||
|
||||
return new THREE.Vector2( u, v );
|
||||
|
||||
}
|
||||
|
||||
function face3( a, b, c, normals ) {
|
||||
|
||||
return new THREE.Face3( a, b, c, normals );
|
||||
|
||||
}
|
||||
|
||||
var face_offset = 0;
|
||||
|
||||
function meshN( meshName, materialName ) {
|
||||
|
||||
if ( vertices.length > 0 ) {
|
||||
|
||||
geometry.vertices = vertices;
|
||||
|
||||
geometry.mergeVertices();
|
||||
geometry.computeFaceNormals();
|
||||
geometry.computeBoundingSphere();
|
||||
|
||||
object.add( mesh );
|
||||
|
||||
geometry = new THREE.Geometry();
|
||||
mesh = new THREE.Mesh( geometry, material );
|
||||
|
||||
}
|
||||
|
||||
if ( meshName !== undefined ) mesh.name = meshName;
|
||||
|
||||
if ( materialName !== undefined ) {
|
||||
|
||||
material = new THREE.MeshLambertMaterial();
|
||||
material.name = materialName;
|
||||
|
||||
mesh.material = material;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
var group = new THREE.Group();
|
||||
var object = group;
|
||||
|
||||
var geometry = new THREE.Geometry();
|
||||
var material = new THREE.MeshLambertMaterial();
|
||||
var mesh = new THREE.Mesh( geometry, material );
|
||||
|
||||
var vertices = [];
|
||||
var normals = [];
|
||||
var uvs = [];
|
||||
|
||||
function add_face( a, b, c, normals_inds ) {
|
||||
|
||||
if ( normals_inds === undefined ) {
|
||||
|
||||
geometry.faces.push( face3(
|
||||
parseInt( a ) - (face_offset + 1),
|
||||
parseInt( b ) - (face_offset + 1),
|
||||
parseInt( c ) - (face_offset + 1)
|
||||
) );
|
||||
|
||||
} else {
|
||||
|
||||
geometry.faces.push( face3(
|
||||
parseInt( a ) - (face_offset + 1),
|
||||
parseInt( b ) - (face_offset + 1),
|
||||
parseInt( c ) - (face_offset + 1),
|
||||
[
|
||||
normals[ parseInt( normals_inds[ 0 ] ) - 1 ].clone(),
|
||||
normals[ parseInt( normals_inds[ 1 ] ) - 1 ].clone(),
|
||||
normals[ parseInt( normals_inds[ 2 ] ) - 1 ].clone()
|
||||
]
|
||||
) );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
function add_uvs( a, b, c ) {
|
||||
|
||||
geometry.faceVertexUvs[ 0 ].push( [
|
||||
uvs[ parseInt( a ) - 1 ].clone(),
|
||||
uvs[ parseInt( b ) - 1 ].clone(),
|
||||
uvs[ parseInt( c ) - 1 ].clone()
|
||||
] );
|
||||
|
||||
}
|
||||
|
||||
function handle_face_line(faces, uvs, normals_inds) {
|
||||
|
||||
if ( faces[ 3 ] === undefined ) {
|
||||
|
||||
add_face( faces[ 0 ], faces[ 1 ], faces[ 2 ], normals_inds );
|
||||
|
||||
if (!(uvs === undefined) && uvs.length > 0) {
|
||||
add_uvs( uvs[ 0 ], uvs[ 1 ], uvs[ 2 ] );
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
if (!(normals_inds === undefined) && normals_inds.length > 0) {
|
||||
|
||||
add_face( faces[ 0 ], faces[ 1 ], faces[ 3 ], [ normals_inds[ 0 ], normals_inds[ 1 ], normals_inds[ 3 ] ]);
|
||||
add_face( faces[ 1 ], faces[ 2 ], faces[ 3 ], [ normals_inds[ 1 ], normals_inds[ 2 ], normals_inds[ 3 ] ]);
|
||||
|
||||
} else {
|
||||
|
||||
add_face( faces[ 0 ], faces[ 1 ], faces[ 3 ]);
|
||||
add_face( faces[ 1 ], faces[ 2 ], faces[ 3 ]);
|
||||
|
||||
}
|
||||
|
||||
if (!(uvs === undefined) && uvs.length > 0) {
|
||||
|
||||
add_uvs( uvs[ 0 ], uvs[ 1 ], uvs[ 3 ] );
|
||||
add_uvs( uvs[ 1 ], uvs[ 2 ], uvs[ 3 ] );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
// v float float float
|
||||
|
||||
var vertex_pattern = /v( +[\d|\.|\+|\-|e]+)( +[\d|\.|\+|\-|e]+)( +[\d|\.|\+|\-|e]+)/;
|
||||
|
||||
// vn float float float
|
||||
|
||||
var normal_pattern = /vn( +[\d|\.|\+|\-|e]+)( +[\d|\.|\+|\-|e]+)( +[\d|\.|\+|\-|e]+)/;
|
||||
|
||||
// vt float float
|
||||
|
||||
var uv_pattern = /vt( +[\d|\.|\+|\-|e]+)( +[\d|\.|\+|\-|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 = data.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( vector(
|
||||
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( vector(
|
||||
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( uv(
|
||||
parseFloat( result[ 1 ] ),
|
||||
parseFloat( result[ 2 ] )
|
||||
) );
|
||||
|
||||
} else if ( ( result = face_pattern1.exec( line ) ) !== null ) {
|
||||
|
||||
// ["f 1 2 3", "1", "2", "3", undefined]
|
||||
|
||||
handle_face_line([ 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]
|
||||
|
||||
handle_face_line(
|
||||
[ result[ 2 ], result[ 5 ], result[ 8 ], result[ 11 ] ], //faces
|
||||
[ result[ 3 ], result[ 6 ], result[ 9 ], result[ 12 ] ] //uv
|
||||
);
|
||||
|
||||
} 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]
|
||||
|
||||
handle_face_line(
|
||||
[ result[ 2 ], result[ 6 ], result[ 10 ], result[ 14 ] ], //faces
|
||||
[ result[ 3 ], result[ 7 ], result[ 11 ], result[ 15 ] ], //uv
|
||||
[ result[ 4 ], result[ 8 ], result[ 12 ], result[ 16 ] ] //normal
|
||||
);
|
||||
|
||||
} 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]
|
||||
|
||||
handle_face_line(
|
||||
[ result[ 2 ], result[ 5 ], result[ 8 ], result[ 11 ] ], //faces
|
||||
[ ], //uv
|
||||
[ result[ 3 ], result[ 6 ], result[ 9 ], result[ 12 ] ] //normal
|
||||
);
|
||||
|
||||
} else if ( /^o /.test( line ) ) {
|
||||
|
||||
// object
|
||||
|
||||
meshN();
|
||||
face_offset = face_offset + vertices.length;
|
||||
vertices = [];
|
||||
object = new THREE.Object3D();
|
||||
object.name = line.substring( 2 ).trim();
|
||||
group.add( object );
|
||||
|
||||
} else if ( /^g /.test( line ) ) {
|
||||
|
||||
// group
|
||||
|
||||
meshN( line.substring( 2 ).trim(), undefined );
|
||||
|
||||
} else if ( /^usemtl /.test( line ) ) {
|
||||
|
||||
// material
|
||||
|
||||
meshN( undefined, line.substring( 7 ).trim() );
|
||||
|
||||
} else if ( /^mtllib /.test( line ) ) {
|
||||
|
||||
// mtl file
|
||||
|
||||
if ( mtllibCallback ) {
|
||||
|
||||
var mtlfile = line.substring( 7 );
|
||||
mtlfile = mtlfile.trim();
|
||||
mtllibCallback( mtlfile );
|
||||
|
||||
}
|
||||
|
||||
} else if ( /^s /.test( line ) ) {
|
||||
|
||||
// Smooth shading
|
||||
|
||||
} else {
|
||||
|
||||
console.log( "THREE.OBJMTLLoader: Unhandled line " + line );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
//Add last object
|
||||
meshN(undefined, undefined);
|
||||
|
||||
return group;
|
||||
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
THREE.EventDispatcher.prototype.apply( THREE.OBJMTLLoader.prototype );
|
||||
706
static/js/three/OrbitControls.js
Normal file
706
static/js/three/OrbitControls.js
Normal file
@@ -0,0 +1,706 @@
|
||||
/**
|
||||
* @author qiao / https://github.com/qiao
|
||||
* @author mrdoob / http://mrdoob.com
|
||||
* @author alteredq / http://alteredqualia.com/
|
||||
* @author WestLangley / http://github.com/WestLangley
|
||||
* @author erich666 / http://erichaines.com
|
||||
*/
|
||||
/*global THREE, console */
|
||||
|
||||
// This set of controls performs orbiting, dollying (zooming), and panning. It maintains
|
||||
// the "up" direction as +Y, unlike the TrackballControls. Touch on tablet and phones is
|
||||
// supported.
|
||||
//
|
||||
// Orbit - left mouse / touch: one finger move
|
||||
// Zoom - middle mouse, or mousewheel / touch: two finger spread or squish
|
||||
// Pan - right mouse, or arrow keys / touch: three finter swipe
|
||||
|
||||
THREE.OrbitControls = function ( object, domElement ) {
|
||||
|
||||
this.object = object;
|
||||
this.domElement = ( domElement !== undefined ) ? domElement : document;
|
||||
|
||||
// API
|
||||
|
||||
// Set to false to disable this control
|
||||
this.enabled = true;
|
||||
|
||||
// "target" sets the location of focus, where the control orbits around
|
||||
// and where it pans with respect to.
|
||||
this.target = new THREE.Vector3();
|
||||
|
||||
// center is old, deprecated; use "target" instead
|
||||
this.center = this.target;
|
||||
|
||||
// This option actually enables dollying in and out; left as "zoom" for
|
||||
// backwards compatibility
|
||||
this.noZoom = false;
|
||||
this.zoomSpeed = 1.0;
|
||||
|
||||
// Limits to how far you can dolly in and out ( PerspectiveCamera only )
|
||||
this.minDistance = 0;
|
||||
this.maxDistance = Infinity;
|
||||
|
||||
// Limits to how far you can zoom in and out ( OrthographicCamera only )
|
||||
this.minZoom = 0;
|
||||
this.maxZoom = Infinity;
|
||||
|
||||
// Set to true to disable this control
|
||||
this.noRotate = false;
|
||||
this.rotateSpeed = 1.0;
|
||||
|
||||
// Set to true to disable this control
|
||||
this.noPan = false;
|
||||
this.keyPanSpeed = 7.0; // pixels moved per arrow key push
|
||||
|
||||
// Set to true to automatically rotate around the target
|
||||
this.autoRotate = false;
|
||||
this.autoRotateSpeed = 2.0; // 30 seconds per round when fps is 60
|
||||
|
||||
// How far you can orbit vertically, upper and lower limits.
|
||||
// Range is 0 to Math.PI radians.
|
||||
this.minPolarAngle = 0; // radians
|
||||
this.maxPolarAngle = Math.PI; // radians
|
||||
|
||||
// How far you can orbit horizontally, upper and lower limits.
|
||||
// If set, must be a sub-interval of the interval [ - Math.PI, Math.PI ].
|
||||
this.minAzimuthAngle = - Infinity; // radians
|
||||
this.maxAzimuthAngle = Infinity; // radians
|
||||
|
||||
// Set to true to disable use of the keys
|
||||
this.noKeys = false;
|
||||
|
||||
// The four arrow keys
|
||||
this.keys = { LEFT: 37, UP: 38, RIGHT: 39, BOTTOM: 40 };
|
||||
|
||||
// Mouse buttons
|
||||
this.mouseButtons = { ORBIT: THREE.MOUSE.LEFT, ZOOM: THREE.MOUSE.MIDDLE, PAN: THREE.MOUSE.RIGHT };
|
||||
|
||||
////////////
|
||||
// internals
|
||||
|
||||
var scope = this;
|
||||
|
||||
var EPS = 0.000001;
|
||||
|
||||
var rotateStart = new THREE.Vector2();
|
||||
var rotateEnd = new THREE.Vector2();
|
||||
var rotateDelta = new THREE.Vector2();
|
||||
|
||||
var panStart = new THREE.Vector2();
|
||||
var panEnd = new THREE.Vector2();
|
||||
var panDelta = new THREE.Vector2();
|
||||
var panOffset = new THREE.Vector3();
|
||||
|
||||
var offset = new THREE.Vector3();
|
||||
|
||||
var dollyStart = new THREE.Vector2();
|
||||
var dollyEnd = new THREE.Vector2();
|
||||
var dollyDelta = new THREE.Vector2();
|
||||
|
||||
var theta;
|
||||
var phi;
|
||||
var phiDelta = 0;
|
||||
var thetaDelta = 0;
|
||||
var scale = 1;
|
||||
var pan = new THREE.Vector3();
|
||||
|
||||
var lastPosition = new THREE.Vector3();
|
||||
var lastQuaternion = new THREE.Quaternion();
|
||||
|
||||
var STATE = { NONE : -1, ROTATE : 0, DOLLY : 1, PAN : 2, TOUCH_ROTATE : 3, TOUCH_DOLLY : 4, TOUCH_PAN : 5 };
|
||||
|
||||
var state = STATE.NONE;
|
||||
|
||||
// for reset
|
||||
|
||||
this.target0 = this.target.clone();
|
||||
this.position0 = this.object.position.clone();
|
||||
this.zoom0 = this.object.zoom;
|
||||
|
||||
// so camera.up is the orbit axis
|
||||
|
||||
var quat = new THREE.Quaternion().setFromUnitVectors( object.up, new THREE.Vector3( 0, 1, 0 ) );
|
||||
var quatInverse = quat.clone().inverse();
|
||||
|
||||
// events
|
||||
|
||||
var changeEvent = { type: 'change' };
|
||||
var startEvent = { type: 'start' };
|
||||
var endEvent = { type: 'end' };
|
||||
|
||||
this.rotateLeft = function ( angle ) {
|
||||
|
||||
if ( angle === undefined ) {
|
||||
|
||||
angle = getAutoRotationAngle();
|
||||
|
||||
}
|
||||
|
||||
thetaDelta -= angle;
|
||||
|
||||
};
|
||||
|
||||
this.rotateUp = function ( angle ) {
|
||||
|
||||
if ( angle === undefined ) {
|
||||
|
||||
angle = getAutoRotationAngle();
|
||||
|
||||
}
|
||||
|
||||
phiDelta -= angle;
|
||||
|
||||
};
|
||||
|
||||
// pass in distance in world space to move left
|
||||
this.panLeft = function ( distance ) {
|
||||
|
||||
var te = this.object.matrix.elements;
|
||||
|
||||
// get X column of matrix
|
||||
panOffset.set( te[ 0 ], te[ 1 ], te[ 2 ] );
|
||||
panOffset.multiplyScalar( - distance );
|
||||
|
||||
pan.add( panOffset );
|
||||
|
||||
};
|
||||
|
||||
// pass in distance in world space to move up
|
||||
this.panUp = function ( distance ) {
|
||||
|
||||
var te = this.object.matrix.elements;
|
||||
|
||||
// get Y column of matrix
|
||||
panOffset.set( te[ 4 ], te[ 5 ], te[ 6 ] );
|
||||
panOffset.multiplyScalar( distance );
|
||||
|
||||
pan.add( panOffset );
|
||||
|
||||
};
|
||||
|
||||
// pass in x,y of change desired in pixel space,
|
||||
// right and down are positive
|
||||
this.pan = function ( deltaX, deltaY ) {
|
||||
|
||||
var element = scope.domElement === document ? scope.domElement.body : scope.domElement;
|
||||
|
||||
if ( scope.object instanceof THREE.PerspectiveCamera ) {
|
||||
|
||||
// perspective
|
||||
var position = scope.object.position;
|
||||
var offset = position.clone().sub( scope.target );
|
||||
var targetDistance = offset.length();
|
||||
|
||||
// half of the fov is center to top of screen
|
||||
targetDistance *= Math.tan( ( scope.object.fov / 2 ) * Math.PI / 180.0 );
|
||||
|
||||
// we actually don't use screenWidth, since perspective camera is fixed to screen height
|
||||
scope.panLeft( 2 * deltaX * targetDistance / element.clientHeight );
|
||||
scope.panUp( 2 * deltaY * targetDistance / element.clientHeight );
|
||||
|
||||
} else if ( scope.object instanceof THREE.OrthographicCamera ) {
|
||||
|
||||
// orthographic
|
||||
scope.panLeft( deltaX * (scope.object.right - scope.object.left) / element.clientWidth );
|
||||
scope.panUp( deltaY * (scope.object.top - scope.object.bottom) / element.clientHeight );
|
||||
|
||||
} else {
|
||||
|
||||
// camera neither orthographic or perspective
|
||||
console.warn( 'WARNING: OrbitControls.js encountered an unknown camera type - pan disabled.' );
|
||||
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
this.dollyIn = function ( dollyScale ) {
|
||||
|
||||
if ( dollyScale === undefined ) {
|
||||
|
||||
dollyScale = getZoomScale();
|
||||
|
||||
}
|
||||
|
||||
if ( scope.object instanceof THREE.PerspectiveCamera ) {
|
||||
|
||||
scale /= dollyScale;
|
||||
|
||||
} else if ( scope.object instanceof THREE.OrthographicCamera ) {
|
||||
|
||||
scope.object.zoom = Math.max( this.minZoom, Math.min( this.maxZoom, this.object.zoom * dollyScale ) );
|
||||
scope.object.updateProjectionMatrix();
|
||||
scope.dispatchEvent( changeEvent );
|
||||
|
||||
} else {
|
||||
|
||||
console.warn( 'WARNING: OrbitControls.js encountered an unknown camera type - dolly/zoom disabled.' );
|
||||
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
this.dollyOut = function ( dollyScale ) {
|
||||
|
||||
if ( dollyScale === undefined ) {
|
||||
|
||||
dollyScale = getZoomScale();
|
||||
|
||||
}
|
||||
|
||||
if ( scope.object instanceof THREE.PerspectiveCamera ) {
|
||||
|
||||
scale *= dollyScale;
|
||||
|
||||
} else if ( scope.object instanceof THREE.OrthographicCamera ) {
|
||||
|
||||
scope.object.zoom = Math.max( this.minZoom, Math.min( this.maxZoom, this.object.zoom / dollyScale ) );
|
||||
scope.object.updateProjectionMatrix();
|
||||
scope.dispatchEvent( changeEvent );
|
||||
|
||||
} else {
|
||||
|
||||
console.warn( 'WARNING: OrbitControls.js encountered an unknown camera type - dolly/zoom disabled.' );
|
||||
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
this.update = function () {
|
||||
|
||||
var position = this.object.position;
|
||||
|
||||
offset.copy( position ).sub( this.target );
|
||||
|
||||
// rotate offset to "y-axis-is-up" space
|
||||
offset.applyQuaternion( quat );
|
||||
|
||||
// angle from z-axis around y-axis
|
||||
|
||||
theta = Math.atan2( offset.x, offset.z );
|
||||
|
||||
// angle from y-axis
|
||||
|
||||
phi = Math.atan2( Math.sqrt( offset.x * offset.x + offset.z * offset.z ), offset.y );
|
||||
|
||||
if ( this.autoRotate && state === STATE.NONE ) {
|
||||
|
||||
this.rotateLeft( getAutoRotationAngle() );
|
||||
|
||||
}
|
||||
|
||||
theta += thetaDelta;
|
||||
phi += phiDelta;
|
||||
|
||||
// restrict theta to be between desired limits
|
||||
theta = Math.max( this.minAzimuthAngle, Math.min( this.maxAzimuthAngle, theta ) );
|
||||
|
||||
// restrict phi to be between desired limits
|
||||
phi = Math.max( this.minPolarAngle, Math.min( this.maxPolarAngle, phi ) );
|
||||
|
||||
// restrict phi to be betwee EPS and PI-EPS
|
||||
phi = Math.max( EPS, Math.min( Math.PI - EPS, phi ) );
|
||||
|
||||
var radius = offset.length() * scale;
|
||||
|
||||
// restrict radius to be between desired limits
|
||||
radius = Math.max( this.minDistance, Math.min( this.maxDistance, radius ) );
|
||||
|
||||
// move target to panned location
|
||||
this.target.add( pan );
|
||||
|
||||
offset.x = radius * Math.sin( phi ) * Math.sin( theta );
|
||||
offset.y = radius * Math.cos( phi );
|
||||
offset.z = radius * Math.sin( phi ) * Math.cos( theta );
|
||||
|
||||
// rotate offset back to "camera-up-vector-is-up" space
|
||||
offset.applyQuaternion( quatInverse );
|
||||
|
||||
position.copy( this.target ).add( offset );
|
||||
|
||||
this.object.lookAt( this.target );
|
||||
|
||||
thetaDelta = 0;
|
||||
phiDelta = 0;
|
||||
scale = 1;
|
||||
pan.set( 0, 0, 0 );
|
||||
|
||||
// update condition is:
|
||||
// min(camera displacement, camera rotation in radians)^2 > EPS
|
||||
// using small-angle approximation cos(x/2) = 1 - x^2 / 8
|
||||
|
||||
if ( lastPosition.distanceToSquared( this.object.position ) > EPS
|
||||
|| 8 * (1 - lastQuaternion.dot(this.object.quaternion)) > EPS ) {
|
||||
|
||||
this.dispatchEvent( changeEvent );
|
||||
|
||||
lastPosition.copy( this.object.position );
|
||||
lastQuaternion.copy (this.object.quaternion );
|
||||
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
|
||||
this.reset = function () {
|
||||
|
||||
state = STATE.NONE;
|
||||
|
||||
this.target.copy( this.target0 );
|
||||
this.object.position.copy( this.position0 );
|
||||
this.object.zoom = this.zoom0;
|
||||
|
||||
this.object.updateProjectionMatrix();
|
||||
this.dispatchEvent( changeEvent );
|
||||
|
||||
this.update();
|
||||
|
||||
};
|
||||
|
||||
this.getPolarAngle = function () {
|
||||
|
||||
return phi;
|
||||
|
||||
};
|
||||
|
||||
this.getAzimuthalAngle = function () {
|
||||
|
||||
return theta
|
||||
|
||||
};
|
||||
|
||||
function getAutoRotationAngle() {
|
||||
|
||||
return 2 * Math.PI / 60 / 60 * scope.autoRotateSpeed;
|
||||
|
||||
}
|
||||
|
||||
function getZoomScale() {
|
||||
|
||||
return Math.pow( 0.95, scope.zoomSpeed );
|
||||
|
||||
}
|
||||
|
||||
function onMouseDown( event ) {
|
||||
|
||||
if ( scope.enabled === false ) return;
|
||||
event.preventDefault();
|
||||
|
||||
if ( event.button === scope.mouseButtons.ORBIT ) {
|
||||
if ( scope.noRotate === true ) return;
|
||||
|
||||
state = STATE.ROTATE;
|
||||
|
||||
rotateStart.set( event.clientX, event.clientY );
|
||||
|
||||
} else if ( event.button === scope.mouseButtons.ZOOM ) {
|
||||
if ( scope.noZoom === true ) return;
|
||||
|
||||
state = STATE.DOLLY;
|
||||
|
||||
dollyStart.set( event.clientX, event.clientY );
|
||||
|
||||
} else if ( event.button === scope.mouseButtons.PAN ) {
|
||||
if ( scope.noPan === true ) return;
|
||||
|
||||
state = STATE.PAN;
|
||||
|
||||
panStart.set( event.clientX, event.clientY );
|
||||
|
||||
}
|
||||
|
||||
if ( state !== STATE.NONE ) {
|
||||
document.addEventListener( 'mousemove', onMouseMove, false );
|
||||
document.addEventListener( 'mouseup', onMouseUp, false );
|
||||
scope.dispatchEvent( startEvent );
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
function onMouseMove( event ) {
|
||||
|
||||
if ( scope.enabled === false ) return;
|
||||
|
||||
event.preventDefault();
|
||||
|
||||
var element = scope.domElement === document ? scope.domElement.body : scope.domElement;
|
||||
|
||||
if ( state === STATE.ROTATE ) {
|
||||
|
||||
if ( scope.noRotate === true ) return;
|
||||
|
||||
rotateEnd.set( event.clientX, event.clientY );
|
||||
rotateDelta.subVectors( rotateEnd, rotateStart );
|
||||
|
||||
// rotating across whole screen goes 360 degrees around
|
||||
scope.rotateLeft( 2 * Math.PI * rotateDelta.x / element.clientWidth * scope.rotateSpeed );
|
||||
|
||||
// rotating up and down along whole screen attempts to go 360, but limited to 180
|
||||
scope.rotateUp( 2 * Math.PI * rotateDelta.y / element.clientHeight * scope.rotateSpeed );
|
||||
|
||||
rotateStart.copy( rotateEnd );
|
||||
|
||||
} else if ( state === STATE.DOLLY ) {
|
||||
|
||||
if ( scope.noZoom === true ) return;
|
||||
|
||||
dollyEnd.set( event.clientX, event.clientY );
|
||||
dollyDelta.subVectors( dollyEnd, dollyStart );
|
||||
|
||||
if ( dollyDelta.y > 0 ) {
|
||||
|
||||
scope.dollyIn();
|
||||
|
||||
} else if ( dollyDelta.y < 0 ) {
|
||||
|
||||
scope.dollyOut();
|
||||
|
||||
}
|
||||
|
||||
dollyStart.copy( dollyEnd );
|
||||
|
||||
} else if ( state === STATE.PAN ) {
|
||||
|
||||
if ( scope.noPan === true ) return;
|
||||
|
||||
panEnd.set( event.clientX, event.clientY );
|
||||
panDelta.subVectors( panEnd, panStart );
|
||||
|
||||
scope.pan( panDelta.x, panDelta.y );
|
||||
|
||||
panStart.copy( panEnd );
|
||||
|
||||
}
|
||||
|
||||
if ( state !== STATE.NONE ) scope.update();
|
||||
|
||||
}
|
||||
|
||||
function onMouseUp( /* event */ ) {
|
||||
|
||||
if ( scope.enabled === false ) return;
|
||||
|
||||
document.removeEventListener( 'mousemove', onMouseMove, false );
|
||||
document.removeEventListener( 'mouseup', onMouseUp, false );
|
||||
scope.dispatchEvent( endEvent );
|
||||
state = STATE.NONE;
|
||||
|
||||
}
|
||||
|
||||
function onMouseWheel( event ) {
|
||||
|
||||
if ( scope.enabled === false || scope.noZoom === true || state !== STATE.NONE ) return;
|
||||
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
|
||||
var delta = 0;
|
||||
|
||||
if ( event.wheelDelta !== undefined ) { // WebKit / Opera / Explorer 9
|
||||
|
||||
delta = event.wheelDelta;
|
||||
|
||||
} else if ( event.detail !== undefined ) { // Firefox
|
||||
|
||||
delta = - event.detail;
|
||||
|
||||
}
|
||||
|
||||
if ( delta > 0 ) {
|
||||
|
||||
scope.dollyOut();
|
||||
|
||||
} else if ( delta < 0 ) {
|
||||
|
||||
scope.dollyIn();
|
||||
|
||||
}
|
||||
|
||||
scope.update();
|
||||
scope.dispatchEvent( startEvent );
|
||||
scope.dispatchEvent( endEvent );
|
||||
|
||||
}
|
||||
|
||||
function onKeyDown( event ) {
|
||||
|
||||
if ( scope.enabled === false || scope.noKeys === true || scope.noPan === true ) return;
|
||||
|
||||
switch ( event.keyCode ) {
|
||||
|
||||
case scope.keys.UP:
|
||||
scope.pan( 0, scope.keyPanSpeed );
|
||||
scope.update();
|
||||
break;
|
||||
|
||||
case scope.keys.BOTTOM:
|
||||
scope.pan( 0, - scope.keyPanSpeed );
|
||||
scope.update();
|
||||
break;
|
||||
|
||||
case scope.keys.LEFT:
|
||||
scope.pan( scope.keyPanSpeed, 0 );
|
||||
scope.update();
|
||||
break;
|
||||
|
||||
case scope.keys.RIGHT:
|
||||
scope.pan( - scope.keyPanSpeed, 0 );
|
||||
scope.update();
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
function touchstart( event ) {
|
||||
|
||||
if ( scope.enabled === false ) return;
|
||||
|
||||
switch ( event.touches.length ) {
|
||||
|
||||
case 1: // one-fingered touch: rotate
|
||||
|
||||
if ( scope.noRotate === true ) return;
|
||||
|
||||
state = STATE.TOUCH_ROTATE;
|
||||
|
||||
rotateStart.set( event.touches[ 0 ].pageX, event.touches[ 0 ].pageY );
|
||||
break;
|
||||
|
||||
case 2: // two-fingered touch: dolly
|
||||
|
||||
if ( scope.noZoom === true ) return;
|
||||
|
||||
state = STATE.TOUCH_DOLLY;
|
||||
|
||||
var dx = event.touches[ 0 ].pageX - event.touches[ 1 ].pageX;
|
||||
var dy = event.touches[ 0 ].pageY - event.touches[ 1 ].pageY;
|
||||
var distance = Math.sqrt( dx * dx + dy * dy );
|
||||
dollyStart.set( 0, distance );
|
||||
break;
|
||||
|
||||
case 3: // three-fingered touch: pan
|
||||
|
||||
if ( scope.noPan === true ) return;
|
||||
|
||||
state = STATE.TOUCH_PAN;
|
||||
|
||||
panStart.set( event.touches[ 0 ].pageX, event.touches[ 0 ].pageY );
|
||||
break;
|
||||
|
||||
default:
|
||||
|
||||
state = STATE.NONE;
|
||||
|
||||
}
|
||||
|
||||
if ( state !== STATE.NONE ) scope.dispatchEvent( startEvent );
|
||||
|
||||
}
|
||||
|
||||
function touchmove( event ) {
|
||||
|
||||
if ( scope.enabled === false ) return;
|
||||
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
|
||||
var element = scope.domElement === document ? scope.domElement.body : scope.domElement;
|
||||
|
||||
switch ( event.touches.length ) {
|
||||
|
||||
case 1: // one-fingered touch: rotate
|
||||
|
||||
if ( scope.noRotate === true ) return;
|
||||
if ( state !== STATE.TOUCH_ROTATE ) return;
|
||||
|
||||
rotateEnd.set( event.touches[ 0 ].pageX, event.touches[ 0 ].pageY );
|
||||
rotateDelta.subVectors( rotateEnd, rotateStart );
|
||||
|
||||
// rotating across whole screen goes 360 degrees around
|
||||
scope.rotateLeft( 2 * Math.PI * rotateDelta.x / element.clientWidth * scope.rotateSpeed );
|
||||
// rotating up and down along whole screen attempts to go 360, but limited to 180
|
||||
scope.rotateUp( 2 * Math.PI * rotateDelta.y / element.clientHeight * scope.rotateSpeed );
|
||||
|
||||
rotateStart.copy( rotateEnd );
|
||||
|
||||
scope.update();
|
||||
break;
|
||||
|
||||
case 2: // two-fingered touch: dolly
|
||||
|
||||
if ( scope.noZoom === true ) return;
|
||||
if ( state !== STATE.TOUCH_DOLLY ) return;
|
||||
|
||||
var dx = event.touches[ 0 ].pageX - event.touches[ 1 ].pageX;
|
||||
var dy = event.touches[ 0 ].pageY - event.touches[ 1 ].pageY;
|
||||
var distance = Math.sqrt( dx * dx + dy * dy );
|
||||
|
||||
dollyEnd.set( 0, distance );
|
||||
dollyDelta.subVectors( dollyEnd, dollyStart );
|
||||
|
||||
if ( dollyDelta.y > 0 ) {
|
||||
|
||||
scope.dollyOut();
|
||||
|
||||
} else if ( dollyDelta.y < 0 ) {
|
||||
|
||||
scope.dollyIn();
|
||||
|
||||
}
|
||||
|
||||
dollyStart.copy( dollyEnd );
|
||||
|
||||
scope.update();
|
||||
break;
|
||||
|
||||
case 3: // three-fingered touch: pan
|
||||
|
||||
if ( scope.noPan === true ) return;
|
||||
if ( state !== STATE.TOUCH_PAN ) return;
|
||||
|
||||
panEnd.set( event.touches[ 0 ].pageX, event.touches[ 0 ].pageY );
|
||||
panDelta.subVectors( panEnd, panStart );
|
||||
|
||||
scope.pan( panDelta.x, panDelta.y );
|
||||
|
||||
panStart.copy( panEnd );
|
||||
|
||||
scope.update();
|
||||
break;
|
||||
|
||||
default:
|
||||
|
||||
state = STATE.NONE;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
function touchend( /* event */ ) {
|
||||
|
||||
if ( scope.enabled === false ) return;
|
||||
|
||||
scope.dispatchEvent( endEvent );
|
||||
state = STATE.NONE;
|
||||
|
||||
}
|
||||
|
||||
this.domElement.addEventListener( 'contextmenu', function ( event ) { event.preventDefault(); }, false );
|
||||
this.domElement.addEventListener( 'mousedown', onMouseDown, false );
|
||||
this.domElement.addEventListener( 'mousewheel', onMouseWheel, false );
|
||||
this.domElement.addEventListener( 'DOMMouseScroll', onMouseWheel, false ); // firefox
|
||||
|
||||
this.domElement.addEventListener( 'touchstart', touchstart, false );
|
||||
this.domElement.addEventListener( 'touchend', touchend, false );
|
||||
this.domElement.addEventListener( 'touchmove', touchmove, false );
|
||||
|
||||
window.addEventListener( 'keydown', onKeyDown, false );
|
||||
|
||||
// force an update at start
|
||||
this.update();
|
||||
|
||||
};
|
||||
|
||||
THREE.OrbitControls.prototype = Object.create( THREE.EventDispatcher.prototype );
|
||||
THREE.OrbitControls.prototype.constructor = THREE.OrbitControls;
|
||||
6
static/js/three/stats.min.js
vendored
Normal file
6
static/js/three/stats.min.js
vendored
Normal file
@@ -0,0 +1,6 @@
|
||||
// stats.js - http://github.com/mrdoob/stats.js
|
||||
var Stats=function(){var l=Date.now(),m=l,g=0,n=Infinity,o=0,h=0,p=Infinity,q=0,r=0,s=0,f=document.createElement("div");f.id="stats";f.addEventListener("mousedown",function(b){b.preventDefault();t(++s%2)},!1);f.style.cssText="width:80px;opacity:0.9;cursor:pointer";var a=document.createElement("div");a.id="fps";a.style.cssText="padding:0 0 3px 3px;text-align:left;background-color:#002";f.appendChild(a);var i=document.createElement("div");i.id="fpsText";i.style.cssText="color:#0ff;font-family:Helvetica,Arial,sans-serif;font-size:9px;font-weight:bold;line-height:15px";
|
||||
i.innerHTML="FPS";a.appendChild(i);var c=document.createElement("div");c.id="fpsGraph";c.style.cssText="position:relative;width:74px;height:30px;background-color:#0ff";for(a.appendChild(c);74>c.children.length;){var j=document.createElement("span");j.style.cssText="width:1px;height:30px;float:left;background-color:#113";c.appendChild(j)}var d=document.createElement("div");d.id="ms";d.style.cssText="padding:0 0 3px 3px;text-align:left;background-color:#020;display:none";f.appendChild(d);var k=document.createElement("div");
|
||||
k.id="msText";k.style.cssText="color:#0f0;font-family:Helvetica,Arial,sans-serif;font-size:9px;font-weight:bold;line-height:15px";k.innerHTML="MS";d.appendChild(k);var e=document.createElement("div");e.id="msGraph";e.style.cssText="position:relative;width:74px;height:30px;background-color:#0f0";for(d.appendChild(e);74>e.children.length;)j=document.createElement("span"),j.style.cssText="width:1px;height:30px;float:left;background-color:#131",e.appendChild(j);var t=function(b){s=b;switch(s){case 0:a.style.display=
|
||||
"block";d.style.display="none";break;case 1:a.style.display="none",d.style.display="block"}};return{REVISION:12,domElement:f,setMode:t,begin:function(){l=Date.now()},end:function(){var b=Date.now();g=b-l;n=Math.min(n,g);o=Math.max(o,g);k.textContent=g+" MS ("+n+"-"+o+")";var a=Math.min(30,30-30*(g/200));e.appendChild(e.firstChild).style.height=a+"px";r++;b>m+1E3&&(h=Math.round(1E3*r/(b-m)),p=Math.min(p,h),q=Math.max(q,h),i.textContent=h+" FPS ("+p+"-"+q+")",a=Math.min(30,30-30*(h/100)),c.appendChild(c.firstChild).style.height=
|
||||
a+"px",m=b,r=0);return b},update:function(){l=this.end()}}};"object"===typeof module&&(module.exports=Stats);
|
||||
835
static/js/three/three.min.js
vendored
Normal file
835
static/js/three/three.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user