├── [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 | - Setup
6 | - Draw Geometry
7 | - Update Viewport On Resize
8 | - Controls
9 | - Textures/Colours
10 | - Lighting
11 | - Model Loading
12 | - FPS
13 | - Anaglyphic 3D
14 | - Skybox
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 |
--------------------------------------------------------------------------------