├── .gitignore
├── img
├── mesh.jpg
├── basic.jpg
├── image.jpg
├── noise.jpg
├── animation.jpg
├── morph_0.jpg
├── morph_1.jpg
└── morph_2.jpg
├── model
├── bust.bin
└── bust.js
├── glsl
├── basic
│ ├── render_fs.glsl
│ ├── simulation_vs.glsl
│ ├── simulation_fs.glsl
│ └── render_vs.glsl
├── image
│ ├── render_fs.glsl
│ ├── simulation_vs.glsl
│ ├── simulation_fs.glsl
│ └── render_vs.glsl
├── morph
│ ├── render_fs.glsl
│ ├── simulation_vs.glsl
│ ├── simulation_fs.glsl
│ └── render_vs.glsl
├── mesh
│ ├── simulation_vs.glsl
│ ├── simulation_fs.glsl
│ ├── render_fs.glsl
│ └── render_vs.glsl
└── noise
│ ├── simulation_vs.glsl
│ ├── render_fs.glsl
│ ├── render_vs.glsl
│ └── simulation_fs.glsl
├── README.md
├── ShaderLoader.js
├── fbo.js
├── basic.html
├── mesh.html
├── image.html
├── noise.html
├── morph.html
└── vendor
├── BinaryLoader.js
└── OrbitControls.js
/.gitignore:
--------------------------------------------------------------------------------
1 | /img/compo.psd
2 |
3 | .idea/
4 | /convert
5 |
--------------------------------------------------------------------------------
/img/mesh.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nicoptere/FBO/HEAD/img/mesh.jpg
--------------------------------------------------------------------------------
/img/basic.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nicoptere/FBO/HEAD/img/basic.jpg
--------------------------------------------------------------------------------
/img/image.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nicoptere/FBO/HEAD/img/image.jpg
--------------------------------------------------------------------------------
/img/noise.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nicoptere/FBO/HEAD/img/noise.jpg
--------------------------------------------------------------------------------
/model/bust.bin:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nicoptere/FBO/HEAD/model/bust.bin
--------------------------------------------------------------------------------
/img/animation.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nicoptere/FBO/HEAD/img/animation.jpg
--------------------------------------------------------------------------------
/img/morph_0.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nicoptere/FBO/HEAD/img/morph_0.jpg
--------------------------------------------------------------------------------
/img/morph_1.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nicoptere/FBO/HEAD/img/morph_1.jpg
--------------------------------------------------------------------------------
/img/morph_2.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nicoptere/FBO/HEAD/img/morph_2.jpg
--------------------------------------------------------------------------------
/glsl/basic/render_fs.glsl:
--------------------------------------------------------------------------------
1 | void main()
2 | {
3 | gl_FragColor = vec4( vec3( 1. ), .25 );
4 | }
--------------------------------------------------------------------------------
/glsl/image/render_fs.glsl:
--------------------------------------------------------------------------------
1 | void main()
2 | {
3 | gl_FragColor = vec4( vec3( .5 ), 1. );
4 | }
--------------------------------------------------------------------------------
/glsl/morph/render_fs.glsl:
--------------------------------------------------------------------------------
1 | void main()
2 | {
3 | gl_FragColor = vec4( vec3( 1. ), .25 );
4 | }
--------------------------------------------------------------------------------
/glsl/mesh/simulation_vs.glsl:
--------------------------------------------------------------------------------
1 | varying vec2 vUv;
2 | void main() {
3 | vUv = vec2(uv.x, uv.y);
4 | gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );
5 | }
--------------------------------------------------------------------------------
/glsl/basic/simulation_vs.glsl:
--------------------------------------------------------------------------------
1 | varying vec2 vUv;
2 | void main() {
3 | vUv = vec2(uv.x, uv.y);
4 | gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );
5 | }
--------------------------------------------------------------------------------
/glsl/image/simulation_vs.glsl:
--------------------------------------------------------------------------------
1 | varying vec2 vUv;
2 | void main() {
3 | vUv = vec2(uv.x, uv.y);
4 | gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );
5 | }
--------------------------------------------------------------------------------
/glsl/morph/simulation_vs.glsl:
--------------------------------------------------------------------------------
1 | varying vec2 vUv;
2 | void main() {
3 | vUv = vec2(uv.x, uv.y);
4 | gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );
5 | }
--------------------------------------------------------------------------------
/glsl/noise/simulation_vs.glsl:
--------------------------------------------------------------------------------
1 | varying vec2 vUv;
2 | varying float fragDepth;
3 | void main() {
4 | vUv = vec2(uv.x, uv.y);
5 | gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );
6 | }
--------------------------------------------------------------------------------
/glsl/basic/simulation_fs.glsl:
--------------------------------------------------------------------------------
1 | //basic simulation: displays the particles in place.
2 | uniform sampler2D positions;
3 | varying vec2 vUv;
4 | void main() {
5 |
6 | vec3 pos = texture2D( positions, vUv ).rgb;
7 | gl_FragColor = vec4( pos,1.0 );
8 | }
--------------------------------------------------------------------------------
/glsl/image/simulation_fs.glsl:
--------------------------------------------------------------------------------
1 | //basic simulation: displays the particles in place.
2 | uniform sampler2D positions;
3 | varying vec2 vUv;
4 | void main() {
5 |
6 | vec3 pos = texture2D( positions, vUv ).rgb;
7 | gl_FragColor = vec4( pos,1.0 );
8 | }
--------------------------------------------------------------------------------
/glsl/mesh/simulation_fs.glsl:
--------------------------------------------------------------------------------
1 | //basic simulation: displays the particles in place.
2 | uniform sampler2D positions;
3 | varying vec2 vUv;
4 | void main() {
5 |
6 | vec3 pos = texture2D( positions, vUv ).rgb;
7 | gl_FragColor = vec4( pos,1.0 );
8 | }
--------------------------------------------------------------------------------
/glsl/noise/render_fs.glsl:
--------------------------------------------------------------------------------
1 | uniform vec2 nearFar;
2 | uniform vec3 small;
3 | uniform vec3 big;
4 |
5 | varying float size;
6 | void main()
7 | {
8 |
9 |
10 |
11 | gl_FragColor = vec4( small, .2 );
12 |
13 | if( size > 1. )
14 | {
15 | gl_FragColor = vec4( big * vec3( 1. - length( gl_PointCoord.xy-vec2(.5) ) ) * 1.5, .95 );
16 | }
17 |
18 | }
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # FBO
2 | using FBO to render particles in THREE.js
3 | [longer article](http://barradeau.com/blog/?p=621)
4 |
5 |
6 | ## UPDATE 210525
7 |
8 | [Mario Carrillo](https://twitter.com/marioecg) was kind enough to port the code samples to ES6, something I’ve been willing to do for years.
9 | so check out his repo: https://github.com/marioecg/gpu-party/ ( and check out his work while you’re at it )
10 |
--------------------------------------------------------------------------------
/glsl/morph/simulation_fs.glsl:
--------------------------------------------------------------------------------
1 |
2 | // simulation
3 | uniform sampler2D textureA;
4 | uniform sampler2D textureB;
5 | uniform float timer;
6 |
7 | varying vec2 vUv;
8 | void main() {
9 |
10 | //origin
11 | vec3 origin = texture2D( textureA, vUv ).xyz;
12 |
13 | //destination
14 | vec3 destination = texture2D( textureB, vUv ).xyz;
15 |
16 | //lerp
17 | vec3 pos = mix( origin, destination, timer );
18 | gl_FragColor = vec4( pos,1.0 );
19 |
20 | }
--------------------------------------------------------------------------------
/model/bust.js:
--------------------------------------------------------------------------------
1 | {
2 |
3 | "metadata" :
4 | {
5 | "formatVersion" : 3.1,
6 | "sourceFile" : "bust.obj",
7 | "generatedBy" : "OBJConverter",
8 | "vertices" : 47516,
9 | "faces" : 95028,
10 | "normals" : 0,
11 | "uvs" : 0,
12 | "materials" : 0
13 | },
14 |
15 | "materials": [ {
16 | "DbgColor" : 15658734,
17 | "DbgIndex" : 0,
18 | "DbgName" : "default"
19 | }],
20 |
21 | "buffers": "bust.bin"
22 |
23 | }
24 |
--------------------------------------------------------------------------------
/glsl/basic/render_vs.glsl:
--------------------------------------------------------------------------------
1 |
2 | //float texture containing the positions of each particle
3 | uniform sampler2D positions;
4 |
5 | //size
6 | uniform float pointSize;
7 |
8 | void main() {
9 |
10 | //the mesh is a nomrliazed square so the uvs = the xy positions of the vertices
11 | vec3 pos = texture2D( positions, position.xy ).xyz;
12 |
13 | //pos now contains the position of a point in space taht can be transformed
14 | gl_Position = projectionMatrix * modelViewMatrix * vec4( pos, 1.0 );
15 |
16 | gl_PointSize = pointSize;
17 | }
--------------------------------------------------------------------------------
/glsl/image/render_vs.glsl:
--------------------------------------------------------------------------------
1 |
2 | //float texture containing the positions of each particle
3 | uniform sampler2D positions;
4 |
5 | //size
6 | uniform float pointSize;
7 |
8 | void main() {
9 |
10 | //the mesh is a nomrliazed square so the uvs = the xy positions of the vertices
11 | vec3 pos = texture2D( positions, position.xy ).xyz;
12 |
13 | //pos now contains the position of a point in space taht can be transformed
14 | gl_Position = projectionMatrix * modelViewMatrix * vec4( pos, 1.0 );
15 |
16 | gl_PointSize = pointSize;
17 | }
--------------------------------------------------------------------------------
/glsl/morph/render_vs.glsl:
--------------------------------------------------------------------------------
1 |
2 | //float texture containing the positions of each particle
3 | uniform sampler2D positions;
4 |
5 | //size
6 | uniform float pointSize;
7 |
8 | void main() {
9 |
10 | //the mesh is a nomrliazed square so the uvs = the xy positions of the vertices
11 | vec3 pos = texture2D( positions, position.xy ).xyz;
12 |
13 | //pos now contains the position of a point in space taht can be transformed
14 | gl_Position = projectionMatrix * modelViewMatrix * vec4( pos, 1.0 );
15 |
16 | gl_PointSize = pointSize;
17 | }
--------------------------------------------------------------------------------
/glsl/noise/render_vs.glsl:
--------------------------------------------------------------------------------
1 |
2 |
3 | //float texture containing the positions of each particle
4 | uniform sampler2D positions;
5 | uniform vec2 nearFar;
6 | uniform float pointSize;
7 |
8 | varying float size;
9 | void main() {
10 |
11 | //the mesh is a nomrliazed square so the uvs = the xy positions of the vertices
12 | vec3 pos = texture2D( positions, position.xy ).xyz;
13 |
14 | //pos now contains the position of a point in space taht can be transformed
15 | gl_Position = projectionMatrix * modelViewMatrix * vec4( pos, 1.0 );
16 |
17 | //size
18 | gl_PointSize = size = max( 1., ( step( 1. - ( 1. / 512. ), position.x ) ) * pointSize );
19 |
20 |
21 | }
--------------------------------------------------------------------------------
/glsl/mesh/render_fs.glsl:
--------------------------------------------------------------------------------
1 | uniform vec2 nearFar;
2 | varying float size;
3 | #ifdef USE_LOGDEPTHBUF
4 | uniform float logDepthBufFC;
5 | #ifdef USE_LOGDEPTHBUF_EXT
6 | varying float vFragDepth;
7 | #endif
8 | #endif
9 | void main()
10 | {
11 |
12 | if( size < 8. )discard;
13 |
14 | #if defined(USE_LOGDEPTHBUF) && defined(USE_LOGDEPTHBUF_EXT)
15 | gl_FragDepthEXT = log2(vFragDepth) * logDepthBufFC * 0.5;
16 | #endif
17 |
18 | #ifdef USE_LOGDEPTHBUF_EXT
19 | float depth = gl_FragDepthEXT / gl_FragCoord.w;
20 | #else
21 | float depth = gl_FragCoord.z / gl_FragCoord.w;
22 | #endif
23 |
24 | float color = 1.0 - smoothstep( nearFar.x, nearFar.y, depth );
25 | gl_FragColor = vec4( vec3( color ), 1. );
26 |
27 | }
--------------------------------------------------------------------------------
/glsl/mesh/render_vs.glsl:
--------------------------------------------------------------------------------
1 |
2 |
3 | //float texture containing the positions of each particle
4 | uniform sampler2D positions;
5 |
6 | //size
7 | uniform vec2 nearFar;
8 | uniform float pointSize;
9 | varying float size;
10 |
11 | #define EPSILON 1e-6
12 | #ifdef USE_LOGDEPTHBUF
13 | #ifdef USE_LOGDEPTHBUF_EXT
14 | varying float vFragDepth;
15 | #endif
16 | uniform float logDepthBufFC;
17 | #endif
18 | void main() {
19 |
20 | //the mesh is a nomrliazed square so the uvs = the xy positions of the vertices
21 | vec3 pos = texture2D( positions, position.xy ).xyz;
22 |
23 | //pos now contains the position of a point in space taht can be transformed
24 | gl_Position = projectionMatrix * modelViewMatrix * vec4( pos, 1.0 );
25 |
26 | #ifdef USE_LOGDEPTHBUF
27 | gl_Position.z = log2(max( EPSILON, gl_Position.w + 1.0 )) * logDepthBufFC;
28 |
29 | #ifdef USE_LOGDEPTHBUF_EXT
30 | vFragDepth = 1.0 + gl_Position.w;
31 | #else
32 | gl_Position.z = (gl_Position.z - 1.0) * gl_Position.w;
33 | #endif
34 | #endif
35 |
36 |
37 | float depth = 1.0 - smoothstep( nearFar.x, nearFar.y, gl_Position.z / gl_Position.w );
38 | gl_PointSize = size = depth * pointSize;
39 |
40 |
41 | }
--------------------------------------------------------------------------------
/ShaderLoader.js:
--------------------------------------------------------------------------------
1 | var ShaderLoader = function()
2 | {
3 | // Shaders
4 |
5 | ShaderLoader.get = function( id )
6 | {
7 | return ShaderLoader.shaders[ id ];
8 | };
9 |
10 | };
11 |
12 | ShaderLoader.prototype =
13 | {
14 |
15 | loadShaders : function( shaders, baseUrl, callback )
16 | {
17 | ShaderLoader.shaders = shaders;
18 |
19 | this.baseUrl = baseUrl || "./";
20 | this.callback = callback;
21 | this.batchLoad( this, 'onShadersReady' );
22 |
23 | },
24 |
25 | batchLoad : function( scope, callback )
26 | {
27 | var queue = 0;
28 | for ( var name in ShaderLoader.shaders ) {
29 |
30 | queue++;
31 | var req = new XMLHttpRequest();
32 | req.onload = loadHandler( name, req );
33 | req.open( 'get', scope.baseUrl + name + '.glsl', true );
34 | req.send();
35 | }
36 |
37 | function loadHandler( name, req ) {
38 | return function()
39 | {
40 | ShaderLoader.shaders[ name ] = req.responseText;
41 | if ( --queue <= 0 ) scope[ callback ]();
42 | };
43 | }
44 | },
45 |
46 | onShadersReady : function()
47 | {
48 | if( this.callback ) this.callback();
49 | }
50 | };
51 |
52 |
--------------------------------------------------------------------------------
/fbo.js:
--------------------------------------------------------------------------------
1 | var FBO = function( exports ){
2 |
3 | var scene, orthoCamera, rtt;
4 | exports.init = function( width, height, renderer, simulationMaterial, renderMaterial ){
5 |
6 | var gl = renderer.getContext();
7 |
8 | //1 we need FLOAT Textures to store positions
9 | //https://github.com/KhronosGroup/WebGL/blob/master/sdk/tests/conformance/extensions/oes-texture-float.html
10 | if (!gl.getExtension("OES_texture_float")){
11 | throw new Error( "float textures not supported" );
12 | }
13 |
14 | //2 we need to access textures from within the vertex shader
15 | //https://github.com/KhronosGroup/WebGL/blob/90ceaac0c4546b1aad634a6a5c4d2dfae9f4d124/conformance-suites/1.0.0/extra/webgl-info.html
16 | if( gl.getParameter(gl.MAX_VERTEX_TEXTURE_IMAGE_UNITS) == 0 ) {
17 | throw new Error( "vertex shader cannot read textures" );
18 | }
19 |
20 | //3 rtt setup
21 | scene = new THREE.Scene();
22 | orthoCamera = new THREE.OrthographicCamera(-1,1,1,-1,1/Math.pow( 2, 53 ),1 );
23 |
24 | //4 create a target texture
25 | var options = {
26 | minFilter: THREE.NearestFilter,//important as we want to sample square pixels
27 | magFilter: THREE.NearestFilter,//
28 | format: THREE.RGBAFormat,//180407 changed to RGBAFormat
29 | type:THREE.FloatType//important as we need precise coordinates (not ints)
30 | };
31 | rtt = new THREE.WebGLRenderTarget( width,height, options);
32 |
33 |
34 | //5 the simulation:
35 | //create a bi-unit quadrilateral and uses the simulation material to update the Float Texture
36 | var geom = new THREE.BufferGeometry();
37 | geom.addAttribute( 'position', new THREE.BufferAttribute( new Float32Array([ -1,-1,0, 1,-1,0, 1,1,0, -1,-1, 0, 1, 1, 0, -1,1,0 ]), 3 ) );
38 | geom.addAttribute( 'uv', new THREE.BufferAttribute( new Float32Array([ 0,1, 1,1, 1,0, 0,1, 1,0, 0,0 ]), 2 ) );
39 | scene.add( new THREE.Mesh( geom, simulationMaterial ) );
40 |
41 |
42 | //6 the particles:
43 | //create a vertex buffer of size width * height with normalized coordinates
44 | var l = (width * height );
45 | var vertices = new Float32Array( l * 3 );
46 | for ( var i = 0; i < l; i++ ) {
47 |
48 | var i3 = i * 3;
49 | vertices[ i3 ] = ( i % width ) / width ;
50 | vertices[ i3 + 1 ] = ( i / width ) / height;
51 | }
52 |
53 | //create the particles geometry
54 | var geometry = new THREE.BufferGeometry();
55 | geometry.addAttribute( 'position', new THREE.BufferAttribute( vertices, 3 ) );
56 |
57 | //the rendermaterial is used to render the particles
58 | exports.particles = new THREE.Points( geometry, renderMaterial );
59 | exports.renderer = renderer;
60 |
61 | };
62 |
63 | //7 update loop
64 | exports.update = function(){
65 |
66 | //1 update the simulation and render the result in a target texture
67 | exports.renderer.render( scene, orthoCamera, rtt, true );
68 |
69 | //2 use the result of the swap as the new position for the particles' renderer
70 | exports.particles.material.uniforms.positions.value = rtt;
71 |
72 | };
73 | return exports;
74 | }({});
75 |
--------------------------------------------------------------------------------
/glsl/noise/simulation_fs.glsl:
--------------------------------------------------------------------------------
1 |
2 | // simulation
3 |
4 | varying vec2 vUv;
5 | uniform sampler2D texture;
6 | uniform float timer;
7 | uniform float frequency;
8 | uniform float amplitude;
9 | uniform float maxDistance;
10 |
11 | //
12 | // Description : Array and textureless GLSL 2D simplex noise function.
13 | // Author : Ian McEwan, Ashima Arts.
14 | // Maintainer : ijm
15 | // Lastmod : 20110822 (ijm)
16 | // License : Copyright (C) 2011 Ashima Arts. All rights reserved.
17 | // Distributed under the MIT License. See LICENSE file.
18 | // https://github.com/ashima/webgl-noise
19 | //
20 |
21 | vec3 mod289(vec3 x) {
22 | return x - floor(x * (1.0 / 289.0)) * 289.0;
23 | }
24 |
25 | vec2 mod289(vec2 x) {
26 | return x - floor(x * (1.0 / 289.0)) * 289.0;
27 | }
28 |
29 | vec3 permute(vec3 x) {
30 | return mod289(((x*34.0)+1.0)*x);
31 | }
32 |
33 | float noise(vec2 v)
34 | {
35 | const vec4 C = vec4(0.211324865405187, // (3.0-sqrt(3.0))/6.0
36 | 0.366025403784439, // 0.5*(sqrt(3.0)-1.0)
37 | -0.577350269189626, // -1.0 + 2.0 * C.x
38 | 0.024390243902439); // 1.0 / 41.0
39 | // First corner
40 | vec2 i = floor(v + dot(v, C.yy) );
41 | vec2 x0 = v - i + dot(i, C.xx);
42 |
43 | // Other corners
44 | vec2 i1;
45 | //i1.x = step( x0.y, x0.x ); // x0.x > x0.y ? 1.0 : 0.0
46 | //i1.y = 1.0 - i1.x;
47 | i1 = (x0.x > x0.y) ? vec2(1.0, 0.0) : vec2(0.0, 1.0);
48 | // x0 = x0 - 0.0 + 0.0 * C.xx ;
49 | // x1 = x0 - i1 + 1.0 * C.xx ;
50 | // x2 = x0 - 1.0 + 2.0 * C.xx ;
51 | vec4 x12 = x0.xyxy + C.xxzz;
52 | x12.xy -= i1;
53 |
54 | // Permutations
55 | i = mod289(i); // Avoid truncation effects in permutation
56 | vec3 p = permute( permute( i.y + vec3(0.0, i1.y, 1.0 ))
57 | + i.x + vec3(0.0, i1.x, 1.0 ));
58 |
59 | vec3 m = max(0.5 - vec3(dot(x0,x0), dot(x12.xy,x12.xy), dot(x12.zw,x12.zw)), 0.0);
60 | m = m*m ;
61 | m = m*m ;
62 |
63 | // Gradients: 41 points uniformly over a line, mapped onto a diamond.
64 | // The ring size 17*17 = 289 is close to a multiple of 41 (41*7 = 287)
65 |
66 | vec3 x = 2.0 * fract(p * C.www) - 1.0;
67 | vec3 h = abs(x) - 0.5;
68 | vec3 ox = floor(x + 0.5);
69 | vec3 a0 = x - ox;
70 |
71 | // Normalise gradients implicitly by scaling m
72 | // Approximation of: m *= inversesqrt( a0*a0 + h*h );
73 | m *= 1.79284291400159 - 0.85373472095314 * ( a0*a0 + h*h );
74 |
75 | // Compute final noise value at P
76 | vec3 g;
77 | g.x = a0.x * x0.x + h.x * x0.y;
78 | g.yz = a0.yz * x12.xz + h.yz * x12.yw;
79 | return 130.0 * dot(m, g);
80 | }
81 |
82 | vec3 curl(float x, float y, float z)
83 | {
84 |
85 | float eps = 1., eps2 = 2. * eps;
86 | float n1, n2, a, b;
87 |
88 | x += timer * .05;
89 | y += timer * .05;
90 | z += timer * .05;
91 |
92 | vec3 curl = vec3(0.);
93 |
94 | n1 = noise(vec2( x, y + eps ));
95 | n2 = noise(vec2( x, y - eps ));
96 | a = (n1 - n2)/eps2;
97 |
98 | n1 = noise(vec2( x, z + eps));
99 | n2 = noise(vec2( x, z - eps));
100 | b = (n1 - n2)/eps2;
101 |
102 | curl.x = a - b;
103 |
104 | n1 = noise(vec2( y, z + eps));
105 | n2 = noise(vec2( y, z - eps));
106 | a = (n1 - n2)/eps2;
107 |
108 | n1 = noise(vec2( x + eps, z));
109 | n2 = noise(vec2( x + eps, z));
110 | b = (n1 - n2)/eps2;
111 |
112 | curl.y = a - b;
113 |
114 | n1 = noise(vec2( x + eps, y));
115 | n2 = noise(vec2( x - eps, y));
116 | a = (n1 - n2)/eps2;
117 |
118 | n1 = noise(vec2( y + eps, z));
119 | n2 = noise(vec2( y - eps, z));
120 | b = (n1 - n2)/eps2;
121 |
122 | curl.z = a - b;
123 |
124 | return curl;
125 | }
126 |
127 |
128 |
129 | void main() {
130 |
131 | vec3 pos = texture2D( texture, vUv ).xyz;
132 |
133 | vec3 tar = pos + curl( pos.x * frequency, pos.y * frequency, pos.z * frequency ) * amplitude;
134 |
135 | float d = length( pos-tar ) / maxDistance;
136 | pos = mix( pos, tar, pow( d, 5. ) );
137 |
138 | gl_FragColor = vec4( pos, 1. );
139 |
140 | }
--------------------------------------------------------------------------------
/basic.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | FBO
6 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
139 |
140 |
141 |
--------------------------------------------------------------------------------
/mesh.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | FBO
6 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
153 |
154 |
155 |
--------------------------------------------------------------------------------
/image.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | FBO
6 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
176 |
177 |
178 |
--------------------------------------------------------------------------------
/noise.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | FBO
6 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
180 |
181 |
182 |
--------------------------------------------------------------------------------
/morph.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | FBO
6 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
189 |
190 |
191 |
--------------------------------------------------------------------------------
/vendor/BinaryLoader.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @author alteredq / http://alteredqualia.com/
3 | */
4 |
5 | THREE.BinaryLoader = function ( manager ) {
6 |
7 | if ( typeof manager === 'boolean' ) {
8 |
9 | console.warn( 'THREE.BinaryLoader: showStatus parameter has been removed from constructor.' );
10 | manager = undefined;
11 |
12 | }
13 |
14 | this.manager = ( manager !== undefined ) ? manager : THREE.DefaultLoadingManager;
15 |
16 | };
17 |
18 | THREE.BinaryLoader.prototype = {
19 |
20 | constructor: THREE.BinaryLoader,
21 |
22 | // Deprecated
23 |
24 | get statusDomElement () {
25 |
26 | if ( this._statusDomElement === undefined ) {
27 |
28 | this._statusDomElement = document.createElement( 'div' );
29 |
30 | }
31 |
32 | console.warn( 'THREE.BinaryLoader: .statusDomElement has been removed.' );
33 | return this._statusDomElement;
34 |
35 | },
36 |
37 | // Load models generated by slim OBJ converter with BINARY option (converter_obj_three_slim.py -t binary)
38 | // - binary models consist of two files: JS and BIN
39 | // - parameters
40 | // - url (required)
41 | // - callback (required)
42 | // - texturePath (optional: if not specified, textures will be assumed to be in the same folder as JS model file)
43 | // - binaryPath (optional: if not specified, binary file will be assumed to be in the same folder as JS model file)
44 | load: function ( url, onLoad, onProgress, onError ) {
45 |
46 | // todo: unify load API to for easier SceneLoader use
47 |
48 | var texturePath = this.texturePath || THREE.Loader.prototype.extractUrlBase( url );
49 | var binaryPath = this.binaryPath || THREE.Loader.prototype.extractUrlBase( url );
50 |
51 | // #1 load JS part via web worker
52 |
53 | var scope = this;
54 |
55 | var jsonloader = new THREE.XHRLoader( this.manager );
56 | jsonloader.setCrossOrigin( this.crossOrigin );
57 | jsonloader.load( url, function ( data ) {
58 |
59 | var json = JSON.parse( data );
60 |
61 | var bufferUrl = binaryPath + json.buffers;
62 |
63 | var bufferLoader = new THREE.XHRLoader( scope.manager );
64 | bufferLoader.setCrossOrigin( scope.crossOrigin );
65 | bufferLoader.setResponseType( 'arraybuffer' );
66 | bufferLoader.load( bufferUrl, function ( bufData ) {
67 |
68 | // IEWEBGL needs this ???
69 | //buffer = ( new Uint8Array( xhr.responseBody ) ).buffer;
70 |
71 | //// iOS and other XMLHttpRequest level 1 ???
72 |
73 | scope.parse( bufData, onLoad, texturePath, json.materials );
74 |
75 | }, onProgress, onError );
76 |
77 | }, onProgress, onError );
78 |
79 | },
80 |
81 | setBinaryPath: function ( value ) {
82 |
83 | this.binaryPath = value;
84 |
85 | },
86 |
87 | setCrossOrigin: function ( value ) {
88 |
89 | this.crossOrigin = value;
90 |
91 | },
92 |
93 | setTexturePath: function ( value ) {
94 |
95 | this.texturePath = value;
96 |
97 | },
98 |
99 | parse: function ( data, callback, texturePath, jsonMaterials ) {
100 |
101 | var Model = function ( texturePath ) {
102 |
103 | var scope = this,
104 | currentOffset = 0,
105 | md,
106 | normals = [],
107 | uvs = [],
108 | start_tri_flat, start_tri_smooth, start_tri_flat_uv, start_tri_smooth_uv,
109 | start_quad_flat, start_quad_smooth, start_quad_flat_uv, start_quad_smooth_uv,
110 | tri_size, quad_size,
111 | len_tri_flat, len_tri_smooth, len_tri_flat_uv, len_tri_smooth_uv,
112 | len_quad_flat, len_quad_smooth, len_quad_flat_uv, len_quad_smooth_uv;
113 |
114 |
115 | THREE.Geometry.call( this );
116 |
117 | md = parseMetaData( data, currentOffset );
118 |
119 | currentOffset += md.header_bytes;
120 | /*
121 | md.vertex_index_bytes = Uint32Array.BYTES_PER_ELEMENT;
122 | md.material_index_bytes = Uint16Array.BYTES_PER_ELEMENT;
123 | md.normal_index_bytes = Uint32Array.BYTES_PER_ELEMENT;
124 | md.uv_index_bytes = Uint32Array.BYTES_PER_ELEMENT;
125 | */
126 | // buffers sizes
127 |
128 | tri_size = md.vertex_index_bytes * 3 + md.material_index_bytes;
129 | quad_size = md.vertex_index_bytes * 4 + md.material_index_bytes;
130 |
131 | len_tri_flat = md.ntri_flat * ( tri_size );
132 | len_tri_smooth = md.ntri_smooth * ( tri_size + md.normal_index_bytes * 3 );
133 | len_tri_flat_uv = md.ntri_flat_uv * ( tri_size + md.uv_index_bytes * 3 );
134 | len_tri_smooth_uv = md.ntri_smooth_uv * ( tri_size + md.normal_index_bytes * 3 + md.uv_index_bytes * 3 );
135 |
136 | len_quad_flat = md.nquad_flat * ( quad_size );
137 | len_quad_smooth = md.nquad_smooth * ( quad_size + md.normal_index_bytes * 4 );
138 | len_quad_flat_uv = md.nquad_flat_uv * ( quad_size + md.uv_index_bytes * 4 );
139 | len_quad_smooth_uv = md.nquad_smooth_uv * ( quad_size + md.normal_index_bytes * 4 + md.uv_index_bytes * 4 );
140 |
141 | // read buffers
142 |
143 | currentOffset += init_vertices( currentOffset );
144 |
145 | currentOffset += init_normals( currentOffset );
146 | currentOffset += handlePadding( md.nnormals * 3 );
147 |
148 | currentOffset += init_uvs( currentOffset );
149 |
150 | start_tri_flat = currentOffset;
151 | start_tri_smooth = start_tri_flat + len_tri_flat + handlePadding( md.ntri_flat * 2 );
152 | start_tri_flat_uv = start_tri_smooth + len_tri_smooth + handlePadding( md.ntri_smooth * 2 );
153 | start_tri_smooth_uv = start_tri_flat_uv + len_tri_flat_uv + handlePadding( md.ntri_flat_uv * 2 );
154 |
155 | start_quad_flat = start_tri_smooth_uv + len_tri_smooth_uv + handlePadding( md.ntri_smooth_uv * 2 );
156 | start_quad_smooth = start_quad_flat + len_quad_flat + handlePadding( md.nquad_flat * 2 );
157 | start_quad_flat_uv = start_quad_smooth + len_quad_smooth + handlePadding( md.nquad_smooth * 2 );
158 | start_quad_smooth_uv = start_quad_flat_uv + len_quad_flat_uv + handlePadding( md.nquad_flat_uv * 2 );
159 |
160 | // have to first process faces with uvs
161 | // so that face and uv indices match
162 |
163 | init_triangles_flat_uv( start_tri_flat_uv );
164 | init_triangles_smooth_uv( start_tri_smooth_uv );
165 |
166 | init_quads_flat_uv( start_quad_flat_uv );
167 | init_quads_smooth_uv( start_quad_smooth_uv );
168 |
169 | // now we can process untextured faces
170 |
171 | init_triangles_flat( start_tri_flat );
172 | init_triangles_smooth( start_tri_smooth );
173 |
174 | init_quads_flat( start_quad_flat );
175 | init_quads_smooth( start_quad_smooth );
176 |
177 | this.computeFaceNormals();
178 |
179 | function handlePadding( n ) {
180 |
181 | return ( n % 4 ) ? ( 4 - n % 4 ) : 0;
182 |
183 | }
184 |
185 | function parseMetaData( data, offset ) {
186 |
187 | var metaData = {
188 |
189 | 'signature' : parseString( data, offset, 12 ),
190 | 'header_bytes' : parseUChar8( data, offset + 12 ),
191 |
192 | 'vertex_coordinate_bytes' : parseUChar8( data, offset + 13 ),
193 | 'normal_coordinate_bytes' : parseUChar8( data, offset + 14 ),
194 | 'uv_coordinate_bytes' : parseUChar8( data, offset + 15 ),
195 |
196 | 'vertex_index_bytes' : parseUChar8( data, offset + 16 ),
197 | 'normal_index_bytes' : parseUChar8( data, offset + 17 ),
198 | 'uv_index_bytes' : parseUChar8( data, offset + 18 ),
199 | 'material_index_bytes' : parseUChar8( data, offset + 19 ),
200 |
201 | 'nvertices' : parseUInt32( data, offset + 20 ),
202 | 'nnormals' : parseUInt32( data, offset + 20 + 4 * 1 ),
203 | 'nuvs' : parseUInt32( data, offset + 20 + 4 * 2 ),
204 |
205 | 'ntri_flat' : parseUInt32( data, offset + 20 + 4 * 3 ),
206 | 'ntri_smooth' : parseUInt32( data, offset + 20 + 4 * 4 ),
207 | 'ntri_flat_uv' : parseUInt32( data, offset + 20 + 4 * 5 ),
208 | 'ntri_smooth_uv' : parseUInt32( data, offset + 20 + 4 * 6 ),
209 |
210 | 'nquad_flat' : parseUInt32( data, offset + 20 + 4 * 7 ),
211 | 'nquad_smooth' : parseUInt32( data, offset + 20 + 4 * 8 ),
212 | 'nquad_flat_uv' : parseUInt32( data, offset + 20 + 4 * 9 ),
213 | 'nquad_smooth_uv' : parseUInt32( data, offset + 20 + 4 * 10 )
214 |
215 | };
216 | /*
217 | console.log( "signature: " + metaData.signature );
218 |
219 | console.log( "header_bytes: " + metaData.header_bytes );
220 | console.log( "vertex_coordinate_bytes: " + metaData.vertex_coordinate_bytes );
221 | console.log( "normal_coordinate_bytes: " + metaData.normal_coordinate_bytes );
222 | console.log( "uv_coordinate_bytes: " + metaData.uv_coordinate_bytes );
223 |
224 | console.log( "vertex_index_bytes: " + metaData.vertex_index_bytes );
225 | console.log( "normal_index_bytes: " + metaData.normal_index_bytes );
226 | console.log( "uv_index_bytes: " + metaData.uv_index_bytes );
227 | console.log( "material_index_bytes: " + metaData.material_index_bytes );
228 |
229 | console.log( "nvertices: " + metaData.nvertices );
230 | console.log( "nnormals: " + metaData.nnormals );
231 | console.log( "nuvs: " + metaData.nuvs );
232 |
233 | console.log( "ntri_flat: " + metaData.ntri_flat );
234 | console.log( "ntri_smooth: " + metaData.ntri_smooth );
235 | console.log( "ntri_flat_uv: " + metaData.ntri_flat_uv );
236 | console.log( "ntri_smooth_uv: " + metaData.ntri_smooth_uv );
237 |
238 | console.log( "nquad_flat: " + metaData.nquad_flat );
239 | console.log( "nquad_smooth: " + metaData.nquad_smooth );
240 | console.log( "nquad_flat_uv: " + metaData.nquad_flat_uv );
241 | console.log( "nquad_smooth_uv: " + metaData.nquad_smooth_uv );
242 |
243 | var total = metaData.header_bytes
244 | + metaData.nvertices * metaData.vertex_coordinate_bytes * 3
245 | + metaData.nnormals * metaData.normal_coordinate_bytes * 3
246 | + metaData.nuvs * metaData.uv_coordinate_bytes * 2
247 | + metaData.ntri_flat * ( metaData.vertex_index_bytes*3 + metaData.material_index_bytes )
248 | + metaData.ntri_smooth * ( metaData.vertex_index_bytes*3 + metaData.material_index_bytes + metaData.normal_index_bytes*3 )
249 | + metaData.ntri_flat_uv * ( metaData.vertex_index_bytes*3 + metaData.material_index_bytes + metaData.uv_index_bytes*3 )
250 | + metaData.ntri_smooth_uv * ( metaData.vertex_index_bytes*3 + metaData.material_index_bytes + metaData.normal_index_bytes*3 + metaData.uv_index_bytes*3 )
251 | + metaData.nquad_flat * ( metaData.vertex_index_bytes*4 + metaData.material_index_bytes )
252 | + metaData.nquad_smooth * ( metaData.vertex_index_bytes*4 + metaData.material_index_bytes + metaData.normal_index_bytes*4 )
253 | + metaData.nquad_flat_uv * ( metaData.vertex_index_bytes*4 + metaData.material_index_bytes + metaData.uv_index_bytes*4 )
254 | + metaData.nquad_smooth_uv * ( metaData.vertex_index_bytes*4 + metaData.material_index_bytes + metaData.normal_index_bytes*4 + metaData.uv_index_bytes*4 );
255 | console.log( "total bytes: " + total );
256 | */
257 |
258 | return metaData;
259 |
260 | }
261 |
262 | function parseString( data, offset, length ) {
263 |
264 | var charArray = new Uint8Array( data, offset, length );
265 |
266 | var text = "";
267 |
268 | for ( var i = 0; i < length; i ++ ) {
269 |
270 | text += String.fromCharCode( charArray[ offset + i ] );
271 |
272 | }
273 |
274 | return text;
275 |
276 | }
277 |
278 | function parseUChar8( data, offset ) {
279 |
280 | var charArray = new Uint8Array( data, offset, 1 );
281 |
282 | return charArray[ 0 ];
283 |
284 | }
285 |
286 | function parseUInt32( data, offset ) {
287 |
288 | var intArray = new Uint32Array( data, offset, 1 );
289 |
290 | return intArray[ 0 ];
291 |
292 | }
293 |
294 | function init_vertices( start ) {
295 |
296 | var nElements = md.nvertices;
297 |
298 | var coordArray = new Float32Array( data, start, nElements * 3 );
299 |
300 | var i, x, y, z;
301 |
302 | for ( i = 0; i < nElements; i ++ ) {
303 |
304 | x = coordArray[ i * 3 ];
305 | y = coordArray[ i * 3 + 1 ];
306 | z = coordArray[ i * 3 + 2 ];
307 |
308 | scope.vertices.push( new THREE.Vector3( x, y, z ) );
309 |
310 | }
311 |
312 | return nElements * 3 * Float32Array.BYTES_PER_ELEMENT;
313 |
314 | }
315 |
316 | function init_normals( start ) {
317 |
318 | var nElements = md.nnormals;
319 |
320 | if ( nElements ) {
321 |
322 | var normalArray = new Int8Array( data, start, nElements * 3 );
323 |
324 | var i, x, y, z;
325 |
326 | for ( i = 0; i < nElements; i ++ ) {
327 |
328 | x = normalArray[ i * 3 ];
329 | y = normalArray[ i * 3 + 1 ];
330 | z = normalArray[ i * 3 + 2 ];
331 |
332 | normals.push( x / 127, y / 127, z / 127 );
333 |
334 | }
335 |
336 | }
337 |
338 | return nElements * 3 * Int8Array.BYTES_PER_ELEMENT;
339 |
340 | }
341 |
342 | function init_uvs( start ) {
343 |
344 | var nElements = md.nuvs;
345 |
346 | if ( nElements ) {
347 |
348 | var uvArray = new Float32Array( data, start, nElements * 2 );
349 |
350 | var i, u, v;
351 |
352 | for ( i = 0; i < nElements; i ++ ) {
353 |
354 | u = uvArray[ i * 2 ];
355 | v = uvArray[ i * 2 + 1 ];
356 |
357 | uvs.push( u, v );
358 |
359 | }
360 |
361 | }
362 |
363 | return nElements * 2 * Float32Array.BYTES_PER_ELEMENT;
364 |
365 | }
366 |
367 | function init_uvs3( nElements, offset ) {
368 |
369 | var i, uva, uvb, uvc, u1, u2, u3, v1, v2, v3;
370 |
371 | var uvIndexBuffer = new Uint32Array( data, offset, 3 * nElements );
372 |
373 | for ( i = 0; i < nElements; i ++ ) {
374 |
375 | uva = uvIndexBuffer[ i * 3 ];
376 | uvb = uvIndexBuffer[ i * 3 + 1 ];
377 | uvc = uvIndexBuffer[ i * 3 + 2 ];
378 |
379 | u1 = uvs[ uva * 2 ];
380 | v1 = uvs[ uva * 2 + 1 ];
381 |
382 | u2 = uvs[ uvb * 2 ];
383 | v2 = uvs[ uvb * 2 + 1 ];
384 |
385 | u3 = uvs[ uvc * 2 ];
386 | v3 = uvs[ uvc * 2 + 1 ];
387 |
388 | scope.faceVertexUvs[ 0 ].push( [
389 | new THREE.Vector2( u1, v1 ),
390 | new THREE.Vector2( u2, v2 ),
391 | new THREE.Vector2( u3, v3 )
392 | ] );
393 |
394 | }
395 |
396 | }
397 |
398 | function init_uvs4( nElements, offset ) {
399 |
400 | var i, uva, uvb, uvc, uvd, u1, u2, u3, u4, v1, v2, v3, v4;
401 |
402 | var uvIndexBuffer = new Uint32Array( data, offset, 4 * nElements );
403 |
404 | for ( i = 0; i < nElements; i ++ ) {
405 |
406 | uva = uvIndexBuffer[ i * 4 ];
407 | uvb = uvIndexBuffer[ i * 4 + 1 ];
408 | uvc = uvIndexBuffer[ i * 4 + 2 ];
409 | uvd = uvIndexBuffer[ i * 4 + 3 ];
410 |
411 | u1 = uvs[ uva * 2 ];
412 | v1 = uvs[ uva * 2 + 1 ];
413 |
414 | u2 = uvs[ uvb * 2 ];
415 | v2 = uvs[ uvb * 2 + 1 ];
416 |
417 | u3 = uvs[ uvc * 2 ];
418 | v3 = uvs[ uvc * 2 + 1 ];
419 |
420 | u4 = uvs[ uvd * 2 ];
421 | v4 = uvs[ uvd * 2 + 1 ];
422 |
423 | scope.faceVertexUvs[ 0 ].push( [
424 | new THREE.Vector2( u1, v1 ),
425 | new THREE.Vector2( u2, v2 ),
426 | new THREE.Vector2( u4, v4 )
427 | ] );
428 |
429 | scope.faceVertexUvs[ 0 ].push( [
430 | new THREE.Vector2( u2, v2 ),
431 | new THREE.Vector2( u3, v3 ),
432 | new THREE.Vector2( u4, v4 )
433 | ] );
434 |
435 | }
436 |
437 | }
438 |
439 | function init_faces3_flat( nElements, offsetVertices, offsetMaterials ) {
440 |
441 | var i, a, b, c, m;
442 |
443 | var vertexIndexBuffer = new Uint32Array( data, offsetVertices, 3 * nElements );
444 | var materialIndexBuffer = new Uint16Array( data, offsetMaterials, nElements );
445 |
446 | for ( i = 0; i < nElements; i ++ ) {
447 |
448 | a = vertexIndexBuffer[ i * 3 ];
449 | b = vertexIndexBuffer[ i * 3 + 1 ];
450 | c = vertexIndexBuffer[ i * 3 + 2 ];
451 |
452 | m = materialIndexBuffer[ i ];
453 |
454 | scope.faces.push( new THREE.Face3( a, b, c, null, null, m ) );
455 |
456 | }
457 |
458 | }
459 |
460 | function init_faces4_flat( nElements, offsetVertices, offsetMaterials ) {
461 |
462 | var i, a, b, c, d, m;
463 |
464 | var vertexIndexBuffer = new Uint32Array( data, offsetVertices, 4 * nElements );
465 | var materialIndexBuffer = new Uint16Array( data, offsetMaterials, nElements );
466 |
467 | for ( i = 0; i < nElements; i ++ ) {
468 |
469 | a = vertexIndexBuffer[ i * 4 ];
470 | b = vertexIndexBuffer[ i * 4 + 1 ];
471 | c = vertexIndexBuffer[ i * 4 + 2 ];
472 | d = vertexIndexBuffer[ i * 4 + 3 ];
473 |
474 | m = materialIndexBuffer[ i ];
475 |
476 | scope.faces.push( new THREE.Face3( a, b, d, null, null, m ) );
477 | scope.faces.push( new THREE.Face3( b, c, d, null, null, m ) );
478 |
479 | }
480 |
481 | }
482 |
483 | function init_faces3_smooth( nElements, offsetVertices, offsetNormals, offsetMaterials ) {
484 |
485 | var i, a, b, c, m;
486 | var na, nb, nc;
487 |
488 | var vertexIndexBuffer = new Uint32Array( data, offsetVertices, 3 * nElements );
489 | var normalIndexBuffer = new Uint32Array( data, offsetNormals, 3 * nElements );
490 | var materialIndexBuffer = new Uint16Array( data, offsetMaterials, nElements );
491 |
492 | for ( i = 0; i < nElements; i ++ ) {
493 |
494 | a = vertexIndexBuffer[ i * 3 ];
495 | b = vertexIndexBuffer[ i * 3 + 1 ];
496 | c = vertexIndexBuffer[ i * 3 + 2 ];
497 |
498 | na = normalIndexBuffer[ i * 3 ];
499 | nb = normalIndexBuffer[ i * 3 + 1 ];
500 | nc = normalIndexBuffer[ i * 3 + 2 ];
501 |
502 | m = materialIndexBuffer[ i ];
503 |
504 | var nax = normals[ na * 3 ],
505 | nay = normals[ na * 3 + 1 ],
506 | naz = normals[ na * 3 + 2 ],
507 |
508 | nbx = normals[ nb * 3 ],
509 | nby = normals[ nb * 3 + 1 ],
510 | nbz = normals[ nb * 3 + 2 ],
511 |
512 | ncx = normals[ nc * 3 ],
513 | ncy = normals[ nc * 3 + 1 ],
514 | ncz = normals[ nc * 3 + 2 ];
515 |
516 | scope.faces.push( new THREE.Face3( a, b, c, [
517 | new THREE.Vector3( nax, nay, naz ),
518 | new THREE.Vector3( nbx, nby, nbz ),
519 | new THREE.Vector3( ncx, ncy, ncz )
520 | ], null, m ) );
521 |
522 | }
523 |
524 | }
525 |
526 | function init_faces4_smooth( nElements, offsetVertices, offsetNormals, offsetMaterials ) {
527 |
528 | var i, a, b, c, d, m;
529 | var na, nb, nc, nd;
530 |
531 | var vertexIndexBuffer = new Uint32Array( data, offsetVertices, 4 * nElements );
532 | var normalIndexBuffer = new Uint32Array( data, offsetNormals, 4 * nElements );
533 | var materialIndexBuffer = new Uint16Array( data, offsetMaterials, nElements );
534 |
535 | for ( i = 0; i < nElements; i ++ ) {
536 |
537 | a = vertexIndexBuffer[ i * 4 ];
538 | b = vertexIndexBuffer[ i * 4 + 1 ];
539 | c = vertexIndexBuffer[ i * 4 + 2 ];
540 | d = vertexIndexBuffer[ i * 4 + 3 ];
541 |
542 | na = normalIndexBuffer[ i * 4 ];
543 | nb = normalIndexBuffer[ i * 4 + 1 ];
544 | nc = normalIndexBuffer[ i * 4 + 2 ];
545 | nd = normalIndexBuffer[ i * 4 + 3 ];
546 |
547 | m = materialIndexBuffer[ i ];
548 |
549 | var nax = normals[ na * 3 ],
550 | nay = normals[ na * 3 + 1 ],
551 | naz = normals[ na * 3 + 2 ],
552 |
553 | nbx = normals[ nb * 3 ],
554 | nby = normals[ nb * 3 + 1 ],
555 | nbz = normals[ nb * 3 + 2 ],
556 |
557 | ncx = normals[ nc * 3 ],
558 | ncy = normals[ nc * 3 + 1 ],
559 | ncz = normals[ nc * 3 + 2 ],
560 |
561 | ndx = normals[ nd * 3 ],
562 | ndy = normals[ nd * 3 + 1 ],
563 | ndz = normals[ nd * 3 + 2 ];
564 |
565 | scope.faces.push( new THREE.Face3( a, b, d, [
566 | new THREE.Vector3( nax, nay, naz ),
567 | new THREE.Vector3( nbx, nby, nbz ),
568 | new THREE.Vector3( ndx, ndy, ndz )
569 | ], null, m ) );
570 |
571 | scope.faces.push( new THREE.Face3( b, c, d, [
572 | new THREE.Vector3( nbx, nby, nbz ),
573 | new THREE.Vector3( ncx, ncy, ncz ),
574 | new THREE.Vector3( ndx, ndy, ndz )
575 | ], null, m ) );
576 |
577 | }
578 |
579 | }
580 |
581 | function init_triangles_flat( start ) {
582 |
583 | var nElements = md.ntri_flat;
584 |
585 | if ( nElements ) {
586 |
587 | var offsetMaterials = start + nElements * Uint32Array.BYTES_PER_ELEMENT * 3;
588 | init_faces3_flat( nElements, start, offsetMaterials );
589 |
590 | }
591 |
592 | }
593 |
594 | function init_triangles_flat_uv( start ) {
595 |
596 | var nElements = md.ntri_flat_uv;
597 |
598 | if ( nElements ) {
599 |
600 | var offsetUvs = start + nElements * Uint32Array.BYTES_PER_ELEMENT * 3;
601 | var offsetMaterials = offsetUvs + nElements * Uint32Array.BYTES_PER_ELEMENT * 3;
602 |
603 | init_faces3_flat( nElements, start, offsetMaterials );
604 | init_uvs3( nElements, offsetUvs );
605 |
606 | }
607 |
608 | }
609 |
610 | function init_triangles_smooth( start ) {
611 |
612 | var nElements = md.ntri_smooth;
613 |
614 | if ( nElements ) {
615 |
616 | var offsetNormals = start + nElements * Uint32Array.BYTES_PER_ELEMENT * 3;
617 | var offsetMaterials = offsetNormals + nElements * Uint32Array.BYTES_PER_ELEMENT * 3;
618 |
619 | init_faces3_smooth( nElements, start, offsetNormals, offsetMaterials );
620 |
621 | }
622 |
623 | }
624 |
625 | function init_triangles_smooth_uv( start ) {
626 |
627 | var nElements = md.ntri_smooth_uv;
628 |
629 | if ( nElements ) {
630 |
631 | var offsetNormals = start + nElements * Uint32Array.BYTES_PER_ELEMENT * 3;
632 | var offsetUvs = offsetNormals + nElements * Uint32Array.BYTES_PER_ELEMENT * 3;
633 | var offsetMaterials = offsetUvs + nElements * Uint32Array.BYTES_PER_ELEMENT * 3;
634 |
635 | init_faces3_smooth( nElements, start, offsetNormals, offsetMaterials );
636 | init_uvs3( nElements, offsetUvs );
637 |
638 | }
639 |
640 | }
641 |
642 | function init_quads_flat( start ) {
643 |
644 | var nElements = md.nquad_flat;
645 |
646 | if ( nElements ) {
647 |
648 | var offsetMaterials = start + nElements * Uint32Array.BYTES_PER_ELEMENT * 4;
649 | init_faces4_flat( nElements, start, offsetMaterials );
650 |
651 | }
652 |
653 | }
654 |
655 | function init_quads_flat_uv( start ) {
656 |
657 | var nElements = md.nquad_flat_uv;
658 |
659 | if ( nElements ) {
660 |
661 | var offsetUvs = start + nElements * Uint32Array.BYTES_PER_ELEMENT * 4;
662 | var offsetMaterials = offsetUvs + nElements * Uint32Array.BYTES_PER_ELEMENT * 4;
663 |
664 | init_faces4_flat( nElements, start, offsetMaterials );
665 | init_uvs4( nElements, offsetUvs );
666 |
667 | }
668 |
669 | }
670 |
671 | function init_quads_smooth( start ) {
672 |
673 | var nElements = md.nquad_smooth;
674 |
675 | if ( nElements ) {
676 |
677 | var offsetNormals = start + nElements * Uint32Array.BYTES_PER_ELEMENT * 4;
678 | var offsetMaterials = offsetNormals + nElements * Uint32Array.BYTES_PER_ELEMENT * 4;
679 |
680 | init_faces4_smooth( nElements, start, offsetNormals, offsetMaterials );
681 |
682 | }
683 |
684 | }
685 |
686 | function init_quads_smooth_uv( start ) {
687 |
688 | var nElements = md.nquad_smooth_uv;
689 |
690 | if ( nElements ) {
691 |
692 | var offsetNormals = start + nElements * Uint32Array.BYTES_PER_ELEMENT * 4;
693 | var offsetUvs = offsetNormals + nElements * Uint32Array.BYTES_PER_ELEMENT * 4;
694 | var offsetMaterials = offsetUvs + nElements * Uint32Array.BYTES_PER_ELEMENT * 4;
695 |
696 | init_faces4_smooth( nElements, start, offsetNormals, offsetMaterials );
697 | init_uvs4( nElements, offsetUvs );
698 |
699 | }
700 |
701 | }
702 |
703 | };
704 |
705 | Model.prototype = Object.create( THREE.Geometry.prototype );
706 | Model.prototype.constructor = Model;
707 |
708 | var geometry = new Model( texturePath );
709 | var materials = THREE.Loader.prototype.initMaterials( jsonMaterials, texturePath, this.crossOrigin );
710 |
711 | callback( geometry, materials );
712 |
713 | }
714 |
715 | };
716 |
--------------------------------------------------------------------------------
/vendor/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 | ( function () {
11 |
12 | function OrbitConstraint ( object ) {
13 |
14 | this.object = object;
15 |
16 | // "target" sets the location of focus, where the object orbits around
17 | // and where it pans with respect to.
18 | this.target = new THREE.Vector3();
19 |
20 | // Limits to how far you can dolly in and out ( PerspectiveCamera only )
21 | this.minDistance = 0;
22 | this.maxDistance = Infinity;
23 |
24 | // Limits to how far you can zoom in and out ( OrthographicCamera only )
25 | this.minZoom = 0;
26 | this.maxZoom = Infinity;
27 |
28 | // How far you can orbit vertically, upper and lower limits.
29 | // Range is 0 to Math.PI radians.
30 | this.minPolarAngle = 0; // radians
31 | this.maxPolarAngle = Math.PI; // radians
32 |
33 | // How far you can orbit horizontally, upper and lower limits.
34 | // If set, must be a sub-interval of the interval [ - Math.PI, Math.PI ].
35 | this.minAzimuthAngle = - Infinity; // radians
36 | this.maxAzimuthAngle = Infinity; // radians
37 |
38 | // Set to true to enable damping (inertia)
39 | // If damping is enabled, you must call controls.update() in your animation loop
40 | this.enableDamping = false;
41 | this.dampingFactor = 0.25;
42 |
43 | ////////////
44 | // internals
45 |
46 | var scope = this;
47 |
48 | var EPS = 0.000001;
49 |
50 | // Current position in spherical coordinate system.
51 | var theta;
52 | var phi;
53 |
54 | // Pending changes
55 | var phiDelta = 0;
56 | var thetaDelta = 0;
57 | var scale = 1;
58 | var panOffset = new THREE.Vector3();
59 | var zoomChanged = false;
60 |
61 | // API
62 |
63 | this.getPolarAngle = function () {
64 |
65 | return phi;
66 |
67 | };
68 |
69 | this.getAzimuthalAngle = function () {
70 |
71 | return theta;
72 |
73 | };
74 |
75 | this.rotateLeft = function ( angle ) {
76 |
77 | thetaDelta -= angle;
78 |
79 | };
80 |
81 | this.rotateUp = function ( angle ) {
82 |
83 | phiDelta -= angle;
84 |
85 | };
86 |
87 | // pass in distance in world space to move left
88 | this.panLeft = function() {
89 |
90 | var v = new THREE.Vector3();
91 |
92 | return function panLeft ( distance ) {
93 |
94 | var te = this.object.matrix.elements;
95 |
96 | // get X column of matrix
97 | v.set( te[ 0 ], te[ 1 ], te[ 2 ] );
98 | v.multiplyScalar( - distance );
99 |
100 | panOffset.add( v );
101 |
102 | };
103 |
104 | }();
105 |
106 | // pass in distance in world space to move up
107 | this.panUp = function() {
108 |
109 | var v = new THREE.Vector3();
110 |
111 | return function panUp ( distance ) {
112 |
113 | var te = this.object.matrix.elements;
114 |
115 | // get Y column of matrix
116 | v.set( te[ 4 ], te[ 5 ], te[ 6 ] );
117 | v.multiplyScalar( distance );
118 |
119 | panOffset.add( v );
120 |
121 | };
122 |
123 | }();
124 |
125 | // pass in x,y of change desired in pixel space,
126 | // right and down are positive
127 | this.pan = function ( deltaX, deltaY, screenWidth, screenHeight ) {
128 |
129 | if ( scope.object instanceof THREE.PerspectiveCamera ) {
130 |
131 | // perspective
132 | var position = scope.object.position;
133 | var offset = position.clone().sub( scope.target );
134 | var targetDistance = offset.length();
135 |
136 | // half of the fov is center to top of screen
137 | targetDistance *= Math.tan( ( scope.object.fov / 2 ) * Math.PI / 180.0 );
138 |
139 | // we actually don't use screenWidth, since perspective camera is fixed to screen height
140 | scope.panLeft( 2 * deltaX * targetDistance / screenHeight );
141 | scope.panUp( 2 * deltaY * targetDistance / screenHeight );
142 |
143 | } else if ( scope.object instanceof THREE.OrthographicCamera ) {
144 |
145 | // orthographic
146 | scope.panLeft( deltaX * ( scope.object.right - scope.object.left ) / screenWidth );
147 | scope.panUp( deltaY * ( scope.object.top - scope.object.bottom ) / screenHeight );
148 |
149 | } else {
150 |
151 | // camera neither orthographic or perspective
152 | console.warn( 'WARNING: OrbitControls.js encountered an unknown camera type - pan disabled.' );
153 |
154 | }
155 |
156 | };
157 |
158 | this.dollyIn = function ( dollyScale ) {
159 |
160 | if ( scope.object instanceof THREE.PerspectiveCamera ) {
161 |
162 | scale /= dollyScale;
163 |
164 | } else if ( scope.object instanceof THREE.OrthographicCamera ) {
165 |
166 | scope.object.zoom = Math.max( this.minZoom, Math.min( this.maxZoom, this.object.zoom * dollyScale ) );
167 | scope.object.updateProjectionMatrix();
168 | zoomChanged = true;
169 |
170 | } else {
171 |
172 | console.warn( 'WARNING: OrbitControls.js encountered an unknown camera type - dolly/zoom disabled.' );
173 |
174 | }
175 |
176 | };
177 |
178 | this.dollyOut = function ( dollyScale ) {
179 |
180 | if ( scope.object instanceof THREE.PerspectiveCamera ) {
181 |
182 | scale *= dollyScale;
183 |
184 | } else if ( scope.object instanceof THREE.OrthographicCamera ) {
185 |
186 | scope.object.zoom = Math.max( this.minZoom, Math.min( this.maxZoom, this.object.zoom / dollyScale ) );
187 | scope.object.updateProjectionMatrix();
188 | zoomChanged = true;
189 |
190 | } else {
191 |
192 | console.warn( 'WARNING: OrbitControls.js encountered an unknown camera type - dolly/zoom disabled.' );
193 |
194 | }
195 |
196 | };
197 |
198 | this.update = function() {
199 |
200 | var offset = new THREE.Vector3();
201 |
202 | // so camera.up is the orbit axis
203 | var quat = new THREE.Quaternion().setFromUnitVectors( object.up, new THREE.Vector3( 0, 1, 0 ) );
204 | var quatInverse = quat.clone().inverse();
205 |
206 | var lastPosition = new THREE.Vector3();
207 | var lastQuaternion = new THREE.Quaternion();
208 |
209 | return function () {
210 |
211 | var position = this.object.position;
212 |
213 | offset.copy( position ).sub( this.target );
214 |
215 | // rotate offset to "y-axis-is-up" space
216 | offset.applyQuaternion( quat );
217 |
218 | // angle from z-axis around y-axis
219 |
220 | theta = Math.atan2( offset.x, offset.z );
221 |
222 | // angle from y-axis
223 |
224 | phi = Math.atan2( Math.sqrt( offset.x * offset.x + offset.z * offset.z ), offset.y );
225 |
226 | theta += thetaDelta;
227 | phi += phiDelta;
228 |
229 | // restrict theta to be between desired limits
230 | theta = Math.max( this.minAzimuthAngle, Math.min( this.maxAzimuthAngle, theta ) );
231 |
232 | // restrict phi to be between desired limits
233 | phi = Math.max( this.minPolarAngle, Math.min( this.maxPolarAngle, phi ) );
234 |
235 | // restrict phi to be betwee EPS and PI-EPS
236 | phi = Math.max( EPS, Math.min( Math.PI - EPS, phi ) );
237 |
238 | var radius = offset.length() * scale;
239 |
240 | // restrict radius to be between desired limits
241 | radius = Math.max( this.minDistance, Math.min( this.maxDistance, radius ) );
242 |
243 | // move target to panned location
244 | this.target.add( panOffset );
245 |
246 | offset.x = radius * Math.sin( phi ) * Math.sin( theta );
247 | offset.y = radius * Math.cos( phi );
248 | offset.z = radius * Math.sin( phi ) * Math.cos( theta );
249 |
250 | // rotate offset back to "camera-up-vector-is-up" space
251 | offset.applyQuaternion( quatInverse );
252 |
253 | position.copy( this.target ).add( offset );
254 |
255 | this.object.lookAt( this.target );
256 |
257 | if ( this.enableDamping === true ) {
258 |
259 | thetaDelta *= ( 1 - this.dampingFactor );
260 | phiDelta *= ( 1 - this.dampingFactor );
261 |
262 | } else {
263 |
264 | thetaDelta = 0;
265 | phiDelta = 0;
266 |
267 | }
268 |
269 | scale = 1;
270 | panOffset.set( 0, 0, 0 );
271 |
272 | // update condition is:
273 | // min(camera displacement, camera rotation in radians)^2 > EPS
274 | // using small-angle approximation cos(x/2) = 1 - x^2 / 8
275 |
276 | if ( zoomChanged ||
277 | lastPosition.distanceToSquared( this.object.position ) > EPS ||
278 | 8 * ( 1 - lastQuaternion.dot( this.object.quaternion ) ) > EPS ) {
279 |
280 | lastPosition.copy( this.object.position );
281 | lastQuaternion.copy( this.object.quaternion );
282 | zoomChanged = false;
283 |
284 | return true;
285 |
286 | }
287 |
288 | return false;
289 |
290 | };
291 |
292 | }();
293 |
294 | };
295 |
296 |
297 | // This set of controls performs orbiting, dollying (zooming), and panning. It maintains
298 | // the "up" direction as +Y, unlike the TrackballControls. Touch on tablet and phones is
299 | // supported.
300 | //
301 | // Orbit - left mouse / touch: one finger move
302 | // Zoom - middle mouse, or mousewheel / touch: two finger spread or squish
303 | // Pan - right mouse, or arrow keys / touch: three finter swipe
304 |
305 | THREE.OrbitControls = function ( object, domElement ) {
306 |
307 | var constraint = new OrbitConstraint( object );
308 |
309 | this.domElement = ( domElement !== undefined ) ? domElement : document;
310 |
311 | // API
312 |
313 | Object.defineProperty( this, 'constraint', {
314 |
315 | get: function() {
316 |
317 | return constraint;
318 |
319 | }
320 |
321 | } );
322 |
323 | this.getPolarAngle = function () {
324 |
325 | return constraint.getPolarAngle();
326 |
327 | };
328 |
329 | this.getAzimuthalAngle = function () {
330 |
331 | return constraint.getAzimuthalAngle();
332 |
333 | };
334 |
335 | // Set to false to disable this control
336 | this.enabled = true;
337 |
338 | // center is old, deprecated; use "target" instead
339 | this.center = this.target;
340 |
341 | // This option actually enables dollying in and out; left as "zoom" for
342 | // backwards compatibility.
343 | // Set to false to disable zooming
344 | this.enableZoom = true;
345 | this.zoomSpeed = 1.0;
346 |
347 | // Set to false to disable rotating
348 | this.enableRotate = true;
349 | this.rotateSpeed = 1.0;
350 |
351 | // Set to false to disable panning
352 | this.enablePan = true;
353 | this.keyPanSpeed = 7.0; // pixels moved per arrow key push
354 |
355 | // Set to true to automatically rotate around the target
356 | // If auto-rotate is enabled, you must call controls.update() in your animation loop
357 | this.autoRotate = false;
358 | this.autoRotateSpeed = 2.0; // 30 seconds per round when fps is 60
359 |
360 | // Set to false to disable use of the keys
361 | this.enableKeys = true;
362 |
363 | // The four arrow keys
364 | this.keys = { LEFT: 37, UP: 38, RIGHT: 39, BOTTOM: 40 };
365 |
366 | // Mouse buttons
367 | this.mouseButtons = { ORBIT: THREE.MOUSE.LEFT, ZOOM: THREE.MOUSE.MIDDLE, PAN: THREE.MOUSE.RIGHT };
368 |
369 | ////////////
370 | // internals
371 |
372 | var scope = this;
373 |
374 | var rotateStart = new THREE.Vector2();
375 | var rotateEnd = new THREE.Vector2();
376 | var rotateDelta = new THREE.Vector2();
377 |
378 | var panStart = new THREE.Vector2();
379 | var panEnd = new THREE.Vector2();
380 | var panDelta = new THREE.Vector2();
381 |
382 | var dollyStart = new THREE.Vector2();
383 | var dollyEnd = new THREE.Vector2();
384 | var dollyDelta = new THREE.Vector2();
385 |
386 | var STATE = { NONE : - 1, ROTATE : 0, DOLLY : 1, PAN : 2, TOUCH_ROTATE : 3, TOUCH_DOLLY : 4, TOUCH_PAN : 5 };
387 |
388 | var state = STATE.NONE;
389 |
390 | // for reset
391 |
392 | this.target0 = this.target.clone();
393 | this.position0 = this.object.position.clone();
394 | this.zoom0 = this.object.zoom;
395 |
396 | // events
397 |
398 | var changeEvent = { type: 'change' };
399 | var startEvent = { type: 'start' };
400 | var endEvent = { type: 'end' };
401 |
402 | // pass in x,y of change desired in pixel space,
403 | // right and down are positive
404 | function pan( deltaX, deltaY ) {
405 |
406 | var element = scope.domElement === document ? scope.domElement.body : scope.domElement;
407 |
408 | constraint.pan( deltaX, deltaY, element.clientWidth, element.clientHeight );
409 |
410 | }
411 |
412 | this.update = function () {
413 |
414 | if ( this.autoRotate && state === STATE.NONE ) {
415 |
416 | constraint.rotateLeft( getAutoRotationAngle() );
417 |
418 | }
419 |
420 | if ( constraint.update() === true ) {
421 |
422 | this.dispatchEvent( changeEvent );
423 |
424 | }
425 |
426 | };
427 |
428 | this.reset = function () {
429 |
430 | state = STATE.NONE;
431 |
432 | this.target.copy( this.target0 );
433 | this.object.position.copy( this.position0 );
434 | this.object.zoom = this.zoom0;
435 |
436 | this.object.updateProjectionMatrix();
437 | this.dispatchEvent( changeEvent );
438 |
439 | this.update();
440 |
441 | };
442 |
443 | function getAutoRotationAngle() {
444 |
445 | return 2 * Math.PI / 60 / 60 * scope.autoRotateSpeed;
446 |
447 | }
448 |
449 | function getZoomScale() {
450 |
451 | return Math.pow( 0.95, scope.zoomSpeed );
452 |
453 | }
454 |
455 | function onMouseDown( event ) {
456 |
457 | if ( scope.enabled === false ) return;
458 |
459 | event.preventDefault();
460 |
461 | if ( event.button === scope.mouseButtons.ORBIT ) {
462 |
463 | if ( scope.enableRotate === false ) return;
464 |
465 | state = STATE.ROTATE;
466 |
467 | rotateStart.set( event.clientX, event.clientY );
468 |
469 | } else if ( event.button === scope.mouseButtons.ZOOM ) {
470 |
471 | if ( scope.enableZoom === false ) return;
472 |
473 | state = STATE.DOLLY;
474 |
475 | dollyStart.set( event.clientX, event.clientY );
476 |
477 | } else if ( event.button === scope.mouseButtons.PAN ) {
478 |
479 | if ( scope.enablePan === false ) return;
480 |
481 | state = STATE.PAN;
482 |
483 | panStart.set( event.clientX, event.clientY );
484 |
485 | }
486 |
487 | if ( state !== STATE.NONE ) {
488 |
489 | document.addEventListener( 'mousemove', onMouseMove, false );
490 | document.addEventListener( 'mouseup', onMouseUp, false );
491 | scope.dispatchEvent( startEvent );
492 |
493 | }
494 |
495 | }
496 |
497 | function onMouseMove( event ) {
498 |
499 | if ( scope.enabled === false ) return;
500 |
501 | event.preventDefault();
502 |
503 | var element = scope.domElement === document ? scope.domElement.body : scope.domElement;
504 |
505 | if ( state === STATE.ROTATE ) {
506 |
507 | if ( scope.enableRotate === false ) return;
508 |
509 | rotateEnd.set( event.clientX, event.clientY );
510 | rotateDelta.subVectors( rotateEnd, rotateStart );
511 |
512 | // rotating across whole screen goes 360 degrees around
513 | constraint.rotateLeft( 2 * Math.PI * rotateDelta.x / element.clientWidth * scope.rotateSpeed );
514 |
515 | // rotating up and down along whole screen attempts to go 360, but limited to 180
516 | constraint.rotateUp( 2 * Math.PI * rotateDelta.y / element.clientHeight * scope.rotateSpeed );
517 |
518 | rotateStart.copy( rotateEnd );
519 |
520 | } else if ( state === STATE.DOLLY ) {
521 |
522 | if ( scope.enableZoom === false ) return;
523 |
524 | dollyEnd.set( event.clientX, event.clientY );
525 | dollyDelta.subVectors( dollyEnd, dollyStart );
526 |
527 | if ( dollyDelta.y > 0 ) {
528 |
529 | constraint.dollyIn( getZoomScale() );
530 |
531 | } else if ( dollyDelta.y < 0 ) {
532 |
533 | constraint.dollyOut( getZoomScale() );
534 |
535 | }
536 |
537 | dollyStart.copy( dollyEnd );
538 |
539 | } else if ( state === STATE.PAN ) {
540 |
541 | if ( scope.enablePan === false ) return;
542 |
543 | panEnd.set( event.clientX, event.clientY );
544 | panDelta.subVectors( panEnd, panStart );
545 |
546 | pan( panDelta.x, panDelta.y );
547 |
548 | panStart.copy( panEnd );
549 |
550 | }
551 |
552 | if ( state !== STATE.NONE ) scope.update();
553 |
554 | }
555 |
556 | function onMouseUp( /* event */ ) {
557 |
558 | if ( scope.enabled === false ) return;
559 |
560 | document.removeEventListener( 'mousemove', onMouseMove, false );
561 | document.removeEventListener( 'mouseup', onMouseUp, false );
562 | scope.dispatchEvent( endEvent );
563 | state = STATE.NONE;
564 |
565 | }
566 |
567 | function onMouseWheel( event ) {
568 |
569 | if ( scope.enabled === false || scope.enableZoom === false || state !== STATE.NONE ) return;
570 |
571 | event.preventDefault();
572 | event.stopPropagation();
573 |
574 | var delta = 0;
575 |
576 | if ( event.wheelDelta !== undefined ) {
577 |
578 | // WebKit / Opera / Explorer 9
579 |
580 | delta = event.wheelDelta;
581 |
582 | } else if ( event.detail !== undefined ) {
583 |
584 | // Firefox
585 |
586 | delta = - event.detail;
587 |
588 | }
589 |
590 | if ( delta > 0 ) {
591 |
592 | constraint.dollyOut( getZoomScale() );
593 |
594 | } else if ( delta < 0 ) {
595 |
596 | constraint.dollyIn( getZoomScale() );
597 |
598 | }
599 |
600 | scope.update();
601 | scope.dispatchEvent( startEvent );
602 | scope.dispatchEvent( endEvent );
603 |
604 | }
605 |
606 | function onKeyDown( event ) {
607 |
608 | if ( scope.enabled === false || scope.enableKeys === false || scope.enablePan === false ) return;
609 |
610 | switch ( event.keyCode ) {
611 |
612 | case scope.keys.UP:
613 | pan( 0, scope.keyPanSpeed );
614 | scope.update();
615 | break;
616 |
617 | case scope.keys.BOTTOM:
618 | pan( 0, - scope.keyPanSpeed );
619 | scope.update();
620 | break;
621 |
622 | case scope.keys.LEFT:
623 | pan( scope.keyPanSpeed, 0 );
624 | scope.update();
625 | break;
626 |
627 | case scope.keys.RIGHT:
628 | pan( - scope.keyPanSpeed, 0 );
629 | scope.update();
630 | break;
631 |
632 | }
633 |
634 | }
635 |
636 | function touchstart( event ) {
637 |
638 | if ( scope.enabled === false ) return;
639 |
640 | switch ( event.touches.length ) {
641 |
642 | case 1: // one-fingered touch: rotate
643 |
644 | if ( scope.enableRotate === false ) return;
645 |
646 | state = STATE.TOUCH_ROTATE;
647 |
648 | rotateStart.set( event.touches[ 0 ].pageX, event.touches[ 0 ].pageY );
649 | break;
650 |
651 | case 2: // two-fingered touch: dolly
652 |
653 | if ( scope.enableZoom === false ) return;
654 |
655 | state = STATE.TOUCH_DOLLY;
656 |
657 | var dx = event.touches[ 0 ].pageX - event.touches[ 1 ].pageX;
658 | var dy = event.touches[ 0 ].pageY - event.touches[ 1 ].pageY;
659 | var distance = Math.sqrt( dx * dx + dy * dy );
660 | dollyStart.set( 0, distance );
661 | break;
662 |
663 | case 3: // three-fingered touch: pan
664 |
665 | if ( scope.enablePan === false ) return;
666 |
667 | state = STATE.TOUCH_PAN;
668 |
669 | panStart.set( event.touches[ 0 ].pageX, event.touches[ 0 ].pageY );
670 | break;
671 |
672 | default:
673 |
674 | state = STATE.NONE;
675 |
676 | }
677 |
678 | if ( state !== STATE.NONE ) scope.dispatchEvent( startEvent );
679 |
680 | }
681 |
682 | function touchmove( event ) {
683 |
684 | if ( scope.enabled === false ) return;
685 |
686 | event.preventDefault();
687 | event.stopPropagation();
688 |
689 | var element = scope.domElement === document ? scope.domElement.body : scope.domElement;
690 |
691 | switch ( event.touches.length ) {
692 |
693 | case 1: // one-fingered touch: rotate
694 |
695 | if ( scope.enableRotate === false ) return;
696 | if ( state !== STATE.TOUCH_ROTATE ) return;
697 |
698 | rotateEnd.set( event.touches[ 0 ].pageX, event.touches[ 0 ].pageY );
699 | rotateDelta.subVectors( rotateEnd, rotateStart );
700 |
701 | // rotating across whole screen goes 360 degrees around
702 | constraint.rotateLeft( 2 * Math.PI * rotateDelta.x / element.clientWidth * scope.rotateSpeed );
703 | // rotating up and down along whole screen attempts to go 360, but limited to 180
704 | constraint.rotateUp( 2 * Math.PI * rotateDelta.y / element.clientHeight * scope.rotateSpeed );
705 |
706 | rotateStart.copy( rotateEnd );
707 |
708 | scope.update();
709 | break;
710 |
711 | case 2: // two-fingered touch: dolly
712 |
713 | if ( scope.enableZoom === false ) return;
714 | if ( state !== STATE.TOUCH_DOLLY ) return;
715 |
716 | var dx = event.touches[ 0 ].pageX - event.touches[ 1 ].pageX;
717 | var dy = event.touches[ 0 ].pageY - event.touches[ 1 ].pageY;
718 | var distance = Math.sqrt( dx * dx + dy * dy );
719 |
720 | dollyEnd.set( 0, distance );
721 | dollyDelta.subVectors( dollyEnd, dollyStart );
722 |
723 | if ( dollyDelta.y > 0 ) {
724 |
725 | constraint.dollyOut( getZoomScale() );
726 |
727 | } else if ( dollyDelta.y < 0 ) {
728 |
729 | constraint.dollyIn( getZoomScale() );
730 |
731 | }
732 |
733 | dollyStart.copy( dollyEnd );
734 |
735 | scope.update();
736 | break;
737 |
738 | case 3: // three-fingered touch: pan
739 |
740 | if ( scope.enablePan === false ) return;
741 | if ( state !== STATE.TOUCH_PAN ) return;
742 |
743 | panEnd.set( event.touches[ 0 ].pageX, event.touches[ 0 ].pageY );
744 | panDelta.subVectors( panEnd, panStart );
745 |
746 | pan( panDelta.x, panDelta.y );
747 |
748 | panStart.copy( panEnd );
749 |
750 | scope.update();
751 | break;
752 |
753 | default:
754 |
755 | state = STATE.NONE;
756 |
757 | }
758 |
759 | }
760 |
761 | function touchend( /* event */ ) {
762 |
763 | if ( scope.enabled === false ) return;
764 |
765 | scope.dispatchEvent( endEvent );
766 | state = STATE.NONE;
767 |
768 | }
769 |
770 | function contextmenu( event ) {
771 |
772 | event.preventDefault();
773 |
774 | }
775 |
776 | this.dispose = function() {
777 |
778 | this.domElement.removeEventListener( 'contextmenu', contextmenu, false );
779 | this.domElement.removeEventListener( 'mousedown', onMouseDown, false );
780 | this.domElement.removeEventListener( 'mousewheel', onMouseWheel, false );
781 | this.domElement.removeEventListener( 'MozMousePixelScroll', onMouseWheel, false ); // firefox
782 |
783 | this.domElement.removeEventListener( 'touchstart', touchstart, false );
784 | this.domElement.removeEventListener( 'touchend', touchend, false );
785 | this.domElement.removeEventListener( 'touchmove', touchmove, false );
786 |
787 | document.removeEventListener( 'mousemove', onMouseMove, false );
788 | document.removeEventListener( 'mouseup', onMouseUp, false );
789 |
790 | window.removeEventListener( 'keydown', onKeyDown, false );
791 |
792 | }
793 |
794 | this.domElement.addEventListener( 'contextmenu', contextmenu, false );
795 |
796 | this.domElement.addEventListener( 'mousedown', onMouseDown, false );
797 | this.domElement.addEventListener( 'mousewheel', onMouseWheel, false );
798 | this.domElement.addEventListener( 'MozMousePixelScroll', onMouseWheel, false ); // firefox
799 |
800 | this.domElement.addEventListener( 'touchstart', touchstart, false );
801 | this.domElement.addEventListener( 'touchend', touchend, false );
802 | this.domElement.addEventListener( 'touchmove', touchmove, false );
803 |
804 | window.addEventListener( 'keydown', onKeyDown, false );
805 |
806 | // force an update at start
807 | this.update();
808 |
809 | };
810 |
811 | THREE.OrbitControls.prototype = Object.create( THREE.EventDispatcher.prototype );
812 | THREE.OrbitControls.prototype.constructor = THREE.OrbitControls;
813 |
814 | Object.defineProperties( THREE.OrbitControls.prototype, {
815 |
816 | object: {
817 |
818 | get: function () {
819 |
820 | return this.constraint.object;
821 |
822 | }
823 |
824 | },
825 |
826 | target: {
827 |
828 | get: function () {
829 |
830 | return this.constraint.target;
831 |
832 | },
833 |
834 | set: function ( value ) {
835 |
836 | console.warn( 'THREE.OrbitControls: target is now immutable. Use target.set() instead.' );
837 | this.constraint.target.copy( value );
838 |
839 | }
840 |
841 | },
842 |
843 | minDistance : {
844 |
845 | get: function () {
846 |
847 | return this.constraint.minDistance;
848 |
849 | },
850 |
851 | set: function ( value ) {
852 |
853 | this.constraint.minDistance = value;
854 |
855 | }
856 |
857 | },
858 |
859 | maxDistance : {
860 |
861 | get: function () {
862 |
863 | return this.constraint.maxDistance;
864 |
865 | },
866 |
867 | set: function ( value ) {
868 |
869 | this.constraint.maxDistance = value;
870 |
871 | }
872 |
873 | },
874 |
875 | minZoom : {
876 |
877 | get: function () {
878 |
879 | return this.constraint.minZoom;
880 |
881 | },
882 |
883 | set: function ( value ) {
884 |
885 | this.constraint.minZoom = value;
886 |
887 | }
888 |
889 | },
890 |
891 | maxZoom : {
892 |
893 | get: function () {
894 |
895 | return this.constraint.maxZoom;
896 |
897 | },
898 |
899 | set: function ( value ) {
900 |
901 | this.constraint.maxZoom = value;
902 |
903 | }
904 |
905 | },
906 |
907 | minPolarAngle : {
908 |
909 | get: function () {
910 |
911 | return this.constraint.minPolarAngle;
912 |
913 | },
914 |
915 | set: function ( value ) {
916 |
917 | this.constraint.minPolarAngle = value;
918 |
919 | }
920 |
921 | },
922 |
923 | maxPolarAngle : {
924 |
925 | get: function () {
926 |
927 | return this.constraint.maxPolarAngle;
928 |
929 | },
930 |
931 | set: function ( value ) {
932 |
933 | this.constraint.maxPolarAngle = value;
934 |
935 | }
936 |
937 | },
938 |
939 | minAzimuthAngle : {
940 |
941 | get: function () {
942 |
943 | return this.constraint.minAzimuthAngle;
944 |
945 | },
946 |
947 | set: function ( value ) {
948 |
949 | this.constraint.minAzimuthAngle = value;
950 |
951 | }
952 |
953 | },
954 |
955 | maxAzimuthAngle : {
956 |
957 | get: function () {
958 |
959 | return this.constraint.maxAzimuthAngle;
960 |
961 | },
962 |
963 | set: function ( value ) {
964 |
965 | this.constraint.maxAzimuthAngle = value;
966 |
967 | }
968 |
969 | },
970 |
971 | enableDamping : {
972 |
973 | get: function () {
974 |
975 | return this.constraint.enableDamping;
976 |
977 | },
978 |
979 | set: function ( value ) {
980 |
981 | this.constraint.enableDamping = value;
982 |
983 | }
984 |
985 | },
986 |
987 | dampingFactor : {
988 |
989 | get: function () {
990 |
991 | return this.constraint.dampingFactor;
992 |
993 | },
994 |
995 | set: function ( value ) {
996 |
997 | this.constraint.dampingFactor = value;
998 |
999 | }
1000 |
1001 | },
1002 |
1003 | // backward compatibility
1004 |
1005 | noZoom: {
1006 |
1007 | get: function () {
1008 |
1009 | console.warn( 'THREE.OrbitControls: .noZoom has been deprecated. Use .enableZoom instead.' );
1010 | return ! this.enableZoom;
1011 |
1012 | },
1013 |
1014 | set: function ( value ) {
1015 |
1016 | console.warn( 'THREE.OrbitControls: .noZoom has been deprecated. Use .enableZoom instead.' );
1017 | this.enableZoom = ! value;
1018 |
1019 | }
1020 |
1021 | },
1022 |
1023 | noRotate: {
1024 |
1025 | get: function () {
1026 |
1027 | console.warn( 'THREE.OrbitControls: .noRotate has been deprecated. Use .enableRotate instead.' );
1028 | return ! this.enableRotate;
1029 |
1030 | },
1031 |
1032 | set: function ( value ) {
1033 |
1034 | console.warn( 'THREE.OrbitControls: .noRotate has been deprecated. Use .enableRotate instead.' );
1035 | this.enableRotate = ! value;
1036 |
1037 | }
1038 |
1039 | },
1040 |
1041 | noPan: {
1042 |
1043 | get: function () {
1044 |
1045 | console.warn( 'THREE.OrbitControls: .noPan has been deprecated. Use .enablePan instead.' );
1046 | return ! this.enablePan;
1047 |
1048 | },
1049 |
1050 | set: function ( value ) {
1051 |
1052 | console.warn( 'THREE.OrbitControls: .noPan has been deprecated. Use .enablePan instead.' );
1053 | this.enablePan = ! value;
1054 |
1055 | }
1056 |
1057 | },
1058 |
1059 | noKeys: {
1060 |
1061 | get: function () {
1062 |
1063 | console.warn( 'THREE.OrbitControls: .noKeys has been deprecated. Use .enableKeys instead.' );
1064 | return ! this.enableKeys;
1065 |
1066 | },
1067 |
1068 | set: function ( value ) {
1069 |
1070 | console.warn( 'THREE.OrbitControls: .noKeys has been deprecated. Use .enableKeys instead.' );
1071 | this.enableKeys = ! value;
1072 |
1073 | }
1074 |
1075 | },
1076 |
1077 | staticMoving : {
1078 |
1079 | get: function () {
1080 |
1081 | console.warn( 'THREE.OrbitControls: .staticMoving has been deprecated. Use .enableDamping instead.' );
1082 | return ! this.constraint.enableDamping;
1083 |
1084 | },
1085 |
1086 | set: function ( value ) {
1087 |
1088 | console.warn( 'THREE.OrbitControls: .staticMoving has been deprecated. Use .enableDamping instead.' );
1089 | this.constraint.enableDamping = ! value;
1090 |
1091 | }
1092 |
1093 | },
1094 |
1095 | dynamicDampingFactor : {
1096 |
1097 | get: function () {
1098 |
1099 | console.warn( 'THREE.OrbitControls: .dynamicDampingFactor has been renamed. Use .dampingFactor instead.' );
1100 | return this.constraint.dampingFactor;
1101 |
1102 | },
1103 |
1104 | set: function ( value ) {
1105 |
1106 | console.warn( 'THREE.OrbitControls: .dynamicDampingFactor has been renamed. Use .dampingFactor instead.' );
1107 | this.constraint.dampingFactor = value;
1108 |
1109 | }
1110 |
1111 | }
1112 |
1113 | } );
1114 |
1115 | }() );
1116 |
--------------------------------------------------------------------------------