├── [6] Lighting ├── img │ ├── 1.png │ ├── 2.png │ ├── 3.png │ ├── 4.png │ ├── 5.png │ ├── 6.png │ ├── Wall.jpg │ ├── Ceiling.jpg │ └── Ground.jpg ├── index.html └── js │ └── OrbitControls.js ├── [10] Skybox ├── img │ └── Skybox │ │ ├── up.png │ │ ├── back.png │ │ ├── down.png │ │ ├── left.png │ │ ├── front.png │ │ └── right.png ├── models │ ├── Face_Color.jpg │ └── Face_Disp.jpg ├── index.html └── js │ └── OrbitControls.js ├── [7] Model Loading ├── img │ ├── 1.png │ ├── 2.png │ ├── 3.png │ ├── 4.png │ ├── 5.png │ ├── 6.png │ ├── Wall.jpg │ ├── Ceiling.jpg │ └── Ground.jpg ├── models │ ├── Face_Color.jpg │ └── Face_Disp.jpg ├── index.html └── js │ ├── JSONLoader.js │ ├── ObjectLoader.js │ └── OrbitControls.js ├── [9] Anaglyphic 3D ├── img │ ├── 1.png │ ├── 2.png │ ├── 3.png │ ├── 4.png │ ├── 5.png │ ├── 6.png │ ├── Wall.jpg │ ├── Ceiling.jpg │ └── Ground.jpg ├── models │ ├── Face_Color.jpg │ └── Face_Disp.jpg ├── js │ ├── AnaglyphEffect.js │ ├── ObjectLoader.js │ └── OrbitControls.js └── index.html ├── [5] Textures:Colours ├── img │ ├── 1.png │ ├── 2.png │ ├── 3.png │ ├── 4.png │ ├── 5.png │ └── 6.png ├── index.html └── js │ └── OrbitControls.js ├── [8] FPS (Frames Per Second:Framerate) ├── img │ ├── 1.png │ ├── 2.png │ ├── 3.png │ ├── 4.png │ ├── 5.png │ ├── 6.png │ ├── Wall.jpg │ ├── Ceiling.jpg │ └── Ground.jpg ├── models │ ├── Face_Color.jpg │ └── Face_Disp.jpg ├── index.html └── js │ ├── ObjectLoader.js │ └── OrbitControls.js ├── README.md ├── [1] Setup └── index.html ├── [2] Draw Geometry └── index.html ├── [3] Update Viewport On Resize └── index.html └── [4] Controls ├── index.html └── js └── OrbitControls.js /[6] Lighting/img/1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SonarSystems/three.js-Crash-Course/HEAD/[6] Lighting/img/1.png -------------------------------------------------------------------------------- /[6] Lighting/img/2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SonarSystems/three.js-Crash-Course/HEAD/[6] Lighting/img/2.png -------------------------------------------------------------------------------- /[6] Lighting/img/3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SonarSystems/three.js-Crash-Course/HEAD/[6] Lighting/img/3.png -------------------------------------------------------------------------------- /[6] Lighting/img/4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SonarSystems/three.js-Crash-Course/HEAD/[6] Lighting/img/4.png -------------------------------------------------------------------------------- /[6] Lighting/img/5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SonarSystems/three.js-Crash-Course/HEAD/[6] Lighting/img/5.png -------------------------------------------------------------------------------- /[6] Lighting/img/6.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SonarSystems/three.js-Crash-Course/HEAD/[6] Lighting/img/6.png -------------------------------------------------------------------------------- /[6] Lighting/img/Wall.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SonarSystems/three.js-Crash-Course/HEAD/[6] Lighting/img/Wall.jpg -------------------------------------------------------------------------------- /[10] Skybox/img/Skybox/up.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SonarSystems/three.js-Crash-Course/HEAD/[10] Skybox/img/Skybox/up.png -------------------------------------------------------------------------------- /[6] Lighting/img/Ceiling.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SonarSystems/three.js-Crash-Course/HEAD/[6] Lighting/img/Ceiling.jpg -------------------------------------------------------------------------------- /[6] Lighting/img/Ground.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SonarSystems/three.js-Crash-Course/HEAD/[6] Lighting/img/Ground.jpg -------------------------------------------------------------------------------- /[7] Model Loading/img/1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SonarSystems/three.js-Crash-Course/HEAD/[7] Model Loading/img/1.png -------------------------------------------------------------------------------- /[7] Model Loading/img/2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SonarSystems/three.js-Crash-Course/HEAD/[7] Model Loading/img/2.png -------------------------------------------------------------------------------- /[7] Model Loading/img/3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SonarSystems/three.js-Crash-Course/HEAD/[7] Model Loading/img/3.png -------------------------------------------------------------------------------- /[7] Model Loading/img/4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SonarSystems/three.js-Crash-Course/HEAD/[7] Model Loading/img/4.png -------------------------------------------------------------------------------- /[7] Model Loading/img/5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SonarSystems/three.js-Crash-Course/HEAD/[7] Model Loading/img/5.png -------------------------------------------------------------------------------- /[7] Model Loading/img/6.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SonarSystems/three.js-Crash-Course/HEAD/[7] Model Loading/img/6.png -------------------------------------------------------------------------------- /[9] Anaglyphic 3D/img/1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SonarSystems/three.js-Crash-Course/HEAD/[9] Anaglyphic 3D/img/1.png -------------------------------------------------------------------------------- /[9] Anaglyphic 3D/img/2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SonarSystems/three.js-Crash-Course/HEAD/[9] Anaglyphic 3D/img/2.png -------------------------------------------------------------------------------- /[9] Anaglyphic 3D/img/3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SonarSystems/three.js-Crash-Course/HEAD/[9] Anaglyphic 3D/img/3.png -------------------------------------------------------------------------------- /[9] Anaglyphic 3D/img/4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SonarSystems/three.js-Crash-Course/HEAD/[9] Anaglyphic 3D/img/4.png -------------------------------------------------------------------------------- /[9] Anaglyphic 3D/img/5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SonarSystems/three.js-Crash-Course/HEAD/[9] Anaglyphic 3D/img/5.png -------------------------------------------------------------------------------- /[9] Anaglyphic 3D/img/6.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SonarSystems/three.js-Crash-Course/HEAD/[9] Anaglyphic 3D/img/6.png -------------------------------------------------------------------------------- /[10] Skybox/img/Skybox/back.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SonarSystems/three.js-Crash-Course/HEAD/[10] Skybox/img/Skybox/back.png -------------------------------------------------------------------------------- /[10] Skybox/img/Skybox/down.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SonarSystems/three.js-Crash-Course/HEAD/[10] Skybox/img/Skybox/down.png -------------------------------------------------------------------------------- /[10] Skybox/img/Skybox/left.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SonarSystems/three.js-Crash-Course/HEAD/[10] Skybox/img/Skybox/left.png -------------------------------------------------------------------------------- /[5] Textures:Colours/img/1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SonarSystems/three.js-Crash-Course/HEAD/[5] Textures:Colours/img/1.png -------------------------------------------------------------------------------- /[5] Textures:Colours/img/2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SonarSystems/three.js-Crash-Course/HEAD/[5] Textures:Colours/img/2.png -------------------------------------------------------------------------------- /[5] Textures:Colours/img/3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SonarSystems/three.js-Crash-Course/HEAD/[5] Textures:Colours/img/3.png -------------------------------------------------------------------------------- /[5] Textures:Colours/img/4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SonarSystems/three.js-Crash-Course/HEAD/[5] Textures:Colours/img/4.png -------------------------------------------------------------------------------- /[5] Textures:Colours/img/5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SonarSystems/three.js-Crash-Course/HEAD/[5] Textures:Colours/img/5.png -------------------------------------------------------------------------------- /[5] Textures:Colours/img/6.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SonarSystems/three.js-Crash-Course/HEAD/[5] Textures:Colours/img/6.png -------------------------------------------------------------------------------- /[7] Model Loading/img/Wall.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SonarSystems/three.js-Crash-Course/HEAD/[7] Model Loading/img/Wall.jpg -------------------------------------------------------------------------------- /[9] Anaglyphic 3D/img/Wall.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SonarSystems/three.js-Crash-Course/HEAD/[9] Anaglyphic 3D/img/Wall.jpg -------------------------------------------------------------------------------- /[10] Skybox/img/Skybox/front.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SonarSystems/three.js-Crash-Course/HEAD/[10] Skybox/img/Skybox/front.png -------------------------------------------------------------------------------- /[10] Skybox/img/Skybox/right.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SonarSystems/three.js-Crash-Course/HEAD/[10] Skybox/img/Skybox/right.png -------------------------------------------------------------------------------- /[10] Skybox/models/Face_Color.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SonarSystems/three.js-Crash-Course/HEAD/[10] Skybox/models/Face_Color.jpg -------------------------------------------------------------------------------- /[10] Skybox/models/Face_Disp.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SonarSystems/three.js-Crash-Course/HEAD/[10] Skybox/models/Face_Disp.jpg -------------------------------------------------------------------------------- /[7] Model Loading/img/Ceiling.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SonarSystems/three.js-Crash-Course/HEAD/[7] Model Loading/img/Ceiling.jpg -------------------------------------------------------------------------------- /[7] Model Loading/img/Ground.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SonarSystems/three.js-Crash-Course/HEAD/[7] Model Loading/img/Ground.jpg -------------------------------------------------------------------------------- /[9] Anaglyphic 3D/img/Ceiling.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SonarSystems/three.js-Crash-Course/HEAD/[9] Anaglyphic 3D/img/Ceiling.jpg -------------------------------------------------------------------------------- /[9] Anaglyphic 3D/img/Ground.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SonarSystems/three.js-Crash-Course/HEAD/[9] Anaglyphic 3D/img/Ground.jpg -------------------------------------------------------------------------------- /[7] Model Loading/models/Face_Color.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SonarSystems/three.js-Crash-Course/HEAD/[7] Model Loading/models/Face_Color.jpg -------------------------------------------------------------------------------- /[7] Model Loading/models/Face_Disp.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SonarSystems/three.js-Crash-Course/HEAD/[7] Model Loading/models/Face_Disp.jpg -------------------------------------------------------------------------------- /[9] Anaglyphic 3D/models/Face_Color.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SonarSystems/three.js-Crash-Course/HEAD/[9] Anaglyphic 3D/models/Face_Color.jpg -------------------------------------------------------------------------------- /[9] Anaglyphic 3D/models/Face_Disp.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SonarSystems/three.js-Crash-Course/HEAD/[9] Anaglyphic 3D/models/Face_Disp.jpg -------------------------------------------------------------------------------- /[8] FPS (Frames Per Second:Framerate)/img/1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SonarSystems/three.js-Crash-Course/HEAD/[8] FPS (Frames Per Second:Framerate)/img/1.png -------------------------------------------------------------------------------- /[8] FPS (Frames Per Second:Framerate)/img/2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SonarSystems/three.js-Crash-Course/HEAD/[8] FPS (Frames Per Second:Framerate)/img/2.png -------------------------------------------------------------------------------- /[8] FPS (Frames Per Second:Framerate)/img/3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SonarSystems/three.js-Crash-Course/HEAD/[8] FPS (Frames Per Second:Framerate)/img/3.png -------------------------------------------------------------------------------- /[8] FPS (Frames Per Second:Framerate)/img/4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SonarSystems/three.js-Crash-Course/HEAD/[8] FPS (Frames Per Second:Framerate)/img/4.png -------------------------------------------------------------------------------- /[8] FPS (Frames Per Second:Framerate)/img/5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SonarSystems/three.js-Crash-Course/HEAD/[8] FPS (Frames Per Second:Framerate)/img/5.png -------------------------------------------------------------------------------- /[8] FPS (Frames Per Second:Framerate)/img/6.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SonarSystems/three.js-Crash-Course/HEAD/[8] FPS (Frames Per Second:Framerate)/img/6.png -------------------------------------------------------------------------------- /[8] FPS (Frames Per Second:Framerate)/img/Wall.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SonarSystems/three.js-Crash-Course/HEAD/[8] FPS (Frames Per Second:Framerate)/img/Wall.jpg -------------------------------------------------------------------------------- /[8] FPS (Frames Per Second:Framerate)/img/Ceiling.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SonarSystems/three.js-Crash-Course/HEAD/[8] FPS (Frames Per Second:Framerate)/img/Ceiling.jpg -------------------------------------------------------------------------------- /[8] FPS (Frames Per Second:Framerate)/img/Ground.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SonarSystems/three.js-Crash-Course/HEAD/[8] FPS (Frames Per Second:Framerate)/img/Ground.jpg -------------------------------------------------------------------------------- /[8] FPS (Frames Per Second:Framerate)/models/Face_Color.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SonarSystems/three.js-Crash-Course/HEAD/[8] FPS (Frames Per Second:Framerate)/models/Face_Color.jpg -------------------------------------------------------------------------------- /[8] FPS (Frames Per Second:Framerate)/models/Face_Disp.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SonarSystems/three.js-Crash-Course/HEAD/[8] FPS (Frames Per Second:Framerate)/models/Face_Disp.jpg -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Three.js Crash Course 2 | 3 | Tutorial List: 4 |
    5 |
  1. Setup
  2. 6 |
  3. Draw Geometry
  4. 7 |
  5. Update Viewport On Resize
  6. 8 |
  7. Controls
  8. 9 |
  9. Textures/Colours
  10. 10 |
  11. Lighting
  12. 11 |
  13. Model Loading
  14. 12 |
  15. FPS
  16. 13 |
  17. Anaglyphic 3D
  18. 14 |
  19. Skybox
  20. 15 |
16 | -------------------------------------------------------------------------------- /[1] Setup/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | Three.js Tutorial 4 | 8 | 9 | 10 | 11 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /[2] Draw Geometry/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | Three.js Tutorial 4 | 8 | 9 | 10 | 11 | 53 | 54 | 55 | -------------------------------------------------------------------------------- /[3] Update Viewport On Resize/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | Three.js Tutorial 4 | 8 | 9 | 10 | 11 | 63 | 64 | 65 | -------------------------------------------------------------------------------- /[4] Controls/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | Three.js Tutorial 4 | 8 | 9 | 10 | 11 | 12 | 67 | 68 | 69 | -------------------------------------------------------------------------------- /[7] Model Loading/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Three.js Tutorial 5 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 88 | 89 | 90 | 91 | -------------------------------------------------------------------------------- /[8] FPS (Frames Per Second:Framerate)/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Three.js Tutorial 5 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 90 | 91 | 92 | 93 | -------------------------------------------------------------------------------- /[5] Textures:Colours/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Three.js Tutorial 5 | 17 | 18 | 19 | 20 | 21 | 22 | 85 | 86 | 87 | 88 | -------------------------------------------------------------------------------- /[9] Anaglyphic 3D/js/AnaglyphEffect.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @author mrdoob / http://mrdoob.com/ 3 | * @author marklundin / http://mark-lundin.com/ 4 | * @author alteredq / http://alteredqualia.com/ 5 | */ 6 | 7 | THREE.AnaglyphEffect = function ( renderer, width, height ) { 8 | 9 | var _camera = new THREE.OrthographicCamera( - 1, 1, 1, - 1, 0, 1 ); 10 | 11 | var _scene = new THREE.Scene(); 12 | 13 | var _stereo = new THREE.StereoCamera(); 14 | 15 | var _params = { minFilter: THREE.LinearFilter, magFilter: THREE.NearestFilter, format: THREE.RGBAFormat }; 16 | 17 | if ( width === undefined ) width = 512; 18 | if ( height === undefined ) height = 512; 19 | 20 | var _renderTargetL = new THREE.WebGLRenderTarget( width, height, _params ); 21 | var _renderTargetR = new THREE.WebGLRenderTarget( width, height, _params ); 22 | 23 | var _material = new THREE.ShaderMaterial( { 24 | 25 | uniforms: { 26 | 27 | "mapLeft": { type: "t", value: _renderTargetL }, 28 | "mapRight": { type: "t", value: _renderTargetR } 29 | 30 | }, 31 | 32 | vertexShader: [ 33 | 34 | "varying vec2 vUv;", 35 | 36 | "void main() {", 37 | 38 | " vUv = vec2( uv.x, uv.y );", 39 | " gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );", 40 | 41 | "}" 42 | 43 | ].join( "\n" ), 44 | 45 | fragmentShader: [ 46 | 47 | "uniform sampler2D mapLeft;", 48 | "uniform sampler2D mapRight;", 49 | "varying vec2 vUv;", 50 | 51 | "void main() {", 52 | 53 | " vec4 colorL, colorR;", 54 | " vec2 uv = vUv;", 55 | 56 | " colorL = texture2D( mapLeft, uv );", 57 | " colorR = texture2D( mapRight, uv );", 58 | 59 | // http://3dtv.at/Knowhow/AnaglyphComparison_en.aspx 60 | 61 | " gl_FragColor = vec4( colorL.g * 0.7 + colorL.b * 0.3, colorR.g, colorR.b, colorL.a + colorR.a ) * 1.1;", 62 | 63 | "}" 64 | 65 | ].join( "\n" ) 66 | 67 | } ); 68 | 69 | var mesh = new THREE.Mesh( new THREE.PlaneBufferGeometry( 2, 2 ), _material ); 70 | _scene.add( mesh ); 71 | 72 | this.setSize = function ( width, height ) { 73 | 74 | _renderTargetL.setSize( width, height ); 75 | _renderTargetR.setSize( width, height ); 76 | 77 | renderer.setSize( width, height ); 78 | 79 | }; 80 | 81 | this.render = function ( scene, camera ) { 82 | 83 | scene.updateMatrixWorld(); 84 | 85 | if ( camera.parent === null ) camera.updateMatrixWorld(); 86 | 87 | _stereo.update( camera ); 88 | 89 | renderer.render( scene, _stereo.cameraL, _renderTargetL, true ); 90 | renderer.render( scene, _stereo.cameraR, _renderTargetR, true ); 91 | renderer.render( _scene, _camera ); 92 | 93 | }; 94 | 95 | this.dispose = function() { 96 | 97 | if ( _renderTargetL ) _renderTargetL.dispose(); 98 | if ( _renderTargetR ) _renderTargetR.dispose(); 99 | 100 | }; 101 | 102 | }; 103 | -------------------------------------------------------------------------------- /[10] Skybox/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Three.js Tutorial 5 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 117 | 118 | 119 | 120 | -------------------------------------------------------------------------------- /[6] Lighting/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Three.js Tutorial 5 | 17 | 18 | 19 | 20 | 21 | 22 | 153 | 154 | 155 | 156 | -------------------------------------------------------------------------------- /[9] Anaglyphic 3D/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Three.js Tutorial 5 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 172 | 173 | 174 | 175 | -------------------------------------------------------------------------------- /[7] Model Loading/js/JSONLoader.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @author mrdoob / http://mrdoob.com/ 3 | * @author alteredq / http://alteredqualia.com/ 4 | */ 5 | 6 | THREE.JSONLoader = function ( manager ) { 7 | 8 | if ( typeof manager === 'boolean' ) { 9 | 10 | console.warn( 'THREE.JSONLoader: showStatus parameter has been removed from constructor.' ); 11 | manager = undefined; 12 | 13 | } 14 | 15 | this.manager = ( manager !== undefined ) ? manager : THREE.DefaultLoadingManager; 16 | 17 | this.withCredentials = false; 18 | 19 | }; 20 | 21 | THREE.JSONLoader.prototype = { 22 | 23 | constructor: THREE.JSONLoader, 24 | 25 | // Deprecated 26 | 27 | get statusDomElement () { 28 | 29 | if ( this._statusDomElement === undefined ) { 30 | 31 | this._statusDomElement = document.createElement( 'div' ); 32 | 33 | } 34 | 35 | console.warn( 'THREE.JSONLoader: .statusDomElement has been removed.' ); 36 | return this._statusDomElement; 37 | 38 | }, 39 | 40 | load: function( url, onLoad, onProgress, onError ) { 41 | 42 | var scope = this; 43 | 44 | var texturePath = this.texturePath && ( typeof this.texturePath === "string" ) ? this.texturePath : THREE.Loader.prototype.extractUrlBase( url ); 45 | 46 | var loader = new THREE.XHRLoader( this.manager ); 47 | loader.setWithCredentials( this.withCredentials ); 48 | loader.load( url, function ( text ) { 49 | 50 | var json = JSON.parse( text ); 51 | var metadata = json.metadata; 52 | 53 | if ( metadata !== undefined ) { 54 | 55 | var type = metadata.type; 56 | 57 | if ( type !== undefined ) { 58 | 59 | if ( type.toLowerCase() === 'object' ) { 60 | 61 | console.error( 'THREE.JSONLoader: ' + url + ' should be loaded with THREE.ObjectLoader instead.' ); 62 | return; 63 | 64 | } 65 | 66 | if ( type.toLowerCase() === 'scene' ) { 67 | 68 | console.error( 'THREE.JSONLoader: ' + url + ' should be loaded with THREE.SceneLoader instead.' ); 69 | return; 70 | 71 | } 72 | 73 | } 74 | 75 | } 76 | 77 | var object = scope.parse( json, texturePath ); 78 | onLoad( object.geometry, object.materials ); 79 | 80 | }, onProgress, onError ); 81 | 82 | }, 83 | 84 | setTexturePath: function ( value ) { 85 | 86 | this.texturePath = value; 87 | 88 | }, 89 | 90 | parse: function ( json, texturePath ) { 91 | 92 | var geometry = new THREE.Geometry(), 93 | scale = ( json.scale !== undefined ) ? 1.0 / json.scale : 1.0; 94 | 95 | parseModel( scale ); 96 | 97 | parseSkin(); 98 | parseMorphing( scale ); 99 | parseAnimations(); 100 | 101 | geometry.computeFaceNormals(); 102 | geometry.computeBoundingSphere(); 103 | 104 | function parseModel( scale ) { 105 | 106 | function isBitSet( value, position ) { 107 | 108 | return value & ( 1 << position ); 109 | 110 | } 111 | 112 | var i, j, fi, 113 | 114 | offset, zLength, 115 | 116 | colorIndex, normalIndex, uvIndex, materialIndex, 117 | 118 | type, 119 | isQuad, 120 | hasMaterial, 121 | hasFaceVertexUv, 122 | hasFaceNormal, hasFaceVertexNormal, 123 | hasFaceColor, hasFaceVertexColor, 124 | 125 | vertex, face, faceA, faceB, hex, normal, 126 | 127 | uvLayer, uv, u, v, 128 | 129 | faces = json.faces, 130 | vertices = json.vertices, 131 | normals = json.normals, 132 | colors = json.colors, 133 | 134 | nUvLayers = 0; 135 | 136 | if ( json.uvs !== undefined ) { 137 | 138 | // disregard empty arrays 139 | 140 | for ( i = 0; i < json.uvs.length; i ++ ) { 141 | 142 | if ( json.uvs[ i ].length ) nUvLayers ++; 143 | 144 | } 145 | 146 | for ( i = 0; i < nUvLayers; i ++ ) { 147 | 148 | geometry.faceVertexUvs[ i ] = []; 149 | 150 | } 151 | 152 | } 153 | 154 | offset = 0; 155 | zLength = vertices.length; 156 | 157 | while ( offset < zLength ) { 158 | 159 | vertex = new THREE.Vector3(); 160 | 161 | vertex.x = vertices[ offset ++ ] * scale; 162 | vertex.y = vertices[ offset ++ ] * scale; 163 | vertex.z = vertices[ offset ++ ] * scale; 164 | 165 | geometry.vertices.push( vertex ); 166 | 167 | } 168 | 169 | offset = 0; 170 | zLength = faces.length; 171 | 172 | while ( offset < zLength ) { 173 | 174 | type = faces[ offset ++ ]; 175 | 176 | 177 | isQuad = isBitSet( type, 0 ); 178 | hasMaterial = isBitSet( type, 1 ); 179 | hasFaceVertexUv = isBitSet( type, 3 ); 180 | hasFaceNormal = isBitSet( type, 4 ); 181 | hasFaceVertexNormal = isBitSet( type, 5 ); 182 | hasFaceColor = isBitSet( type, 6 ); 183 | hasFaceVertexColor = isBitSet( type, 7 ); 184 | 185 | // console.log("type", type, "bits", isQuad, hasMaterial, hasFaceVertexUv, hasFaceNormal, hasFaceVertexNormal, hasFaceColor, hasFaceVertexColor); 186 | 187 | if ( isQuad ) { 188 | 189 | faceA = new THREE.Face3(); 190 | faceA.a = faces[ offset ]; 191 | faceA.b = faces[ offset + 1 ]; 192 | faceA.c = faces[ offset + 3 ]; 193 | 194 | faceB = new THREE.Face3(); 195 | faceB.a = faces[ offset + 1 ]; 196 | faceB.b = faces[ offset + 2 ]; 197 | faceB.c = faces[ offset + 3 ]; 198 | 199 | offset += 4; 200 | 201 | if ( hasMaterial ) { 202 | 203 | materialIndex = faces[ offset ++ ]; 204 | faceA.materialIndex = materialIndex; 205 | faceB.materialIndex = materialIndex; 206 | 207 | } 208 | 209 | // to get face <=> uv index correspondence 210 | 211 | fi = geometry.faces.length; 212 | 213 | if ( hasFaceVertexUv ) { 214 | 215 | for ( i = 0; i < nUvLayers; i ++ ) { 216 | 217 | uvLayer = json.uvs[ i ]; 218 | 219 | geometry.faceVertexUvs[ i ][ fi ] = []; 220 | geometry.faceVertexUvs[ i ][ fi + 1 ] = []; 221 | 222 | for ( j = 0; j < 4; j ++ ) { 223 | 224 | uvIndex = faces[ offset ++ ]; 225 | 226 | u = uvLayer[ uvIndex * 2 ]; 227 | v = uvLayer[ uvIndex * 2 + 1 ]; 228 | 229 | uv = new THREE.Vector2( u, v ); 230 | 231 | if ( j !== 2 ) geometry.faceVertexUvs[ i ][ fi ].push( uv ); 232 | if ( j !== 0 ) geometry.faceVertexUvs[ i ][ fi + 1 ].push( uv ); 233 | 234 | } 235 | 236 | } 237 | 238 | } 239 | 240 | if ( hasFaceNormal ) { 241 | 242 | normalIndex = faces[ offset ++ ] * 3; 243 | 244 | faceA.normal.set( 245 | normals[ normalIndex ++ ], 246 | normals[ normalIndex ++ ], 247 | normals[ normalIndex ] 248 | ); 249 | 250 | faceB.normal.copy( faceA.normal ); 251 | 252 | } 253 | 254 | if ( hasFaceVertexNormal ) { 255 | 256 | for ( i = 0; i < 4; i ++ ) { 257 | 258 | normalIndex = faces[ offset ++ ] * 3; 259 | 260 | normal = new THREE.Vector3( 261 | normals[ normalIndex ++ ], 262 | normals[ normalIndex ++ ], 263 | normals[ normalIndex ] 264 | ); 265 | 266 | 267 | if ( i !== 2 ) faceA.vertexNormals.push( normal ); 268 | if ( i !== 0 ) faceB.vertexNormals.push( normal ); 269 | 270 | } 271 | 272 | } 273 | 274 | 275 | if ( hasFaceColor ) { 276 | 277 | colorIndex = faces[ offset ++ ]; 278 | hex = colors[ colorIndex ]; 279 | 280 | faceA.color.setHex( hex ); 281 | faceB.color.setHex( hex ); 282 | 283 | } 284 | 285 | 286 | if ( hasFaceVertexColor ) { 287 | 288 | for ( i = 0; i < 4; i ++ ) { 289 | 290 | colorIndex = faces[ offset ++ ]; 291 | hex = colors[ colorIndex ]; 292 | 293 | if ( i !== 2 ) faceA.vertexColors.push( new THREE.Color( hex ) ); 294 | if ( i !== 0 ) faceB.vertexColors.push( new THREE.Color( hex ) ); 295 | 296 | } 297 | 298 | } 299 | 300 | geometry.faces.push( faceA ); 301 | geometry.faces.push( faceB ); 302 | 303 | } else { 304 | 305 | face = new THREE.Face3(); 306 | face.a = faces[ offset ++ ]; 307 | face.b = faces[ offset ++ ]; 308 | face.c = faces[ offset ++ ]; 309 | 310 | if ( hasMaterial ) { 311 | 312 | materialIndex = faces[ offset ++ ]; 313 | face.materialIndex = materialIndex; 314 | 315 | } 316 | 317 | // to get face <=> uv index correspondence 318 | 319 | fi = geometry.faces.length; 320 | 321 | if ( hasFaceVertexUv ) { 322 | 323 | for ( i = 0; i < nUvLayers; i ++ ) { 324 | 325 | uvLayer = json.uvs[ i ]; 326 | 327 | geometry.faceVertexUvs[ i ][ fi ] = []; 328 | 329 | for ( j = 0; j < 3; j ++ ) { 330 | 331 | uvIndex = faces[ offset ++ ]; 332 | 333 | u = uvLayer[ uvIndex * 2 ]; 334 | v = uvLayer[ uvIndex * 2 + 1 ]; 335 | 336 | uv = new THREE.Vector2( u, v ); 337 | 338 | geometry.faceVertexUvs[ i ][ fi ].push( uv ); 339 | 340 | } 341 | 342 | } 343 | 344 | } 345 | 346 | if ( hasFaceNormal ) { 347 | 348 | normalIndex = faces[ offset ++ ] * 3; 349 | 350 | face.normal.set( 351 | normals[ normalIndex ++ ], 352 | normals[ normalIndex ++ ], 353 | normals[ normalIndex ] 354 | ); 355 | 356 | } 357 | 358 | if ( hasFaceVertexNormal ) { 359 | 360 | for ( i = 0; i < 3; i ++ ) { 361 | 362 | normalIndex = faces[ offset ++ ] * 3; 363 | 364 | normal = new THREE.Vector3( 365 | normals[ normalIndex ++ ], 366 | normals[ normalIndex ++ ], 367 | normals[ normalIndex ] 368 | ); 369 | 370 | face.vertexNormals.push( normal ); 371 | 372 | } 373 | 374 | } 375 | 376 | 377 | if ( hasFaceColor ) { 378 | 379 | colorIndex = faces[ offset ++ ]; 380 | face.color.setHex( colors[ colorIndex ] ); 381 | 382 | } 383 | 384 | 385 | if ( hasFaceVertexColor ) { 386 | 387 | for ( i = 0; i < 3; i ++ ) { 388 | 389 | colorIndex = faces[ offset ++ ]; 390 | face.vertexColors.push( new THREE.Color( colors[ colorIndex ] ) ); 391 | 392 | } 393 | 394 | } 395 | 396 | geometry.faces.push( face ); 397 | 398 | } 399 | 400 | } 401 | 402 | }; 403 | 404 | function parseSkin() { 405 | 406 | var influencesPerVertex = ( json.influencesPerVertex !== undefined ) ? json.influencesPerVertex : 2; 407 | 408 | if ( json.skinWeights ) { 409 | 410 | for ( var i = 0, l = json.skinWeights.length; i < l; i += influencesPerVertex ) { 411 | 412 | var x = json.skinWeights[ i ]; 413 | var y = ( influencesPerVertex > 1 ) ? json.skinWeights[ i + 1 ] : 0; 414 | var z = ( influencesPerVertex > 2 ) ? json.skinWeights[ i + 2 ] : 0; 415 | var w = ( influencesPerVertex > 3 ) ? json.skinWeights[ i + 3 ] : 0; 416 | 417 | geometry.skinWeights.push( new THREE.Vector4( x, y, z, w ) ); 418 | 419 | } 420 | 421 | } 422 | 423 | if ( json.skinIndices ) { 424 | 425 | for ( var i = 0, l = json.skinIndices.length; i < l; i += influencesPerVertex ) { 426 | 427 | var a = json.skinIndices[ i ]; 428 | var b = ( influencesPerVertex > 1 ) ? json.skinIndices[ i + 1 ] : 0; 429 | var c = ( influencesPerVertex > 2 ) ? json.skinIndices[ i + 2 ] : 0; 430 | var d = ( influencesPerVertex > 3 ) ? json.skinIndices[ i + 3 ] : 0; 431 | 432 | geometry.skinIndices.push( new THREE.Vector4( a, b, c, d ) ); 433 | 434 | } 435 | 436 | } 437 | 438 | geometry.bones = json.bones; 439 | 440 | if ( geometry.bones && geometry.bones.length > 0 && ( geometry.skinWeights.length !== geometry.skinIndices.length || geometry.skinIndices.length !== geometry.vertices.length ) ) { 441 | 442 | console.warn( 'When skinning, number of vertices (' + geometry.vertices.length + '), skinIndices (' + 443 | geometry.skinIndices.length + '), and skinWeights (' + geometry.skinWeights.length + ') should match.' ); 444 | 445 | } 446 | 447 | }; 448 | 449 | function parseMorphing( scale ) { 450 | 451 | if ( json.morphTargets !== undefined ) { 452 | 453 | for ( var i = 0, l = json.morphTargets.length; i < l; i ++ ) { 454 | 455 | geometry.morphTargets[ i ] = {}; 456 | geometry.morphTargets[ i ].name = json.morphTargets[ i ].name; 457 | geometry.morphTargets[ i ].vertices = []; 458 | 459 | var dstVertices = geometry.morphTargets[ i ].vertices; 460 | var srcVertices = json.morphTargets[ i ].vertices; 461 | 462 | for ( var v = 0, vl = srcVertices.length; v < vl; v += 3 ) { 463 | 464 | var vertex = new THREE.Vector3(); 465 | vertex.x = srcVertices[ v ] * scale; 466 | vertex.y = srcVertices[ v + 1 ] * scale; 467 | vertex.z = srcVertices[ v + 2 ] * scale; 468 | 469 | dstVertices.push( vertex ); 470 | 471 | } 472 | 473 | } 474 | 475 | } 476 | 477 | if ( json.morphColors !== undefined && json.morphColors.length > 0 ) { 478 | 479 | console.warn( 'THREE.JSONLoader: "morphColors" no longer supported. Using them as face colors.' ); 480 | 481 | var faces = geometry.faces; 482 | var morphColors = json.morphColors[ 0 ].colors; 483 | 484 | for ( var i = 0, l = faces.length; i < l; i ++ ) { 485 | 486 | faces[ i ].color.fromArray( morphColors, i * 3 ); 487 | 488 | } 489 | 490 | } 491 | 492 | } 493 | 494 | function parseAnimations() { 495 | 496 | var outputAnimations = []; 497 | 498 | // parse old style Bone/Hierarchy animations 499 | var animations = []; 500 | 501 | if ( json.animation !== undefined ) { 502 | 503 | animations.push( json.animation ); 504 | 505 | } 506 | 507 | if ( json.animations !== undefined ) { 508 | 509 | if ( json.animations.length ) { 510 | 511 | animations = animations.concat( json.animations ); 512 | 513 | } else { 514 | 515 | animations.push( json.animations ); 516 | 517 | } 518 | 519 | } 520 | 521 | for ( var i = 0; i < animations.length; i ++ ) { 522 | 523 | var clip = THREE.AnimationClip.parseAnimation( animations[ i ], geometry.bones ); 524 | if ( clip ) outputAnimations.push( clip ); 525 | 526 | } 527 | 528 | // parse implicit morph animations 529 | if ( geometry.morphTargets ) { 530 | 531 | // TODO: Figure out what an appropraite FPS is for morph target animations -- defaulting to 10, but really it is completely arbitrary. 532 | var morphAnimationClips = THREE.AnimationClip.CreateClipsFromMorphTargetSequences( geometry.morphTargets, 10 ); 533 | outputAnimations = outputAnimations.concat( morphAnimationClips ); 534 | 535 | } 536 | 537 | if ( outputAnimations.length > 0 ) geometry.animations = outputAnimations; 538 | 539 | }; 540 | 541 | if ( json.materials === undefined || json.materials.length === 0 ) { 542 | 543 | return { geometry: geometry }; 544 | 545 | } else { 546 | 547 | var materials = THREE.Loader.prototype.initMaterials( json.materials, texturePath, this.crossOrigin ); 548 | 549 | return { geometry: geometry, materials: materials }; 550 | 551 | } 552 | 553 | } 554 | 555 | }; 556 | -------------------------------------------------------------------------------- /[7] Model Loading/js/ObjectLoader.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @author mrdoob / http://mrdoob.com/ 3 | */ 4 | 5 | THREE.ObjectLoader = function ( manager ) { 6 | 7 | this.manager = ( manager !== undefined ) ? manager : THREE.DefaultLoadingManager; 8 | this.texturePath = ''; 9 | 10 | }; 11 | 12 | THREE.ObjectLoader.prototype = { 13 | 14 | constructor: THREE.ObjectLoader, 15 | 16 | load: function ( url, onLoad, onProgress, onError ) { 17 | 18 | if ( this.texturePath === '' ) { 19 | 20 | this.texturePath = url.substring( 0, url.lastIndexOf( '/' ) + 1 ); 21 | 22 | } 23 | 24 | var scope = this; 25 | 26 | var loader = new THREE.XHRLoader( scope.manager ); 27 | loader.load( url, function ( text ) { 28 | 29 | scope.parse( JSON.parse( text ), onLoad ); 30 | 31 | }, onProgress, onError ); 32 | 33 | }, 34 | 35 | setTexturePath: function ( value ) { 36 | 37 | this.texturePath = value; 38 | 39 | }, 40 | 41 | setCrossOrigin: function ( value ) { 42 | 43 | this.crossOrigin = value; 44 | 45 | }, 46 | 47 | parse: function ( json, onLoad ) { 48 | 49 | var geometries = this.parseGeometries( json.geometries ); 50 | 51 | var images = this.parseImages( json.images, function () { 52 | 53 | if ( onLoad !== undefined ) onLoad( object ); 54 | 55 | } ); 56 | 57 | var textures = this.parseTextures( json.textures, images ); 58 | var materials = this.parseMaterials( json.materials, textures ); 59 | 60 | var object = this.parseObject( json.object, geometries, materials ); 61 | 62 | if ( json.animations ) { 63 | 64 | object.animations = this.parseAnimations( json.animations ); 65 | 66 | } 67 | 68 | if ( json.images === undefined || json.images.length === 0 ) { 69 | 70 | if ( onLoad !== undefined ) onLoad( object ); 71 | 72 | } 73 | 74 | return object; 75 | 76 | }, 77 | 78 | parseGeometries: function ( json ) { 79 | 80 | var geometries = {}; 81 | 82 | if ( json !== undefined ) { 83 | 84 | var geometryLoader = new THREE.JSONLoader(); 85 | var bufferGeometryLoader = new THREE.BufferGeometryLoader(); 86 | 87 | for ( var i = 0, l = json.length; i < l; i ++ ) { 88 | 89 | var geometry; 90 | var data = json[ i ]; 91 | 92 | switch ( data.type ) { 93 | 94 | case 'PlaneGeometry': 95 | case 'PlaneBufferGeometry': 96 | 97 | geometry = new THREE[ data.type ]( 98 | data.width, 99 | data.height, 100 | data.widthSegments, 101 | data.heightSegments 102 | ); 103 | 104 | break; 105 | 106 | case 'BoxGeometry': 107 | case 'CubeGeometry': // backwards compatible 108 | 109 | geometry = new THREE.BoxGeometry( 110 | data.width, 111 | data.height, 112 | data.depth, 113 | data.widthSegments, 114 | data.heightSegments, 115 | data.depthSegments 116 | ); 117 | 118 | break; 119 | 120 | case 'CircleBufferGeometry': 121 | 122 | geometry = new THREE.CircleBufferGeometry( 123 | data.radius, 124 | data.segments, 125 | data.thetaStart, 126 | data.thetaLength 127 | ); 128 | 129 | break; 130 | 131 | case 'CircleGeometry': 132 | 133 | geometry = new THREE.CircleGeometry( 134 | data.radius, 135 | data.segments, 136 | data.thetaStart, 137 | data.thetaLength 138 | ); 139 | 140 | break; 141 | 142 | case 'CylinderGeometry': 143 | 144 | geometry = new THREE.CylinderGeometry( 145 | data.radiusTop, 146 | data.radiusBottom, 147 | data.height, 148 | data.radialSegments, 149 | data.heightSegments, 150 | data.openEnded, 151 | data.thetaStart, 152 | data.thetaLength 153 | ); 154 | 155 | break; 156 | 157 | case 'SphereGeometry': 158 | 159 | geometry = new THREE.SphereGeometry( 160 | data.radius, 161 | data.widthSegments, 162 | data.heightSegments, 163 | data.phiStart, 164 | data.phiLength, 165 | data.thetaStart, 166 | data.thetaLength 167 | ); 168 | 169 | break; 170 | 171 | case 'SphereBufferGeometry': 172 | 173 | geometry = new THREE.SphereBufferGeometry( 174 | data.radius, 175 | data.widthSegments, 176 | data.heightSegments, 177 | data.phiStart, 178 | data.phiLength, 179 | data.thetaStart, 180 | data.thetaLength 181 | ); 182 | 183 | break; 184 | 185 | case 'DodecahedronGeometry': 186 | 187 | geometry = new THREE.DodecahedronGeometry( 188 | data.radius, 189 | data.detail 190 | ); 191 | 192 | break; 193 | 194 | case 'IcosahedronGeometry': 195 | 196 | geometry = new THREE.IcosahedronGeometry( 197 | data.radius, 198 | data.detail 199 | ); 200 | 201 | break; 202 | 203 | case 'OctahedronGeometry': 204 | 205 | geometry = new THREE.OctahedronGeometry( 206 | data.radius, 207 | data.detail 208 | ); 209 | 210 | break; 211 | 212 | case 'TetrahedronGeometry': 213 | 214 | geometry = new THREE.TetrahedronGeometry( 215 | data.radius, 216 | data.detail 217 | ); 218 | 219 | break; 220 | 221 | case 'RingGeometry': 222 | 223 | geometry = new THREE.RingGeometry( 224 | data.innerRadius, 225 | data.outerRadius, 226 | data.thetaSegments, 227 | data.phiSegments, 228 | data.thetaStart, 229 | data.thetaLength 230 | ); 231 | 232 | break; 233 | 234 | case 'TorusGeometry': 235 | 236 | geometry = new THREE.TorusGeometry( 237 | data.radius, 238 | data.tube, 239 | data.radialSegments, 240 | data.tubularSegments, 241 | data.arc 242 | ); 243 | 244 | break; 245 | 246 | case 'TorusKnotGeometry': 247 | 248 | geometry = new THREE.TorusKnotGeometry( 249 | data.radius, 250 | data.tube, 251 | data.radialSegments, 252 | data.tubularSegments, 253 | data.p, 254 | data.q, 255 | data.heightScale 256 | ); 257 | 258 | break; 259 | 260 | case 'LatheGeometry': 261 | 262 | geometry = new THREE.LatheGeometry( 263 | data.points, 264 | data.segments, 265 | data.phiStart, 266 | data.phiLength 267 | ); 268 | 269 | break; 270 | 271 | case 'BufferGeometry': 272 | 273 | geometry = bufferGeometryLoader.parse( data ); 274 | 275 | break; 276 | 277 | case 'Geometry': 278 | 279 | geometry = geometryLoader.parse( data.data, this.texturePath ).geometry; 280 | 281 | break; 282 | 283 | default: 284 | 285 | console.warn( 'THREE.ObjectLoader: Unsupported geometry type "' + data.type + '"' ); 286 | 287 | continue; 288 | 289 | } 290 | 291 | geometry.uuid = data.uuid; 292 | 293 | if ( data.name !== undefined ) geometry.name = data.name; 294 | 295 | geometries[ data.uuid ] = geometry; 296 | 297 | } 298 | 299 | } 300 | 301 | return geometries; 302 | 303 | }, 304 | 305 | parseMaterials: function ( json, textures ) { 306 | 307 | var materials = {}; 308 | 309 | if ( json !== undefined ) { 310 | 311 | var loader = new THREE.MaterialLoader(); 312 | loader.setTextures( textures ); 313 | 314 | for ( var i = 0, l = json.length; i < l; i ++ ) { 315 | 316 | var material = loader.parse( json[ i ] ); 317 | materials[ material.uuid ] = material; 318 | 319 | } 320 | 321 | } 322 | 323 | return materials; 324 | 325 | }, 326 | 327 | parseAnimations: function ( json ) { 328 | 329 | var animations = []; 330 | 331 | for ( var i = 0; i < json.length; i ++ ) { 332 | 333 | var clip = THREE.AnimationClip.parse( json[ i ] ); 334 | 335 | animations.push( clip ); 336 | 337 | } 338 | 339 | return animations; 340 | 341 | }, 342 | 343 | parseImages: function ( json, onLoad ) { 344 | 345 | var scope = this; 346 | var images = {}; 347 | 348 | function loadImage( url ) { 349 | 350 | scope.manager.itemStart( url ); 351 | 352 | return loader.load( url, function () { 353 | 354 | scope.manager.itemEnd( url ); 355 | 356 | } ); 357 | 358 | } 359 | 360 | if ( json !== undefined && json.length > 0 ) { 361 | 362 | var manager = new THREE.LoadingManager( onLoad ); 363 | 364 | var loader = new THREE.ImageLoader( manager ); 365 | loader.setCrossOrigin( this.crossOrigin ); 366 | 367 | for ( var i = 0, l = json.length; i < l; i ++ ) { 368 | 369 | var image = json[ i ]; 370 | var path = /^(\/\/)|([a-z]+:(\/\/)?)/i.test( image.url ) ? image.url : scope.texturePath + image.url; 371 | 372 | images[ image.uuid ] = loadImage( path ); 373 | 374 | } 375 | 376 | } 377 | 378 | return images; 379 | 380 | }, 381 | 382 | parseTextures: function ( json, images ) { 383 | 384 | function parseConstant( value ) { 385 | 386 | if ( typeof( value ) === 'number' ) return value; 387 | 388 | console.warn( 'THREE.ObjectLoader.parseTexture: Constant should be in numeric form.', value ); 389 | 390 | return THREE[ value ]; 391 | 392 | } 393 | 394 | var textures = {}; 395 | 396 | if ( json !== undefined ) { 397 | 398 | for ( var i = 0, l = json.length; i < l; i ++ ) { 399 | 400 | var data = json[ i ]; 401 | 402 | if ( data.image === undefined ) { 403 | 404 | console.warn( 'THREE.ObjectLoader: No "image" specified for', data.uuid ); 405 | 406 | } 407 | 408 | if ( images[ data.image ] === undefined ) { 409 | 410 | console.warn( 'THREE.ObjectLoader: Undefined image', data.image ); 411 | 412 | } 413 | 414 | var texture = new THREE.Texture( images[ data.image ] ); 415 | texture.needsUpdate = true; 416 | 417 | texture.uuid = data.uuid; 418 | 419 | if ( data.name !== undefined ) texture.name = data.name; 420 | if ( data.mapping !== undefined ) texture.mapping = parseConstant( data.mapping ); 421 | if ( data.offset !== undefined ) texture.offset = new THREE.Vector2( data.offset[ 0 ], data.offset[ 1 ] ); 422 | if ( data.repeat !== undefined ) texture.repeat = new THREE.Vector2( data.repeat[ 0 ], data.repeat[ 1 ] ); 423 | if ( data.minFilter !== undefined ) texture.minFilter = parseConstant( data.minFilter ); 424 | if ( data.magFilter !== undefined ) texture.magFilter = parseConstant( data.magFilter ); 425 | if ( data.anisotropy !== undefined ) texture.anisotropy = data.anisotropy; 426 | if ( Array.isArray( data.wrap ) ) { 427 | 428 | texture.wrapS = parseConstant( data.wrap[ 0 ] ); 429 | texture.wrapT = parseConstant( data.wrap[ 1 ] ); 430 | 431 | } 432 | 433 | textures[ data.uuid ] = texture; 434 | 435 | } 436 | 437 | } 438 | 439 | return textures; 440 | 441 | }, 442 | 443 | parseObject: function () { 444 | 445 | var matrix = new THREE.Matrix4(); 446 | 447 | return function ( data, geometries, materials ) { 448 | 449 | var object; 450 | 451 | function getGeometry( name ) { 452 | 453 | if ( geometries[ name ] === undefined ) { 454 | 455 | console.warn( 'THREE.ObjectLoader: Undefined geometry', name ); 456 | 457 | } 458 | 459 | return geometries[ name ]; 460 | 461 | } 462 | 463 | function getMaterial( name ) { 464 | 465 | if ( name === undefined ) return undefined; 466 | 467 | if ( materials[ name ] === undefined ) { 468 | 469 | console.warn( 'THREE.ObjectLoader: Undefined material', name ); 470 | 471 | } 472 | 473 | return materials[ name ]; 474 | 475 | } 476 | 477 | switch ( data.type ) { 478 | 479 | case 'Scene': 480 | 481 | object = new THREE.Scene(); 482 | 483 | break; 484 | 485 | case 'PerspectiveCamera': 486 | 487 | object = new THREE.PerspectiveCamera( data.fov, data.aspect, data.near, data.far ); 488 | 489 | break; 490 | 491 | case 'OrthographicCamera': 492 | 493 | object = new THREE.OrthographicCamera( data.left, data.right, data.top, data.bottom, data.near, data.far ); 494 | 495 | break; 496 | 497 | case 'AmbientLight': 498 | 499 | object = new THREE.AmbientLight( data.color, data.intensity ); 500 | 501 | break; 502 | 503 | case 'DirectionalLight': 504 | 505 | object = new THREE.DirectionalLight( data.color, data.intensity ); 506 | 507 | break; 508 | 509 | case 'PointLight': 510 | 511 | object = new THREE.PointLight( data.color, data.intensity, data.distance, data.decay ); 512 | 513 | break; 514 | 515 | case 'SpotLight': 516 | 517 | object = new THREE.SpotLight( data.color, data.intensity, data.distance, data.angle, data.exponent, data.decay ); 518 | 519 | break; 520 | 521 | case 'HemisphereLight': 522 | 523 | object = new THREE.HemisphereLight( data.color, data.groundColor, data.intensity ); 524 | 525 | break; 526 | 527 | case 'Mesh': 528 | 529 | var geometry = getGeometry( data.geometry ); 530 | var material = getMaterial( data.material ); 531 | 532 | if ( geometry.bones && geometry.bones.length > 0 ) { 533 | 534 | object = new THREE.SkinnedMesh( geometry, material ); 535 | 536 | } else { 537 | 538 | object = new THREE.Mesh( geometry, material ); 539 | 540 | } 541 | 542 | break; 543 | 544 | case 'LOD': 545 | 546 | object = new THREE.LOD(); 547 | 548 | break; 549 | 550 | case 'Line': 551 | 552 | object = new THREE.Line( getGeometry( data.geometry ), getMaterial( data.material ), data.mode ); 553 | 554 | break; 555 | 556 | case 'PointCloud': 557 | case 'Points': 558 | 559 | object = new THREE.Points( getGeometry( data.geometry ), getMaterial( data.material ) ); 560 | 561 | break; 562 | 563 | case 'Sprite': 564 | 565 | object = new THREE.Sprite( getMaterial( data.material ) ); 566 | 567 | break; 568 | 569 | case 'Group': 570 | 571 | object = new THREE.Group(); 572 | 573 | break; 574 | 575 | default: 576 | 577 | object = new THREE.Object3D(); 578 | 579 | } 580 | 581 | object.uuid = data.uuid; 582 | 583 | if ( data.name !== undefined ) object.name = data.name; 584 | if ( data.matrix !== undefined ) { 585 | 586 | matrix.fromArray( data.matrix ); 587 | matrix.decompose( object.position, object.quaternion, object.scale ); 588 | 589 | } else { 590 | 591 | if ( data.position !== undefined ) object.position.fromArray( data.position ); 592 | if ( data.rotation !== undefined ) object.rotation.fromArray( data.rotation ); 593 | if ( data.scale !== undefined ) object.scale.fromArray( data.scale ); 594 | 595 | } 596 | 597 | if ( data.castShadow !== undefined ) object.castShadow = data.castShadow; 598 | if ( data.receiveShadow !== undefined ) object.receiveShadow = data.receiveShadow; 599 | 600 | if ( data.visible !== undefined ) object.visible = data.visible; 601 | if ( data.userData !== undefined ) object.userData = data.userData; 602 | 603 | if ( data.children !== undefined ) { 604 | 605 | for ( var child in data.children ) { 606 | 607 | object.add( this.parseObject( data.children[ child ], geometries, materials ) ); 608 | 609 | } 610 | 611 | } 612 | 613 | if ( data.type === 'LOD' ) { 614 | 615 | var levels = data.levels; 616 | 617 | for ( var l = 0; l < levels.length; l ++ ) { 618 | 619 | var level = levels[ l ]; 620 | var child = object.getObjectByProperty( 'uuid', level.object ); 621 | 622 | if ( child !== undefined ) { 623 | 624 | object.addLevel( child, level.distance ); 625 | 626 | } 627 | 628 | } 629 | 630 | } 631 | 632 | return object; 633 | 634 | } 635 | 636 | }() 637 | 638 | }; 639 | -------------------------------------------------------------------------------- /[9] Anaglyphic 3D/js/ObjectLoader.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @author mrdoob / http://mrdoob.com/ 3 | */ 4 | 5 | THREE.ObjectLoader = function ( manager ) { 6 | 7 | this.manager = ( manager !== undefined ) ? manager : THREE.DefaultLoadingManager; 8 | this.texturePath = ''; 9 | 10 | }; 11 | 12 | THREE.ObjectLoader.prototype = { 13 | 14 | constructor: THREE.ObjectLoader, 15 | 16 | load: function ( url, onLoad, onProgress, onError ) { 17 | 18 | if ( this.texturePath === '' ) { 19 | 20 | this.texturePath = url.substring( 0, url.lastIndexOf( '/' ) + 1 ); 21 | 22 | } 23 | 24 | var scope = this; 25 | 26 | var loader = new THREE.XHRLoader( scope.manager ); 27 | loader.load( url, function ( text ) { 28 | 29 | scope.parse( JSON.parse( text ), onLoad ); 30 | 31 | }, onProgress, onError ); 32 | 33 | }, 34 | 35 | setTexturePath: function ( value ) { 36 | 37 | this.texturePath = value; 38 | 39 | }, 40 | 41 | setCrossOrigin: function ( value ) { 42 | 43 | this.crossOrigin = value; 44 | 45 | }, 46 | 47 | parse: function ( json, onLoad ) { 48 | 49 | var geometries = this.parseGeometries( json.geometries ); 50 | 51 | var images = this.parseImages( json.images, function () { 52 | 53 | if ( onLoad !== undefined ) onLoad( object ); 54 | 55 | } ); 56 | 57 | var textures = this.parseTextures( json.textures, images ); 58 | var materials = this.parseMaterials( json.materials, textures ); 59 | 60 | var object = this.parseObject( json.object, geometries, materials ); 61 | 62 | if ( json.animations ) { 63 | 64 | object.animations = this.parseAnimations( json.animations ); 65 | 66 | } 67 | 68 | if ( json.images === undefined || json.images.length === 0 ) { 69 | 70 | if ( onLoad !== undefined ) onLoad( object ); 71 | 72 | } 73 | 74 | return object; 75 | 76 | }, 77 | 78 | parseGeometries: function ( json ) { 79 | 80 | var geometries = {}; 81 | 82 | if ( json !== undefined ) { 83 | 84 | var geometryLoader = new THREE.JSONLoader(); 85 | var bufferGeometryLoader = new THREE.BufferGeometryLoader(); 86 | 87 | for ( var i = 0, l = json.length; i < l; i ++ ) { 88 | 89 | var geometry; 90 | var data = json[ i ]; 91 | 92 | switch ( data.type ) { 93 | 94 | case 'PlaneGeometry': 95 | case 'PlaneBufferGeometry': 96 | 97 | geometry = new THREE[ data.type ]( 98 | data.width, 99 | data.height, 100 | data.widthSegments, 101 | data.heightSegments 102 | ); 103 | 104 | break; 105 | 106 | case 'BoxGeometry': 107 | case 'CubeGeometry': // backwards compatible 108 | 109 | geometry = new THREE.BoxGeometry( 110 | data.width, 111 | data.height, 112 | data.depth, 113 | data.widthSegments, 114 | data.heightSegments, 115 | data.depthSegments 116 | ); 117 | 118 | break; 119 | 120 | case 'CircleBufferGeometry': 121 | 122 | geometry = new THREE.CircleBufferGeometry( 123 | data.radius, 124 | data.segments, 125 | data.thetaStart, 126 | data.thetaLength 127 | ); 128 | 129 | break; 130 | 131 | case 'CircleGeometry': 132 | 133 | geometry = new THREE.CircleGeometry( 134 | data.radius, 135 | data.segments, 136 | data.thetaStart, 137 | data.thetaLength 138 | ); 139 | 140 | break; 141 | 142 | case 'CylinderGeometry': 143 | 144 | geometry = new THREE.CylinderGeometry( 145 | data.radiusTop, 146 | data.radiusBottom, 147 | data.height, 148 | data.radialSegments, 149 | data.heightSegments, 150 | data.openEnded, 151 | data.thetaStart, 152 | data.thetaLength 153 | ); 154 | 155 | break; 156 | 157 | case 'SphereGeometry': 158 | 159 | geometry = new THREE.SphereGeometry( 160 | data.radius, 161 | data.widthSegments, 162 | data.heightSegments, 163 | data.phiStart, 164 | data.phiLength, 165 | data.thetaStart, 166 | data.thetaLength 167 | ); 168 | 169 | break; 170 | 171 | case 'SphereBufferGeometry': 172 | 173 | geometry = new THREE.SphereBufferGeometry( 174 | data.radius, 175 | data.widthSegments, 176 | data.heightSegments, 177 | data.phiStart, 178 | data.phiLength, 179 | data.thetaStart, 180 | data.thetaLength 181 | ); 182 | 183 | break; 184 | 185 | case 'DodecahedronGeometry': 186 | 187 | geometry = new THREE.DodecahedronGeometry( 188 | data.radius, 189 | data.detail 190 | ); 191 | 192 | break; 193 | 194 | case 'IcosahedronGeometry': 195 | 196 | geometry = new THREE.IcosahedronGeometry( 197 | data.radius, 198 | data.detail 199 | ); 200 | 201 | break; 202 | 203 | case 'OctahedronGeometry': 204 | 205 | geometry = new THREE.OctahedronGeometry( 206 | data.radius, 207 | data.detail 208 | ); 209 | 210 | break; 211 | 212 | case 'TetrahedronGeometry': 213 | 214 | geometry = new THREE.TetrahedronGeometry( 215 | data.radius, 216 | data.detail 217 | ); 218 | 219 | break; 220 | 221 | case 'RingGeometry': 222 | 223 | geometry = new THREE.RingGeometry( 224 | data.innerRadius, 225 | data.outerRadius, 226 | data.thetaSegments, 227 | data.phiSegments, 228 | data.thetaStart, 229 | data.thetaLength 230 | ); 231 | 232 | break; 233 | 234 | case 'TorusGeometry': 235 | 236 | geometry = new THREE.TorusGeometry( 237 | data.radius, 238 | data.tube, 239 | data.radialSegments, 240 | data.tubularSegments, 241 | data.arc 242 | ); 243 | 244 | break; 245 | 246 | case 'TorusKnotGeometry': 247 | 248 | geometry = new THREE.TorusKnotGeometry( 249 | data.radius, 250 | data.tube, 251 | data.radialSegments, 252 | data.tubularSegments, 253 | data.p, 254 | data.q, 255 | data.heightScale 256 | ); 257 | 258 | break; 259 | 260 | case 'LatheGeometry': 261 | 262 | geometry = new THREE.LatheGeometry( 263 | data.points, 264 | data.segments, 265 | data.phiStart, 266 | data.phiLength 267 | ); 268 | 269 | break; 270 | 271 | case 'BufferGeometry': 272 | 273 | geometry = bufferGeometryLoader.parse( data ); 274 | 275 | break; 276 | 277 | case 'Geometry': 278 | 279 | geometry = geometryLoader.parse( data.data, this.texturePath ).geometry; 280 | 281 | break; 282 | 283 | default: 284 | 285 | console.warn( 'THREE.ObjectLoader: Unsupported geometry type "' + data.type + '"' ); 286 | 287 | continue; 288 | 289 | } 290 | 291 | geometry.uuid = data.uuid; 292 | 293 | if ( data.name !== undefined ) geometry.name = data.name; 294 | 295 | geometries[ data.uuid ] = geometry; 296 | 297 | } 298 | 299 | } 300 | 301 | return geometries; 302 | 303 | }, 304 | 305 | parseMaterials: function ( json, textures ) { 306 | 307 | var materials = {}; 308 | 309 | if ( json !== undefined ) { 310 | 311 | var loader = new THREE.MaterialLoader(); 312 | loader.setTextures( textures ); 313 | 314 | for ( var i = 0, l = json.length; i < l; i ++ ) { 315 | 316 | var material = loader.parse( json[ i ] ); 317 | materials[ material.uuid ] = material; 318 | 319 | } 320 | 321 | } 322 | 323 | return materials; 324 | 325 | }, 326 | 327 | parseAnimations: function ( json ) { 328 | 329 | var animations = []; 330 | 331 | for ( var i = 0; i < json.length; i ++ ) { 332 | 333 | var clip = THREE.AnimationClip.parse( json[ i ] ); 334 | 335 | animations.push( clip ); 336 | 337 | } 338 | 339 | return animations; 340 | 341 | }, 342 | 343 | parseImages: function ( json, onLoad ) { 344 | 345 | var scope = this; 346 | var images = {}; 347 | 348 | function loadImage( url ) { 349 | 350 | scope.manager.itemStart( url ); 351 | 352 | return loader.load( url, function () { 353 | 354 | scope.manager.itemEnd( url ); 355 | 356 | } ); 357 | 358 | } 359 | 360 | if ( json !== undefined && json.length > 0 ) { 361 | 362 | var manager = new THREE.LoadingManager( onLoad ); 363 | 364 | var loader = new THREE.ImageLoader( manager ); 365 | loader.setCrossOrigin( this.crossOrigin ); 366 | 367 | for ( var i = 0, l = json.length; i < l; i ++ ) { 368 | 369 | var image = json[ i ]; 370 | var path = /^(\/\/)|([a-z]+:(\/\/)?)/i.test( image.url ) ? image.url : scope.texturePath + image.url; 371 | 372 | images[ image.uuid ] = loadImage( path ); 373 | 374 | } 375 | 376 | } 377 | 378 | return images; 379 | 380 | }, 381 | 382 | parseTextures: function ( json, images ) { 383 | 384 | function parseConstant( value ) { 385 | 386 | if ( typeof( value ) === 'number' ) return value; 387 | 388 | console.warn( 'THREE.ObjectLoader.parseTexture: Constant should be in numeric form.', value ); 389 | 390 | return THREE[ value ]; 391 | 392 | } 393 | 394 | var textures = {}; 395 | 396 | if ( json !== undefined ) { 397 | 398 | for ( var i = 0, l = json.length; i < l; i ++ ) { 399 | 400 | var data = json[ i ]; 401 | 402 | if ( data.image === undefined ) { 403 | 404 | console.warn( 'THREE.ObjectLoader: No "image" specified for', data.uuid ); 405 | 406 | } 407 | 408 | if ( images[ data.image ] === undefined ) { 409 | 410 | console.warn( 'THREE.ObjectLoader: Undefined image', data.image ); 411 | 412 | } 413 | 414 | var texture = new THREE.Texture( images[ data.image ] ); 415 | texture.needsUpdate = true; 416 | 417 | texture.uuid = data.uuid; 418 | 419 | if ( data.name !== undefined ) texture.name = data.name; 420 | if ( data.mapping !== undefined ) texture.mapping = parseConstant( data.mapping ); 421 | if ( data.offset !== undefined ) texture.offset = new THREE.Vector2( data.offset[ 0 ], data.offset[ 1 ] ); 422 | if ( data.repeat !== undefined ) texture.repeat = new THREE.Vector2( data.repeat[ 0 ], data.repeat[ 1 ] ); 423 | if ( data.minFilter !== undefined ) texture.minFilter = parseConstant( data.minFilter ); 424 | if ( data.magFilter !== undefined ) texture.magFilter = parseConstant( data.magFilter ); 425 | if ( data.anisotropy !== undefined ) texture.anisotropy = data.anisotropy; 426 | if ( Array.isArray( data.wrap ) ) { 427 | 428 | texture.wrapS = parseConstant( data.wrap[ 0 ] ); 429 | texture.wrapT = parseConstant( data.wrap[ 1 ] ); 430 | 431 | } 432 | 433 | textures[ data.uuid ] = texture; 434 | 435 | } 436 | 437 | } 438 | 439 | return textures; 440 | 441 | }, 442 | 443 | parseObject: function () { 444 | 445 | var matrix = new THREE.Matrix4(); 446 | 447 | return function ( data, geometries, materials ) { 448 | 449 | var object; 450 | 451 | function getGeometry( name ) { 452 | 453 | if ( geometries[ name ] === undefined ) { 454 | 455 | console.warn( 'THREE.ObjectLoader: Undefined geometry', name ); 456 | 457 | } 458 | 459 | return geometries[ name ]; 460 | 461 | } 462 | 463 | function getMaterial( name ) { 464 | 465 | if ( name === undefined ) return undefined; 466 | 467 | if ( materials[ name ] === undefined ) { 468 | 469 | console.warn( 'THREE.ObjectLoader: Undefined material', name ); 470 | 471 | } 472 | 473 | return materials[ name ]; 474 | 475 | } 476 | 477 | switch ( data.type ) { 478 | 479 | case 'Scene': 480 | 481 | object = new THREE.Scene(); 482 | 483 | break; 484 | 485 | case 'PerspectiveCamera': 486 | 487 | object = new THREE.PerspectiveCamera( data.fov, data.aspect, data.near, data.far ); 488 | 489 | break; 490 | 491 | case 'OrthographicCamera': 492 | 493 | object = new THREE.OrthographicCamera( data.left, data.right, data.top, data.bottom, data.near, data.far ); 494 | 495 | break; 496 | 497 | case 'AmbientLight': 498 | 499 | object = new THREE.AmbientLight( data.color, data.intensity ); 500 | 501 | break; 502 | 503 | case 'DirectionalLight': 504 | 505 | object = new THREE.DirectionalLight( data.color, data.intensity ); 506 | 507 | break; 508 | 509 | case 'PointLight': 510 | 511 | object = new THREE.PointLight( data.color, data.intensity, data.distance, data.decay ); 512 | 513 | break; 514 | 515 | case 'SpotLight': 516 | 517 | object = new THREE.SpotLight( data.color, data.intensity, data.distance, data.angle, data.exponent, data.decay ); 518 | 519 | break; 520 | 521 | case 'HemisphereLight': 522 | 523 | object = new THREE.HemisphereLight( data.color, data.groundColor, data.intensity ); 524 | 525 | break; 526 | 527 | case 'Mesh': 528 | 529 | var geometry = getGeometry( data.geometry ); 530 | var material = getMaterial( data.material ); 531 | 532 | if ( geometry.bones && geometry.bones.length > 0 ) { 533 | 534 | object = new THREE.SkinnedMesh( geometry, material ); 535 | 536 | } else { 537 | 538 | object = new THREE.Mesh( geometry, material ); 539 | 540 | } 541 | 542 | break; 543 | 544 | case 'LOD': 545 | 546 | object = new THREE.LOD(); 547 | 548 | break; 549 | 550 | case 'Line': 551 | 552 | object = new THREE.Line( getGeometry( data.geometry ), getMaterial( data.material ), data.mode ); 553 | 554 | break; 555 | 556 | case 'PointCloud': 557 | case 'Points': 558 | 559 | object = new THREE.Points( getGeometry( data.geometry ), getMaterial( data.material ) ); 560 | 561 | break; 562 | 563 | case 'Sprite': 564 | 565 | object = new THREE.Sprite( getMaterial( data.material ) ); 566 | 567 | break; 568 | 569 | case 'Group': 570 | 571 | object = new THREE.Group(); 572 | 573 | break; 574 | 575 | default: 576 | 577 | object = new THREE.Object3D(); 578 | 579 | } 580 | 581 | object.uuid = data.uuid; 582 | 583 | if ( data.name !== undefined ) object.name = data.name; 584 | if ( data.matrix !== undefined ) { 585 | 586 | matrix.fromArray( data.matrix ); 587 | matrix.decompose( object.position, object.quaternion, object.scale ); 588 | 589 | } else { 590 | 591 | if ( data.position !== undefined ) object.position.fromArray( data.position ); 592 | if ( data.rotation !== undefined ) object.rotation.fromArray( data.rotation ); 593 | if ( data.scale !== undefined ) object.scale.fromArray( data.scale ); 594 | 595 | } 596 | 597 | if ( data.castShadow !== undefined ) object.castShadow = data.castShadow; 598 | if ( data.receiveShadow !== undefined ) object.receiveShadow = data.receiveShadow; 599 | 600 | if ( data.visible !== undefined ) object.visible = data.visible; 601 | if ( data.userData !== undefined ) object.userData = data.userData; 602 | 603 | if ( data.children !== undefined ) { 604 | 605 | for ( var child in data.children ) { 606 | 607 | object.add( this.parseObject( data.children[ child ], geometries, materials ) ); 608 | 609 | } 610 | 611 | } 612 | 613 | if ( data.type === 'LOD' ) { 614 | 615 | var levels = data.levels; 616 | 617 | for ( var l = 0; l < levels.length; l ++ ) { 618 | 619 | var level = levels[ l ]; 620 | var child = object.getObjectByProperty( 'uuid', level.object ); 621 | 622 | if ( child !== undefined ) { 623 | 624 | object.addLevel( child, level.distance ); 625 | 626 | } 627 | 628 | } 629 | 630 | } 631 | 632 | return object; 633 | 634 | } 635 | 636 | }() 637 | 638 | }; 639 | -------------------------------------------------------------------------------- /[8] FPS (Frames Per Second:Framerate)/js/ObjectLoader.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @author mrdoob / http://mrdoob.com/ 3 | */ 4 | 5 | THREE.ObjectLoader = function ( manager ) { 6 | 7 | this.manager = ( manager !== undefined ) ? manager : THREE.DefaultLoadingManager; 8 | this.texturePath = ''; 9 | 10 | }; 11 | 12 | THREE.ObjectLoader.prototype = { 13 | 14 | constructor: THREE.ObjectLoader, 15 | 16 | load: function ( url, onLoad, onProgress, onError ) { 17 | 18 | if ( this.texturePath === '' ) { 19 | 20 | this.texturePath = url.substring( 0, url.lastIndexOf( '/' ) + 1 ); 21 | 22 | } 23 | 24 | var scope = this; 25 | 26 | var loader = new THREE.XHRLoader( scope.manager ); 27 | loader.load( url, function ( text ) { 28 | 29 | scope.parse( JSON.parse( text ), onLoad ); 30 | 31 | }, onProgress, onError ); 32 | 33 | }, 34 | 35 | setTexturePath: function ( value ) { 36 | 37 | this.texturePath = value; 38 | 39 | }, 40 | 41 | setCrossOrigin: function ( value ) { 42 | 43 | this.crossOrigin = value; 44 | 45 | }, 46 | 47 | parse: function ( json, onLoad ) { 48 | 49 | var geometries = this.parseGeometries( json.geometries ); 50 | 51 | var images = this.parseImages( json.images, function () { 52 | 53 | if ( onLoad !== undefined ) onLoad( object ); 54 | 55 | } ); 56 | 57 | var textures = this.parseTextures( json.textures, images ); 58 | var materials = this.parseMaterials( json.materials, textures ); 59 | 60 | var object = this.parseObject( json.object, geometries, materials ); 61 | 62 | if ( json.animations ) { 63 | 64 | object.animations = this.parseAnimations( json.animations ); 65 | 66 | } 67 | 68 | if ( json.images === undefined || json.images.length === 0 ) { 69 | 70 | if ( onLoad !== undefined ) onLoad( object ); 71 | 72 | } 73 | 74 | return object; 75 | 76 | }, 77 | 78 | parseGeometries: function ( json ) { 79 | 80 | var geometries = {}; 81 | 82 | if ( json !== undefined ) { 83 | 84 | var geometryLoader = new THREE.JSONLoader(); 85 | var bufferGeometryLoader = new THREE.BufferGeometryLoader(); 86 | 87 | for ( var i = 0, l = json.length; i < l; i ++ ) { 88 | 89 | var geometry; 90 | var data = json[ i ]; 91 | 92 | switch ( data.type ) { 93 | 94 | case 'PlaneGeometry': 95 | case 'PlaneBufferGeometry': 96 | 97 | geometry = new THREE[ data.type ]( 98 | data.width, 99 | data.height, 100 | data.widthSegments, 101 | data.heightSegments 102 | ); 103 | 104 | break; 105 | 106 | case 'BoxGeometry': 107 | case 'CubeGeometry': // backwards compatible 108 | 109 | geometry = new THREE.BoxGeometry( 110 | data.width, 111 | data.height, 112 | data.depth, 113 | data.widthSegments, 114 | data.heightSegments, 115 | data.depthSegments 116 | ); 117 | 118 | break; 119 | 120 | case 'CircleBufferGeometry': 121 | 122 | geometry = new THREE.CircleBufferGeometry( 123 | data.radius, 124 | data.segments, 125 | data.thetaStart, 126 | data.thetaLength 127 | ); 128 | 129 | break; 130 | 131 | case 'CircleGeometry': 132 | 133 | geometry = new THREE.CircleGeometry( 134 | data.radius, 135 | data.segments, 136 | data.thetaStart, 137 | data.thetaLength 138 | ); 139 | 140 | break; 141 | 142 | case 'CylinderGeometry': 143 | 144 | geometry = new THREE.CylinderGeometry( 145 | data.radiusTop, 146 | data.radiusBottom, 147 | data.height, 148 | data.radialSegments, 149 | data.heightSegments, 150 | data.openEnded, 151 | data.thetaStart, 152 | data.thetaLength 153 | ); 154 | 155 | break; 156 | 157 | case 'SphereGeometry': 158 | 159 | geometry = new THREE.SphereGeometry( 160 | data.radius, 161 | data.widthSegments, 162 | data.heightSegments, 163 | data.phiStart, 164 | data.phiLength, 165 | data.thetaStart, 166 | data.thetaLength 167 | ); 168 | 169 | break; 170 | 171 | case 'SphereBufferGeometry': 172 | 173 | geometry = new THREE.SphereBufferGeometry( 174 | data.radius, 175 | data.widthSegments, 176 | data.heightSegments, 177 | data.phiStart, 178 | data.phiLength, 179 | data.thetaStart, 180 | data.thetaLength 181 | ); 182 | 183 | break; 184 | 185 | case 'DodecahedronGeometry': 186 | 187 | geometry = new THREE.DodecahedronGeometry( 188 | data.radius, 189 | data.detail 190 | ); 191 | 192 | break; 193 | 194 | case 'IcosahedronGeometry': 195 | 196 | geometry = new THREE.IcosahedronGeometry( 197 | data.radius, 198 | data.detail 199 | ); 200 | 201 | break; 202 | 203 | case 'OctahedronGeometry': 204 | 205 | geometry = new THREE.OctahedronGeometry( 206 | data.radius, 207 | data.detail 208 | ); 209 | 210 | break; 211 | 212 | case 'TetrahedronGeometry': 213 | 214 | geometry = new THREE.TetrahedronGeometry( 215 | data.radius, 216 | data.detail 217 | ); 218 | 219 | break; 220 | 221 | case 'RingGeometry': 222 | 223 | geometry = new THREE.RingGeometry( 224 | data.innerRadius, 225 | data.outerRadius, 226 | data.thetaSegments, 227 | data.phiSegments, 228 | data.thetaStart, 229 | data.thetaLength 230 | ); 231 | 232 | break; 233 | 234 | case 'TorusGeometry': 235 | 236 | geometry = new THREE.TorusGeometry( 237 | data.radius, 238 | data.tube, 239 | data.radialSegments, 240 | data.tubularSegments, 241 | data.arc 242 | ); 243 | 244 | break; 245 | 246 | case 'TorusKnotGeometry': 247 | 248 | geometry = new THREE.TorusKnotGeometry( 249 | data.radius, 250 | data.tube, 251 | data.radialSegments, 252 | data.tubularSegments, 253 | data.p, 254 | data.q, 255 | data.heightScale 256 | ); 257 | 258 | break; 259 | 260 | case 'LatheGeometry': 261 | 262 | geometry = new THREE.LatheGeometry( 263 | data.points, 264 | data.segments, 265 | data.phiStart, 266 | data.phiLength 267 | ); 268 | 269 | break; 270 | 271 | case 'BufferGeometry': 272 | 273 | geometry = bufferGeometryLoader.parse( data ); 274 | 275 | break; 276 | 277 | case 'Geometry': 278 | 279 | geometry = geometryLoader.parse( data.data, this.texturePath ).geometry; 280 | 281 | break; 282 | 283 | default: 284 | 285 | console.warn( 'THREE.ObjectLoader: Unsupported geometry type "' + data.type + '"' ); 286 | 287 | continue; 288 | 289 | } 290 | 291 | geometry.uuid = data.uuid; 292 | 293 | if ( data.name !== undefined ) geometry.name = data.name; 294 | 295 | geometries[ data.uuid ] = geometry; 296 | 297 | } 298 | 299 | } 300 | 301 | return geometries; 302 | 303 | }, 304 | 305 | parseMaterials: function ( json, textures ) { 306 | 307 | var materials = {}; 308 | 309 | if ( json !== undefined ) { 310 | 311 | var loader = new THREE.MaterialLoader(); 312 | loader.setTextures( textures ); 313 | 314 | for ( var i = 0, l = json.length; i < l; i ++ ) { 315 | 316 | var material = loader.parse( json[ i ] ); 317 | materials[ material.uuid ] = material; 318 | 319 | } 320 | 321 | } 322 | 323 | return materials; 324 | 325 | }, 326 | 327 | parseAnimations: function ( json ) { 328 | 329 | var animations = []; 330 | 331 | for ( var i = 0; i < json.length; i ++ ) { 332 | 333 | var clip = THREE.AnimationClip.parse( json[ i ] ); 334 | 335 | animations.push( clip ); 336 | 337 | } 338 | 339 | return animations; 340 | 341 | }, 342 | 343 | parseImages: function ( json, onLoad ) { 344 | 345 | var scope = this; 346 | var images = {}; 347 | 348 | function loadImage( url ) { 349 | 350 | scope.manager.itemStart( url ); 351 | 352 | return loader.load( url, function () { 353 | 354 | scope.manager.itemEnd( url ); 355 | 356 | } ); 357 | 358 | } 359 | 360 | if ( json !== undefined && json.length > 0 ) { 361 | 362 | var manager = new THREE.LoadingManager( onLoad ); 363 | 364 | var loader = new THREE.ImageLoader( manager ); 365 | loader.setCrossOrigin( this.crossOrigin ); 366 | 367 | for ( var i = 0, l = json.length; i < l; i ++ ) { 368 | 369 | var image = json[ i ]; 370 | var path = /^(\/\/)|([a-z]+:(\/\/)?)/i.test( image.url ) ? image.url : scope.texturePath + image.url; 371 | 372 | images[ image.uuid ] = loadImage( path ); 373 | 374 | } 375 | 376 | } 377 | 378 | return images; 379 | 380 | }, 381 | 382 | parseTextures: function ( json, images ) { 383 | 384 | function parseConstant( value ) { 385 | 386 | if ( typeof( value ) === 'number' ) return value; 387 | 388 | console.warn( 'THREE.ObjectLoader.parseTexture: Constant should be in numeric form.', value ); 389 | 390 | return THREE[ value ]; 391 | 392 | } 393 | 394 | var textures = {}; 395 | 396 | if ( json !== undefined ) { 397 | 398 | for ( var i = 0, l = json.length; i < l; i ++ ) { 399 | 400 | var data = json[ i ]; 401 | 402 | if ( data.image === undefined ) { 403 | 404 | console.warn( 'THREE.ObjectLoader: No "image" specified for', data.uuid ); 405 | 406 | } 407 | 408 | if ( images[ data.image ] === undefined ) { 409 | 410 | console.warn( 'THREE.ObjectLoader: Undefined image', data.image ); 411 | 412 | } 413 | 414 | var texture = new THREE.Texture( images[ data.image ] ); 415 | texture.needsUpdate = true; 416 | 417 | texture.uuid = data.uuid; 418 | 419 | if ( data.name !== undefined ) texture.name = data.name; 420 | if ( data.mapping !== undefined ) texture.mapping = parseConstant( data.mapping ); 421 | if ( data.offset !== undefined ) texture.offset = new THREE.Vector2( data.offset[ 0 ], data.offset[ 1 ] ); 422 | if ( data.repeat !== undefined ) texture.repeat = new THREE.Vector2( data.repeat[ 0 ], data.repeat[ 1 ] ); 423 | if ( data.minFilter !== undefined ) texture.minFilter = parseConstant( data.minFilter ); 424 | if ( data.magFilter !== undefined ) texture.magFilter = parseConstant( data.magFilter ); 425 | if ( data.anisotropy !== undefined ) texture.anisotropy = data.anisotropy; 426 | if ( Array.isArray( data.wrap ) ) { 427 | 428 | texture.wrapS = parseConstant( data.wrap[ 0 ] ); 429 | texture.wrapT = parseConstant( data.wrap[ 1 ] ); 430 | 431 | } 432 | 433 | textures[ data.uuid ] = texture; 434 | 435 | } 436 | 437 | } 438 | 439 | return textures; 440 | 441 | }, 442 | 443 | parseObject: function () { 444 | 445 | var matrix = new THREE.Matrix4(); 446 | 447 | return function ( data, geometries, materials ) { 448 | 449 | var object; 450 | 451 | function getGeometry( name ) { 452 | 453 | if ( geometries[ name ] === undefined ) { 454 | 455 | console.warn( 'THREE.ObjectLoader: Undefined geometry', name ); 456 | 457 | } 458 | 459 | return geometries[ name ]; 460 | 461 | } 462 | 463 | function getMaterial( name ) { 464 | 465 | if ( name === undefined ) return undefined; 466 | 467 | if ( materials[ name ] === undefined ) { 468 | 469 | console.warn( 'THREE.ObjectLoader: Undefined material', name ); 470 | 471 | } 472 | 473 | return materials[ name ]; 474 | 475 | } 476 | 477 | switch ( data.type ) { 478 | 479 | case 'Scene': 480 | 481 | object = new THREE.Scene(); 482 | 483 | break; 484 | 485 | case 'PerspectiveCamera': 486 | 487 | object = new THREE.PerspectiveCamera( data.fov, data.aspect, data.near, data.far ); 488 | 489 | break; 490 | 491 | case 'OrthographicCamera': 492 | 493 | object = new THREE.OrthographicCamera( data.left, data.right, data.top, data.bottom, data.near, data.far ); 494 | 495 | break; 496 | 497 | case 'AmbientLight': 498 | 499 | object = new THREE.AmbientLight( data.color, data.intensity ); 500 | 501 | break; 502 | 503 | case 'DirectionalLight': 504 | 505 | object = new THREE.DirectionalLight( data.color, data.intensity ); 506 | 507 | break; 508 | 509 | case 'PointLight': 510 | 511 | object = new THREE.PointLight( data.color, data.intensity, data.distance, data.decay ); 512 | 513 | break; 514 | 515 | case 'SpotLight': 516 | 517 | object = new THREE.SpotLight( data.color, data.intensity, data.distance, data.angle, data.exponent, data.decay ); 518 | 519 | break; 520 | 521 | case 'HemisphereLight': 522 | 523 | object = new THREE.HemisphereLight( data.color, data.groundColor, data.intensity ); 524 | 525 | break; 526 | 527 | case 'Mesh': 528 | 529 | var geometry = getGeometry( data.geometry ); 530 | var material = getMaterial( data.material ); 531 | 532 | if ( geometry.bones && geometry.bones.length > 0 ) { 533 | 534 | object = new THREE.SkinnedMesh( geometry, material ); 535 | 536 | } else { 537 | 538 | object = new THREE.Mesh( geometry, material ); 539 | 540 | } 541 | 542 | break; 543 | 544 | case 'LOD': 545 | 546 | object = new THREE.LOD(); 547 | 548 | break; 549 | 550 | case 'Line': 551 | 552 | object = new THREE.Line( getGeometry( data.geometry ), getMaterial( data.material ), data.mode ); 553 | 554 | break; 555 | 556 | case 'PointCloud': 557 | case 'Points': 558 | 559 | object = new THREE.Points( getGeometry( data.geometry ), getMaterial( data.material ) ); 560 | 561 | break; 562 | 563 | case 'Sprite': 564 | 565 | object = new THREE.Sprite( getMaterial( data.material ) ); 566 | 567 | break; 568 | 569 | case 'Group': 570 | 571 | object = new THREE.Group(); 572 | 573 | break; 574 | 575 | default: 576 | 577 | object = new THREE.Object3D(); 578 | 579 | } 580 | 581 | object.uuid = data.uuid; 582 | 583 | if ( data.name !== undefined ) object.name = data.name; 584 | if ( data.matrix !== undefined ) { 585 | 586 | matrix.fromArray( data.matrix ); 587 | matrix.decompose( object.position, object.quaternion, object.scale ); 588 | 589 | } else { 590 | 591 | if ( data.position !== undefined ) object.position.fromArray( data.position ); 592 | if ( data.rotation !== undefined ) object.rotation.fromArray( data.rotation ); 593 | if ( data.scale !== undefined ) object.scale.fromArray( data.scale ); 594 | 595 | } 596 | 597 | if ( data.castShadow !== undefined ) object.castShadow = data.castShadow; 598 | if ( data.receiveShadow !== undefined ) object.receiveShadow = data.receiveShadow; 599 | 600 | if ( data.visible !== undefined ) object.visible = data.visible; 601 | if ( data.userData !== undefined ) object.userData = data.userData; 602 | 603 | if ( data.children !== undefined ) { 604 | 605 | for ( var child in data.children ) { 606 | 607 | object.add( this.parseObject( data.children[ child ], geometries, materials ) ); 608 | 609 | } 610 | 611 | } 612 | 613 | if ( data.type === 'LOD' ) { 614 | 615 | var levels = data.levels; 616 | 617 | for ( var l = 0; l < levels.length; l ++ ) { 618 | 619 | var level = levels[ l ]; 620 | var child = object.getObjectByProperty( 'uuid', level.object ); 621 | 622 | if ( child !== undefined ) { 623 | 624 | object.addLevel( child, level.distance ); 625 | 626 | } 627 | 628 | } 629 | 630 | } 631 | 632 | return object; 633 | 634 | } 635 | 636 | }() 637 | 638 | }; 639 | -------------------------------------------------------------------------------- /[10] Skybox/js/OrbitControls.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @author qiao / https://github.com/qiao 3 | * @author mrdoob / http://mrdoob.com 4 | * @author alteredq / http://alteredqualia.com/ 5 | * @author WestLangley / http://github.com/WestLangley 6 | * @author erich666 / http://erichaines.com 7 | */ 8 | /*global THREE, console */ 9 | 10 | // This set of controls performs orbiting, dollying (zooming), and panning. It maintains 11 | // the "up" direction as +Y, unlike the TrackballControls. Touch on tablet and phones is 12 | // supported. 13 | // 14 | // Orbit - left mouse / touch: one finger move 15 | // Zoom - middle mouse, or mousewheel / touch: two finger spread or squish 16 | // Pan - right mouse, or arrow keys / touch: three finter swipe 17 | // 18 | // This is a drop-in replacement for (most) TrackballControls used in examples. 19 | // That is, include this js file and wherever you see: 20 | // controls = new THREE.TrackballControls( camera ); 21 | // controls.target.z = 150; 22 | // Simple substitute "OrbitControls" and the control should work as-is. 23 | 24 | THREE.OrbitControls = function ( object, domElement ) { 25 | 26 | this.object = object; 27 | this.domElement = ( domElement !== undefined ) ? domElement : document; 28 | 29 | // API 30 | 31 | // Set to false to disable this control 32 | this.enabled = true; 33 | 34 | // "target" sets the location of focus, where the control orbits around 35 | // and where it pans with respect to. 36 | this.target = new THREE.Vector3(); 37 | // center is old, deprecated; use "target" instead 38 | this.center = this.target; 39 | 40 | // This option actually enables dollying in and out; left as "zoom" for 41 | // backwards compatibility 42 | this.noZoom = false; 43 | this.zoomSpeed = 1.0; 44 | // Limits to how far you can dolly in and out 45 | this.minDistance = 0; 46 | this.maxDistance = Infinity; 47 | 48 | // Set to true to disable this control 49 | this.noRotate = false; 50 | this.rotateSpeed = 1.0; 51 | 52 | // Set to true to disable this control 53 | this.noPan = false; 54 | this.keyPanSpeed = 7.0; // pixels moved per arrow key push 55 | 56 | // Set to true to automatically rotate around the target 57 | this.autoRotate = false; 58 | this.autoRotateSpeed = 2.0; // 30 seconds per round when fps is 60 59 | 60 | // How far you can orbit vertically, upper and lower limits. 61 | // Range is 0 to Math.PI radians. 62 | this.minPolarAngle = 0; // radians 63 | this.maxPolarAngle = Math.PI; // radians 64 | 65 | // Set to true to disable use of the keys 66 | this.noKeys = false; 67 | // The four arrow keys 68 | this.keys = { LEFT: 37, UP: 38, RIGHT: 39, BOTTOM: 40 }; 69 | 70 | //////////// 71 | // internals 72 | 73 | var scope = this; 74 | 75 | var EPS = 0.000001; 76 | 77 | var rotateStart = new THREE.Vector2(); 78 | var rotateEnd = new THREE.Vector2(); 79 | var rotateDelta = new THREE.Vector2(); 80 | 81 | var panStart = new THREE.Vector2(); 82 | var panEnd = new THREE.Vector2(); 83 | var panDelta = new THREE.Vector2(); 84 | 85 | var dollyStart = new THREE.Vector2(); 86 | var dollyEnd = new THREE.Vector2(); 87 | var dollyDelta = new THREE.Vector2(); 88 | 89 | var phiDelta = 0; 90 | var thetaDelta = 0; 91 | var scale = 1; 92 | var pan = new THREE.Vector3(); 93 | 94 | var lastPosition = new THREE.Vector3(); 95 | 96 | var STATE = { NONE : -1, ROTATE : 0, DOLLY : 1, PAN : 2, TOUCH_ROTATE : 3, TOUCH_DOLLY : 4, TOUCH_PAN : 5 }; 97 | var state = STATE.NONE; 98 | 99 | // events 100 | 101 | var changeEvent = { type: 'change' }; 102 | 103 | 104 | this.rotateLeft = function ( angle ) { 105 | 106 | if ( angle === undefined ) { 107 | 108 | angle = getAutoRotationAngle(); 109 | 110 | } 111 | 112 | thetaDelta -= angle; 113 | 114 | }; 115 | 116 | this.rotateUp = function ( angle ) { 117 | 118 | if ( angle === undefined ) { 119 | 120 | angle = getAutoRotationAngle(); 121 | 122 | } 123 | 124 | phiDelta -= angle; 125 | 126 | }; 127 | 128 | // pass in distance in world space to move left 129 | this.panLeft = function ( distance ) { 130 | 131 | var panOffset = new THREE.Vector3(); 132 | var te = this.object.matrix.elements; 133 | // get X column of matrix 134 | panOffset.set( te[0], te[1], te[2] ); 135 | panOffset.multiplyScalar(-distance); 136 | 137 | pan.add( panOffset ); 138 | 139 | }; 140 | 141 | // pass in distance in world space to move up 142 | this.panUp = function ( distance ) { 143 | 144 | var panOffset = new THREE.Vector3(); 145 | var te = this.object.matrix.elements; 146 | // get Y column of matrix 147 | panOffset.set( te[4], te[5], te[6] ); 148 | panOffset.multiplyScalar(distance); 149 | 150 | pan.add( panOffset ); 151 | }; 152 | 153 | // main entry point; pass in Vector2 of change desired in pixel space, 154 | // right and down are positive 155 | this.pan = function ( delta ) { 156 | 157 | var element = scope.domElement === document ? scope.domElement.body : scope.domElement; 158 | 159 | if ( scope.object.fov !== undefined ) { 160 | 161 | // perspective 162 | var position = scope.object.position; 163 | var offset = position.clone().sub( scope.target ); 164 | var targetDistance = offset.length(); 165 | 166 | // half of the fov is center to top of screen 167 | targetDistance *= Math.tan( (scope.object.fov/2) * Math.PI / 180.0 ); 168 | // we actually don't use screenWidth, since perspective camera is fixed to screen height 169 | scope.panLeft( 2 * delta.x * targetDistance / element.clientHeight ); 170 | scope.panUp( 2 * delta.y * targetDistance / element.clientHeight ); 171 | 172 | } else if ( scope.object.top !== undefined ) { 173 | 174 | // orthographic 175 | scope.panLeft( delta.x * (scope.object.right - scope.object.left) / element.clientWidth ); 176 | scope.panUp( delta.y * (scope.object.top - scope.object.bottom) / element.clientHeight ); 177 | 178 | } else { 179 | 180 | // camera neither orthographic or perspective - warn user 181 | console.warn( 'WARNING: OrbitControls.js encountered an unknown camera type - pan disabled.' ); 182 | 183 | } 184 | 185 | }; 186 | 187 | this.dollyIn = function ( dollyScale ) { 188 | 189 | if ( dollyScale === undefined ) { 190 | 191 | dollyScale = getZoomScale(); 192 | 193 | } 194 | 195 | scale /= dollyScale; 196 | 197 | }; 198 | 199 | this.dollyOut = function ( dollyScale ) { 200 | 201 | if ( dollyScale === undefined ) { 202 | 203 | dollyScale = getZoomScale(); 204 | 205 | } 206 | 207 | scale *= dollyScale; 208 | 209 | }; 210 | 211 | this.update = function () { 212 | 213 | var position = this.object.position; 214 | var offset = position.clone().sub( this.target ); 215 | 216 | // angle from z-axis around y-axis 217 | 218 | var theta = Math.atan2( offset.x, offset.z ); 219 | 220 | // angle from y-axis 221 | 222 | var phi = Math.atan2( Math.sqrt( offset.x * offset.x + offset.z * offset.z ), offset.y ); 223 | 224 | if ( this.autoRotate ) { 225 | 226 | this.rotateLeft( getAutoRotationAngle() ); 227 | 228 | } 229 | 230 | theta += thetaDelta; 231 | phi += phiDelta; 232 | 233 | // restrict phi to be between desired limits 234 | phi = Math.max( this.minPolarAngle, Math.min( this.maxPolarAngle, phi ) ); 235 | 236 | // restrict phi to be betwee EPS and PI-EPS 237 | phi = Math.max( EPS, Math.min( Math.PI - EPS, phi ) ); 238 | 239 | var radius = offset.length() * scale; 240 | 241 | // restrict radius to be between desired limits 242 | radius = Math.max( this.minDistance, Math.min( this.maxDistance, radius ) ); 243 | 244 | // move target to panned location 245 | this.target.add( pan ); 246 | 247 | offset.x = radius * Math.sin( phi ) * Math.sin( theta ); 248 | offset.y = radius * Math.cos( phi ); 249 | offset.z = radius * Math.sin( phi ) * Math.cos( theta ); 250 | 251 | position.copy( this.target ).add( offset ); 252 | 253 | this.object.lookAt( this.target ); 254 | 255 | thetaDelta = 0; 256 | phiDelta = 0; 257 | scale = 1; 258 | pan.set(0,0,0); 259 | 260 | if ( lastPosition.distanceTo( this.object.position ) > 0 ) { 261 | 262 | this.dispatchEvent( changeEvent ); 263 | 264 | lastPosition.copy( this.object.position ); 265 | 266 | } 267 | 268 | }; 269 | 270 | 271 | function getAutoRotationAngle() { 272 | 273 | return 2 * Math.PI / 60 / 60 * scope.autoRotateSpeed; 274 | 275 | } 276 | 277 | function getZoomScale() { 278 | 279 | return Math.pow( 0.95, scope.zoomSpeed ); 280 | 281 | } 282 | 283 | function onMouseDown( event ) { 284 | 285 | if ( scope.enabled === false ) { return; } 286 | event.preventDefault(); 287 | 288 | if ( event.button === 0 ) { 289 | if ( scope.noRotate === true ) { return; } 290 | 291 | state = STATE.ROTATE; 292 | 293 | rotateStart.set( event.clientX, event.clientY ); 294 | 295 | } else if ( event.button === 1 ) { 296 | if ( scope.noZoom === true ) { return; } 297 | 298 | state = STATE.DOLLY; 299 | 300 | dollyStart.set( event.clientX, event.clientY ); 301 | 302 | } else if ( event.button === 2 ) { 303 | if ( scope.noPan === true ) { return; } 304 | 305 | state = STATE.PAN; 306 | 307 | panStart.set( event.clientX, event.clientY ); 308 | 309 | } 310 | 311 | // Greggman fix: https://github.com/greggman/three.js/commit/fde9f9917d6d8381f06bf22cdff766029d1761be 312 | scope.domElement.addEventListener( 'mousemove', onMouseMove, false ); 313 | scope.domElement.addEventListener( 'mouseup', onMouseUp, false ); 314 | 315 | } 316 | 317 | function onMouseMove( event ) { 318 | 319 | if ( scope.enabled === false ) return; 320 | 321 | event.preventDefault(); 322 | 323 | var element = scope.domElement === document ? scope.domElement.body : scope.domElement; 324 | 325 | if ( state === STATE.ROTATE ) { 326 | 327 | if ( scope.noRotate === true ) return; 328 | 329 | rotateEnd.set( event.clientX, event.clientY ); 330 | rotateDelta.subVectors( rotateEnd, rotateStart ); 331 | 332 | // rotating across whole screen goes 360 degrees around 333 | scope.rotateLeft( 2 * Math.PI * rotateDelta.x / element.clientWidth * scope.rotateSpeed ); 334 | // rotating up and down along whole screen attempts to go 360, but limited to 180 335 | scope.rotateUp( 2 * Math.PI * rotateDelta.y / element.clientHeight * scope.rotateSpeed ); 336 | 337 | rotateStart.copy( rotateEnd ); 338 | 339 | } else if ( state === STATE.DOLLY ) { 340 | 341 | if ( scope.noZoom === true ) return; 342 | 343 | dollyEnd.set( event.clientX, event.clientY ); 344 | dollyDelta.subVectors( dollyEnd, dollyStart ); 345 | 346 | if ( dollyDelta.y > 0 ) { 347 | 348 | scope.dollyIn(); 349 | 350 | } else { 351 | 352 | scope.dollyOut(); 353 | 354 | } 355 | 356 | dollyStart.copy( dollyEnd ); 357 | 358 | } else if ( state === STATE.PAN ) { 359 | 360 | if ( scope.noPan === true ) return; 361 | 362 | panEnd.set( event.clientX, event.clientY ); 363 | panDelta.subVectors( panEnd, panStart ); 364 | 365 | scope.pan( panDelta ); 366 | 367 | panStart.copy( panEnd ); 368 | 369 | } 370 | 371 | // Greggman fix: https://github.com/greggman/three.js/commit/fde9f9917d6d8381f06bf22cdff766029d1761be 372 | scope.update(); 373 | 374 | } 375 | 376 | function onMouseUp( /* event */ ) { 377 | 378 | if ( scope.enabled === false ) return; 379 | 380 | // Greggman fix: https://github.com/greggman/three.js/commit/fde9f9917d6d8381f06bf22cdff766029d1761be 381 | scope.domElement.removeEventListener( 'mousemove', onMouseMove, false ); 382 | scope.domElement.removeEventListener( 'mouseup', onMouseUp, false ); 383 | 384 | state = STATE.NONE; 385 | 386 | } 387 | 388 | function onMouseWheel( event ) { 389 | 390 | if ( scope.enabled === false || scope.noZoom === true ) return; 391 | 392 | var delta = 0; 393 | 394 | if ( event.wheelDelta ) { // WebKit / Opera / Explorer 9 395 | 396 | delta = event.wheelDelta; 397 | 398 | } else if ( event.detail ) { // Firefox 399 | 400 | delta = - event.detail; 401 | 402 | } 403 | 404 | if ( delta > 0 ) { 405 | 406 | scope.dollyOut(); 407 | 408 | } else { 409 | 410 | scope.dollyIn(); 411 | 412 | } 413 | 414 | } 415 | 416 | function onKeyDown( event ) { 417 | 418 | if ( scope.enabled === false ) { return; } 419 | if ( scope.noKeys === true ) { return; } 420 | if ( scope.noPan === true ) { return; } 421 | 422 | // pan a pixel - I guess for precise positioning? 423 | // Greggman fix: https://github.com/greggman/three.js/commit/fde9f9917d6d8381f06bf22cdff766029d1761be 424 | var needUpdate = false; 425 | 426 | switch ( event.keyCode ) { 427 | 428 | case scope.keys.UP: 429 | scope.pan( new THREE.Vector2( 0, scope.keyPanSpeed ) ); 430 | needUpdate = true; 431 | break; 432 | case scope.keys.BOTTOM: 433 | scope.pan( new THREE.Vector2( 0, -scope.keyPanSpeed ) ); 434 | needUpdate = true; 435 | break; 436 | case scope.keys.LEFT: 437 | scope.pan( new THREE.Vector2( scope.keyPanSpeed, 0 ) ); 438 | needUpdate = true; 439 | break; 440 | case scope.keys.RIGHT: 441 | scope.pan( new THREE.Vector2( -scope.keyPanSpeed, 0 ) ); 442 | needUpdate = true; 443 | break; 444 | } 445 | 446 | // Greggman fix: https://github.com/greggman/three.js/commit/fde9f9917d6d8381f06bf22cdff766029d1761be 447 | if ( needUpdate ) { 448 | 449 | scope.update(); 450 | 451 | } 452 | 453 | } 454 | 455 | function touchstart( event ) { 456 | 457 | if ( scope.enabled === false ) { return; } 458 | 459 | switch ( event.touches.length ) { 460 | 461 | case 1: // one-fingered touch: rotate 462 | if ( scope.noRotate === true ) { return; } 463 | 464 | state = STATE.TOUCH_ROTATE; 465 | 466 | rotateStart.set( event.touches[ 0 ].pageX, event.touches[ 0 ].pageY ); 467 | break; 468 | 469 | case 2: // two-fingered touch: dolly 470 | if ( scope.noZoom === true ) { return; } 471 | 472 | state = STATE.TOUCH_DOLLY; 473 | 474 | var dx = event.touches[ 0 ].pageX - event.touches[ 1 ].pageX; 475 | var dy = event.touches[ 0 ].pageY - event.touches[ 1 ].pageY; 476 | var distance = Math.sqrt( dx * dx + dy * dy ); 477 | dollyStart.set( 0, distance ); 478 | break; 479 | 480 | case 3: // three-fingered touch: pan 481 | if ( scope.noPan === true ) { return; } 482 | 483 | state = STATE.TOUCH_PAN; 484 | 485 | panStart.set( event.touches[ 0 ].pageX, event.touches[ 0 ].pageY ); 486 | break; 487 | 488 | default: 489 | state = STATE.NONE; 490 | 491 | } 492 | } 493 | 494 | function touchmove( event ) { 495 | 496 | if ( scope.enabled === false ) { return; } 497 | 498 | event.preventDefault(); 499 | event.stopPropagation(); 500 | 501 | var element = scope.domElement === document ? scope.domElement.body : scope.domElement; 502 | 503 | switch ( event.touches.length ) { 504 | 505 | case 1: // one-fingered touch: rotate 506 | if ( scope.noRotate === true ) { return; } 507 | if ( state !== STATE.TOUCH_ROTATE ) { return; } 508 | 509 | rotateEnd.set( event.touches[ 0 ].pageX, event.touches[ 0 ].pageY ); 510 | rotateDelta.subVectors( rotateEnd, rotateStart ); 511 | 512 | // rotating across whole screen goes 360 degrees around 513 | scope.rotateLeft( 2 * Math.PI * rotateDelta.x / element.clientWidth * scope.rotateSpeed ); 514 | // rotating up and down along whole screen attempts to go 360, but limited to 180 515 | scope.rotateUp( 2 * Math.PI * rotateDelta.y / element.clientHeight * scope.rotateSpeed ); 516 | 517 | rotateStart.copy( rotateEnd ); 518 | break; 519 | 520 | case 2: // two-fingered touch: dolly 521 | if ( scope.noZoom === true ) { return; } 522 | if ( state !== STATE.TOUCH_DOLLY ) { return; } 523 | 524 | var dx = event.touches[ 0 ].pageX - event.touches[ 1 ].pageX; 525 | var dy = event.touches[ 0 ].pageY - event.touches[ 1 ].pageY; 526 | var distance = Math.sqrt( dx * dx + dy * dy ); 527 | 528 | dollyEnd.set( 0, distance ); 529 | dollyDelta.subVectors( dollyEnd, dollyStart ); 530 | 531 | if ( dollyDelta.y > 0 ) { 532 | 533 | scope.dollyOut(); 534 | 535 | } else { 536 | 537 | scope.dollyIn(); 538 | 539 | } 540 | 541 | dollyStart.copy( dollyEnd ); 542 | break; 543 | 544 | case 3: // three-fingered touch: pan 545 | if ( scope.noPan === true ) { return; } 546 | if ( state !== STATE.TOUCH_PAN ) { return; } 547 | 548 | panEnd.set( event.touches[ 0 ].pageX, event.touches[ 0 ].pageY ); 549 | panDelta.subVectors( panEnd, panStart ); 550 | 551 | scope.pan( panDelta ); 552 | 553 | panStart.copy( panEnd ); 554 | break; 555 | 556 | default: 557 | state = STATE.NONE; 558 | 559 | } 560 | 561 | } 562 | 563 | function touchend( /* event */ ) { 564 | 565 | if ( scope.enabled === false ) { return; } 566 | 567 | state = STATE.NONE; 568 | } 569 | 570 | this.domElement.addEventListener( 'contextmenu', function ( event ) { event.preventDefault(); }, false ); 571 | this.domElement.addEventListener( 'mousedown', onMouseDown, false ); 572 | this.domElement.addEventListener( 'mousewheel', onMouseWheel, false ); 573 | this.domElement.addEventListener( 'DOMMouseScroll', onMouseWheel, false ); // firefox 574 | 575 | this.domElement.addEventListener( 'keydown', onKeyDown, false ); 576 | 577 | this.domElement.addEventListener( 'touchstart', touchstart, false ); 578 | this.domElement.addEventListener( 'touchend', touchend, false ); 579 | this.domElement.addEventListener( 'touchmove', touchmove, false ); 580 | 581 | }; 582 | 583 | THREE.OrbitControls.prototype = Object.create( THREE.EventDispatcher.prototype ); 584 | -------------------------------------------------------------------------------- /[4] Controls/js/OrbitControls.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @author qiao / https://github.com/qiao 3 | * @author mrdoob / http://mrdoob.com 4 | * @author alteredq / http://alteredqualia.com/ 5 | * @author WestLangley / http://github.com/WestLangley 6 | * @author erich666 / http://erichaines.com 7 | */ 8 | /*global THREE, console */ 9 | 10 | // This set of controls performs orbiting, dollying (zooming), and panning. It maintains 11 | // the "up" direction as +Y, unlike the TrackballControls. Touch on tablet and phones is 12 | // supported. 13 | // 14 | // Orbit - left mouse / touch: one finger move 15 | // Zoom - middle mouse, or mousewheel / touch: two finger spread or squish 16 | // Pan - right mouse, or arrow keys / touch: three finter swipe 17 | // 18 | // This is a drop-in replacement for (most) TrackballControls used in examples. 19 | // That is, include this js file and wherever you see: 20 | // controls = new THREE.TrackballControls( camera ); 21 | // controls.target.z = 150; 22 | // Simple substitute "OrbitControls" and the control should work as-is. 23 | 24 | THREE.OrbitControls = function ( object, domElement ) { 25 | 26 | this.object = object; 27 | this.domElement = ( domElement !== undefined ) ? domElement : document; 28 | 29 | // API 30 | 31 | // Set to false to disable this control 32 | this.enabled = true; 33 | 34 | // "target" sets the location of focus, where the control orbits around 35 | // and where it pans with respect to. 36 | this.target = new THREE.Vector3(); 37 | // center is old, deprecated; use "target" instead 38 | this.center = this.target; 39 | 40 | // This option actually enables dollying in and out; left as "zoom" for 41 | // backwards compatibility 42 | this.noZoom = false; 43 | this.zoomSpeed = 1.0; 44 | // Limits to how far you can dolly in and out 45 | this.minDistance = 0; 46 | this.maxDistance = Infinity; 47 | 48 | // Set to true to disable this control 49 | this.noRotate = false; 50 | this.rotateSpeed = 1.0; 51 | 52 | // Set to true to disable this control 53 | this.noPan = false; 54 | this.keyPanSpeed = 7.0; // pixels moved per arrow key push 55 | 56 | // Set to true to automatically rotate around the target 57 | this.autoRotate = false; 58 | this.autoRotateSpeed = 2.0; // 30 seconds per round when fps is 60 59 | 60 | // How far you can orbit vertically, upper and lower limits. 61 | // Range is 0 to Math.PI radians. 62 | this.minPolarAngle = 0; // radians 63 | this.maxPolarAngle = Math.PI; // radians 64 | 65 | // Set to true to disable use of the keys 66 | this.noKeys = false; 67 | // The four arrow keys 68 | this.keys = { LEFT: 37, UP: 38, RIGHT: 39, BOTTOM: 40 }; 69 | 70 | //////////// 71 | // internals 72 | 73 | var scope = this; 74 | 75 | var EPS = 0.000001; 76 | 77 | var rotateStart = new THREE.Vector2(); 78 | var rotateEnd = new THREE.Vector2(); 79 | var rotateDelta = new THREE.Vector2(); 80 | 81 | var panStart = new THREE.Vector2(); 82 | var panEnd = new THREE.Vector2(); 83 | var panDelta = new THREE.Vector2(); 84 | 85 | var dollyStart = new THREE.Vector2(); 86 | var dollyEnd = new THREE.Vector2(); 87 | var dollyDelta = new THREE.Vector2(); 88 | 89 | var phiDelta = 0; 90 | var thetaDelta = 0; 91 | var scale = 1; 92 | var pan = new THREE.Vector3(); 93 | 94 | var lastPosition = new THREE.Vector3(); 95 | 96 | var STATE = { NONE : -1, ROTATE : 0, DOLLY : 1, PAN : 2, TOUCH_ROTATE : 3, TOUCH_DOLLY : 4, TOUCH_PAN : 5 }; 97 | var state = STATE.NONE; 98 | 99 | // events 100 | 101 | var changeEvent = { type: 'change' }; 102 | 103 | 104 | this.rotateLeft = function ( angle ) { 105 | 106 | if ( angle === undefined ) { 107 | 108 | angle = getAutoRotationAngle(); 109 | 110 | } 111 | 112 | thetaDelta -= angle; 113 | 114 | }; 115 | 116 | this.rotateUp = function ( angle ) { 117 | 118 | if ( angle === undefined ) { 119 | 120 | angle = getAutoRotationAngle(); 121 | 122 | } 123 | 124 | phiDelta -= angle; 125 | 126 | }; 127 | 128 | // pass in distance in world space to move left 129 | this.panLeft = function ( distance ) { 130 | 131 | var panOffset = new THREE.Vector3(); 132 | var te = this.object.matrix.elements; 133 | // get X column of matrix 134 | panOffset.set( te[0], te[1], te[2] ); 135 | panOffset.multiplyScalar(-distance); 136 | 137 | pan.add( panOffset ); 138 | 139 | }; 140 | 141 | // pass in distance in world space to move up 142 | this.panUp = function ( distance ) { 143 | 144 | var panOffset = new THREE.Vector3(); 145 | var te = this.object.matrix.elements; 146 | // get Y column of matrix 147 | panOffset.set( te[4], te[5], te[6] ); 148 | panOffset.multiplyScalar(distance); 149 | 150 | pan.add( panOffset ); 151 | }; 152 | 153 | // main entry point; pass in Vector2 of change desired in pixel space, 154 | // right and down are positive 155 | this.pan = function ( delta ) { 156 | 157 | var element = scope.domElement === document ? scope.domElement.body : scope.domElement; 158 | 159 | if ( scope.object.fov !== undefined ) { 160 | 161 | // perspective 162 | var position = scope.object.position; 163 | var offset = position.clone().sub( scope.target ); 164 | var targetDistance = offset.length(); 165 | 166 | // half of the fov is center to top of screen 167 | targetDistance *= Math.tan( (scope.object.fov/2) * Math.PI / 180.0 ); 168 | // we actually don't use screenWidth, since perspective camera is fixed to screen height 169 | scope.panLeft( 2 * delta.x * targetDistance / element.clientHeight ); 170 | scope.panUp( 2 * delta.y * targetDistance / element.clientHeight ); 171 | 172 | } else if ( scope.object.top !== undefined ) { 173 | 174 | // orthographic 175 | scope.panLeft( delta.x * (scope.object.right - scope.object.left) / element.clientWidth ); 176 | scope.panUp( delta.y * (scope.object.top - scope.object.bottom) / element.clientHeight ); 177 | 178 | } else { 179 | 180 | // camera neither orthographic or perspective - warn user 181 | console.warn( 'WARNING: OrbitControls.js encountered an unknown camera type - pan disabled.' ); 182 | 183 | } 184 | 185 | }; 186 | 187 | this.dollyIn = function ( dollyScale ) { 188 | 189 | if ( dollyScale === undefined ) { 190 | 191 | dollyScale = getZoomScale(); 192 | 193 | } 194 | 195 | scale /= dollyScale; 196 | 197 | }; 198 | 199 | this.dollyOut = function ( dollyScale ) { 200 | 201 | if ( dollyScale === undefined ) { 202 | 203 | dollyScale = getZoomScale(); 204 | 205 | } 206 | 207 | scale *= dollyScale; 208 | 209 | }; 210 | 211 | this.update = function () { 212 | 213 | var position = this.object.position; 214 | var offset = position.clone().sub( this.target ); 215 | 216 | // angle from z-axis around y-axis 217 | 218 | var theta = Math.atan2( offset.x, offset.z ); 219 | 220 | // angle from y-axis 221 | 222 | var phi = Math.atan2( Math.sqrt( offset.x * offset.x + offset.z * offset.z ), offset.y ); 223 | 224 | if ( this.autoRotate ) { 225 | 226 | this.rotateLeft( getAutoRotationAngle() ); 227 | 228 | } 229 | 230 | theta += thetaDelta; 231 | phi += phiDelta; 232 | 233 | // restrict phi to be between desired limits 234 | phi = Math.max( this.minPolarAngle, Math.min( this.maxPolarAngle, phi ) ); 235 | 236 | // restrict phi to be betwee EPS and PI-EPS 237 | phi = Math.max( EPS, Math.min( Math.PI - EPS, phi ) ); 238 | 239 | var radius = offset.length() * scale; 240 | 241 | // restrict radius to be between desired limits 242 | radius = Math.max( this.minDistance, Math.min( this.maxDistance, radius ) ); 243 | 244 | // move target to panned location 245 | this.target.add( pan ); 246 | 247 | offset.x = radius * Math.sin( phi ) * Math.sin( theta ); 248 | offset.y = radius * Math.cos( phi ); 249 | offset.z = radius * Math.sin( phi ) * Math.cos( theta ); 250 | 251 | position.copy( this.target ).add( offset ); 252 | 253 | this.object.lookAt( this.target ); 254 | 255 | thetaDelta = 0; 256 | phiDelta = 0; 257 | scale = 1; 258 | pan.set(0,0,0); 259 | 260 | if ( lastPosition.distanceTo( this.object.position ) > 0 ) { 261 | 262 | this.dispatchEvent( changeEvent ); 263 | 264 | lastPosition.copy( this.object.position ); 265 | 266 | } 267 | 268 | }; 269 | 270 | 271 | function getAutoRotationAngle() { 272 | 273 | return 2 * Math.PI / 60 / 60 * scope.autoRotateSpeed; 274 | 275 | } 276 | 277 | function getZoomScale() { 278 | 279 | return Math.pow( 0.95, scope.zoomSpeed ); 280 | 281 | } 282 | 283 | function onMouseDown( event ) { 284 | 285 | if ( scope.enabled === false ) { return; } 286 | event.preventDefault(); 287 | 288 | if ( event.button === 0 ) { 289 | if ( scope.noRotate === true ) { return; } 290 | 291 | state = STATE.ROTATE; 292 | 293 | rotateStart.set( event.clientX, event.clientY ); 294 | 295 | } else if ( event.button === 1 ) { 296 | if ( scope.noZoom === true ) { return; } 297 | 298 | state = STATE.DOLLY; 299 | 300 | dollyStart.set( event.clientX, event.clientY ); 301 | 302 | } else if ( event.button === 2 ) { 303 | if ( scope.noPan === true ) { return; } 304 | 305 | state = STATE.PAN; 306 | 307 | panStart.set( event.clientX, event.clientY ); 308 | 309 | } 310 | 311 | // Greggman fix: https://github.com/greggman/three.js/commit/fde9f9917d6d8381f06bf22cdff766029d1761be 312 | scope.domElement.addEventListener( 'mousemove', onMouseMove, false ); 313 | scope.domElement.addEventListener( 'mouseup', onMouseUp, false ); 314 | 315 | } 316 | 317 | function onMouseMove( event ) { 318 | 319 | if ( scope.enabled === false ) return; 320 | 321 | event.preventDefault(); 322 | 323 | var element = scope.domElement === document ? scope.domElement.body : scope.domElement; 324 | 325 | if ( state === STATE.ROTATE ) { 326 | 327 | if ( scope.noRotate === true ) return; 328 | 329 | rotateEnd.set( event.clientX, event.clientY ); 330 | rotateDelta.subVectors( rotateEnd, rotateStart ); 331 | 332 | // rotating across whole screen goes 360 degrees around 333 | scope.rotateLeft( 2 * Math.PI * rotateDelta.x / element.clientWidth * scope.rotateSpeed ); 334 | // rotating up and down along whole screen attempts to go 360, but limited to 180 335 | scope.rotateUp( 2 * Math.PI * rotateDelta.y / element.clientHeight * scope.rotateSpeed ); 336 | 337 | rotateStart.copy( rotateEnd ); 338 | 339 | } else if ( state === STATE.DOLLY ) { 340 | 341 | if ( scope.noZoom === true ) return; 342 | 343 | dollyEnd.set( event.clientX, event.clientY ); 344 | dollyDelta.subVectors( dollyEnd, dollyStart ); 345 | 346 | if ( dollyDelta.y > 0 ) { 347 | 348 | scope.dollyIn(); 349 | 350 | } else { 351 | 352 | scope.dollyOut(); 353 | 354 | } 355 | 356 | dollyStart.copy( dollyEnd ); 357 | 358 | } else if ( state === STATE.PAN ) { 359 | 360 | if ( scope.noPan === true ) return; 361 | 362 | panEnd.set( event.clientX, event.clientY ); 363 | panDelta.subVectors( panEnd, panStart ); 364 | 365 | scope.pan( panDelta ); 366 | 367 | panStart.copy( panEnd ); 368 | 369 | } 370 | 371 | // Greggman fix: https://github.com/greggman/three.js/commit/fde9f9917d6d8381f06bf22cdff766029d1761be 372 | scope.update(); 373 | 374 | } 375 | 376 | function onMouseUp( /* event */ ) { 377 | 378 | if ( scope.enabled === false ) return; 379 | 380 | // Greggman fix: https://github.com/greggman/three.js/commit/fde9f9917d6d8381f06bf22cdff766029d1761be 381 | scope.domElement.removeEventListener( 'mousemove', onMouseMove, false ); 382 | scope.domElement.removeEventListener( 'mouseup', onMouseUp, false ); 383 | 384 | state = STATE.NONE; 385 | 386 | } 387 | 388 | function onMouseWheel( event ) { 389 | 390 | if ( scope.enabled === false || scope.noZoom === true ) return; 391 | 392 | var delta = 0; 393 | 394 | if ( event.wheelDelta ) { // WebKit / Opera / Explorer 9 395 | 396 | delta = event.wheelDelta; 397 | 398 | } else if ( event.detail ) { // Firefox 399 | 400 | delta = - event.detail; 401 | 402 | } 403 | 404 | if ( delta > 0 ) { 405 | 406 | scope.dollyOut(); 407 | 408 | } else { 409 | 410 | scope.dollyIn(); 411 | 412 | } 413 | 414 | } 415 | 416 | function onKeyDown( event ) { 417 | 418 | if ( scope.enabled === false ) { return; } 419 | if ( scope.noKeys === true ) { return; } 420 | if ( scope.noPan === true ) { return; } 421 | 422 | // pan a pixel - I guess for precise positioning? 423 | // Greggman fix: https://github.com/greggman/three.js/commit/fde9f9917d6d8381f06bf22cdff766029d1761be 424 | var needUpdate = false; 425 | 426 | switch ( event.keyCode ) { 427 | 428 | case scope.keys.UP: 429 | scope.pan( new THREE.Vector2( 0, scope.keyPanSpeed ) ); 430 | needUpdate = true; 431 | break; 432 | case scope.keys.BOTTOM: 433 | scope.pan( new THREE.Vector2( 0, -scope.keyPanSpeed ) ); 434 | needUpdate = true; 435 | break; 436 | case scope.keys.LEFT: 437 | scope.pan( new THREE.Vector2( scope.keyPanSpeed, 0 ) ); 438 | needUpdate = true; 439 | break; 440 | case scope.keys.RIGHT: 441 | scope.pan( new THREE.Vector2( -scope.keyPanSpeed, 0 ) ); 442 | needUpdate = true; 443 | break; 444 | } 445 | 446 | // Greggman fix: https://github.com/greggman/three.js/commit/fde9f9917d6d8381f06bf22cdff766029d1761be 447 | if ( needUpdate ) { 448 | 449 | scope.update(); 450 | 451 | } 452 | 453 | } 454 | 455 | function touchstart( event ) { 456 | 457 | if ( scope.enabled === false ) { return; } 458 | 459 | switch ( event.touches.length ) { 460 | 461 | case 1: // one-fingered touch: rotate 462 | if ( scope.noRotate === true ) { return; } 463 | 464 | state = STATE.TOUCH_ROTATE; 465 | 466 | rotateStart.set( event.touches[ 0 ].pageX, event.touches[ 0 ].pageY ); 467 | break; 468 | 469 | case 2: // two-fingered touch: dolly 470 | if ( scope.noZoom === true ) { return; } 471 | 472 | state = STATE.TOUCH_DOLLY; 473 | 474 | var dx = event.touches[ 0 ].pageX - event.touches[ 1 ].pageX; 475 | var dy = event.touches[ 0 ].pageY - event.touches[ 1 ].pageY; 476 | var distance = Math.sqrt( dx * dx + dy * dy ); 477 | dollyStart.set( 0, distance ); 478 | break; 479 | 480 | case 3: // three-fingered touch: pan 481 | if ( scope.noPan === true ) { return; } 482 | 483 | state = STATE.TOUCH_PAN; 484 | 485 | panStart.set( event.touches[ 0 ].pageX, event.touches[ 0 ].pageY ); 486 | break; 487 | 488 | default: 489 | state = STATE.NONE; 490 | 491 | } 492 | } 493 | 494 | function touchmove( event ) { 495 | 496 | if ( scope.enabled === false ) { return; } 497 | 498 | event.preventDefault(); 499 | event.stopPropagation(); 500 | 501 | var element = scope.domElement === document ? scope.domElement.body : scope.domElement; 502 | 503 | switch ( event.touches.length ) { 504 | 505 | case 1: // one-fingered touch: rotate 506 | if ( scope.noRotate === true ) { return; } 507 | if ( state !== STATE.TOUCH_ROTATE ) { return; } 508 | 509 | rotateEnd.set( event.touches[ 0 ].pageX, event.touches[ 0 ].pageY ); 510 | rotateDelta.subVectors( rotateEnd, rotateStart ); 511 | 512 | // rotating across whole screen goes 360 degrees around 513 | scope.rotateLeft( 2 * Math.PI * rotateDelta.x / element.clientWidth * scope.rotateSpeed ); 514 | // rotating up and down along whole screen attempts to go 360, but limited to 180 515 | scope.rotateUp( 2 * Math.PI * rotateDelta.y / element.clientHeight * scope.rotateSpeed ); 516 | 517 | rotateStart.copy( rotateEnd ); 518 | break; 519 | 520 | case 2: // two-fingered touch: dolly 521 | if ( scope.noZoom === true ) { return; } 522 | if ( state !== STATE.TOUCH_DOLLY ) { return; } 523 | 524 | var dx = event.touches[ 0 ].pageX - event.touches[ 1 ].pageX; 525 | var dy = event.touches[ 0 ].pageY - event.touches[ 1 ].pageY; 526 | var distance = Math.sqrt( dx * dx + dy * dy ); 527 | 528 | dollyEnd.set( 0, distance ); 529 | dollyDelta.subVectors( dollyEnd, dollyStart ); 530 | 531 | if ( dollyDelta.y > 0 ) { 532 | 533 | scope.dollyOut(); 534 | 535 | } else { 536 | 537 | scope.dollyIn(); 538 | 539 | } 540 | 541 | dollyStart.copy( dollyEnd ); 542 | break; 543 | 544 | case 3: // three-fingered touch: pan 545 | if ( scope.noPan === true ) { return; } 546 | if ( state !== STATE.TOUCH_PAN ) { return; } 547 | 548 | panEnd.set( event.touches[ 0 ].pageX, event.touches[ 0 ].pageY ); 549 | panDelta.subVectors( panEnd, panStart ); 550 | 551 | scope.pan( panDelta ); 552 | 553 | panStart.copy( panEnd ); 554 | break; 555 | 556 | default: 557 | state = STATE.NONE; 558 | 559 | } 560 | 561 | } 562 | 563 | function touchend( /* event */ ) { 564 | 565 | if ( scope.enabled === false ) { return; } 566 | 567 | state = STATE.NONE; 568 | } 569 | 570 | this.domElement.addEventListener( 'contextmenu', function ( event ) { event.preventDefault(); }, false ); 571 | this.domElement.addEventListener( 'mousedown', onMouseDown, false ); 572 | this.domElement.addEventListener( 'mousewheel', onMouseWheel, false ); 573 | this.domElement.addEventListener( 'DOMMouseScroll', onMouseWheel, false ); // firefox 574 | 575 | this.domElement.addEventListener( 'keydown', onKeyDown, false ); 576 | 577 | this.domElement.addEventListener( 'touchstart', touchstart, false ); 578 | this.domElement.addEventListener( 'touchend', touchend, false ); 579 | this.domElement.addEventListener( 'touchmove', touchmove, false ); 580 | 581 | }; 582 | 583 | THREE.OrbitControls.prototype = Object.create( THREE.EventDispatcher.prototype ); 584 | -------------------------------------------------------------------------------- /[6] Lighting/js/OrbitControls.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @author qiao / https://github.com/qiao 3 | * @author mrdoob / http://mrdoob.com 4 | * @author alteredq / http://alteredqualia.com/ 5 | * @author WestLangley / http://github.com/WestLangley 6 | * @author erich666 / http://erichaines.com 7 | */ 8 | /*global THREE, console */ 9 | 10 | // This set of controls performs orbiting, dollying (zooming), and panning. It maintains 11 | // the "up" direction as +Y, unlike the TrackballControls. Touch on tablet and phones is 12 | // supported. 13 | // 14 | // Orbit - left mouse / touch: one finger move 15 | // Zoom - middle mouse, or mousewheel / touch: two finger spread or squish 16 | // Pan - right mouse, or arrow keys / touch: three finter swipe 17 | // 18 | // This is a drop-in replacement for (most) TrackballControls used in examples. 19 | // That is, include this js file and wherever you see: 20 | // controls = new THREE.TrackballControls( camera ); 21 | // controls.target.z = 150; 22 | // Simple substitute "OrbitControls" and the control should work as-is. 23 | 24 | THREE.OrbitControls = function ( object, domElement ) { 25 | 26 | this.object = object; 27 | this.domElement = ( domElement !== undefined ) ? domElement : document; 28 | 29 | // API 30 | 31 | // Set to false to disable this control 32 | this.enabled = true; 33 | 34 | // "target" sets the location of focus, where the control orbits around 35 | // and where it pans with respect to. 36 | this.target = new THREE.Vector3(); 37 | // center is old, deprecated; use "target" instead 38 | this.center = this.target; 39 | 40 | // This option actually enables dollying in and out; left as "zoom" for 41 | // backwards compatibility 42 | this.noZoom = false; 43 | this.zoomSpeed = 1.0; 44 | // Limits to how far you can dolly in and out 45 | this.minDistance = 0; 46 | this.maxDistance = Infinity; 47 | 48 | // Set to true to disable this control 49 | this.noRotate = false; 50 | this.rotateSpeed = 1.0; 51 | 52 | // Set to true to disable this control 53 | this.noPan = false; 54 | this.keyPanSpeed = 7.0; // pixels moved per arrow key push 55 | 56 | // Set to true to automatically rotate around the target 57 | this.autoRotate = false; 58 | this.autoRotateSpeed = 2.0; // 30 seconds per round when fps is 60 59 | 60 | // How far you can orbit vertically, upper and lower limits. 61 | // Range is 0 to Math.PI radians. 62 | this.minPolarAngle = 0; // radians 63 | this.maxPolarAngle = Math.PI; // radians 64 | 65 | // Set to true to disable use of the keys 66 | this.noKeys = false; 67 | // The four arrow keys 68 | this.keys = { LEFT: 37, UP: 38, RIGHT: 39, BOTTOM: 40 }; 69 | 70 | //////////// 71 | // internals 72 | 73 | var scope = this; 74 | 75 | var EPS = 0.000001; 76 | 77 | var rotateStart = new THREE.Vector2(); 78 | var rotateEnd = new THREE.Vector2(); 79 | var rotateDelta = new THREE.Vector2(); 80 | 81 | var panStart = new THREE.Vector2(); 82 | var panEnd = new THREE.Vector2(); 83 | var panDelta = new THREE.Vector2(); 84 | 85 | var dollyStart = new THREE.Vector2(); 86 | var dollyEnd = new THREE.Vector2(); 87 | var dollyDelta = new THREE.Vector2(); 88 | 89 | var phiDelta = 0; 90 | var thetaDelta = 0; 91 | var scale = 1; 92 | var pan = new THREE.Vector3(); 93 | 94 | var lastPosition = new THREE.Vector3(); 95 | 96 | var STATE = { NONE : -1, ROTATE : 0, DOLLY : 1, PAN : 2, TOUCH_ROTATE : 3, TOUCH_DOLLY : 4, TOUCH_PAN : 5 }; 97 | var state = STATE.NONE; 98 | 99 | // events 100 | 101 | var changeEvent = { type: 'change' }; 102 | 103 | 104 | this.rotateLeft = function ( angle ) { 105 | 106 | if ( angle === undefined ) { 107 | 108 | angle = getAutoRotationAngle(); 109 | 110 | } 111 | 112 | thetaDelta -= angle; 113 | 114 | }; 115 | 116 | this.rotateUp = function ( angle ) { 117 | 118 | if ( angle === undefined ) { 119 | 120 | angle = getAutoRotationAngle(); 121 | 122 | } 123 | 124 | phiDelta -= angle; 125 | 126 | }; 127 | 128 | // pass in distance in world space to move left 129 | this.panLeft = function ( distance ) { 130 | 131 | var panOffset = new THREE.Vector3(); 132 | var te = this.object.matrix.elements; 133 | // get X column of matrix 134 | panOffset.set( te[0], te[1], te[2] ); 135 | panOffset.multiplyScalar(-distance); 136 | 137 | pan.add( panOffset ); 138 | 139 | }; 140 | 141 | // pass in distance in world space to move up 142 | this.panUp = function ( distance ) { 143 | 144 | var panOffset = new THREE.Vector3(); 145 | var te = this.object.matrix.elements; 146 | // get Y column of matrix 147 | panOffset.set( te[4], te[5], te[6] ); 148 | panOffset.multiplyScalar(distance); 149 | 150 | pan.add( panOffset ); 151 | }; 152 | 153 | // main entry point; pass in Vector2 of change desired in pixel space, 154 | // right and down are positive 155 | this.pan = function ( delta ) { 156 | 157 | var element = scope.domElement === document ? scope.domElement.body : scope.domElement; 158 | 159 | if ( scope.object.fov !== undefined ) { 160 | 161 | // perspective 162 | var position = scope.object.position; 163 | var offset = position.clone().sub( scope.target ); 164 | var targetDistance = offset.length(); 165 | 166 | // half of the fov is center to top of screen 167 | targetDistance *= Math.tan( (scope.object.fov/2) * Math.PI / 180.0 ); 168 | // we actually don't use screenWidth, since perspective camera is fixed to screen height 169 | scope.panLeft( 2 * delta.x * targetDistance / element.clientHeight ); 170 | scope.panUp( 2 * delta.y * targetDistance / element.clientHeight ); 171 | 172 | } else if ( scope.object.top !== undefined ) { 173 | 174 | // orthographic 175 | scope.panLeft( delta.x * (scope.object.right - scope.object.left) / element.clientWidth ); 176 | scope.panUp( delta.y * (scope.object.top - scope.object.bottom) / element.clientHeight ); 177 | 178 | } else { 179 | 180 | // camera neither orthographic or perspective - warn user 181 | console.warn( 'WARNING: OrbitControls.js encountered an unknown camera type - pan disabled.' ); 182 | 183 | } 184 | 185 | }; 186 | 187 | this.dollyIn = function ( dollyScale ) { 188 | 189 | if ( dollyScale === undefined ) { 190 | 191 | dollyScale = getZoomScale(); 192 | 193 | } 194 | 195 | scale /= dollyScale; 196 | 197 | }; 198 | 199 | this.dollyOut = function ( dollyScale ) { 200 | 201 | if ( dollyScale === undefined ) { 202 | 203 | dollyScale = getZoomScale(); 204 | 205 | } 206 | 207 | scale *= dollyScale; 208 | 209 | }; 210 | 211 | this.update = function () { 212 | 213 | var position = this.object.position; 214 | var offset = position.clone().sub( this.target ); 215 | 216 | // angle from z-axis around y-axis 217 | 218 | var theta = Math.atan2( offset.x, offset.z ); 219 | 220 | // angle from y-axis 221 | 222 | var phi = Math.atan2( Math.sqrt( offset.x * offset.x + offset.z * offset.z ), offset.y ); 223 | 224 | if ( this.autoRotate ) { 225 | 226 | this.rotateLeft( getAutoRotationAngle() ); 227 | 228 | } 229 | 230 | theta += thetaDelta; 231 | phi += phiDelta; 232 | 233 | // restrict phi to be between desired limits 234 | phi = Math.max( this.minPolarAngle, Math.min( this.maxPolarAngle, phi ) ); 235 | 236 | // restrict phi to be betwee EPS and PI-EPS 237 | phi = Math.max( EPS, Math.min( Math.PI - EPS, phi ) ); 238 | 239 | var radius = offset.length() * scale; 240 | 241 | // restrict radius to be between desired limits 242 | radius = Math.max( this.minDistance, Math.min( this.maxDistance, radius ) ); 243 | 244 | // move target to panned location 245 | this.target.add( pan ); 246 | 247 | offset.x = radius * Math.sin( phi ) * Math.sin( theta ); 248 | offset.y = radius * Math.cos( phi ); 249 | offset.z = radius * Math.sin( phi ) * Math.cos( theta ); 250 | 251 | position.copy( this.target ).add( offset ); 252 | 253 | this.object.lookAt( this.target ); 254 | 255 | thetaDelta = 0; 256 | phiDelta = 0; 257 | scale = 1; 258 | pan.set(0,0,0); 259 | 260 | if ( lastPosition.distanceTo( this.object.position ) > 0 ) { 261 | 262 | this.dispatchEvent( changeEvent ); 263 | 264 | lastPosition.copy( this.object.position ); 265 | 266 | } 267 | 268 | }; 269 | 270 | 271 | function getAutoRotationAngle() { 272 | 273 | return 2 * Math.PI / 60 / 60 * scope.autoRotateSpeed; 274 | 275 | } 276 | 277 | function getZoomScale() { 278 | 279 | return Math.pow( 0.95, scope.zoomSpeed ); 280 | 281 | } 282 | 283 | function onMouseDown( event ) { 284 | 285 | if ( scope.enabled === false ) { return; } 286 | event.preventDefault(); 287 | 288 | if ( event.button === 0 ) { 289 | if ( scope.noRotate === true ) { return; } 290 | 291 | state = STATE.ROTATE; 292 | 293 | rotateStart.set( event.clientX, event.clientY ); 294 | 295 | } else if ( event.button === 1 ) { 296 | if ( scope.noZoom === true ) { return; } 297 | 298 | state = STATE.DOLLY; 299 | 300 | dollyStart.set( event.clientX, event.clientY ); 301 | 302 | } else if ( event.button === 2 ) { 303 | if ( scope.noPan === true ) { return; } 304 | 305 | state = STATE.PAN; 306 | 307 | panStart.set( event.clientX, event.clientY ); 308 | 309 | } 310 | 311 | // Greggman fix: https://github.com/greggman/three.js/commit/fde9f9917d6d8381f06bf22cdff766029d1761be 312 | scope.domElement.addEventListener( 'mousemove', onMouseMove, false ); 313 | scope.domElement.addEventListener( 'mouseup', onMouseUp, false ); 314 | 315 | } 316 | 317 | function onMouseMove( event ) { 318 | 319 | if ( scope.enabled === false ) return; 320 | 321 | event.preventDefault(); 322 | 323 | var element = scope.domElement === document ? scope.domElement.body : scope.domElement; 324 | 325 | if ( state === STATE.ROTATE ) { 326 | 327 | if ( scope.noRotate === true ) return; 328 | 329 | rotateEnd.set( event.clientX, event.clientY ); 330 | rotateDelta.subVectors( rotateEnd, rotateStart ); 331 | 332 | // rotating across whole screen goes 360 degrees around 333 | scope.rotateLeft( 2 * Math.PI * rotateDelta.x / element.clientWidth * scope.rotateSpeed ); 334 | // rotating up and down along whole screen attempts to go 360, but limited to 180 335 | scope.rotateUp( 2 * Math.PI * rotateDelta.y / element.clientHeight * scope.rotateSpeed ); 336 | 337 | rotateStart.copy( rotateEnd ); 338 | 339 | } else if ( state === STATE.DOLLY ) { 340 | 341 | if ( scope.noZoom === true ) return; 342 | 343 | dollyEnd.set( event.clientX, event.clientY ); 344 | dollyDelta.subVectors( dollyEnd, dollyStart ); 345 | 346 | if ( dollyDelta.y > 0 ) { 347 | 348 | scope.dollyIn(); 349 | 350 | } else { 351 | 352 | scope.dollyOut(); 353 | 354 | } 355 | 356 | dollyStart.copy( dollyEnd ); 357 | 358 | } else if ( state === STATE.PAN ) { 359 | 360 | if ( scope.noPan === true ) return; 361 | 362 | panEnd.set( event.clientX, event.clientY ); 363 | panDelta.subVectors( panEnd, panStart ); 364 | 365 | scope.pan( panDelta ); 366 | 367 | panStart.copy( panEnd ); 368 | 369 | } 370 | 371 | // Greggman fix: https://github.com/greggman/three.js/commit/fde9f9917d6d8381f06bf22cdff766029d1761be 372 | scope.update(); 373 | 374 | } 375 | 376 | function onMouseUp( /* event */ ) { 377 | 378 | if ( scope.enabled === false ) return; 379 | 380 | // Greggman fix: https://github.com/greggman/three.js/commit/fde9f9917d6d8381f06bf22cdff766029d1761be 381 | scope.domElement.removeEventListener( 'mousemove', onMouseMove, false ); 382 | scope.domElement.removeEventListener( 'mouseup', onMouseUp, false ); 383 | 384 | state = STATE.NONE; 385 | 386 | } 387 | 388 | function onMouseWheel( event ) { 389 | 390 | if ( scope.enabled === false || scope.noZoom === true ) return; 391 | 392 | var delta = 0; 393 | 394 | if ( event.wheelDelta ) { // WebKit / Opera / Explorer 9 395 | 396 | delta = event.wheelDelta; 397 | 398 | } else if ( event.detail ) { // Firefox 399 | 400 | delta = - event.detail; 401 | 402 | } 403 | 404 | if ( delta > 0 ) { 405 | 406 | scope.dollyOut(); 407 | 408 | } else { 409 | 410 | scope.dollyIn(); 411 | 412 | } 413 | 414 | } 415 | 416 | function onKeyDown( event ) { 417 | 418 | if ( scope.enabled === false ) { return; } 419 | if ( scope.noKeys === true ) { return; } 420 | if ( scope.noPan === true ) { return; } 421 | 422 | // pan a pixel - I guess for precise positioning? 423 | // Greggman fix: https://github.com/greggman/three.js/commit/fde9f9917d6d8381f06bf22cdff766029d1761be 424 | var needUpdate = false; 425 | 426 | switch ( event.keyCode ) { 427 | 428 | case scope.keys.UP: 429 | scope.pan( new THREE.Vector2( 0, scope.keyPanSpeed ) ); 430 | needUpdate = true; 431 | break; 432 | case scope.keys.BOTTOM: 433 | scope.pan( new THREE.Vector2( 0, -scope.keyPanSpeed ) ); 434 | needUpdate = true; 435 | break; 436 | case scope.keys.LEFT: 437 | scope.pan( new THREE.Vector2( scope.keyPanSpeed, 0 ) ); 438 | needUpdate = true; 439 | break; 440 | case scope.keys.RIGHT: 441 | scope.pan( new THREE.Vector2( -scope.keyPanSpeed, 0 ) ); 442 | needUpdate = true; 443 | break; 444 | } 445 | 446 | // Greggman fix: https://github.com/greggman/three.js/commit/fde9f9917d6d8381f06bf22cdff766029d1761be 447 | if ( needUpdate ) { 448 | 449 | scope.update(); 450 | 451 | } 452 | 453 | } 454 | 455 | function touchstart( event ) { 456 | 457 | if ( scope.enabled === false ) { return; } 458 | 459 | switch ( event.touches.length ) { 460 | 461 | case 1: // one-fingered touch: rotate 462 | if ( scope.noRotate === true ) { return; } 463 | 464 | state = STATE.TOUCH_ROTATE; 465 | 466 | rotateStart.set( event.touches[ 0 ].pageX, event.touches[ 0 ].pageY ); 467 | break; 468 | 469 | case 2: // two-fingered touch: dolly 470 | if ( scope.noZoom === true ) { return; } 471 | 472 | state = STATE.TOUCH_DOLLY; 473 | 474 | var dx = event.touches[ 0 ].pageX - event.touches[ 1 ].pageX; 475 | var dy = event.touches[ 0 ].pageY - event.touches[ 1 ].pageY; 476 | var distance = Math.sqrt( dx * dx + dy * dy ); 477 | dollyStart.set( 0, distance ); 478 | break; 479 | 480 | case 3: // three-fingered touch: pan 481 | if ( scope.noPan === true ) { return; } 482 | 483 | state = STATE.TOUCH_PAN; 484 | 485 | panStart.set( event.touches[ 0 ].pageX, event.touches[ 0 ].pageY ); 486 | break; 487 | 488 | default: 489 | state = STATE.NONE; 490 | 491 | } 492 | } 493 | 494 | function touchmove( event ) { 495 | 496 | if ( scope.enabled === false ) { return; } 497 | 498 | event.preventDefault(); 499 | event.stopPropagation(); 500 | 501 | var element = scope.domElement === document ? scope.domElement.body : scope.domElement; 502 | 503 | switch ( event.touches.length ) { 504 | 505 | case 1: // one-fingered touch: rotate 506 | if ( scope.noRotate === true ) { return; } 507 | if ( state !== STATE.TOUCH_ROTATE ) { return; } 508 | 509 | rotateEnd.set( event.touches[ 0 ].pageX, event.touches[ 0 ].pageY ); 510 | rotateDelta.subVectors( rotateEnd, rotateStart ); 511 | 512 | // rotating across whole screen goes 360 degrees around 513 | scope.rotateLeft( 2 * Math.PI * rotateDelta.x / element.clientWidth * scope.rotateSpeed ); 514 | // rotating up and down along whole screen attempts to go 360, but limited to 180 515 | scope.rotateUp( 2 * Math.PI * rotateDelta.y / element.clientHeight * scope.rotateSpeed ); 516 | 517 | rotateStart.copy( rotateEnd ); 518 | break; 519 | 520 | case 2: // two-fingered touch: dolly 521 | if ( scope.noZoom === true ) { return; } 522 | if ( state !== STATE.TOUCH_DOLLY ) { return; } 523 | 524 | var dx = event.touches[ 0 ].pageX - event.touches[ 1 ].pageX; 525 | var dy = event.touches[ 0 ].pageY - event.touches[ 1 ].pageY; 526 | var distance = Math.sqrt( dx * dx + dy * dy ); 527 | 528 | dollyEnd.set( 0, distance ); 529 | dollyDelta.subVectors( dollyEnd, dollyStart ); 530 | 531 | if ( dollyDelta.y > 0 ) { 532 | 533 | scope.dollyOut(); 534 | 535 | } else { 536 | 537 | scope.dollyIn(); 538 | 539 | } 540 | 541 | dollyStart.copy( dollyEnd ); 542 | break; 543 | 544 | case 3: // three-fingered touch: pan 545 | if ( scope.noPan === true ) { return; } 546 | if ( state !== STATE.TOUCH_PAN ) { return; } 547 | 548 | panEnd.set( event.touches[ 0 ].pageX, event.touches[ 0 ].pageY ); 549 | panDelta.subVectors( panEnd, panStart ); 550 | 551 | scope.pan( panDelta ); 552 | 553 | panStart.copy( panEnd ); 554 | break; 555 | 556 | default: 557 | state = STATE.NONE; 558 | 559 | } 560 | 561 | } 562 | 563 | function touchend( /* event */ ) { 564 | 565 | if ( scope.enabled === false ) { return; } 566 | 567 | state = STATE.NONE; 568 | } 569 | 570 | this.domElement.addEventListener( 'contextmenu', function ( event ) { event.preventDefault(); }, false ); 571 | this.domElement.addEventListener( 'mousedown', onMouseDown, false ); 572 | this.domElement.addEventListener( 'mousewheel', onMouseWheel, false ); 573 | this.domElement.addEventListener( 'DOMMouseScroll', onMouseWheel, false ); // firefox 574 | 575 | this.domElement.addEventListener( 'keydown', onKeyDown, false ); 576 | 577 | this.domElement.addEventListener( 'touchstart', touchstart, false ); 578 | this.domElement.addEventListener( 'touchend', touchend, false ); 579 | this.domElement.addEventListener( 'touchmove', touchmove, false ); 580 | 581 | }; 582 | 583 | THREE.OrbitControls.prototype = Object.create( THREE.EventDispatcher.prototype ); 584 | -------------------------------------------------------------------------------- /[7] Model Loading/js/OrbitControls.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @author qiao / https://github.com/qiao 3 | * @author mrdoob / http://mrdoob.com 4 | * @author alteredq / http://alteredqualia.com/ 5 | * @author WestLangley / http://github.com/WestLangley 6 | * @author erich666 / http://erichaines.com 7 | */ 8 | /*global THREE, console */ 9 | 10 | // This set of controls performs orbiting, dollying (zooming), and panning. It maintains 11 | // the "up" direction as +Y, unlike the TrackballControls. Touch on tablet and phones is 12 | // supported. 13 | // 14 | // Orbit - left mouse / touch: one finger move 15 | // Zoom - middle mouse, or mousewheel / touch: two finger spread or squish 16 | // Pan - right mouse, or arrow keys / touch: three finter swipe 17 | // 18 | // This is a drop-in replacement for (most) TrackballControls used in examples. 19 | // That is, include this js file and wherever you see: 20 | // controls = new THREE.TrackballControls( camera ); 21 | // controls.target.z = 150; 22 | // Simple substitute "OrbitControls" and the control should work as-is. 23 | 24 | THREE.OrbitControls = function ( object, domElement ) { 25 | 26 | this.object = object; 27 | this.domElement = ( domElement !== undefined ) ? domElement : document; 28 | 29 | // API 30 | 31 | // Set to false to disable this control 32 | this.enabled = true; 33 | 34 | // "target" sets the location of focus, where the control orbits around 35 | // and where it pans with respect to. 36 | this.target = new THREE.Vector3(); 37 | // center is old, deprecated; use "target" instead 38 | this.center = this.target; 39 | 40 | // This option actually enables dollying in and out; left as "zoom" for 41 | // backwards compatibility 42 | this.noZoom = false; 43 | this.zoomSpeed = 1.0; 44 | // Limits to how far you can dolly in and out 45 | this.minDistance = 0; 46 | this.maxDistance = Infinity; 47 | 48 | // Set to true to disable this control 49 | this.noRotate = false; 50 | this.rotateSpeed = 1.0; 51 | 52 | // Set to true to disable this control 53 | this.noPan = false; 54 | this.keyPanSpeed = 7.0; // pixels moved per arrow key push 55 | 56 | // Set to true to automatically rotate around the target 57 | this.autoRotate = false; 58 | this.autoRotateSpeed = 2.0; // 30 seconds per round when fps is 60 59 | 60 | // How far you can orbit vertically, upper and lower limits. 61 | // Range is 0 to Math.PI radians. 62 | this.minPolarAngle = 0; // radians 63 | this.maxPolarAngle = Math.PI; // radians 64 | 65 | // Set to true to disable use of the keys 66 | this.noKeys = false; 67 | // The four arrow keys 68 | this.keys = { LEFT: 37, UP: 38, RIGHT: 39, BOTTOM: 40 }; 69 | 70 | //////////// 71 | // internals 72 | 73 | var scope = this; 74 | 75 | var EPS = 0.000001; 76 | 77 | var rotateStart = new THREE.Vector2(); 78 | var rotateEnd = new THREE.Vector2(); 79 | var rotateDelta = new THREE.Vector2(); 80 | 81 | var panStart = new THREE.Vector2(); 82 | var panEnd = new THREE.Vector2(); 83 | var panDelta = new THREE.Vector2(); 84 | 85 | var dollyStart = new THREE.Vector2(); 86 | var dollyEnd = new THREE.Vector2(); 87 | var dollyDelta = new THREE.Vector2(); 88 | 89 | var phiDelta = 0; 90 | var thetaDelta = 0; 91 | var scale = 1; 92 | var pan = new THREE.Vector3(); 93 | 94 | var lastPosition = new THREE.Vector3(); 95 | 96 | var STATE = { NONE : -1, ROTATE : 0, DOLLY : 1, PAN : 2, TOUCH_ROTATE : 3, TOUCH_DOLLY : 4, TOUCH_PAN : 5 }; 97 | var state = STATE.NONE; 98 | 99 | // events 100 | 101 | var changeEvent = { type: 'change' }; 102 | 103 | 104 | this.rotateLeft = function ( angle ) { 105 | 106 | if ( angle === undefined ) { 107 | 108 | angle = getAutoRotationAngle(); 109 | 110 | } 111 | 112 | thetaDelta -= angle; 113 | 114 | }; 115 | 116 | this.rotateUp = function ( angle ) { 117 | 118 | if ( angle === undefined ) { 119 | 120 | angle = getAutoRotationAngle(); 121 | 122 | } 123 | 124 | phiDelta -= angle; 125 | 126 | }; 127 | 128 | // pass in distance in world space to move left 129 | this.panLeft = function ( distance ) { 130 | 131 | var panOffset = new THREE.Vector3(); 132 | var te = this.object.matrix.elements; 133 | // get X column of matrix 134 | panOffset.set( te[0], te[1], te[2] ); 135 | panOffset.multiplyScalar(-distance); 136 | 137 | pan.add( panOffset ); 138 | 139 | }; 140 | 141 | // pass in distance in world space to move up 142 | this.panUp = function ( distance ) { 143 | 144 | var panOffset = new THREE.Vector3(); 145 | var te = this.object.matrix.elements; 146 | // get Y column of matrix 147 | panOffset.set( te[4], te[5], te[6] ); 148 | panOffset.multiplyScalar(distance); 149 | 150 | pan.add( panOffset ); 151 | }; 152 | 153 | // main entry point; pass in Vector2 of change desired in pixel space, 154 | // right and down are positive 155 | this.pan = function ( delta ) { 156 | 157 | var element = scope.domElement === document ? scope.domElement.body : scope.domElement; 158 | 159 | if ( scope.object.fov !== undefined ) { 160 | 161 | // perspective 162 | var position = scope.object.position; 163 | var offset = position.clone().sub( scope.target ); 164 | var targetDistance = offset.length(); 165 | 166 | // half of the fov is center to top of screen 167 | targetDistance *= Math.tan( (scope.object.fov/2) * Math.PI / 180.0 ); 168 | // we actually don't use screenWidth, since perspective camera is fixed to screen height 169 | scope.panLeft( 2 * delta.x * targetDistance / element.clientHeight ); 170 | scope.panUp( 2 * delta.y * targetDistance / element.clientHeight ); 171 | 172 | } else if ( scope.object.top !== undefined ) { 173 | 174 | // orthographic 175 | scope.panLeft( delta.x * (scope.object.right - scope.object.left) / element.clientWidth ); 176 | scope.panUp( delta.y * (scope.object.top - scope.object.bottom) / element.clientHeight ); 177 | 178 | } else { 179 | 180 | // camera neither orthographic or perspective - warn user 181 | console.warn( 'WARNING: OrbitControls.js encountered an unknown camera type - pan disabled.' ); 182 | 183 | } 184 | 185 | }; 186 | 187 | this.dollyIn = function ( dollyScale ) { 188 | 189 | if ( dollyScale === undefined ) { 190 | 191 | dollyScale = getZoomScale(); 192 | 193 | } 194 | 195 | scale /= dollyScale; 196 | 197 | }; 198 | 199 | this.dollyOut = function ( dollyScale ) { 200 | 201 | if ( dollyScale === undefined ) { 202 | 203 | dollyScale = getZoomScale(); 204 | 205 | } 206 | 207 | scale *= dollyScale; 208 | 209 | }; 210 | 211 | this.update = function () { 212 | 213 | var position = this.object.position; 214 | var offset = position.clone().sub( this.target ); 215 | 216 | // angle from z-axis around y-axis 217 | 218 | var theta = Math.atan2( offset.x, offset.z ); 219 | 220 | // angle from y-axis 221 | 222 | var phi = Math.atan2( Math.sqrt( offset.x * offset.x + offset.z * offset.z ), offset.y ); 223 | 224 | if ( this.autoRotate ) { 225 | 226 | this.rotateLeft( getAutoRotationAngle() ); 227 | 228 | } 229 | 230 | theta += thetaDelta; 231 | phi += phiDelta; 232 | 233 | // restrict phi to be between desired limits 234 | phi = Math.max( this.minPolarAngle, Math.min( this.maxPolarAngle, phi ) ); 235 | 236 | // restrict phi to be betwee EPS and PI-EPS 237 | phi = Math.max( EPS, Math.min( Math.PI - EPS, phi ) ); 238 | 239 | var radius = offset.length() * scale; 240 | 241 | // restrict radius to be between desired limits 242 | radius = Math.max( this.minDistance, Math.min( this.maxDistance, radius ) ); 243 | 244 | // move target to panned location 245 | this.target.add( pan ); 246 | 247 | offset.x = radius * Math.sin( phi ) * Math.sin( theta ); 248 | offset.y = radius * Math.cos( phi ); 249 | offset.z = radius * Math.sin( phi ) * Math.cos( theta ); 250 | 251 | position.copy( this.target ).add( offset ); 252 | 253 | this.object.lookAt( this.target ); 254 | 255 | thetaDelta = 0; 256 | phiDelta = 0; 257 | scale = 1; 258 | pan.set(0,0,0); 259 | 260 | if ( lastPosition.distanceTo( this.object.position ) > 0 ) { 261 | 262 | this.dispatchEvent( changeEvent ); 263 | 264 | lastPosition.copy( this.object.position ); 265 | 266 | } 267 | 268 | }; 269 | 270 | 271 | function getAutoRotationAngle() { 272 | 273 | return 2 * Math.PI / 60 / 60 * scope.autoRotateSpeed; 274 | 275 | } 276 | 277 | function getZoomScale() { 278 | 279 | return Math.pow( 0.95, scope.zoomSpeed ); 280 | 281 | } 282 | 283 | function onMouseDown( event ) { 284 | 285 | if ( scope.enabled === false ) { return; } 286 | event.preventDefault(); 287 | 288 | if ( event.button === 0 ) { 289 | if ( scope.noRotate === true ) { return; } 290 | 291 | state = STATE.ROTATE; 292 | 293 | rotateStart.set( event.clientX, event.clientY ); 294 | 295 | } else if ( event.button === 1 ) { 296 | if ( scope.noZoom === true ) { return; } 297 | 298 | state = STATE.DOLLY; 299 | 300 | dollyStart.set( event.clientX, event.clientY ); 301 | 302 | } else if ( event.button === 2 ) { 303 | if ( scope.noPan === true ) { return; } 304 | 305 | state = STATE.PAN; 306 | 307 | panStart.set( event.clientX, event.clientY ); 308 | 309 | } 310 | 311 | // Greggman fix: https://github.com/greggman/three.js/commit/fde9f9917d6d8381f06bf22cdff766029d1761be 312 | scope.domElement.addEventListener( 'mousemove', onMouseMove, false ); 313 | scope.domElement.addEventListener( 'mouseup', onMouseUp, false ); 314 | 315 | } 316 | 317 | function onMouseMove( event ) { 318 | 319 | if ( scope.enabled === false ) return; 320 | 321 | event.preventDefault(); 322 | 323 | var element = scope.domElement === document ? scope.domElement.body : scope.domElement; 324 | 325 | if ( state === STATE.ROTATE ) { 326 | 327 | if ( scope.noRotate === true ) return; 328 | 329 | rotateEnd.set( event.clientX, event.clientY ); 330 | rotateDelta.subVectors( rotateEnd, rotateStart ); 331 | 332 | // rotating across whole screen goes 360 degrees around 333 | scope.rotateLeft( 2 * Math.PI * rotateDelta.x / element.clientWidth * scope.rotateSpeed ); 334 | // rotating up and down along whole screen attempts to go 360, but limited to 180 335 | scope.rotateUp( 2 * Math.PI * rotateDelta.y / element.clientHeight * scope.rotateSpeed ); 336 | 337 | rotateStart.copy( rotateEnd ); 338 | 339 | } else if ( state === STATE.DOLLY ) { 340 | 341 | if ( scope.noZoom === true ) return; 342 | 343 | dollyEnd.set( event.clientX, event.clientY ); 344 | dollyDelta.subVectors( dollyEnd, dollyStart ); 345 | 346 | if ( dollyDelta.y > 0 ) { 347 | 348 | scope.dollyIn(); 349 | 350 | } else { 351 | 352 | scope.dollyOut(); 353 | 354 | } 355 | 356 | dollyStart.copy( dollyEnd ); 357 | 358 | } else if ( state === STATE.PAN ) { 359 | 360 | if ( scope.noPan === true ) return; 361 | 362 | panEnd.set( event.clientX, event.clientY ); 363 | panDelta.subVectors( panEnd, panStart ); 364 | 365 | scope.pan( panDelta ); 366 | 367 | panStart.copy( panEnd ); 368 | 369 | } 370 | 371 | // Greggman fix: https://github.com/greggman/three.js/commit/fde9f9917d6d8381f06bf22cdff766029d1761be 372 | scope.update(); 373 | 374 | } 375 | 376 | function onMouseUp( /* event */ ) { 377 | 378 | if ( scope.enabled === false ) return; 379 | 380 | // Greggman fix: https://github.com/greggman/three.js/commit/fde9f9917d6d8381f06bf22cdff766029d1761be 381 | scope.domElement.removeEventListener( 'mousemove', onMouseMove, false ); 382 | scope.domElement.removeEventListener( 'mouseup', onMouseUp, false ); 383 | 384 | state = STATE.NONE; 385 | 386 | } 387 | 388 | function onMouseWheel( event ) { 389 | 390 | if ( scope.enabled === false || scope.noZoom === true ) return; 391 | 392 | var delta = 0; 393 | 394 | if ( event.wheelDelta ) { // WebKit / Opera / Explorer 9 395 | 396 | delta = event.wheelDelta; 397 | 398 | } else if ( event.detail ) { // Firefox 399 | 400 | delta = - event.detail; 401 | 402 | } 403 | 404 | if ( delta > 0 ) { 405 | 406 | scope.dollyOut(); 407 | 408 | } else { 409 | 410 | scope.dollyIn(); 411 | 412 | } 413 | 414 | } 415 | 416 | function onKeyDown( event ) { 417 | 418 | if ( scope.enabled === false ) { return; } 419 | if ( scope.noKeys === true ) { return; } 420 | if ( scope.noPan === true ) { return; } 421 | 422 | // pan a pixel - I guess for precise positioning? 423 | // Greggman fix: https://github.com/greggman/three.js/commit/fde9f9917d6d8381f06bf22cdff766029d1761be 424 | var needUpdate = false; 425 | 426 | switch ( event.keyCode ) { 427 | 428 | case scope.keys.UP: 429 | scope.pan( new THREE.Vector2( 0, scope.keyPanSpeed ) ); 430 | needUpdate = true; 431 | break; 432 | case scope.keys.BOTTOM: 433 | scope.pan( new THREE.Vector2( 0, -scope.keyPanSpeed ) ); 434 | needUpdate = true; 435 | break; 436 | case scope.keys.LEFT: 437 | scope.pan( new THREE.Vector2( scope.keyPanSpeed, 0 ) ); 438 | needUpdate = true; 439 | break; 440 | case scope.keys.RIGHT: 441 | scope.pan( new THREE.Vector2( -scope.keyPanSpeed, 0 ) ); 442 | needUpdate = true; 443 | break; 444 | } 445 | 446 | // Greggman fix: https://github.com/greggman/three.js/commit/fde9f9917d6d8381f06bf22cdff766029d1761be 447 | if ( needUpdate ) { 448 | 449 | scope.update(); 450 | 451 | } 452 | 453 | } 454 | 455 | function touchstart( event ) { 456 | 457 | if ( scope.enabled === false ) { return; } 458 | 459 | switch ( event.touches.length ) { 460 | 461 | case 1: // one-fingered touch: rotate 462 | if ( scope.noRotate === true ) { return; } 463 | 464 | state = STATE.TOUCH_ROTATE; 465 | 466 | rotateStart.set( event.touches[ 0 ].pageX, event.touches[ 0 ].pageY ); 467 | break; 468 | 469 | case 2: // two-fingered touch: dolly 470 | if ( scope.noZoom === true ) { return; } 471 | 472 | state = STATE.TOUCH_DOLLY; 473 | 474 | var dx = event.touches[ 0 ].pageX - event.touches[ 1 ].pageX; 475 | var dy = event.touches[ 0 ].pageY - event.touches[ 1 ].pageY; 476 | var distance = Math.sqrt( dx * dx + dy * dy ); 477 | dollyStart.set( 0, distance ); 478 | break; 479 | 480 | case 3: // three-fingered touch: pan 481 | if ( scope.noPan === true ) { return; } 482 | 483 | state = STATE.TOUCH_PAN; 484 | 485 | panStart.set( event.touches[ 0 ].pageX, event.touches[ 0 ].pageY ); 486 | break; 487 | 488 | default: 489 | state = STATE.NONE; 490 | 491 | } 492 | } 493 | 494 | function touchmove( event ) { 495 | 496 | if ( scope.enabled === false ) { return; } 497 | 498 | event.preventDefault(); 499 | event.stopPropagation(); 500 | 501 | var element = scope.domElement === document ? scope.domElement.body : scope.domElement; 502 | 503 | switch ( event.touches.length ) { 504 | 505 | case 1: // one-fingered touch: rotate 506 | if ( scope.noRotate === true ) { return; } 507 | if ( state !== STATE.TOUCH_ROTATE ) { return; } 508 | 509 | rotateEnd.set( event.touches[ 0 ].pageX, event.touches[ 0 ].pageY ); 510 | rotateDelta.subVectors( rotateEnd, rotateStart ); 511 | 512 | // rotating across whole screen goes 360 degrees around 513 | scope.rotateLeft( 2 * Math.PI * rotateDelta.x / element.clientWidth * scope.rotateSpeed ); 514 | // rotating up and down along whole screen attempts to go 360, but limited to 180 515 | scope.rotateUp( 2 * Math.PI * rotateDelta.y / element.clientHeight * scope.rotateSpeed ); 516 | 517 | rotateStart.copy( rotateEnd ); 518 | break; 519 | 520 | case 2: // two-fingered touch: dolly 521 | if ( scope.noZoom === true ) { return; } 522 | if ( state !== STATE.TOUCH_DOLLY ) { return; } 523 | 524 | var dx = event.touches[ 0 ].pageX - event.touches[ 1 ].pageX; 525 | var dy = event.touches[ 0 ].pageY - event.touches[ 1 ].pageY; 526 | var distance = Math.sqrt( dx * dx + dy * dy ); 527 | 528 | dollyEnd.set( 0, distance ); 529 | dollyDelta.subVectors( dollyEnd, dollyStart ); 530 | 531 | if ( dollyDelta.y > 0 ) { 532 | 533 | scope.dollyOut(); 534 | 535 | } else { 536 | 537 | scope.dollyIn(); 538 | 539 | } 540 | 541 | dollyStart.copy( dollyEnd ); 542 | break; 543 | 544 | case 3: // three-fingered touch: pan 545 | if ( scope.noPan === true ) { return; } 546 | if ( state !== STATE.TOUCH_PAN ) { return; } 547 | 548 | panEnd.set( event.touches[ 0 ].pageX, event.touches[ 0 ].pageY ); 549 | panDelta.subVectors( panEnd, panStart ); 550 | 551 | scope.pan( panDelta ); 552 | 553 | panStart.copy( panEnd ); 554 | break; 555 | 556 | default: 557 | state = STATE.NONE; 558 | 559 | } 560 | 561 | } 562 | 563 | function touchend( /* event */ ) { 564 | 565 | if ( scope.enabled === false ) { return; } 566 | 567 | state = STATE.NONE; 568 | } 569 | 570 | this.domElement.addEventListener( 'contextmenu', function ( event ) { event.preventDefault(); }, false ); 571 | this.domElement.addEventListener( 'mousedown', onMouseDown, false ); 572 | this.domElement.addEventListener( 'mousewheel', onMouseWheel, false ); 573 | this.domElement.addEventListener( 'DOMMouseScroll', onMouseWheel, false ); // firefox 574 | 575 | this.domElement.addEventListener( 'keydown', onKeyDown, false ); 576 | 577 | this.domElement.addEventListener( 'touchstart', touchstart, false ); 578 | this.domElement.addEventListener( 'touchend', touchend, false ); 579 | this.domElement.addEventListener( 'touchmove', touchmove, false ); 580 | 581 | }; 582 | 583 | THREE.OrbitControls.prototype = Object.create( THREE.EventDispatcher.prototype ); 584 | -------------------------------------------------------------------------------- /[9] Anaglyphic 3D/js/OrbitControls.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @author qiao / https://github.com/qiao 3 | * @author mrdoob / http://mrdoob.com 4 | * @author alteredq / http://alteredqualia.com/ 5 | * @author WestLangley / http://github.com/WestLangley 6 | * @author erich666 / http://erichaines.com 7 | */ 8 | /*global THREE, console */ 9 | 10 | // This set of controls performs orbiting, dollying (zooming), and panning. It maintains 11 | // the "up" direction as +Y, unlike the TrackballControls. Touch on tablet and phones is 12 | // supported. 13 | // 14 | // Orbit - left mouse / touch: one finger move 15 | // Zoom - middle mouse, or mousewheel / touch: two finger spread or squish 16 | // Pan - right mouse, or arrow keys / touch: three finter swipe 17 | // 18 | // This is a drop-in replacement for (most) TrackballControls used in examples. 19 | // That is, include this js file and wherever you see: 20 | // controls = new THREE.TrackballControls( camera ); 21 | // controls.target.z = 150; 22 | // Simple substitute "OrbitControls" and the control should work as-is. 23 | 24 | THREE.OrbitControls = function ( object, domElement ) { 25 | 26 | this.object = object; 27 | this.domElement = ( domElement !== undefined ) ? domElement : document; 28 | 29 | // API 30 | 31 | // Set to false to disable this control 32 | this.enabled = true; 33 | 34 | // "target" sets the location of focus, where the control orbits around 35 | // and where it pans with respect to. 36 | this.target = new THREE.Vector3(); 37 | // center is old, deprecated; use "target" instead 38 | this.center = this.target; 39 | 40 | // This option actually enables dollying in and out; left as "zoom" for 41 | // backwards compatibility 42 | this.noZoom = false; 43 | this.zoomSpeed = 1.0; 44 | // Limits to how far you can dolly in and out 45 | this.minDistance = 0; 46 | this.maxDistance = Infinity; 47 | 48 | // Set to true to disable this control 49 | this.noRotate = false; 50 | this.rotateSpeed = 1.0; 51 | 52 | // Set to true to disable this control 53 | this.noPan = false; 54 | this.keyPanSpeed = 7.0; // pixels moved per arrow key push 55 | 56 | // Set to true to automatically rotate around the target 57 | this.autoRotate = false; 58 | this.autoRotateSpeed = 2.0; // 30 seconds per round when fps is 60 59 | 60 | // How far you can orbit vertically, upper and lower limits. 61 | // Range is 0 to Math.PI radians. 62 | this.minPolarAngle = 0; // radians 63 | this.maxPolarAngle = Math.PI; // radians 64 | 65 | // Set to true to disable use of the keys 66 | this.noKeys = false; 67 | // The four arrow keys 68 | this.keys = { LEFT: 37, UP: 38, RIGHT: 39, BOTTOM: 40 }; 69 | 70 | //////////// 71 | // internals 72 | 73 | var scope = this; 74 | 75 | var EPS = 0.000001; 76 | 77 | var rotateStart = new THREE.Vector2(); 78 | var rotateEnd = new THREE.Vector2(); 79 | var rotateDelta = new THREE.Vector2(); 80 | 81 | var panStart = new THREE.Vector2(); 82 | var panEnd = new THREE.Vector2(); 83 | var panDelta = new THREE.Vector2(); 84 | 85 | var dollyStart = new THREE.Vector2(); 86 | var dollyEnd = new THREE.Vector2(); 87 | var dollyDelta = new THREE.Vector2(); 88 | 89 | var phiDelta = 0; 90 | var thetaDelta = 0; 91 | var scale = 1; 92 | var pan = new THREE.Vector3(); 93 | 94 | var lastPosition = new THREE.Vector3(); 95 | 96 | var STATE = { NONE : -1, ROTATE : 0, DOLLY : 1, PAN : 2, TOUCH_ROTATE : 3, TOUCH_DOLLY : 4, TOUCH_PAN : 5 }; 97 | var state = STATE.NONE; 98 | 99 | // events 100 | 101 | var changeEvent = { type: 'change' }; 102 | 103 | 104 | this.rotateLeft = function ( angle ) { 105 | 106 | if ( angle === undefined ) { 107 | 108 | angle = getAutoRotationAngle(); 109 | 110 | } 111 | 112 | thetaDelta -= angle; 113 | 114 | }; 115 | 116 | this.rotateUp = function ( angle ) { 117 | 118 | if ( angle === undefined ) { 119 | 120 | angle = getAutoRotationAngle(); 121 | 122 | } 123 | 124 | phiDelta -= angle; 125 | 126 | }; 127 | 128 | // pass in distance in world space to move left 129 | this.panLeft = function ( distance ) { 130 | 131 | var panOffset = new THREE.Vector3(); 132 | var te = this.object.matrix.elements; 133 | // get X column of matrix 134 | panOffset.set( te[0], te[1], te[2] ); 135 | panOffset.multiplyScalar(-distance); 136 | 137 | pan.add( panOffset ); 138 | 139 | }; 140 | 141 | // pass in distance in world space to move up 142 | this.panUp = function ( distance ) { 143 | 144 | var panOffset = new THREE.Vector3(); 145 | var te = this.object.matrix.elements; 146 | // get Y column of matrix 147 | panOffset.set( te[4], te[5], te[6] ); 148 | panOffset.multiplyScalar(distance); 149 | 150 | pan.add( panOffset ); 151 | }; 152 | 153 | // main entry point; pass in Vector2 of change desired in pixel space, 154 | // right and down are positive 155 | this.pan = function ( delta ) { 156 | 157 | var element = scope.domElement === document ? scope.domElement.body : scope.domElement; 158 | 159 | if ( scope.object.fov !== undefined ) { 160 | 161 | // perspective 162 | var position = scope.object.position; 163 | var offset = position.clone().sub( scope.target ); 164 | var targetDistance = offset.length(); 165 | 166 | // half of the fov is center to top of screen 167 | targetDistance *= Math.tan( (scope.object.fov/2) * Math.PI / 180.0 ); 168 | // we actually don't use screenWidth, since perspective camera is fixed to screen height 169 | scope.panLeft( 2 * delta.x * targetDistance / element.clientHeight ); 170 | scope.panUp( 2 * delta.y * targetDistance / element.clientHeight ); 171 | 172 | } else if ( scope.object.top !== undefined ) { 173 | 174 | // orthographic 175 | scope.panLeft( delta.x * (scope.object.right - scope.object.left) / element.clientWidth ); 176 | scope.panUp( delta.y * (scope.object.top - scope.object.bottom) / element.clientHeight ); 177 | 178 | } else { 179 | 180 | // camera neither orthographic or perspective - warn user 181 | console.warn( 'WARNING: OrbitControls.js encountered an unknown camera type - pan disabled.' ); 182 | 183 | } 184 | 185 | }; 186 | 187 | this.dollyIn = function ( dollyScale ) { 188 | 189 | if ( dollyScale === undefined ) { 190 | 191 | dollyScale = getZoomScale(); 192 | 193 | } 194 | 195 | scale /= dollyScale; 196 | 197 | }; 198 | 199 | this.dollyOut = function ( dollyScale ) { 200 | 201 | if ( dollyScale === undefined ) { 202 | 203 | dollyScale = getZoomScale(); 204 | 205 | } 206 | 207 | scale *= dollyScale; 208 | 209 | }; 210 | 211 | this.update = function () { 212 | 213 | var position = this.object.position; 214 | var offset = position.clone().sub( this.target ); 215 | 216 | // angle from z-axis around y-axis 217 | 218 | var theta = Math.atan2( offset.x, offset.z ); 219 | 220 | // angle from y-axis 221 | 222 | var phi = Math.atan2( Math.sqrt( offset.x * offset.x + offset.z * offset.z ), offset.y ); 223 | 224 | if ( this.autoRotate ) { 225 | 226 | this.rotateLeft( getAutoRotationAngle() ); 227 | 228 | } 229 | 230 | theta += thetaDelta; 231 | phi += phiDelta; 232 | 233 | // restrict phi to be between desired limits 234 | phi = Math.max( this.minPolarAngle, Math.min( this.maxPolarAngle, phi ) ); 235 | 236 | // restrict phi to be betwee EPS and PI-EPS 237 | phi = Math.max( EPS, Math.min( Math.PI - EPS, phi ) ); 238 | 239 | var radius = offset.length() * scale; 240 | 241 | // restrict radius to be between desired limits 242 | radius = Math.max( this.minDistance, Math.min( this.maxDistance, radius ) ); 243 | 244 | // move target to panned location 245 | this.target.add( pan ); 246 | 247 | offset.x = radius * Math.sin( phi ) * Math.sin( theta ); 248 | offset.y = radius * Math.cos( phi ); 249 | offset.z = radius * Math.sin( phi ) * Math.cos( theta ); 250 | 251 | position.copy( this.target ).add( offset ); 252 | 253 | this.object.lookAt( this.target ); 254 | 255 | thetaDelta = 0; 256 | phiDelta = 0; 257 | scale = 1; 258 | pan.set(0,0,0); 259 | 260 | if ( lastPosition.distanceTo( this.object.position ) > 0 ) { 261 | 262 | this.dispatchEvent( changeEvent ); 263 | 264 | lastPosition.copy( this.object.position ); 265 | 266 | } 267 | 268 | }; 269 | 270 | 271 | function getAutoRotationAngle() { 272 | 273 | return 2 * Math.PI / 60 / 60 * scope.autoRotateSpeed; 274 | 275 | } 276 | 277 | function getZoomScale() { 278 | 279 | return Math.pow( 0.95, scope.zoomSpeed ); 280 | 281 | } 282 | 283 | function onMouseDown( event ) { 284 | 285 | if ( scope.enabled === false ) { return; } 286 | event.preventDefault(); 287 | 288 | if ( event.button === 0 ) { 289 | if ( scope.noRotate === true ) { return; } 290 | 291 | state = STATE.ROTATE; 292 | 293 | rotateStart.set( event.clientX, event.clientY ); 294 | 295 | } else if ( event.button === 1 ) { 296 | if ( scope.noZoom === true ) { return; } 297 | 298 | state = STATE.DOLLY; 299 | 300 | dollyStart.set( event.clientX, event.clientY ); 301 | 302 | } else if ( event.button === 2 ) { 303 | if ( scope.noPan === true ) { return; } 304 | 305 | state = STATE.PAN; 306 | 307 | panStart.set( event.clientX, event.clientY ); 308 | 309 | } 310 | 311 | // Greggman fix: https://github.com/greggman/three.js/commit/fde9f9917d6d8381f06bf22cdff766029d1761be 312 | scope.domElement.addEventListener( 'mousemove', onMouseMove, false ); 313 | scope.domElement.addEventListener( 'mouseup', onMouseUp, false ); 314 | 315 | } 316 | 317 | function onMouseMove( event ) { 318 | 319 | if ( scope.enabled === false ) return; 320 | 321 | event.preventDefault(); 322 | 323 | var element = scope.domElement === document ? scope.domElement.body : scope.domElement; 324 | 325 | if ( state === STATE.ROTATE ) { 326 | 327 | if ( scope.noRotate === true ) return; 328 | 329 | rotateEnd.set( event.clientX, event.clientY ); 330 | rotateDelta.subVectors( rotateEnd, rotateStart ); 331 | 332 | // rotating across whole screen goes 360 degrees around 333 | scope.rotateLeft( 2 * Math.PI * rotateDelta.x / element.clientWidth * scope.rotateSpeed ); 334 | // rotating up and down along whole screen attempts to go 360, but limited to 180 335 | scope.rotateUp( 2 * Math.PI * rotateDelta.y / element.clientHeight * scope.rotateSpeed ); 336 | 337 | rotateStart.copy( rotateEnd ); 338 | 339 | } else if ( state === STATE.DOLLY ) { 340 | 341 | if ( scope.noZoom === true ) return; 342 | 343 | dollyEnd.set( event.clientX, event.clientY ); 344 | dollyDelta.subVectors( dollyEnd, dollyStart ); 345 | 346 | if ( dollyDelta.y > 0 ) { 347 | 348 | scope.dollyIn(); 349 | 350 | } else { 351 | 352 | scope.dollyOut(); 353 | 354 | } 355 | 356 | dollyStart.copy( dollyEnd ); 357 | 358 | } else if ( state === STATE.PAN ) { 359 | 360 | if ( scope.noPan === true ) return; 361 | 362 | panEnd.set( event.clientX, event.clientY ); 363 | panDelta.subVectors( panEnd, panStart ); 364 | 365 | scope.pan( panDelta ); 366 | 367 | panStart.copy( panEnd ); 368 | 369 | } 370 | 371 | // Greggman fix: https://github.com/greggman/three.js/commit/fde9f9917d6d8381f06bf22cdff766029d1761be 372 | scope.update(); 373 | 374 | } 375 | 376 | function onMouseUp( /* event */ ) { 377 | 378 | if ( scope.enabled === false ) return; 379 | 380 | // Greggman fix: https://github.com/greggman/three.js/commit/fde9f9917d6d8381f06bf22cdff766029d1761be 381 | scope.domElement.removeEventListener( 'mousemove', onMouseMove, false ); 382 | scope.domElement.removeEventListener( 'mouseup', onMouseUp, false ); 383 | 384 | state = STATE.NONE; 385 | 386 | } 387 | 388 | function onMouseWheel( event ) { 389 | 390 | if ( scope.enabled === false || scope.noZoom === true ) return; 391 | 392 | var delta = 0; 393 | 394 | if ( event.wheelDelta ) { // WebKit / Opera / Explorer 9 395 | 396 | delta = event.wheelDelta; 397 | 398 | } else if ( event.detail ) { // Firefox 399 | 400 | delta = - event.detail; 401 | 402 | } 403 | 404 | if ( delta > 0 ) { 405 | 406 | scope.dollyOut(); 407 | 408 | } else { 409 | 410 | scope.dollyIn(); 411 | 412 | } 413 | 414 | } 415 | 416 | function onKeyDown( event ) { 417 | 418 | if ( scope.enabled === false ) { return; } 419 | if ( scope.noKeys === true ) { return; } 420 | if ( scope.noPan === true ) { return; } 421 | 422 | // pan a pixel - I guess for precise positioning? 423 | // Greggman fix: https://github.com/greggman/three.js/commit/fde9f9917d6d8381f06bf22cdff766029d1761be 424 | var needUpdate = false; 425 | 426 | switch ( event.keyCode ) { 427 | 428 | case scope.keys.UP: 429 | scope.pan( new THREE.Vector2( 0, scope.keyPanSpeed ) ); 430 | needUpdate = true; 431 | break; 432 | case scope.keys.BOTTOM: 433 | scope.pan( new THREE.Vector2( 0, -scope.keyPanSpeed ) ); 434 | needUpdate = true; 435 | break; 436 | case scope.keys.LEFT: 437 | scope.pan( new THREE.Vector2( scope.keyPanSpeed, 0 ) ); 438 | needUpdate = true; 439 | break; 440 | case scope.keys.RIGHT: 441 | scope.pan( new THREE.Vector2( -scope.keyPanSpeed, 0 ) ); 442 | needUpdate = true; 443 | break; 444 | } 445 | 446 | // Greggman fix: https://github.com/greggman/three.js/commit/fde9f9917d6d8381f06bf22cdff766029d1761be 447 | if ( needUpdate ) { 448 | 449 | scope.update(); 450 | 451 | } 452 | 453 | } 454 | 455 | function touchstart( event ) { 456 | 457 | if ( scope.enabled === false ) { return; } 458 | 459 | switch ( event.touches.length ) { 460 | 461 | case 1: // one-fingered touch: rotate 462 | if ( scope.noRotate === true ) { return; } 463 | 464 | state = STATE.TOUCH_ROTATE; 465 | 466 | rotateStart.set( event.touches[ 0 ].pageX, event.touches[ 0 ].pageY ); 467 | break; 468 | 469 | case 2: // two-fingered touch: dolly 470 | if ( scope.noZoom === true ) { return; } 471 | 472 | state = STATE.TOUCH_DOLLY; 473 | 474 | var dx = event.touches[ 0 ].pageX - event.touches[ 1 ].pageX; 475 | var dy = event.touches[ 0 ].pageY - event.touches[ 1 ].pageY; 476 | var distance = Math.sqrt( dx * dx + dy * dy ); 477 | dollyStart.set( 0, distance ); 478 | break; 479 | 480 | case 3: // three-fingered touch: pan 481 | if ( scope.noPan === true ) { return; } 482 | 483 | state = STATE.TOUCH_PAN; 484 | 485 | panStart.set( event.touches[ 0 ].pageX, event.touches[ 0 ].pageY ); 486 | break; 487 | 488 | default: 489 | state = STATE.NONE; 490 | 491 | } 492 | } 493 | 494 | function touchmove( event ) { 495 | 496 | if ( scope.enabled === false ) { return; } 497 | 498 | event.preventDefault(); 499 | event.stopPropagation(); 500 | 501 | var element = scope.domElement === document ? scope.domElement.body : scope.domElement; 502 | 503 | switch ( event.touches.length ) { 504 | 505 | case 1: // one-fingered touch: rotate 506 | if ( scope.noRotate === true ) { return; } 507 | if ( state !== STATE.TOUCH_ROTATE ) { return; } 508 | 509 | rotateEnd.set( event.touches[ 0 ].pageX, event.touches[ 0 ].pageY ); 510 | rotateDelta.subVectors( rotateEnd, rotateStart ); 511 | 512 | // rotating across whole screen goes 360 degrees around 513 | scope.rotateLeft( 2 * Math.PI * rotateDelta.x / element.clientWidth * scope.rotateSpeed ); 514 | // rotating up and down along whole screen attempts to go 360, but limited to 180 515 | scope.rotateUp( 2 * Math.PI * rotateDelta.y / element.clientHeight * scope.rotateSpeed ); 516 | 517 | rotateStart.copy( rotateEnd ); 518 | break; 519 | 520 | case 2: // two-fingered touch: dolly 521 | if ( scope.noZoom === true ) { return; } 522 | if ( state !== STATE.TOUCH_DOLLY ) { return; } 523 | 524 | var dx = event.touches[ 0 ].pageX - event.touches[ 1 ].pageX; 525 | var dy = event.touches[ 0 ].pageY - event.touches[ 1 ].pageY; 526 | var distance = Math.sqrt( dx * dx + dy * dy ); 527 | 528 | dollyEnd.set( 0, distance ); 529 | dollyDelta.subVectors( dollyEnd, dollyStart ); 530 | 531 | if ( dollyDelta.y > 0 ) { 532 | 533 | scope.dollyOut(); 534 | 535 | } else { 536 | 537 | scope.dollyIn(); 538 | 539 | } 540 | 541 | dollyStart.copy( dollyEnd ); 542 | break; 543 | 544 | case 3: // three-fingered touch: pan 545 | if ( scope.noPan === true ) { return; } 546 | if ( state !== STATE.TOUCH_PAN ) { return; } 547 | 548 | panEnd.set( event.touches[ 0 ].pageX, event.touches[ 0 ].pageY ); 549 | panDelta.subVectors( panEnd, panStart ); 550 | 551 | scope.pan( panDelta ); 552 | 553 | panStart.copy( panEnd ); 554 | break; 555 | 556 | default: 557 | state = STATE.NONE; 558 | 559 | } 560 | 561 | } 562 | 563 | function touchend( /* event */ ) { 564 | 565 | if ( scope.enabled === false ) { return; } 566 | 567 | state = STATE.NONE; 568 | } 569 | 570 | this.domElement.addEventListener( 'contextmenu', function ( event ) { event.preventDefault(); }, false ); 571 | this.domElement.addEventListener( 'mousedown', onMouseDown, false ); 572 | this.domElement.addEventListener( 'mousewheel', onMouseWheel, false ); 573 | this.domElement.addEventListener( 'DOMMouseScroll', onMouseWheel, false ); // firefox 574 | 575 | this.domElement.addEventListener( 'keydown', onKeyDown, false ); 576 | 577 | this.domElement.addEventListener( 'touchstart', touchstart, false ); 578 | this.domElement.addEventListener( 'touchend', touchend, false ); 579 | this.domElement.addEventListener( 'touchmove', touchmove, false ); 580 | 581 | }; 582 | 583 | THREE.OrbitControls.prototype = Object.create( THREE.EventDispatcher.prototype ); 584 | -------------------------------------------------------------------------------- /[5] Textures:Colours/js/OrbitControls.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @author qiao / https://github.com/qiao 3 | * @author mrdoob / http://mrdoob.com 4 | * @author alteredq / http://alteredqualia.com/ 5 | * @author WestLangley / http://github.com/WestLangley 6 | * @author erich666 / http://erichaines.com 7 | */ 8 | /*global THREE, console */ 9 | 10 | // This set of controls performs orbiting, dollying (zooming), and panning. It maintains 11 | // the "up" direction as +Y, unlike the TrackballControls. Touch on tablet and phones is 12 | // supported. 13 | // 14 | // Orbit - left mouse / touch: one finger move 15 | // Zoom - middle mouse, or mousewheel / touch: two finger spread or squish 16 | // Pan - right mouse, or arrow keys / touch: three finter swipe 17 | // 18 | // This is a drop-in replacement for (most) TrackballControls used in examples. 19 | // That is, include this js file and wherever you see: 20 | // controls = new THREE.TrackballControls( camera ); 21 | // controls.target.z = 150; 22 | // Simple substitute "OrbitControls" and the control should work as-is. 23 | 24 | THREE.OrbitControls = function ( object, domElement ) { 25 | 26 | this.object = object; 27 | this.domElement = ( domElement !== undefined ) ? domElement : document; 28 | 29 | // API 30 | 31 | // Set to false to disable this control 32 | this.enabled = true; 33 | 34 | // "target" sets the location of focus, where the control orbits around 35 | // and where it pans with respect to. 36 | this.target = new THREE.Vector3(); 37 | // center is old, deprecated; use "target" instead 38 | this.center = this.target; 39 | 40 | // This option actually enables dollying in and out; left as "zoom" for 41 | // backwards compatibility 42 | this.noZoom = false; 43 | this.zoomSpeed = 1.0; 44 | // Limits to how far you can dolly in and out 45 | this.minDistance = 0; 46 | this.maxDistance = Infinity; 47 | 48 | // Set to true to disable this control 49 | this.noRotate = false; 50 | this.rotateSpeed = 1.0; 51 | 52 | // Set to true to disable this control 53 | this.noPan = false; 54 | this.keyPanSpeed = 7.0; // pixels moved per arrow key push 55 | 56 | // Set to true to automatically rotate around the target 57 | this.autoRotate = false; 58 | this.autoRotateSpeed = 2.0; // 30 seconds per round when fps is 60 59 | 60 | // How far you can orbit vertically, upper and lower limits. 61 | // Range is 0 to Math.PI radians. 62 | this.minPolarAngle = 0; // radians 63 | this.maxPolarAngle = Math.PI; // radians 64 | 65 | // Set to true to disable use of the keys 66 | this.noKeys = false; 67 | // The four arrow keys 68 | this.keys = { LEFT: 37, UP: 38, RIGHT: 39, BOTTOM: 40 }; 69 | 70 | //////////// 71 | // internals 72 | 73 | var scope = this; 74 | 75 | var EPS = 0.000001; 76 | 77 | var rotateStart = new THREE.Vector2(); 78 | var rotateEnd = new THREE.Vector2(); 79 | var rotateDelta = new THREE.Vector2(); 80 | 81 | var panStart = new THREE.Vector2(); 82 | var panEnd = new THREE.Vector2(); 83 | var panDelta = new THREE.Vector2(); 84 | 85 | var dollyStart = new THREE.Vector2(); 86 | var dollyEnd = new THREE.Vector2(); 87 | var dollyDelta = new THREE.Vector2(); 88 | 89 | var phiDelta = 0; 90 | var thetaDelta = 0; 91 | var scale = 1; 92 | var pan = new THREE.Vector3(); 93 | 94 | var lastPosition = new THREE.Vector3(); 95 | 96 | var STATE = { NONE : -1, ROTATE : 0, DOLLY : 1, PAN : 2, TOUCH_ROTATE : 3, TOUCH_DOLLY : 4, TOUCH_PAN : 5 }; 97 | var state = STATE.NONE; 98 | 99 | // events 100 | 101 | var changeEvent = { type: 'change' }; 102 | 103 | 104 | this.rotateLeft = function ( angle ) { 105 | 106 | if ( angle === undefined ) { 107 | 108 | angle = getAutoRotationAngle(); 109 | 110 | } 111 | 112 | thetaDelta -= angle; 113 | 114 | }; 115 | 116 | this.rotateUp = function ( angle ) { 117 | 118 | if ( angle === undefined ) { 119 | 120 | angle = getAutoRotationAngle(); 121 | 122 | } 123 | 124 | phiDelta -= angle; 125 | 126 | }; 127 | 128 | // pass in distance in world space to move left 129 | this.panLeft = function ( distance ) { 130 | 131 | var panOffset = new THREE.Vector3(); 132 | var te = this.object.matrix.elements; 133 | // get X column of matrix 134 | panOffset.set( te[0], te[1], te[2] ); 135 | panOffset.multiplyScalar(-distance); 136 | 137 | pan.add( panOffset ); 138 | 139 | }; 140 | 141 | // pass in distance in world space to move up 142 | this.panUp = function ( distance ) { 143 | 144 | var panOffset = new THREE.Vector3(); 145 | var te = this.object.matrix.elements; 146 | // get Y column of matrix 147 | panOffset.set( te[4], te[5], te[6] ); 148 | panOffset.multiplyScalar(distance); 149 | 150 | pan.add( panOffset ); 151 | }; 152 | 153 | // main entry point; pass in Vector2 of change desired in pixel space, 154 | // right and down are positive 155 | this.pan = function ( delta ) { 156 | 157 | var element = scope.domElement === document ? scope.domElement.body : scope.domElement; 158 | 159 | if ( scope.object.fov !== undefined ) { 160 | 161 | // perspective 162 | var position = scope.object.position; 163 | var offset = position.clone().sub( scope.target ); 164 | var targetDistance = offset.length(); 165 | 166 | // half of the fov is center to top of screen 167 | targetDistance *= Math.tan( (scope.object.fov/2) * Math.PI / 180.0 ); 168 | // we actually don't use screenWidth, since perspective camera is fixed to screen height 169 | scope.panLeft( 2 * delta.x * targetDistance / element.clientHeight ); 170 | scope.panUp( 2 * delta.y * targetDistance / element.clientHeight ); 171 | 172 | } else if ( scope.object.top !== undefined ) { 173 | 174 | // orthographic 175 | scope.panLeft( delta.x * (scope.object.right - scope.object.left) / element.clientWidth ); 176 | scope.panUp( delta.y * (scope.object.top - scope.object.bottom) / element.clientHeight ); 177 | 178 | } else { 179 | 180 | // camera neither orthographic or perspective - warn user 181 | console.warn( 'WARNING: OrbitControls.js encountered an unknown camera type - pan disabled.' ); 182 | 183 | } 184 | 185 | }; 186 | 187 | this.dollyIn = function ( dollyScale ) { 188 | 189 | if ( dollyScale === undefined ) { 190 | 191 | dollyScale = getZoomScale(); 192 | 193 | } 194 | 195 | scale /= dollyScale; 196 | 197 | }; 198 | 199 | this.dollyOut = function ( dollyScale ) { 200 | 201 | if ( dollyScale === undefined ) { 202 | 203 | dollyScale = getZoomScale(); 204 | 205 | } 206 | 207 | scale *= dollyScale; 208 | 209 | }; 210 | 211 | this.update = function () { 212 | 213 | var position = this.object.position; 214 | var offset = position.clone().sub( this.target ); 215 | 216 | // angle from z-axis around y-axis 217 | 218 | var theta = Math.atan2( offset.x, offset.z ); 219 | 220 | // angle from y-axis 221 | 222 | var phi = Math.atan2( Math.sqrt( offset.x * offset.x + offset.z * offset.z ), offset.y ); 223 | 224 | if ( this.autoRotate ) { 225 | 226 | this.rotateLeft( getAutoRotationAngle() ); 227 | 228 | } 229 | 230 | theta += thetaDelta; 231 | phi += phiDelta; 232 | 233 | // restrict phi to be between desired limits 234 | phi = Math.max( this.minPolarAngle, Math.min( this.maxPolarAngle, phi ) ); 235 | 236 | // restrict phi to be betwee EPS and PI-EPS 237 | phi = Math.max( EPS, Math.min( Math.PI - EPS, phi ) ); 238 | 239 | var radius = offset.length() * scale; 240 | 241 | // restrict radius to be between desired limits 242 | radius = Math.max( this.minDistance, Math.min( this.maxDistance, radius ) ); 243 | 244 | // move target to panned location 245 | this.target.add( pan ); 246 | 247 | offset.x = radius * Math.sin( phi ) * Math.sin( theta ); 248 | offset.y = radius * Math.cos( phi ); 249 | offset.z = radius * Math.sin( phi ) * Math.cos( theta ); 250 | 251 | position.copy( this.target ).add( offset ); 252 | 253 | this.object.lookAt( this.target ); 254 | 255 | thetaDelta = 0; 256 | phiDelta = 0; 257 | scale = 1; 258 | pan.set(0,0,0); 259 | 260 | if ( lastPosition.distanceTo( this.object.position ) > 0 ) { 261 | 262 | this.dispatchEvent( changeEvent ); 263 | 264 | lastPosition.copy( this.object.position ); 265 | 266 | } 267 | 268 | }; 269 | 270 | 271 | function getAutoRotationAngle() { 272 | 273 | return 2 * Math.PI / 60 / 60 * scope.autoRotateSpeed; 274 | 275 | } 276 | 277 | function getZoomScale() { 278 | 279 | return Math.pow( 0.95, scope.zoomSpeed ); 280 | 281 | } 282 | 283 | function onMouseDown( event ) { 284 | 285 | if ( scope.enabled === false ) { return; } 286 | event.preventDefault(); 287 | 288 | if ( event.button === 0 ) { 289 | if ( scope.noRotate === true ) { return; } 290 | 291 | state = STATE.ROTATE; 292 | 293 | rotateStart.set( event.clientX, event.clientY ); 294 | 295 | } else if ( event.button === 1 ) { 296 | if ( scope.noZoom === true ) { return; } 297 | 298 | state = STATE.DOLLY; 299 | 300 | dollyStart.set( event.clientX, event.clientY ); 301 | 302 | } else if ( event.button === 2 ) { 303 | if ( scope.noPan === true ) { return; } 304 | 305 | state = STATE.PAN; 306 | 307 | panStart.set( event.clientX, event.clientY ); 308 | 309 | } 310 | 311 | // Greggman fix: https://github.com/greggman/three.js/commit/fde9f9917d6d8381f06bf22cdff766029d1761be 312 | scope.domElement.addEventListener( 'mousemove', onMouseMove, false ); 313 | scope.domElement.addEventListener( 'mouseup', onMouseUp, false ); 314 | 315 | } 316 | 317 | function onMouseMove( event ) { 318 | 319 | if ( scope.enabled === false ) return; 320 | 321 | event.preventDefault(); 322 | 323 | var element = scope.domElement === document ? scope.domElement.body : scope.domElement; 324 | 325 | if ( state === STATE.ROTATE ) { 326 | 327 | if ( scope.noRotate === true ) return; 328 | 329 | rotateEnd.set( event.clientX, event.clientY ); 330 | rotateDelta.subVectors( rotateEnd, rotateStart ); 331 | 332 | // rotating across whole screen goes 360 degrees around 333 | scope.rotateLeft( 2 * Math.PI * rotateDelta.x / element.clientWidth * scope.rotateSpeed ); 334 | // rotating up and down along whole screen attempts to go 360, but limited to 180 335 | scope.rotateUp( 2 * Math.PI * rotateDelta.y / element.clientHeight * scope.rotateSpeed ); 336 | 337 | rotateStart.copy( rotateEnd ); 338 | 339 | } else if ( state === STATE.DOLLY ) { 340 | 341 | if ( scope.noZoom === true ) return; 342 | 343 | dollyEnd.set( event.clientX, event.clientY ); 344 | dollyDelta.subVectors( dollyEnd, dollyStart ); 345 | 346 | if ( dollyDelta.y > 0 ) { 347 | 348 | scope.dollyIn(); 349 | 350 | } else { 351 | 352 | scope.dollyOut(); 353 | 354 | } 355 | 356 | dollyStart.copy( dollyEnd ); 357 | 358 | } else if ( state === STATE.PAN ) { 359 | 360 | if ( scope.noPan === true ) return; 361 | 362 | panEnd.set( event.clientX, event.clientY ); 363 | panDelta.subVectors( panEnd, panStart ); 364 | 365 | scope.pan( panDelta ); 366 | 367 | panStart.copy( panEnd ); 368 | 369 | } 370 | 371 | // Greggman fix: https://github.com/greggman/three.js/commit/fde9f9917d6d8381f06bf22cdff766029d1761be 372 | scope.update(); 373 | 374 | } 375 | 376 | function onMouseUp( /* event */ ) { 377 | 378 | if ( scope.enabled === false ) return; 379 | 380 | // Greggman fix: https://github.com/greggman/three.js/commit/fde9f9917d6d8381f06bf22cdff766029d1761be 381 | scope.domElement.removeEventListener( 'mousemove', onMouseMove, false ); 382 | scope.domElement.removeEventListener( 'mouseup', onMouseUp, false ); 383 | 384 | state = STATE.NONE; 385 | 386 | } 387 | 388 | function onMouseWheel( event ) { 389 | 390 | if ( scope.enabled === false || scope.noZoom === true ) return; 391 | 392 | var delta = 0; 393 | 394 | if ( event.wheelDelta ) { // WebKit / Opera / Explorer 9 395 | 396 | delta = event.wheelDelta; 397 | 398 | } else if ( event.detail ) { // Firefox 399 | 400 | delta = - event.detail; 401 | 402 | } 403 | 404 | if ( delta > 0 ) { 405 | 406 | scope.dollyOut(); 407 | 408 | } else { 409 | 410 | scope.dollyIn(); 411 | 412 | } 413 | 414 | } 415 | 416 | function onKeyDown( event ) { 417 | 418 | if ( scope.enabled === false ) { return; } 419 | if ( scope.noKeys === true ) { return; } 420 | if ( scope.noPan === true ) { return; } 421 | 422 | // pan a pixel - I guess for precise positioning? 423 | // Greggman fix: https://github.com/greggman/three.js/commit/fde9f9917d6d8381f06bf22cdff766029d1761be 424 | var needUpdate = false; 425 | 426 | switch ( event.keyCode ) { 427 | 428 | case scope.keys.UP: 429 | scope.pan( new THREE.Vector2( 0, scope.keyPanSpeed ) ); 430 | needUpdate = true; 431 | break; 432 | case scope.keys.BOTTOM: 433 | scope.pan( new THREE.Vector2( 0, -scope.keyPanSpeed ) ); 434 | needUpdate = true; 435 | break; 436 | case scope.keys.LEFT: 437 | scope.pan( new THREE.Vector2( scope.keyPanSpeed, 0 ) ); 438 | needUpdate = true; 439 | break; 440 | case scope.keys.RIGHT: 441 | scope.pan( new THREE.Vector2( -scope.keyPanSpeed, 0 ) ); 442 | needUpdate = true; 443 | break; 444 | } 445 | 446 | // Greggman fix: https://github.com/greggman/three.js/commit/fde9f9917d6d8381f06bf22cdff766029d1761be 447 | if ( needUpdate ) { 448 | 449 | scope.update(); 450 | 451 | } 452 | 453 | } 454 | 455 | function touchstart( event ) { 456 | 457 | if ( scope.enabled === false ) { return; } 458 | 459 | switch ( event.touches.length ) { 460 | 461 | case 1: // one-fingered touch: rotate 462 | if ( scope.noRotate === true ) { return; } 463 | 464 | state = STATE.TOUCH_ROTATE; 465 | 466 | rotateStart.set( event.touches[ 0 ].pageX, event.touches[ 0 ].pageY ); 467 | break; 468 | 469 | case 2: // two-fingered touch: dolly 470 | if ( scope.noZoom === true ) { return; } 471 | 472 | state = STATE.TOUCH_DOLLY; 473 | 474 | var dx = event.touches[ 0 ].pageX - event.touches[ 1 ].pageX; 475 | var dy = event.touches[ 0 ].pageY - event.touches[ 1 ].pageY; 476 | var distance = Math.sqrt( dx * dx + dy * dy ); 477 | dollyStart.set( 0, distance ); 478 | break; 479 | 480 | case 3: // three-fingered touch: pan 481 | if ( scope.noPan === true ) { return; } 482 | 483 | state = STATE.TOUCH_PAN; 484 | 485 | panStart.set( event.touches[ 0 ].pageX, event.touches[ 0 ].pageY ); 486 | break; 487 | 488 | default: 489 | state = STATE.NONE; 490 | 491 | } 492 | } 493 | 494 | function touchmove( event ) { 495 | 496 | if ( scope.enabled === false ) { return; } 497 | 498 | event.preventDefault(); 499 | event.stopPropagation(); 500 | 501 | var element = scope.domElement === document ? scope.domElement.body : scope.domElement; 502 | 503 | switch ( event.touches.length ) { 504 | 505 | case 1: // one-fingered touch: rotate 506 | if ( scope.noRotate === true ) { return; } 507 | if ( state !== STATE.TOUCH_ROTATE ) { return; } 508 | 509 | rotateEnd.set( event.touches[ 0 ].pageX, event.touches[ 0 ].pageY ); 510 | rotateDelta.subVectors( rotateEnd, rotateStart ); 511 | 512 | // rotating across whole screen goes 360 degrees around 513 | scope.rotateLeft( 2 * Math.PI * rotateDelta.x / element.clientWidth * scope.rotateSpeed ); 514 | // rotating up and down along whole screen attempts to go 360, but limited to 180 515 | scope.rotateUp( 2 * Math.PI * rotateDelta.y / element.clientHeight * scope.rotateSpeed ); 516 | 517 | rotateStart.copy( rotateEnd ); 518 | break; 519 | 520 | case 2: // two-fingered touch: dolly 521 | if ( scope.noZoom === true ) { return; } 522 | if ( state !== STATE.TOUCH_DOLLY ) { return; } 523 | 524 | var dx = event.touches[ 0 ].pageX - event.touches[ 1 ].pageX; 525 | var dy = event.touches[ 0 ].pageY - event.touches[ 1 ].pageY; 526 | var distance = Math.sqrt( dx * dx + dy * dy ); 527 | 528 | dollyEnd.set( 0, distance ); 529 | dollyDelta.subVectors( dollyEnd, dollyStart ); 530 | 531 | if ( dollyDelta.y > 0 ) { 532 | 533 | scope.dollyOut(); 534 | 535 | } else { 536 | 537 | scope.dollyIn(); 538 | 539 | } 540 | 541 | dollyStart.copy( dollyEnd ); 542 | break; 543 | 544 | case 3: // three-fingered touch: pan 545 | if ( scope.noPan === true ) { return; } 546 | if ( state !== STATE.TOUCH_PAN ) { return; } 547 | 548 | panEnd.set( event.touches[ 0 ].pageX, event.touches[ 0 ].pageY ); 549 | panDelta.subVectors( panEnd, panStart ); 550 | 551 | scope.pan( panDelta ); 552 | 553 | panStart.copy( panEnd ); 554 | break; 555 | 556 | default: 557 | state = STATE.NONE; 558 | 559 | } 560 | 561 | } 562 | 563 | function touchend( /* event */ ) { 564 | 565 | if ( scope.enabled === false ) { return; } 566 | 567 | state = STATE.NONE; 568 | } 569 | 570 | this.domElement.addEventListener( 'contextmenu', function ( event ) { event.preventDefault(); }, false ); 571 | this.domElement.addEventListener( 'mousedown', onMouseDown, false ); 572 | this.domElement.addEventListener( 'mousewheel', onMouseWheel, false ); 573 | this.domElement.addEventListener( 'DOMMouseScroll', onMouseWheel, false ); // firefox 574 | 575 | this.domElement.addEventListener( 'keydown', onKeyDown, false ); 576 | 577 | this.domElement.addEventListener( 'touchstart', touchstart, false ); 578 | this.domElement.addEventListener( 'touchend', touchend, false ); 579 | this.domElement.addEventListener( 'touchmove', touchmove, false ); 580 | 581 | }; 582 | 583 | THREE.OrbitControls.prototype = Object.create( THREE.EventDispatcher.prototype ); 584 | -------------------------------------------------------------------------------- /[8] FPS (Frames Per Second:Framerate)/js/OrbitControls.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @author qiao / https://github.com/qiao 3 | * @author mrdoob / http://mrdoob.com 4 | * @author alteredq / http://alteredqualia.com/ 5 | * @author WestLangley / http://github.com/WestLangley 6 | * @author erich666 / http://erichaines.com 7 | */ 8 | /*global THREE, console */ 9 | 10 | // This set of controls performs orbiting, dollying (zooming), and panning. It maintains 11 | // the "up" direction as +Y, unlike the TrackballControls. Touch on tablet and phones is 12 | // supported. 13 | // 14 | // Orbit - left mouse / touch: one finger move 15 | // Zoom - middle mouse, or mousewheel / touch: two finger spread or squish 16 | // Pan - right mouse, or arrow keys / touch: three finter swipe 17 | // 18 | // This is a drop-in replacement for (most) TrackballControls used in examples. 19 | // That is, include this js file and wherever you see: 20 | // controls = new THREE.TrackballControls( camera ); 21 | // controls.target.z = 150; 22 | // Simple substitute "OrbitControls" and the control should work as-is. 23 | 24 | THREE.OrbitControls = function ( object, domElement ) { 25 | 26 | this.object = object; 27 | this.domElement = ( domElement !== undefined ) ? domElement : document; 28 | 29 | // API 30 | 31 | // Set to false to disable this control 32 | this.enabled = true; 33 | 34 | // "target" sets the location of focus, where the control orbits around 35 | // and where it pans with respect to. 36 | this.target = new THREE.Vector3(); 37 | // center is old, deprecated; use "target" instead 38 | this.center = this.target; 39 | 40 | // This option actually enables dollying in and out; left as "zoom" for 41 | // backwards compatibility 42 | this.noZoom = false; 43 | this.zoomSpeed = 1.0; 44 | // Limits to how far you can dolly in and out 45 | this.minDistance = 0; 46 | this.maxDistance = Infinity; 47 | 48 | // Set to true to disable this control 49 | this.noRotate = false; 50 | this.rotateSpeed = 1.0; 51 | 52 | // Set to true to disable this control 53 | this.noPan = false; 54 | this.keyPanSpeed = 7.0; // pixels moved per arrow key push 55 | 56 | // Set to true to automatically rotate around the target 57 | this.autoRotate = false; 58 | this.autoRotateSpeed = 2.0; // 30 seconds per round when fps is 60 59 | 60 | // How far you can orbit vertically, upper and lower limits. 61 | // Range is 0 to Math.PI radians. 62 | this.minPolarAngle = 0; // radians 63 | this.maxPolarAngle = Math.PI; // radians 64 | 65 | // Set to true to disable use of the keys 66 | this.noKeys = false; 67 | // The four arrow keys 68 | this.keys = { LEFT: 37, UP: 38, RIGHT: 39, BOTTOM: 40 }; 69 | 70 | //////////// 71 | // internals 72 | 73 | var scope = this; 74 | 75 | var EPS = 0.000001; 76 | 77 | var rotateStart = new THREE.Vector2(); 78 | var rotateEnd = new THREE.Vector2(); 79 | var rotateDelta = new THREE.Vector2(); 80 | 81 | var panStart = new THREE.Vector2(); 82 | var panEnd = new THREE.Vector2(); 83 | var panDelta = new THREE.Vector2(); 84 | 85 | var dollyStart = new THREE.Vector2(); 86 | var dollyEnd = new THREE.Vector2(); 87 | var dollyDelta = new THREE.Vector2(); 88 | 89 | var phiDelta = 0; 90 | var thetaDelta = 0; 91 | var scale = 1; 92 | var pan = new THREE.Vector3(); 93 | 94 | var lastPosition = new THREE.Vector3(); 95 | 96 | var STATE = { NONE : -1, ROTATE : 0, DOLLY : 1, PAN : 2, TOUCH_ROTATE : 3, TOUCH_DOLLY : 4, TOUCH_PAN : 5 }; 97 | var state = STATE.NONE; 98 | 99 | // events 100 | 101 | var changeEvent = { type: 'change' }; 102 | 103 | 104 | this.rotateLeft = function ( angle ) { 105 | 106 | if ( angle === undefined ) { 107 | 108 | angle = getAutoRotationAngle(); 109 | 110 | } 111 | 112 | thetaDelta -= angle; 113 | 114 | }; 115 | 116 | this.rotateUp = function ( angle ) { 117 | 118 | if ( angle === undefined ) { 119 | 120 | angle = getAutoRotationAngle(); 121 | 122 | } 123 | 124 | phiDelta -= angle; 125 | 126 | }; 127 | 128 | // pass in distance in world space to move left 129 | this.panLeft = function ( distance ) { 130 | 131 | var panOffset = new THREE.Vector3(); 132 | var te = this.object.matrix.elements; 133 | // get X column of matrix 134 | panOffset.set( te[0], te[1], te[2] ); 135 | panOffset.multiplyScalar(-distance); 136 | 137 | pan.add( panOffset ); 138 | 139 | }; 140 | 141 | // pass in distance in world space to move up 142 | this.panUp = function ( distance ) { 143 | 144 | var panOffset = new THREE.Vector3(); 145 | var te = this.object.matrix.elements; 146 | // get Y column of matrix 147 | panOffset.set( te[4], te[5], te[6] ); 148 | panOffset.multiplyScalar(distance); 149 | 150 | pan.add( panOffset ); 151 | }; 152 | 153 | // main entry point; pass in Vector2 of change desired in pixel space, 154 | // right and down are positive 155 | this.pan = function ( delta ) { 156 | 157 | var element = scope.domElement === document ? scope.domElement.body : scope.domElement; 158 | 159 | if ( scope.object.fov !== undefined ) { 160 | 161 | // perspective 162 | var position = scope.object.position; 163 | var offset = position.clone().sub( scope.target ); 164 | var targetDistance = offset.length(); 165 | 166 | // half of the fov is center to top of screen 167 | targetDistance *= Math.tan( (scope.object.fov/2) * Math.PI / 180.0 ); 168 | // we actually don't use screenWidth, since perspective camera is fixed to screen height 169 | scope.panLeft( 2 * delta.x * targetDistance / element.clientHeight ); 170 | scope.panUp( 2 * delta.y * targetDistance / element.clientHeight ); 171 | 172 | } else if ( scope.object.top !== undefined ) { 173 | 174 | // orthographic 175 | scope.panLeft( delta.x * (scope.object.right - scope.object.left) / element.clientWidth ); 176 | scope.panUp( delta.y * (scope.object.top - scope.object.bottom) / element.clientHeight ); 177 | 178 | } else { 179 | 180 | // camera neither orthographic or perspective - warn user 181 | console.warn( 'WARNING: OrbitControls.js encountered an unknown camera type - pan disabled.' ); 182 | 183 | } 184 | 185 | }; 186 | 187 | this.dollyIn = function ( dollyScale ) { 188 | 189 | if ( dollyScale === undefined ) { 190 | 191 | dollyScale = getZoomScale(); 192 | 193 | } 194 | 195 | scale /= dollyScale; 196 | 197 | }; 198 | 199 | this.dollyOut = function ( dollyScale ) { 200 | 201 | if ( dollyScale === undefined ) { 202 | 203 | dollyScale = getZoomScale(); 204 | 205 | } 206 | 207 | scale *= dollyScale; 208 | 209 | }; 210 | 211 | this.update = function () { 212 | 213 | var position = this.object.position; 214 | var offset = position.clone().sub( this.target ); 215 | 216 | // angle from z-axis around y-axis 217 | 218 | var theta = Math.atan2( offset.x, offset.z ); 219 | 220 | // angle from y-axis 221 | 222 | var phi = Math.atan2( Math.sqrt( offset.x * offset.x + offset.z * offset.z ), offset.y ); 223 | 224 | if ( this.autoRotate ) { 225 | 226 | this.rotateLeft( getAutoRotationAngle() ); 227 | 228 | } 229 | 230 | theta += thetaDelta; 231 | phi += phiDelta; 232 | 233 | // restrict phi to be between desired limits 234 | phi = Math.max( this.minPolarAngle, Math.min( this.maxPolarAngle, phi ) ); 235 | 236 | // restrict phi to be betwee EPS and PI-EPS 237 | phi = Math.max( EPS, Math.min( Math.PI - EPS, phi ) ); 238 | 239 | var radius = offset.length() * scale; 240 | 241 | // restrict radius to be between desired limits 242 | radius = Math.max( this.minDistance, Math.min( this.maxDistance, radius ) ); 243 | 244 | // move target to panned location 245 | this.target.add( pan ); 246 | 247 | offset.x = radius * Math.sin( phi ) * Math.sin( theta ); 248 | offset.y = radius * Math.cos( phi ); 249 | offset.z = radius * Math.sin( phi ) * Math.cos( theta ); 250 | 251 | position.copy( this.target ).add( offset ); 252 | 253 | this.object.lookAt( this.target ); 254 | 255 | thetaDelta = 0; 256 | phiDelta = 0; 257 | scale = 1; 258 | pan.set(0,0,0); 259 | 260 | if ( lastPosition.distanceTo( this.object.position ) > 0 ) { 261 | 262 | this.dispatchEvent( changeEvent ); 263 | 264 | lastPosition.copy( this.object.position ); 265 | 266 | } 267 | 268 | }; 269 | 270 | 271 | function getAutoRotationAngle() { 272 | 273 | return 2 * Math.PI / 60 / 60 * scope.autoRotateSpeed; 274 | 275 | } 276 | 277 | function getZoomScale() { 278 | 279 | return Math.pow( 0.95, scope.zoomSpeed ); 280 | 281 | } 282 | 283 | function onMouseDown( event ) { 284 | 285 | if ( scope.enabled === false ) { return; } 286 | event.preventDefault(); 287 | 288 | if ( event.button === 0 ) { 289 | if ( scope.noRotate === true ) { return; } 290 | 291 | state = STATE.ROTATE; 292 | 293 | rotateStart.set( event.clientX, event.clientY ); 294 | 295 | } else if ( event.button === 1 ) { 296 | if ( scope.noZoom === true ) { return; } 297 | 298 | state = STATE.DOLLY; 299 | 300 | dollyStart.set( event.clientX, event.clientY ); 301 | 302 | } else if ( event.button === 2 ) { 303 | if ( scope.noPan === true ) { return; } 304 | 305 | state = STATE.PAN; 306 | 307 | panStart.set( event.clientX, event.clientY ); 308 | 309 | } 310 | 311 | // Greggman fix: https://github.com/greggman/three.js/commit/fde9f9917d6d8381f06bf22cdff766029d1761be 312 | scope.domElement.addEventListener( 'mousemove', onMouseMove, false ); 313 | scope.domElement.addEventListener( 'mouseup', onMouseUp, false ); 314 | 315 | } 316 | 317 | function onMouseMove( event ) { 318 | 319 | if ( scope.enabled === false ) return; 320 | 321 | event.preventDefault(); 322 | 323 | var element = scope.domElement === document ? scope.domElement.body : scope.domElement; 324 | 325 | if ( state === STATE.ROTATE ) { 326 | 327 | if ( scope.noRotate === true ) return; 328 | 329 | rotateEnd.set( event.clientX, event.clientY ); 330 | rotateDelta.subVectors( rotateEnd, rotateStart ); 331 | 332 | // rotating across whole screen goes 360 degrees around 333 | scope.rotateLeft( 2 * Math.PI * rotateDelta.x / element.clientWidth * scope.rotateSpeed ); 334 | // rotating up and down along whole screen attempts to go 360, but limited to 180 335 | scope.rotateUp( 2 * Math.PI * rotateDelta.y / element.clientHeight * scope.rotateSpeed ); 336 | 337 | rotateStart.copy( rotateEnd ); 338 | 339 | } else if ( state === STATE.DOLLY ) { 340 | 341 | if ( scope.noZoom === true ) return; 342 | 343 | dollyEnd.set( event.clientX, event.clientY ); 344 | dollyDelta.subVectors( dollyEnd, dollyStart ); 345 | 346 | if ( dollyDelta.y > 0 ) { 347 | 348 | scope.dollyIn(); 349 | 350 | } else { 351 | 352 | scope.dollyOut(); 353 | 354 | } 355 | 356 | dollyStart.copy( dollyEnd ); 357 | 358 | } else if ( state === STATE.PAN ) { 359 | 360 | if ( scope.noPan === true ) return; 361 | 362 | panEnd.set( event.clientX, event.clientY ); 363 | panDelta.subVectors( panEnd, panStart ); 364 | 365 | scope.pan( panDelta ); 366 | 367 | panStart.copy( panEnd ); 368 | 369 | } 370 | 371 | // Greggman fix: https://github.com/greggman/three.js/commit/fde9f9917d6d8381f06bf22cdff766029d1761be 372 | scope.update(); 373 | 374 | } 375 | 376 | function onMouseUp( /* event */ ) { 377 | 378 | if ( scope.enabled === false ) return; 379 | 380 | // Greggman fix: https://github.com/greggman/three.js/commit/fde9f9917d6d8381f06bf22cdff766029d1761be 381 | scope.domElement.removeEventListener( 'mousemove', onMouseMove, false ); 382 | scope.domElement.removeEventListener( 'mouseup', onMouseUp, false ); 383 | 384 | state = STATE.NONE; 385 | 386 | } 387 | 388 | function onMouseWheel( event ) { 389 | 390 | if ( scope.enabled === false || scope.noZoom === true ) return; 391 | 392 | var delta = 0; 393 | 394 | if ( event.wheelDelta ) { // WebKit / Opera / Explorer 9 395 | 396 | delta = event.wheelDelta; 397 | 398 | } else if ( event.detail ) { // Firefox 399 | 400 | delta = - event.detail; 401 | 402 | } 403 | 404 | if ( delta > 0 ) { 405 | 406 | scope.dollyOut(); 407 | 408 | } else { 409 | 410 | scope.dollyIn(); 411 | 412 | } 413 | 414 | } 415 | 416 | function onKeyDown( event ) { 417 | 418 | if ( scope.enabled === false ) { return; } 419 | if ( scope.noKeys === true ) { return; } 420 | if ( scope.noPan === true ) { return; } 421 | 422 | // pan a pixel - I guess for precise positioning? 423 | // Greggman fix: https://github.com/greggman/three.js/commit/fde9f9917d6d8381f06bf22cdff766029d1761be 424 | var needUpdate = false; 425 | 426 | switch ( event.keyCode ) { 427 | 428 | case scope.keys.UP: 429 | scope.pan( new THREE.Vector2( 0, scope.keyPanSpeed ) ); 430 | needUpdate = true; 431 | break; 432 | case scope.keys.BOTTOM: 433 | scope.pan( new THREE.Vector2( 0, -scope.keyPanSpeed ) ); 434 | needUpdate = true; 435 | break; 436 | case scope.keys.LEFT: 437 | scope.pan( new THREE.Vector2( scope.keyPanSpeed, 0 ) ); 438 | needUpdate = true; 439 | break; 440 | case scope.keys.RIGHT: 441 | scope.pan( new THREE.Vector2( -scope.keyPanSpeed, 0 ) ); 442 | needUpdate = true; 443 | break; 444 | } 445 | 446 | // Greggman fix: https://github.com/greggman/three.js/commit/fde9f9917d6d8381f06bf22cdff766029d1761be 447 | if ( needUpdate ) { 448 | 449 | scope.update(); 450 | 451 | } 452 | 453 | } 454 | 455 | function touchstart( event ) { 456 | 457 | if ( scope.enabled === false ) { return; } 458 | 459 | switch ( event.touches.length ) { 460 | 461 | case 1: // one-fingered touch: rotate 462 | if ( scope.noRotate === true ) { return; } 463 | 464 | state = STATE.TOUCH_ROTATE; 465 | 466 | rotateStart.set( event.touches[ 0 ].pageX, event.touches[ 0 ].pageY ); 467 | break; 468 | 469 | case 2: // two-fingered touch: dolly 470 | if ( scope.noZoom === true ) { return; } 471 | 472 | state = STATE.TOUCH_DOLLY; 473 | 474 | var dx = event.touches[ 0 ].pageX - event.touches[ 1 ].pageX; 475 | var dy = event.touches[ 0 ].pageY - event.touches[ 1 ].pageY; 476 | var distance = Math.sqrt( dx * dx + dy * dy ); 477 | dollyStart.set( 0, distance ); 478 | break; 479 | 480 | case 3: // three-fingered touch: pan 481 | if ( scope.noPan === true ) { return; } 482 | 483 | state = STATE.TOUCH_PAN; 484 | 485 | panStart.set( event.touches[ 0 ].pageX, event.touches[ 0 ].pageY ); 486 | break; 487 | 488 | default: 489 | state = STATE.NONE; 490 | 491 | } 492 | } 493 | 494 | function touchmove( event ) { 495 | 496 | if ( scope.enabled === false ) { return; } 497 | 498 | event.preventDefault(); 499 | event.stopPropagation(); 500 | 501 | var element = scope.domElement === document ? scope.domElement.body : scope.domElement; 502 | 503 | switch ( event.touches.length ) { 504 | 505 | case 1: // one-fingered touch: rotate 506 | if ( scope.noRotate === true ) { return; } 507 | if ( state !== STATE.TOUCH_ROTATE ) { return; } 508 | 509 | rotateEnd.set( event.touches[ 0 ].pageX, event.touches[ 0 ].pageY ); 510 | rotateDelta.subVectors( rotateEnd, rotateStart ); 511 | 512 | // rotating across whole screen goes 360 degrees around 513 | scope.rotateLeft( 2 * Math.PI * rotateDelta.x / element.clientWidth * scope.rotateSpeed ); 514 | // rotating up and down along whole screen attempts to go 360, but limited to 180 515 | scope.rotateUp( 2 * Math.PI * rotateDelta.y / element.clientHeight * scope.rotateSpeed ); 516 | 517 | rotateStart.copy( rotateEnd ); 518 | break; 519 | 520 | case 2: // two-fingered touch: dolly 521 | if ( scope.noZoom === true ) { return; } 522 | if ( state !== STATE.TOUCH_DOLLY ) { return; } 523 | 524 | var dx = event.touches[ 0 ].pageX - event.touches[ 1 ].pageX; 525 | var dy = event.touches[ 0 ].pageY - event.touches[ 1 ].pageY; 526 | var distance = Math.sqrt( dx * dx + dy * dy ); 527 | 528 | dollyEnd.set( 0, distance ); 529 | dollyDelta.subVectors( dollyEnd, dollyStart ); 530 | 531 | if ( dollyDelta.y > 0 ) { 532 | 533 | scope.dollyOut(); 534 | 535 | } else { 536 | 537 | scope.dollyIn(); 538 | 539 | } 540 | 541 | dollyStart.copy( dollyEnd ); 542 | break; 543 | 544 | case 3: // three-fingered touch: pan 545 | if ( scope.noPan === true ) { return; } 546 | if ( state !== STATE.TOUCH_PAN ) { return; } 547 | 548 | panEnd.set( event.touches[ 0 ].pageX, event.touches[ 0 ].pageY ); 549 | panDelta.subVectors( panEnd, panStart ); 550 | 551 | scope.pan( panDelta ); 552 | 553 | panStart.copy( panEnd ); 554 | break; 555 | 556 | default: 557 | state = STATE.NONE; 558 | 559 | } 560 | 561 | } 562 | 563 | function touchend( /* event */ ) { 564 | 565 | if ( scope.enabled === false ) { return; } 566 | 567 | state = STATE.NONE; 568 | } 569 | 570 | this.domElement.addEventListener( 'contextmenu', function ( event ) { event.preventDefault(); }, false ); 571 | this.domElement.addEventListener( 'mousedown', onMouseDown, false ); 572 | this.domElement.addEventListener( 'mousewheel', onMouseWheel, false ); 573 | this.domElement.addEventListener( 'DOMMouseScroll', onMouseWheel, false ); // firefox 574 | 575 | this.domElement.addEventListener( 'keydown', onKeyDown, false ); 576 | 577 | this.domElement.addEventListener( 'touchstart', touchstart, false ); 578 | this.domElement.addEventListener( 'touchend', touchend, false ); 579 | this.domElement.addEventListener( 'touchmove', touchmove, false ); 580 | 581 | }; 582 | 583 | THREE.OrbitControls.prototype = Object.create( THREE.EventDispatcher.prototype ); 584 | --------------------------------------------------------------------------------