├── .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 drop
Townscaper 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 --------------------------------------------------------------------------------