├── .gitignore
├── README.md
├── dst
├── assets
│ ├── css
│ │ └── styles.css
│ ├── glsl
│ │ ├── output_box.frag
│ │ ├── output_box.vert
│ │ ├── output_floor.frag
│ │ ├── output_floor.vert
│ │ └── shadow.frag
│ └── js
│ │ └── main.min.js
├── fb.png
├── index.html
└── tw.png
├── gulp
├── config.js
└── tasks
│ ├── browsersync.js
│ ├── glsl.js
│ ├── html.js
│ ├── imagemin.js
│ ├── javascript.js
│ └── sass.js
├── gulpfile.js
├── package.json
├── screenshot1.png
├── screenshot2.png
├── screenshot3.png
└── src
├── assets
├── css
│ └── styles.scss
├── glsl
│ ├── output_box.frag
│ ├── output_box.vert
│ ├── output_floor.frag
│ ├── output_floor.vert
│ ├── shadow.frag
│ └── utils
│ │ ├── blendOverlay.glsl
│ │ ├── blendSoftlight.glsl
│ │ ├── getShadow.glsl
│ │ ├── noise2D.glsl
│ │ ├── noise3D.glsl
│ │ ├── noise4D.glsl
│ │ └── random.glsl
└── js
│ ├── lib-head
│ ├── modernizr-custom.js
│ └── useragnt-all.min.js
│ ├── lib
│ ├── CustomEase.min.js
│ ├── EasePack.min.js
│ ├── GPUComputationRenderer.js
│ ├── OrbitControls.js
│ ├── ShadowMapViewer.js
│ ├── TweenMax.min.js
│ └── UnpackDepthRGBAShader.js
│ ├── main.js
│ └── module
│ ├── controls.js
│ ├── loading.js
│ ├── objs.js
│ ├── resize-watch.js
│ ├── toonManager.js
│ └── webgl.js
└── index.html
/.gitignore:
--------------------------------------------------------------------------------
1 | # MacOS
2 | .DS_Store
3 | .AppleDouble
4 | .LSOverride
5 |
6 |
7 | # Thumbnails
8 | ._*
9 |
10 | # Files that might appear on external disk
11 | .Spotlight-V100
12 | .Trashes
13 |
14 |
15 | # node.js
16 | lib-cov
17 | *.seed
18 | *.log
19 | *.csv
20 | *.dat
21 | *.out
22 | *.pid
23 | *.gz
24 | pids
25 | logs
26 | results
27 | npm-debug.log
28 | node_modules
29 |
30 | # Sass
31 | .sass-cache
32 |
33 | # SublimeText
34 | *.sublime-project
35 | *.sublime-workspace
36 | sftp-config.json
37 |
38 | #vim
39 | .*.s[a-w][a-z]
40 | *.un~
41 | Session.vim
42 | .netrwhist
43 | *~
44 |
45 | .idea
46 | .htaccess
47 | .htpasswd
48 | pull.php
49 |
50 | #dev
51 | #dst
52 | build
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Halftone Effect
2 | * [demo](https://mnmxmx.github.io/halftone-effect/dst/)
3 |
4 | [](./screenshot1.png)
5 | [
](./screenshot2.png)
6 | [
](./screenshot3.png)
7 |
8 | ## Resources
9 | * [halftone(in Japanese)](https://wgld.org/d/webgl/w076.html)
10 | * [line shading(in Japanese)](https://wgld.org/d/webgl/w077.html)
11 |
12 | ## Usage
13 | * Clone repository
14 | * Install Node.js
15 | * Run following commands
16 | ```
17 | npm install
18 | npm start
19 | ```
20 |
21 |
--------------------------------------------------------------------------------
/dst/assets/css/styles.css:
--------------------------------------------------------------------------------
1 | body {
2 | overflow: hidden;
3 | margin: 0;
4 | }
5 |
--------------------------------------------------------------------------------
/dst/assets/glsl/output_box.frag:
--------------------------------------------------------------------------------
1 | #define GLSLIFY 1
2 | uniform float uTick;
3 | uniform vec3 uDirLightPos;
4 | uniform float uDotScale;
5 | uniform float uLineScale;
6 | uniform int uTonePattern;
7 |
8 | uniform vec3 uFogColor;
9 |
10 | varying vec3 vPos;
11 | varying vec3 vNormal;
12 |
13 | varying vec3 vColor;
14 | varying vec3 vToneColor;
15 |
16 | varying float vFogFactor;
17 |
18 | float bias;
19 | uniform sampler2D shadowMap;
20 | uniform vec2 shadowMapSize;
21 |
22 | varying vec4 vShadowCoord;
23 |
24 | float unpackDepth( const in vec4 rgba_depth ) {
25 | const vec4 bit_shift = vec4(1.0/(256.0*256.0*256.0), 1.0/(256.0*256.0), 1.0/256.0, 1.0);
26 | return dot(rgba_depth, bit_shift);
27 | }
28 |
29 | float sampleVisibility( vec3 coord ) {
30 | return step( coord.z, unpackDepth( texture2D( shadowMap, coord.xy ) ) + bias );
31 | }
32 |
33 | float getShadow(vec3 normal, vec3 lightPos, vec4 _shadowCoord){
34 | bias = 0.0;//max(0.05 * (1.0 - dot(normal, lightPos)), 0.001);
35 |
36 | float shadow = 0.0;
37 | vec3 shadowCoord = _shadowCoord.xyz / _shadowCoord.w;
38 |
39 | float step = 1.0;
40 |
41 | vec2 inc = vec2( step ) / shadowMapSize;
42 |
43 | shadow += sampleVisibility( shadowCoord + vec3( -inc.x, -inc.y, 0. ) );
44 | shadow += sampleVisibility( shadowCoord + vec3( 0., -inc.y, 0. ) );
45 | shadow += sampleVisibility( shadowCoord + vec3( inc.x, -inc.y, 0. ) );
46 | shadow += sampleVisibility( shadowCoord + vec3( -inc.x, 0., 0. ) );
47 | shadow += sampleVisibility( shadowCoord + vec3( -inc.x, inc.y, 0. ) );
48 | shadow += sampleVisibility( shadowCoord + vec3( 0., inc.y, 0. ) );
49 | shadow += sampleVisibility( shadowCoord + vec3( inc.x, inc.y, 0. ) );
50 | shadow += sampleVisibility( shadowCoord + vec3( inc.x, 0., 0. ) );
51 | shadow += sampleVisibility( shadowCoord + vec3( 0., 0, 0. ) );
52 | shadow /= 9.;
53 |
54 | return shadow;
55 | }
56 |
57 | float blendSoftLight(float base, float blend) {
58 | return (blend < 0.5)?( 2.0 * base * blend + base * base * (1.0-2.0*blend)):(sqrt(base)*(2.0*blend-1.0)+2.0*base*(1.0-blend));
59 | }
60 |
61 | vec3 blendSoftLight(vec3 base, vec3 blend) {
62 | return vec3(blendSoftLight(base.r,blend.r),blendSoftLight(base.g,blend.g),blendSoftLight(base.b,blend.b));
63 | }
64 |
65 | vec3 blendSoftLight(vec4 base, vec4 blend) {
66 | return base.rgb * (1.0 - blend.a) + blend.a * (blendSoftLight(base.rgb, blend.rgb) * base.a + blend.rgb * (1.0 - base.a));
67 | }
68 | float blendOverlay(float base, float blend) {
69 | return base<0.5?(2.0*base*blend):(1.0-2.0*(1.0-base)*(1.0-blend));
70 | }
71 |
72 | vec3 blendOverlay(vec3 base, vec3 blend) {
73 | return vec3(blendOverlay(base.r,blend.r),blendOverlay(base.g,blend.g),blendOverlay(base.b,blend.b));
74 | }
75 |
76 | vec3 blendOverlay(vec3 base, vec3 blend, float opacity) {
77 | return (blendOverlay(base, blend) * opacity + base * (1.0 - opacity));
78 | }
79 |
80 | const float PI = 3.1415926;
81 |
82 | // hemisphere ground color
83 | const vec3 hemiLight_g = vec3(1.0, 0.96, 0.8);
84 |
85 | // hemisphere sky color
86 | const vec3 hemiLight_s_1 = vec3(0.9,0.8,0.6);
87 | const vec3 hemiLight_s_2 = vec3(0.9,0.6,0.7);
88 |
89 | const vec3 hemiLightPos_1 = vec3(-100.0, -100.0, 100.0);
90 | const vec3 hemiLightPos_2 = vec3(-100.0, 100.0, -100.0);
91 |
92 | // directional light color
93 | const vec3 dirLight_1 = vec3(1.0);
94 |
95 | vec3 calcIrradiance_hemi(vec3 newNormal, vec3 lightPos, vec3 grd, vec3 sky){
96 | float dotNL = clamp(dot(newNormal, normalize(lightPos)), 0.0, 1.0);
97 |
98 | return mix(grd, sky, dotNL);
99 | }
100 |
101 | vec3 calcIrradiance_dir(vec3 normal, vec3 lightPos, vec3 light){
102 | float dotNL = dot(normal, normalize(lightPos));
103 |
104 | return light * max(dotNL, 0.0);
105 | }
106 |
107 | float getScreenToneEffect(float dotDiffuse){
108 |
109 | vec2 v_dot;
110 | vec2 v_line;
111 |
112 | float f_dot;
113 | float f_line;
114 | float g_line;
115 |
116 | float result;
117 |
118 | if(uTonePattern == 1){
119 | v_dot = gl_FragCoord.xy * uDotScale;
120 | f_dot = max(sin(v_dot.x) * cos(v_dot.y) * 1.5, 0.0);
121 |
122 | if(dotDiffuse > 0.6){
123 | result = 1.0;
124 | }else if(dotDiffuse > 0.2){
125 | result = 1.0 - (pow(f_dot, 1.0));
126 | }else{
127 | result = pow(f_dot, 2.0);
128 | }
129 |
130 | } else if(uTonePattern == 2){
131 | v_line = gl_FragCoord.xy * uLineScale;
132 | f_line = max(sin(v_line.x + v_line.y), 0.0);
133 | g_line = max(sin(v_line.x - v_line.y), 0.0);
134 |
135 | if(dotDiffuse > 0.6){
136 | result = 0.8;
137 | }else if(dotDiffuse > 0.2){
138 | result = 1.0 - pow(f_line, 2.0);
139 | }else{
140 | result = 1.0 - (pow(f_line, 2.0) + pow(g_line, 2.0));
141 | }
142 | }
143 |
144 | result = clamp(result, 0.0, 1.0);
145 |
146 | return result;
147 | }
148 |
149 | void main(){
150 | vec3 dirColor = vec3(0.0);
151 |
152 | float dirColorRatio = 0.2;
153 | dirColor += calcIrradiance_dir(vNormal, uDirLightPos, dirLight_1) * dirColorRatio;
154 |
155 | vec3 hemiColor = vec3(0.0);
156 | hemiColor += calcIrradiance_hemi(vNormal, hemiLightPos_1, hemiLight_g, hemiLight_s_1) * (0.7 - dirColorRatio) * 0.5;
157 | hemiColor += calcIrradiance_hemi(vNormal, hemiLightPos_2, hemiLight_g, hemiLight_s_2) * (0.7 - dirColorRatio) * 0.5;
158 |
159 | float shadow = getShadow(vNormal, uDirLightPos, vShadowCoord);
160 |
161 | vec3 _color = vColor * 0.8;
162 | vec3 ambient = _color;
163 |
164 | vec3 color = (ambient + shadow * dirColor) * (1.0 + hemiColor);
165 |
166 | // half-tone effect
167 | float dotDiffuse = clamp(dot(vNormal, normalize(uDirLightPos)), 0.0, 1.0);
168 | dotDiffuse *= shadow;
169 |
170 | float f = getScreenToneEffect(dotDiffuse);
171 |
172 | vec3 dotColor = vToneColor;//blendOverlay(color, vToneColor);
173 |
174 | color = mix(dotColor, color, f);
175 | color = mix(uFogColor, color, min(1.0, vFogFactor));
176 |
177 | gl_FragColor = vec4(color, 1.0);
178 | }
--------------------------------------------------------------------------------
/dst/assets/glsl/output_box.vert:
--------------------------------------------------------------------------------
1 | #define GLSLIFY 1
2 | // #pragma glslify: snoise = require("./noise2D")
3 | attribute float aNum;
4 | attribute float aRandom;
5 |
6 | attribute float aColorNum;
7 |
8 | attribute vec3 aDefTranslate;
9 | attribute vec3 aTranslate;
10 | attribute vec3 aScale;
11 | attribute float aRotateY;
12 |
13 | uniform float uRange;
14 |
15 | uniform float uTick;
16 | uniform vec3 uTileSize;
17 |
18 | uniform mat4 shadowP;
19 | uniform mat4 shadowV;
20 | uniform vec3 uDirLightPos;
21 |
22 | uniform vec3 uEyePosition;
23 | uniform float uFogNear;
24 | uniform float uFogFar;
25 | uniform float uFogStart;
26 | uniform float uFogEnd;
27 |
28 | uniform float uOffsetHeight;
29 |
30 | uniform vec3 uColorArray[2];
31 | uniform vec3 uToneColorArray[2];
32 |
33 | varying vec3 vColor;
34 | varying vec3 vToneColor;
35 | varying vec3 vNormal;
36 |
37 | varying vec3 vPos;
38 | varying vec4 vShadowCoord;
39 | varying float vFogFactor;
40 |
41 | const float PI = 3.1415926;
42 |
43 | const mat4 biasMatrix = mat4(
44 | 0.5, 0.0, 0.0, 0.0,
45 | 0.0, 0.5, 0.0, 0.0,
46 | 0.0, 0.0, 0.5, 0.0,
47 | 0.5, 0.5, 0.5, 1.0
48 | );
49 |
50 | mat2 calcRotate2D(float _radian){
51 | float _sin = sin(_radian);
52 | float _cos = cos(_radian);
53 | return mat2(_cos, _sin, -_sin, _cos);
54 | }
55 |
56 | float parabola( float x) {
57 | return 4.0 * (1.0 - x) * x;
58 | }
59 |
60 | mat3 calcLookAtMatrix(vec3 vector, float roll) {
61 | vec3 rr = vec3(sin(roll), cos(roll), 0.0);
62 | vec3 ww = normalize(vector);
63 | vec3 uu = normalize(cross(ww, rr));
64 | vec3 vv = normalize(cross(uu, ww));
65 |
66 | return mat3(uu, ww, vv);
67 | }
68 |
69 | void main(){
70 |
71 | vec3 pos = (position + vec3(0.0, uTileSize.y / 2.0, 0.0)) * aScale * (1.0 + aRandom - 0.5);
72 | pos.xz = calcRotate2D(aRotateY * 2.0 * PI) * pos.xz;
73 | pos += aTranslate + aDefTranslate + vec3(0.0, uOffsetHeight, 0.0);
74 |
75 | vec4 worldPosition = modelMatrix * vec4(pos, 1.0);
76 |
77 | gl_Position = projectionMatrix * viewMatrix * worldPosition;
78 |
79 | vPos = worldPosition.xyz;
80 | vColor = uColorArray[int(aColorNum)];
81 | vToneColor = uToneColorArray[int(aColorNum)];
82 |
83 | vNormal = normal;
84 | vNormal.xz = calcRotate2D(aRotateY * 2.0 * PI) * vNormal.xz;
85 |
86 | // vDotDiffuse = dot(normal, uDirLightPos);
87 |
88 | float fogLinerDepth = 1.0 / (uFogFar - uFogNear);
89 |
90 | float fogLinerPos = length(uEyePosition - pos) * fogLinerDepth;
91 | vFogFactor = clamp((uFogEnd - fogLinerPos) / (uFogEnd - uFogStart), 0.0, 1.0);
92 |
93 | vShadowCoord = biasMatrix * shadowP * shadowV * worldPosition;
94 | }
--------------------------------------------------------------------------------
/dst/assets/glsl/output_floor.frag:
--------------------------------------------------------------------------------
1 | #define GLSLIFY 1
2 | uniform float uTick;
3 | uniform vec3 uDirLightPos;
4 | uniform float uDotScale;
5 | uniform float uLineScale;
6 | uniform vec3 uFloorToneColor;
7 |
8 | uniform int uTonePattern;
9 |
10 | varying vec3 vNormal;
11 |
12 | uniform vec3 uFogColor;
13 | varying float vFogFactor;
14 |
15 | const float PI = 3.1415926;
16 |
17 | float bias;
18 | uniform sampler2D shadowMap;
19 | uniform vec2 shadowMapSize;
20 |
21 | varying vec4 vShadowCoord;
22 |
23 | float unpackDepth( const in vec4 rgba_depth ) {
24 | const vec4 bit_shift = vec4(1.0/(256.0*256.0*256.0), 1.0/(256.0*256.0), 1.0/256.0, 1.0);
25 | return dot(rgba_depth, bit_shift);
26 | }
27 |
28 | float sampleVisibility( vec3 coord ) {
29 | return step( coord.z, unpackDepth( texture2D( shadowMap, coord.xy ) ) + bias );
30 | }
31 |
32 | float getShadow(vec3 normal, vec3 lightPos, vec4 _shadowCoord){
33 | bias = 0.0;//max(0.05 * (1.0 - dot(normal, lightPos)), 0.001);
34 |
35 | float shadow = 0.0;
36 | vec3 shadowCoord = _shadowCoord.xyz / _shadowCoord.w;
37 |
38 | float step = 1.0;
39 |
40 | vec2 inc = vec2( step ) / shadowMapSize;
41 |
42 | shadow += sampleVisibility( shadowCoord + vec3( -inc.x, -inc.y, 0. ) );
43 | shadow += sampleVisibility( shadowCoord + vec3( 0., -inc.y, 0. ) );
44 | shadow += sampleVisibility( shadowCoord + vec3( inc.x, -inc.y, 0. ) );
45 | shadow += sampleVisibility( shadowCoord + vec3( -inc.x, 0., 0. ) );
46 | shadow += sampleVisibility( shadowCoord + vec3( -inc.x, inc.y, 0. ) );
47 | shadow += sampleVisibility( shadowCoord + vec3( 0., inc.y, 0. ) );
48 | shadow += sampleVisibility( shadowCoord + vec3( inc.x, inc.y, 0. ) );
49 | shadow += sampleVisibility( shadowCoord + vec3( inc.x, 0., 0. ) );
50 | shadow += sampleVisibility( shadowCoord + vec3( 0., 0, 0. ) );
51 | shadow /= 9.;
52 |
53 | return shadow;
54 | }
55 |
56 | float getHalfToneEffect(float dotDiffuse){
57 | vec2 v_dot;
58 | vec2 v_line;
59 |
60 | float f_dot;
61 | float f_line;
62 | float g_line;
63 |
64 | float result;
65 |
66 | if(uTonePattern == 1){
67 | v_dot = gl_FragCoord.xy * uDotScale;
68 | f_dot = max(sin(v_dot.x) * cos(v_dot.y) * 1.5, 0.0);
69 |
70 | if(dotDiffuse > 0.2){
71 | result = 1.0;
72 | } else{
73 | result = f_dot;
74 | }
75 |
76 | } else if(uTonePattern == 2){
77 | v_line = gl_FragCoord.xy * uLineScale;
78 | f_line = max(sin(v_line.x + v_line.y), 0.0);
79 | g_line = max(sin(v_line.x - v_line.y), 0.0);
80 |
81 | if(dotDiffuse > 0.2){
82 | result = 1.0;
83 | } else{
84 | result = (pow(f_line, 2.0) + pow(g_line, 2.0));
85 | }
86 | }
87 |
88 |
89 |
90 |
91 | result = min(1.0, result);
92 |
93 | return result;
94 | }
95 |
96 | void main(){
97 | float shadow = getShadow(vNormal, uDirLightPos, vShadowCoord);
98 |
99 | // half tone effect
100 | float dotDiffuse = clamp(dot(vNormal, normalize(uDirLightPos)), 0.0, 1.0);
101 | dotDiffuse *= shadow;
102 |
103 | float f = getHalfToneEffect(dotDiffuse);
104 |
105 | vec3 color = uFogColor;
106 |
107 | color = mix(uFloorToneColor, color, f);
108 | color = mix(uFogColor, color, min(1.0, vFogFactor));
109 |
110 | gl_FragColor = vec4(color, 1.0);
111 | }
--------------------------------------------------------------------------------
/dst/assets/glsl/output_floor.vert:
--------------------------------------------------------------------------------
1 | #define GLSLIFY 1
2 | uniform mat4 shadowP;
3 | uniform mat4 shadowV;
4 | uniform vec3 uDirLightPos;
5 |
6 | uniform vec3 uEyePosition;
7 | uniform float uFogNear;
8 | uniform float uFogFar;
9 | uniform float uFogStart;
10 | uniform float uFogEnd;
11 |
12 | uniform float uOffsetHeight;
13 |
14 | varying vec3 vNormal;
15 |
16 | varying vec4 vShadowCoord;
17 | varying float vFogFactor;
18 |
19 | const float PI = 3.1415926;
20 |
21 | const mat4 biasMatrix = mat4(
22 | 0.5, 0.0, 0.0, 0.0,
23 | 0.0, 0.5, 0.0, 0.0,
24 | 0.0, 0.0, 0.5, 0.0,
25 | 0.5, 0.5, 0.5, 1.0
26 | );
27 |
28 | void main(){
29 | vec3 pos = position + vec3(0.0, uOffsetHeight, 0.0);
30 |
31 | vec4 worldPosition = modelMatrix * vec4(pos, 1.0);
32 | gl_Position = projectionMatrix * viewMatrix * worldPosition;
33 |
34 | float fogLinerDepth = 1.0 / (uFogFar - uFogNear);
35 |
36 | float fogLinerPos = length(uEyePosition - pos) * fogLinerDepth;
37 | vFogFactor = clamp((uFogEnd - fogLinerPos) / (uFogEnd - uFogStart), 0.0, 1.0);
38 |
39 | vNormal = normal;
40 |
41 | // vDotDiffuse = dot(normal, uDirLightPos) * 0.5 + 0.5;
42 |
43 | vShadowCoord = biasMatrix * shadowP * shadowV * worldPosition;
44 | }
--------------------------------------------------------------------------------
/dst/assets/glsl/shadow.frag:
--------------------------------------------------------------------------------
1 | precision highp float;
2 | #define GLSLIFY 1
3 | vec4 packDepth(const in float depth) {
4 | const vec4 bit_shift = vec4(256.0*256.0*256.0, 256.0*256.0, 256.0, 1.0);
5 | const vec4 bit_mask = vec4(0.0, 1.0/256.0, 1.0/256.0, 1.0/256.0);
6 | vec4 res = mod(depth*bit_shift*vec4(255), vec4(256))/vec4(255);
7 | res -= res.xxyz * bit_mask;
8 | return res;
9 | }
10 | void main() {
11 | gl_FragColor = packDepth( gl_FragCoord.z );
12 | }
--------------------------------------------------------------------------------
/dst/fb.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mnmxmx/halftone-effect/97446f172f5ee982ffa7e5cfeec7a8b622765c65/dst/fb.png
--------------------------------------------------------------------------------
/dst/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
l;l++)o.nxf;f+=2)d.push(((q+b.rawBezier[f]*o)*n|0)/n+","+((r+b.rawBezier[f+1]*-p)*n|0)/n);d[0]="M"+d[0],d[1]="C"+d[1]}else for(d=["M"+q+","+r],j=Math.max(5,200*(c.precision||1)),g=1/j,j+=2,k=5/j,l=((q+g*o)*n|0)/n,m=((r+b.getRatio(g)*-p)*n|0)/n,e=(m-r)/(l-q),f=2;j>f;f++)h=((q+f*g*o)*n|0)/n,i=((r+b.getRatio(f*g)*-p)*n|0)/n,(Math.abs((i-m)/(h-l)-e)>k||f===j-1)&&(d.push(l+","+m),e=(i-m)/(h-l)),l=h,m=i;return s&&("string"==typeof s?document.querySelector(s):s).setAttribute("d",d.join(" ")),d.join(" ")},l},!0)}),_gsScope._gsDefine&&_gsScope._gsQueue.pop()(),function(a){"use strict";var b=function(){return(_gsScope.GreenSockGlobals||_gsScope)[a]};"undefined"!=typeof module&&module.exports?(require("../TweenLite.min.js"),module.exports=b()):"function"==typeof define&&define.amd&&define(["TweenLite"],b)}("CustomEase"); -------------------------------------------------------------------------------- /src/assets/js/lib/EasePack.min.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * VERSION: 1.16.0 3 | * DATE: 2018-02-15 4 | * UPDATES AND DOCS AT: http://greensock.com 5 | * 6 | * @license Copyright (c) 2008-2018, GreenSock. All rights reserved. 7 | * This work is subject to the terms at http://greensock.com/standard-license or for 8 | * Club GreenSock members, the software agreement that was issued with your membership. 9 | * 10 | * @author: Jack Doyle, jack@greensock.com 11 | **/ 12 | var _gsScope="undefined"!=typeof module&&module.exports&&"undefined"!=typeof global?global:this||window;(_gsScope._gsQueue||(_gsScope._gsQueue=[])).push(function(){"use strict";_gsScope._gsDefine("easing.Back",["easing.Ease"],function(a){var b,c,d,e,f=_gsScope.GreenSockGlobals||_gsScope,g=f.com.greensock,h=2*Math.PI,i=Math.PI/2,j=g._class,k=function(b,c){var d=j("easing."+b,function(){},!0),e=d.prototype=new a;return e.constructor=d,e.getRatio=c,d},l=a.register||function(){},m=function(a,b,c,d,e){var f=j("easing."+a,{easeOut:new b,easeIn:new c,easeInOut:new d},!0);return l(f,a),f},n=function(a,b,c){this.t=a,this.v=b,c&&(this.next=c,c.prev=this,this.c=c.v-b,this.gap=c.t-a)},o=function(b,c){var d=j("easing."+b,function(a){this._p1=a||0===a?a:1.70158,this._p2=1.525*this._p1},!0),e=d.prototype=new a;return e.constructor=d,e.getRatio=c,e.config=function(a){return new d(a)},d},p=m("Back",o("BackOut",function(a){return(a-=1)*a*((this._p1+1)*a+this._p1)+1}),o("BackIn",function(a){return a*a*((this._p1+1)*a-this._p1)}),o("BackInOut",function(a){return(a*=2)<1?.5*a*a*((this._p2+1)*a-this._p2):.5*((a-=2)*a*((this._p2+1)*a+this._p2)+2)})),q=j("easing.SlowMo",function(a,b,c){b=b||0===b?b:.7,null==a?a=.7:a>1&&(a=1),this._p=1!==a?b:0,this._p1=(1-a)/2,this._p2=a,this._p3=this._p1+this._p2,this._calcEnd=c===!0},!0),r=q.prototype=new a;return r.constructor=q,r.getRatio=function(a){var b=a+(.5-a)*this._p;return a this._p3?this._calcEnd?1===a?0:1-(a=(a-this._p3)/this._p1)*a:b+(a-b)*(a=(a-this._p3)/this._p1)*a*a*a:this._calcEnd?1:b},q.ease=new q(.7,.7),r.config=q.config=function(a,b,c){return new q(a,b,c)},b=j("easing.SteppedEase",function(a,b){a=a||1,this._p1=1/a,this._p2=a+(b?0:1),this._p3=b?1:0},!0),r=b.prototype=new a,r.constructor=b,r.getRatio=function(a){return 0>a?a=0:a>=1&&(a=.999999999),((this._p2*a|0)+this._p3)*this._p1},r.config=b.config=function(a,c){return new b(a,c)},c=j("easing.ExpoScaleEase",function(a,b,c){this._p1=Math.log(b/a),this._p2=b-a,this._p3=a,this._ease=c},!0),r=c.prototype=new a,r.constructor=c,r.getRatio=function(a){return this._ease&&(a=this._ease.getRatio(a)),(this._p3*Math.exp(this._p1*a)-this._p3)/this._p2},r.config=c.config=function(a,b,d){return new c(a,b,d)},d=j("easing.RoughEase",function(b){b=b||{};for(var c,d,e,f,g,h,i=b.taper||"none",j=[],k=0,l=0|(b.points||20),m=l,o=b.randomize!==!1,p=b.clamp===!0,q=b.template instanceof a?b.template:null,r="number"==typeof b.strength?.4*b.strength:.4;--m>-1;)c=o?Math.random():1/l*m,d=q?q.getRatio(c):c,"none"===i?e=r:"out"===i?(f=1-c,e=f*f*r):"in"===i?e=c*c*r:.5>c?(f=2*c,e=f*f*.5*r):(f=2*(1-c),e=f*f*.5*r),o?d+=Math.random()*e-.5*e:m%2?d+=.5*e:d-=.5*e,p&&(d>1?d=1:0>d&&(d=0)),j[k++]={x:c,y:d};for(j.sort(function(a,b){return a.x-b.x}),h=new n(1,1,null),m=l;--m>-1;)g=j[m],h=new n(g.x,g.y,h);this._prev=new n(0,0,0!==h.t?h:h.next)},!0),r=d.prototype=new a,r.constructor=d,r.getRatio=function(a){var b=this._prev;if(a>b.t){for(;b.next&&a>=b.t;)b=b.next;b=b.prev}else for(;b.prev&&a<=b.t;)b=b.prev;return this._prev=b,b.v+(a-b.t)/b.gap*b.c},r.config=function(a){return new d(a)},d.ease=new d,m("Bounce",k("BounceOut",function(a){return 1/2.75>a?7.5625*a*a:2/2.75>a?7.5625*(a-=1.5/2.75)*a+.75:2.5/2.75>a?7.5625*(a-=2.25/2.75)*a+.9375:7.5625*(a-=2.625/2.75)*a+.984375}),k("BounceIn",function(a){return(a=1-a)<1/2.75?1-7.5625*a*a:2/2.75>a?1-(7.5625*(a-=1.5/2.75)*a+.75):2.5/2.75>a?1-(7.5625*(a-=2.25/2.75)*a+.9375):1-(7.5625*(a-=2.625/2.75)*a+.984375)}),k("BounceInOut",function(a){var b=.5>a;return a=b?1-2*a:2*a-1,a=1/2.75>a?7.5625*a*a:2/2.75>a?7.5625*(a-=1.5/2.75)*a+.75:2.5/2.75>a?7.5625*(a-=2.25/2.75)*a+.9375:7.5625*(a-=2.625/2.75)*a+.984375,b?.5*(1-a):.5*a+.5})),m("Circ",k("CircOut",function(a){return Math.sqrt(1-(a-=1)*a)}),k("CircIn",function(a){return-(Math.sqrt(1-a*a)-1)}),k("CircInOut",function(a){return(a*=2)<1?-.5*(Math.sqrt(1-a*a)-1):.5*(Math.sqrt(1-(a-=2)*a)+1)})),e=function(b,c,d){var e=j("easing."+b,function(a,b){this._p1=a>=1?a:1,this._p2=(b||d)/(1>a?a:1),this._p3=this._p2/h*(Math.asin(1/this._p1)||0),this._p2=h/this._p2},!0),f=e.prototype=new a;return f.constructor=e,f.getRatio=c,f.config=function(a,b){return new e(a,b)},e},m("Elastic",e("ElasticOut",function(a){return this._p1*Math.pow(2,-10*a)*Math.sin((a-this._p3)*this._p2)+1},.3),e("ElasticIn",function(a){return-(this._p1*Math.pow(2,10*(a-=1))*Math.sin((a-this._p3)*this._p2))},.3),e("ElasticInOut",function(a){return(a*=2)<1?-.5*(this._p1*Math.pow(2,10*(a-=1))*Math.sin((a-this._p3)*this._p2)):this._p1*Math.pow(2,-10*(a-=1))*Math.sin((a-this._p3)*this._p2)*.5+1},.45)),m("Expo",k("ExpoOut",function(a){return 1-Math.pow(2,-10*a)}),k("ExpoIn",function(a){return Math.pow(2,10*(a-1))-.001}),k("ExpoInOut",function(a){return(a*=2)<1?.5*Math.pow(2,10*(a-1)):.5*(2-Math.pow(2,-10*(a-1)))})),m("Sine",k("SineOut",function(a){return Math.sin(a*i)}),k("SineIn",function(a){return-Math.cos(a*i)+1}),k("SineInOut",function(a){return-.5*(Math.cos(Math.PI*a)-1)})),j("easing.EaseLookup",{find:function(b){return a.map[b]}},!0),l(f.SlowMo,"SlowMo","ease,"),l(d,"RoughEase","ease,"),l(b,"SteppedEase","ease,"),p},!0)}),_gsScope._gsDefine&&_gsScope._gsQueue.pop()(),function(){"use strict";var a=function(){return _gsScope.GreenSockGlobals||_gsScope};"undefined"!=typeof module&&module.exports?(require("../TweenLite.min.js"),module.exports=a()):"function"==typeof define&&define.amd&&define(["TweenLite"],a)}(); -------------------------------------------------------------------------------- /src/assets/js/lib/GPUComputationRenderer.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @author yomboprime https://github.com/yomboprime 3 | * 4 | * GPUComputationRenderer, based on SimulationRenderer by zz85 5 | * 6 | * The GPUComputationRenderer uses the concept of variables. These variables are RGBA float textures that hold 4 floats 7 | * for each compute element (texel) 8 | * 9 | * Each variable has a fragment shader that defines the computation made to obtain the variable in question. 10 | * You can use as many variables you need, and make dependencies so you can use textures of other variables in the shader 11 | * (the sampler uniforms are added automatically) Most of the variables will need themselves as dependency. 12 | * 13 | * The renderer has actually two render targets per variable, to make ping-pong. Textures from the current frame are used 14 | * as inputs to render the textures of the next frame. 15 | * 16 | * The render targets of the variables can be used as input textures for your visualization shaders. 17 | * 18 | * Variable names should be valid identifiers and should not collide with THREE GLSL used identifiers. 19 | * a common approach could be to use 'texture' prefixing the variable name; i.e texturePosition, textureVelocity... 20 | * 21 | * The size of the computation (sizeX * sizeY) is defined as 'resolution' automatically in the shader. For example: 22 | * #DEFINE resolution vec2( 1024.0, 1024.0 ) 23 | * 24 | * ------------- 25 | * 26 | * Basic use: 27 | * 28 | * // Initialization... 29 | * 30 | * // Create computation renderer 31 | * var gpuCompute = new GPUComputationRenderer( 1024, 1024, renderer ); 32 | * 33 | * // Create initial state float textures 34 | * var pos0 = gpuCompute.createTexture(); 35 | * var vel0 = gpuCompute.createTexture(); 36 | * // and fill in here the texture data... 37 | * 38 | * // Add texture variables 39 | * var velVar = gpuCompute.addVariable( "textureVelocity", fragmentShaderVel, pos0 ); 40 | * var posVar = gpuCompute.addVariable( "texturePosition", fragmentShaderPos, vel0 ); 41 | * 42 | * // Add variable dependencies 43 | * gpuCompute.setVariableDependencies( velVar, [ velVar, posVar ] ); 44 | * gpuCompute.setVariableDependencies( posVar, [ velVar, posVar ] ); 45 | * 46 | * // Add custom uniforms 47 | * velVar.material.uniforms.time = { value: 0.0 }; 48 | * 49 | * // Check for completeness 50 | * var error = gpuCompute.init(); 51 | * if ( error !== null ) { 52 | * console.error( error ); 53 | * } 54 | * 55 | * 56 | * // In each frame... 57 | * 58 | * // Compute! 59 | * gpuCompute.compute(); 60 | * 61 | * // Update texture uniforms in your visualization materials with the gpu renderer output 62 | * myMaterial.uniforms.myTexture.value = gpuCompute.getCurrentRenderTarget( posVar ).texture; 63 | * 64 | * // Do your rendering 65 | * renderer.render( myScene, myCamera ); 66 | * 67 | * ------------- 68 | * 69 | * Also, you can use utility functions to create ShaderMaterial and perform computations (rendering between textures) 70 | * Note that the shaders can have multiple input textures. 71 | * 72 | * var myFilter1 = gpuCompute.createShaderMaterial( myFilterFragmentShader1, { theTexture: { value: null } } ); 73 | * var myFilter2 = gpuCompute.createShaderMaterial( myFilterFragmentShader2, { theTexture: { value: null } } ); 74 | * 75 | * var inputTexture = gpuCompute.createTexture(); 76 | * 77 | * // Fill in here inputTexture... 78 | * 79 | * myFilter1.uniforms.theTexture.value = inputTexture; 80 | * 81 | * var myRenderTarget = gpuCompute.createRenderTarget(); 82 | * myFilter2.uniforms.theTexture.value = myRenderTarget.texture; 83 | * 84 | * var outputRenderTarget = gpuCompute.createRenderTarget(); 85 | * 86 | * // Now use the output texture where you want: 87 | * myMaterial.uniforms.map.value = outputRenderTarget.texture; 88 | * 89 | * // And compute each frame, before rendering to screen: 90 | * gpuCompute.doRenderTarget( myFilter1, myRenderTarget ); 91 | * gpuCompute.doRenderTarget( myFilter2, outputRenderTarget ); 92 | * 93 | * 94 | * 95 | * @param {int} sizeX Computation problem size is always 2d: sizeX * sizeY elements. 96 | * @param {int} sizeY Computation problem size is always 2d: sizeX * sizeY elements. 97 | * @param {WebGLRenderer} renderer The renderer 98 | */ 99 | 100 | function GPUComputationRenderer( sizeX, sizeY, renderer ) { 101 | 102 | this.variables = []; 103 | 104 | this.currentTextureIndex = 0; 105 | 106 | var scene = new THREE.Scene(); 107 | 108 | var camera = new THREE.Camera(); 109 | camera.position.z = 1; 110 | 111 | var passThruUniforms = { 112 | texture: { value: null } 113 | }; 114 | 115 | var passThruShader = createShaderMaterial( getPassThroughFragmentShader(), passThruUniforms ); 116 | 117 | var mesh = new THREE.Mesh( new THREE.PlaneBufferGeometry( 2, 2 ), passThruShader ); 118 | scene.add( mesh ); 119 | 120 | 121 | this.addVariable = function( variableName, computeFragmentShader, initialValueTexture ) { 122 | 123 | var material = this.createShaderMaterial( computeFragmentShader ); 124 | 125 | var variable = { 126 | name: variableName, 127 | initialValueTexture: initialValueTexture, 128 | material: material, 129 | dependencies: null, 130 | renderTargets: [], 131 | wrapS: null, 132 | wrapT: null, 133 | minFilter: THREE.NearestFilter, 134 | magFilter: THREE.NearestFilter 135 | }; 136 | 137 | this.variables.push( variable ); 138 | 139 | return variable; 140 | 141 | }; 142 | 143 | this.setVariableDependencies = function( variable, dependencies ) { 144 | 145 | variable.dependencies = dependencies; 146 | 147 | }; 148 | 149 | this.init = function() { 150 | 151 | if ( ! renderer.extensions.get( "OES_texture_float" ) ) { 152 | 153 | return "No OES_texture_float support for float textures."; 154 | 155 | } 156 | 157 | if ( renderer.capabilities.maxVertexTextures === 0 ) { 158 | 159 | return "No support for vertex shader textures."; 160 | 161 | } 162 | 163 | for ( var i = 0; i < this.variables.length; i++ ) { 164 | 165 | var variable = this.variables[ i ]; 166 | 167 | // Creates rendertargets and initialize them with input texture 168 | variable.renderTargets[ 0 ] = this.createRenderTarget( sizeX, sizeY, variable.wrapS, variable.wrapT, variable.minFilter, variable.magFilter ); 169 | variable.renderTargets[ 1 ] = this.createRenderTarget( sizeX, sizeY, variable.wrapS, variable.wrapT, variable.minFilter, variable.magFilter ); 170 | this.renderTexture( variable.initialValueTexture, variable.renderTargets[ 0 ] ); 171 | this.renderTexture( variable.initialValueTexture, variable.renderTargets[ 1 ] ); 172 | 173 | // Adds dependencies uniforms to the ShaderMaterial 174 | var material = variable.material; 175 | var uniforms = material.uniforms; 176 | if ( variable.dependencies !== null ) { 177 | 178 | for ( var d = 0; d < variable.dependencies.length; d++ ) { 179 | 180 | var depVar = variable.dependencies[ d ]; 181 | 182 | if ( depVar.name !== variable.name ) { 183 | 184 | // Checks if variable exists 185 | var found = false; 186 | for ( var j = 0; j < this.variables.length; j++ ) { 187 | 188 | if ( depVar.name === this.variables[ j ].name ) { 189 | found = true; 190 | break; 191 | } 192 | 193 | } 194 | if ( ! found ) { 195 | return "Variable dependency not found. Variable=" + variable.name + ", dependency=" + depVar.name; 196 | } 197 | 198 | } 199 | 200 | uniforms[ depVar.name ] = { value: null }; 201 | 202 | material.fragmentShader = "\nuniform sampler2D " + depVar.name + ";\n" + material.fragmentShader; 203 | 204 | } 205 | } 206 | } 207 | 208 | this.currentTextureIndex = 0; 209 | 210 | return null; 211 | 212 | }; 213 | 214 | this.compute = function() { 215 | 216 | var currentTextureIndex = this.currentTextureIndex; 217 | var nextTextureIndex = this.currentTextureIndex === 0 ? 1 : 0; 218 | 219 | for ( var i = 0, il = this.variables.length; i < il; i++ ) { 220 | 221 | var variable = this.variables[ i ]; 222 | 223 | // Sets texture dependencies uniforms 224 | if ( variable.dependencies !== null ) { 225 | 226 | var uniforms = variable.material.uniforms; 227 | for ( var d = 0, dl = variable.dependencies.length; d < dl; d++ ) { 228 | 229 | var depVar = variable.dependencies[ d ]; 230 | // console.log(depVar); 231 | 232 | uniforms[ depVar.name ].value = depVar.renderTargets[ currentTextureIndex ].texture; 233 | 234 | } 235 | 236 | } 237 | 238 | // Performs the computation for this variable 239 | this.doRenderTarget( variable.material, variable.renderTargets[ nextTextureIndex ] ); 240 | 241 | } 242 | 243 | this.currentTextureIndex = nextTextureIndex; 244 | }; 245 | 246 | this.getCurrentRenderTarget = function( variable ) { 247 | 248 | return variable.renderTargets[ this.currentTextureIndex ]; 249 | 250 | }; 251 | 252 | this.getAlternateRenderTarget = function( variable ) { 253 | 254 | return variable.renderTargets[ this.currentTextureIndex === 0 ? 1 : 0 ]; 255 | 256 | }; 257 | 258 | function addResolutionDefine( materialShader ) { 259 | 260 | materialShader.defines.resolution = 'vec2( ' + sizeX.toFixed( 1 ) + ', ' + sizeY.toFixed( 1 ) + " )"; 261 | 262 | }; 263 | this.addResolutionDefine = addResolutionDefine; 264 | 265 | 266 | // The following functions can be used to compute things manually 267 | 268 | function createShaderMaterial( computeFragmentShader, uniforms ) { 269 | 270 | uniforms = uniforms || {}; 271 | 272 | var material = new THREE.ShaderMaterial( { 273 | uniforms: uniforms, 274 | vertexShader: getPassThroughVertexShader(), 275 | fragmentShader: computeFragmentShader 276 | } ); 277 | 278 | addResolutionDefine( material ); 279 | 280 | return material; 281 | }; 282 | this.createShaderMaterial = createShaderMaterial; 283 | 284 | this.createRenderTarget = function( sizeXTexture, sizeYTexture, wrapS, wrapT, minFilter, magFilter ) { 285 | 286 | sizeXTexture = sizeXTexture || sizeX; 287 | sizeYTexture = sizeYTexture || sizeY; 288 | 289 | wrapS = wrapS || THREE.ClampToEdgeWrapping; 290 | wrapT = wrapT || THREE.ClampToEdgeWrapping; 291 | 292 | minFilter = minFilter || THREE.NearestFilter; 293 | magFilter = magFilter || THREE.NearestFilter; 294 | 295 | var floatType = (Useragnt.ios) ? THREE.HalfFloatType : THREE.FloatType; 296 | 297 | var renderTarget = new THREE.WebGLRenderTarget( sizeXTexture, sizeYTexture, { 298 | wrapS: wrapS, 299 | wrapT: wrapT, 300 | minFilter: minFilter, 301 | magFilter: magFilter, 302 | format: THREE.RGBAFormat, 303 | type: floatType, 304 | stencilBuffer: false 305 | } ); 306 | 307 | return renderTarget; 308 | 309 | }; 310 | 311 | this.createTexture = function( sizeXTexture, sizeYTexture ) { 312 | 313 | sizeXTexture = sizeXTexture || sizeX; 314 | sizeYTexture = sizeYTexture || sizeY; 315 | 316 | var floatType = (Useragnt.ios) ? THREE.HalfFloatType : THREE.FloatType; 317 | 318 | var a = new Float32Array( sizeXTexture * sizeYTexture * 4 ); 319 | var texture = new THREE.DataTexture( a, sizeX, sizeY, THREE.RGBAFormat, THREE.FloatType ); 320 | texture.needsUpdate = true; 321 | 322 | return texture; 323 | 324 | }; 325 | 326 | 327 | this.renderTexture = function( input, output ) { 328 | 329 | // Takes a texture, and render out in rendertarget 330 | // input = Texture 331 | // output = RenderTarget 332 | 333 | passThruUniforms.texture.value = input; 334 | 335 | this.doRenderTarget( passThruShader, output); 336 | 337 | passThruUniforms.texture.value = null; 338 | 339 | }; 340 | 341 | this.doRenderTarget = function( material, output ) { 342 | 343 | mesh.material = material; 344 | renderer.render( scene, camera, output ); 345 | mesh.material = passThruShader; 346 | 347 | }; 348 | 349 | // Shaders 350 | 351 | function getPassThroughVertexShader() { 352 | 353 | return "void main() {\n" + 354 | "\n" + 355 | " gl_Position = vec4( position, 1.0 );\n" + 356 | "\n" + 357 | "}\n"; 358 | 359 | } 360 | 361 | function getPassThroughFragmentShader() { 362 | 363 | return "uniform sampler2D texture;\n" + 364 | "\n" + 365 | "void main() {\n" + 366 | "\n" + 367 | " vec2 uv = gl_FragCoord.xy / resolution.xy;\n" + 368 | "\n" + 369 | " gl_FragColor = texture2D( texture, uv );\n" + 370 | "\n" + 371 | "}\n"; 372 | 373 | } 374 | 375 | } -------------------------------------------------------------------------------- /src/assets/js/lib/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 | 9 | // This set of controls performs orbiting, dollying (zooming), and panning. 10 | // Unlike TrackballControls, it maintains the "up" direction object.up (+Y by default). 11 | // 12 | // Orbit - left mouse / touch: one finger move 13 | // Zoom - middle mouse, or mousewheel / touch: two finger spread or squish 14 | // Pan - right mouse, or arrow keys / touch: three finger swipe 15 | 16 | THREE.OrbitControls = function ( object, domElement ) { 17 | 18 | this.object = object; 19 | 20 | this.domElement = ( domElement !== undefined ) ? domElement : document; 21 | 22 | // Set to false to disable this control 23 | this.enabled = true; 24 | 25 | // "target" sets the location of focus, where the object orbits around 26 | this.target = new THREE.Vector3(); 27 | 28 | // How far you can dolly in and out ( PerspectiveCamera only ) 29 | this.minDistance = 0; 30 | this.maxDistance = Infinity; 31 | 32 | // How far you can zoom in and out ( OrthographicCamera only ) 33 | this.minZoom = 0; 34 | this.maxZoom = Infinity; 35 | 36 | // How far you can orbit vertically, upper and lower limits. 37 | // Range is 0 to Math.PI radians. 38 | this.minPolarAngle = 0; // radians 39 | this.maxPolarAngle = Math.PI; // radians 40 | 41 | // How far you can orbit horizontally, upper and lower limits. 42 | // If set, must be a sub-interval of the interval [ - Math.PI, Math.PI ]. 43 | this.minAzimuthAngle = - Infinity; // radians 44 | this.maxAzimuthAngle = Infinity; // radians 45 | 46 | // Set to true to enable damping (inertia) 47 | // If damping is enabled, you must call controls.update() in your animation loop 48 | this.enableDamping = false; 49 | this.dampingFactor = 0.25; 50 | 51 | // This option actually enables dollying in and out; left as "zoom" for backwards compatibility. 52 | // Set to false to disable zooming 53 | this.enableZoom = true; 54 | this.zoomSpeed = 1.0; 55 | 56 | // Set to false to disable rotating 57 | this.enableRotate = true; 58 | this.rotateSpeed = 1.0; 59 | 60 | // Set to false to disable panning 61 | this.enablePan = true; 62 | this.keyPanSpeed = 7.0; // pixels moved per arrow key push 63 | 64 | // Set to true to automatically rotate around the target 65 | // If auto-rotate is enabled, you must call controls.update() in your animation loop 66 | this.autoRotate = false; 67 | this.autoRotateSpeed = 2.0; // 30 seconds per round when fps is 60 68 | 69 | // Set to false to disable use of the keys 70 | this.enableKeys = true; 71 | 72 | // The four arrow keys 73 | this.keys = { LEFT: 37, UP: 38, RIGHT: 39, BOTTOM: 40 }; 74 | 75 | // Mouse buttons 76 | this.mouseButtons = { ORBIT: THREE.MOUSE.LEFT, ZOOM: THREE.MOUSE.MIDDLE, PAN: THREE.MOUSE.RIGHT }; 77 | 78 | // for reset 79 | this.target0 = this.target.clone(); 80 | this.position0 = this.object.position.clone(); 81 | this.zoom0 = this.object.zoom; 82 | 83 | // 84 | // public methods 85 | // 86 | 87 | this.getPolarAngle = function () { 88 | 89 | return spherical.phi; 90 | 91 | }; 92 | 93 | this.getAzimuthalAngle = function () { 94 | 95 | return spherical.theta; 96 | 97 | }; 98 | 99 | this.reset = function () { 100 | 101 | scope.target.copy( scope.target0 ); 102 | scope.object.position.copy( scope.position0 ); 103 | scope.object.zoom = scope.zoom0; 104 | 105 | scope.object.updateProjectionMatrix(); 106 | scope.dispatchEvent( changeEvent ); 107 | 108 | scope.update(); 109 | 110 | state = STATE.NONE; 111 | 112 | }; 113 | 114 | // this method is exposed, but perhaps it would be better if we can make it private... 115 | this.update = function () { 116 | 117 | var offset = new THREE.Vector3(); 118 | 119 | // so camera.up is the orbit axis 120 | var quat = new THREE.Quaternion().setFromUnitVectors( object.up, new THREE.Vector3( 0, 1, 0 ) ); 121 | var quatInverse = quat.clone().inverse(); 122 | 123 | var lastPosition = new THREE.Vector3(); 124 | var lastQuaternion = new THREE.Quaternion(); 125 | 126 | return function update() { 127 | 128 | var position = scope.object.position; 129 | 130 | offset.copy( position ).sub( scope.target ); 131 | 132 | // rotate offset to "y-axis-is-up" space 133 | offset.applyQuaternion( quat ); 134 | 135 | // angle from z-axis around y-axis 136 | spherical.setFromVector3( offset ); 137 | 138 | if ( scope.autoRotate && state === STATE.NONE ) { 139 | 140 | rotateLeft( getAutoRotationAngle() ); 141 | 142 | } 143 | 144 | spherical.theta += sphericalDelta.theta; 145 | spherical.phi += sphericalDelta.phi; 146 | 147 | // restrict theta to be between desired limits 148 | spherical.theta = Math.max( scope.minAzimuthAngle, Math.min( scope.maxAzimuthAngle, spherical.theta ) ); 149 | 150 | // restrict phi to be between desired limits 151 | spherical.phi = Math.max( scope.minPolarAngle, Math.min( scope.maxPolarAngle, spherical.phi ) ); 152 | 153 | spherical.makeSafe(); 154 | 155 | 156 | spherical.radius *= scale; 157 | 158 | // restrict radius to be between desired limits 159 | spherical.radius = Math.max( scope.minDistance, Math.min( scope.maxDistance, spherical.radius ) ); 160 | 161 | // move target to panned location 162 | scope.target.add( panOffset ); 163 | 164 | offset.setFromSpherical( spherical ); 165 | 166 | // rotate offset back to "camera-up-vector-is-up" space 167 | offset.applyQuaternion( quatInverse ); 168 | 169 | position.copy( scope.target ).add( offset ); 170 | 171 | scope.object.lookAt( scope.target ); 172 | 173 | if ( scope.enableDamping === true ) { 174 | 175 | sphericalDelta.theta *= ( 1 - scope.dampingFactor ); 176 | sphericalDelta.phi *= ( 1 - scope.dampingFactor ); 177 | 178 | } else { 179 | 180 | sphericalDelta.set( 0, 0, 0 ); 181 | 182 | } 183 | 184 | scale = 1; 185 | panOffset.set( 0, 0, 0 ); 186 | 187 | // update condition is: 188 | // min(camera displacement, camera rotation in radians)^2 > EPS 189 | // using small-angle approximation cos(x/2) = 1 - x^2 / 8 190 | 191 | if ( zoomChanged || 192 | lastPosition.distanceToSquared( scope.object.position ) > EPS || 193 | 8 * ( 1 - lastQuaternion.dot( scope.object.quaternion ) ) > EPS ) { 194 | 195 | scope.dispatchEvent( changeEvent ); 196 | 197 | lastPosition.copy( scope.object.position ); 198 | lastQuaternion.copy( scope.object.quaternion ); 199 | zoomChanged = false; 200 | 201 | return true; 202 | 203 | } 204 | 205 | return false; 206 | 207 | }; 208 | 209 | }(); 210 | 211 | this.dispose = function () { 212 | 213 | scope.domElement.removeEventListener( 'contextmenu', onContextMenu, false ); 214 | scope.domElement.removeEventListener( 'mousedown', onMouseDown, false ); 215 | scope.domElement.removeEventListener( 'wheel', onMouseWheel, false ); 216 | 217 | scope.domElement.removeEventListener( 'touchstart', onTouchStart, false ); 218 | scope.domElement.removeEventListener( 'touchend', onTouchEnd, false ); 219 | scope.domElement.removeEventListener( 'touchmove', onTouchMove, false ); 220 | 221 | document.removeEventListener( 'mousemove', onMouseMove, false ); 222 | document.removeEventListener( 'mouseup', onMouseUp, false ); 223 | 224 | window.removeEventListener( 'keydown', onKeyDown, false ); 225 | 226 | //scope.dispatchEvent( { type: 'dispose' } ); // should this be added here? 227 | 228 | }; 229 | 230 | // 231 | // internals 232 | // 233 | 234 | var scope = this; 235 | 236 | var changeEvent = { type: 'change' }; 237 | var startEvent = { type: 'start' }; 238 | var endEvent = { type: 'end' }; 239 | 240 | var STATE = { NONE: - 1, ROTATE: 0, DOLLY: 1, PAN: 2, TOUCH_ROTATE: 3, TOUCH_DOLLY: 4, TOUCH_PAN: 5 }; 241 | 242 | var state = STATE.NONE; 243 | 244 | var EPS = 0.000001; 245 | 246 | // current position in spherical coordinates 247 | var spherical = new THREE.Spherical(); 248 | var sphericalDelta = new THREE.Spherical(); 249 | 250 | var scale = 1; 251 | var panOffset = new THREE.Vector3(); 252 | var zoomChanged = false; 253 | 254 | var rotateStart = new THREE.Vector2(); 255 | var rotateEnd = new THREE.Vector2(); 256 | var rotateDelta = new THREE.Vector2(); 257 | 258 | var panStart = new THREE.Vector2(); 259 | var panEnd = new THREE.Vector2(); 260 | var panDelta = new THREE.Vector2(); 261 | 262 | var dollyStart = new THREE.Vector2(); 263 | var dollyEnd = new THREE.Vector2(); 264 | var dollyDelta = new THREE.Vector2(); 265 | 266 | function getAutoRotationAngle() { 267 | 268 | return 2 * Math.PI / 60 / 60 * scope.autoRotateSpeed; 269 | 270 | } 271 | 272 | function getZoomScale() { 273 | 274 | return Math.pow( 0.95, scope.zoomSpeed ); 275 | 276 | } 277 | 278 | function rotateLeft( angle ) { 279 | 280 | sphericalDelta.theta -= angle; 281 | 282 | } 283 | 284 | function rotateUp( angle ) { 285 | 286 | sphericalDelta.phi -= angle; 287 | 288 | } 289 | 290 | var panLeft = function () { 291 | 292 | var v = new THREE.Vector3(); 293 | 294 | return function panLeft( distance, objectMatrix ) { 295 | 296 | v.setFromMatrixColumn( objectMatrix, 0 ); // get X column of objectMatrix 297 | v.multiplyScalar( - distance ); 298 | 299 | panOffset.add( v ); 300 | 301 | }; 302 | 303 | }(); 304 | 305 | var panUp = function () { 306 | 307 | var v = new THREE.Vector3(); 308 | 309 | return function panUp( distance, objectMatrix ) { 310 | 311 | v.setFromMatrixColumn( objectMatrix, 1 ); // get Y column of objectMatrix 312 | v.multiplyScalar( distance ); 313 | 314 | panOffset.add( v ); 315 | 316 | }; 317 | 318 | }(); 319 | 320 | // deltaX and deltaY are in pixels; right and down are positive 321 | var pan = function () { 322 | 323 | var offset = new THREE.Vector3(); 324 | 325 | return function pan( deltaX, deltaY ) { 326 | 327 | var element = scope.domElement === document ? scope.domElement.body : scope.domElement; 328 | 329 | if ( scope.object instanceof THREE.PerspectiveCamera ) { 330 | 331 | // perspective 332 | var position = scope.object.position; 333 | offset.copy( position ).sub( scope.target ); 334 | var targetDistance = offset.length(); 335 | 336 | // half of the fov is center to top of screen 337 | targetDistance *= Math.tan( ( scope.object.fov / 2 ) * Math.PI / 180.0 ); 338 | 339 | // we actually don't use screenWidth, since perspective camera is fixed to screen height 340 | panLeft( 2 * deltaX * targetDistance / element.clientHeight, scope.object.matrix ); 341 | panUp( 2 * deltaY * targetDistance / element.clientHeight, scope.object.matrix ); 342 | 343 | } else if ( scope.object instanceof THREE.OrthographicCamera ) { 344 | 345 | // orthographic 346 | panLeft( deltaX * ( scope.object.right - scope.object.left ) / scope.object.zoom / element.clientWidth, scope.object.matrix ); 347 | panUp( deltaY * ( scope.object.top - scope.object.bottom ) / scope.object.zoom / element.clientHeight, scope.object.matrix ); 348 | 349 | } else { 350 | 351 | // camera neither orthographic nor perspective 352 | console.warn( 'WARNING: OrbitControls.js encountered an unknown camera type - pan disabled.' ); 353 | scope.enablePan = false; 354 | 355 | } 356 | 357 | }; 358 | 359 | }(); 360 | 361 | function dollyIn( dollyScale ) { 362 | 363 | if ( scope.object instanceof THREE.PerspectiveCamera ) { 364 | 365 | scale /= dollyScale; 366 | 367 | } else if ( scope.object instanceof THREE.OrthographicCamera ) { 368 | 369 | scope.object.zoom = Math.max( scope.minZoom, Math.min( scope.maxZoom, scope.object.zoom * dollyScale ) ); 370 | scope.object.updateProjectionMatrix(); 371 | zoomChanged = true; 372 | 373 | } else { 374 | 375 | console.warn( 'WARNING: OrbitControls.js encountered an unknown camera type - dolly/zoom disabled.' ); 376 | scope.enableZoom = false; 377 | 378 | } 379 | 380 | } 381 | 382 | function dollyOut( dollyScale ) { 383 | 384 | if ( scope.object instanceof THREE.PerspectiveCamera ) { 385 | 386 | scale *= dollyScale; 387 | 388 | } else if ( scope.object instanceof THREE.OrthographicCamera ) { 389 | 390 | scope.object.zoom = Math.max( scope.minZoom, Math.min( scope.maxZoom, scope.object.zoom / dollyScale ) ); 391 | scope.object.updateProjectionMatrix(); 392 | zoomChanged = true; 393 | 394 | } else { 395 | 396 | console.warn( 'WARNING: OrbitControls.js encountered an unknown camera type - dolly/zoom disabled.' ); 397 | scope.enableZoom = false; 398 | 399 | } 400 | 401 | } 402 | 403 | // 404 | // event callbacks - update the object state 405 | // 406 | 407 | function handleMouseDownRotate( event ) { 408 | 409 | //console.log( 'handleMouseDownRotate' ); 410 | 411 | rotateStart.set( event.clientX, event.clientY ); 412 | 413 | } 414 | 415 | function handleMouseDownDolly( event ) { 416 | 417 | //console.log( 'handleMouseDownDolly' ); 418 | 419 | dollyStart.set( event.clientX, event.clientY ); 420 | 421 | } 422 | 423 | function handleMouseDownPan( event ) { 424 | 425 | //console.log( 'handleMouseDownPan' ); 426 | 427 | panStart.set( event.clientX, event.clientY ); 428 | 429 | } 430 | 431 | function handleMouseMoveRotate( event ) { 432 | 433 | //console.log( 'handleMouseMoveRotate' ); 434 | 435 | rotateEnd.set( event.clientX, event.clientY ); 436 | rotateDelta.subVectors( rotateEnd, rotateStart ); 437 | 438 | var element = scope.domElement === document ? scope.domElement.body : scope.domElement; 439 | 440 | // rotating across whole screen goes 360 degrees around 441 | rotateLeft( 2 * Math.PI * rotateDelta.x / element.clientWidth * scope.rotateSpeed ); 442 | 443 | // rotating up and down along whole screen attempts to go 360, but limited to 180 444 | rotateUp( 2 * Math.PI * rotateDelta.y / element.clientHeight * scope.rotateSpeed ); 445 | 446 | rotateStart.copy( rotateEnd ); 447 | 448 | scope.update(); 449 | 450 | } 451 | 452 | function handleMouseMoveDolly( event ) { 453 | 454 | //console.log( 'handleMouseMoveDolly' ); 455 | 456 | dollyEnd.set( event.clientX, event.clientY ); 457 | 458 | dollyDelta.subVectors( dollyEnd, dollyStart ); 459 | 460 | if ( dollyDelta.y > 0 ) { 461 | 462 | dollyIn( getZoomScale() ); 463 | 464 | } else if ( dollyDelta.y < 0 ) { 465 | 466 | dollyOut( getZoomScale() ); 467 | 468 | } 469 | 470 | dollyStart.copy( dollyEnd ); 471 | 472 | scope.update(); 473 | 474 | } 475 | 476 | function handleMouseMovePan( event ) { 477 | 478 | //console.log( 'handleMouseMovePan' ); 479 | 480 | panEnd.set( event.clientX, event.clientY ); 481 | 482 | panDelta.subVectors( panEnd, panStart ); 483 | 484 | pan( panDelta.x, panDelta.y ); 485 | 486 | panStart.copy( panEnd ); 487 | 488 | scope.update(); 489 | 490 | } 491 | 492 | function handleMouseUp( event ) { 493 | 494 | // console.log( 'handleMouseUp' ); 495 | 496 | } 497 | 498 | function handleMouseWheel( event ) { 499 | 500 | // console.log( 'handleMouseWheel' ); 501 | 502 | if ( event.deltaY < 0 ) { 503 | 504 | dollyOut( getZoomScale() ); 505 | 506 | } else if ( event.deltaY > 0 ) { 507 | 508 | dollyIn( getZoomScale() ); 509 | 510 | } 511 | 512 | scope.update(); 513 | 514 | } 515 | 516 | function handleKeyDown( event ) { 517 | 518 | //console.log( 'handleKeyDown' ); 519 | 520 | switch ( event.keyCode ) { 521 | 522 | case scope.keys.UP: 523 | pan( 0, scope.keyPanSpeed ); 524 | scope.update(); 525 | break; 526 | 527 | case scope.keys.BOTTOM: 528 | pan( 0, - scope.keyPanSpeed ); 529 | scope.update(); 530 | break; 531 | 532 | case scope.keys.LEFT: 533 | pan( scope.keyPanSpeed, 0 ); 534 | scope.update(); 535 | break; 536 | 537 | case scope.keys.RIGHT: 538 | pan( - scope.keyPanSpeed, 0 ); 539 | scope.update(); 540 | break; 541 | 542 | } 543 | 544 | } 545 | 546 | function handleTouchStartRotate( event ) { 547 | 548 | //console.log( 'handleTouchStartRotate' ); 549 | 550 | rotateStart.set( event.touches[ 0 ].pageX, event.touches[ 0 ].pageY ); 551 | 552 | } 553 | 554 | function handleTouchStartDolly( event ) { 555 | 556 | //console.log( 'handleTouchStartDolly' ); 557 | 558 | var dx = event.touches[ 0 ].pageX - event.touches[ 1 ].pageX; 559 | var dy = event.touches[ 0 ].pageY - event.touches[ 1 ].pageY; 560 | 561 | var distance = Math.sqrt( dx * dx + dy * dy ); 562 | 563 | dollyStart.set( 0, distance ); 564 | 565 | } 566 | 567 | function handleTouchStartPan( event ) { 568 | 569 | //console.log( 'handleTouchStartPan' ); 570 | 571 | panStart.set( event.touches[ 0 ].pageX, event.touches[ 0 ].pageY ); 572 | 573 | } 574 | 575 | function handleTouchMoveRotate( event ) { 576 | 577 | //console.log( 'handleTouchMoveRotate' ); 578 | 579 | rotateEnd.set( event.touches[ 0 ].pageX, event.touches[ 0 ].pageY ); 580 | rotateDelta.subVectors( rotateEnd, rotateStart ); 581 | 582 | var element = scope.domElement === document ? scope.domElement.body : scope.domElement; 583 | 584 | // rotating across whole screen goes 360 degrees around 585 | rotateLeft( 2 * Math.PI * rotateDelta.x / element.clientWidth * scope.rotateSpeed ); 586 | 587 | // rotating up and down along whole screen attempts to go 360, but limited to 180 588 | rotateUp( 2 * Math.PI * rotateDelta.y / element.clientHeight * scope.rotateSpeed ); 589 | 590 | rotateStart.copy( rotateEnd ); 591 | 592 | scope.update(); 593 | 594 | } 595 | 596 | function handleTouchMoveDolly( event ) { 597 | 598 | //console.log( 'handleTouchMoveDolly' ); 599 | 600 | var dx = event.touches[ 0 ].pageX - event.touches[ 1 ].pageX; 601 | var dy = event.touches[ 0 ].pageY - event.touches[ 1 ].pageY; 602 | 603 | var distance = Math.sqrt( dx * dx + dy * dy ); 604 | 605 | dollyEnd.set( 0, distance ); 606 | 607 | dollyDelta.subVectors( dollyEnd, dollyStart ); 608 | 609 | if ( dollyDelta.y > 0 ) { 610 | 611 | dollyOut( getZoomScale() ); 612 | 613 | } else if ( dollyDelta.y < 0 ) { 614 | 615 | dollyIn( getZoomScale() ); 616 | 617 | } 618 | 619 | dollyStart.copy( dollyEnd ); 620 | 621 | scope.update(); 622 | 623 | } 624 | 625 | function handleTouchMovePan( event ) { 626 | 627 | //console.log( 'handleTouchMovePan' ); 628 | 629 | panEnd.set( event.touches[ 0 ].pageX, event.touches[ 0 ].pageY ); 630 | 631 | panDelta.subVectors( panEnd, panStart ); 632 | 633 | pan( panDelta.x, panDelta.y ); 634 | 635 | panStart.copy( panEnd ); 636 | 637 | scope.update(); 638 | 639 | } 640 | 641 | function handleTouchEnd( event ) { 642 | 643 | //console.log( 'handleTouchEnd' ); 644 | 645 | } 646 | 647 | // 648 | // event handlers - FSM: listen for events and reset state 649 | // 650 | 651 | function onMouseDown( event ) { 652 | 653 | if ( scope.enabled === false ) return; 654 | 655 | event.preventDefault(); 656 | 657 | if ( event.button === scope.mouseButtons.ORBIT ) { 658 | 659 | if ( scope.enableRotate === false ) return; 660 | 661 | handleMouseDownRotate( event ); 662 | 663 | state = STATE.ROTATE; 664 | 665 | } else if ( event.button === scope.mouseButtons.ZOOM ) { 666 | 667 | if ( scope.enableZoom === false ) return; 668 | 669 | handleMouseDownDolly( event ); 670 | 671 | state = STATE.DOLLY; 672 | 673 | } else if ( event.button === scope.mouseButtons.PAN ) { 674 | 675 | if ( scope.enablePan === false ) return; 676 | 677 | handleMouseDownPan( event ); 678 | 679 | state = STATE.PAN; 680 | 681 | } 682 | 683 | if ( state !== STATE.NONE ) { 684 | 685 | document.addEventListener( 'mousemove', onMouseMove, false ); 686 | document.addEventListener( 'mouseup', onMouseUp, false ); 687 | 688 | scope.dispatchEvent( startEvent ); 689 | 690 | } 691 | 692 | } 693 | 694 | function onMouseMove( event ) { 695 | 696 | if ( scope.enabled === false ) return; 697 | 698 | event.preventDefault(); 699 | 700 | if ( state === STATE.ROTATE ) { 701 | 702 | if ( scope.enableRotate === false ) return; 703 | 704 | handleMouseMoveRotate( event ); 705 | 706 | } else if ( state === STATE.DOLLY ) { 707 | 708 | if ( scope.enableZoom === false ) return; 709 | 710 | handleMouseMoveDolly( event ); 711 | 712 | } else if ( state === STATE.PAN ) { 713 | 714 | if ( scope.enablePan === false ) return; 715 | 716 | handleMouseMovePan( event ); 717 | 718 | } 719 | 720 | } 721 | 722 | function onMouseUp( event ) { 723 | 724 | if ( scope.enabled === false ) return; 725 | 726 | handleMouseUp( event ); 727 | 728 | document.removeEventListener( 'mousemove', onMouseMove, false ); 729 | document.removeEventListener( 'mouseup', onMouseUp, false ); 730 | 731 | scope.dispatchEvent( endEvent ); 732 | 733 | state = STATE.NONE; 734 | 735 | } 736 | 737 | function onMouseWheel( event ) { 738 | 739 | if ( scope.enabled === false || scope.enableZoom === false || ( state !== STATE.NONE && state !== STATE.ROTATE ) ) return; 740 | 741 | event.preventDefault(); 742 | event.stopPropagation(); 743 | 744 | handleMouseWheel( event ); 745 | 746 | scope.dispatchEvent( startEvent ); // not sure why these are here... 747 | scope.dispatchEvent( endEvent ); 748 | 749 | } 750 | 751 | function onKeyDown( event ) { 752 | 753 | if ( scope.enabled === false || scope.enableKeys === false || scope.enablePan === false ) return; 754 | 755 | handleKeyDown( event ); 756 | 757 | } 758 | 759 | function onTouchStart( event ) { 760 | 761 | if ( scope.enabled === false ) return; 762 | 763 | switch ( event.touches.length ) { 764 | 765 | case 1: // one-fingered touch: rotate 766 | 767 | if ( scope.enableRotate === false ) return; 768 | 769 | handleTouchStartRotate( event ); 770 | 771 | state = STATE.TOUCH_ROTATE; 772 | 773 | break; 774 | 775 | case 2: // two-fingered touch: dolly 776 | 777 | if ( scope.enableZoom === false ) return; 778 | 779 | handleTouchStartDolly( event ); 780 | 781 | state = STATE.TOUCH_DOLLY; 782 | 783 | break; 784 | 785 | case 3: // three-fingered touch: pan 786 | 787 | if ( scope.enablePan === false ) return; 788 | 789 | handleTouchStartPan( event ); 790 | 791 | state = STATE.TOUCH_PAN; 792 | 793 | break; 794 | 795 | default: 796 | 797 | state = STATE.NONE; 798 | 799 | } 800 | 801 | if ( state !== STATE.NONE ) { 802 | 803 | scope.dispatchEvent( startEvent ); 804 | 805 | } 806 | 807 | } 808 | 809 | function onTouchMove( event ) { 810 | 811 | if ( scope.enabled === false ) return; 812 | 813 | event.preventDefault(); 814 | event.stopPropagation(); 815 | 816 | switch ( event.touches.length ) { 817 | 818 | case 1: // one-fingered touch: rotate 819 | 820 | if ( scope.enableRotate === false ) return; 821 | if ( state !== STATE.TOUCH_ROTATE ) return; // is this needed?... 822 | 823 | handleTouchMoveRotate( event ); 824 | 825 | break; 826 | 827 | case 2: // two-fingered touch: dolly 828 | 829 | if ( scope.enableZoom === false ) return; 830 | if ( state !== STATE.TOUCH_DOLLY ) return; // is this needed?... 831 | 832 | handleTouchMoveDolly( event ); 833 | 834 | break; 835 | 836 | case 3: // three-fingered touch: pan 837 | 838 | if ( scope.enablePan === false ) return; 839 | if ( state !== STATE.TOUCH_PAN ) return; // is this needed?... 840 | 841 | handleTouchMovePan( event ); 842 | 843 | break; 844 | 845 | default: 846 | 847 | state = STATE.NONE; 848 | 849 | } 850 | 851 | } 852 | 853 | function onTouchEnd( event ) { 854 | 855 | if ( scope.enabled === false ) return; 856 | 857 | handleTouchEnd( event ); 858 | 859 | scope.dispatchEvent( endEvent ); 860 | 861 | state = STATE.NONE; 862 | 863 | } 864 | 865 | function onContextMenu( event ) { 866 | 867 | event.preventDefault(); 868 | 869 | } 870 | 871 | // 872 | 873 | scope.domElement.addEventListener( 'contextmenu', onContextMenu, false ); 874 | 875 | scope.domElement.addEventListener( 'mousedown', onMouseDown, false ); 876 | scope.domElement.addEventListener( 'wheel', onMouseWheel, false ); 877 | 878 | scope.domElement.addEventListener( 'touchstart', onTouchStart, false ); 879 | scope.domElement.addEventListener( 'touchend', onTouchEnd, false ); 880 | scope.domElement.addEventListener( 'touchmove', onTouchMove, false ); 881 | 882 | window.addEventListener( 'keydown', onKeyDown, false ); 883 | 884 | // force an update at start 885 | 886 | this.update(); 887 | 888 | }; 889 | 890 | THREE.OrbitControls.prototype = Object.create( THREE.EventDispatcher.prototype ); 891 | THREE.OrbitControls.prototype.constructor = THREE.OrbitControls; 892 | 893 | Object.defineProperties( THREE.OrbitControls.prototype, { 894 | 895 | center: { 896 | 897 | get: function () { 898 | 899 | console.warn( 'THREE.OrbitControls: .center has been renamed to .target' ); 900 | return this.target; 901 | 902 | } 903 | 904 | }, 905 | 906 | // backward compatibility 907 | 908 | noZoom: { 909 | 910 | get: function () { 911 | 912 | console.warn( 'THREE.OrbitControls: .noZoom has been deprecated. Use .enableZoom instead.' ); 913 | return ! this.enableZoom; 914 | 915 | }, 916 | 917 | set: function ( value ) { 918 | 919 | console.warn( 'THREE.OrbitControls: .noZoom has been deprecated. Use .enableZoom instead.' ); 920 | this.enableZoom = ! value; 921 | 922 | } 923 | 924 | }, 925 | 926 | noRotate: { 927 | 928 | get: function () { 929 | 930 | console.warn( 'THREE.OrbitControls: .noRotate has been deprecated. Use .enableRotate instead.' ); 931 | return ! this.enableRotate; 932 | 933 | }, 934 | 935 | set: function ( value ) { 936 | 937 | console.warn( 'THREE.OrbitControls: .noRotate has been deprecated. Use .enableRotate instead.' ); 938 | this.enableRotate = ! value; 939 | 940 | } 941 | 942 | }, 943 | 944 | noPan: { 945 | 946 | get: function () { 947 | 948 | console.warn( 'THREE.OrbitControls: .noPan has been deprecated. Use .enablePan instead.' ); 949 | return ! this.enablePan; 950 | 951 | }, 952 | 953 | set: function ( value ) { 954 | 955 | console.warn( 'THREE.OrbitControls: .noPan has been deprecated. Use .enablePan instead.' ); 956 | this.enablePan = ! value; 957 | 958 | } 959 | 960 | }, 961 | 962 | noKeys: { 963 | 964 | get: function () { 965 | 966 | console.warn( 'THREE.OrbitControls: .noKeys has been deprecated. Use .enableKeys instead.' ); 967 | return ! this.enableKeys; 968 | 969 | }, 970 | 971 | set: function ( value ) { 972 | 973 | console.warn( 'THREE.OrbitControls: .noKeys has been deprecated. Use .enableKeys instead.' ); 974 | this.enableKeys = ! value; 975 | 976 | } 977 | 978 | }, 979 | 980 | staticMoving: { 981 | 982 | get: function () { 983 | 984 | console.warn( 'THREE.OrbitControls: .staticMoving has been deprecated. Use .enableDamping instead.' ); 985 | return ! this.enableDamping; 986 | 987 | }, 988 | 989 | set: function ( value ) { 990 | 991 | console.warn( 'THREE.OrbitControls: .staticMoving has been deprecated. Use .enableDamping instead.' ); 992 | this.enableDamping = ! value; 993 | 994 | } 995 | 996 | }, 997 | 998 | dynamicDampingFactor: { 999 | 1000 | get: function () { 1001 | 1002 | console.warn( 'THREE.OrbitControls: .dynamicDampingFactor has been renamed. Use .dampingFactor instead.' ); 1003 | return this.dampingFactor; 1004 | 1005 | }, 1006 | 1007 | set: function ( value ) { 1008 | 1009 | console.warn( 'THREE.OrbitControls: .dynamicDampingFactor has been renamed. Use .dampingFactor instead.' ); 1010 | this.dampingFactor = value; 1011 | 1012 | } 1013 | 1014 | } 1015 | 1016 | } ); 1017 | -------------------------------------------------------------------------------- /src/assets/js/lib/ShadowMapViewer.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @author arya-s / https://github.com/arya-s 3 | * 4 | * This is a helper for visualising a given light's shadow map. 5 | * It works for shadow casting lights: THREE.DirectionalLight and THREE.SpotLight. 6 | * It renders out the shadow map and displays it on a HUD. 7 | * 8 | * Example usage: 9 | * 1) Include 34 | 35 | 36 |