├── .gitignore
├── 3d
├── Town.mtl
├── TownCZ8BM8CVyXfgpu7u.obj
├── TownColor.png
├── TownColor_updated.png
├── TownMaterial.png
├── TownPalette.png
├── TownZ5hejPFfRgxLZ0G9.obj
├── clouds.jpg
├── clouds.png
├── clouds_side.jpg
└── clouds_top.jpg
├── README.md
├── index.html
├── js
├── PointerLockControls.js
├── Sky.js
├── Water.js
├── build
│ └── three.module.js
├── dat.gui.module.js
└── examples
│ └── jsm
│ ├── libs
│ └── stats.module.js
│ ├── loaders
│ ├── DDSLoader.js
│ ├── MTLLoader.js
│ └── OBJLoader.js
│ ├── math
│ └── SimplexNoise.js
│ ├── postprocessing
│ ├── EffectComposer.js
│ ├── MaskPass.js
│ ├── Pass.js
│ ├── RenderPass.js
│ └── ShaderPass.js
│ └── shaders
│ ├── CopyShader.js
│ ├── DepthLimitedBlurShader.js
│ └── UnpackDepthRGBAShader.js
├── main.css
└── textures
└── waternormals.jpg
/.gitignore:
--------------------------------------------------------------------------------
1 | working_dir
2 |
--------------------------------------------------------------------------------
/3d/Town.mtl:
--------------------------------------------------------------------------------
1 | newmtl Town
2 | map_Kd ./TownColor.png
3 |
--------------------------------------------------------------------------------
/3d/TownColor.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/meliharvey/threescaper/08208379aeda35186e240f79ccb976f11a1a7d0c/3d/TownColor.png
--------------------------------------------------------------------------------
/3d/TownColor_updated.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/meliharvey/threescaper/08208379aeda35186e240f79ccb976f11a1a7d0c/3d/TownColor_updated.png
--------------------------------------------------------------------------------
/3d/TownMaterial.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/meliharvey/threescaper/08208379aeda35186e240f79ccb976f11a1a7d0c/3d/TownMaterial.png
--------------------------------------------------------------------------------
/3d/TownPalette.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/meliharvey/threescaper/08208379aeda35186e240f79ccb976f11a1a7d0c/3d/TownPalette.png
--------------------------------------------------------------------------------
/3d/clouds.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/meliharvey/threescaper/08208379aeda35186e240f79ccb976f11a1a7d0c/3d/clouds.jpg
--------------------------------------------------------------------------------
/3d/clouds.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/meliharvey/threescaper/08208379aeda35186e240f79ccb976f11a1a7d0c/3d/clouds.png
--------------------------------------------------------------------------------
/3d/clouds_side.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/meliharvey/threescaper/08208379aeda35186e240f79ccb976f11a1a7d0c/3d/clouds_side.jpg
--------------------------------------------------------------------------------
/3d/clouds_top.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/meliharvey/threescaper/08208379aeda35186e240f79ccb976f11a1a7d0c/3d/clouds_top.jpg
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # threescaper
2 |
3 | A website for loading Townscaper models into three.js.
4 |
5 | Visit the [live demo here](https://meliharvey.github.io/threescaper/)!
6 |
--------------------------------------------------------------------------------
/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Threescaper
6 |
7 |
8 |
9 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
Drag and dropTownscaper OBJ file here
53 |
54 |
55 |
Click to play
56 |
57 | Move: WASD
58 | Jump: SPACE
59 | Look: MOUSE
60 | Sprint: SHIFT
61 |
62 |
63 |
64 |
65 |
751 |
752 |
753 |
754 |
--------------------------------------------------------------------------------
/js/PointerLockControls.js:
--------------------------------------------------------------------------------
1 | import {
2 | Euler,
3 | EventDispatcher,
4 | Vector3
5 | } from "./build/three.module.js";
6 |
7 | var PointerLockControls = function ( camera, domElement ) {
8 |
9 | if ( domElement === undefined ) {
10 |
11 | console.warn( 'THREE.PointerLockControls: The second parameter "domElement" is now mandatory.' );
12 | domElement = document.body;
13 |
14 | }
15 |
16 | this.domElement = domElement;
17 | this.isLocked = false;
18 |
19 | // Set to constrain the pitch of the camera
20 | // Range is 0 to Math.PI radians
21 | this.minPolarAngle = 0; // radians
22 | this.maxPolarAngle = Math.PI; // radians
23 |
24 | //
25 | // internals
26 | //
27 |
28 | var scope = this;
29 |
30 | var changeEvent = { type: 'change' };
31 | var lockEvent = { type: 'lock' };
32 | var unlockEvent = { type: 'unlock' };
33 |
34 | var euler = new Euler( 0, 0, 0, 'YXZ' );
35 |
36 | var PI_2 = Math.PI / 2;
37 |
38 | var vec = new Vector3();
39 |
40 | function onMouseMove( event ) {
41 |
42 | if ( scope.isLocked === false ) return;
43 |
44 | var movementX = event.movementX || event.mozMovementX || event.webkitMovementX || 0;
45 | var movementY = event.movementY || event.mozMovementY || event.webkitMovementY || 0;
46 |
47 | euler.setFromQuaternion( camera.quaternion );
48 |
49 | euler.y -= movementX * 0.002;
50 | euler.x -= movementY * 0.002;
51 |
52 | euler.x = Math.max( PI_2 - scope.maxPolarAngle, Math.min( PI_2 - scope.minPolarAngle, euler.x ) );
53 |
54 | camera.quaternion.setFromEuler( euler );
55 |
56 | scope.dispatchEvent( changeEvent );
57 |
58 | }
59 |
60 | function onPointerlockChange() {
61 |
62 | if ( scope.domElement.ownerDocument.pointerLockElement === scope.domElement ) {
63 |
64 | scope.dispatchEvent( lockEvent );
65 |
66 | scope.isLocked = true;
67 |
68 | } else {
69 |
70 | scope.dispatchEvent( unlockEvent );
71 |
72 | scope.isLocked = false;
73 |
74 | }
75 |
76 | }
77 |
78 | function onPointerlockError() {
79 |
80 | console.error( 'THREE.PointerLockControls: Unable to use Pointer Lock API' );
81 |
82 | }
83 |
84 | this.connect = function () {
85 |
86 | scope.domElement.ownerDocument.addEventListener( 'mousemove', onMouseMove, false );
87 | scope.domElement.ownerDocument.addEventListener( 'pointerlockchange', onPointerlockChange, false );
88 | scope.domElement.ownerDocument.addEventListener( 'pointerlockerror', onPointerlockError, false );
89 |
90 | };
91 |
92 | this.disconnect = function () {
93 |
94 | scope.domElement.ownerDocument.removeEventListener( 'mousemove', onMouseMove, false );
95 | scope.domElement.ownerDocument.removeEventListener( 'pointerlockchange', onPointerlockChange, false );
96 | scope.domElement.ownerDocument.removeEventListener( 'pointerlockerror', onPointerlockError, false );
97 |
98 | };
99 |
100 | this.dispose = function () {
101 |
102 | this.disconnect();
103 |
104 | };
105 |
106 | this.getObject = function () { // retaining this method for backward compatibility
107 |
108 | return camera;
109 |
110 | };
111 |
112 | this.getDirection = function () {
113 |
114 | var direction = new Vector3( 0, 0, - 1 );
115 |
116 | return function ( v ) {
117 |
118 | return v.copy( direction ).applyQuaternion( camera.quaternion );
119 |
120 | };
121 |
122 | }();
123 |
124 | this.moveForward = function ( distance ) {
125 |
126 | // move forward parallel to the xz-plane
127 | // assumes camera.up is y-up
128 |
129 | vec.setFromMatrixColumn( camera.matrix, 0 );
130 |
131 | vec.crossVectors( camera.up, vec );
132 |
133 | camera.position.addScaledVector( vec, distance );
134 |
135 | };
136 |
137 | this.moveRight = function ( distance ) {
138 |
139 | vec.setFromMatrixColumn( camera.matrix, 0 );
140 |
141 | camera.position.addScaledVector( vec, distance );
142 |
143 | };
144 |
145 | this.lock = function () {
146 |
147 | this.domElement.requestPointerLock();
148 |
149 | };
150 |
151 | this.unlock = function () {
152 |
153 | scope.domElement.ownerDocument.exitPointerLock();
154 |
155 | };
156 |
157 | this.connect();
158 |
159 | };
160 |
161 | PointerLockControls.prototype = Object.create( EventDispatcher.prototype );
162 | PointerLockControls.prototype.constructor = PointerLockControls;
163 |
164 | export { PointerLockControls };
165 |
--------------------------------------------------------------------------------
/js/Sky.js:
--------------------------------------------------------------------------------
1 | import {
2 | BackSide,
3 | BoxBufferGeometry,
4 | Mesh,
5 | ShaderMaterial,
6 | UniformsUtils,
7 | Vector3
8 | } from "./build/three.module.js";
9 |
10 | /**
11 | * Based on "A Practical Analytic Model for Daylight"
12 | * aka The Preetham Model, the de facto standard analytic skydome model
13 | * https://www.researchgate.net/publication/220720443_A_Practical_Analytic_Model_for_Daylight
14 | *
15 | * First implemented by Simon Wallner
16 | * http://www.simonwallner.at/projects/atmospheric-scattering
17 | *
18 | * Improved by Martin Upitis
19 | * http://blenderartists.org/forum/showthread.php?245954-preethams-sky-impementation-HDR
20 | *
21 | * Three.js integration by zz85 http://twitter.com/blurspline
22 | */
23 |
24 | var Sky = function () {
25 |
26 | var shader = Sky.SkyShader;
27 |
28 | var material = new ShaderMaterial( {
29 | name: 'SkyShader',
30 | fragmentShader: shader.fragmentShader,
31 | vertexShader: shader.vertexShader,
32 | uniforms: UniformsUtils.clone( shader.uniforms ),
33 | side: BackSide,
34 | depthWrite: false
35 | } );
36 |
37 | Mesh.call( this, new BoxBufferGeometry( 1, 1, 1 ), material );
38 |
39 | };
40 |
41 | Sky.prototype = Object.create( Mesh.prototype );
42 |
43 | Sky.SkyShader = {
44 |
45 | uniforms: {
46 | "turbidity": { value: 2 },
47 | "rayleigh": { value: 1 },
48 | "mieCoefficient": { value: 0.005 },
49 | "mieDirectionalG": { value: 0.8 },
50 | "sunPosition": { value: new Vector3() },
51 | "up": { value: new Vector3( 0, 1, 0 ) }
52 | },
53 |
54 | vertexShader: [
55 | 'uniform vec3 sunPosition;',
56 | 'uniform float rayleigh;',
57 | 'uniform float turbidity;',
58 | 'uniform float mieCoefficient;',
59 | 'uniform vec3 up;',
60 |
61 | 'varying vec3 vWorldPosition;',
62 | 'varying vec3 vSunDirection;',
63 | 'varying float vSunfade;',
64 | 'varying vec3 vBetaR;',
65 | 'varying vec3 vBetaM;',
66 | 'varying float vSunE;',
67 |
68 | // constants for atmospheric scattering
69 | 'const float e = 2.71828182845904523536028747135266249775724709369995957;',
70 | 'const float pi = 3.141592653589793238462643383279502884197169;',
71 |
72 | // wavelength of used primaries, according to preetham
73 | 'const vec3 lambda = vec3( 680E-9, 550E-9, 450E-9 );',
74 | // this pre-calcuation replaces older TotalRayleigh(vec3 lambda) function:
75 | // (8.0 * pow(pi, 3.0) * pow(pow(n, 2.0) - 1.0, 2.0) * (6.0 + 3.0 * pn)) / (3.0 * N * pow(lambda, vec3(4.0)) * (6.0 - 7.0 * pn))
76 | 'const vec3 totalRayleigh = vec3( 5.804542996261093E-6, 1.3562911419845635E-5, 3.0265902468824876E-5 );',
77 |
78 | // mie stuff
79 | // K coefficient for the primaries
80 | 'const float v = 4.0;',
81 | 'const vec3 K = vec3( 0.686, 0.678, 0.666 );',
82 | // MieConst = pi * pow( ( 2.0 * pi ) / lambda, vec3( v - 2.0 ) ) * K
83 | 'const vec3 MieConst = vec3( 1.8399918514433978E14, 2.7798023919660528E14, 4.0790479543861094E14 );',
84 |
85 | // earth shadow hack
86 | // cutoffAngle = pi / 1.95;
87 | 'const float cutoffAngle = 1.6110731556870734;',
88 | 'const float steepness = 1.5;',
89 | 'const float EE = 1000.0;',
90 |
91 | 'float sunIntensity( float zenithAngleCos ) {',
92 | ' zenithAngleCos = clamp( zenithAngleCos, -1.0, 1.0 );',
93 | ' return EE * max( 0.0, 1.0 - pow( e, -( ( cutoffAngle - acos( zenithAngleCos ) ) / steepness ) ) );',
94 | '}',
95 |
96 | 'vec3 totalMie( float T ) {',
97 | ' float c = ( 0.2 * T ) * 10E-18;',
98 | ' return 0.434 * c * MieConst;',
99 | '}',
100 |
101 | 'void main() {',
102 |
103 | ' vec4 worldPosition = modelMatrix * vec4( position, 1.0 );',
104 | ' vWorldPosition = worldPosition.xyz;',
105 |
106 | ' gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );',
107 | ' gl_Position.z = gl_Position.w;', // set z to camera.far
108 |
109 | ' vSunDirection = normalize( sunPosition );',
110 |
111 | ' vSunE = sunIntensity( dot( vSunDirection, up ) );',
112 |
113 | ' vSunfade = 1.0 - clamp( 1.0 - exp( ( sunPosition.y / 450000.0 ) ), 0.0, 1.0 );',
114 |
115 | ' float rayleighCoefficient = rayleigh - ( 1.0 * ( 1.0 - vSunfade ) );',
116 |
117 | // extinction (absorbtion + out scattering)
118 | // rayleigh coefficients
119 | ' vBetaR = totalRayleigh * rayleighCoefficient;',
120 |
121 | // mie coefficients
122 | ' vBetaM = totalMie( turbidity ) * mieCoefficient;',
123 |
124 | '}'
125 | ].join( '\n' ),
126 |
127 | fragmentShader: [
128 | 'varying vec3 vWorldPosition;',
129 | 'varying vec3 vSunDirection;',
130 | 'varying float vSunfade;',
131 | 'varying vec3 vBetaR;',
132 | 'varying vec3 vBetaM;',
133 | 'varying float vSunE;',
134 |
135 | 'uniform float mieDirectionalG;',
136 | 'uniform vec3 up;',
137 |
138 | 'const vec3 cameraPos = vec3( 0.0, 0.0, 0.0 );',
139 |
140 | // constants for atmospheric scattering
141 | 'const float pi = 3.141592653589793238462643383279502884197169;',
142 |
143 | 'const float n = 1.0003;', // refractive index of air
144 | 'const float N = 2.545E25;', // number of molecules per unit volume for air at 288.15K and 1013mb (sea level -45 celsius)
145 |
146 | // optical length at zenith for molecules
147 | 'const float rayleighZenithLength = 8.4E3;',
148 | 'const float mieZenithLength = 1.25E3;',
149 | // 66 arc seconds -> degrees, and the cosine of that
150 | 'const float sunAngularDiameterCos = 0.999956676946448443553574619906976478926848692873900859324;',
151 |
152 | // 3.0 / ( 16.0 * pi )
153 | 'const float THREE_OVER_SIXTEENPI = 0.05968310365946075;',
154 | // 1.0 / ( 4.0 * pi )
155 | 'const float ONE_OVER_FOURPI = 0.07957747154594767;',
156 |
157 | 'float rayleighPhase( float cosTheta ) {',
158 | ' return THREE_OVER_SIXTEENPI * ( 1.0 + pow( cosTheta, 2.0 ) );',
159 | '}',
160 |
161 | 'float hgPhase( float cosTheta, float g ) {',
162 | ' float g2 = pow( g, 2.0 );',
163 | ' float inverse = 1.0 / pow( 1.0 - 2.0 * g * cosTheta + g2, 1.5 );',
164 | ' return ONE_OVER_FOURPI * ( ( 1.0 - g2 ) * inverse );',
165 | '}',
166 |
167 | 'void main() {',
168 |
169 | ' vec3 direction = normalize( vWorldPosition - cameraPos );',
170 |
171 | // optical length
172 | // cutoff angle at 90 to avoid singularity in next formula.
173 | ' float zenithAngle = acos( max( 0.0, dot( up, direction ) ) );',
174 | ' float inverse = 1.0 / ( cos( zenithAngle ) + 0.15 * pow( 93.885 - ( ( zenithAngle * 180.0 ) / pi ), -1.253 ) );',
175 | ' float sR = rayleighZenithLength * inverse;',
176 | ' float sM = mieZenithLength * inverse;',
177 |
178 | // combined extinction factor
179 | ' vec3 Fex = exp( -( vBetaR * sR + vBetaM * sM ) );',
180 |
181 | // in scattering
182 | ' float cosTheta = dot( direction, vSunDirection );',
183 |
184 | ' float rPhase = rayleighPhase( cosTheta * 0.5 + 0.5 );',
185 | ' vec3 betaRTheta = vBetaR * rPhase;',
186 |
187 | ' float mPhase = hgPhase( cosTheta, mieDirectionalG );',
188 | ' vec3 betaMTheta = vBetaM * mPhase;',
189 |
190 | ' vec3 Lin = pow( vSunE * ( ( betaRTheta + betaMTheta ) / ( vBetaR + vBetaM ) ) * ( 1.0 - Fex ), vec3( 1.5 ) );',
191 | ' Lin *= mix( vec3( 1.0 ), pow( vSunE * ( ( betaRTheta + betaMTheta ) / ( vBetaR + vBetaM ) ) * Fex, vec3( 1.0 / 2.0 ) ), clamp( pow( 1.0 - dot( up, vSunDirection ), 5.0 ), 0.0, 1.0 ) );',
192 |
193 | // nightsky
194 | ' float theta = acos( direction.y ); // elevation --> y-axis, [-pi/2, pi/2]',
195 | ' float phi = atan( direction.z, direction.x ); // azimuth --> x-axis [-pi/2, pi/2]',
196 | ' vec2 uv = vec2( phi, theta ) / vec2( 2.0 * pi, pi ) + vec2( 0.5, 0.0 );',
197 | ' vec3 L0 = vec3( 0.1 ) * Fex;',
198 |
199 | // composition + solar disc
200 | ' float sundisk = smoothstep( sunAngularDiameterCos, sunAngularDiameterCos + 0.00002, cosTheta );',
201 | ' L0 += ( vSunE * 19000.0 * Fex ) * sundisk;',
202 |
203 | ' vec3 texColor = ( Lin + L0 ) * 0.04 + vec3( 0.0, 0.0003, 0.00075 );',
204 |
205 | ' vec3 retColor = pow( texColor, vec3( 1.0 / ( 1.2 + ( 1.2 * vSunfade ) ) ) );',
206 |
207 | ' gl_FragColor = vec4( retColor, 1.0 );',
208 |
209 | '#include ',
210 | '#include ',
211 |
212 | '}'
213 | ].join( '\n' )
214 |
215 | };
216 |
217 | export { Sky };
218 |
--------------------------------------------------------------------------------
/js/Water.js:
--------------------------------------------------------------------------------
1 | import {
2 | Color,
3 | FrontSide,
4 | LinearEncoding,
5 | LinearFilter,
6 | MathUtils,
7 | Matrix4,
8 | Mesh,
9 | NoToneMapping,
10 | PerspectiveCamera,
11 | Plane,
12 | RGBFormat,
13 | ShaderMaterial,
14 | UniformsLib,
15 | UniformsUtils,
16 | Vector3,
17 | Vector4,
18 | WebGLRenderTarget
19 | } from "./build/three.module.js";
20 |
21 | /**
22 | * Work based on :
23 | * http://slayvin.net : Flat mirror for three.js
24 | * http://www.adelphi.edu/~stemkoski : An implementation of water shader based on the flat mirror
25 | * http://29a.ch/ && http://29a.ch/slides/2012/webglwater/ : Water shader explanations in WebGL
26 | */
27 |
28 | var Water = function ( geometry, options ) {
29 |
30 | Mesh.call( this, geometry );
31 |
32 | var scope = this;
33 |
34 | options = options || {};
35 |
36 | var textureWidth = options.textureWidth !== undefined ? options.textureWidth : 512;
37 | var textureHeight = options.textureHeight !== undefined ? options.textureHeight : 512;
38 |
39 | var clipBias = options.clipBias !== undefined ? options.clipBias : 0.0;
40 | var alpha = options.alpha !== undefined ? options.alpha : 1.0;
41 | var time = options.time !== undefined ? options.time : 0.0;
42 | var normalSampler = options.waterNormals !== undefined ? options.waterNormals : null;
43 | var sunDirection = options.sunDirection !== undefined ? options.sunDirection : new Vector3( 0.70707, 0.70707, 0.0 );
44 | var sunColor = new Color( options.sunColor !== undefined ? options.sunColor : 0xffffff );
45 | var waterColor = new Color( options.waterColor !== undefined ? options.waterColor : 0x7F7F7F );
46 | var eye = options.eye !== undefined ? options.eye : new Vector3( 0, 0, 0 );
47 | var distortionScale = options.distortionScale !== undefined ? options.distortionScale : 20.0;
48 | var side = options.side !== undefined ? options.side : FrontSide;
49 | var fog = options.fog !== undefined ? options.fog : false;
50 |
51 | //
52 |
53 | var mirrorPlane = new Plane();
54 | var normal = new Vector3();
55 | var mirrorWorldPosition = new Vector3();
56 | var cameraWorldPosition = new Vector3();
57 | var rotationMatrix = new Matrix4();
58 | var lookAtPosition = new Vector3( 0, 0, - 1 );
59 | var clipPlane = new Vector4();
60 |
61 | var view = new Vector3();
62 | var target = new Vector3();
63 | var q = new Vector4();
64 |
65 | var textureMatrix = new Matrix4();
66 |
67 | var mirrorCamera = new PerspectiveCamera();
68 |
69 | var parameters = {
70 | minFilter: LinearFilter,
71 | magFilter: LinearFilter,
72 | format: RGBFormat
73 | };
74 |
75 | var renderTarget = new WebGLRenderTarget( textureWidth, textureHeight, parameters );
76 |
77 | if ( ! MathUtils.isPowerOfTwo( textureWidth ) || ! MathUtils.isPowerOfTwo( textureHeight ) ) {
78 |
79 | renderTarget.texture.generateMipmaps = false;
80 |
81 | }
82 |
83 | var mirrorShader = {
84 |
85 | uniforms: UniformsUtils.merge( [
86 | UniformsLib[ 'fog' ],
87 | UniformsLib[ 'lights' ],
88 | {
89 | "normalSampler": { value: null },
90 | "mirrorSampler": { value: null },
91 | "alpha": { value: 1.0 },
92 | "time": { value: 0.0 },
93 | "size": { value: 1.0 },
94 | "distortionScale": { value: 20.0 },
95 | "textureMatrix": { value: new Matrix4() },
96 | "sunColor": { value: new Color( 0x7F7F7F ) },
97 | "sunDirection": { value: new Vector3( 0.70707, 0.70707, 0 ) },
98 | "eye": { value: new Vector3() },
99 | "waterColor": { value: new Color( 0x555555 ) }
100 | }
101 | ] ),
102 |
103 | vertexShader: [
104 | 'uniform mat4 textureMatrix;',
105 | 'uniform float time;',
106 |
107 | 'varying vec4 mirrorCoord;',
108 | 'varying vec4 worldPosition;',
109 |
110 | '#include ',
111 | '#include ',
112 | '#include ',
113 | '#include ',
114 |
115 | 'void main() {',
116 | ' mirrorCoord = modelMatrix * vec4( position, 1.0 );',
117 | ' worldPosition = mirrorCoord.xyzw;',
118 | ' mirrorCoord = textureMatrix * mirrorCoord;',
119 | ' vec4 mvPosition = modelViewMatrix * vec4( position, 1.0 );',
120 | ' gl_Position = projectionMatrix * mvPosition;',
121 |
122 | '#include ',
123 | '#include ',
124 | '#include ',
125 | '#include ',
126 | '#include ',
127 | '}'
128 | ].join( '\n' ),
129 |
130 | fragmentShader: [
131 | 'uniform sampler2D mirrorSampler;',
132 | 'uniform float alpha;',
133 | 'uniform float time;',
134 | 'uniform float size;',
135 | 'uniform float distortionScale;',
136 | 'uniform sampler2D normalSampler;',
137 | 'uniform vec3 sunColor;',
138 | 'uniform vec3 sunDirection;',
139 | 'uniform vec3 eye;',
140 | 'uniform vec3 waterColor;',
141 |
142 | 'varying vec4 mirrorCoord;',
143 | 'varying vec4 worldPosition;',
144 |
145 |
146 |
147 | 'vec4 getNoise( vec2 uv ) {',
148 | ' float speed = time / 6.0;',
149 | ' vec2 uv0 = ( uv / 103.0 ) + vec2(speed / 17.0, speed / 29.0);',
150 | ' vec2 uv1 = uv / 107.0-vec2( speed / -19.0, speed / 31.0 );',
151 | ' vec2 uv2 = uv / vec2( 8907.0, 9803.0 ) + vec2( speed / 101.0, speed / 97.0 );',
152 | ' vec2 uv3 = uv / vec2( 1091.0, 1027.0 ) - vec2( speed / 109.0, speed / -113.0 );',
153 | ' vec4 noise = texture2D( normalSampler, uv0 ) +',
154 | ' texture2D( normalSampler, uv1 ) +',
155 | ' texture2D( normalSampler, uv2 ) +',
156 | ' texture2D( normalSampler, uv3 );',
157 | ' return noise * 0.5 - 1.0;',
158 | '}',
159 |
160 | 'void sunLight( const vec3 surfaceNormal, const vec3 eyeDirection, float shiny, float spec, float diffuse, inout vec3 diffuseColor, inout vec3 specularColor ) {',
161 | ' vec3 reflection = normalize( reflect( -sunDirection, surfaceNormal ) );',
162 | ' float direction = max( 0.0, dot( eyeDirection, reflection ) );',
163 | ' specularColor += pow( direction, shiny ) * sunColor * spec;',
164 | ' diffuseColor += max( dot( sunDirection, surfaceNormal ), 0.0 ) * sunColor * diffuse;',
165 | '}',
166 |
167 | '#include ',
168 | '#include ',
169 | '#include ',
170 | '#include ',
171 | '#include ',
172 | '#include ',
173 | '#include ',
174 | '#include ',
175 |
176 | 'void main() {',
177 |
178 | '#include ',
179 | ' vec4 noise = getNoise( worldPosition.xz * size );',
180 | ' vec3 surfaceNormal = normalize( noise.xzy * vec3( 1.5, 1.0, 1.5 ) );',
181 |
182 | ' vec3 diffuseLight = vec3(0.0);',
183 | ' vec3 specularLight = vec3(0.0);',
184 |
185 | ' vec3 worldToEye = eye-worldPosition.xyz;',
186 | ' vec3 eyeDirection = normalize( worldToEye );',
187 | ' sunLight( surfaceNormal, eyeDirection, 100.0, 2.0, 0.5, diffuseLight, specularLight );',
188 |
189 | ' float distance = length(worldToEye);',
190 |
191 | ' vec2 distortion = surfaceNormal.xz * ( 0.001 + 1.0 / distance ) * distortionScale;',
192 | ' vec3 reflectionSample = vec3( texture2D( mirrorSampler, mirrorCoord.xy / mirrorCoord.w + distortion ) );',
193 |
194 | ' float theta = max( dot( eyeDirection, surfaceNormal ), 0.0 );',
195 | ' float rf0 = 0.3;',
196 | ' float reflectance = rf0 + ( 1.0 - rf0 ) * pow( ( 1.0 - theta ), 5.0 );',
197 | ' vec3 scatter = max( 0.0, dot( surfaceNormal, eyeDirection ) ) * waterColor;',
198 | ' vec3 albedo = mix( ( sunColor * diffuseLight * 0.3 + scatter ) * getShadowMask(), ( vec3( 0.0 ) + reflectionSample * 1.0 + reflectionSample * specularLight ), reflectance);',
199 | ' vec3 outgoingLight = albedo;',
200 | ' gl_FragColor = vec4( outgoingLight, alpha );',
201 |
202 | '#include ',
203 | '#include ',
204 | '}'
205 | ].join( '\n' )
206 |
207 | };
208 |
209 | var material = new ShaderMaterial( {
210 | fragmentShader: mirrorShader.fragmentShader,
211 | vertexShader: mirrorShader.vertexShader,
212 | uniforms: UniformsUtils.clone( mirrorShader.uniforms ),
213 | lights: true,
214 | side: side,
215 | fog: fog
216 | } );
217 |
218 | material.uniforms[ "mirrorSampler" ].value = renderTarget.texture;
219 | material.uniforms[ "textureMatrix" ].value = textureMatrix;
220 | material.uniforms[ "alpha" ].value = alpha;
221 | material.uniforms[ "time" ].value = time;
222 | material.uniforms[ "normalSampler" ].value = normalSampler;
223 | material.uniforms[ "sunColor" ].value = sunColor;
224 | material.uniforms[ "waterColor" ].value = waterColor;
225 | material.uniforms[ "sunDirection" ].value = sunDirection;
226 | material.uniforms[ "distortionScale" ].value = distortionScale;
227 |
228 | material.uniforms[ "eye" ].value = eye;
229 |
230 | scope.material = material;
231 |
232 | scope.onBeforeRender = function ( renderer, scene, camera ) {
233 |
234 | mirrorWorldPosition.setFromMatrixPosition( scope.matrixWorld );
235 | cameraWorldPosition.setFromMatrixPosition( camera.matrixWorld );
236 |
237 | rotationMatrix.extractRotation( scope.matrixWorld );
238 |
239 | normal.set( 0, 0, 1 );
240 | normal.applyMatrix4( rotationMatrix );
241 |
242 | view.subVectors( mirrorWorldPosition, cameraWorldPosition );
243 |
244 | // Avoid rendering when mirror is facing away
245 |
246 | if ( view.dot( normal ) > 0 ) return;
247 |
248 | view.reflect( normal ).negate();
249 | view.add( mirrorWorldPosition );
250 |
251 | rotationMatrix.extractRotation( camera.matrixWorld );
252 |
253 | lookAtPosition.set( 0, 0, - 1 );
254 | lookAtPosition.applyMatrix4( rotationMatrix );
255 | lookAtPosition.add( cameraWorldPosition );
256 |
257 | target.subVectors( mirrorWorldPosition, lookAtPosition );
258 | target.reflect( normal ).negate();
259 | target.add( mirrorWorldPosition );
260 |
261 | mirrorCamera.position.copy( view );
262 | mirrorCamera.up.set( 0, 1, 0 );
263 | mirrorCamera.up.applyMatrix4( rotationMatrix );
264 | mirrorCamera.up.reflect( normal );
265 | mirrorCamera.lookAt( target );
266 |
267 | mirrorCamera.far = camera.far; // Used in WebGLBackground
268 |
269 | mirrorCamera.updateMatrixWorld();
270 | mirrorCamera.projectionMatrix.copy( camera.projectionMatrix );
271 |
272 | // Update the texture matrix
273 | textureMatrix.set(
274 | 0.5, 0.0, 0.0, 0.5,
275 | 0.0, 0.5, 0.0, 0.5,
276 | 0.0, 0.0, 0.5, 0.5,
277 | 0.0, 0.0, 0.0, 1.0
278 | );
279 | textureMatrix.multiply( mirrorCamera.projectionMatrix );
280 | textureMatrix.multiply( mirrorCamera.matrixWorldInverse );
281 |
282 | // Now update projection matrix with new clip plane, implementing code from: http://www.terathon.com/code/oblique.html
283 | // Paper explaining this technique: http://www.terathon.com/lengyel/Lengyel-Oblique.pdf
284 | mirrorPlane.setFromNormalAndCoplanarPoint( normal, mirrorWorldPosition );
285 | mirrorPlane.applyMatrix4( mirrorCamera.matrixWorldInverse );
286 |
287 | clipPlane.set( mirrorPlane.normal.x, mirrorPlane.normal.y, mirrorPlane.normal.z, mirrorPlane.constant );
288 |
289 | var projectionMatrix = mirrorCamera.projectionMatrix;
290 |
291 | q.x = ( Math.sign( clipPlane.x ) + projectionMatrix.elements[ 8 ] ) / projectionMatrix.elements[ 0 ];
292 | q.y = ( Math.sign( clipPlane.y ) + projectionMatrix.elements[ 9 ] ) / projectionMatrix.elements[ 5 ];
293 | q.z = - 1.0;
294 | q.w = ( 1.0 + projectionMatrix.elements[ 10 ] ) / projectionMatrix.elements[ 14 ];
295 |
296 | // Calculate the scaled plane vector
297 | clipPlane.multiplyScalar( 2.0 / clipPlane.dot( q ) );
298 |
299 | // Replacing the third row of the projection matrix
300 | projectionMatrix.elements[ 2 ] = clipPlane.x;
301 | projectionMatrix.elements[ 6 ] = clipPlane.y;
302 | projectionMatrix.elements[ 10 ] = clipPlane.z + 1.0 - clipBias;
303 | projectionMatrix.elements[ 14 ] = clipPlane.w;
304 |
305 | eye.setFromMatrixPosition( camera.matrixWorld );
306 |
307 | // Render
308 |
309 | if ( renderer.outputEncoding !== LinearEncoding ) {
310 |
311 | console.warn( 'THREE.Water: WebGLRenderer must use LinearEncoding as outputEncoding.' );
312 | scope.onBeforeRender = function () {};
313 |
314 | return;
315 |
316 | }
317 |
318 | if ( renderer.toneMapping !== NoToneMapping ) {
319 |
320 | console.warn( 'THREE.Water: WebGLRenderer must use NoToneMapping as toneMapping.' );
321 | scope.onBeforeRender = function () {};
322 |
323 | return;
324 |
325 | }
326 |
327 | var currentRenderTarget = renderer.getRenderTarget();
328 |
329 | var currentXrEnabled = renderer.xr.enabled;
330 | var currentShadowAutoUpdate = renderer.shadowMap.autoUpdate;
331 |
332 | scope.visible = false;
333 |
334 | renderer.xr.enabled = false; // Avoid camera modification and recursion
335 | renderer.shadowMap.autoUpdate = false; // Avoid re-computing shadows
336 |
337 | renderer.setRenderTarget( renderTarget );
338 |
339 | renderer.state.buffers.depth.setMask( true ); // make sure the depth buffer is writable so it can be properly cleared, see #18897
340 |
341 | if ( renderer.autoClear === false ) renderer.clear();
342 | renderer.render( scene, mirrorCamera );
343 |
344 | scope.visible = true;
345 |
346 | renderer.xr.enabled = currentXrEnabled;
347 | renderer.shadowMap.autoUpdate = currentShadowAutoUpdate;
348 |
349 | renderer.setRenderTarget( currentRenderTarget );
350 |
351 | // Restore viewport
352 |
353 | var viewport = camera.viewport;
354 |
355 | if ( viewport !== undefined ) {
356 |
357 | renderer.state.viewport( viewport );
358 |
359 | }
360 |
361 | };
362 |
363 | };
364 |
365 | Water.prototype = Object.create( Mesh.prototype );
366 | Water.prototype.constructor = Water;
367 |
368 | export { Water };
369 |
--------------------------------------------------------------------------------
/js/examples/jsm/libs/stats.module.js:
--------------------------------------------------------------------------------
1 | var Stats = function () {
2 |
3 | var mode = 0;
4 |
5 | var container = document.createElement( 'div' );
6 | container.style.cssText = 'position:fixed;top:0;left:0;cursor:pointer;opacity:0.9;z-index:10000';
7 | container.addEventListener( 'click', function ( event ) {
8 |
9 | event.preventDefault();
10 | showPanel( ++ mode % container.children.length );
11 |
12 | }, false );
13 |
14 | //
15 |
16 | function addPanel( panel ) {
17 |
18 | container.appendChild( panel.dom );
19 | return panel;
20 |
21 | }
22 |
23 | function showPanel( id ) {
24 |
25 | for ( var i = 0; i < container.children.length; i ++ ) {
26 |
27 | container.children[ i ].style.display = i === id ? 'block' : 'none';
28 |
29 | }
30 |
31 | mode = id;
32 |
33 | }
34 |
35 | //
36 |
37 | var beginTime = ( performance || Date ).now(), prevTime = beginTime, frames = 0;
38 |
39 | var fpsPanel = addPanel( new Stats.Panel( 'FPS', '#0ff', '#002' ) );
40 | var msPanel = addPanel( new Stats.Panel( 'MS', '#0f0', '#020' ) );
41 |
42 | if ( self.performance && self.performance.memory ) {
43 |
44 | var memPanel = addPanel( new Stats.Panel( 'MB', '#f08', '#201' ) );
45 |
46 | }
47 |
48 | showPanel( 0 );
49 |
50 | return {
51 |
52 | REVISION: 16,
53 |
54 | dom: container,
55 |
56 | addPanel: addPanel,
57 | showPanel: showPanel,
58 |
59 | begin: function () {
60 |
61 | beginTime = ( performance || Date ).now();
62 |
63 | },
64 |
65 | end: function () {
66 |
67 | frames ++;
68 |
69 | var time = ( performance || Date ).now();
70 |
71 | msPanel.update( time - beginTime, 200 );
72 |
73 | if ( time >= prevTime + 1000 ) {
74 |
75 | fpsPanel.update( ( frames * 1000 ) / ( time - prevTime ), 100 );
76 |
77 | prevTime = time;
78 | frames = 0;
79 |
80 | if ( memPanel ) {
81 |
82 | var memory = performance.memory;
83 | memPanel.update( memory.usedJSHeapSize / 1048576, memory.jsHeapSizeLimit / 1048576 );
84 |
85 | }
86 |
87 | }
88 |
89 | return time;
90 |
91 | },
92 |
93 | update: function () {
94 |
95 | beginTime = this.end();
96 |
97 | },
98 |
99 | // Backwards Compatibility
100 |
101 | domElement: container,
102 | setMode: showPanel
103 |
104 | };
105 |
106 | };
107 |
108 | Stats.Panel = function ( name, fg, bg ) {
109 |
110 | var min = Infinity, max = 0, round = Math.round;
111 | var PR = round( window.devicePixelRatio || 1 );
112 |
113 | var WIDTH = 80 * PR, HEIGHT = 48 * PR,
114 | TEXT_X = 3 * PR, TEXT_Y = 2 * PR,
115 | GRAPH_X = 3 * PR, GRAPH_Y = 15 * PR,
116 | GRAPH_WIDTH = 74 * PR, GRAPH_HEIGHT = 30 * PR;
117 |
118 | var canvas = document.createElement( 'canvas' );
119 | canvas.width = WIDTH;
120 | canvas.height = HEIGHT;
121 | canvas.style.cssText = 'width:80px;height:48px';
122 |
123 | var context = canvas.getContext( '2d' );
124 | context.font = 'bold ' + ( 9 * PR ) + 'px Helvetica,Arial,sans-serif';
125 | context.textBaseline = 'top';
126 |
127 | context.fillStyle = bg;
128 | context.fillRect( 0, 0, WIDTH, HEIGHT );
129 |
130 | context.fillStyle = fg;
131 | context.fillText( name, TEXT_X, TEXT_Y );
132 | context.fillRect( GRAPH_X, GRAPH_Y, GRAPH_WIDTH, GRAPH_HEIGHT );
133 |
134 | context.fillStyle = bg;
135 | context.globalAlpha = 0.9;
136 | context.fillRect( GRAPH_X, GRAPH_Y, GRAPH_WIDTH, GRAPH_HEIGHT );
137 |
138 | return {
139 |
140 | dom: canvas,
141 |
142 | update: function ( value, maxValue ) {
143 |
144 | min = Math.min( min, value );
145 | max = Math.max( max, value );
146 |
147 | context.fillStyle = bg;
148 | context.globalAlpha = 1;
149 | context.fillRect( 0, 0, WIDTH, GRAPH_Y );
150 | context.fillStyle = fg;
151 | context.fillText( round( value ) + ' ' + name + ' (' + round( min ) + '-' + round( max ) + ')', TEXT_X, TEXT_Y );
152 |
153 | context.drawImage( canvas, GRAPH_X + PR, GRAPH_Y, GRAPH_WIDTH - PR, GRAPH_HEIGHT, GRAPH_X, GRAPH_Y, GRAPH_WIDTH - PR, GRAPH_HEIGHT );
154 |
155 | context.fillRect( GRAPH_X + GRAPH_WIDTH - PR, GRAPH_Y, PR, GRAPH_HEIGHT );
156 |
157 | context.fillStyle = bg;
158 | context.globalAlpha = 0.9;
159 | context.fillRect( GRAPH_X + GRAPH_WIDTH - PR, GRAPH_Y, PR, round( ( 1 - ( value / maxValue ) ) * GRAPH_HEIGHT ) );
160 |
161 | }
162 |
163 | };
164 |
165 | };
166 |
167 | export default Stats;
168 |
--------------------------------------------------------------------------------
/js/examples/jsm/loaders/DDSLoader.js:
--------------------------------------------------------------------------------
1 | import {
2 | CompressedTextureLoader,
3 | RGBAFormat,
4 | RGBA_S3TC_DXT3_Format,
5 | RGBA_S3TC_DXT5_Format,
6 | RGB_ETC1_Format,
7 | RGB_S3TC_DXT1_Format
8 | } from "../../../build/three.module.js";
9 |
10 | var DDSLoader = function ( manager ) {
11 |
12 | CompressedTextureLoader.call( this, manager );
13 |
14 | };
15 |
16 | DDSLoader.prototype = Object.assign( Object.create( CompressedTextureLoader.prototype ), {
17 |
18 | constructor: DDSLoader,
19 |
20 | parse: function ( buffer, loadMipmaps ) {
21 |
22 | var dds = { mipmaps: [], width: 0, height: 0, format: null, mipmapCount: 1 };
23 |
24 | // Adapted from @toji's DDS utils
25 | // https://github.com/toji/webgl-texture-utils/blob/master/texture-util/dds.js
26 |
27 | // All values and structures referenced from:
28 | // http://msdn.microsoft.com/en-us/library/bb943991.aspx/
29 |
30 | var DDS_MAGIC = 0x20534444;
31 |
32 | // var DDSD_CAPS = 0x1;
33 | // var DDSD_HEIGHT = 0x2;
34 | // var DDSD_WIDTH = 0x4;
35 | // var DDSD_PITCH = 0x8;
36 | // var DDSD_PIXELFORMAT = 0x1000;
37 | var DDSD_MIPMAPCOUNT = 0x20000;
38 | // var DDSD_LINEARSIZE = 0x80000;
39 | // var DDSD_DEPTH = 0x800000;
40 |
41 | // var DDSCAPS_COMPLEX = 0x8;
42 | // var DDSCAPS_MIPMAP = 0x400000;
43 | // var DDSCAPS_TEXTURE = 0x1000;
44 |
45 | var DDSCAPS2_CUBEMAP = 0x200;
46 | var DDSCAPS2_CUBEMAP_POSITIVEX = 0x400;
47 | var DDSCAPS2_CUBEMAP_NEGATIVEX = 0x800;
48 | var DDSCAPS2_CUBEMAP_POSITIVEY = 0x1000;
49 | var DDSCAPS2_CUBEMAP_NEGATIVEY = 0x2000;
50 | var DDSCAPS2_CUBEMAP_POSITIVEZ = 0x4000;
51 | var DDSCAPS2_CUBEMAP_NEGATIVEZ = 0x8000;
52 | // var DDSCAPS2_VOLUME = 0x200000;
53 |
54 | // var DDPF_ALPHAPIXELS = 0x1;
55 | // var DDPF_ALPHA = 0x2;
56 | var DDPF_FOURCC = 0x4;
57 | // var DDPF_RGB = 0x40;
58 | // var DDPF_YUV = 0x200;
59 | // var DDPF_LUMINANCE = 0x20000;
60 |
61 | function fourCCToInt32( value ) {
62 |
63 | return value.charCodeAt( 0 ) +
64 | ( value.charCodeAt( 1 ) << 8 ) +
65 | ( value.charCodeAt( 2 ) << 16 ) +
66 | ( value.charCodeAt( 3 ) << 24 );
67 |
68 | }
69 |
70 | function int32ToFourCC( value ) {
71 |
72 | return String.fromCharCode(
73 | value & 0xff,
74 | ( value >> 8 ) & 0xff,
75 | ( value >> 16 ) & 0xff,
76 | ( value >> 24 ) & 0xff
77 | );
78 |
79 | }
80 |
81 | function loadARGBMip( buffer, dataOffset, width, height ) {
82 |
83 | var dataLength = width * height * 4;
84 | var srcBuffer = new Uint8Array( buffer, dataOffset, dataLength );
85 | var byteArray = new Uint8Array( dataLength );
86 | var dst = 0;
87 | var src = 0;
88 | for ( var y = 0; y < height; y ++ ) {
89 |
90 | for ( var x = 0; x < width; x ++ ) {
91 |
92 | var b = srcBuffer[ src ]; src ++;
93 | var g = srcBuffer[ src ]; src ++;
94 | var r = srcBuffer[ src ]; src ++;
95 | var a = srcBuffer[ src ]; src ++;
96 | byteArray[ dst ] = r; dst ++; //r
97 | byteArray[ dst ] = g; dst ++; //g
98 | byteArray[ dst ] = b; dst ++; //b
99 | byteArray[ dst ] = a; dst ++; //a
100 |
101 | }
102 |
103 | }
104 |
105 | return byteArray;
106 |
107 | }
108 |
109 | var FOURCC_DXT1 = fourCCToInt32( "DXT1" );
110 | var FOURCC_DXT3 = fourCCToInt32( "DXT3" );
111 | var FOURCC_DXT5 = fourCCToInt32( "DXT5" );
112 | var FOURCC_ETC1 = fourCCToInt32( "ETC1" );
113 |
114 | var headerLengthInt = 31; // The header length in 32 bit ints
115 |
116 | // Offsets into the header array
117 |
118 | var off_magic = 0;
119 |
120 | var off_size = 1;
121 | var off_flags = 2;
122 | var off_height = 3;
123 | var off_width = 4;
124 |
125 | var off_mipmapCount = 7;
126 |
127 | var off_pfFlags = 20;
128 | var off_pfFourCC = 21;
129 | var off_RGBBitCount = 22;
130 | var off_RBitMask = 23;
131 | var off_GBitMask = 24;
132 | var off_BBitMask = 25;
133 | var off_ABitMask = 26;
134 |
135 | // var off_caps = 27;
136 | var off_caps2 = 28;
137 | // var off_caps3 = 29;
138 | // var off_caps4 = 30;
139 |
140 | // Parse header
141 |
142 | var header = new Int32Array( buffer, 0, headerLengthInt );
143 |
144 | if ( header[ off_magic ] !== DDS_MAGIC ) {
145 |
146 | console.error( 'THREE.DDSLoader.parse: Invalid magic number in DDS header.' );
147 | return dds;
148 |
149 | }
150 |
151 | if ( ! header[ off_pfFlags ] & DDPF_FOURCC ) {
152 |
153 | console.error( 'THREE.DDSLoader.parse: Unsupported format, must contain a FourCC code.' );
154 | return dds;
155 |
156 | }
157 |
158 | var blockBytes;
159 |
160 | var fourCC = header[ off_pfFourCC ];
161 |
162 | var isRGBAUncompressed = false;
163 |
164 | switch ( fourCC ) {
165 |
166 | case FOURCC_DXT1:
167 |
168 | blockBytes = 8;
169 | dds.format = RGB_S3TC_DXT1_Format;
170 | break;
171 |
172 | case FOURCC_DXT3:
173 |
174 | blockBytes = 16;
175 | dds.format = RGBA_S3TC_DXT3_Format;
176 | break;
177 |
178 | case FOURCC_DXT5:
179 |
180 | blockBytes = 16;
181 | dds.format = RGBA_S3TC_DXT5_Format;
182 | break;
183 |
184 | case FOURCC_ETC1:
185 |
186 | blockBytes = 8;
187 | dds.format = RGB_ETC1_Format;
188 | break;
189 |
190 | default:
191 |
192 | if ( header[ off_RGBBitCount ] === 32
193 | && header[ off_RBitMask ] & 0xff0000
194 | && header[ off_GBitMask ] & 0xff00
195 | && header[ off_BBitMask ] & 0xff
196 | && header[ off_ABitMask ] & 0xff000000 ) {
197 |
198 | isRGBAUncompressed = true;
199 | blockBytes = 64;
200 | dds.format = RGBAFormat;
201 |
202 | } else {
203 |
204 | console.error( 'THREE.DDSLoader.parse: Unsupported FourCC code ', int32ToFourCC( fourCC ) );
205 | return dds;
206 |
207 | }
208 |
209 | }
210 |
211 | dds.mipmapCount = 1;
212 |
213 | if ( header[ off_flags ] & DDSD_MIPMAPCOUNT && loadMipmaps !== false ) {
214 |
215 | dds.mipmapCount = Math.max( 1, header[ off_mipmapCount ] );
216 |
217 | }
218 |
219 | var caps2 = header[ off_caps2 ];
220 | dds.isCubemap = caps2 & DDSCAPS2_CUBEMAP ? true : false;
221 | if ( dds.isCubemap && (
222 | ! ( caps2 & DDSCAPS2_CUBEMAP_POSITIVEX ) ||
223 | ! ( caps2 & DDSCAPS2_CUBEMAP_NEGATIVEX ) ||
224 | ! ( caps2 & DDSCAPS2_CUBEMAP_POSITIVEY ) ||
225 | ! ( caps2 & DDSCAPS2_CUBEMAP_NEGATIVEY ) ||
226 | ! ( caps2 & DDSCAPS2_CUBEMAP_POSITIVEZ ) ||
227 | ! ( caps2 & DDSCAPS2_CUBEMAP_NEGATIVEZ )
228 | ) ) {
229 |
230 | console.error( 'THREE.DDSLoader.parse: Incomplete cubemap faces' );
231 | return dds;
232 |
233 | }
234 |
235 | dds.width = header[ off_width ];
236 | dds.height = header[ off_height ];
237 |
238 | var dataOffset = header[ off_size ] + 4;
239 |
240 | // Extract mipmaps buffers
241 |
242 | var faces = dds.isCubemap ? 6 : 1;
243 |
244 | for ( var face = 0; face < faces; face ++ ) {
245 |
246 | var width = dds.width;
247 | var height = dds.height;
248 |
249 | for ( var i = 0; i < dds.mipmapCount; i ++ ) {
250 |
251 | if ( isRGBAUncompressed ) {
252 |
253 | var byteArray = loadARGBMip( buffer, dataOffset, width, height );
254 | var dataLength = byteArray.length;
255 |
256 | } else {
257 |
258 | var dataLength = Math.max( 4, width ) / 4 * Math.max( 4, height ) / 4 * blockBytes;
259 | var byteArray = new Uint8Array( buffer, dataOffset, dataLength );
260 |
261 | }
262 |
263 | var mipmap = { "data": byteArray, "width": width, "height": height };
264 | dds.mipmaps.push( mipmap );
265 |
266 | dataOffset += dataLength;
267 |
268 | width = Math.max( width >> 1, 1 );
269 | height = Math.max( height >> 1, 1 );
270 |
271 | }
272 |
273 | }
274 |
275 | return dds;
276 |
277 | }
278 |
279 | } );
280 |
281 | export { DDSLoader };
282 |
--------------------------------------------------------------------------------
/js/examples/jsm/loaders/MTLLoader.js:
--------------------------------------------------------------------------------
1 | import {
2 | Color,
3 | DefaultLoadingManager,
4 | FileLoader,
5 | FrontSide,
6 | Loader,
7 | LoaderUtils,
8 | MeshPhongMaterial,
9 | RepeatWrapping,
10 | TextureLoader,
11 | Vector2
12 | } from "../../../build/three.module.js";
13 |
14 | /**
15 | * Loads a Wavefront .mtl file specifying materials
16 | */
17 |
18 | var MTLLoader = function ( manager ) {
19 |
20 | Loader.call( this, manager );
21 |
22 | };
23 |
24 | MTLLoader.prototype = Object.assign( Object.create( Loader.prototype ), {
25 |
26 | constructor: MTLLoader,
27 |
28 | /**
29 | * Loads and parses a MTL asset from a URL.
30 | *
31 | * @param {String} url - URL to the MTL file.
32 | * @param {Function} [onLoad] - Callback invoked with the loaded object.
33 | * @param {Function} [onProgress] - Callback for download progress.
34 | * @param {Function} [onError] - Callback for download errors.
35 | *
36 | * @see setPath setResourcePath
37 | *
38 | * @note In order for relative texture references to resolve correctly
39 | * you must call setResourcePath() explicitly prior to load.
40 | */
41 | load: function ( url, onLoad, onProgress, onError ) {
42 |
43 | var scope = this;
44 |
45 | var path = ( this.path === '' ) ? LoaderUtils.extractUrlBase( url ) : this.path;
46 |
47 | var loader = new FileLoader( this.manager );
48 | loader.setPath( this.path );
49 | loader.setRequestHeader( this.requestHeader );
50 | loader.setWithCredentials( this.withCredentials );
51 | loader.load( url, function ( text ) {
52 |
53 | try {
54 |
55 | onLoad( scope.parse( text, path ) );
56 |
57 | } catch ( e ) {
58 |
59 | if ( onError ) {
60 |
61 | onError( e );
62 |
63 | } else {
64 |
65 | console.error( e );
66 |
67 | }
68 |
69 | scope.manager.itemError( url );
70 |
71 | }
72 |
73 | }, onProgress, onError );
74 |
75 | },
76 |
77 | setMaterialOptions: function ( value ) {
78 |
79 | this.materialOptions = value;
80 | return this;
81 |
82 | },
83 |
84 | /**
85 | * Parses a MTL file.
86 | *
87 | * @param {String} text - Content of MTL file
88 | * @return {MTLLoader.MaterialCreator}
89 | *
90 | * @see setPath setResourcePath
91 | *
92 | * @note In order for relative texture references to resolve correctly
93 | * you must call setResourcePath() explicitly prior to parse.
94 | */
95 | parse: function ( text, path ) {
96 |
97 | var lines = text.split( '\n' );
98 | var info = {};
99 | var delimiter_pattern = /\s+/;
100 | var materialsInfo = {};
101 |
102 | for ( var i = 0; i < lines.length; i ++ ) {
103 |
104 | var line = lines[ i ];
105 | line = line.trim();
106 |
107 | if ( line.length === 0 || line.charAt( 0 ) === '#' ) {
108 |
109 | // Blank line or comment ignore
110 | continue;
111 |
112 | }
113 |
114 | var pos = line.indexOf( ' ' );
115 |
116 | var key = ( pos >= 0 ) ? line.substring( 0, pos ) : line;
117 | key = key.toLowerCase();
118 |
119 | var value = ( pos >= 0 ) ? line.substring( pos + 1 ) : '';
120 | value = value.trim();
121 |
122 | if ( key === 'newmtl' ) {
123 |
124 | // New material
125 |
126 | info = { name: value };
127 | materialsInfo[ value ] = info;
128 |
129 | } else {
130 |
131 | if ( key === 'ka' || key === 'kd' || key === 'ks' || key === 'ke' ) {
132 |
133 | var ss = value.split( delimiter_pattern, 3 );
134 | info[ key ] = [ parseFloat( ss[ 0 ] ), parseFloat( ss[ 1 ] ), parseFloat( ss[ 2 ] ) ];
135 |
136 | } else {
137 |
138 | info[ key ] = value;
139 |
140 | }
141 |
142 | }
143 |
144 | }
145 |
146 | var materialCreator = new MTLLoader.MaterialCreator( this.resourcePath || path, this.materialOptions );
147 | materialCreator.setCrossOrigin( this.crossOrigin );
148 | materialCreator.setManager( this.manager );
149 | materialCreator.setMaterials( materialsInfo );
150 | return materialCreator;
151 |
152 | }
153 |
154 | } );
155 |
156 | /**
157 | * Create a new MTLLoader.MaterialCreator
158 | * @param baseUrl - Url relative to which textures are loaded
159 | * @param options - Set of options on how to construct the materials
160 | * side: Which side to apply the material
161 | * FrontSide (default), THREE.BackSide, THREE.DoubleSide
162 | * wrap: What type of wrapping to apply for textures
163 | * RepeatWrapping (default), THREE.ClampToEdgeWrapping, THREE.MirroredRepeatWrapping
164 | * normalizeRGB: RGBs need to be normalized to 0-1 from 0-255
165 | * Default: false, assumed to be already normalized
166 | * ignoreZeroRGBs: Ignore values of RGBs (Ka,Kd,Ks) that are all 0's
167 | * Default: false
168 | * @constructor
169 | */
170 |
171 | MTLLoader.MaterialCreator = function ( baseUrl, options ) {
172 |
173 | this.baseUrl = baseUrl || '';
174 | this.options = options;
175 | this.materialsInfo = {};
176 | this.materials = {};
177 | this.materialsArray = [];
178 | this.nameLookup = {};
179 |
180 | this.side = ( this.options && this.options.side ) ? this.options.side : FrontSide;
181 | this.wrap = ( this.options && this.options.wrap ) ? this.options.wrap : RepeatWrapping;
182 |
183 | };
184 |
185 | MTLLoader.MaterialCreator.prototype = {
186 |
187 | constructor: MTLLoader.MaterialCreator,
188 |
189 | crossOrigin: 'anonymous',
190 |
191 | setCrossOrigin: function ( value ) {
192 |
193 | this.crossOrigin = value;
194 | return this;
195 |
196 | },
197 |
198 | setManager: function ( value ) {
199 |
200 | this.manager = value;
201 |
202 | },
203 |
204 | setMaterials: function ( materialsInfo ) {
205 |
206 | this.materialsInfo = this.convert( materialsInfo );
207 | this.materials = {};
208 | this.materialsArray = [];
209 | this.nameLookup = {};
210 |
211 | },
212 |
213 | convert: function ( materialsInfo ) {
214 |
215 | if ( ! this.options ) return materialsInfo;
216 |
217 | var converted = {};
218 |
219 | for ( var mn in materialsInfo ) {
220 |
221 | // Convert materials info into normalized form based on options
222 |
223 | var mat = materialsInfo[ mn ];
224 |
225 | var covmat = {};
226 |
227 | converted[ mn ] = covmat;
228 |
229 | for ( var prop in mat ) {
230 |
231 | var save = true;
232 | var value = mat[ prop ];
233 | var lprop = prop.toLowerCase();
234 |
235 | switch ( lprop ) {
236 |
237 | case 'kd':
238 | case 'ka':
239 | case 'ks':
240 |
241 | // Diffuse color (color under white light) using RGB values
242 |
243 | if ( this.options && this.options.normalizeRGB ) {
244 |
245 | value = [ value[ 0 ] / 255, value[ 1 ] / 255, value[ 2 ] / 255 ];
246 |
247 | }
248 |
249 | if ( this.options && this.options.ignoreZeroRGBs ) {
250 |
251 | if ( value[ 0 ] === 0 && value[ 1 ] === 0 && value[ 2 ] === 0 ) {
252 |
253 | // ignore
254 |
255 | save = false;
256 |
257 | }
258 |
259 | }
260 |
261 | break;
262 |
263 | default:
264 |
265 | break;
266 |
267 | }
268 |
269 | if ( save ) {
270 |
271 | covmat[ lprop ] = value;
272 |
273 | }
274 |
275 | }
276 |
277 | }
278 |
279 | return converted;
280 |
281 | },
282 |
283 | preload: function () {
284 |
285 | for ( var mn in this.materialsInfo ) {
286 |
287 | this.create( mn );
288 |
289 | }
290 |
291 | },
292 |
293 | getIndex: function ( materialName ) {
294 |
295 | return this.nameLookup[ materialName ];
296 |
297 | },
298 |
299 | getAsArray: function () {
300 |
301 | var index = 0;
302 |
303 | for ( var mn in this.materialsInfo ) {
304 |
305 | this.materialsArray[ index ] = this.create( mn );
306 | this.nameLookup[ mn ] = index;
307 | index ++;
308 |
309 | }
310 |
311 | return this.materialsArray;
312 |
313 | },
314 |
315 | create: function ( materialName ) {
316 |
317 | if ( this.materials[ materialName ] === undefined ) {
318 |
319 | this.createMaterial_( materialName );
320 |
321 | }
322 |
323 | return this.materials[ materialName ];
324 |
325 | },
326 |
327 | createMaterial_: function ( materialName ) {
328 |
329 | // Create material
330 |
331 | var scope = this;
332 | var mat = this.materialsInfo[ materialName ];
333 | var params = {
334 |
335 | name: materialName,
336 | side: this.side
337 |
338 | };
339 |
340 | function resolveURL( baseUrl, url ) {
341 |
342 | if ( typeof url !== 'string' || url === '' )
343 | return '';
344 |
345 | // Absolute URL
346 | if ( /^https?:\/\//i.test( url ) ) return url;
347 |
348 | return baseUrl + url;
349 |
350 | }
351 |
352 | function setMapForType( mapType, value ) {
353 |
354 | if ( params[ mapType ] ) return; // Keep the first encountered texture
355 |
356 | var texParams = scope.getTextureParams( value, params );
357 | var map = scope.loadTexture( resolveURL( scope.baseUrl, texParams.url ) );
358 |
359 | map.repeat.copy( texParams.scale );
360 | map.offset.copy( texParams.offset );
361 |
362 | map.wrapS = scope.wrap;
363 | map.wrapT = scope.wrap;
364 |
365 | params[ mapType ] = map;
366 |
367 | }
368 |
369 | for ( var prop in mat ) {
370 |
371 | var value = mat[ prop ];
372 | var n;
373 |
374 | if ( value === '' ) continue;
375 |
376 | switch ( prop.toLowerCase() ) {
377 |
378 | // Ns is material specular exponent
379 |
380 | case 'kd':
381 |
382 | // Diffuse color (color under white light) using RGB values
383 |
384 | params.color = new Color().fromArray( value );
385 |
386 | break;
387 |
388 | case 'ks':
389 |
390 | // Specular color (color when light is reflected from shiny surface) using RGB values
391 | params.specular = new Color().fromArray( value );
392 |
393 | break;
394 |
395 | case 'ke':
396 |
397 | // Emissive using RGB values
398 | params.emissive = new Color().fromArray( value );
399 |
400 | break;
401 |
402 | case 'map_kd':
403 |
404 | // Diffuse texture map
405 |
406 | setMapForType( "map", value );
407 |
408 | break;
409 |
410 | case 'map_ks':
411 |
412 | // Specular map
413 |
414 | setMapForType( "specularMap", value );
415 |
416 | break;
417 |
418 | case 'map_ke':
419 |
420 | // Emissive map
421 |
422 | setMapForType( "emissiveMap", value );
423 |
424 | break;
425 |
426 | case 'norm':
427 |
428 | setMapForType( "normalMap", value );
429 |
430 | break;
431 |
432 | case 'map_bump':
433 | case 'bump':
434 |
435 | // Bump texture map
436 |
437 | setMapForType( "bumpMap", value );
438 |
439 | break;
440 |
441 | case 'map_d':
442 |
443 | // Alpha map
444 |
445 | setMapForType( "alphaMap", value );
446 | params.transparent = true;
447 |
448 | break;
449 |
450 | case 'ns':
451 |
452 | // The specular exponent (defines the focus of the specular highlight)
453 | // A high exponent results in a tight, concentrated highlight. Ns values normally range from 0 to 1000.
454 |
455 | params.shininess = parseFloat( value );
456 |
457 | break;
458 |
459 | case 'd':
460 | n = parseFloat( value );
461 |
462 | if ( n < 1 ) {
463 |
464 | params.opacity = n;
465 | params.transparent = true;
466 |
467 | }
468 |
469 | break;
470 |
471 | case 'tr':
472 | n = parseFloat( value );
473 |
474 | if ( this.options && this.options.invertTrProperty ) n = 1 - n;
475 |
476 | if ( n > 0 ) {
477 |
478 | params.opacity = 1 - n;
479 | params.transparent = true;
480 |
481 | }
482 |
483 | break;
484 |
485 | default:
486 | break;
487 |
488 | }
489 |
490 | }
491 |
492 | this.materials[ materialName ] = new MeshPhongMaterial( params );
493 | return this.materials[ materialName ];
494 |
495 | },
496 |
497 | getTextureParams: function ( value, matParams ) {
498 |
499 | var texParams = {
500 |
501 | scale: new Vector2( 1, 1 ),
502 | offset: new Vector2( 0, 0 )
503 |
504 | };
505 |
506 | var items = value.split( /\s+/ );
507 | var pos;
508 |
509 | pos = items.indexOf( '-bm' );
510 |
511 | if ( pos >= 0 ) {
512 |
513 | matParams.bumpScale = parseFloat( items[ pos + 1 ] );
514 | items.splice( pos, 2 );
515 |
516 | }
517 |
518 | pos = items.indexOf( '-s' );
519 |
520 | if ( pos >= 0 ) {
521 |
522 | texParams.scale.set( parseFloat( items[ pos + 1 ] ), parseFloat( items[ pos + 2 ] ) );
523 | items.splice( pos, 4 ); // we expect 3 parameters here!
524 |
525 | }
526 |
527 | pos = items.indexOf( '-o' );
528 |
529 | if ( pos >= 0 ) {
530 |
531 | texParams.offset.set( parseFloat( items[ pos + 1 ] ), parseFloat( items[ pos + 2 ] ) );
532 | items.splice( pos, 4 ); // we expect 3 parameters here!
533 |
534 | }
535 |
536 | texParams.url = items.join( ' ' ).trim();
537 | return texParams;
538 |
539 | },
540 |
541 | loadTexture: function ( url, mapping, onLoad, onProgress, onError ) {
542 |
543 | var texture;
544 | var manager = ( this.manager !== undefined ) ? this.manager : DefaultLoadingManager;
545 | var loader = manager.getHandler( url );
546 |
547 | if ( loader === null ) {
548 |
549 | loader = new TextureLoader( manager );
550 |
551 | }
552 |
553 | if ( loader.setCrossOrigin ) loader.setCrossOrigin( this.crossOrigin );
554 | texture = loader.load( url, onLoad, onProgress, onError );
555 |
556 | if ( mapping !== undefined ) texture.mapping = mapping;
557 |
558 | return texture;
559 |
560 | }
561 |
562 | };
563 |
564 | export { MTLLoader };
565 |
--------------------------------------------------------------------------------
/js/examples/jsm/loaders/OBJLoader.js:
--------------------------------------------------------------------------------
1 | import {
2 | BufferGeometry,
3 | FileLoader,
4 | Float32BufferAttribute,
5 | Group,
6 | LineBasicMaterial,
7 | LineSegments,
8 | Loader,
9 | Material,
10 | Mesh,
11 | MeshPhongMaterial,
12 | Points,
13 | PointsMaterial,
14 | Vector3
15 | } from "../../../build/three.module.js";
16 |
17 | var OBJLoader = ( function () {
18 |
19 | // o object_name | g group_name
20 | var object_pattern = /^[og]\s*(.+)?/;
21 | // mtllib file_reference
22 | var material_library_pattern = /^mtllib /;
23 | // usemtl material_name
24 | var material_use_pattern = /^usemtl /;
25 | // usemap map_name
26 | var map_use_pattern = /^usemap /;
27 |
28 | var vA = new Vector3();
29 | var vB = new Vector3();
30 | var vC = new Vector3();
31 |
32 | var ab = new Vector3();
33 | var cb = new Vector3();
34 |
35 | function ParserState() {
36 |
37 | var state = {
38 | objects: [],
39 | object: {},
40 |
41 | vertices: [],
42 | normals: [],
43 | colors: [],
44 | uvs: [],
45 |
46 | materials: {},
47 | materialLibraries: [],
48 |
49 | startObject: function ( name, fromDeclaration ) {
50 |
51 | // If the current object (initial from reset) is not from a g/o declaration in the parsed
52 | // file. We need to use it for the first parsed g/o to keep things in sync.
53 | if ( this.object && this.object.fromDeclaration === false ) {
54 |
55 | this.object.name = name;
56 | this.object.fromDeclaration = ( fromDeclaration !== false );
57 | return;
58 |
59 | }
60 |
61 | var previousMaterial = ( this.object && typeof this.object.currentMaterial === 'function' ? this.object.currentMaterial() : undefined );
62 |
63 | if ( this.object && typeof this.object._finalize === 'function' ) {
64 |
65 | this.object._finalize( true );
66 |
67 | }
68 |
69 | this.object = {
70 | name: name || '',
71 | fromDeclaration: ( fromDeclaration !== false ),
72 |
73 | geometry: {
74 | vertices: [],
75 | normals: [],
76 | colors: [],
77 | uvs: [],
78 | hasUVIndices: false
79 | },
80 | materials: [],
81 | smooth: true,
82 |
83 | startMaterial: function ( name, libraries ) {
84 |
85 | var previous = this._finalize( false );
86 |
87 | // New usemtl declaration overwrites an inherited material, except if faces were declared
88 | // after the material, then it must be preserved for proper MultiMaterial continuation.
89 | if ( previous && ( previous.inherited || previous.groupCount <= 0 ) ) {
90 |
91 | this.materials.splice( previous.index, 1 );
92 |
93 | }
94 |
95 | var material = {
96 | index: this.materials.length,
97 | name: name || '',
98 | mtllib: ( Array.isArray( libraries ) && libraries.length > 0 ? libraries[ libraries.length - 1 ] : '' ),
99 | smooth: ( previous !== undefined ? previous.smooth : this.smooth ),
100 | groupStart: ( previous !== undefined ? previous.groupEnd : 0 ),
101 | groupEnd: - 1,
102 | groupCount: - 1,
103 | inherited: false,
104 |
105 | clone: function ( index ) {
106 |
107 | var cloned = {
108 | index: ( typeof index === 'number' ? index : this.index ),
109 | name: this.name,
110 | mtllib: this.mtllib,
111 | smooth: this.smooth,
112 | groupStart: 0,
113 | groupEnd: - 1,
114 | groupCount: - 1,
115 | inherited: false
116 | };
117 | cloned.clone = this.clone.bind( cloned );
118 | return cloned;
119 |
120 | }
121 | };
122 |
123 | this.materials.push( material );
124 |
125 | return material;
126 |
127 | },
128 |
129 | currentMaterial: function () {
130 |
131 | if ( this.materials.length > 0 ) {
132 |
133 | return this.materials[ this.materials.length - 1 ];
134 |
135 | }
136 |
137 | return undefined;
138 |
139 | },
140 |
141 | _finalize: function ( end ) {
142 |
143 | var lastMultiMaterial = this.currentMaterial();
144 | if ( lastMultiMaterial && lastMultiMaterial.groupEnd === - 1 ) {
145 |
146 | lastMultiMaterial.groupEnd = this.geometry.vertices.length / 3;
147 | lastMultiMaterial.groupCount = lastMultiMaterial.groupEnd - lastMultiMaterial.groupStart;
148 | lastMultiMaterial.inherited = false;
149 |
150 | }
151 |
152 | // Ignore objects tail materials if no face declarations followed them before a new o/g started.
153 | if ( end && this.materials.length > 1 ) {
154 |
155 | for ( var mi = this.materials.length - 1; mi >= 0; mi -- ) {
156 |
157 | if ( this.materials[ mi ].groupCount <= 0 ) {
158 |
159 | this.materials.splice( mi, 1 );
160 |
161 | }
162 |
163 | }
164 |
165 | }
166 |
167 | // Guarantee at least one empty material, this makes the creation later more straight forward.
168 | if ( end && this.materials.length === 0 ) {
169 |
170 | this.materials.push( {
171 | name: '',
172 | smooth: this.smooth
173 | } );
174 |
175 | }
176 |
177 | return lastMultiMaterial;
178 |
179 | }
180 | };
181 |
182 | // Inherit previous objects material.
183 | // Spec tells us that a declared material must be set to all objects until a new material is declared.
184 | // If a usemtl declaration is encountered while this new object is being parsed, it will
185 | // overwrite the inherited material. Exception being that there was already face declarations
186 | // to the inherited material, then it will be preserved for proper MultiMaterial continuation.
187 |
188 | if ( previousMaterial && previousMaterial.name && typeof previousMaterial.clone === 'function' ) {
189 |
190 | var declared = previousMaterial.clone( 0 );
191 | declared.inherited = true;
192 | this.object.materials.push( declared );
193 |
194 | }
195 |
196 | this.objects.push( this.object );
197 |
198 | },
199 |
200 | finalize: function () {
201 |
202 | if ( this.object && typeof this.object._finalize === 'function' ) {
203 |
204 | this.object._finalize( true );
205 |
206 | }
207 |
208 | },
209 |
210 | parseVertexIndex: function ( value, len ) {
211 |
212 | var index = parseInt( value, 10 );
213 | return ( index >= 0 ? index - 1 : index + len / 3 ) * 3;
214 |
215 | },
216 |
217 | parseNormalIndex: function ( value, len ) {
218 |
219 | var index = parseInt( value, 10 );
220 | return ( index >= 0 ? index - 1 : index + len / 3 ) * 3;
221 |
222 | },
223 |
224 | parseUVIndex: function ( value, len ) {
225 |
226 | var index = parseInt( value, 10 );
227 | return ( index >= 0 ? index - 1 : index + len / 2 ) * 2;
228 |
229 | },
230 |
231 | addVertex: function ( a, b, c ) {
232 |
233 | var src = this.vertices;
234 | var dst = this.object.geometry.vertices;
235 |
236 | dst.push( src[ a + 0 ], src[ a + 1 ], src[ a + 2 ] );
237 | dst.push( src[ b + 0 ], src[ b + 1 ], src[ b + 2 ] );
238 | dst.push( src[ c + 0 ], src[ c + 1 ], src[ c + 2 ] );
239 |
240 | },
241 |
242 | addVertexPoint: function ( a ) {
243 |
244 | var src = this.vertices;
245 | var dst = this.object.geometry.vertices;
246 |
247 | dst.push( src[ a + 0 ], src[ a + 1 ], src[ a + 2 ] );
248 |
249 | },
250 |
251 | addVertexLine: function ( a ) {
252 |
253 | var src = this.vertices;
254 | var dst = this.object.geometry.vertices;
255 |
256 | dst.push( src[ a + 0 ], src[ a + 1 ], src[ a + 2 ] );
257 |
258 | },
259 |
260 | addNormal: function ( a, b, c ) {
261 |
262 | var src = this.normals;
263 | var dst = this.object.geometry.normals;
264 |
265 | dst.push( src[ a + 0 ], src[ a + 1 ], src[ a + 2 ] );
266 | dst.push( src[ b + 0 ], src[ b + 1 ], src[ b + 2 ] );
267 | dst.push( src[ c + 0 ], src[ c + 1 ], src[ c + 2 ] );
268 |
269 | },
270 |
271 | addFaceNormal: function ( a, b, c ) {
272 |
273 | var src = this.vertices;
274 | var dst = this.object.geometry.normals;
275 |
276 | vA.fromArray( src, a );
277 | vB.fromArray( src, b );
278 | vC.fromArray( src, c );
279 |
280 | cb.subVectors( vC, vB );
281 | ab.subVectors( vA, vB );
282 | cb.cross( ab );
283 |
284 | cb.normalize();
285 |
286 | dst.push( cb.x, cb.y, cb.z );
287 | dst.push( cb.x, cb.y, cb.z );
288 | dst.push( cb.x, cb.y, cb.z );
289 |
290 | },
291 |
292 | addColor: function ( a, b, c ) {
293 |
294 | var src = this.colors;
295 | var dst = this.object.geometry.colors;
296 |
297 | if ( src[ a ] !== undefined ) dst.push( src[ a + 0 ], src[ a + 1 ], src[ a + 2 ] );
298 | if ( src[ b ] !== undefined ) dst.push( src[ b + 0 ], src[ b + 1 ], src[ b + 2 ] );
299 | if ( src[ c ] !== undefined ) dst.push( src[ c + 0 ], src[ c + 1 ], src[ c + 2 ] );
300 |
301 | },
302 |
303 | addUV: function ( a, b, c ) {
304 |
305 | var src = this.uvs;
306 | var dst = this.object.geometry.uvs;
307 |
308 | dst.push( src[ a + 0 ], src[ a + 1 ] );
309 | dst.push( src[ b + 0 ], src[ b + 1 ] );
310 | dst.push( src[ c + 0 ], src[ c + 1 ] );
311 |
312 | },
313 |
314 | addDefaultUV: function () {
315 |
316 | var dst = this.object.geometry.uvs;
317 |
318 | dst.push( 0, 0 );
319 | dst.push( 0, 0 );
320 | dst.push( 0, 0 );
321 |
322 | },
323 |
324 | addUVLine: function ( a ) {
325 |
326 | var src = this.uvs;
327 | var dst = this.object.geometry.uvs;
328 |
329 | dst.push( src[ a + 0 ], src[ a + 1 ] );
330 |
331 | },
332 |
333 | addFace: function ( a, b, c, ua, ub, uc, na, nb, nc ) {
334 |
335 | var vLen = this.vertices.length;
336 |
337 | var ia = this.parseVertexIndex( a, vLen );
338 | var ib = this.parseVertexIndex( b, vLen );
339 | var ic = this.parseVertexIndex( c, vLen );
340 |
341 | this.addVertex( ia, ib, ic );
342 | this.addColor( ia, ib, ic );
343 |
344 | // normals
345 |
346 | if ( na !== undefined && na !== '' ) {
347 |
348 | var nLen = this.normals.length;
349 |
350 | ia = this.parseNormalIndex( na, nLen );
351 | ib = this.parseNormalIndex( nb, nLen );
352 | ic = this.parseNormalIndex( nc, nLen );
353 |
354 | this.addNormal( ia, ib, ic );
355 |
356 | } else {
357 |
358 | this.addFaceNormal( ia, ib, ic );
359 |
360 | }
361 |
362 | // uvs
363 |
364 | if ( ua !== undefined && ua !== '' ) {
365 |
366 | var uvLen = this.uvs.length;
367 |
368 | ia = this.parseUVIndex( ua, uvLen );
369 | ib = this.parseUVIndex( ub, uvLen );
370 | ic = this.parseUVIndex( uc, uvLen );
371 |
372 | this.addUV( ia, ib, ic );
373 |
374 | this.object.geometry.hasUVIndices = true;
375 |
376 | } else {
377 |
378 | // add placeholder values (for inconsistent face definitions)
379 |
380 | this.addDefaultUV();
381 |
382 | }
383 |
384 | },
385 |
386 | addPointGeometry: function ( vertices ) {
387 |
388 | this.object.geometry.type = 'Points';
389 |
390 | var vLen = this.vertices.length;
391 |
392 | for ( var vi = 0, l = vertices.length; vi < l; vi ++ ) {
393 |
394 | var index = this.parseVertexIndex( vertices[ vi ], vLen );
395 |
396 | this.addVertexPoint( index );
397 | this.addColor( index );
398 |
399 | }
400 |
401 | },
402 |
403 | addLineGeometry: function ( vertices, uvs ) {
404 |
405 | this.object.geometry.type = 'Line';
406 |
407 | var vLen = this.vertices.length;
408 | var uvLen = this.uvs.length;
409 |
410 | for ( var vi = 0, l = vertices.length; vi < l; vi ++ ) {
411 |
412 | this.addVertexLine( this.parseVertexIndex( vertices[ vi ], vLen ) );
413 |
414 | }
415 |
416 | for ( var uvi = 0, l = uvs.length; uvi < l; uvi ++ ) {
417 |
418 | this.addUVLine( this.parseUVIndex( uvs[ uvi ], uvLen ) );
419 |
420 | }
421 |
422 | }
423 |
424 | };
425 |
426 | state.startObject( '', false );
427 |
428 | return state;
429 |
430 | }
431 |
432 | //
433 |
434 | function OBJLoader( manager ) {
435 |
436 | Loader.call( this, manager );
437 |
438 | this.materials = null;
439 |
440 | }
441 |
442 | OBJLoader.prototype = Object.assign( Object.create( Loader.prototype ), {
443 |
444 | constructor: OBJLoader,
445 |
446 | load: function ( url, onLoad, onProgress, onError ) {
447 |
448 | var scope = this;
449 |
450 | var loader = new FileLoader( this.manager );
451 | loader.setPath( this.path );
452 | loader.setRequestHeader( this.requestHeader );
453 | loader.setWithCredentials( this.withCredentials );
454 | loader.load( url, function ( text ) {
455 |
456 | try {
457 |
458 | onLoad( scope.parse( text ) );
459 |
460 | } catch ( e ) {
461 |
462 | if ( onError ) {
463 |
464 | onError( e );
465 |
466 | } else {
467 |
468 | console.error( e );
469 |
470 | }
471 |
472 | scope.manager.itemError( url );
473 |
474 | }
475 |
476 | }, onProgress, onError );
477 |
478 | },
479 |
480 | setMaterials: function ( materials ) {
481 |
482 | this.materials = materials;
483 |
484 | return this;
485 |
486 | },
487 |
488 | parse: function ( text ) {
489 |
490 | var state = new ParserState();
491 |
492 | if ( text.indexOf( '\r\n' ) !== - 1 ) {
493 |
494 | // This is faster than String.split with regex that splits on both
495 | text = text.replace( /\r\n/g, '\n' );
496 |
497 | }
498 |
499 | if ( text.indexOf( '\\\n' ) !== - 1 ) {
500 |
501 | // join lines separated by a line continuation character (\)
502 | text = text.replace( /\\\n/g, '' );
503 |
504 | }
505 |
506 | var lines = text.split( '\n' );
507 | var line = '', lineFirstChar = '';
508 | var lineLength = 0;
509 | var result = [];
510 |
511 | // Faster to just trim left side of the line. Use if available.
512 | var trimLeft = ( typeof ''.trimLeft === 'function' );
513 |
514 | for ( var i = 0, l = lines.length; i < l; i ++ ) {
515 |
516 | line = lines[ i ];
517 |
518 | line = trimLeft ? line.trimLeft() : line.trim();
519 |
520 | lineLength = line.length;
521 |
522 | if ( lineLength === 0 ) continue;
523 |
524 | lineFirstChar = line.charAt( 0 );
525 |
526 | // @todo invoke passed in handler if any
527 | if ( lineFirstChar === '#' ) continue;
528 |
529 | if ( lineFirstChar === 'v' ) {
530 |
531 | var data = line.split( /\s+/ );
532 |
533 | switch ( data[ 0 ] ) {
534 |
535 | case 'v':
536 | state.vertices.push(
537 | parseFloat( data[ 1 ] ),
538 | parseFloat( data[ 2 ] ),
539 | parseFloat( data[ 3 ] )
540 | );
541 | if ( data.length >= 7 ) {
542 |
543 | state.colors.push(
544 | parseFloat( data[ 4 ] ),
545 | parseFloat( data[ 5 ] ),
546 | parseFloat( data[ 6 ] )
547 |
548 | );
549 |
550 | } else {
551 |
552 | // if no colors are defined, add placeholders so color and vertex indices match
553 |
554 | state.colors.push( undefined, undefined, undefined );
555 |
556 | }
557 |
558 | break;
559 | case 'vn':
560 | state.normals.push(
561 | parseFloat( data[ 1 ] ),
562 | parseFloat( data[ 2 ] ),
563 | parseFloat( data[ 3 ] )
564 | );
565 | break;
566 | case 'vt':
567 | state.uvs.push(
568 | parseFloat( data[ 1 ] ),
569 | parseFloat( data[ 2 ] )
570 | );
571 | break;
572 |
573 | }
574 |
575 | } else if ( lineFirstChar === 'f' ) {
576 |
577 | var lineData = line.substr( 1 ).trim();
578 | var vertexData = lineData.split( /\s+/ );
579 | var faceVertices = [];
580 |
581 | // Parse the face vertex data into an easy to work with format
582 |
583 | for ( var j = 0, jl = vertexData.length; j < jl; j ++ ) {
584 |
585 | var vertex = vertexData[ j ];
586 |
587 | if ( vertex.length > 0 ) {
588 |
589 | var vertexParts = vertex.split( '/' );
590 | faceVertices.push( vertexParts );
591 |
592 | }
593 |
594 | }
595 |
596 | // Draw an edge between the first vertex and all subsequent vertices to form an n-gon
597 |
598 | var v1 = faceVertices[ 0 ];
599 |
600 | for ( var j = 1, jl = faceVertices.length - 1; j < jl; j ++ ) {
601 |
602 | var v2 = faceVertices[ j ];
603 | var v3 = faceVertices[ j + 1 ];
604 |
605 | state.addFace(
606 | v1[ 0 ], v2[ 0 ], v3[ 0 ],
607 | v1[ 1 ], v2[ 1 ], v3[ 1 ],
608 | v1[ 2 ], v2[ 2 ], v3[ 2 ]
609 | );
610 |
611 | }
612 |
613 | } else if ( lineFirstChar === 'l' ) {
614 |
615 | var lineParts = line.substring( 1 ).trim().split( " " );
616 | var lineVertices = [], lineUVs = [];
617 |
618 | if ( line.indexOf( "/" ) === - 1 ) {
619 |
620 | lineVertices = lineParts;
621 |
622 | } else {
623 |
624 | for ( var li = 0, llen = lineParts.length; li < llen; li ++ ) {
625 |
626 | var parts = lineParts[ li ].split( "/" );
627 |
628 | if ( parts[ 0 ] !== "" ) lineVertices.push( parts[ 0 ] );
629 | if ( parts[ 1 ] !== "" ) lineUVs.push( parts[ 1 ] );
630 |
631 | }
632 |
633 | }
634 |
635 | state.addLineGeometry( lineVertices, lineUVs );
636 |
637 | } else if ( lineFirstChar === 'p' ) {
638 |
639 | var lineData = line.substr( 1 ).trim();
640 | var pointData = lineData.split( " " );
641 |
642 | state.addPointGeometry( pointData );
643 |
644 | } else if ( ( result = object_pattern.exec( line ) ) !== null ) {
645 |
646 | // o object_name
647 | // or
648 | // g group_name
649 |
650 | // WORKAROUND: https://bugs.chromium.org/p/v8/issues/detail?id=2869
651 | // var name = result[ 0 ].substr( 1 ).trim();
652 | var name = ( " " + result[ 0 ].substr( 1 ).trim() ).substr( 1 );
653 |
654 | state.startObject( name );
655 |
656 | } else if ( material_use_pattern.test( line ) ) {
657 |
658 | // material
659 |
660 | state.object.startMaterial( line.substring( 7 ).trim(), state.materialLibraries );
661 |
662 | } else if ( material_library_pattern.test( line ) ) {
663 |
664 | // mtl file
665 |
666 | state.materialLibraries.push( line.substring( 7 ).trim() );
667 |
668 | } else if ( map_use_pattern.test( line ) ) {
669 |
670 | // the line is parsed but ignored since the loader assumes textures are defined MTL files
671 | // (according to https://www.okino.com/conv/imp_wave.htm, 'usemap' is the old-style Wavefront texture reference method)
672 |
673 | console.warn( 'THREE.OBJLoader: Rendering identifier "usemap" not supported. Textures must be defined in MTL files.' );
674 |
675 | } else if ( lineFirstChar === 's' ) {
676 |
677 | result = line.split( ' ' );
678 |
679 | // smooth shading
680 |
681 | // @todo Handle files that have varying smooth values for a set of faces inside one geometry,
682 | // but does not define a usemtl for each face set.
683 | // This should be detected and a dummy material created (later MultiMaterial and geometry groups).
684 | // This requires some care to not create extra material on each smooth value for "normal" obj files.
685 | // where explicit usemtl defines geometry groups.
686 | // Example asset: examples/models/obj/cerberus/Cerberus.obj
687 |
688 | /*
689 | * http://paulbourke.net/dataformats/obj/
690 | * or
691 | * http://www.cs.utah.edu/~boulos/cs3505/obj_spec.pdf
692 | *
693 | * From chapter "Grouping" Syntax explanation "s group_number":
694 | * "group_number is the smoothing group number. To turn off smoothing groups, use a value of 0 or off.
695 | * Polygonal elements use group numbers to put elements in different smoothing groups. For free-form
696 | * surfaces, smoothing groups are either turned on or off; there is no difference between values greater
697 | * than 0."
698 | */
699 | if ( result.length > 1 ) {
700 |
701 | var value = result[ 1 ].trim().toLowerCase();
702 | state.object.smooth = ( value !== '0' && value !== 'off' );
703 |
704 | } else {
705 |
706 | // ZBrush can produce "s" lines #11707
707 | state.object.smooth = true;
708 |
709 | }
710 |
711 | var material = state.object.currentMaterial();
712 | if ( material ) material.smooth = state.object.smooth;
713 |
714 | } else {
715 |
716 | // Handle null terminated files without exception
717 | if ( line === '\0' ) continue;
718 |
719 | console.warn( 'THREE.OBJLoader: Unexpected line: "' + line + '"' );
720 |
721 | }
722 |
723 | }
724 |
725 | state.finalize();
726 |
727 | var container = new Group();
728 | container.materialLibraries = [].concat( state.materialLibraries );
729 |
730 | var hasPrimitives = ! ( state.objects.length === 1 && state.objects[ 0 ].geometry.vertices.length === 0 );
731 |
732 | if ( hasPrimitives === true ) {
733 |
734 | for ( var i = 0, l = state.objects.length; i < l; i ++ ) {
735 |
736 | var object = state.objects[ i ];
737 | var geometry = object.geometry;
738 | var materials = object.materials;
739 | var isLine = ( geometry.type === 'Line' );
740 | var isPoints = ( geometry.type === 'Points' );
741 | var hasVertexColors = false;
742 |
743 | // Skip o/g line declarations that did not follow with any faces
744 | if ( geometry.vertices.length === 0 ) continue;
745 |
746 | var buffergeometry = new BufferGeometry();
747 |
748 | buffergeometry.setAttribute( 'position', new Float32BufferAttribute( geometry.vertices, 3 ) );
749 |
750 | if ( geometry.normals.length > 0 ) {
751 |
752 | buffergeometry.setAttribute( 'normal', new Float32BufferAttribute( geometry.normals, 3 ) );
753 |
754 | }
755 |
756 | if ( geometry.colors.length > 0 ) {
757 |
758 | hasVertexColors = true;
759 | buffergeometry.setAttribute( 'color', new Float32BufferAttribute( geometry.colors, 3 ) );
760 |
761 | }
762 |
763 | if ( geometry.hasUVIndices === true ) {
764 |
765 | buffergeometry.setAttribute( 'uv', new Float32BufferAttribute( geometry.uvs, 2 ) );
766 |
767 | }
768 |
769 | // Create materials
770 |
771 | var createdMaterials = [];
772 |
773 | for ( var mi = 0, miLen = materials.length; mi < miLen; mi ++ ) {
774 |
775 | var sourceMaterial = materials[ mi ];
776 | var materialHash = sourceMaterial.name + '_' + sourceMaterial.smooth + '_' + hasVertexColors;
777 | var material = state.materials[ materialHash ];
778 |
779 | if ( this.materials !== null ) {
780 |
781 | material = this.materials.create( sourceMaterial.name );
782 |
783 | // mtl etc. loaders probably can't create line materials correctly, copy properties to a line material.
784 | if ( isLine && material && ! ( material instanceof LineBasicMaterial ) ) {
785 |
786 | var materialLine = new LineBasicMaterial();
787 | Material.prototype.copy.call( materialLine, material );
788 | materialLine.color.copy( material.color );
789 | material = materialLine;
790 |
791 | } else if ( isPoints && material && ! ( material instanceof PointsMaterial ) ) {
792 |
793 | var materialPoints = new PointsMaterial( { size: 10, sizeAttenuation: false } );
794 | Material.prototype.copy.call( materialPoints, material );
795 | materialPoints.color.copy( material.color );
796 | materialPoints.map = material.map;
797 | material = materialPoints;
798 |
799 | }
800 |
801 | }
802 |
803 | if ( material === undefined ) {
804 |
805 | if ( isLine ) {
806 |
807 | material = new LineBasicMaterial();
808 |
809 | } else if ( isPoints ) {
810 |
811 | material = new PointsMaterial( { size: 1, sizeAttenuation: false } );
812 |
813 | } else {
814 |
815 | material = new MeshPhongMaterial();
816 |
817 | }
818 |
819 | material.name = sourceMaterial.name;
820 | material.flatShading = sourceMaterial.smooth ? false : true;
821 | material.vertexColors = hasVertexColors;
822 |
823 | state.materials[ materialHash ] = material;
824 |
825 | }
826 |
827 | createdMaterials.push( material );
828 |
829 | }
830 |
831 | // Create mesh
832 |
833 | var mesh;
834 |
835 | if ( createdMaterials.length > 1 ) {
836 |
837 | for ( var mi = 0, miLen = materials.length; mi < miLen; mi ++ ) {
838 |
839 | var sourceMaterial = materials[ mi ];
840 | buffergeometry.addGroup( sourceMaterial.groupStart, sourceMaterial.groupCount, mi );
841 |
842 | }
843 |
844 | if ( isLine ) {
845 |
846 | mesh = new LineSegments( buffergeometry, createdMaterials );
847 |
848 | } else if ( isPoints ) {
849 |
850 | mesh = new Points( buffergeometry, createdMaterials );
851 |
852 | } else {
853 |
854 | mesh = new Mesh( buffergeometry, createdMaterials );
855 |
856 | }
857 |
858 | } else {
859 |
860 | if ( isLine ) {
861 |
862 | mesh = new LineSegments( buffergeometry, createdMaterials[ 0 ] );
863 |
864 | } else if ( isPoints ) {
865 |
866 | mesh = new Points( buffergeometry, createdMaterials[ 0 ] );
867 |
868 | } else {
869 |
870 | mesh = new Mesh( buffergeometry, createdMaterials[ 0 ] );
871 |
872 | }
873 |
874 | }
875 |
876 | mesh.name = object.name;
877 |
878 | container.add( mesh );
879 |
880 | }
881 |
882 | } else {
883 |
884 | // if there is only the default parser state object with no geometry data, interpret data as point cloud
885 |
886 | if ( state.vertices.length > 0 ) {
887 |
888 | var material = new PointsMaterial( { size: 1, sizeAttenuation: false } );
889 |
890 | var buffergeometry = new BufferGeometry();
891 |
892 | buffergeometry.setAttribute( 'position', new Float32BufferAttribute( state.vertices, 3 ) );
893 |
894 | if ( state.colors.length > 0 ) {
895 |
896 | buffergeometry.setAttribute( 'color', new Float32BufferAttribute( state.colors, 3 ) );
897 | material.vertexColors = true;
898 |
899 | }
900 |
901 | var points = new Points( buffergeometry, material );
902 | container.add( points );
903 |
904 | }
905 |
906 | }
907 |
908 | return container;
909 |
910 | }
911 |
912 | } );
913 |
914 | return OBJLoader;
915 |
916 | } )();
917 |
918 | export { OBJLoader };
919 |
--------------------------------------------------------------------------------
/js/examples/jsm/math/SimplexNoise.js:
--------------------------------------------------------------------------------
1 | // Ported from Stefan Gustavson's java implementation
2 | // http://staffwww.itn.liu.se/~stegu/simplexnoise/simplexnoise.pdf
3 | // Read Stefan's excellent paper for details on how this code works.
4 | //
5 | // Sean McCullough banksean@gmail.com
6 | //
7 | // Added 4D noise
8 |
9 | /**
10 | * You can pass in a random number generator object if you like.
11 | * It is assumed to have a random() method.
12 | */
13 | var SimplexNoise = function ( r ) {
14 |
15 | if ( r == undefined ) r = Math;
16 | this.grad3 = [[ 1, 1, 0 ], [ - 1, 1, 0 ], [ 1, - 1, 0 ], [ - 1, - 1, 0 ],
17 | [ 1, 0, 1 ], [ - 1, 0, 1 ], [ 1, 0, - 1 ], [ - 1, 0, - 1 ],
18 | [ 0, 1, 1 ], [ 0, - 1, 1 ], [ 0, 1, - 1 ], [ 0, - 1, - 1 ]];
19 |
20 | this.grad4 = [[ 0, 1, 1, 1 ], [ 0, 1, 1, - 1 ], [ 0, 1, - 1, 1 ], [ 0, 1, - 1, - 1 ],
21 | [ 0, - 1, 1, 1 ], [ 0, - 1, 1, - 1 ], [ 0, - 1, - 1, 1 ], [ 0, - 1, - 1, - 1 ],
22 | [ 1, 0, 1, 1 ], [ 1, 0, 1, - 1 ], [ 1, 0, - 1, 1 ], [ 1, 0, - 1, - 1 ],
23 | [ - 1, 0, 1, 1 ], [ - 1, 0, 1, - 1 ], [ - 1, 0, - 1, 1 ], [ - 1, 0, - 1, - 1 ],
24 | [ 1, 1, 0, 1 ], [ 1, 1, 0, - 1 ], [ 1, - 1, 0, 1 ], [ 1, - 1, 0, - 1 ],
25 | [ - 1, 1, 0, 1 ], [ - 1, 1, 0, - 1 ], [ - 1, - 1, 0, 1 ], [ - 1, - 1, 0, - 1 ],
26 | [ 1, 1, 1, 0 ], [ 1, 1, - 1, 0 ], [ 1, - 1, 1, 0 ], [ 1, - 1, - 1, 0 ],
27 | [ - 1, 1, 1, 0 ], [ - 1, 1, - 1, 0 ], [ - 1, - 1, 1, 0 ], [ - 1, - 1, - 1, 0 ]];
28 |
29 | this.p = [];
30 |
31 | for ( var i = 0; i < 256; i ++ ) {
32 |
33 | this.p[ i ] = Math.floor( r.random() * 256 );
34 |
35 | }
36 |
37 | // To remove the need for index wrapping, double the permutation table length
38 | this.perm = [];
39 |
40 | for ( var i = 0; i < 512; i ++ ) {
41 |
42 | this.perm[ i ] = this.p[ i & 255 ];
43 |
44 | }
45 |
46 | // A lookup table to traverse the simplex around a given point in 4D.
47 | // Details can be found where this table is used, in the 4D noise method.
48 | this.simplex = [
49 | [ 0, 1, 2, 3 ], [ 0, 1, 3, 2 ], [ 0, 0, 0, 0 ], [ 0, 2, 3, 1 ], [ 0, 0, 0, 0 ], [ 0, 0, 0, 0 ], [ 0, 0, 0, 0 ], [ 1, 2, 3, 0 ],
50 | [ 0, 2, 1, 3 ], [ 0, 0, 0, 0 ], [ 0, 3, 1, 2 ], [ 0, 3, 2, 1 ], [ 0, 0, 0, 0 ], [ 0, 0, 0, 0 ], [ 0, 0, 0, 0 ], [ 1, 3, 2, 0 ],
51 | [ 0, 0, 0, 0 ], [ 0, 0, 0, 0 ], [ 0, 0, 0, 0 ], [ 0, 0, 0, 0 ], [ 0, 0, 0, 0 ], [ 0, 0, 0, 0 ], [ 0, 0, 0, 0 ], [ 0, 0, 0, 0 ],
52 | [ 1, 2, 0, 3 ], [ 0, 0, 0, 0 ], [ 1, 3, 0, 2 ], [ 0, 0, 0, 0 ], [ 0, 0, 0, 0 ], [ 0, 0, 0, 0 ], [ 2, 3, 0, 1 ], [ 2, 3, 1, 0 ],
53 | [ 1, 0, 2, 3 ], [ 1, 0, 3, 2 ], [ 0, 0, 0, 0 ], [ 0, 0, 0, 0 ], [ 0, 0, 0, 0 ], [ 2, 0, 3, 1 ], [ 0, 0, 0, 0 ], [ 2, 1, 3, 0 ],
54 | [ 0, 0, 0, 0 ], [ 0, 0, 0, 0 ], [ 0, 0, 0, 0 ], [ 0, 0, 0, 0 ], [ 0, 0, 0, 0 ], [ 0, 0, 0, 0 ], [ 0, 0, 0, 0 ], [ 0, 0, 0, 0 ],
55 | [ 2, 0, 1, 3 ], [ 0, 0, 0, 0 ], [ 0, 0, 0, 0 ], [ 0, 0, 0, 0 ], [ 3, 0, 1, 2 ], [ 3, 0, 2, 1 ], [ 0, 0, 0, 0 ], [ 3, 1, 2, 0 ],
56 | [ 2, 1, 0, 3 ], [ 0, 0, 0, 0 ], [ 0, 0, 0, 0 ], [ 0, 0, 0, 0 ], [ 3, 1, 0, 2 ], [ 0, 0, 0, 0 ], [ 3, 2, 0, 1 ], [ 3, 2, 1, 0 ]];
57 |
58 | };
59 |
60 | SimplexNoise.prototype.dot = function ( g, x, y ) {
61 |
62 | return g[ 0 ] * x + g[ 1 ] * y;
63 |
64 | };
65 |
66 | SimplexNoise.prototype.dot3 = function ( g, x, y, z ) {
67 |
68 | return g[ 0 ] * x + g[ 1 ] * y + g[ 2 ] * z;
69 |
70 | };
71 |
72 | SimplexNoise.prototype.dot4 = function ( g, x, y, z, w ) {
73 |
74 | return g[ 0 ] * x + g[ 1 ] * y + g[ 2 ] * z + g[ 3 ] * w;
75 |
76 | };
77 |
78 | SimplexNoise.prototype.noise = function ( xin, yin ) {
79 |
80 | var n0, n1, n2; // Noise contributions from the three corners
81 | // Skew the input space to determine which simplex cell we're in
82 | var F2 = 0.5 * ( Math.sqrt( 3.0 ) - 1.0 );
83 | var s = ( xin + yin ) * F2; // Hairy factor for 2D
84 | var i = Math.floor( xin + s );
85 | var j = Math.floor( yin + s );
86 | var G2 = ( 3.0 - Math.sqrt( 3.0 ) ) / 6.0;
87 | var t = ( i + j ) * G2;
88 | var X0 = i - t; // Unskew the cell origin back to (x,y) space
89 | var Y0 = j - t;
90 | var x0 = xin - X0; // The x,y distances from the cell origin
91 | var y0 = yin - Y0;
92 | // For the 2D case, the simplex shape is an equilateral triangle.
93 | // Determine which simplex we are in.
94 | var i1, j1; // Offsets for second (middle) corner of simplex in (i,j) coords
95 | if ( x0 > y0 ) {
96 |
97 | i1 = 1; j1 = 0;
98 |
99 | // lower triangle, XY order: (0,0)->(1,0)->(1,1)
100 |
101 | } else {
102 |
103 | i1 = 0; j1 = 1;
104 |
105 | } // upper triangle, YX order: (0,0)->(0,1)->(1,1)
106 |
107 | // A step of (1,0) in (i,j) means a step of (1-c,-c) in (x,y), and
108 | // a step of (0,1) in (i,j) means a step of (-c,1-c) in (x,y), where
109 | // c = (3-sqrt(3))/6
110 | var x1 = x0 - i1 + G2; // Offsets for middle corner in (x,y) unskewed coords
111 | var y1 = y0 - j1 + G2;
112 | var x2 = x0 - 1.0 + 2.0 * G2; // Offsets for last corner in (x,y) unskewed coords
113 | var y2 = y0 - 1.0 + 2.0 * G2;
114 | // Work out the hashed gradient indices of the three simplex corners
115 | var ii = i & 255;
116 | var jj = j & 255;
117 | var gi0 = this.perm[ ii + this.perm[ jj ] ] % 12;
118 | var gi1 = this.perm[ ii + i1 + this.perm[ jj + j1 ] ] % 12;
119 | var gi2 = this.perm[ ii + 1 + this.perm[ jj + 1 ] ] % 12;
120 | // Calculate the contribution from the three corners
121 | var t0 = 0.5 - x0 * x0 - y0 * y0;
122 | if ( t0 < 0 ) n0 = 0.0;
123 | else {
124 |
125 | t0 *= t0;
126 | n0 = t0 * t0 * this.dot( this.grad3[ gi0 ], x0, y0 ); // (x,y) of grad3 used for 2D gradient
127 |
128 | }
129 |
130 | var t1 = 0.5 - x1 * x1 - y1 * y1;
131 | if ( t1 < 0 ) n1 = 0.0;
132 | else {
133 |
134 | t1 *= t1;
135 | n1 = t1 * t1 * this.dot( this.grad3[ gi1 ], x1, y1 );
136 |
137 | }
138 |
139 | var t2 = 0.5 - x2 * x2 - y2 * y2;
140 | if ( t2 < 0 ) n2 = 0.0;
141 | else {
142 |
143 | t2 *= t2;
144 | n2 = t2 * t2 * this.dot( this.grad3[ gi2 ], x2, y2 );
145 |
146 | }
147 |
148 | // Add contributions from each corner to get the final noise value.
149 | // The result is scaled to return values in the interval [-1,1].
150 | return 70.0 * ( n0 + n1 + n2 );
151 |
152 | };
153 |
154 | // 3D simplex noise
155 | SimplexNoise.prototype.noise3d = function ( xin, yin, zin ) {
156 |
157 | var n0, n1, n2, n3; // Noise contributions from the four corners
158 | // Skew the input space to determine which simplex cell we're in
159 | var F3 = 1.0 / 3.0;
160 | var s = ( xin + yin + zin ) * F3; // Very nice and simple skew factor for 3D
161 | var i = Math.floor( xin + s );
162 | var j = Math.floor( yin + s );
163 | var k = Math.floor( zin + s );
164 | var G3 = 1.0 / 6.0; // Very nice and simple unskew factor, too
165 | var t = ( i + j + k ) * G3;
166 | var X0 = i - t; // Unskew the cell origin back to (x,y,z) space
167 | var Y0 = j - t;
168 | var Z0 = k - t;
169 | var x0 = xin - X0; // The x,y,z distances from the cell origin
170 | var y0 = yin - Y0;
171 | var z0 = zin - Z0;
172 | // For the 3D case, the simplex shape is a slightly irregular tetrahedron.
173 | // Determine which simplex we are in.
174 | var i1, j1, k1; // Offsets for second corner of simplex in (i,j,k) coords
175 | var i2, j2, k2; // Offsets for third corner of simplex in (i,j,k) coords
176 | if ( x0 >= y0 ) {
177 |
178 | if ( y0 >= z0 ) {
179 |
180 | i1 = 1; j1 = 0; k1 = 0; i2 = 1; j2 = 1; k2 = 0;
181 |
182 | // X Y Z order
183 |
184 | } else if ( x0 >= z0 ) {
185 |
186 | i1 = 1; j1 = 0; k1 = 0; i2 = 1; j2 = 0; k2 = 1;
187 |
188 | // X Z Y order
189 |
190 | } else {
191 |
192 | i1 = 0; j1 = 0; k1 = 1; i2 = 1; j2 = 0; k2 = 1;
193 |
194 | } // Z X Y order
195 |
196 | } else { // x0 y0 ) ? 32 : 0;
319 | var c2 = ( x0 > z0 ) ? 16 : 0;
320 | var c3 = ( y0 > z0 ) ? 8 : 0;
321 | var c4 = ( x0 > w0 ) ? 4 : 0;
322 | var c5 = ( y0 > w0 ) ? 2 : 0;
323 | var c6 = ( z0 > w0 ) ? 1 : 0;
324 | var c = c1 + c2 + c3 + c4 + c5 + c6;
325 | var i1, j1, k1, l1; // The integer offsets for the second simplex corner
326 | var i2, j2, k2, l2; // The integer offsets for the third simplex corner
327 | var i3, j3, k3, l3; // The integer offsets for the fourth simplex corner
328 | // simplex[c] is a 4-vector with the numbers 0, 1, 2 and 3 in some order.
329 | // Many values of c will never occur, since e.g. x>y>z>w makes x= 3 ? 1 : 0;
334 | j1 = simplex[ c ][ 1 ] >= 3 ? 1 : 0;
335 | k1 = simplex[ c ][ 2 ] >= 3 ? 1 : 0;
336 | l1 = simplex[ c ][ 3 ] >= 3 ? 1 : 0;
337 | // The number 2 in the "simplex" array is at the second largest coordinate.
338 | i2 = simplex[ c ][ 0 ] >= 2 ? 1 : 0;
339 | j2 = simplex[ c ][ 1 ] >= 2 ? 1 : 0; k2 = simplex[ c ][ 2 ] >= 2 ? 1 : 0;
340 | l2 = simplex[ c ][ 3 ] >= 2 ? 1 : 0;
341 | // The number 1 in the "simplex" array is at the second smallest coordinate.
342 | i3 = simplex[ c ][ 0 ] >= 1 ? 1 : 0;
343 | j3 = simplex[ c ][ 1 ] >= 1 ? 1 : 0;
344 | k3 = simplex[ c ][ 2 ] >= 1 ? 1 : 0;
345 | l3 = simplex[ c ][ 3 ] >= 1 ? 1 : 0;
346 | // The fifth corner has all coordinate offsets = 1, so no need to look that up.
347 | var x1 = x0 - i1 + G4; // Offsets for second corner in (x,y,z,w) coords
348 | var y1 = y0 - j1 + G4;
349 | var z1 = z0 - k1 + G4;
350 | var w1 = w0 - l1 + G4;
351 | var x2 = x0 - i2 + 2.0 * G4; // Offsets for third corner in (x,y,z,w) coords
352 | var y2 = y0 - j2 + 2.0 * G4;
353 | var z2 = z0 - k2 + 2.0 * G4;
354 | var w2 = w0 - l2 + 2.0 * G4;
355 | var x3 = x0 - i3 + 3.0 * G4; // Offsets for fourth corner in (x,y,z,w) coords
356 | var y3 = y0 - j3 + 3.0 * G4;
357 | var z3 = z0 - k3 + 3.0 * G4;
358 | var w3 = w0 - l3 + 3.0 * G4;
359 | var x4 = x0 - 1.0 + 4.0 * G4; // Offsets for last corner in (x,y,z,w) coords
360 | var y4 = y0 - 1.0 + 4.0 * G4;
361 | var z4 = z0 - 1.0 + 4.0 * G4;
362 | var w4 = w0 - 1.0 + 4.0 * G4;
363 | // Work out the hashed gradient indices of the five simplex corners
364 | var ii = i & 255;
365 | var jj = j & 255;
366 | var kk = k & 255;
367 | var ll = l & 255;
368 | var gi0 = perm[ ii + perm[ jj + perm[ kk + perm[ ll ] ] ] ] % 32;
369 | var gi1 = perm[ ii + i1 + perm[ jj + j1 + perm[ kk + k1 + perm[ ll + l1 ] ] ] ] % 32;
370 | var gi2 = perm[ ii + i2 + perm[ jj + j2 + perm[ kk + k2 + perm[ ll + l2 ] ] ] ] % 32;
371 | var gi3 = perm[ ii + i3 + perm[ jj + j3 + perm[ kk + k3 + perm[ ll + l3 ] ] ] ] % 32;
372 | var gi4 = perm[ ii + 1 + perm[ jj + 1 + perm[ kk + 1 + perm[ ll + 1 ] ] ] ] % 32;
373 | // Calculate the contribution from the five corners
374 | var t0 = 0.6 - x0 * x0 - y0 * y0 - z0 * z0 - w0 * w0;
375 | if ( t0 < 0 ) n0 = 0.0;
376 | else {
377 |
378 | t0 *= t0;
379 | n0 = t0 * t0 * this.dot4( grad4[ gi0 ], x0, y0, z0, w0 );
380 |
381 | }
382 |
383 | var t1 = 0.6 - x1 * x1 - y1 * y1 - z1 * z1 - w1 * w1;
384 | if ( t1 < 0 ) n1 = 0.0;
385 | else {
386 |
387 | t1 *= t1;
388 | n1 = t1 * t1 * this.dot4( grad4[ gi1 ], x1, y1, z1, w1 );
389 |
390 | }
391 |
392 | var t2 = 0.6 - x2 * x2 - y2 * y2 - z2 * z2 - w2 * w2;
393 | if ( t2 < 0 ) n2 = 0.0;
394 | else {
395 |
396 | t2 *= t2;
397 | n2 = t2 * t2 * this.dot4( grad4[ gi2 ], x2, y2, z2, w2 );
398 |
399 | }
400 |
401 | var t3 = 0.6 - x3 * x3 - y3 * y3 - z3 * z3 - w3 * w3;
402 | if ( t3 < 0 ) n3 = 0.0;
403 | else {
404 |
405 | t3 *= t3;
406 | n3 = t3 * t3 * this.dot4( grad4[ gi3 ], x3, y3, z3, w3 );
407 |
408 | }
409 |
410 | var t4 = 0.6 - x4 * x4 - y4 * y4 - z4 * z4 - w4 * w4;
411 | if ( t4 < 0 ) n4 = 0.0;
412 | else {
413 |
414 | t4 *= t4;
415 | n4 = t4 * t4 * this.dot4( grad4[ gi4 ], x4, y4, z4, w4 );
416 |
417 | }
418 |
419 | // Sum up and scale the result to cover the range [-1,1]
420 | return 27.0 * ( n0 + n1 + n2 + n3 + n4 );
421 |
422 | };
423 |
424 | export { SimplexNoise };
425 |
--------------------------------------------------------------------------------
/js/examples/jsm/postprocessing/EffectComposer.js:
--------------------------------------------------------------------------------
1 | import {
2 | Clock,
3 | LinearFilter,
4 | Mesh,
5 | OrthographicCamera,
6 | PlaneBufferGeometry,
7 | RGBAFormat,
8 | Vector2,
9 | WebGLRenderTarget
10 | } from "../../../build/three.module.js";
11 | import { CopyShader } from "../shaders/CopyShader.js";
12 | import { ShaderPass } from "../postprocessing/ShaderPass.js";
13 | import { MaskPass } from "../postprocessing/MaskPass.js";
14 | import { ClearMaskPass } from "../postprocessing/MaskPass.js";
15 |
16 | var EffectComposer = function ( renderer, renderTarget ) {
17 |
18 | this.renderer = renderer;
19 |
20 | if ( renderTarget === undefined ) {
21 |
22 | var parameters = {
23 | minFilter: LinearFilter,
24 | magFilter: LinearFilter,
25 | format: RGBAFormat
26 | };
27 |
28 | var size = renderer.getSize( new Vector2() );
29 | this._pixelRatio = renderer.getPixelRatio();
30 | this._width = size.width;
31 | this._height = size.height;
32 |
33 | renderTarget = new WebGLRenderTarget( this._width * this._pixelRatio, this._height * this._pixelRatio, parameters );
34 | renderTarget.texture.name = 'EffectComposer.rt1';
35 |
36 | } else {
37 |
38 | this._pixelRatio = 1;
39 | this._width = renderTarget.width;
40 | this._height = renderTarget.height;
41 |
42 | }
43 |
44 | this.renderTarget1 = renderTarget;
45 | this.renderTarget2 = renderTarget.clone();
46 | this.renderTarget2.texture.name = 'EffectComposer.rt2';
47 |
48 | this.writeBuffer = this.renderTarget1;
49 | this.readBuffer = this.renderTarget2;
50 |
51 | this.renderToScreen = true;
52 |
53 | this.passes = [];
54 |
55 | // dependencies
56 |
57 | if ( CopyShader === undefined ) {
58 |
59 | console.error( 'THREE.EffectComposer relies on CopyShader' );
60 |
61 | }
62 |
63 | if ( ShaderPass === undefined ) {
64 |
65 | console.error( 'THREE.EffectComposer relies on ShaderPass' );
66 |
67 | }
68 |
69 | this.copyPass = new ShaderPass( CopyShader );
70 |
71 | this.clock = new Clock();
72 |
73 | };
74 |
75 | Object.assign( EffectComposer.prototype, {
76 |
77 | swapBuffers: function () {
78 |
79 | var tmp = this.readBuffer;
80 | this.readBuffer = this.writeBuffer;
81 | this.writeBuffer = tmp;
82 |
83 | },
84 |
85 | addPass: function ( pass ) {
86 |
87 | this.passes.push( pass );
88 | pass.setSize( this._width * this._pixelRatio, this._height * this._pixelRatio );
89 |
90 | },
91 |
92 | insertPass: function ( pass, index ) {
93 |
94 | this.passes.splice( index, 0, pass );
95 | pass.setSize( this._width * this._pixelRatio, this._height * this._pixelRatio );
96 |
97 | },
98 |
99 | removePass: function ( pass ) {
100 |
101 | const index = this.passes.indexOf( pass );
102 |
103 | if ( index !== - 1 ) {
104 |
105 | this.passes.splice( index, 1 );
106 |
107 | }
108 |
109 | },
110 |
111 | isLastEnabledPass: function ( passIndex ) {
112 |
113 | for ( var i = passIndex + 1; i < this.passes.length; i ++ ) {
114 |
115 | if ( this.passes[ i ].enabled ) {
116 |
117 | return false;
118 |
119 | }
120 |
121 | }
122 |
123 | return true;
124 |
125 | },
126 |
127 | render: function ( deltaTime ) {
128 |
129 | // deltaTime value is in seconds
130 |
131 | if ( deltaTime === undefined ) {
132 |
133 | deltaTime = this.clock.getDelta();
134 |
135 | }
136 |
137 | var currentRenderTarget = this.renderer.getRenderTarget();
138 |
139 | var maskActive = false;
140 |
141 | var pass, i, il = this.passes.length;
142 |
143 | for ( i = 0; i < il; i ++ ) {
144 |
145 | pass = this.passes[ i ];
146 |
147 | if ( pass.enabled === false ) continue;
148 |
149 | pass.renderToScreen = ( this.renderToScreen && this.isLastEnabledPass( i ) );
150 | pass.render( this.renderer, this.writeBuffer, this.readBuffer, deltaTime, maskActive );
151 |
152 | if ( pass.needsSwap ) {
153 |
154 | if ( maskActive ) {
155 |
156 | var context = this.renderer.getContext();
157 | var stencil = this.renderer.state.buffers.stencil;
158 |
159 | //context.stencilFunc( context.NOTEQUAL, 1, 0xffffffff );
160 | stencil.setFunc( context.NOTEQUAL, 1, 0xffffffff );
161 |
162 | this.copyPass.render( this.renderer, this.writeBuffer, this.readBuffer, deltaTime );
163 |
164 | //context.stencilFunc( context.EQUAL, 1, 0xffffffff );
165 | stencil.setFunc( context.EQUAL, 1, 0xffffffff );
166 |
167 | }
168 |
169 | this.swapBuffers();
170 |
171 | }
172 |
173 | if ( MaskPass !== undefined ) {
174 |
175 | if ( pass instanceof MaskPass ) {
176 |
177 | maskActive = true;
178 |
179 | } else if ( pass instanceof ClearMaskPass ) {
180 |
181 | maskActive = false;
182 |
183 | }
184 |
185 | }
186 |
187 | }
188 |
189 | this.renderer.setRenderTarget( currentRenderTarget );
190 |
191 | },
192 |
193 | reset: function ( renderTarget ) {
194 |
195 | if ( renderTarget === undefined ) {
196 |
197 | var size = this.renderer.getSize( new Vector2() );
198 | this._pixelRatio = this.renderer.getPixelRatio();
199 | this._width = size.width;
200 | this._height = size.height;
201 |
202 | renderTarget = this.renderTarget1.clone();
203 | renderTarget.setSize( this._width * this._pixelRatio, this._height * this._pixelRatio );
204 |
205 | }
206 |
207 | this.renderTarget1.dispose();
208 | this.renderTarget2.dispose();
209 | this.renderTarget1 = renderTarget;
210 | this.renderTarget2 = renderTarget.clone();
211 |
212 | this.writeBuffer = this.renderTarget1;
213 | this.readBuffer = this.renderTarget2;
214 |
215 | },
216 |
217 | setSize: function ( width, height ) {
218 |
219 | this._width = width;
220 | this._height = height;
221 |
222 | var effectiveWidth = this._width * this._pixelRatio;
223 | var effectiveHeight = this._height * this._pixelRatio;
224 |
225 | this.renderTarget1.setSize( effectiveWidth, effectiveHeight );
226 | this.renderTarget2.setSize( effectiveWidth, effectiveHeight );
227 |
228 | for ( var i = 0; i < this.passes.length; i ++ ) {
229 |
230 | this.passes[ i ].setSize( effectiveWidth, effectiveHeight );
231 |
232 | }
233 |
234 | },
235 |
236 | setPixelRatio: function ( pixelRatio ) {
237 |
238 | this._pixelRatio = pixelRatio;
239 |
240 | this.setSize( this._width, this._height );
241 |
242 | }
243 |
244 | } );
245 |
246 |
247 | var Pass = function () {
248 |
249 | // if set to true, the pass is processed by the composer
250 | this.enabled = true;
251 |
252 | // if set to true, the pass indicates to swap read and write buffer after rendering
253 | this.needsSwap = true;
254 |
255 | // if set to true, the pass clears its buffer before rendering
256 | this.clear = false;
257 |
258 | // if set to true, the result of the pass is rendered to screen. This is set automatically by EffectComposer.
259 | this.renderToScreen = false;
260 |
261 | };
262 |
263 | Object.assign( Pass.prototype, {
264 |
265 | setSize: function ( /* width, height */ ) {},
266 |
267 | render: function ( /* renderer, writeBuffer, readBuffer, deltaTime, maskActive */ ) {
268 |
269 | console.error( 'THREE.Pass: .render() must be implemented in derived pass.' );
270 |
271 | }
272 |
273 | } );
274 |
275 | // Helper for passes that need to fill the viewport with a single quad.
276 | Pass.FullScreenQuad = ( function () {
277 |
278 | var camera = new OrthographicCamera( - 1, 1, 1, - 1, 0, 1 );
279 | var geometry = new PlaneBufferGeometry( 2, 2 );
280 |
281 | var FullScreenQuad = function ( material ) {
282 |
283 | this._mesh = new Mesh( geometry, material );
284 |
285 | };
286 |
287 | Object.defineProperty( FullScreenQuad.prototype, 'material', {
288 |
289 | get: function () {
290 |
291 | return this._mesh.material;
292 |
293 | },
294 |
295 | set: function ( value ) {
296 |
297 | this._mesh.material = value;
298 |
299 | }
300 |
301 | } );
302 |
303 | Object.assign( FullScreenQuad.prototype, {
304 |
305 | dispose: function () {
306 |
307 | this._mesh.geometry.dispose();
308 |
309 | },
310 |
311 | render: function ( renderer ) {
312 |
313 | renderer.render( this._mesh, camera );
314 |
315 | }
316 |
317 | } );
318 |
319 | return FullScreenQuad;
320 |
321 | } )();
322 |
323 | export { EffectComposer, Pass };
324 |
--------------------------------------------------------------------------------
/js/examples/jsm/postprocessing/MaskPass.js:
--------------------------------------------------------------------------------
1 | import { Pass } from "../postprocessing/Pass.js";
2 |
3 | var MaskPass = function ( scene, camera ) {
4 |
5 | Pass.call( this );
6 |
7 | this.scene = scene;
8 | this.camera = camera;
9 |
10 | this.clear = true;
11 | this.needsSwap = false;
12 |
13 | this.inverse = false;
14 |
15 | };
16 |
17 | MaskPass.prototype = Object.assign( Object.create( Pass.prototype ), {
18 |
19 | constructor: MaskPass,
20 |
21 | render: function ( renderer, writeBuffer, readBuffer /*, deltaTime, maskActive */ ) {
22 |
23 | var context = renderer.getContext();
24 | var state = renderer.state;
25 |
26 | // don't update color or depth
27 |
28 | state.buffers.color.setMask( false );
29 | state.buffers.depth.setMask( false );
30 |
31 | // lock buffers
32 |
33 | state.buffers.color.setLocked( true );
34 | state.buffers.depth.setLocked( true );
35 |
36 | // set up stencil
37 |
38 | var writeValue, clearValue;
39 |
40 | if ( this.inverse ) {
41 |
42 | writeValue = 0;
43 | clearValue = 1;
44 |
45 | } else {
46 |
47 | writeValue = 1;
48 | clearValue = 0;
49 |
50 | }
51 |
52 | state.buffers.stencil.setTest( true );
53 | state.buffers.stencil.setOp( context.REPLACE, context.REPLACE, context.REPLACE );
54 | state.buffers.stencil.setFunc( context.ALWAYS, writeValue, 0xffffffff );
55 | state.buffers.stencil.setClear( clearValue );
56 | state.buffers.stencil.setLocked( true );
57 |
58 | // draw into the stencil buffer
59 |
60 | renderer.setRenderTarget( readBuffer );
61 | if ( this.clear ) renderer.clear();
62 | renderer.render( this.scene, this.camera );
63 |
64 | renderer.setRenderTarget( writeBuffer );
65 | if ( this.clear ) renderer.clear();
66 | renderer.render( this.scene, this.camera );
67 |
68 | // unlock color and depth buffer for subsequent rendering
69 |
70 | state.buffers.color.setLocked( false );
71 | state.buffers.depth.setLocked( false );
72 |
73 | // only render where stencil is set to 1
74 |
75 | state.buffers.stencil.setLocked( false );
76 | state.buffers.stencil.setFunc( context.EQUAL, 1, 0xffffffff ); // draw if == 1
77 | state.buffers.stencil.setOp( context.KEEP, context.KEEP, context.KEEP );
78 | state.buffers.stencil.setLocked( true );
79 |
80 | }
81 |
82 | } );
83 |
84 |
85 | var ClearMaskPass = function () {
86 |
87 | Pass.call( this );
88 |
89 | this.needsSwap = false;
90 |
91 | };
92 |
93 | ClearMaskPass.prototype = Object.create( Pass.prototype );
94 |
95 | Object.assign( ClearMaskPass.prototype, {
96 |
97 | render: function ( renderer /*, writeBuffer, readBuffer, deltaTime, maskActive */ ) {
98 |
99 | renderer.state.buffers.stencil.setLocked( false );
100 | renderer.state.buffers.stencil.setTest( false );
101 |
102 | }
103 |
104 | } );
105 |
106 | export { MaskPass, ClearMaskPass };
107 |
--------------------------------------------------------------------------------
/js/examples/jsm/postprocessing/Pass.js:
--------------------------------------------------------------------------------
1 | import {
2 | OrthographicCamera,
3 | PlaneBufferGeometry,
4 | Mesh
5 | } from "../../../build/three.module.js";
6 |
7 | function Pass() {
8 |
9 | // if set to true, the pass is processed by the composer
10 | this.enabled = true;
11 |
12 | // if set to true, the pass indicates to swap read and write buffer after rendering
13 | this.needsSwap = true;
14 |
15 | // if set to true, the pass clears its buffer before rendering
16 | this.clear = false;
17 |
18 | // if set to true, the result of the pass is rendered to screen. This is set automatically by EffectComposer.
19 | this.renderToScreen = false;
20 |
21 | }
22 |
23 | Object.assign( Pass.prototype, {
24 |
25 | setSize: function ( /* width, height */ ) {},
26 |
27 | render: function ( /* renderer, writeBuffer, readBuffer, deltaTime, maskActive */ ) {
28 |
29 | console.error( 'THREE.Pass: .render() must be implemented in derived pass.' );
30 |
31 | }
32 |
33 | } );
34 |
35 | // Helper for passes that need to fill the viewport with a single quad.
36 |
37 | // Important: It's actually a hack to put FullScreenQuad into the Pass namespace. This is only
38 | // done to make examples/js code work. Normally, FullScreenQuad should be exported
39 | // from this module like Pass.
40 |
41 | Pass.FullScreenQuad = ( function () {
42 |
43 | var camera = new OrthographicCamera( - 1, 1, 1, - 1, 0, 1 );
44 | var geometry = new PlaneBufferGeometry( 2, 2 );
45 |
46 | var FullScreenQuad = function ( material ) {
47 |
48 | this._mesh = new Mesh( geometry, material );
49 |
50 | };
51 |
52 | Object.defineProperty( FullScreenQuad.prototype, 'material', {
53 |
54 | get: function () {
55 |
56 | return this._mesh.material;
57 |
58 | },
59 |
60 | set: function ( value ) {
61 |
62 | this._mesh.material = value;
63 |
64 | }
65 |
66 | } );
67 |
68 | Object.assign( FullScreenQuad.prototype, {
69 |
70 | dispose: function () {
71 |
72 | this._mesh.geometry.dispose();
73 |
74 | },
75 |
76 | render: function ( renderer ) {
77 |
78 | renderer.render( this._mesh, camera );
79 |
80 | }
81 |
82 | } );
83 |
84 | return FullScreenQuad;
85 |
86 | } )();
87 |
88 | export { Pass };
89 |
--------------------------------------------------------------------------------
/js/examples/jsm/postprocessing/RenderPass.js:
--------------------------------------------------------------------------------
1 | import { Pass } from "../postprocessing/Pass.js";
2 |
3 | var RenderPass = function ( scene, camera, overrideMaterial, clearColor, clearAlpha ) {
4 |
5 | Pass.call( this );
6 |
7 | this.scene = scene;
8 | this.camera = camera;
9 |
10 | this.overrideMaterial = overrideMaterial;
11 |
12 | this.clearColor = clearColor;
13 | this.clearAlpha = ( clearAlpha !== undefined ) ? clearAlpha : 0;
14 |
15 | this.clear = true;
16 | this.clearDepth = false;
17 | this.needsSwap = false;
18 |
19 | };
20 |
21 | RenderPass.prototype = Object.assign( Object.create( Pass.prototype ), {
22 |
23 | constructor: RenderPass,
24 |
25 | render: function ( renderer, writeBuffer, readBuffer /*, deltaTime, maskActive */ ) {
26 |
27 | var oldAutoClear = renderer.autoClear;
28 | renderer.autoClear = false;
29 |
30 | var oldClearColor, oldClearAlpha, oldOverrideMaterial;
31 |
32 | if ( this.overrideMaterial !== undefined ) {
33 |
34 | oldOverrideMaterial = this.scene.overrideMaterial;
35 |
36 | this.scene.overrideMaterial = this.overrideMaterial;
37 |
38 | }
39 |
40 | if ( this.clearColor ) {
41 |
42 | oldClearColor = renderer.getClearColor().getHex();
43 | oldClearAlpha = renderer.getClearAlpha();
44 |
45 | renderer.setClearColor( this.clearColor, this.clearAlpha );
46 |
47 | }
48 |
49 | if ( this.clearDepth ) {
50 |
51 | renderer.clearDepth();
52 |
53 | }
54 |
55 | renderer.setRenderTarget( this.renderToScreen ? null : readBuffer );
56 |
57 | // TODO: Avoid using autoClear properties, see https://github.com/mrdoob/three.js/pull/15571#issuecomment-465669600
58 | if ( this.clear ) renderer.clear( renderer.autoClearColor, renderer.autoClearDepth, renderer.autoClearStencil );
59 | renderer.render( this.scene, this.camera );
60 |
61 | if ( this.clearColor ) {
62 |
63 | renderer.setClearColor( oldClearColor, oldClearAlpha );
64 |
65 | }
66 |
67 | if ( this.overrideMaterial !== undefined ) {
68 |
69 | this.scene.overrideMaterial = oldOverrideMaterial;
70 |
71 | }
72 |
73 | renderer.autoClear = oldAutoClear;
74 |
75 | }
76 |
77 | } );
78 |
79 | export { RenderPass };
80 |
--------------------------------------------------------------------------------
/js/examples/jsm/postprocessing/ShaderPass.js:
--------------------------------------------------------------------------------
1 | import {
2 | ShaderMaterial,
3 | UniformsUtils
4 | } from "../../../build/three.module.js";
5 | import { Pass } from "../postprocessing/Pass.js";
6 |
7 | var ShaderPass = function ( shader, textureID ) {
8 |
9 | Pass.call( this );
10 |
11 | this.textureID = ( textureID !== undefined ) ? textureID : "tDiffuse";
12 |
13 | if ( shader instanceof ShaderMaterial ) {
14 |
15 | this.uniforms = shader.uniforms;
16 |
17 | this.material = shader;
18 |
19 | } else if ( shader ) {
20 |
21 | this.uniforms = UniformsUtils.clone( shader.uniforms );
22 |
23 | this.material = new ShaderMaterial( {
24 |
25 | defines: Object.assign( {}, shader.defines ),
26 | uniforms: this.uniforms,
27 | vertexShader: shader.vertexShader,
28 | fragmentShader: shader.fragmentShader
29 |
30 | } );
31 |
32 | }
33 |
34 | this.fsQuad = new Pass.FullScreenQuad( this.material );
35 |
36 | };
37 |
38 | ShaderPass.prototype = Object.assign( Object.create( Pass.prototype ), {
39 |
40 | constructor: ShaderPass,
41 |
42 | render: function ( renderer, writeBuffer, readBuffer /*, deltaTime, maskActive */ ) {
43 |
44 | if ( this.uniforms[ this.textureID ] ) {
45 |
46 | this.uniforms[ this.textureID ].value = readBuffer.texture;
47 |
48 | }
49 |
50 | this.fsQuad.material = this.material;
51 |
52 | if ( this.renderToScreen ) {
53 |
54 | renderer.setRenderTarget( null );
55 | this.fsQuad.render( renderer );
56 |
57 | } else {
58 |
59 | renderer.setRenderTarget( writeBuffer );
60 | // TODO: Avoid using autoClear properties, see https://github.com/mrdoob/three.js/pull/15571#issuecomment-465669600
61 | if ( this.clear ) renderer.clear( renderer.autoClearColor, renderer.autoClearDepth, renderer.autoClearStencil );
62 | this.fsQuad.render( renderer );
63 |
64 | }
65 |
66 | }
67 |
68 | } );
69 |
70 | export { ShaderPass };
71 |
--------------------------------------------------------------------------------
/js/examples/jsm/shaders/CopyShader.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Full-screen textured quad shader
3 | */
4 |
5 | var CopyShader = {
6 |
7 | uniforms: {
8 |
9 | "tDiffuse": { value: null },
10 | "opacity": { value: 1.0 }
11 |
12 | },
13 |
14 | vertexShader: [
15 |
16 | "varying vec2 vUv;",
17 |
18 | "void main() {",
19 |
20 | " vUv = uv;",
21 | " gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );",
22 |
23 | "}"
24 |
25 | ].join( "\n" ),
26 |
27 | fragmentShader: [
28 |
29 | "uniform float opacity;",
30 |
31 | "uniform sampler2D tDiffuse;",
32 |
33 | "varying vec2 vUv;",
34 |
35 | "void main() {",
36 |
37 | " vec4 texel = texture2D( tDiffuse, vUv );",
38 | " gl_FragColor = opacity * texel;",
39 |
40 | "}"
41 |
42 | ].join( "\n" )
43 |
44 | };
45 |
46 | export { CopyShader };
47 |
--------------------------------------------------------------------------------
/js/examples/jsm/shaders/DepthLimitedBlurShader.js:
--------------------------------------------------------------------------------
1 | import {
2 | Vector2
3 | } from "../../../build/three.module.js";
4 |
5 | /**
6 | * TODO
7 | */
8 |
9 | var DepthLimitedBlurShader = {
10 | defines: {
11 | "KERNEL_RADIUS": 4,
12 | "DEPTH_PACKING": 1,
13 | "PERSPECTIVE_CAMERA": 1
14 | },
15 | uniforms: {
16 | "tDiffuse": { value: null },
17 | "size": { value: new Vector2( 512, 512 ) },
18 | "sampleUvOffsets": { value: [ new Vector2( 0, 0 ) ] },
19 | "sampleWeights": { value: [ 1.0 ] },
20 | "tDepth": { value: null },
21 | "cameraNear": { value: 10 },
22 | "cameraFar": { value: 1000 },
23 | "depthCutoff": { value: 10 },
24 | },
25 | vertexShader: [
26 | "#include ",
27 |
28 | "uniform vec2 size;",
29 |
30 | "varying vec2 vUv;",
31 | "varying vec2 vInvSize;",
32 |
33 | "void main() {",
34 | " vUv = uv;",
35 | " vInvSize = 1.0 / size;",
36 |
37 | " gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );",
38 | "}"
39 |
40 | ].join( "\n" ),
41 | fragmentShader: [
42 | "#include ",
43 | "#include ",
44 |
45 | "uniform sampler2D tDiffuse;",
46 | "uniform sampler2D tDepth;",
47 |
48 | "uniform float cameraNear;",
49 | "uniform float cameraFar;",
50 | "uniform float depthCutoff;",
51 |
52 | "uniform vec2 sampleUvOffsets[ KERNEL_RADIUS + 1 ];",
53 | "uniform float sampleWeights[ KERNEL_RADIUS + 1 ];",
54 |
55 | "varying vec2 vUv;",
56 | "varying vec2 vInvSize;",
57 |
58 | "float getDepth( const in vec2 screenPosition ) {",
59 | " #if DEPTH_PACKING == 1",
60 | " return unpackRGBAToDepth( texture2D( tDepth, screenPosition ) );",
61 | " #else",
62 | " return texture2D( tDepth, screenPosition ).x;",
63 | " #endif",
64 | "}",
65 |
66 | "float getViewZ( const in float depth ) {",
67 | " #if PERSPECTIVE_CAMERA == 1",
68 | " return perspectiveDepthToViewZ( depth, cameraNear, cameraFar );",
69 | " #else",
70 | " return orthographicDepthToViewZ( depth, cameraNear, cameraFar );",
71 | " #endif",
72 | "}",
73 |
74 | "void main() {",
75 | " float depth = getDepth( vUv );",
76 | " if( depth >= ( 1.0 - EPSILON ) ) {",
77 | " discard;",
78 | " }",
79 |
80 | " float centerViewZ = -getViewZ( depth );",
81 | " bool rBreak = false, lBreak = false;",
82 |
83 | " float weightSum = sampleWeights[0];",
84 | " vec4 diffuseSum = texture2D( tDiffuse, vUv ) * weightSum;",
85 |
86 | " for( int i = 1; i <= KERNEL_RADIUS; i ++ ) {",
87 |
88 | " float sampleWeight = sampleWeights[i];",
89 | " vec2 sampleUvOffset = sampleUvOffsets[i] * vInvSize;",
90 |
91 | " vec2 sampleUv = vUv + sampleUvOffset;",
92 | " float viewZ = -getViewZ( getDepth( sampleUv ) );",
93 |
94 | " if( abs( viewZ - centerViewZ ) > depthCutoff ) rBreak = true;",
95 |
96 | " if( ! rBreak ) {",
97 | " diffuseSum += texture2D( tDiffuse, sampleUv ) * sampleWeight;",
98 | " weightSum += sampleWeight;",
99 | " }",
100 |
101 | " sampleUv = vUv - sampleUvOffset;",
102 | " viewZ = -getViewZ( getDepth( sampleUv ) );",
103 |
104 | " if( abs( viewZ - centerViewZ ) > depthCutoff ) lBreak = true;",
105 |
106 | " if( ! lBreak ) {",
107 | " diffuseSum += texture2D( tDiffuse, sampleUv ) * sampleWeight;",
108 | " weightSum += sampleWeight;",
109 | " }",
110 |
111 | " }",
112 |
113 | " gl_FragColor = diffuseSum / weightSum;",
114 | "}"
115 | ].join( "\n" )
116 | };
117 |
118 | var BlurShaderUtils = {
119 |
120 | createSampleWeights: function ( kernelRadius, stdDev ) {
121 |
122 | var gaussian = function ( x, stdDev ) {
123 |
124 | return Math.exp( - ( x * x ) / ( 2.0 * ( stdDev * stdDev ) ) ) / ( Math.sqrt( 2.0 * Math.PI ) * stdDev );
125 |
126 | };
127 |
128 | var weights = [];
129 |
130 | for ( var i = 0; i <= kernelRadius; i ++ ) {
131 |
132 | weights.push( gaussian( i, stdDev ) );
133 |
134 | }
135 |
136 | return weights;
137 |
138 | },
139 |
140 | createSampleOffsets: function ( kernelRadius, uvIncrement ) {
141 |
142 | var offsets = [];
143 |
144 | for ( var i = 0; i <= kernelRadius; i ++ ) {
145 |
146 | offsets.push( uvIncrement.clone().multiplyScalar( i ) );
147 |
148 | }
149 |
150 | return offsets;
151 |
152 | },
153 |
154 | configure: function ( material, kernelRadius, stdDev, uvIncrement ) {
155 |
156 | material.defines[ "KERNEL_RADIUS" ] = kernelRadius;
157 | material.uniforms[ "sampleUvOffsets" ].value = BlurShaderUtils.createSampleOffsets( kernelRadius, uvIncrement );
158 | material.uniforms[ "sampleWeights" ].value = BlurShaderUtils.createSampleWeights( kernelRadius, stdDev );
159 | material.needsUpdate = true;
160 |
161 | }
162 |
163 | };
164 |
165 | export { DepthLimitedBlurShader, BlurShaderUtils };
166 |
--------------------------------------------------------------------------------
/js/examples/jsm/shaders/UnpackDepthRGBAShader.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Unpack RGBA depth shader
3 | * - show RGBA encoded depth as monochrome color
4 | */
5 |
6 | var UnpackDepthRGBAShader = {
7 |
8 | uniforms: {
9 |
10 | "tDiffuse": { value: null },
11 | "opacity": { value: 1.0 }
12 |
13 | },
14 |
15 | vertexShader: [
16 |
17 | "varying vec2 vUv;",
18 |
19 | "void main() {",
20 |
21 | " vUv = uv;",
22 | " gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );",
23 |
24 | "}"
25 |
26 | ].join( "\n" ),
27 |
28 | fragmentShader: [
29 |
30 | "uniform float opacity;",
31 |
32 | "uniform sampler2D tDiffuse;",
33 |
34 | "varying vec2 vUv;",
35 |
36 | "#include ",
37 |
38 | "void main() {",
39 |
40 | " float depth = 1.0 - unpackRGBAToDepth( texture2D( tDiffuse, vUv ) );",
41 | " gl_FragColor = vec4( vec3( depth ), opacity );",
42 |
43 | "}"
44 |
45 | ].join( "\n" )
46 |
47 | };
48 |
49 | export { UnpackDepthRGBAShader };
50 |
--------------------------------------------------------------------------------
/main.css:
--------------------------------------------------------------------------------
1 | body {
2 | margin: 0;
3 | background-color: #000;
4 | color: #fff;
5 | font-family: Monospace;
6 | font-size: 13px;
7 | line-height: 24px;
8 | overscroll-behavior: none;
9 | }
10 |
11 | a {
12 | color: #ff0;
13 | text-decoration: none;
14 | }
15 |
16 | a:hover {
17 | text-decoration: underline;
18 | }
19 |
20 | button {
21 | cursor: pointer;
22 | text-transform: uppercase;
23 | }
24 |
25 | #info {
26 | position: absolute;
27 | top: 0px;
28 | width: 100%;
29 | padding: 10px;
30 | box-sizing: border-box;
31 | text-align: center;
32 | -moz-user-select: none;
33 | -webkit-user-select: none;
34 | -ms-user-select: none;
35 | user-select: none;
36 | pointer-events: none;
37 | z-index: 1;
38 | /* TODO Solve this in HTML */
39 | }
40 |
41 | #holder {
42 | border: 10px dashed #fff;
43 | width: 200px;
44 | height: 200px;
45 | margin: 20px;
46 | display: flex;
47 | justify-content: center;
48 | align-items: center;
49 | }
50 |
51 | #holder.hover {
52 | border: 16px dashed #fff;
53 | }
54 |
55 | a,
56 | button,
57 | input,
58 | select {
59 | pointer-events: auto;
60 | }
61 |
62 | .dg.ac {
63 | -moz-user-select: none;
64 | -webkit-user-select: none;
65 | -ms-user-select: none;
66 | user-select: none;
67 | z-index: 2 !important;
68 | /* TODO Solve this in HTML */
69 | }
70 |
71 | #overlay {
72 | position: absolute;
73 | z-index: 2;
74 | top: 0;
75 | left: 0;
76 | width: 100%;
77 | height: 100%;
78 | display: flex;
79 | align-items: center;
80 | justify-content: center;
81 | background: rgba(0, 0, 0, 0.7);
82 | }
83 |
84 | #overlay button {
85 | background: #ffffff;
86 | border: 0;
87 | color: #000000;
88 | padding: 16px 20px;
89 | text-transform: uppercase;
90 | cursor: pointer;
91 | }
92 |
93 | #notSupported {
94 | width: 50%;
95 | margin: auto;
96 | background-color: #f00;
97 | margin-top: 20px;
98 | padding: 10px;
99 | }
--------------------------------------------------------------------------------
/textures/waternormals.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/meliharvey/threescaper/08208379aeda35186e240f79ccb976f11a1a7d0c/textures/waternormals.jpg
--------------------------------------------------------------------------------