Fork of https://timseverien.com/posts/2017-08-17-sorting-pixels-with-webgl/ but uses an odd-even merge sort for faster sorting.
See https://developer.nvidia.com/gpugems/GPUGems2/gpugems2_chapter46.html
Fork of https://timseverien.com/posts/2017-08-17-sorting-pixels-with-webgl/ but uses an odd-even merge sort for faster sorting.
See https://developer.nvidia.com/gpugems/GPUGems2/gpugems2_chapter46.html
| /** | |
| * @author yomboprime https://github.com/yomboprime | |
| * | |
| * GPUComputationRenderer, based on SimulationRenderer by zz85 | |
| * | |
| * The GPUComputationRenderer uses the concept of variables. These variables are RGBA float textures that hold 4 floats | |
| * for each compute element (texel) | |
| * | |
| * Each variable has a fragment shader that defines the computation made to obtain the variable in question. | |
| * You can use as many variables you need, and make dependencies so you can use textures of other variables in the shader | |
| * (the sampler uniforms are added automatically) Most of the variables will need themselves as dependency. | |
| * | |
| * The renderer has actually two render targets per variable, to make ping-pong. Textures from the current frame are used | |
| * as inputs to render the textures of the next frame. | |
| * | |
| * The render targets of the variables can be used as input textures for your visualization shaders. | |
| * | |
| * Variable names should be valid identifiers and should not collide with THREE GLSL used identifiers. | |
| * a common approach could be to use 'texture' prefixing the variable name; i.e texturePosition, textureVelocity... | |
| * | |
| * The size of the computation (sizeX * sizeY) is defined as 'resolution' automatically in the shader. For example: | |
| * #DEFINE resolution vec2( 1024.0, 1024.0 ) | |
| * | |
| * ------------- | |
| * | |
| * Basic use: | |
| * | |
| * // Initialization... | |
| * | |
| * // Create computation renderer | |
| * var gpuCompute = new GPUComputationRenderer( 1024, 1024, renderer ); | |
| * | |
| * // Create initial state float textures | |
| * var pos0 = gpuCompute.createTexture(); | |
| * var vel0 = gpuCompute.createTexture(); | |
| * // and fill in here the texture data... | |
| * | |
| * // Add texture variables | |
| * var velVar = gpuCompute.addVariable( "textureVelocity", fragmentShaderVel, pos0 ); | |
| * var posVar = gpuCompute.addVariable( "texturePosition", fragmentShaderPos, vel0 ); | |
| * | |
| * // Add variable dependencies | |
| * gpuCompute.setVariableDependencies( velVar, [ velVar, posVar ] ); | |
| * gpuCompute.setVariableDependencies( posVar, [ velVar, posVar ] ); | |
| * | |
| * // Add custom uniforms | |
| * velVar.material.uniforms.time = { value: 0.0 }; | |
| * | |
| * // Check for completeness | |
| * var error = gpuCompute.init(); | |
| * if ( error !== null ) { | |
| * console.error( error ); | |
| * } | |
| * | |
| * | |
| * // In each frame... | |
| * | |
| * // Compute! | |
| * gpuCompute.compute(); | |
| * | |
| * // Update texture uniforms in your visualization materials with the gpu renderer output | |
| * myMaterial.uniforms.myTexture.value = gpuCompute.getCurrentRenderTarget( posVar ).texture; | |
| * | |
| * // Do your rendering | |
| * renderer.render( myScene, myCamera ); | |
| * | |
| * ------------- | |
| * | |
| * Also, you can use utility functions to create ShaderMaterial and perform computations (rendering between textures) | |
| * Note that the shaders can have multiple input textures. | |
| * | |
| * var myFilter1 = gpuCompute.createShaderMaterial( myFilterFragmentShader1, { theTexture: { value: null } } ); | |
| * var myFilter2 = gpuCompute.createShaderMaterial( myFilterFragmentShader2, { theTexture: { value: null } } ); | |
| * | |
| * var inputTexture = gpuCompute.createTexture(); | |
| * | |
| * // Fill in here inputTexture... | |
| * | |
| * myFilter1.uniforms.theTexture.value = inputTexture; | |
| * | |
| * var myRenderTarget = gpuCompute.createRenderTarget(); | |
| * myFilter2.uniforms.theTexture.value = myRenderTarget.texture; | |
| * | |
| * var outputRenderTarget = gpuCompute.createRenderTarget(); | |
| * | |
| * // Now use the output texture where you want: | |
| * myMaterial.uniforms.map.value = outputRenderTarget.texture; | |
| * | |
| * // And compute each frame, before rendering to screen: | |
| * gpuCompute.doRenderTarget( myFilter1, myRenderTarget ); | |
| * gpuCompute.doRenderTarget( myFilter2, outputRenderTarget ); | |
| * | |
| * | |
| * | |
| * @param {int} sizeX Computation problem size is always 2d: sizeX * sizeY elements. | |
| * @param {int} sizeY Computation problem size is always 2d: sizeX * sizeY elements. | |
| * @param {WebGLRenderer} renderer The renderer | |
| */ | |
| function GPUComputationRenderer( sizeX, sizeY, renderer ) { | |
| this.variables = []; | |
| this.currentTextureIndex = 0; | |
| var scene = new THREE.Scene(); | |
| var camera = new THREE.Camera(); | |
| camera.position.z = 1; | |
| var passThruUniforms = { | |
| texture: { value: null } | |
| }; | |
| var passThruShader = createShaderMaterial( getPassThroughFragmentShader(), passThruUniforms ); | |
| var mesh = new THREE.Mesh( new THREE.PlaneBufferGeometry( 2, 2 ), passThruShader ); | |
| scene.add( mesh ); | |
| this.addVariable = function( variableName, computeFragmentShader, initialValueTexture ) { | |
| var material = this.createShaderMaterial( computeFragmentShader ); | |
| var variable = { | |
| name: variableName, | |
| initialValueTexture: initialValueTexture, | |
| material: material, | |
| dependencies: null, | |
| renderTargets: [], | |
| wrapS: null, | |
| wrapT: null, | |
| minFilter: THREE.NearestFilter, | |
| magFilter: THREE.NearestFilter | |
| }; | |
| this.variables.push( variable ); | |
| return variable; | |
| }; | |
| this.setVariableDependencies = function( variable, dependencies ) { | |
| variable.dependencies = dependencies; | |
| }; | |
| this.init = function() { | |
| if ( ! renderer.extensions.get( "OES_texture_float" ) ) { | |
| return "No OES_texture_float support for float textures."; | |
| } | |
| if ( renderer.capabilities.maxVertexTextures === 0 ) { | |
| return "No support for vertex shader textures."; | |
| } | |
| for ( var i = 0; i < this.variables.length; i++ ) { | |
| var variable = this.variables[ i ]; | |
| // Creates rendertargets and initialize them with input texture | |
| variable.renderTargets[ 0 ] = this.createRenderTarget( sizeX, sizeY, variable.wrapS, variable.wrapT, variable.minFilter, variable.magFilter ); | |
| variable.renderTargets[ 1 ] = this.createRenderTarget( sizeX, sizeY, variable.wrapS, variable.wrapT, variable.minFilter, variable.magFilter ); | |
| this.renderTexture( variable.initialValueTexture, variable.renderTargets[ 0 ] ); | |
| this.renderTexture( variable.initialValueTexture, variable.renderTargets[ 1 ] ); | |
| // Adds dependencies uniforms to the ShaderMaterial | |
| var material = variable.material; | |
| var uniforms = material.uniforms; | |
| if ( variable.dependencies !== null ) { | |
| for ( var d = 0; d < variable.dependencies.length; d++ ) { | |
| var depVar = variable.dependencies[ d ]; | |
| if ( depVar.name !== variable.name ) { | |
| // Checks if variable exists | |
| var found = false; | |
| for ( var j = 0; j < this.variables.length; j++ ) { | |
| if ( depVar.name === this.variables[ j ].name ) { | |
| found = true; | |
| break; | |
| } | |
| } | |
| if ( ! found ) { | |
| return "Variable dependency not found. Variable=" + variable.name + ", dependency=" + depVar.name; | |
| } | |
| } | |
| uniforms[ depVar.name ] = { value: null }; | |
| material.fragmentShader = "\nuniform sampler2D " + depVar.name + ";\n" + material.fragmentShader; | |
| } | |
| } | |
| } | |
| this.currentTextureIndex = 0; | |
| return null; | |
| }; | |
| this.compute = function() { | |
| var currentTextureIndex = this.currentTextureIndex; | |
| var nextTextureIndex = this.currentTextureIndex === 0 ? 1 : 0; | |
| for ( var i = 0, il = this.variables.length; i < il; i++ ) { | |
| var variable = this.variables[ i ]; | |
| // Sets texture dependencies uniforms | |
| if ( variable.dependencies !== null ) { | |
| var uniforms = variable.material.uniforms; | |
| for ( var d = 0, dl = variable.dependencies.length; d < dl; d++ ) { | |
| var depVar = variable.dependencies[ d ]; | |
| uniforms[ depVar.name ].value = depVar.renderTargets[ currentTextureIndex ].texture; | |
| } | |
| } | |
| // Performs the computation for this variable | |
| this.doRenderTarget( variable.material, variable.renderTargets[ nextTextureIndex ] ); | |
| } | |
| this.currentTextureIndex = nextTextureIndex; | |
| }; | |
| this.getCurrentRenderTarget = function( variable ) { | |
| return variable.renderTargets[ this.currentTextureIndex ]; | |
| }; | |
| this.getAlternateRenderTarget = function( variable ) { | |
| return variable.renderTargets[ this.currentTextureIndex === 0 ? 1 : 0 ]; | |
| }; | |
| function addResolutionDefine( materialShader ) { | |
| materialShader.defines.resolution = 'vec2( ' + sizeX.toFixed( 1 ) + ', ' + sizeY.toFixed( 1 ) + " )"; | |
| } | |
| this.addResolutionDefine = addResolutionDefine; | |
| // The following functions can be used to compute things manually | |
| function createShaderMaterial( computeFragmentShader, uniforms ) { | |
| uniforms = uniforms || {}; | |
| var material = new THREE.ShaderMaterial( { | |
| uniforms: uniforms, | |
| vertexShader: getPassThroughVertexShader(), | |
| fragmentShader: computeFragmentShader | |
| } ); | |
| addResolutionDefine( material ); | |
| return material; | |
| } | |
| this.createShaderMaterial = createShaderMaterial; | |
| this.createRenderTarget = function( sizeXTexture, sizeYTexture, wrapS, wrapT, minFilter, magFilter ) { | |
| sizeXTexture = sizeXTexture || sizeX; | |
| sizeYTexture = sizeYTexture || sizeY; | |
| wrapS = wrapS || THREE.ClampToEdgeWrapping; | |
| wrapT = wrapT || THREE.ClampToEdgeWrapping; | |
| minFilter = minFilter || THREE.NearestFilter; | |
| magFilter = magFilter || THREE.NearestFilter; | |
| var renderTarget = new THREE.WebGLRenderTarget( sizeXTexture, sizeYTexture, { | |
| wrapS: wrapS, | |
| wrapT: wrapT, | |
| minFilter: minFilter, | |
| magFilter: magFilter, | |
| format: THREE.RGBAFormat, | |
| type: ( /(iPad|iPhone|iPod)/g.test( navigator.userAgent ) ) ? THREE.HalfFloatType : THREE.FloatType, | |
| stencilBuffer: false | |
| } ); | |
| return renderTarget; | |
| }; | |
| this.createTexture = function( sizeXTexture, sizeYTexture ) { | |
| sizeXTexture = sizeXTexture || sizeX; | |
| sizeYTexture = sizeYTexture || sizeY; | |
| var a = new Float32Array( sizeXTexture * sizeYTexture * 4 ); | |
| var texture = new THREE.DataTexture( a, sizeX, sizeY, THREE.RGBAFormat, THREE.FloatType ); | |
| texture.needsUpdate = true; | |
| return texture; | |
| }; | |
| this.renderTexture = function( input, output ) { | |
| // Takes a texture, and render out in rendertarget | |
| // input = Texture | |
| // output = RenderTarget | |
| passThruUniforms.texture.value = input; | |
| this.doRenderTarget( passThruShader, output); | |
| passThruUniforms.texture.value = null; | |
| }; | |
| this.doRenderTarget = function( material, output ) { | |
| mesh.material = material; | |
| renderer.render( scene, camera, output ); | |
| mesh.material = passThruShader; | |
| }; | |
| // Shaders | |
| function getPassThroughVertexShader() { | |
| return "void main() {\n" + | |
| "\n" + | |
| " gl_Position = vec4( position, 1.0 );\n" + | |
| "\n" + | |
| "}\n"; | |
| } | |
| function getPassThroughFragmentShader() { | |
| return "uniform sampler2D texture;\n" + | |
| "\n" + | |
| "void main() {\n" + | |
| "\n" + | |
| " vec2 uv = gl_FragCoord.xy / resolution.xy;\n" + | |
| "\n" + | |
| " gl_FragColor = texture2D( texture, uv );\n" + | |
| "\n" + | |
| "}\n"; | |
| } | |
| } |
| <!DOCTYPE html> | |
| <html lang="en"> | |
| <head> | |
| <title>Faster GPU Sorting</title> | |
| <meta charset="utf-8"> | |
| <meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0"> | |
| <style> | |
| body { | |
| font-family: Monospace; | |
| background-color: #f0f0f0; | |
| margin: 0px; | |
| overflow: hidden; | |
| } | |
| #info { | |
| position: absolute; | |
| top: 0px; | |
| width: 100%; | |
| padding: 5px; | |
| font-family: Monospace; | |
| font-size: 13px; | |
| text-align:center; | |
| } | |
| </style> | |
| </head> | |
| <body> | |
| <script src="three.js"></script> | |
| <script src="GPUComputationRenderer.js"></script> | |
| <div class="container"> | |
| <main> | |
| <div class="content"> | |
| <h1>Faster Pixel Sorter</h1> | |
| (Odd-Even Merge Sort) | |
| </div> | |
| <div class="form-control"> | |
| <input type="file" accept="image/*" class="js-input-image"> | |
| <small>Tip: try uploading a <a href="https://unsplash.com/photos/sf9zGg5DnFw" target="_blank">picture of a landscape</a>.</small> | |
| </div> | |
| <div class="js-output-container" role="presentation"></div> | |
| </main> | |
| <footer class="footer content"> | |
| <p>Pixels are sorted by:</p> | |
| <ol> | |
| <li>Light intensity (Y in <a href="https://en.wikipedia.org/wiki/YIQ">YIQ</a> colour space)</li> | |
| <li>Colour intensity (Chroma)</li> | |
| </ol> | |
| </footer> | |
| </div> | |
| <style> | |
| $color-brand--primary: #08f; | |
| body { | |
| background-color: #f8f8f8; | |
| color: #222; | |
| line-height: 1.6; | |
| } | |
| :focus { | |
| outline: .125rem solid $color-brand--primary; | |
| } | |
| label { | |
| display: block; | |
| font-weight: bold; | |
| } | |
| input { | |
| display: block; | |
| } | |
| canvas { | |
| display: block; | |
| height: auto !important; | |
| max-width: 100%; | |
| image-rendering: pixelated; | |
| } | |
| a { | |
| color: darken($color-brand--primary, 10%); | |
| &:hover, | |
| &:focus { | |
| color: darken($color-brand--primary, 20%); | |
| } | |
| &:active { | |
| color: darken($color-brand--primary, 30%); | |
| } | |
| } | |
| .container { | |
| max-width: 36em; | |
| margin-left: auto; | |
| margin-right: auto; | |
| } | |
| .form-control { | |
| margin-bottom: 1em; | |
| } | |
| .content { | |
| margin-top: 1em; | |
| margin-bottom: 1em; | |
| } | |
| .content > * { | |
| margin-bottom: 0; | |
| } | |
| .content > :first-child { | |
| margin-top: 0; | |
| } | |
| .content > * + * { | |
| margin-top: 1em; | |
| } | |
| .footer { | |
| font-size: smaller; | |
| } | |
| </style> | |
| <script> | |
| const shaderSortFragment = ` | |
| const vec4 kRGBToYPrime = vec4(0.299, 0.587, 0.114, 0.0); | |
| const vec4 kRGBToI = vec4(0.596, -0.275, -0.321, 0.0); | |
| const vec4 kRGBToQ = vec4(0.212, -0.523, 0.311, 0.0); | |
| // Based on https://github.com/genekogan/Processing-Shader-Examples/blob/master/TextureShaders/data/hue.glsl | |
| vec4 getYIQC(vec4 color) { | |
| float YPrime = dot(color, kRGBToYPrime); | |
| float I = dot(color, kRGBToI); | |
| float Q = dot(color, kRGBToQ); | |
| float chroma = sqrt(I * I + Q * Q); | |
| return vec4(YPrime, I, Q, chroma); | |
| } | |
| // Compare colors by light intensity and color intensity | |
| bool compareColor(vec4 a, vec4 b) { | |
| // return (a.x + a.y + a.z) < (b.x + b.y + b.z); | |
| // return a.r > b.r; | |
| vec4 aYIQC = getYIQC(a); | |
| vec4 bYIQC = getYIQC(b); | |
| if (aYIQC.x > bYIQC.x) { | |
| return true; | |
| } | |
| if (aYIQC.x == bYIQC.x && aYIQC.w > bYIQC.w) { | |
| return true; | |
| } | |
| return false; | |
| } | |
| uniform float uPass; | |
| uniform float uStage2; | |
| uniform float uPassModStage; | |
| uniform float uStage2PmS1; | |
| void main() { | |
| vec2 coord = gl_FragCoord.xy; | |
| float i = coord.x; | |
| float j = floor(mod(i, uStage2)); | |
| float compare = | |
| (j < uPassModStage || j > uStage2PmS1) ? 0. : | |
| mod((j + uPassModStage) / uPass, 2.0) < 1.0 ? 1.0: | |
| -1.0; | |
| vec2 offset = vec2(compare * uPass, 0.0) / resolution.xy; | |
| vec2 uv = coord / resolution.xy; | |
| vec4 current = texture2D(uTexture, uv); | |
| vec4 reference = texture2D(uTexture, uv + offset); | |
| bool results = compareColor(current * compare, reference * compare); | |
| gl_FragColor = results ? current : reference; | |
| }`; | |
| const readFile = (file) => { | |
| const reader = new FileReader(); | |
| return new Promise((resolve, reject) => { | |
| reader.addEventListener('load', () => resolve(reader.result)); | |
| reader.readAsDataURL(file); | |
| }); | |
| }; | |
| const createPlane = () => { | |
| const fragmentShader = `uniform sampler2D uMap; | |
| varying vec2 vUv; | |
| void main() { | |
| gl_FragColor = texture2D(uMap, vUv); | |
| }`; | |
| const vertexShader = `varying vec2 vUv; | |
| void main() { | |
| vUv = uv; | |
| ${THREE.ShaderChunk.begin_vertex} | |
| ${THREE.ShaderChunk.project_vertex} | |
| }`; | |
| const geometry = new THREE.PlaneGeometry(2, 2, 1, 1); | |
| const material = new THREE.ShaderMaterial({ | |
| uniforms: { | |
| uMap: { value: null }, | |
| }, | |
| fragmentShader, | |
| vertexShader, | |
| }); | |
| return new THREE.Mesh(geometry, material); | |
| }; | |
| const input = document.querySelector('.js-input-image'); | |
| const outputContainer = document.querySelector('.js-output-container'); | |
| const gpuComputeTextureSize = 512; | |
| const plane = createPlane(); | |
| const camera = new THREE.Camera(); | |
| camera.position.z = 1; | |
| const renderer = new THREE.WebGLRenderer({ antialias: false }); | |
| renderer.setSize(gpuComputeTextureSize, gpuComputeTextureSize); | |
| const scene = new THREE.Scene(); | |
| scene.add(plane); | |
| let gpuCompute, | |
| textureSorted, | |
| variableSorted; | |
| const initGpuCompute = (initialTextureData = null, dispose = false) => { | |
| if (dispose) { | |
| variableSorted.renderTargets.forEach(rt => rt.dispose()); | |
| textureSorted.dispose(); | |
| } | |
| gpuCompute = new GPUComputationRenderer(gpuComputeTextureSize, gpuComputeTextureSize, renderer); | |
| textureSorted = gpuCompute.createTexture(); | |
| const rowColors = new Array(gpuComputeTextureSize).fill(0).map((n, i) => ({ | |
| r: (Math.sin(i) * .124 + 1) / 2, | |
| g: (Math.sin(i + .234) * .563 + 1) / 2, | |
| b: (Math.sin(i + .988) * .348 + 1) / 2, | |
| })); | |
| if (initialTextureData) { | |
| textureSorted.image.data.set(initialTextureData); | |
| } else { | |
| for (let i = 0, channels = 4; i < textureSorted.image.data.length; i += channels) { | |
| const pixelIndex = Math.floor(i / channels); | |
| const y = Math.floor(pixelIndex / gpuComputeTextureSize); | |
| const color = rowColors[(pixelIndex + y * 15838) % rowColors.length]; | |
| textureSorted.image.data[i + 0] = color.r; | |
| textureSorted.image.data[i + 1] = color.g; | |
| textureSorted.image.data[i + 2] = color.b; | |
| textureSorted.image.data[i + 3] = 1; | |
| } | |
| } | |
| variableSorted = gpuCompute.addVariable('uTexture', shaderSortFragment, textureSorted); | |
| gpuCompute.setVariableDependencies(variableSorted, [variableSorted]); | |
| const gpuComputeCompileError = gpuCompute.init(); | |
| uniforms = variableSorted.material.uniforms; | |
| uniforms.uStage2 = { value: 0 }; | |
| uniforms.uPassModStage = { value: 0 }; | |
| uniforms.uStage2PmS1 = { value: 0 }; | |
| uniforms.uPass = { value: 0 }; | |
| if (gpuComputeCompileError !== null) { | |
| console.error(gpuComputeCompileError); | |
| } | |
| }; | |
| const getImageData = (image, size = 1) => { | |
| const canvas = document.createElement('canvas'); | |
| canvas.height = size; | |
| canvas.width = size; | |
| const context = canvas.getContext('2d'); | |
| context.scale(1, -1); | |
| context.drawImage(image, 0, 0, size, -size); | |
| return context.getImageData(0, 0, size, size).data; | |
| }; | |
| initGpuCompute(); | |
| outputContainer.appendChild(renderer.domElement); | |
| input.addEventListener('change', () => { | |
| const file = input.files[0]; | |
| const image = new Image(); | |
| if (!file) return; | |
| readFile(file) | |
| .then(dataUrl => new Promise((resolve, reject) => { | |
| image.addEventListener('load', () => resolve()) | |
| image.src = dataUrl; | |
| })) | |
| .then(() => { | |
| const textureData = new Float32Array(getImageData(image, gpuComputeTextureSize)) | |
| .map(value => value / 256); | |
| restartCounter() | |
| initGpuCompute(textureData, true); | |
| }); | |
| }); | |
| let stage = 0; // stage iterations | |
| let pass = 0; | |
| function restartCounter() { | |
| stage = 0 | |
| pass = 0 | |
| } | |
| const render = () => { | |
| if (stage > Math.log2(512) + 1) { | |
| return; | |
| } | |
| gpuCompute.compute(); | |
| const texture = gpuCompute.getCurrentRenderTarget(variableSorted).texture; | |
| plane.material.uniforms.uMap.value = texture; | |
| var tPass = 1 << pass; | |
| var tStage = 1 << stage | |
| var uStage2 = tStage + tStage | |
| var uPassModStage = tPass % tStage | |
| var uStage2PmS1 = uStage2 - uPassModStage - 1; | |
| var uniforms = variableSorted.material.uniforms; | |
| uniforms.uStage2.value = uStage2; | |
| uniforms.uPassModStage.value = uPassModStage; | |
| uniforms.uStage2PmS1.value = uStage2PmS1; | |
| uniforms.uPass.value = tPass; | |
| console.log('stage', stage, pass); | |
| renderer.render(scene, camera); | |
| pass--; | |
| if (pass < 0) { | |
| stage++; | |
| pass = stage | |
| } | |
| }; | |
| // const animate = (callback) => { | |
| // const update = () => { | |
| // requestAnimationFrame(update); | |
| // callback(); | |
| // }; | |
| // update(); | |
| // }; | |
| // animate(render); | |
| setInterval(() => { | |
| render(); | |
| }, 50); | |
| </script> | |
| </body> | |
| </html> |
| (function (global, factory) { | |
| typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) : | |
| typeof define === 'function' && define.amd ? define(['exports'], factory) : | |
| (factory((global.THREE = {}))); | |
| }(this, (function (exports) { 'use strict'; | |
| // Polyfills | |
| if ( Number.EPSILON === undefined ) { | |
| Number.EPSILON = Math.pow( 2, - 52 ); | |
| } | |
| if ( Number.isInteger === undefined ) { | |
| // Missing in IE | |
| // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number/isInteger | |
| Number.isInteger = function ( value ) { | |
| return typeof value === 'number' && isFinite( value ) && Math.floor( value ) === value; | |
| }; | |
| } | |
| // | |
| if ( Math.sign === undefined ) { | |
| // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/sign | |
| Math.sign = function ( x ) { | |
| return ( x < 0 ) ? - 1 : ( x > 0 ) ? 1 : + x; | |
| }; | |
| } | |
| if ( 'name' in Function.prototype === false ) { | |
| // Missing in IE | |
| // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/name | |
| Object.defineProperty( Function.prototype, 'name', { | |
| get: function () { | |
| return this.toString().match( /^\s*function\s*([^\(\s]*)/ )[ 1 ]; | |
| } | |
| } ); | |
| } | |
| if ( Object.assign === undefined ) { | |
| // Missing in IE | |
| // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/assign | |
| ( function () { | |
| Object.assign = function ( target ) { | |
| if ( target === undefined || target === null ) { | |
| throw new TypeError( 'Cannot convert undefined or null to object' ); | |
| } | |
| var output = Object( target ); | |
| for ( var index = 1; index < arguments.length; index ++ ) { | |
| var source = arguments[ index ]; | |
| if ( source !== undefined && source !== null ) { | |
| for ( var nextKey in source ) { | |
| if ( Object.prototype.hasOwnProperty.call( source, nextKey ) ) { | |
| output[ nextKey ] = source[ nextKey ]; | |
| } | |
| } | |
| } | |
| } | |
| return output; | |
| }; | |
| } )(); | |
| } | |
| /** | |
| * https://github.com/mrdoob/eventdispatcher.js/ | |
| */ | |
| function EventDispatcher() {} | |
| Object.assign( EventDispatcher.prototype, { | |
| addEventListener: function ( type, listener ) { | |
| if ( this._listeners === undefined ) this._listeners = {}; | |
| var listeners = this._listeners; | |
| if ( listeners[ type ] === undefined ) { | |
| listeners[ type ] = []; | |
| } | |
| if ( listeners[ type ].indexOf( listener ) === - 1 ) { | |
| listeners[ type ].push( listener ); | |
| } | |
| }, | |
| hasEventListener: function ( type, listener ) { | |
| if ( this._listeners === undefined ) return false; | |
| var listeners = this._listeners; | |
| return listeners[ type ] !== undefined && listeners[ type ].indexOf( listener ) !== - 1; | |
| }, | |
| removeEventListener: function ( type, listener ) { | |
| if ( this._listeners === undefined ) return; | |
| var listeners = this._listeners; | |
| var listenerArray = listeners[ type ]; | |
| if ( listenerArray !== undefined ) { | |
| var index = listenerArray.indexOf( listener ); | |
| if ( index !== - 1 ) { | |
| listenerArray.splice( index, 1 ); | |
| } | |
| } | |
| }, | |
| dispatchEvent: function ( event ) { | |
| if ( this._listeners === undefined ) return; | |
| var listeners = this._listeners; | |
| var listenerArray = listeners[ event.type ]; | |
| if ( listenerArray !== undefined ) { | |
| event.target = this; | |
| var array = listenerArray.slice( 0 ); | |
| for ( var i = 0, l = array.length; i < l; i ++ ) { | |
| array[ i ].call( this, event ); | |
| } | |
| } | |
| } | |
| } ); | |
| var REVISION = '91dev'; | |
| var MOUSE = { LEFT: 0, MIDDLE: 1, RIGHT: 2 }; | |
| var CullFaceNone = 0; | |
| var CullFaceBack = 1; | |
| var CullFaceFront = 2; | |
| var CullFaceFrontBack = 3; | |
| var FrontFaceDirectionCW = 0; | |
| var FrontFaceDirectionCCW = 1; | |
| var BasicShadowMap = 0; | |
| var PCFShadowMap = 1; | |
| var PCFSoftShadowMap = 2; | |
| var FrontSide = 0; | |
| var BackSide = 1; | |
| var DoubleSide = 2; | |
| var FlatShading = 1; | |
| var SmoothShading = 2; | |
| var NoColors = 0; | |
| var FaceColors = 1; | |
| var VertexColors = 2; | |
| var NoBlending = 0; | |
| var NormalBlending = 1; | |
| var AdditiveBlending = 2; | |
| var SubtractiveBlending = 3; | |
| var MultiplyBlending = 4; | |
| var CustomBlending = 5; | |
| var AddEquation = 100; | |
| var SubtractEquation = 101; | |
| var ReverseSubtractEquation = 102; | |
| var MinEquation = 103; | |
| var MaxEquation = 104; | |
| var ZeroFactor = 200; | |
| var OneFactor = 201; | |
| var SrcColorFactor = 202; | |
| var OneMinusSrcColorFactor = 203; | |
| var SrcAlphaFactor = 204; | |
| var OneMinusSrcAlphaFactor = 205; | |
| var DstAlphaFactor = 206; | |
| var OneMinusDstAlphaFactor = 207; | |
| var DstColorFactor = 208; | |
| var OneMinusDstColorFactor = 209; | |
| var SrcAlphaSaturateFactor = 210; | |
| var NeverDepth = 0; | |
| var AlwaysDepth = 1; | |
| var LessDepth = 2; | |
| var LessEqualDepth = 3; | |
| var EqualDepth = 4; | |
| var GreaterEqualDepth = 5; | |
| var GreaterDepth = 6; | |
| var NotEqualDepth = 7; | |
| var MultiplyOperation = 0; | |
| var MixOperation = 1; | |
| var AddOperation = 2; | |
| var NoToneMapping = 0; | |
| var LinearToneMapping = 1; | |
| var ReinhardToneMapping = 2; | |
| var Uncharted2ToneMapping = 3; | |
| var CineonToneMapping = 4; | |
| var UVMapping = 300; | |
| var CubeReflectionMapping = 301; | |
| var CubeRefractionMapping = 302; | |
| var EquirectangularReflectionMapping = 303; | |
| var EquirectangularRefractionMapping = 304; | |
| var SphericalReflectionMapping = 305; | |
| var CubeUVReflectionMapping = 306; | |
| var CubeUVRefractionMapping = 307; | |
| var RepeatWrapping = 1000; | |
| var ClampToEdgeWrapping = 1001; | |
| var MirroredRepeatWrapping = 1002; | |
| var NearestFilter = 1003; | |
| var NearestMipMapNearestFilter = 1004; | |
| var NearestMipMapLinearFilter = 1005; | |
| var LinearFilter = 1006; | |
| var LinearMipMapNearestFilter = 1007; | |
| var LinearMipMapLinearFilter = 1008; | |
| var UnsignedByteType = 1009; | |
| var ByteType = 1010; | |
| var ShortType = 1011; | |
| var UnsignedShortType = 1012; | |
| var IntType = 1013; | |
| var UnsignedIntType = 1014; | |
| var FloatType = 1015; | |
| var HalfFloatType = 1016; | |
| var UnsignedShort4444Type = 1017; | |
| var UnsignedShort5551Type = 1018; | |
| var UnsignedShort565Type = 1019; | |
| var UnsignedInt248Type = 1020; | |
| var AlphaFormat = 1021; | |
| var RGBFormat = 1022; | |
| var RGBAFormat = 1023; | |
| var LuminanceFormat = 1024; | |
| var LuminanceAlphaFormat = 1025; | |
| var RGBEFormat = RGBAFormat; | |
| var DepthFormat = 1026; | |
| var DepthStencilFormat = 1027; | |
| var RGB_S3TC_DXT1_Format = 33776; | |
| var RGBA_S3TC_DXT1_Format = 33777; | |
| var RGBA_S3TC_DXT3_Format = 33778; | |
| var RGBA_S3TC_DXT5_Format = 33779; | |
| var RGB_PVRTC_4BPPV1_Format = 35840; | |
| var RGB_PVRTC_2BPPV1_Format = 35841; | |
| var RGBA_PVRTC_4BPPV1_Format = 35842; | |
| var RGBA_PVRTC_2BPPV1_Format = 35843; | |
| var RGB_ETC1_Format = 36196; | |
| var RGBA_ASTC_4x4_Format = 37808; | |
| var RGBA_ASTC_5x4_Format = 37809; | |
| var RGBA_ASTC_5x5_Format = 37810; | |
| var RGBA_ASTC_6x5_Format = 37811; | |
| var RGBA_ASTC_6x6_Format = 37812; | |
| var RGBA_ASTC_8x5_Format = 37813; | |
| var RGBA_ASTC_8x6_Format = 37814; | |
| var RGBA_ASTC_8x8_Format = 37815; | |
| var RGBA_ASTC_10x5_Format = 37816; | |
| var RGBA_ASTC_10x6_Format = 37817; | |
| var RGBA_ASTC_10x8_Format = 37818; | |
| var RGBA_ASTC_10x10_Format = 37819; | |
| var RGBA_ASTC_12x10_Format = 37820; | |
| var RGBA_ASTC_12x12_Format = 37821; | |
| var LoopOnce = 2200; | |
| var LoopRepeat = 2201; | |
| var LoopPingPong = 2202; | |
| var InterpolateDiscrete = 2300; | |
| var InterpolateLinear = 2301; | |
| var InterpolateSmooth = 2302; | |
| var ZeroCurvatureEnding = 2400; | |
| var ZeroSlopeEnding = 2401; | |
| var WrapAroundEnding = 2402; | |
| var TrianglesDrawMode = 0; | |
| var TriangleStripDrawMode = 1; | |
| var TriangleFanDrawMode = 2; | |
| var LinearEncoding = 3000; | |
| var sRGBEncoding = 3001; | |
| var GammaEncoding = 3007; | |
| var RGBEEncoding = 3002; | |
| var LogLuvEncoding = 3003; | |
| var RGBM7Encoding = 3004; | |
| var RGBM16Encoding = 3005; | |
| var RGBDEncoding = 3006; | |
| var BasicDepthPacking = 3200; | |
| var RGBADepthPacking = 3201; | |
| /** | |
| * @author alteredq / http://alteredqualia.com/ | |
| * @author mrdoob / http://mrdoob.com/ | |
| */ | |
| var _Math = { | |
| DEG2RAD: Math.PI / 180, | |
| RAD2DEG: 180 / Math.PI, | |
| generateUUID: ( function () { | |
| // http://stackoverflow.com/questions/105034/how-to-create-a-guid-uuid-in-javascript/21963136#21963136 | |
| var lut = []; | |
| for ( var i = 0; i < 256; i ++ ) { | |
| lut[ i ] = ( i < 16 ? '0' : '' ) + ( i ).toString( 16 ).toUpperCase(); | |
| } | |
| return function generateUUID() { | |
| var d0 = Math.random() * 0xffffffff | 0; | |
| var d1 = Math.random() * 0xffffffff | 0; | |
| var d2 = Math.random() * 0xffffffff | 0; | |
| var d3 = Math.random() * 0xffffffff | 0; | |
| return lut[ d0 & 0xff ] + lut[ d0 >> 8 & 0xff ] + lut[ d0 >> 16 & 0xff ] + lut[ d0 >> 24 & 0xff ] + '-' + | |
| lut[ d1 & 0xff ] + lut[ d1 >> 8 & 0xff ] + '-' + lut[ d1 >> 16 & 0x0f | 0x40 ] + lut[ d1 >> 24 & 0xff ] + '-' + | |
| lut[ d2 & 0x3f | 0x80 ] + lut[ d2 >> 8 & 0xff ] + '-' + lut[ d2 >> 16 & 0xff ] + lut[ d2 >> 24 & 0xff ] + | |
| lut[ d3 & 0xff ] + lut[ d3 >> 8 & 0xff ] + lut[ d3 >> 16 & 0xff ] + lut[ d3 >> 24 & 0xff ]; | |
| }; | |
| } )(), | |
| clamp: function ( value, min, max ) { | |
| return Math.max( min, Math.min( max, value ) ); | |
| }, | |
| // compute euclidian modulo of m % n | |
| // https://en.wikipedia.org/wiki/Modulo_operation | |
| euclideanModulo: function ( n, m ) { | |
| return ( ( n % m ) + m ) % m; | |
| }, | |
| // Linear mapping from range <a1, a2> to range <b1, b2> | |
| mapLinear: function ( x, a1, a2, b1, b2 ) { | |
| return b1 + ( x - a1 ) * ( b2 - b1 ) / ( a2 - a1 ); | |
| }, | |
| // https://en.wikipedia.org/wiki/Linear_interpolation | |
| lerp: function ( x, y, t ) { | |
| return ( 1 - t ) * x + t * y; | |
| }, | |
| // http://en.wikipedia.org/wiki/Smoothstep | |
| smoothstep: function ( x, min, max ) { | |
| if ( x <= min ) return 0; | |
| if ( x >= max ) return 1; | |
| x = ( x - min ) / ( max - min ); | |
| return x * x * ( 3 - 2 * x ); | |
| }, | |
| smootherstep: function ( x, min, max ) { | |
| if ( x <= min ) return 0; | |
| if ( x >= max ) return 1; | |
| x = ( x - min ) / ( max - min ); | |
| return x * x * x * ( x * ( x * 6 - 15 ) + 10 ); | |
| }, | |
| // Random integer from <low, high> interval | |
| randInt: function ( low, high ) { | |
| return low + Math.floor( Math.random() * ( high - low + 1 ) ); | |
| }, | |
| // Random float from <low, high> interval | |
| randFloat: function ( low, high ) { | |
| return low + Math.random() * ( high - low ); | |
| }, | |
| // Random float from <-range/2, range/2> interval | |
| randFloatSpread: function ( range ) { | |
| return range * ( 0.5 - Math.random() ); | |
| }, | |
| degToRad: function ( degrees ) { | |
| return degrees * _Math.DEG2RAD; | |
| }, | |
| radToDeg: function ( radians ) { | |
| return radians * _Math.RAD2DEG; | |
| }, | |
| isPowerOfTwo: function ( value ) { | |
| return ( value & ( value - 1 ) ) === 0 && value !== 0; | |
| }, | |
| ceilPowerOfTwo: function ( value ) { | |
| return Math.pow( 2, Math.ceil( Math.log( value ) / Math.LN2 ) ); | |
| }, | |
| floorPowerOfTwo: function ( value ) { | |
| return Math.pow( 2, Math.floor( Math.log( value ) / Math.LN2 ) ); | |
| } | |
| }; | |
| /** | |
| * @author mrdoob / http://mrdoob.com/ | |
| * @author philogb / http://blog.thejit.org/ | |
| * @author egraether / http://egraether.com/ | |
| * @author zz85 / http://www.lab4games.net/zz85/blog | |
| */ | |
| function Vector2( x, y ) { | |
| this.x = x || 0; | |
| this.y = y || 0; | |
| } | |
| Object.defineProperties( Vector2.prototype, { | |
| "width": { | |
| get: function () { | |
| return this.x; | |
| }, | |
| set: function ( value ) { | |
| this.x = value; | |
| } | |
| }, | |
| "height": { | |
| get: function () { | |
| return this.y; | |
| }, | |
| set: function ( value ) { | |
| this.y = value; | |
| } | |
| } | |
| } ); | |
| Object.assign( Vector2.prototype, { | |
| isVector2: true, | |
| set: function ( x, y ) { | |
| this.x = x; | |
| this.y = y; | |
| return this; | |
| }, | |
| setScalar: function ( scalar ) { | |
| this.x = scalar; | |
| this.y = scalar; | |
| return this; | |
| }, | |
| setX: function ( x ) { | |
| this.x = x; | |
| return this; | |
| }, | |
| setY: function ( y ) { | |
| this.y = y; | |
| return this; | |
| }, | |
| setComponent: function ( index, value ) { | |
| switch ( index ) { | |
| case 0: this.x = value; break; | |
| case 1: this.y = value; break; | |
| default: throw new Error( 'index is out of range: ' + index ); | |
| } | |
| return this; | |
| }, | |
| getComponent: function ( index ) { | |
| switch ( index ) { | |
| case 0: return this.x; | |
| case 1: return this.y; | |
| default: throw new Error( 'index is out of range: ' + index ); | |
| } | |
| }, | |
| clone: function () { | |
| return new this.constructor( this.x, this.y ); | |
| }, | |
| copy: function ( v ) { | |
| this.x = v.x; | |
| this.y = v.y; | |
| return this; | |
| }, | |
| add: function ( v, w ) { | |
| if ( w !== undefined ) { | |
| console.warn( 'THREE.Vector2: .add() now only accepts one argument. Use .addVectors( a, b ) instead.' ); | |
| return this.addVectors( v, w ); | |
| } | |
| this.x += v.x; | |
| this.y += v.y; | |
| return this; | |
| }, | |
| addScalar: function ( s ) { | |
| this.x += s; | |
| this.y += s; | |
| return this; | |
| }, | |
| addVectors: function ( a, b ) { | |
| this.x = a.x + b.x; | |
| this.y = a.y + b.y; | |
| return this; | |
| }, | |
| addScaledVector: function ( v, s ) { | |
| this.x += v.x * s; | |
| this.y += v.y * s; | |
| return this; | |
| }, | |
| sub: function ( v, w ) { | |
| if ( w !== undefined ) { | |
| console.warn( 'THREE.Vector2: .sub() now only accepts one argument. Use .subVectors( a, b ) instead.' ); | |
| return this.subVectors( v, w ); | |
| } | |
| this.x -= v.x; | |
| this.y -= v.y; | |
| return this; | |
| }, | |
| subScalar: function ( s ) { | |
| this.x -= s; | |
| this.y -= s; | |
| return this; | |
| }, | |
| subVectors: function ( a, b ) { | |
| this.x = a.x - b.x; | |
| this.y = a.y - b.y; | |
| return this; | |
| }, | |
| multiply: function ( v ) { | |
| this.x *= v.x; | |
| this.y *= v.y; | |
| return this; | |
| }, | |
| multiplyScalar: function ( scalar ) { | |
| this.x *= scalar; | |
| this.y *= scalar; | |
| return this; | |
| }, | |
| divide: function ( v ) { | |
| this.x /= v.x; | |
| this.y /= v.y; | |
| return this; | |
| }, | |
| divideScalar: function ( scalar ) { | |
| return this.multiplyScalar( 1 / scalar ); | |
| }, | |
| applyMatrix3: function ( m ) { | |
| var x = this.x, y = this.y; | |
| var e = m.elements; | |
| this.x = e[ 0 ] * x + e[ 3 ] * y + e[ 6 ]; | |
| this.y = e[ 1 ] * x + e[ 4 ] * y + e[ 7 ]; | |
| return this; | |
| }, | |
| min: function ( v ) { | |
| this.x = Math.min( this.x, v.x ); | |
| this.y = Math.min( this.y, v.y ); | |
| return this; | |
| }, | |
| max: function ( v ) { | |
| this.x = Math.max( this.x, v.x ); | |
| this.y = Math.max( this.y, v.y ); | |
| return this; | |
| }, | |
| clamp: function ( min, max ) { | |
| // assumes min < max, componentwise | |
| this.x = Math.max( min.x, Math.min( max.x, this.x ) ); | |
| this.y = Math.max( min.y, Math.min( max.y, this.y ) ); | |
| return this; | |
| }, | |
| clampScalar: function () { | |
| var min = new Vector2(); | |
| var max = new Vector2(); | |
| return function clampScalar( minVal, maxVal ) { | |
| min.set( minVal, minVal ); | |
| max.set( maxVal, maxVal ); | |
| return this.clamp( min, max ); | |
| }; | |
| }(), | |
| clampLength: function ( min, max ) { | |
| var length = this.length(); | |
| return this.divideScalar( length || 1 ).multiplyScalar( Math.max( min, Math.min( max, length ) ) ); | |
| }, | |
| floor: function () { | |
| this.x = Math.floor( this.x ); | |
| this.y = Math.floor( this.y ); | |
| return this; | |
| }, | |
| ceil: function () { | |
| this.x = Math.ceil( this.x ); | |
| this.y = Math.ceil( this.y ); | |
| return this; | |
| }, | |
| round: function () { | |
| this.x = Math.round( this.x ); | |
| this.y = Math.round( this.y ); | |
| return this; | |
| }, | |
| roundToZero: function () { | |
| this.x = ( this.x < 0 ) ? Math.ceil( this.x ) : Math.floor( this.x ); | |
| this.y = ( this.y < 0 ) ? Math.ceil( this.y ) : Math.floor( this.y ); | |
| return this; | |
| }, | |
| negate: function () { | |
| this.x = - this.x; | |
| this.y = - this.y; | |
| return this; | |
| }, | |
| dot: function ( v ) { | |
| return this.x * v.x + this.y * v.y; | |
| }, | |
| lengthSq: function () { | |
| return this.x * this.x + this.y * this.y; | |
| }, | |
| length: function () { | |
| return Math.sqrt( this.x * this.x + this.y * this.y ); | |
| }, | |
| manhattanLength: function () { | |
| return Math.abs( this.x ) + Math.abs( this.y ); | |
| }, | |
| normalize: function () { | |
| return this.divideScalar( this.length() || 1 ); | |
| }, | |
| angle: function () { | |
| // computes the angle in radians with respect to the positive x-axis | |
| var angle = Math.atan2( this.y, this.x ); | |
| if ( angle < 0 ) angle += 2 * Math.PI; | |
| return angle; | |
| }, | |
| distanceTo: function ( v ) { | |
| return Math.sqrt( this.distanceToSquared( v ) ); | |
| }, | |
| distanceToSquared: function ( v ) { | |
| var dx = this.x - v.x, dy = this.y - v.y; | |
| return dx * dx + dy * dy; | |
| }, | |
| manhattanDistanceTo: function ( v ) { | |
| return Math.abs( this.x - v.x ) + Math.abs( this.y - v.y ); | |
| }, | |
| setLength: function ( length ) { | |
| return this.normalize().multiplyScalar( length ); | |
| }, | |
| lerp: function ( v, alpha ) { | |
| this.x += ( v.x - this.x ) * alpha; | |
| this.y += ( v.y - this.y ) * alpha; | |
| return this; | |
| }, | |
| lerpVectors: function ( v1, v2, alpha ) { | |
| return this.subVectors( v2, v1 ).multiplyScalar( alpha ).add( v1 ); | |
| }, | |
| equals: function ( v ) { | |
| return ( ( v.x === this.x ) && ( v.y === this.y ) ); | |
| }, | |
| fromArray: function ( array, offset ) { | |
| if ( offset === undefined ) offset = 0; | |
| this.x = array[ offset ]; | |
| this.y = array[ offset + 1 ]; | |
| return this; | |
| }, | |
| toArray: function ( array, offset ) { | |
| if ( array === undefined ) array = []; | |
| if ( offset === undefined ) offset = 0; | |
| array[ offset ] = this.x; | |
| array[ offset + 1 ] = this.y; | |
| return array; | |
| }, | |
| fromBufferAttribute: function ( attribute, index, offset ) { | |
| if ( offset !== undefined ) { | |
| console.warn( 'THREE.Vector2: offset has been removed from .fromBufferAttribute().' ); | |
| } | |
| this.x = attribute.getX( index ); | |
| this.y = attribute.getY( index ); | |
| return this; | |
| }, | |
| rotateAround: function ( center, angle ) { | |
| var c = Math.cos( angle ), s = Math.sin( angle ); | |
| var x = this.x - center.x; | |
| var y = this.y - center.y; | |
| this.x = x * c - y * s + center.x; | |
| this.y = x * s + y * c + center.y; | |
| return this; | |
| } | |
| } ); | |
| /** | |
| * @author mrdoob / http://mrdoob.com/ | |
| * @author supereggbert / http://www.paulbrunt.co.uk/ | |
| * @author philogb / http://blog.thejit.org/ | |
| * @author jordi_ros / http://plattsoft.com | |
| * @author D1plo1d / http://github.com/D1plo1d | |
| * @author alteredq / http://alteredqualia.com/ | |
| * @author mikael emtinger / http://gomo.se/ | |
| * @author timknip / http://www.floorplanner.com/ | |
| * @author bhouston / http://clara.io | |
| * @author WestLangley / http://github.com/WestLangley | |
| */ | |
| function Matrix4() { | |
| this.elements = [ | |
| 1, 0, 0, 0, | |
| 0, 1, 0, 0, | |
| 0, 0, 1, 0, | |
| 0, 0, 0, 1 | |
| ]; | |
| if ( arguments.length > 0 ) { | |
| console.error( 'THREE.Matrix4: the constructor no longer reads arguments. use .set() instead.' ); | |
| } | |
| } | |
| Object.assign( Matrix4.prototype, { | |
| isMatrix4: true, | |
| set: function ( n11, n12, n13, n14, n21, n22, n23, n24, n31, n32, n33, n34, n41, n42, n43, n44 ) { | |
| var te = this.elements; | |
| te[ 0 ] = n11; te[ 4 ] = n12; te[ 8 ] = n13; te[ 12 ] = n14; | |
| te[ 1 ] = n21; te[ 5 ] = n22; te[ 9 ] = n23; te[ 13 ] = n24; | |
| te[ 2 ] = n31; te[ 6 ] = n32; te[ 10 ] = n33; te[ 14 ] = n34; | |
| te[ 3 ] = n41; te[ 7 ] = n42; te[ 11 ] = n43; te[ 15 ] = n44; | |
| return this; | |
| }, | |
| identity: function () { | |
| this.set( | |
| 1, 0, 0, 0, | |
| 0, 1, 0, 0, | |
| 0, 0, 1, 0, | |
| 0, 0, 0, 1 | |
| ); | |
| return this; | |
| }, | |
| clone: function () { | |
| return new Matrix4().fromArray( this.elements ); | |
| }, | |
| copy: function ( m ) { | |
| var te = this.elements; | |
| var me = m.elements; | |
| te[ 0 ] = me[ 0 ]; te[ 1 ] = me[ 1 ]; te[ 2 ] = me[ 2 ]; te[ 3 ] = me[ 3 ]; | |
| te[ 4 ] = me[ 4 ]; te[ 5 ] = me[ 5 ]; te[ 6 ] = me[ 6 ]; te[ 7 ] = me[ 7 ]; | |
| te[ 8 ] = me[ 8 ]; te[ 9 ] = me[ 9 ]; te[ 10 ] = me[ 10 ]; te[ 11 ] = me[ 11 ]; | |
| te[ 12 ] = me[ 12 ]; te[ 13 ] = me[ 13 ]; te[ 14 ] = me[ 14 ]; te[ 15 ] = me[ 15 ]; | |
| return this; | |
| }, | |
| copyPosition: function ( m ) { | |
| var te = this.elements, me = m.elements; | |
| te[ 12 ] = me[ 12 ]; | |
| te[ 13 ] = me[ 13 ]; | |
| te[ 14 ] = me[ 14 ]; | |
| return this; | |
| }, | |
| extractBasis: function ( xAxis, yAxis, zAxis ) { | |
| xAxis.setFromMatrixColumn( this, 0 ); | |
| yAxis.setFromMatrixColumn( this, 1 ); | |
| zAxis.setFromMatrixColumn( this, 2 ); | |
| return this; | |
| }, | |
| makeBasis: function ( xAxis, yAxis, zAxis ) { | |
| this.set( | |
| xAxis.x, yAxis.x, zAxis.x, 0, | |
| xAxis.y, yAxis.y, zAxis.y, 0, | |
| xAxis.z, yAxis.z, zAxis.z, 0, | |
| 0, 0, 0, 1 | |
| ); | |
| return this; | |
| }, | |
| extractRotation: function () { | |
| var v1 = new Vector3(); | |
| return function extractRotation( m ) { | |
| var te = this.elements; | |
| var me = m.elements; | |
| var scaleX = 1 / v1.setFromMatrixColumn( m, 0 ).length(); | |
| var scaleY = 1 / v1.setFromMatrixColumn( m, 1 ).length(); | |
| var scaleZ = 1 / v1.setFromMatrixColumn( m, 2 ).length(); | |
| te[ 0 ] = me[ 0 ] * scaleX; | |
| te[ 1 ] = me[ 1 ] * scaleX; | |
| te[ 2 ] = me[ 2 ] * scaleX; | |
| te[ 4 ] = me[ 4 ] * scaleY; | |
| te[ 5 ] = me[ 5 ] * scaleY; | |
| te[ 6 ] = me[ 6 ] * scaleY; | |
| te[ 8 ] = me[ 8 ] * scaleZ; | |
| te[ 9 ] = me[ 9 ] * scaleZ; | |
| te[ 10 ] = me[ 10 ] * scaleZ; | |
| return this; | |
| }; | |
| }(), | |
| makeRotationFromEuler: function ( euler ) { | |
| if ( ! ( euler && euler.isEuler ) ) { | |
| console.error( 'THREE.Matrix4: .makeRotationFromEuler() now expects a Euler rotation rather than a Vector3 and order.' ); | |
| } | |
| var te = this.elements; | |
| var x = euler.x, y = euler.y, z = euler.z; | |
| var a = Math.cos( x ), b = Math.sin( x ); | |
| var c = Math.cos( y ), d = Math.sin( y ); | |
| var e = Math.cos( z ), f = Math.sin( z ); | |
| if ( euler.order === 'XYZ' ) { | |
| var ae = a * e, af = a * f, be = b * e, bf = b * f; | |
| te[ 0 ] = c * e; | |
| te[ 4 ] = - c * f; | |
| te[ 8 ] = d; | |
| te[ 1 ] = af + be * d; | |
| te[ 5 ] = ae - bf * d; | |
| te[ 9 ] = - b * c; | |
| te[ 2 ] = bf - ae * d; | |
| te[ 6 ] = be + af * d; | |
| te[ 10 ] = a * c; | |
| } else if ( euler.order === 'YXZ' ) { | |
| var ce = c * e, cf = c * f, de = d * e, df = d * f; | |
| te[ 0 ] = ce + df * b; | |
| te[ 4 ] = de * b - cf; | |
| te[ 8 ] = a * d; | |
| te[ 1 ] = a * f; | |
| te[ 5 ] = a * e; | |
| te[ 9 ] = - b; | |
| te[ 2 ] = cf * b - de; | |
| te[ 6 ] = df + ce * b; | |
| te[ 10 ] = a * c; | |
| } else if ( euler.order === 'ZXY' ) { | |
| var ce = c * e, cf = c * f, de = d * e, df = d * f; | |
| te[ 0 ] = ce - df * b; | |
| te[ 4 ] = - a * f; | |
| te[ 8 ] = de + cf * b; | |
| te[ 1 ] = cf + de * b; | |
| te[ 5 ] = a * e; | |
| te[ 9 ] = df - ce * b; | |
| te[ 2 ] = - a * d; | |
| te[ 6 ] = b; | |
| te[ 10 ] = a * c; | |
| } else if ( euler.order === 'ZYX' ) { | |
| var ae = a * e, af = a * f, be = b * e, bf = b * f; | |
| te[ 0 ] = c * e; | |
| te[ 4 ] = be * d - af; | |
| te[ 8 ] = ae * d + bf; | |
| te[ 1 ] = c * f; | |
| te[ 5 ] = bf * d + ae; | |
| te[ 9 ] = af * d - be; | |
| te[ 2 ] = - d; | |
| te[ 6 ] = b * c; | |
| te[ 10 ] = a * c; | |
| } else if ( euler.order === 'YZX' ) { | |
| var ac = a * c, ad = a * d, bc = b * c, bd = b * d; | |
| te[ 0 ] = c * e; | |
| te[ 4 ] = bd - ac * f; | |
| te[ 8 ] = bc * f + ad; | |
| te[ 1 ] = f; | |
| te[ 5 ] = a * e; | |
| te[ 9 ] = - b * e; | |
| te[ 2 ] = - d * e; | |
| te[ 6 ] = ad * f + bc; | |
| te[ 10 ] = ac - bd * f; | |
| } else if ( euler.order === 'XZY' ) { | |
| var ac = a * c, ad = a * d, bc = b * c, bd = b * d; | |
| te[ 0 ] = c * e; | |
| te[ 4 ] = - f; | |
| te[ 8 ] = d * e; | |
| te[ 1 ] = ac * f + bd; | |
| te[ 5 ] = a * e; | |
| te[ 9 ] = ad * f - bc; | |
| te[ 2 ] = bc * f - ad; | |
| te[ 6 ] = b * e; | |
| te[ 10 ] = bd * f + ac; | |
| } | |
| // last column | |
| te[ 3 ] = 0; | |
| te[ 7 ] = 0; | |
| te[ 11 ] = 0; | |
| // bottom row | |
| te[ 12 ] = 0; | |
| te[ 13 ] = 0; | |
| te[ 14 ] = 0; | |
| te[ 15 ] = 1; | |
| return this; | |
| }, | |
| makeRotationFromQuaternion: function ( q ) { | |
| var te = this.elements; | |
| var x = q._x, y = q._y, z = q._z, w = q._w; | |
| var x2 = x + x, y2 = y + y, z2 = z + z; | |
| var xx = x * x2, xy = x * y2, xz = x * z2; | |
| var yy = y * y2, yz = y * z2, zz = z * z2; | |
| var wx = w * x2, wy = w * y2, wz = w * z2; | |
| te[ 0 ] = 1 - ( yy + zz ); | |
| te[ 4 ] = xy - wz; | |
| te[ 8 ] = xz + wy; | |
| te[ 1 ] = xy + wz; | |
| te[ 5 ] = 1 - ( xx + zz ); | |
| te[ 9 ] = yz - wx; | |
| te[ 2 ] = xz - wy; | |
| te[ 6 ] = yz + wx; | |
| te[ 10 ] = 1 - ( xx + yy ); | |
| // last column | |
| te[ 3 ] = 0; | |
| te[ 7 ] = 0; | |
| te[ 11 ] = 0; | |
| // bottom row | |
| te[ 12 ] = 0; | |
| te[ 13 ] = 0; | |
| te[ 14 ] = 0; | |
| te[ 15 ] = 1; | |
| return this; | |
| }, | |
| lookAt: function () { | |
| var x = new Vector3(); | |
| var y = new Vector3(); | |
| var z = new Vector3(); | |
| return function lookAt( eye, target, up ) { | |
| var te = this.elements; | |
| z.subVectors( eye, target ); | |
| if ( z.lengthSq() === 0 ) { | |
| // eye and target are in the same position | |
| z.z = 1; | |
| } | |
| z.normalize(); | |
| x.crossVectors( up, z ); | |
| if ( x.lengthSq() === 0 ) { | |
| // up and z are parallel | |
| if ( Math.abs( up.z ) === 1 ) { | |
| z.x += 0.0001; | |
| } else { | |
| z.z += 0.0001; | |
| } | |
| z.normalize(); | |
| x.crossVectors( up, z ); | |
| } | |
| x.normalize(); | |
| y.crossVectors( z, x ); | |
| te[ 0 ] = x.x; te[ 4 ] = y.x; te[ 8 ] = z.x; | |
| te[ 1 ] = x.y; te[ 5 ] = y.y; te[ 9 ] = z.y; | |
| te[ 2 ] = x.z; te[ 6 ] = y.z; te[ 10 ] = z.z; | |
| return this; | |
| }; | |
| }(), | |
| multiply: function ( m, n ) { | |
| if ( n !== undefined ) { | |
| console.warn( 'THREE.Matrix4: .multiply() now only accepts one argument. Use .multiplyMatrices( a, b ) instead.' ); | |
| return this.multiplyMatrices( m, n ); | |
| } | |
| return this.multiplyMatrices( this, m ); | |
| }, | |
| premultiply: function ( m ) { | |
| return this.multiplyMatrices( m, this ); | |
| }, | |
| multiplyMatrices: function ( a, b ) { | |
| var ae = a.elements; | |
| var be = b.elements; | |
| var te = this.elements; | |
| var a11 = ae[ 0 ], a12 = ae[ 4 ], a13 = ae[ 8 ], a14 = ae[ 12 ]; | |
| var a21 = ae[ 1 ], a22 = ae[ 5 ], a23 = ae[ 9 ], a24 = ae[ 13 ]; | |
| var a31 = ae[ 2 ], a32 = ae[ 6 ], a33 = ae[ 10 ], a34 = ae[ 14 ]; | |
| var a41 = ae[ 3 ], a42 = ae[ 7 ], a43 = ae[ 11 ], a44 = ae[ 15 ]; | |
| var b11 = be[ 0 ], b12 = be[ 4 ], b13 = be[ 8 ], b14 = be[ 12 ]; | |
| var b21 = be[ 1 ], b22 = be[ 5 ], b23 = be[ 9 ], b24 = be[ 13 ]; | |
| var b31 = be[ 2 ], b32 = be[ 6 ], b33 = be[ 10 ], b34 = be[ 14 ]; | |
| var b41 = be[ 3 ], b42 = be[ 7 ], b43 = be[ 11 ], b44 = be[ 15 ]; | |
| te[ 0 ] = a11 * b11 + a12 * b21 + a13 * b31 + a14 * b41; | |
| te[ 4 ] = a11 * b12 + a12 * b22 + a13 * b32 + a14 * b42; | |
| te[ 8 ] = a11 * b13 + a12 * b23 + a13 * b33 + a14 * b43; | |
| te[ 12 ] = a11 * b14 + a12 * b24 + a13 * b34 + a14 * b44; | |
| te[ 1 ] = a21 * b11 + a22 * b21 + a23 * b31 + a24 * b41; | |
| te[ 5 ] = a21 * b12 + a22 * b22 + a23 * b32 + a24 * b42; | |
| te[ 9 ] = a21 * b13 + a22 * b23 + a23 * b33 + a24 * b43; | |
| te[ 13 ] = a21 * b14 + a22 * b24 + a23 * b34 + a24 * b44; | |
| te[ 2 ] = a31 * b11 + a32 * b21 + a33 * b31 + a34 * b41; | |
| te[ 6 ] = a31 * b12 + a32 * b22 + a33 * b32 + a34 * b42; | |
| te[ 10 ] = a31 * b13 + a32 * b23 + a33 * b33 + a34 * b43; | |
| te[ 14 ] = a31 * b14 + a32 * b24 + a33 * b34 + a34 * b44; | |
| te[ 3 ] = a41 * b11 + a42 * b21 + a43 * b31 + a44 * b41; | |
| te[ 7 ] = a41 * b12 + a42 * b22 + a43 * b32 + a44 * b42; | |
| te[ 11 ] = a41 * b13 + a42 * b23 + a43 * b33 + a44 * b43; | |
| te[ 15 ] = a41 * b14 + a42 * b24 + a43 * b34 + a44 * b44; | |
| return this; | |
| }, | |
| multiplyScalar: function ( s ) { | |
| var te = this.elements; | |
| te[ 0 ] *= s; te[ 4 ] *= s; te[ 8 ] *= s; te[ 12 ] *= s; | |
| te[ 1 ] *= s; te[ 5 ] *= s; te[ 9 ] *= s; te[ 13 ] *= s; | |
| te[ 2 ] *= s; te[ 6 ] *= s; te[ 10 ] *= s; te[ 14 ] *= s; | |
| te[ 3 ] *= s; te[ 7 ] *= s; te[ 11 ] *= s; te[ 15 ] *= s; | |
| return this; | |
| }, | |
| applyToBufferAttribute: function () { | |
| var v1 = new Vector3(); | |
| return function applyToBufferAttribute( attribute ) { | |
| for ( var i = 0, l = attribute.count; i < l; i ++ ) { | |
| v1.x = attribute.getX( i ); | |
| v1.y = attribute.getY( i ); | |
| v1.z = attribute.getZ( i ); | |
| v1.applyMatrix4( this ); | |
| attribute.setXYZ( i, v1.x, v1.y, v1.z ); | |
| } | |
| return attribute; | |
| }; | |
| }(), | |
| determinant: function () { | |
| var te = this.elements; | |
| var n11 = te[ 0 ], n12 = te[ 4 ], n13 = te[ 8 ], n14 = te[ 12 ]; | |
| var n21 = te[ 1 ], n22 = te[ 5 ], n23 = te[ 9 ], n24 = te[ 13 ]; | |
| var n31 = te[ 2 ], n32 = te[ 6 ], n33 = te[ 10 ], n34 = te[ 14 ]; | |
| var n41 = te[ 3 ], n42 = te[ 7 ], n43 = te[ 11 ], n44 = te[ 15 ]; | |
| //TODO: make this more efficient | |
| //( based on http://www.euclideanspace.com/maths/algebra/matrix/functions/inverse/fourD/index.htm ) | |
| return ( | |
| n41 * ( | |
| + n14 * n23 * n32 | |
| - n13 * n24 * n32 | |
| - n14 * n22 * n33 | |
| + n12 * n24 * n33 | |
| + n13 * n22 * n34 | |
| - n12 * n23 * n34 | |
| ) + | |
| n42 * ( | |
| + n11 * n23 * n34 | |
| - n11 * n24 * n33 | |
| + n14 * n21 * n33 | |
| - n13 * n21 * n34 | |
| + n13 * n24 * n31 | |
| - n14 * n23 * n31 | |
| ) + | |
| n43 * ( | |
| + n11 * n24 * n32 | |
| - n11 * n22 * n34 | |
| - n14 * n21 * n32 | |
| + n12 * n21 * n34 | |
| + n14 * n22 * n31 | |
| - n12 * n24 * n31 | |
| ) + | |
| n44 * ( | |
| - n13 * n22 * n31 | |
| - n11 * n23 * n32 | |
| + n11 * n22 * n33 | |
| + n13 * n21 * n32 | |
| - n12 * n21 * n33 | |
| + n12 * n23 * n31 | |
| ) | |
| ); | |
| }, | |
| transpose: function () { | |
| var te = this.elements; | |
| var tmp; | |
| tmp = te[ 1 ]; te[ 1 ] = te[ 4 ]; te[ 4 ] = tmp; | |
| tmp = te[ 2 ]; te[ 2 ] = te[ 8 ]; te[ 8 ] = tmp; | |
| tmp = te[ 6 ]; te[ 6 ] = te[ 9 ]; te[ 9 ] = tmp; | |
| tmp = te[ 3 ]; te[ 3 ] = te[ 12 ]; te[ 12 ] = tmp; | |
| tmp = te[ 7 ]; te[ 7 ] = te[ 13 ]; te[ 13 ] = tmp; | |
| tmp = te[ 11 ]; te[ 11 ] = te[ 14 ]; te[ 14 ] = tmp; | |
| return this; | |
| }, | |
| setPosition: function ( v ) { | |
| var te = this.elements; | |
| te[ 12 ] = v.x; | |
| te[ 13 ] = v.y; | |
| te[ 14 ] = v.z; | |
| return this; | |
| }, | |
| getInverse: function ( m, throwOnDegenerate ) { | |
| // based on http://www.euclideanspace.com/maths/algebra/matrix/functions/inverse/fourD/index.htm | |
| var te = this.elements, | |
| me = m.elements, | |
| n11 = me[ 0 ], n21 = me[ 1 ], n31 = me[ 2 ], n41 = me[ 3 ], | |
| n12 = me[ 4 ], n22 = me[ 5 ], n32 = me[ 6 ], n42 = me[ 7 ], | |
| n13 = me[ 8 ], n23 = me[ 9 ], n33 = me[ 10 ], n43 = me[ 11 ], | |
| n14 = me[ 12 ], n24 = me[ 13 ], n34 = me[ 14 ], n44 = me[ 15 ], | |
| t11 = n23 * n34 * n42 - n24 * n33 * n42 + n24 * n32 * n43 - n22 * n34 * n43 - n23 * n32 * n44 + n22 * n33 * n44, | |
| t12 = n14 * n33 * n42 - n13 * n34 * n42 - n14 * n32 * n43 + n12 * n34 * n43 + n13 * n32 * n44 - n12 * n33 * n44, | |
| t13 = n13 * n24 * n42 - n14 * n23 * n42 + n14 * n22 * n43 - n12 * n24 * n43 - n13 * n22 * n44 + n12 * n23 * n44, | |
| t14 = n14 * n23 * n32 - n13 * n24 * n32 - n14 * n22 * n33 + n12 * n24 * n33 + n13 * n22 * n34 - n12 * n23 * n34; | |
| var det = n11 * t11 + n21 * t12 + n31 * t13 + n41 * t14; | |
| if ( det === 0 ) { | |
| var msg = "THREE.Matrix4: .getInverse() can't invert matrix, determinant is 0"; | |
| if ( throwOnDegenerate === true ) { | |
| throw new Error( msg ); | |
| } else { | |
| console.warn( msg ); | |
| } | |
| return this.identity(); | |
| } | |
| var detInv = 1 / det; | |
| te[ 0 ] = t11 * detInv; | |
| te[ 1 ] = ( n24 * n33 * n41 - n23 * n34 * n41 - n24 * n31 * n43 + n21 * n34 * n43 + n23 * n31 * n44 - n21 * n33 * n44 ) * detInv; | |
| te[ 2 ] = ( n22 * n34 * n41 - n24 * n32 * n41 + n24 * n31 * n42 - n21 * n34 * n42 - n22 * n31 * n44 + n21 * n32 * n44 ) * detInv; | |
| te[ 3 ] = ( n23 * n32 * n41 - n22 * n33 * n41 - n23 * n31 * n42 + n21 * n33 * n42 + n22 * n31 * n43 - n21 * n32 * n43 ) * detInv; | |
| te[ 4 ] = t12 * detInv; | |
| te[ 5 ] = ( n13 * n34 * n41 - n14 * n33 * n41 + n14 * n31 * n43 - n11 * n34 * n43 - n13 * n31 * n44 + n11 * n33 * n44 ) * detInv; | |
| te[ 6 ] = ( n14 * n32 * n41 - n12 * n34 * n41 - n14 * n31 * n42 + n11 * n34 * n42 + n12 * n31 * n44 - n11 * n32 * n44 ) * detInv; | |
| te[ 7 ] = ( n12 * n33 * n41 - n13 * n32 * n41 + n13 * n31 * n42 - n11 * n33 * n42 - n12 * n31 * n43 + n11 * n32 * n43 ) * detInv; | |
| te[ 8 ] = t13 * detInv; | |
| te[ 9 ] = ( n14 * n23 * n41 - n13 * n24 * n41 - n14 * n21 * n43 + n11 * n24 * n43 + n13 * n21 * n44 - n11 * n23 * n44 ) * detInv; | |
| te[ 10 ] = ( n12 * n24 * n41 - n14 * n22 * n41 + n14 * n21 * n42 - n11 * n24 * n42 - n12 * n21 * n44 + n11 * n22 * n44 ) * detInv; | |
| te[ 11 ] = ( n13 * n22 * n41 - n12 * n23 * n41 - n13 * n21 * n42 + n11 * n23 * n42 + n12 * n21 * n43 - n11 * n22 * n43 ) * detInv; | |
| te[ 12 ] = t14 * detInv; | |
| te[ 13 ] = ( n13 * n24 * n31 - n14 * n23 * n31 + n14 * n21 * n33 - n11 * n24 * n33 - n13 * n21 * n34 + n11 * n23 * n34 ) * detInv; | |
| te[ 14 ] = ( n14 * n22 * n31 - n12 * n24 * n31 - n14 * n21 * n32 + n11 * n24 * n32 + n12 * n21 * n34 - n11 * n22 * n34 ) * detInv; | |
| te[ 15 ] = ( n12 * n23 * n31 - n13 * n22 * n31 + n13 * n21 * n32 - n11 * n23 * n32 - n12 * n21 * n33 + n11 * n22 * n33 ) * detInv; | |
| return this; | |
| }, | |
| scale: function ( v ) { | |
| var te = this.elements; | |
| var x = v.x, y = v.y, z = v.z; | |
| te[ 0 ] *= x; te[ 4 ] *= y; te[ 8 ] *= z; | |
| te[ 1 ] *= x; te[ 5 ] *= y; te[ 9 ] *= z; | |
| te[ 2 ] *= x; te[ 6 ] *= y; te[ 10 ] *= z; | |
| te[ 3 ] *= x; te[ 7 ] *= y; te[ 11 ] *= z; | |
| return this; | |
| }, | |
| getMaxScaleOnAxis: function () { | |
| var te = this.elements; | |
| var scaleXSq = te[ 0 ] * te[ 0 ] + te[ 1 ] * te[ 1 ] + te[ 2 ] * te[ 2 ]; | |
| var scaleYSq = te[ 4 ] * te[ 4 ] + te[ 5 ] * te[ 5 ] + te[ 6 ] * te[ 6 ]; | |
| var scaleZSq = te[ 8 ] * te[ 8 ] + te[ 9 ] * te[ 9 ] + te[ 10 ] * te[ 10 ]; | |
| return Math.sqrt( Math.max( scaleXSq, scaleYSq, scaleZSq ) ); | |
| }, | |
| makeTranslation: function ( x, y, z ) { | |
| this.set( | |
| 1, 0, 0, x, | |
| 0, 1, 0, y, | |
| 0, 0, 1, z, | |
| 0, 0, 0, 1 | |
| ); | |
| return this; | |
| }, | |
| makeRotationX: function ( theta ) { | |
| var c = Math.cos( theta ), s = Math.sin( theta ); | |
| this.set( | |
| 1, 0, 0, 0, | |
| 0, c, - s, 0, | |
| 0, s, c, 0, | |
| 0, 0, 0, 1 | |
| ); | |
| return this; | |
| }, | |
| makeRotationY: function ( theta ) { | |
| var c = Math.cos( theta ), s = Math.sin( theta ); | |
| this.set( | |
| c, 0, s, 0, | |
| 0, 1, 0, 0, | |
| - s, 0, c, 0, | |
| 0, 0, 0, 1 | |
| ); | |
| return this; | |
| }, | |
| makeRotationZ: function ( theta ) { | |
| var c = Math.cos( theta ), s = Math.sin( theta ); | |
| this.set( | |
| c, - s, 0, 0, | |
| s, c, 0, 0, | |
| 0, 0, 1, 0, | |
| 0, 0, 0, 1 | |
| ); | |
| return this; | |
| }, | |
| makeRotationAxis: function ( axis, angle ) { | |
| // Based on http://www.gamedev.net/reference/articles/article1199.asp | |
| var c = Math.cos( angle ); | |
| var s = Math.sin( angle ); | |
| var t = 1 - c; | |
| var x = axis.x, y = axis.y, z = axis.z; | |
| var tx = t * x, ty = t * y; | |
| this.set( | |
| tx * x + c, tx * y - s * z, tx * z + s * y, 0, | |
| tx * y + s * z, ty * y + c, ty * z - s * x, 0, | |
| tx * z - s * y, ty * z + s * x, t * z * z + c, 0, | |
| 0, 0, 0, 1 | |
| ); | |
| return this; | |
| }, | |
| makeScale: function ( x, y, z ) { | |
| this.set( | |
| x, 0, 0, 0, | |
| 0, y, 0, 0, | |
| 0, 0, z, 0, | |
| 0, 0, 0, 1 | |
| ); | |
| return this; | |
| }, | |
| makeShear: function ( x, y, z ) { | |
| this.set( | |
| 1, y, z, 0, | |
| x, 1, z, 0, | |
| x, y, 1, 0, | |
| 0, 0, 0, 1 | |
| ); | |
| return this; | |
| }, | |
| compose: function ( position, quaternion, scale ) { | |
| this.makeRotationFromQuaternion( quaternion ); | |
| this.scale( scale ); | |
| this.setPosition( position ); | |
| return this; | |
| }, | |
| decompose: function () { | |
| var vector = new Vector3(); | |
| var matrix = new Matrix4(); | |
| return function decompose( position, quaternion, scale ) { | |
| var te = this.elements; | |
| var sx = vector.set( te[ 0 ], te[ 1 ], te[ 2 ] ).length(); | |
| var sy = vector.set( te[ 4 ], te[ 5 ], te[ 6 ] ).length(); | |
| var sz = vector.set( te[ 8 ], te[ 9 ], te[ 10 ] ).length(); | |
| // if determine is negative, we need to invert one scale | |
| var det = this.determinant(); | |
| if ( det < 0 ) sx = - sx; | |
| position.x = te[ 12 ]; | |
| position.y = te[ 13 ]; | |
| position.z = te[ 14 ]; | |
| // scale the rotation part | |
| matrix.copy( this ); | |
| var invSX = 1 / sx; | |
| var invSY = 1 / sy; | |
| var invSZ = 1 / sz; | |
| matrix.elements[ 0 ] *= invSX; | |
| matrix.elements[ 1 ] *= invSX; | |
| matrix.elements[ 2 ] *= invSX; | |
| matrix.elements[ 4 ] *= invSY; | |
| matrix.elements[ 5 ] *= invSY; | |
| matrix.elements[ 6 ] *= invSY; | |
| matrix.elements[ 8 ] *= invSZ; | |
| matrix.elements[ 9 ] *= invSZ; | |
| matrix.elements[ 10 ] *= invSZ; | |
| quaternion.setFromRotationMatrix( matrix ); | |
| scale.x = sx; | |
| scale.y = sy; | |
| scale.z = sz; | |
| return this; | |
| }; | |
| }(), | |
| makePerspective: function ( left, right, top, bottom, near, far ) { | |
| if ( far === undefined ) { | |
| console.warn( 'THREE.Matrix4: .makePerspective() has been redefined and has a new signature. Please check the docs.' ); | |
| } | |
| var te = this.elements; | |
| var x = 2 * near / ( right - left ); | |
| var y = 2 * near / ( top - bottom ); | |
| var a = ( right + left ) / ( right - left ); | |
| var b = ( top + bottom ) / ( top - bottom ); | |
| var c = - ( far + near ) / ( far - near ); | |
| var d = - 2 * far * near / ( far - near ); | |
| te[ 0 ] = x; te[ 4 ] = 0; te[ 8 ] = a; te[ 12 ] = 0; | |
| te[ 1 ] = 0; te[ 5 ] = y; te[ 9 ] = b; te[ 13 ] = 0; | |
| te[ 2 ] = 0; te[ 6 ] = 0; te[ 10 ] = c; te[ 14 ] = d; | |
| te[ 3 ] = 0; te[ 7 ] = 0; te[ 11 ] = - 1; te[ 15 ] = 0; | |
| return this; | |
| }, | |
| makeOrthographic: function ( left, right, top, bottom, near, far ) { | |
| var te = this.elements; | |
| var w = 1.0 / ( right - left ); | |
| var h = 1.0 / ( top - bottom ); | |
| var p = 1.0 / ( far - near ); | |
| var x = ( right + left ) * w; | |
| var y = ( top + bottom ) * h; | |
| var z = ( far + near ) * p; | |
| te[ 0 ] = 2 * w; te[ 4 ] = 0; te[ 8 ] = 0; te[ 12 ] = - x; | |
| te[ 1 ] = 0; te[ 5 ] = 2 * h; te[ 9 ] = 0; te[ 13 ] = - y; | |
| te[ 2 ] = 0; te[ 6 ] = 0; te[ 10 ] = - 2 * p; te[ 14 ] = - z; | |
| te[ 3 ] = 0; te[ 7 ] = 0; te[ 11 ] = 0; te[ 15 ] = 1; | |
| return this; | |
| }, | |
| equals: function ( matrix ) { | |
| var te = this.elements; | |
| var me = matrix.elements; | |
| for ( var i = 0; i < 16; i ++ ) { | |
| if ( te[ i ] !== me[ i ] ) return false; | |
| } | |
| return true; | |
| }, | |
| fromArray: function ( array, offset ) { | |
| if ( offset === undefined ) offset = 0; | |
| for ( var i = 0; i < 16; i ++ ) { | |
| this.elements[ i ] = array[ i + offset ]; | |
| } | |
| return this; | |
| }, | |
| toArray: function ( array, offset ) { | |
| if ( array === undefined ) array = []; | |
| if ( offset === undefined ) offset = 0; | |
| var te = this.elements; | |
| array[ offset ] = te[ 0 ]; | |
| array[ offset + 1 ] = te[ 1 ]; | |
| array[ offset + 2 ] = te[ 2 ]; | |
| array[ offset + 3 ] = te[ 3 ]; | |
| array[ offset + 4 ] = te[ 4 ]; | |
| array[ offset + 5 ] = te[ 5 ]; | |
| array[ offset + 6 ] = te[ 6 ]; | |
| array[ offset + 7 ] = te[ 7 ]; | |
| array[ offset + 8 ] = te[ 8 ]; | |
| array[ offset + 9 ] = te[ 9 ]; | |
| array[ offset + 10 ] = te[ 10 ]; | |
| array[ offset + 11 ] = te[ 11 ]; | |
| array[ offset + 12 ] = te[ 12 ]; | |
| array[ offset + 13 ] = te[ 13 ]; | |
| array[ offset + 14 ] = te[ 14 ]; | |
| array[ offset + 15 ] = te[ 15 ]; | |
| return array; | |
| } | |
| } ); | |
| /** | |
| * @author mikael emtinger / http://gomo.se/ | |
| * @author alteredq / http://alteredqualia.com/ | |
| * @author WestLangley / http://github.com/WestLangley | |
| * @author bhouston / http://clara.io | |
| */ | |
| function Quaternion( x, y, z, w ) { | |
| this._x = x || 0; | |
| this._y = y || 0; | |
| this._z = z || 0; | |
| this._w = ( w !== undefined ) ? w : 1; | |
| } | |
| Object.assign( Quaternion, { | |
| slerp: function ( qa, qb, qm, t ) { | |
| return qm.copy( qa ).slerp( qb, t ); | |
| }, | |
| slerpFlat: function ( dst, dstOffset, src0, srcOffset0, src1, srcOffset1, t ) { | |
| // fuzz-free, array-based Quaternion SLERP operation | |
| var x0 = src0[ srcOffset0 + 0 ], | |
| y0 = src0[ srcOffset0 + 1 ], | |
| z0 = src0[ srcOffset0 + 2 ], | |
| w0 = src0[ srcOffset0 + 3 ], | |
| x1 = src1[ srcOffset1 + 0 ], | |
| y1 = src1[ srcOffset1 + 1 ], | |
| z1 = src1[ srcOffset1 + 2 ], | |
| w1 = src1[ srcOffset1 + 3 ]; | |
| if ( w0 !== w1 || x0 !== x1 || y0 !== y1 || z0 !== z1 ) { | |
| var s = 1 - t, | |
| cos = x0 * x1 + y0 * y1 + z0 * z1 + w0 * w1, | |
| dir = ( cos >= 0 ? 1 : - 1 ), | |
| sqrSin = 1 - cos * cos; | |
| // Skip the Slerp for tiny steps to avoid numeric problems: | |
| if ( sqrSin > Number.EPSILON ) { | |
| var sin = Math.sqrt( sqrSin ), | |
| len = Math.atan2( sin, cos * dir ); | |
| s = Math.sin( s * len ) / sin; | |
| t = Math.sin( t * len ) / sin; | |
| } | |
| var tDir = t * dir; | |
| x0 = x0 * s + x1 * tDir; | |
| y0 = y0 * s + y1 * tDir; | |
| z0 = z0 * s + z1 * tDir; | |
| w0 = w0 * s + w1 * tDir; | |
| // Normalize in case we just did a lerp: | |
| if ( s === 1 - t ) { | |
| var f = 1 / Math.sqrt( x0 * x0 + y0 * y0 + z0 * z0 + w0 * w0 ); | |
| x0 *= f; | |
| y0 *= f; | |
| z0 *= f; | |
| w0 *= f; | |
| } | |
| } | |
| dst[ dstOffset ] = x0; | |
| dst[ dstOffset + 1 ] = y0; | |
| dst[ dstOffset + 2 ] = z0; | |
| dst[ dstOffset + 3 ] = w0; | |
| } | |
| } ); | |
| Object.defineProperties( Quaternion.prototype, { | |
| x: { | |
| get: function () { | |
| return this._x; | |
| }, | |
| set: function ( value ) { | |
| this._x = value; | |
| this.onChangeCallback(); | |
| } | |
| }, | |
| y: { | |
| get: function () { | |
| return this._y; | |
| }, | |
| set: function ( value ) { | |
| this._y = value; | |
| this.onChangeCallback(); | |
| } | |
| }, | |
| z: { | |
| get: function () { | |
| return this._z; | |
| }, | |
| set: function ( value ) { | |
| this._z = value; | |
| this.onChangeCallback(); | |
| } | |
| }, | |
| w: { | |
| get: function () { | |
| return this._w; | |
| }, | |
| set: function ( value ) { | |
| this._w = value; | |
| this.onChangeCallback(); | |
| } | |
| } | |
| } ); | |
| Object.assign( Quaternion.prototype, { | |
| set: function ( x, y, z, w ) { | |
| this._x = x; | |
| this._y = y; | |
| this._z = z; | |
| this._w = w; | |
| this.onChangeCallback(); | |
| return this; | |
| }, | |
| clone: function () { | |
| return new this.constructor( this._x, this._y, this._z, this._w ); | |
| }, | |
| copy: function ( quaternion ) { | |
| this._x = quaternion.x; | |
| this._y = quaternion.y; | |
| this._z = quaternion.z; | |
| this._w = quaternion.w; | |
| this.onChangeCallback(); | |
| return this; | |
| }, | |
| setFromEuler: function ( euler, update ) { | |
| if ( ! ( euler && euler.isEuler ) ) { | |
| throw new Error( 'THREE.Quaternion: .setFromEuler() now expects an Euler rotation rather than a Vector3 and order.' ); | |
| } | |
| var x = euler._x, y = euler._y, z = euler._z, order = euler.order; | |
| // http://www.mathworks.com/matlabcentral/fileexchange/ | |
| // 20696-function-to-convert-between-dcm-euler-angles-quaternions-and-euler-vectors/ | |
| // content/SpinCalc.m | |
| var cos = Math.cos; | |
| var sin = Math.sin; | |
| var c1 = cos( x / 2 ); | |
| var c2 = cos( y / 2 ); | |
| var c3 = cos( z / 2 ); | |
| var s1 = sin( x / 2 ); | |
| var s2 = sin( y / 2 ); | |
| var s3 = sin( z / 2 ); | |
| if ( order === 'XYZ' ) { | |
| this._x = s1 * c2 * c3 + c1 * s2 * s3; | |
| this._y = c1 * s2 * c3 - s1 * c2 * s3; | |
| this._z = c1 * c2 * s3 + s1 * s2 * c3; | |
| this._w = c1 * c2 * c3 - s1 * s2 * s3; | |
| } else if ( order === 'YXZ' ) { | |
| this._x = s1 * c2 * c3 + c1 * s2 * s3; | |
| this._y = c1 * s2 * c3 - s1 * c2 * s3; | |
| this._z = c1 * c2 * s3 - s1 * s2 * c3; | |
| this._w = c1 * c2 * c3 + s1 * s2 * s3; | |
| } else if ( order === 'ZXY' ) { | |
| this._x = s1 * c2 * c3 - c1 * s2 * s3; | |
| this._y = c1 * s2 * c3 + s1 * c2 * s3; | |
| this._z = c1 * c2 * s3 + s1 * s2 * c3; | |
| this._w = c1 * c2 * c3 - s1 * s2 * s3; | |
| } else if ( order === 'ZYX' ) { | |
| this._x = s1 * c2 * c3 - c1 * s2 * s3; | |
| this._y = c1 * s2 * c3 + s1 * c2 * s3; | |
| this._z = c1 * c2 * s3 - s1 * s2 * c3; | |
| this._w = c1 * c2 * c3 + s1 * s2 * s3; | |
| } else if ( order === 'YZX' ) { | |
| this._x = s1 * c2 * c3 + c1 * s2 * s3; | |
| this._y = c1 * s2 * c3 + s1 * c2 * s3; | |
| this._z = c1 * c2 * s3 - s1 * s2 * c3; | |
| this._w = c1 * c2 * c3 - s1 * s2 * s3; | |
| } else if ( order === 'XZY' ) { | |
| this._x = s1 * c2 * c3 - c1 * s2 * s3; | |
| this._y = c1 * s2 * c3 - s1 * c2 * s3; | |
| this._z = c1 * c2 * s3 + s1 * s2 * c3; | |
| this._w = c1 * c2 * c3 + s1 * s2 * s3; | |
| } | |
| if ( update !== false ) this.onChangeCallback(); | |
| return this; | |
| }, | |
| setFromAxisAngle: function ( axis, angle ) { | |
| // http://www.euclideanspace.com/maths/geometry/rotations/conversions/angleToQuaternion/index.htm | |
| // assumes axis is normalized | |
| var halfAngle = angle / 2, s = Math.sin( halfAngle ); | |
| this._x = axis.x * s; | |
| this._y = axis.y * s; | |
| this._z = axis.z * s; | |
| this._w = Math.cos( halfAngle ); | |
| this.onChangeCallback(); | |
| return this; | |
| }, | |
| setFromRotationMatrix: function ( m ) { | |
| // http://www.euclideanspace.com/maths/geometry/rotations/conversions/matrixToQuaternion/index.htm | |
| // assumes the upper 3x3 of m is a pure rotation matrix (i.e, unscaled) | |
| var te = m.elements, | |
| m11 = te[ 0 ], m12 = te[ 4 ], m13 = te[ 8 ], | |
| m21 = te[ 1 ], m22 = te[ 5 ], m23 = te[ 9 ], | |
| m31 = te[ 2 ], m32 = te[ 6 ], m33 = te[ 10 ], | |
| trace = m11 + m22 + m33, | |
| s; | |
| if ( trace > 0 ) { | |
| s = 0.5 / Math.sqrt( trace + 1.0 ); | |
| this._w = 0.25 / s; | |
| this._x = ( m32 - m23 ) * s; | |
| this._y = ( m13 - m31 ) * s; | |
| this._z = ( m21 - m12 ) * s; | |
| } else if ( m11 > m22 && m11 > m33 ) { | |
| s = 2.0 * Math.sqrt( 1.0 + m11 - m22 - m33 ); | |
| this._w = ( m32 - m23 ) / s; | |
| this._x = 0.25 * s; | |
| this._y = ( m12 + m21 ) / s; | |
| this._z = ( m13 + m31 ) / s; | |
| } else if ( m22 > m33 ) { | |
| s = 2.0 * Math.sqrt( 1.0 + m22 - m11 - m33 ); | |
| this._w = ( m13 - m31 ) / s; | |
| this._x = ( m12 + m21 ) / s; | |
| this._y = 0.25 * s; | |
| this._z = ( m23 + m32 ) / s; | |
| } else { | |
| s = 2.0 * Math.sqrt( 1.0 + m33 - m11 - m22 ); | |
| this._w = ( m21 - m12 ) / s; | |
| this._x = ( m13 + m31 ) / s; | |
| this._y = ( m23 + m32 ) / s; | |
| this._z = 0.25 * s; | |
| } | |
| this.onChangeCallback(); | |
| return this; | |
| }, | |
| setFromUnitVectors: function () { | |
| // assumes direction vectors vFrom and vTo are normalized | |
| var v1 = new Vector3(); | |
| var r; | |
| var EPS = 0.000001; | |
| return function setFromUnitVectors( vFrom, vTo ) { | |
| if ( v1 === undefined ) v1 = new Vector3(); | |
| r = vFrom.dot( vTo ) + 1; | |
| if ( r < EPS ) { | |
| r = 0; | |
| if ( Math.abs( vFrom.x ) > Math.abs( vFrom.z ) ) { | |
| v1.set( - vFrom.y, vFrom.x, 0 ); | |
| } else { | |
| v1.set( 0, - vFrom.z, vFrom.y ); | |
| } | |
| } else { | |
| v1.crossVectors( vFrom, vTo ); | |
| } | |
| this._x = v1.x; | |
| this._y = v1.y; | |
| this._z = v1.z; | |
| this._w = r; | |
| return this.normalize(); | |
| }; | |
| }(), | |
| inverse: function () { | |
| // quaternion is assumed to have unit length | |
| return this.conjugate(); | |
| }, | |
| conjugate: function () { | |
| this._x *= - 1; | |
| this._y *= - 1; | |
| this._z *= - 1; | |
| this.onChangeCallback(); | |
| return this; | |
| }, | |
| dot: function ( v ) { | |
| return this._x * v._x + this._y * v._y + this._z * v._z + this._w * v._w; | |
| }, | |
| lengthSq: function () { | |
| return this._x * this._x + this._y * this._y + this._z * this._z + this._w * this._w; | |
| }, | |
| length: function () { | |
| return Math.sqrt( this._x * this._x + this._y * this._y + this._z * this._z + this._w * this._w ); | |
| }, | |
| normalize: function () { | |
| var l = this.length(); | |
| if ( l === 0 ) { | |
| this._x = 0; | |
| this._y = 0; | |
| this._z = 0; | |
| this._w = 1; | |
| } else { | |
| l = 1 / l; | |
| this._x = this._x * l; | |
| this._y = this._y * l; | |
| this._z = this._z * l; | |
| this._w = this._w * l; | |
| } | |
| this.onChangeCallback(); | |
| return this; | |
| }, | |
| multiply: function ( q, p ) { | |
| if ( p !== undefined ) { | |
| console.warn( 'THREE.Quaternion: .multiply() now only accepts one argument. Use .multiplyQuaternions( a, b ) instead.' ); | |
| return this.multiplyQuaternions( q, p ); | |
| } | |
| return this.multiplyQuaternions( this, q ); | |
| }, | |
| premultiply: function ( q ) { | |
| return this.multiplyQuaternions( q, this ); | |
| }, | |
| multiplyQuaternions: function ( a, b ) { | |
| // from http://www.euclideanspace.com/maths/algebra/realNormedAlgebra/quaternions/code/index.htm | |
| var qax = a._x, qay = a._y, qaz = a._z, qaw = a._w; | |
| var qbx = b._x, qby = b._y, qbz = b._z, qbw = b._w; | |
| this._x = qax * qbw + qaw * qbx + qay * qbz - qaz * qby; | |
| this._y = qay * qbw + qaw * qby + qaz * qbx - qax * qbz; | |
| this._z = qaz * qbw + qaw * qbz + qax * qby - qay * qbx; | |
| this._w = qaw * qbw - qax * qbx - qay * qby - qaz * qbz; | |
| this.onChangeCallback(); | |
| return this; | |
| }, | |
| slerp: function ( qb, t ) { | |
| if ( t === 0 ) return this; | |
| if ( t === 1 ) return this.copy( qb ); | |
| var x = this._x, y = this._y, z = this._z, w = this._w; | |
| // http://www.euclideanspace.com/maths/algebra/realNormedAlgebra/quaternions/slerp/ | |
| var cosHalfTheta = w * qb._w + x * qb._x + y * qb._y + z * qb._z; | |
| if ( cosHalfTheta < 0 ) { | |
| this._w = - qb._w; | |
| this._x = - qb._x; | |
| this._y = - qb._y; | |
| this._z = - qb._z; | |
| cosHalfTheta = - cosHalfTheta; | |
| } else { | |
| this.copy( qb ); | |
| } | |
| if ( cosHalfTheta >= 1.0 ) { | |
| this._w = w; | |
| this._x = x; | |
| this._y = y; | |
| this._z = z; | |
| return this; | |
| } | |
| var sinHalfTheta = Math.sqrt( 1.0 - cosHalfTheta * cosHalfTheta ); | |
| if ( Math.abs( sinHalfTheta ) < 0.001 ) { | |
| this._w = 0.5 * ( w + this._w ); | |
| this._x = 0.5 * ( x + this._x ); | |
| this._y = 0.5 * ( y + this._y ); | |
| this._z = 0.5 * ( z + this._z ); | |
| return this; | |
| } | |
| var halfTheta = Math.atan2( sinHalfTheta, cosHalfTheta ); | |
| var ratioA = Math.sin( ( 1 - t ) * halfTheta ) / sinHalfTheta, | |
| ratioB = Math.sin( t * halfTheta ) / sinHalfTheta; | |
| this._w = ( w * ratioA + this._w * ratioB ); | |
| this._x = ( x * ratioA + this._x * ratioB ); | |
| this._y = ( y * ratioA + this._y * ratioB ); | |
| this._z = ( z * ratioA + this._z * ratioB ); | |
| this.onChangeCallback(); | |
| return this; | |
| }, | |
| equals: function ( quaternion ) { | |
| return ( quaternion._x === this._x ) && ( quaternion._y === this._y ) && ( quaternion._z === this._z ) && ( quaternion._w === this._w ); | |
| }, | |
| fromArray: function ( array, offset ) { | |
| if ( offset === undefined ) offset = 0; | |
| this._x = array[ offset ]; | |
| this._y = array[ offset + 1 ]; | |
| this._z = array[ offset + 2 ]; | |
| this._w = array[ offset + 3 ]; | |
| this.onChangeCallback(); | |
| return this; | |
| }, | |
| toArray: function ( array, offset ) { | |
| if ( array === undefined ) array = []; | |
| if ( offset === undefined ) offset = 0; | |
| array[ offset ] = this._x; | |
| array[ offset + 1 ] = this._y; | |
| array[ offset + 2 ] = this._z; | |
| array[ offset + 3 ] = this._w; | |
| return array; | |
| }, | |
| onChange: function ( callback ) { | |
| this.onChangeCallback = callback; | |
| return this; | |
| }, | |
| onChangeCallback: function () {} | |
| } ); | |
| /** | |
| * @author mrdoob / http://mrdoob.com/ | |
| * @author kile / http://kile.stravaganza.org/ | |
| * @author philogb / http://blog.thejit.org/ | |
| * @author mikael emtinger / http://gomo.se/ | |
| * @author egraether / http://egraether.com/ | |
| * @author WestLangley / http://github.com/WestLangley | |
| */ | |
| function Vector3( x, y, z ) { | |
| this.x = x || 0; | |
| this.y = y || 0; | |
| this.z = z || 0; | |
| } | |
| Object.assign( Vector3.prototype, { | |
| isVector3: true, | |
| set: function ( x, y, z ) { | |
| this.x = x; | |
| this.y = y; | |
| this.z = z; | |
| return this; | |
| }, | |
| setScalar: function ( scalar ) { | |
| this.x = scalar; | |
| this.y = scalar; | |
| this.z = scalar; | |
| return this; | |
| }, | |
| setX: function ( x ) { | |
| this.x = x; | |
| return this; | |
| }, | |
| setY: function ( y ) { | |
| this.y = y; | |
| return this; | |
| }, | |
| setZ: function ( z ) { | |
| this.z = z; | |
| return this; | |
| }, | |
| setComponent: function ( index, value ) { | |
| switch ( index ) { | |
| case 0: this.x = value; break; | |
| case 1: this.y = value; break; | |
| case 2: this.z = value; break; | |
| default: throw new Error( 'index is out of range: ' + index ); | |
| } | |
| return this; | |
| }, | |
| getComponent: function ( index ) { | |
| switch ( index ) { | |
| case 0: return this.x; | |
| case 1: return this.y; | |
| case 2: return this.z; | |
| default: throw new Error( 'index is out of range: ' + index ); | |
| } | |
| }, | |
| clone: function () { | |
| return new this.constructor( this.x, this.y, this.z ); | |
| }, | |
| copy: function ( v ) { | |
| this.x = v.x; | |
| this.y = v.y; | |
| this.z = v.z; | |
| return this; | |
| }, | |
| add: function ( v, w ) { | |
| if ( w !== undefined ) { | |
| console.warn( 'THREE.Vector3: .add() now only accepts one argument. Use .addVectors( a, b ) instead.' ); | |
| return this.addVectors( v, w ); | |
| } | |
| this.x += v.x; | |
| this.y += v.y; | |
| this.z += v.z; | |
| return this; | |
| }, | |
| addScalar: function ( s ) { | |
| this.x += s; | |
| this.y += s; | |
| this.z += s; | |
| return this; | |
| }, | |
| addVectors: function ( a, b ) { | |
| this.x = a.x + b.x; | |
| this.y = a.y + b.y; | |
| this.z = a.z + b.z; | |
| return this; | |
| }, | |
| addScaledVector: function ( v, s ) { | |
| this.x += v.x * s; | |
| this.y += v.y * s; | |
| this.z += v.z * s; | |
| return this; | |
| }, | |
| sub: function ( v, w ) { | |
| if ( w !== undefined ) { | |
| console.warn( 'THREE.Vector3: .sub() now only accepts one argument. Use .subVectors( a, b ) instead.' ); | |
| return this.subVectors( v, w ); | |
| } | |
| this.x -= v.x; | |
| this.y -= v.y; | |
| this.z -= v.z; | |
| return this; | |
| }, | |
| subScalar: function ( s ) { | |
| this.x -= s; | |
| this.y -= s; | |
| this.z -= s; | |
| return this; | |
| }, | |
| subVectors: function ( a, b ) { | |
| this.x = a.x - b.x; | |
| this.y = a.y - b.y; | |
| this.z = a.z - b.z; | |
| return this; | |
| }, | |
| multiply: function ( v, w ) { | |
| if ( w !== undefined ) { | |
| console.warn( 'THREE.Vector3: .multiply() now only accepts one argument. Use .multiplyVectors( a, b ) instead.' ); | |
| return this.multiplyVectors( v, w ); | |
| } | |
| this.x *= v.x; | |
| this.y *= v.y; | |
| this.z *= v.z; | |
| return this; | |
| }, | |
| multiplyScalar: function ( scalar ) { | |
| this.x *= scalar; | |
| this.y *= scalar; | |
| this.z *= scalar; | |
| return this; | |
| }, | |
| multiplyVectors: function ( a, b ) { | |
| this.x = a.x * b.x; | |
| this.y = a.y * b.y; | |
| this.z = a.z * b.z; | |
| return this; | |
| }, | |
| applyEuler: function () { | |
| var quaternion = new Quaternion(); | |
| return function applyEuler( euler ) { | |
| if ( ! ( euler && euler.isEuler ) ) { | |
| console.error( 'THREE.Vector3: .applyEuler() now expects an Euler rotation rather than a Vector3 and order.' ); | |
| } | |
| return this.applyQuaternion( quaternion.setFromEuler( euler ) ); | |
| }; | |
| }(), | |
| applyAxisAngle: function () { | |
| var quaternion = new Quaternion(); | |
| return function applyAxisAngle( axis, angle ) { | |
| return this.applyQuaternion( quaternion.setFromAxisAngle( axis, angle ) ); | |
| }; | |
| }(), | |
| applyMatrix3: function ( m ) { | |
| var x = this.x, y = this.y, z = this.z; | |
| var e = m.elements; | |
| this.x = e[ 0 ] * x + e[ 3 ] * y + e[ 6 ] * z; | |
| this.y = e[ 1 ] * x + e[ 4 ] * y + e[ 7 ] * z; | |
| this.z = e[ 2 ] * x + e[ 5 ] * y + e[ 8 ] * z; | |
| return this; | |
| }, | |
| applyMatrix4: function ( m ) { | |
| var x = this.x, y = this.y, z = this.z; | |
| var e = m.elements; | |
| var w = 1 / ( e[ 3 ] * x + e[ 7 ] * y + e[ 11 ] * z + e[ 15 ] ); | |
| this.x = ( e[ 0 ] * x + e[ 4 ] * y + e[ 8 ] * z + e[ 12 ] ) * w; | |
| this.y = ( e[ 1 ] * x + e[ 5 ] * y + e[ 9 ] * z + e[ 13 ] ) * w; | |
| this.z = ( e[ 2 ] * x + e[ 6 ] * y + e[ 10 ] * z + e[ 14 ] ) * w; | |
| return this; | |
| }, | |
| applyQuaternion: function ( q ) { | |
| var x = this.x, y = this.y, z = this.z; | |
| var qx = q.x, qy = q.y, qz = q.z, qw = q.w; | |
| // calculate quat * vector | |
| var ix = qw * x + qy * z - qz * y; | |
| var iy = qw * y + qz * x - qx * z; | |
| var iz = qw * z + qx * y - qy * x; | |
| var iw = - qx * x - qy * y - qz * z; | |
| // calculate result * inverse quat | |
| this.x = ix * qw + iw * - qx + iy * - qz - iz * - qy; | |
| this.y = iy * qw + iw * - qy + iz * - qx - ix * - qz; | |
| this.z = iz * qw + iw * - qz + ix * - qy - iy * - qx; | |
| return this; | |
| }, | |
| project: function () { | |
| var matrix = new Matrix4(); | |
| return function project( camera ) { | |
| matrix.multiplyMatrices( camera.projectionMatrix, matrix.getInverse( camera.matrixWorld ) ); | |
| return this.applyMatrix4( matrix ); | |
| }; | |
| }(), | |
| unproject: function () { | |
| var matrix = new Matrix4(); | |
| return function unproject( camera ) { | |
| matrix.multiplyMatrices( camera.matrixWorld, matrix.getInverse( camera.projectionMatrix ) ); | |
| return this.applyMatrix4( matrix ); | |
| }; | |
| }(), | |
| transformDirection: function ( m ) { | |
| // input: THREE.Matrix4 affine matrix | |
| // vector interpreted as a direction | |
| var x = this.x, y = this.y, z = this.z; | |
| var e = m.elements; | |
| this.x = e[ 0 ] * x + e[ 4 ] * y + e[ 8 ] * z; | |
| this.y = e[ 1 ] * x + e[ 5 ] * y + e[ 9 ] * z; | |
| this.z = e[ 2 ] * x + e[ 6 ] * y + e[ 10 ] * z; | |
| return this.normalize(); | |
| }, | |
| divide: function ( v ) { | |
| this.x /= v.x; | |
| this.y /= v.y; | |
| this.z /= v.z; | |
| return this; | |
| }, | |
| divideScalar: function ( scalar ) { | |
| return this.multiplyScalar( 1 / scalar ); | |
| }, | |
| min: function ( v ) { | |
| this.x = Math.min( this.x, v.x ); | |
| this.y = Math.min( this.y, v.y ); | |
| this.z = Math.min( this.z, v.z ); | |
| return this; | |
| }, | |
| max: function ( v ) { | |
| this.x = Math.max( this.x, v.x ); | |
| this.y = Math.max( this.y, v.y ); | |
| this.z = Math.max( this.z, v.z ); | |
| return this; | |
| }, | |
| clamp: function ( min, max ) { | |
| // assumes min < max, componentwise | |
| this.x = Math.max( min.x, Math.min( max.x, this.x ) ); | |
| this.y = Math.max( min.y, Math.min( max.y, this.y ) ); | |
| this.z = Math.max( min.z, Math.min( max.z, this.z ) ); | |
| return this; | |
| }, | |
| clampScalar: function () { | |
| var min = new Vector3(); | |
| var max = new Vector3(); | |
| return function clampScalar( minVal, maxVal ) { | |
| min.set( minVal, minVal, minVal ); | |
| max.set( maxVal, maxVal, maxVal ); | |
| return this.clamp( min, max ); | |
| }; | |
| }(), | |
| clampLength: function ( min, max ) { | |
| var length = this.length(); | |
| return this.divideScalar( length || 1 ).multiplyScalar( Math.max( min, Math.min( max, length ) ) ); | |
| }, | |
| floor: function () { | |
| this.x = Math.floor( this.x ); | |
| this.y = Math.floor( this.y ); | |
| this.z = Math.floor( this.z ); | |
| return this; | |
| }, | |
| ceil: function () { | |
| this.x = Math.ceil( this.x ); | |
| this.y = Math.ceil( this.y ); | |
| this.z = Math.ceil( this.z ); | |
| return this; | |
| }, | |
| round: function () { | |
| this.x = Math.round( this.x ); | |
| this.y = Math.round( this.y ); | |
| this.z = Math.round( this.z ); | |
| return this; | |
| }, | |
| roundToZero: function () { | |
| this.x = ( this.x < 0 ) ? Math.ceil( this.x ) : Math.floor( this.x ); | |
| this.y = ( this.y < 0 ) ? Math.ceil( this.y ) : Math.floor( this.y ); | |
| this.z = ( this.z < 0 ) ? Math.ceil( this.z ) : Math.floor( this.z ); | |
| return this; | |
| }, | |
| negate: function () { | |
| this.x = - this.x; | |
| this.y = - this.y; | |
| this.z = - this.z; | |
| return this; | |
| }, | |
| dot: function ( v ) { | |
| return this.x * v.x + this.y * v.y + this.z * v.z; | |
| }, | |
| // TODO lengthSquared? | |
| lengthSq: function () { | |
| return this.x * this.x + this.y * this.y + this.z * this.z; | |
| }, | |
| length: function () { | |
| return Math.sqrt( this.x * this.x + this.y * this.y + this.z * this.z ); | |
| }, | |
| manhattanLength: function () { | |
| return Math.abs( this.x ) + Math.abs( this.y ) + Math.abs( this.z ); | |
| }, | |
| normalize: function () { | |
| return this.divideScalar( this.length() || 1 ); | |
| }, | |
| setLength: function ( length ) { | |
| return this.normalize().multiplyScalar( length ); | |
| }, | |
| lerp: function ( v, alpha ) { | |
| this.x += ( v.x - this.x ) * alpha; | |
| this.y += ( v.y - this.y ) * alpha; | |
| this.z += ( v.z - this.z ) * alpha; | |
| return this; | |
| }, | |
| lerpVectors: function ( v1, v2, alpha ) { | |
| return this.subVectors( v2, v1 ).multiplyScalar( alpha ).add( v1 ); | |
| }, | |
| cross: function ( v, w ) { | |
| if ( w !== undefined ) { | |
| console.warn( 'THREE.Vector3: .cross() now only accepts one argument. Use .crossVectors( a, b ) instead.' ); | |
| return this.crossVectors( v, w ); | |
| } | |
| return this.crossVectors( this, v ); | |
| }, | |
| crossVectors: function ( a, b ) { | |
| var ax = a.x, ay = a.y, az = a.z; | |
| var bx = b.x, by = b.y, bz = b.z; | |
| this.x = ay * bz - az * by; | |
| this.y = az * bx - ax * bz; | |
| this.z = ax * by - ay * bx; | |
| return this; | |
| }, | |
| projectOnVector: function ( vector ) { | |
| var scalar = vector.dot( this ) / vector.lengthSq(); | |
| return this.copy( vector ).multiplyScalar( scalar ); | |
| }, | |
| projectOnPlane: function () { | |
| var v1 = new Vector3(); | |
| return function projectOnPlane( planeNormal ) { | |
| v1.copy( this ).projectOnVector( planeNormal ); | |
| return this.sub( v1 ); | |
| }; | |
| }(), | |
| reflect: function () { | |
| // reflect incident vector off plane orthogonal to normal | |
| // normal is assumed to have unit length | |
| var v1 = new Vector3(); | |
| return function reflect( normal ) { | |
| return this.sub( v1.copy( normal ).multiplyScalar( 2 * this.dot( normal ) ) ); | |
| }; | |
| }(), | |
| angleTo: function ( v ) { | |
| var theta = this.dot( v ) / ( Math.sqrt( this.lengthSq() * v.lengthSq() ) ); | |
| // clamp, to handle numerical problems | |
| return Math.acos( _Math.clamp( theta, - 1, 1 ) ); | |
| }, | |
| distanceTo: function ( v ) { | |
| return Math.sqrt( this.distanceToSquared( v ) ); | |
| }, | |
| distanceToSquared: function ( v ) { | |
| var dx = this.x - v.x, dy = this.y - v.y, dz = this.z - v.z; | |
| return dx * dx + dy * dy + dz * dz; | |
| }, | |
| manhattanDistanceTo: function ( v ) { | |
| return Math.abs( this.x - v.x ) + Math.abs( this.y - v.y ) + Math.abs( this.z - v.z ); | |
| }, | |
| setFromSpherical: function ( s ) { | |
| var sinPhiRadius = Math.sin( s.phi ) * s.radius; | |
| this.x = sinPhiRadius * Math.sin( s.theta ); | |
| this.y = Math.cos( s.phi ) * s.radius; | |
| this.z = sinPhiRadius * Math.cos( s.theta ); | |
| return this; | |
| }, | |
| setFromCylindrical: function ( c ) { | |
| this.x = c.radius * Math.sin( c.theta ); | |
| this.y = c.y; | |
| this.z = c.radius * Math.cos( c.theta ); | |
| return this; | |
| }, | |
| setFromMatrixPosition: function ( m ) { | |
| var e = m.elements; | |
| this.x = e[ 12 ]; | |
| this.y = e[ 13 ]; | |
| this.z = e[ 14 ]; | |
| return this; | |
| }, | |
| setFromMatrixScale: function ( m ) { | |
| var sx = this.setFromMatrixColumn( m, 0 ).length(); | |
| var sy = this.setFromMatrixColumn( m, 1 ).length(); | |
| var sz = this.setFromMatrixColumn( m, 2 ).length(); | |
| this.x = sx; | |
| this.y = sy; | |
| this.z = sz; | |
| return this; | |
| }, | |
| setFromMatrixColumn: function ( m, index ) { | |
| return this.fromArray( m.elements, index * 4 ); | |
| }, | |
| equals: function ( v ) { | |
| return ( ( v.x === this.x ) && ( v.y === this.y ) && ( v.z === this.z ) ); | |
| }, | |
| fromArray: function ( array, offset ) { | |
| if ( offset === undefined ) offset = 0; | |
| this.x = array[ offset ]; | |
| this.y = array[ offset + 1 ]; | |
| this.z = array[ offset + 2 ]; | |
| return this; | |
| }, | |
| toArray: function ( array, offset ) { | |
| if ( array === undefined ) array = []; | |
| if ( offset === undefined ) offset = 0; | |
| array[ offset ] = this.x; | |
| array[ offset + 1 ] = this.y; | |
| array[ offset + 2 ] = this.z; | |
| return array; | |
| }, | |
| fromBufferAttribute: function ( attribute, index, offset ) { | |
| if ( offset !== undefined ) { | |
| console.warn( 'THREE.Vector3: offset has been removed from .fromBufferAttribute().' ); | |
| } | |
| this.x = attribute.getX( index ); | |
| this.y = attribute.getY( index ); | |
| this.z = attribute.getZ( index ); | |
| return this; | |
| } | |
| } ); | |
| /** | |
| * @author alteredq / http://alteredqualia.com/ | |
| * @author WestLangley / http://github.com/WestLangley | |
| * @author bhouston / http://clara.io | |
| * @author tschw | |
| */ | |
| function Matrix3() { | |
| this.elements = [ | |
| 1, 0, 0, | |
| 0, 1, 0, | |
| 0, 0, 1 | |
| ]; | |
| if ( arguments.length > 0 ) { | |
| console.error( 'THREE.Matrix3: the constructor no longer reads arguments. use .set() instead.' ); | |
| } | |
| } | |
| Object.assign( Matrix3.prototype, { | |
| isMatrix3: true, | |
| set: function ( n11, n12, n13, n21, n22, n23, n31, n32, n33 ) { | |
| var te = this.elements; | |
| te[ 0 ] = n11; te[ 1 ] = n21; te[ 2 ] = n31; | |
| te[ 3 ] = n12; te[ 4 ] = n22; te[ 5 ] = n32; | |
| te[ 6 ] = n13; te[ 7 ] = n23; te[ 8 ] = n33; | |
| return this; | |
| }, | |
| identity: function () { | |
| this.set( | |
| 1, 0, 0, | |
| 0, 1, 0, | |
| 0, 0, 1 | |
| ); | |
| return this; | |
| }, | |
| clone: function () { | |
| return new this.constructor().fromArray( this.elements ); | |
| }, | |
| copy: function ( m ) { | |
| var te = this.elements; | |
| var me = m.elements; | |
| te[ 0 ] = me[ 0 ]; te[ 1 ] = me[ 1 ]; te[ 2 ] = me[ 2 ]; | |
| te[ 3 ] = me[ 3 ]; te[ 4 ] = me[ 4 ]; te[ 5 ] = me[ 5 ]; | |
| te[ 6 ] = me[ 6 ]; te[ 7 ] = me[ 7 ]; te[ 8 ] = me[ 8 ]; | |
| return this; | |
| }, | |
| setFromMatrix4: function ( m ) { | |
| var me = m.elements; | |
| this.set( | |
| me[ 0 ], me[ 4 ], me[ 8 ], | |
| me[ 1 ], me[ 5 ], me[ 9 ], | |
| me[ 2 ], me[ 6 ], me[ 10 ] | |
| ); | |
| return this; | |
| }, | |
| applyToBufferAttribute: function () { | |
| var v1 = new Vector3(); | |
| return function applyToBufferAttribute( attribute ) { | |
| for ( var i = 0, l = attribute.count; i < l; i ++ ) { | |
| v1.x = attribute.getX( i ); | |
| v1.y = attribute.getY( i ); | |
| v1.z = attribute.getZ( i ); | |
| v1.applyMatrix3( this ); | |
| attribute.setXYZ( i, v1.x, v1.y, v1.z ); | |
| } | |
| return attribute; | |
| }; | |
| }(), | |
| multiply: function ( m ) { | |
| return this.multiplyMatrices( this, m ); | |
| }, | |
| premultiply: function ( m ) { | |
| return this.multiplyMatrices( m, this ); | |
| }, | |
| multiplyMatrices: function ( a, b ) { | |
| var ae = a.elements; | |
| var be = b.elements; | |
| var te = this.elements; | |
| var a11 = ae[ 0 ], a12 = ae[ 3 ], a13 = ae[ 6 ]; | |
| var a21 = ae[ 1 ], a22 = ae[ 4 ], a23 = ae[ 7 ]; | |
| var a31 = ae[ 2 ], a32 = ae[ 5 ], a33 = ae[ 8 ]; | |
| var b11 = be[ 0 ], b12 = be[ 3 ], b13 = be[ 6 ]; | |
| var b21 = be[ 1 ], b22 = be[ 4 ], b23 = be[ 7 ]; | |
| var b31 = be[ 2 ], b32 = be[ 5 ], b33 = be[ 8 ]; | |
| te[ 0 ] = a11 * b11 + a12 * b21 + a13 * b31; | |
| te[ 3 ] = a11 * b12 + a12 * b22 + a13 * b32; | |
| te[ 6 ] = a11 * b13 + a12 * b23 + a13 * b33; | |
| te[ 1 ] = a21 * b11 + a22 * b21 + a23 * b31; | |
| te[ 4 ] = a21 * b12 + a22 * b22 + a23 * b32; | |
| te[ 7 ] = a21 * b13 + a22 * b23 + a23 * b33; | |
| te[ 2 ] = a31 * b11 + a32 * b21 + a33 * b31; | |
| te[ 5 ] = a31 * b12 + a32 * b22 + a33 * b32; | |
| te[ 8 ] = a31 * b13 + a32 * b23 + a33 * b33; | |
| return this; | |
| }, | |
| multiplyScalar: function ( s ) { | |
| var te = this.elements; | |
| te[ 0 ] *= s; te[ 3 ] *= s; te[ 6 ] *= s; | |
| te[ 1 ] *= s; te[ 4 ] *= s; te[ 7 ] *= s; | |
| te[ 2 ] *= s; te[ 5 ] *= s; te[ 8 ] *= s; | |
| return this; | |
| }, | |
| determinant: function () { | |
| var te = this.elements; | |
| var a = te[ 0 ], b = te[ 1 ], c = te[ 2 ], | |
| d = te[ 3 ], e = te[ 4 ], f = te[ 5 ], | |
| g = te[ 6 ], h = te[ 7 ], i = te[ 8 ]; | |
| return a * e * i - a * f * h - b * d * i + b * f * g + c * d * h - c * e * g; | |
| }, | |
| getInverse: function ( matrix, throwOnDegenerate ) { | |
| if ( matrix && matrix.isMatrix4 ) { | |
| console.error( "THREE.Matrix3: .getInverse() no longer takes a Matrix4 argument." ); | |
| } | |
| var me = matrix.elements, | |
| te = this.elements, | |
| n11 = me[ 0 ], n21 = me[ 1 ], n31 = me[ 2 ], | |
| n12 = me[ 3 ], n22 = me[ 4 ], n32 = me[ 5 ], | |
| n13 = me[ 6 ], n23 = me[ 7 ], n33 = me[ 8 ], | |
| t11 = n33 * n22 - n32 * n23, | |
| t12 = n32 * n13 - n33 * n12, | |
| t13 = n23 * n12 - n22 * n13, | |
| det = n11 * t11 + n21 * t12 + n31 * t13; | |
| if ( det === 0 ) { | |
| var msg = "THREE.Matrix3: .getInverse() can't invert matrix, determinant is 0"; | |
| if ( throwOnDegenerate === true ) { | |
| throw new Error( msg ); | |
| } else { | |
| console.warn( msg ); | |
| } | |
| return this.identity(); | |
| } | |
| var detInv = 1 / det; | |
| te[ 0 ] = t11 * detInv; | |
| te[ 1 ] = ( n31 * n23 - n33 * n21 ) * detInv; | |
| te[ 2 ] = ( n32 * n21 - n31 * n22 ) * detInv; | |
| te[ 3 ] = t12 * detInv; | |
| te[ 4 ] = ( n33 * n11 - n31 * n13 ) * detInv; | |
| te[ 5 ] = ( n31 * n12 - n32 * n11 ) * detInv; | |
| te[ 6 ] = t13 * detInv; | |
| te[ 7 ] = ( n21 * n13 - n23 * n11 ) * detInv; | |
| te[ 8 ] = ( n22 * n11 - n21 * n12 ) * detInv; | |
| return this; | |
| }, | |
| transpose: function () { | |
| var tmp, m = this.elements; | |
| tmp = m[ 1 ]; m[ 1 ] = m[ 3 ]; m[ 3 ] = tmp; | |
| tmp = m[ 2 ]; m[ 2 ] = m[ 6 ]; m[ 6 ] = tmp; | |
| tmp = m[ 5 ]; m[ 5 ] = m[ 7 ]; m[ 7 ] = tmp; | |
| return this; | |
| }, | |
| getNormalMatrix: function ( matrix4 ) { | |
| return this.setFromMatrix4( matrix4 ).getInverse( this ).transpose(); | |
| }, | |
| transposeIntoArray: function ( r ) { | |
| var m = this.elements; | |
| r[ 0 ] = m[ 0 ]; | |
| r[ 1 ] = m[ 3 ]; | |
| r[ 2 ] = m[ 6 ]; | |
| r[ 3 ] = m[ 1 ]; | |
| r[ 4 ] = m[ 4 ]; | |
| r[ 5 ] = m[ 7 ]; | |
| r[ 6 ] = m[ 2 ]; | |
| r[ 7 ] = m[ 5 ]; | |
| r[ 8 ] = m[ 8 ]; | |
| return this; | |
| }, | |
| setUvTransform: function ( tx, ty, sx, sy, rotation, cx, cy ) { | |
| var c = Math.cos( rotation ); | |
| var s = Math.sin( rotation ); | |
| this.set( | |
| sx * c, sx * s, - sx * ( c * cx + s * cy ) + cx + tx, | |
| - sy * s, sy * c, - sy * ( - s * cx + c * cy ) + cy + ty, | |
| 0, 0, 1 | |
| ); | |
| }, | |
| scale: function ( sx, sy ) { | |
| var te = this.elements; | |
| te[ 0 ] *= sx; te[ 3 ] *= sx; te[ 6 ] *= sx; | |
| te[ 1 ] *= sy; te[ 4 ] *= sy; te[ 7 ] *= sy; | |
| return this; | |
| }, | |
| rotate: function ( theta ) { | |
| var c = Math.cos( theta ); | |
| var s = Math.sin( theta ); | |
| var te = this.elements; | |
| var a11 = te[ 0 ], a12 = te[ 3 ], a13 = te[ 6 ]; | |
| var a21 = te[ 1 ], a22 = te[ 4 ], a23 = te[ 7 ]; | |
| te[ 0 ] = c * a11 + s * a21; | |
| te[ 3 ] = c * a12 + s * a22; | |
| te[ 6 ] = c * a13 + s * a23; | |
| te[ 1 ] = - s * a11 + c * a21; | |
| te[ 4 ] = - s * a12 + c * a22; | |
| te[ 7 ] = - s * a13 + c * a23; | |
| return this; | |
| }, | |
| translate: function ( tx, ty ) { | |
| var te = this.elements; | |
| te[ 0 ] += tx * te[ 2 ]; te[ 3 ] += tx * te[ 5 ]; te[ 6 ] += tx * te[ 8 ]; | |
| te[ 1 ] += ty * te[ 2 ]; te[ 4 ] += ty * te[ 5 ]; te[ 7 ] += ty * te[ 8 ]; | |
| return this; | |
| }, | |
| equals: function ( matrix ) { | |
| var te = this.elements; | |
| var me = matrix.elements; | |
| for ( var i = 0; i < 9; i ++ ) { | |
| if ( te[ i ] !== me[ i ] ) return false; | |
| } | |
| return true; | |
| }, | |
| fromArray: function ( array, offset ) { | |
| if ( offset === undefined ) offset = 0; | |
| for ( var i = 0; i < 9; i ++ ) { | |
| this.elements[ i ] = array[ i + offset ]; | |
| } | |
| return this; | |
| }, | |
| toArray: function ( array, offset ) { | |
| if ( array === undefined ) array = []; | |
| if ( offset === undefined ) offset = 0; | |
| var te = this.elements; | |
| array[ offset ] = te[ 0 ]; | |
| array[ offset + 1 ] = te[ 1 ]; | |
| array[ offset + 2 ] = te[ 2 ]; | |
| array[ offset + 3 ] = te[ 3 ]; | |
| array[ offset + 4 ] = te[ 4 ]; | |
| array[ offset + 5 ] = te[ 5 ]; | |
| array[ offset + 6 ] = te[ 6 ]; | |
| array[ offset + 7 ] = te[ 7 ]; | |
| array[ offset + 8 ] = te[ 8 ]; | |
| return array; | |
| } | |
| } ); | |
| /** | |
| * @author mrdoob / http://mrdoob.com/ | |
| * @author alteredq / http://alteredqualia.com/ | |
| * @author szimek / https://github.com/szimek/ | |
| */ | |
| var textureId = 0; | |
| function Texture( image, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy, encoding ) { | |
| Object.defineProperty( this, 'id', { value: textureId ++ } ); | |
| this.uuid = _Math.generateUUID(); | |
| this.name = ''; | |
| this.image = image !== undefined ? image : Texture.DEFAULT_IMAGE; | |
| this.mipmaps = []; | |
| this.mapping = mapping !== undefined ? mapping : Texture.DEFAULT_MAPPING; | |
| this.wrapS = wrapS !== undefined ? wrapS : ClampToEdgeWrapping; | |
| this.wrapT = wrapT !== undefined ? wrapT : ClampToEdgeWrapping; | |
| this.magFilter = magFilter !== undefined ? magFilter : LinearFilter; | |
| this.minFilter = minFilter !== undefined ? minFilter : LinearMipMapLinearFilter; | |
| this.anisotropy = anisotropy !== undefined ? anisotropy : 1; | |
| this.format = format !== undefined ? format : RGBAFormat; | |
| this.type = type !== undefined ? type : UnsignedByteType; | |
| this.offset = new Vector2( 0, 0 ); | |
| this.repeat = new Vector2( 1, 1 ); | |
| this.center = new Vector2( 0, 0 ); | |
| this.rotation = 0; | |
| this.matrixAutoUpdate = true; | |
| this.matrix = new Matrix3(); | |
| this.generateMipmaps = true; | |
| this.premultiplyAlpha = false; | |
| this.flipY = true; | |
| this.unpackAlignment = 4; // valid values: 1, 2, 4, 8 (see http://www.khronos.org/opengles/sdk/docs/man/xhtml/glPixelStorei.xml) | |
| // Values of encoding !== THREE.LinearEncoding only supported on map, envMap and emissiveMap. | |
| // | |
| // Also changing the encoding after already used by a Material will not automatically make the Material | |
| // update. You need to explicitly call Material.needsUpdate to trigger it to recompile. | |
| this.encoding = encoding !== undefined ? encoding : LinearEncoding; | |
| this.version = 0; | |
| this.onUpdate = null; | |
| } | |
| Texture.DEFAULT_IMAGE = undefined; | |
| Texture.DEFAULT_MAPPING = UVMapping; | |
| Texture.prototype = Object.assign( Object.create( EventDispatcher.prototype ), { | |
| constructor: Texture, | |
| isTexture: true, | |
| clone: function () { | |
| return new this.constructor().copy( this ); | |
| }, | |
| copy: function ( source ) { | |
| this.name = source.name; | |
| this.image = source.image; | |
| this.mipmaps = source.mipmaps.slice( 0 ); | |
| this.mapping = source.mapping; | |
| this.wrapS = source.wrapS; | |
| this.wrapT = source.wrapT; | |
| this.magFilter = source.magFilter; | |
| this.minFilter = source.minFilter; | |
| this.anisotropy = source.anisotropy; | |
| this.format = source.format; | |
| this.type = source.type; | |
| this.offset.copy( source.offset ); | |
| this.repeat.copy( source.repeat ); | |
| this.center.copy( source.center ); | |
| this.rotation = source.rotation; | |
| this.matrixAutoUpdate = source.matrixAutoUpdate; | |
| this.matrix.copy( source.matrix ); | |
| this.generateMipmaps = source.generateMipmaps; | |
| this.premultiplyAlpha = source.premultiplyAlpha; | |
| this.flipY = source.flipY; | |
| this.unpackAlignment = source.unpackAlignment; | |
| this.encoding = source.encoding; | |
| return this; | |
| }, | |
| toJSON: function ( meta ) { | |
| var isRootObject = ( meta === undefined || typeof meta === 'string' ); | |
| if ( ! isRootObject && meta.textures[ this.uuid ] !== undefined ) { | |
| return meta.textures[ this.uuid ]; | |
| } | |
| function getDataURL( image ) { | |
| var canvas; | |
| if ( image instanceof HTMLCanvasElement ) { | |
| canvas = image; | |
| } else { | |
| canvas = document.createElementNS( 'http://www.w3.org/1999/xhtml', 'canvas' ); | |
| canvas.width = image.width; | |
| canvas.height = image.height; | |
| var context = canvas.getContext( '2d' ); | |
| if ( image instanceof ImageData ) { | |
| context.putImageData( image, 0, 0 ); | |
| } else { | |
| context.drawImage( image, 0, 0, image.width, image.height ); | |
| } | |
| } | |
| if ( canvas.width > 2048 || canvas.height > 2048 ) { | |
| return canvas.toDataURL( 'image/jpeg', 0.6 ); | |
| } else { | |
| return canvas.toDataURL( 'image/png' ); | |
| } | |
| } | |
| var output = { | |
| metadata: { | |
| version: 4.5, | |
| type: 'Texture', | |
| generator: 'Texture.toJSON' | |
| }, | |
| uuid: this.uuid, | |
| name: this.name, | |
| mapping: this.mapping, | |
| repeat: [ this.repeat.x, this.repeat.y ], | |
| offset: [ this.offset.x, this.offset.y ], | |
| center: [ this.center.x, this.center.y ], | |
| rotation: this.rotation, | |
| wrap: [ this.wrapS, this.wrapT ], | |
| minFilter: this.minFilter, | |
| magFilter: this.magFilter, | |
| anisotropy: this.anisotropy, | |
| flipY: this.flipY | |
| }; | |
| if ( this.image !== undefined ) { | |
| // TODO: Move to THREE.Image | |
| var image = this.image; | |
| if ( image.uuid === undefined ) { | |
| image.uuid = _Math.generateUUID(); // UGH | |
| } | |
| if ( ! isRootObject && meta.images[ image.uuid ] === undefined ) { | |
| meta.images[ image.uuid ] = { | |
| uuid: image.uuid, | |
| url: getDataURL( image ) | |
| }; | |
| } | |
| output.image = image.uuid; | |
| } | |
| if ( ! isRootObject ) { | |
| meta.textures[ this.uuid ] = output; | |
| } | |
| return output; | |
| }, | |
| dispose: function () { | |
| this.dispatchEvent( { type: 'dispose' } ); | |
| }, | |
| transformUv: function ( uv ) { | |
| if ( this.mapping !== UVMapping ) return; | |
| uv.applyMatrix3( this.matrix ); | |
| if ( uv.x < 0 || uv.x > 1 ) { | |
| switch ( this.wrapS ) { | |
| case RepeatWrapping: | |
| uv.x = uv.x - Math.floor( uv.x ); | |
| break; | |
| case ClampToEdgeWrapping: | |
| uv.x = uv.x < 0 ? 0 : 1; | |
| break; | |
| case MirroredRepeatWrapping: | |
| if ( Math.abs( Math.floor( uv.x ) % 2 ) === 1 ) { | |
| uv.x = Math.ceil( uv.x ) - uv.x; | |
| } else { | |
| uv.x = uv.x - Math.floor( uv.x ); | |
| } | |
| break; | |
| } | |
| } | |
| if ( uv.y < 0 || uv.y > 1 ) { | |
| switch ( this.wrapT ) { | |
| case RepeatWrapping: | |
| uv.y = uv.y - Math.floor( uv.y ); | |
| break; | |
| case ClampToEdgeWrapping: | |
| uv.y = uv.y < 0 ? 0 : 1; | |
| break; | |
| case MirroredRepeatWrapping: | |
| if ( Math.abs( Math.floor( uv.y ) % 2 ) === 1 ) { | |
| uv.y = Math.ceil( uv.y ) - uv.y; | |
| } else { | |
| uv.y = uv.y - Math.floor( uv.y ); | |
| } | |
| break; | |
| } | |
| } | |
| if ( this.flipY ) { | |
| uv.y = 1 - uv.y; | |
| } | |
| } | |
| } ); | |
| Object.defineProperty( Texture.prototype, "needsUpdate", { | |
| set: function ( value ) { | |
| if ( value === true ) this.version ++; | |
| } | |
| } ); | |
| /** | |
| * @author supereggbert / http://www.paulbrunt.co.uk/ | |
| * @author philogb / http://blog.thejit.org/ | |
| * @author mikael emtinger / http://gomo.se/ | |
| * @author egraether / http://egraether.com/ | |
| * @author WestLangley / http://github.com/WestLangley | |
| */ | |
| function Vector4( x, y, z, w ) { | |
| this.x = x || 0; | |
| this.y = y || 0; | |
| this.z = z || 0; | |
| this.w = ( w !== undefined ) ? w : 1; | |
| } | |
| Object.assign( Vector4.prototype, { | |
| isVector4: true, | |
| set: function ( x, y, z, w ) { | |
| this.x = x; | |
| this.y = y; | |
| this.z = z; | |
| this.w = w; | |
| return this; | |
| }, | |
| setScalar: function ( scalar ) { | |
| this.x = scalar; | |
| this.y = scalar; | |
| this.z = scalar; | |
| this.w = scalar; | |
| return this; | |
| }, | |
| setX: function ( x ) { | |
| this.x = x; | |
| return this; | |
| }, | |
| setY: function ( y ) { | |
| this.y = y; | |
| return this; | |
| }, | |
| setZ: function ( z ) { | |
| this.z = z; | |
| return this; | |
| }, | |
| setW: function ( w ) { | |
| this.w = w; | |
| return this; | |
| }, | |
| setComponent: function ( index, value ) { | |
| switch ( index ) { | |
| case 0: this.x = value; break; | |
| case 1: this.y = value; break; | |
| case 2: this.z = value; break; | |
| case 3: this.w = value; break; | |
| default: throw new Error( 'index is out of range: ' + index ); | |
| } | |
| return this; | |
| }, | |
| getComponent: function ( index ) { | |
| switch ( index ) { | |
| case 0: return this.x; | |
| case 1: return this.y; | |
| case 2: return this.z; | |
| case 3: return this.w; | |
| default: throw new Error( 'index is out of range: ' + index ); | |
| } | |
| }, | |
| clone: function () { | |
| return new this.constructor( this.x, this.y, this.z, this.w ); | |
| }, | |
| copy: function ( v ) { | |
| this.x = v.x; | |
| this.y = v.y; | |
| this.z = v.z; | |
| this.w = ( v.w !== undefined ) ? v.w : 1; | |
| return this; | |
| }, | |
| add: function ( v, w ) { | |
| if ( w !== undefined ) { | |
| console.warn( 'THREE.Vector4: .add() now only accepts one argument. Use .addVectors( a, b ) instead.' ); | |
| return this.addVectors( v, w ); | |
| } | |
| this.x += v.x; | |
| this.y += v.y; | |
| this.z += v.z; | |
| this.w += v.w; | |
| return this; | |
| }, | |
| addScalar: function ( s ) { | |
| this.x += s; | |
| this.y += s; | |
| this.z += s; | |
| this.w += s; | |
| return this; | |
| }, | |
| addVectors: function ( a, b ) { | |
| this.x = a.x + b.x; | |
| this.y = a.y + b.y; | |
| this.z = a.z + b.z; | |
| this.w = a.w + b.w; | |
| return this; | |
| }, | |
| addScaledVector: function ( v, s ) { | |
| this.x += v.x * s; | |
| this.y += v.y * s; | |
| this.z += v.z * s; | |
| this.w += v.w * s; | |
| return this; | |
| }, | |
| sub: function ( v, w ) { | |
| if ( w !== undefined ) { | |
| console.warn( 'THREE.Vector4: .sub() now only accepts one argument. Use .subVectors( a, b ) instead.' ); | |
| return this.subVectors( v, w ); | |
| } | |
| this.x -= v.x; | |
| this.y -= v.y; | |
| this.z -= v.z; | |
| this.w -= v.w; | |
| return this; | |
| }, | |
| subScalar: function ( s ) { | |
| this.x -= s; | |
| this.y -= s; | |
| this.z -= s; | |
| this.w -= s; | |
| return this; | |
| }, | |
| subVectors: function ( a, b ) { | |
| this.x = a.x - b.x; | |
| this.y = a.y - b.y; | |
| this.z = a.z - b.z; | |
| this.w = a.w - b.w; | |
| return this; | |
| }, | |
| multiplyScalar: function ( scalar ) { | |
| this.x *= scalar; | |
| this.y *= scalar; | |
| this.z *= scalar; | |
| this.w *= scalar; | |
| return this; | |
| }, | |
| applyMatrix4: function ( m ) { | |
| var x = this.x, y = this.y, z = this.z, w = this.w; | |
| var e = m.elements; | |
| this.x = e[ 0 ] * x + e[ 4 ] * y + e[ 8 ] * z + e[ 12 ] * w; | |
| this.y = e[ 1 ] * x + e[ 5 ] * y + e[ 9 ] * z + e[ 13 ] * w; | |
| this.z = e[ 2 ] * x + e[ 6 ] * y + e[ 10 ] * z + e[ 14 ] * w; | |
| this.w = e[ 3 ] * x + e[ 7 ] * y + e[ 11 ] * z + e[ 15 ] * w; | |
| return this; | |
| }, | |
| divideScalar: function ( scalar ) { | |
| return this.multiplyScalar( 1 / scalar ); | |
| }, | |
| setAxisAngleFromQuaternion: function ( q ) { | |
| // http://www.euclideanspace.com/maths/geometry/rotations/conversions/quaternionToAngle/index.htm | |
| // q is assumed to be normalized | |
| this.w = 2 * Math.acos( q.w ); | |
| var s = Math.sqrt( 1 - q.w * q.w ); | |
| if ( s < 0.0001 ) { | |
| this.x = 1; | |
| this.y = 0; | |
| this.z = 0; | |
| } else { | |
| this.x = q.x / s; | |
| this.y = q.y / s; | |
| this.z = q.z / s; | |
| } | |
| return this; | |
| }, | |
| setAxisAngleFromRotationMatrix: function ( m ) { | |
| // http://www.euclideanspace.com/maths/geometry/rotations/conversions/matrixToAngle/index.htm | |
| // assumes the upper 3x3 of m is a pure rotation matrix (i.e, unscaled) | |
| var angle, x, y, z, // variables for result | |
| epsilon = 0.01, // margin to allow for rounding errors | |
| epsilon2 = 0.1, // margin to distinguish between 0 and 180 degrees | |
| te = m.elements, | |
| m11 = te[ 0 ], m12 = te[ 4 ], m13 = te[ 8 ], | |
| m21 = te[ 1 ], m22 = te[ 5 ], m23 = te[ 9 ], | |
| m31 = te[ 2 ], m32 = te[ 6 ], m33 = te[ 10 ]; | |
| if ( ( Math.abs( m12 - m21 ) < epsilon ) && | |
| ( Math.abs( m13 - m31 ) < epsilon ) && | |
| ( Math.abs( m23 - m32 ) < epsilon ) ) { | |
| // singularity found | |
| // first check for identity matrix which must have +1 for all terms | |
| // in leading diagonal and zero in other terms | |
| if ( ( Math.abs( m12 + m21 ) < epsilon2 ) && | |
| ( Math.abs( m13 + m31 ) < epsilon2 ) && | |
| ( Math.abs( m23 + m32 ) < epsilon2 ) && | |
| ( Math.abs( m11 + m22 + m33 - 3 ) < epsilon2 ) ) { | |
| // this singularity is identity matrix so angle = 0 | |
| this.set( 1, 0, 0, 0 ); | |
| return this; // zero angle, arbitrary axis | |
| } | |
| // otherwise this singularity is angle = 180 | |
| angle = Math.PI; | |
| var xx = ( m11 + 1 ) / 2; | |
| var yy = ( m22 + 1 ) / 2; | |
| var zz = ( m33 + 1 ) / 2; | |
| var xy = ( m12 + m21 ) / 4; | |
| var xz = ( m13 + m31 ) / 4; | |
| var yz = ( m23 + m32 ) / 4; | |
| if ( ( xx > yy ) && ( xx > zz ) ) { | |
| // m11 is the largest diagonal term | |
| if ( xx < epsilon ) { | |
| x = 0; | |
| y = 0.707106781; | |
| z = 0.707106781; | |
| } else { | |
| x = Math.sqrt( xx ); | |
| y = xy / x; | |
| z = xz / x; | |
| } | |
| } else if ( yy > zz ) { | |
| // m22 is the largest diagonal term | |
| if ( yy < epsilon ) { | |
| x = 0.707106781; | |
| y = 0; | |
| z = 0.707106781; | |
| } else { | |
| y = Math.sqrt( yy ); | |
| x = xy / y; | |
| z = yz / y; | |
| } | |
| } else { | |
| // m33 is the largest diagonal term so base result on this | |
| if ( zz < epsilon ) { | |
| x = 0.707106781; | |
| y = 0.707106781; | |
| z = 0; | |
| } else { | |
| z = Math.sqrt( zz ); | |
| x = xz / z; | |
| y = yz / z; | |
| } | |
| } | |
| this.set( x, y, z, angle ); | |
| return this; // return 180 deg rotation | |
| } | |
| // as we have reached here there are no singularities so we can handle normally | |
| var s = Math.sqrt( ( m32 - m23 ) * ( m32 - m23 ) + | |
| ( m13 - m31 ) * ( m13 - m31 ) + | |
| ( m21 - m12 ) * ( m21 - m12 ) ); // used to normalize | |
| if ( Math.abs( s ) < 0.001 ) s = 1; | |
| // prevent divide by zero, should not happen if matrix is orthogonal and should be | |
| // caught by singularity test above, but I've left it in just in case | |
| this.x = ( m32 - m23 ) / s; | |
| this.y = ( m13 - m31 ) / s; | |
| this.z = ( m21 - m12 ) / s; | |
| this.w = Math.acos( ( m11 + m22 + m33 - 1 ) / 2 ); | |
| return this; | |
| }, | |
| min: function ( v ) { | |
| this.x = Math.min( this.x, v.x ); | |
| this.y = Math.min( this.y, v.y ); | |
| this.z = Math.min( this.z, v.z ); | |
| this.w = Math.min( this.w, v.w ); | |
| return this; | |
| }, | |
| max: function ( v ) { | |
| this.x = Math.max( this.x, v.x ); | |
| this.y = Math.max( this.y, v.y ); | |
| this.z = Math.max( this.z, v.z ); | |
| this.w = Math.max( this.w, v.w ); | |
| return this; | |
| }, | |
| clamp: function ( min, max ) { | |
| // assumes min < max, componentwise | |
| this.x = Math.max( min.x, Math.min( max.x, this.x ) ); | |
| this.y = Math.max( min.y, Math.min( max.y, this.y ) ); | |
| this.z = Math.max( min.z, Math.min( max.z, this.z ) ); | |
| this.w = Math.max( min.w, Math.min( max.w, this.w ) ); | |
| return this; | |
| }, | |
| clampScalar: function () { | |
| var min, max; | |
| return function clampScalar( minVal, maxVal ) { | |
| if ( min === undefined ) { | |
| min = new Vector4(); | |
| max = new Vector4(); | |
| } | |
| min.set( minVal, minVal, minVal, minVal ); | |
| max.set( maxVal, maxVal, maxVal, maxVal ); | |
| return this.clamp( min, max ); | |
| }; | |
| }(), | |
| clampLength: function ( min, max ) { | |
| var length = this.length(); | |
| return this.divideScalar( length || 1 ).multiplyScalar( Math.max( min, Math.min( max, length ) ) ); | |
| }, | |
| floor: function () { | |
| this.x = Math.floor( this.x ); | |
| this.y = Math.floor( this.y ); | |
| this.z = Math.floor( this.z ); | |
| this.w = Math.floor( this.w ); | |
| return this; | |
| }, | |
| ceil: function () { | |
| this.x = Math.ceil( this.x ); | |
| this.y = Math.ceil( this.y ); | |
| this.z = Math.ceil( this.z ); | |
| this.w = Math.ceil( this.w ); | |
| return this; | |
| }, | |
| round: function () { | |
| this.x = Math.round( this.x ); | |
| this.y = Math.round( this.y ); | |
| this.z = Math.round( this.z ); | |
| this.w = Math.round( this.w ); | |
| return this; | |
| }, | |
| roundToZero: function () { | |
| this.x = ( this.x < 0 ) ? Math.ceil( this.x ) : Math.floor( this.x ); | |
| this.y = ( this.y < 0 ) ? Math.ceil( this.y ) : Math.floor( this.y ); | |
| this.z = ( this.z < 0 ) ? Math.ceil( this.z ) : Math.floor( this.z ); | |
| this.w = ( this.w < 0 ) ? Math.ceil( this.w ) : Math.floor( this.w ); | |
| return this; | |
| }, | |
| negate: function () { | |
| this.x = - this.x; | |
| this.y = - this.y; | |
| this.z = - this.z; | |
| this.w = - this.w; | |
| return this; | |
| }, | |
| dot: function ( v ) { | |
| return this.x * v.x + this.y * v.y + this.z * v.z + this.w * v.w; | |
| }, | |
| lengthSq: function () { | |
| return this.x * this.x + this.y * this.y + this.z * this.z + this.w * this.w; | |
| }, | |
| length: function () { | |
| return Math.sqrt( this.x * this.x + this.y * this.y + this.z * this.z + this.w * this.w ); | |
| }, | |
| manhattanLength: function () { | |
| return Math.abs( this.x ) + Math.abs( this.y ) + Math.abs( this.z ) + Math.abs( this.w ); | |
| }, | |
| normalize: function () { | |
| return this.divideScalar( this.length() || 1 ); | |
| }, | |
| setLength: function ( length ) { | |
| return this.normalize().multiplyScalar( length ); | |
| }, | |
| lerp: function ( v, alpha ) { | |
| this.x += ( v.x - this.x ) * alpha; | |
| this.y += ( v.y - this.y ) * alpha; | |
| this.z += ( v.z - this.z ) * alpha; | |
| this.w += ( v.w - this.w ) * alpha; | |
| return this; | |
| }, | |
| lerpVectors: function ( v1, v2, alpha ) { | |
| return this.subVectors( v2, v1 ).multiplyScalar( alpha ).add( v1 ); | |
| }, | |
| equals: function ( v ) { | |
| return ( ( v.x === this.x ) && ( v.y === this.y ) && ( v.z === this.z ) && ( v.w === this.w ) ); | |
| }, | |
| fromArray: function ( array, offset ) { | |
| if ( offset === undefined ) offset = 0; | |
| this.x = array[ offset ]; | |
| this.y = array[ offset + 1 ]; | |
| this.z = array[ offset + 2 ]; | |
| this.w = array[ offset + 3 ]; | |
| return this; | |
| }, | |
| toArray: function ( array, offset ) { | |
| if ( array === undefined ) array = []; | |
| if ( offset === undefined ) offset = 0; | |
| array[ offset ] = this.x; | |
| array[ offset + 1 ] = this.y; | |
| array[ offset + 2 ] = this.z; | |
| array[ offset + 3 ] = this.w; | |
| return array; | |
| }, | |
| fromBufferAttribute: function ( attribute, index, offset ) { | |
| if ( offset !== undefined ) { | |
| console.warn( 'THREE.Vector4: offset has been removed from .fromBufferAttribute().' ); | |
| } | |
| this.x = attribute.getX( index ); | |
| this.y = attribute.getY( index ); | |
| this.z = attribute.getZ( index ); | |
| this.w = attribute.getW( index ); | |
| return this; | |
| } | |
| } ); | |
| /** | |
| * @author szimek / https://github.com/szimek/ | |
| * @author alteredq / http://alteredqualia.com/ | |
| * @author Marius Kintel / https://github.com/kintel | |
| */ | |
| /* | |
| In options, we can specify: | |
| * Texture parameters for an auto-generated target texture | |
| * depthBuffer/stencilBuffer: Booleans to indicate if we should generate these buffers | |
| */ | |
| function WebGLRenderTarget( width, height, options ) { | |
| this.uuid = _Math.generateUUID(); | |
| this.width = width; | |
| this.height = height; | |
| this.scissor = new Vector4( 0, 0, width, height ); | |
| this.scissorTest = false; | |
| this.viewport = new Vector4( 0, 0, width, height ); | |
| options = options || {}; | |
| if ( options.minFilter === undefined ) options.minFilter = LinearFilter; | |
| this.texture = new Texture( undefined, undefined, options.wrapS, options.wrapT, options.magFilter, options.minFilter, options.format, options.type, options.anisotropy, options.encoding ); | |
| this.depthBuffer = options.depthBuffer !== undefined ? options.depthBuffer : true; | |
| this.stencilBuffer = options.stencilBuffer !== undefined ? options.stencilBuffer : true; | |
| this.depthTexture = options.depthTexture !== undefined ? options.depthTexture : null; | |
| } | |
| WebGLRenderTarget.prototype = Object.assign( Object.create( EventDispatcher.prototype ), { | |
| constructor: WebGLRenderTarget, | |
| isWebGLRenderTarget: true, | |
| setSize: function ( width, height ) { | |
| if ( this.width !== width || this.height !== height ) { | |
| this.width = width; | |
| this.height = height; | |
| this.dispose(); | |
| } | |
| this.viewport.set( 0, 0, width, height ); | |
| this.scissor.set( 0, 0, width, height ); | |
| }, | |
| clone: function () { | |
| return new this.constructor().copy( this ); | |
| }, | |
| copy: function ( source ) { | |
| this.width = source.width; | |
| this.height = source.height; | |
| this.viewport.copy( source.viewport ); | |
| this.texture = source.texture.clone(); | |
| this.depthBuffer = source.depthBuffer; | |
| this.stencilBuffer = source.stencilBuffer; | |
| this.depthTexture = source.depthTexture; | |
| return this; | |
| }, | |
| dispose: function () { | |
| this.dispatchEvent( { type: 'dispose' } ); | |
| } | |
| } ); | |
| /** | |
| * @author alteredq / http://alteredqualia.com | |
| */ | |
| function WebGLRenderTargetCube( width, height, options ) { | |
| WebGLRenderTarget.call( this, width, height, options ); | |
| this.activeCubeFace = 0; // PX 0, NX 1, PY 2, NY 3, PZ 4, NZ 5 | |
| this.activeMipMapLevel = 0; | |
| } | |
| WebGLRenderTargetCube.prototype = Object.create( WebGLRenderTarget.prototype ); | |
| WebGLRenderTargetCube.prototype.constructor = WebGLRenderTargetCube; | |
| WebGLRenderTargetCube.prototype.isWebGLRenderTargetCube = true; | |
| /** | |
| * @author alteredq / http://alteredqualia.com/ | |
| */ | |
| function DataTexture( data, width, height, format, type, mapping, wrapS, wrapT, magFilter, minFilter, anisotropy, encoding ) { | |
| Texture.call( this, null, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy, encoding ); | |
| this.image = { data: data, width: width, height: height }; | |
| this.magFilter = magFilter !== undefined ? magFilter : NearestFilter; | |
| this.minFilter = minFilter !== undefined ? minFilter : NearestFilter; | |
| this.generateMipmaps = false; | |
| this.flipY = false; | |
| this.unpackAlignment = 1; | |
| } | |
| DataTexture.prototype = Object.create( Texture.prototype ); | |
| DataTexture.prototype.constructor = DataTexture; | |
| DataTexture.prototype.isDataTexture = true; | |
| /** | |
| * @author bhouston / http://clara.io | |
| * @author WestLangley / http://github.com/WestLangley | |
| */ | |
| function Box3( min, max ) { | |
| this.min = ( min !== undefined ) ? min : new Vector3( + Infinity, + Infinity, + Infinity ); | |
| this.max = ( max !== undefined ) ? max : new Vector3( - Infinity, - Infinity, - Infinity ); | |
| } | |
| Object.assign( Box3.prototype, { | |
| isBox3: true, | |
| set: function ( min, max ) { | |
| this.min.copy( min ); | |
| this.max.copy( max ); | |
| return this; | |
| }, | |
| setFromArray: function ( array ) { | |
| var minX = + Infinity; | |
| var minY = + Infinity; | |
| var minZ = + Infinity; | |
| var maxX = - Infinity; | |
| var maxY = - Infinity; | |
| var maxZ = - Infinity; | |
| for ( var i = 0, l = array.length; i < l; i += 3 ) { | |
| var x = array[ i ]; | |
| var y = array[ i + 1 ]; | |
| var z = array[ i + 2 ]; | |
| if ( x < minX ) minX = x; | |
| if ( y < minY ) minY = y; | |
| if ( z < minZ ) minZ = z; | |
| if ( x > maxX ) maxX = x; | |
| if ( y > maxY ) maxY = y; | |
| if ( z > maxZ ) maxZ = z; | |
| } | |
| this.min.set( minX, minY, minZ ); | |
| this.max.set( maxX, maxY, maxZ ); | |
| return this; | |
| }, | |
| setFromBufferAttribute: function ( attribute ) { | |
| var minX = + Infinity; | |
| var minY = + Infinity; | |
| var minZ = + Infinity; | |
| var maxX = - Infinity; | |
| var maxY = - Infinity; | |
| var maxZ = - Infinity; | |
| for ( var i = 0, l = attribute.count; i < l; i ++ ) { | |
| var x = attribute.getX( i ); | |
| var y = attribute.getY( i ); | |
| var z = attribute.getZ( i ); | |
| if ( x < minX ) minX = x; | |
| if ( y < minY ) minY = y; | |
| if ( z < minZ ) minZ = z; | |
| if ( x > maxX ) maxX = x; | |
| if ( y > maxY ) maxY = y; | |
| if ( z > maxZ ) maxZ = z; | |
| } | |
| this.min.set( minX, minY, minZ ); | |
| this.max.set( maxX, maxY, maxZ ); | |
| return this; | |
| }, | |
| setFromPoints: function ( points ) { | |
| this.makeEmpty(); | |
| for ( var i = 0, il = points.length; i < il; i ++ ) { | |
| this.expandByPoint( points[ i ] ); | |
| } | |
| return this; | |
| }, | |
| setFromCenterAndSize: function () { | |
| var v1 = new Vector3(); | |
| return function setFromCenterAndSize( center, size ) { | |
| var halfSize = v1.copy( size ).multiplyScalar( 0.5 ); | |
| this.min.copy( center ).sub( halfSize ); | |
| this.max.copy( center ).add( halfSize ); | |
| return this; | |
| }; | |
| }(), | |
| setFromObject: function ( object ) { | |
| this.makeEmpty(); | |
| return this.expandByObject( object ); | |
| }, | |
| clone: function () { | |
| return new this.constructor().copy( this ); | |
| }, | |
| copy: function ( box ) { | |
| this.min.copy( box.min ); | |
| this.max.copy( box.max ); | |
| return this; | |
| }, | |
| makeEmpty: function () { | |
| this.min.x = this.min.y = this.min.z = + Infinity; | |
| this.max.x = this.max.y = this.max.z = - Infinity; | |
| return this; | |
| }, | |
| isEmpty: function () { | |
| // this is a more robust check for empty than ( volume <= 0 ) because volume can get positive with two negative axes | |
| return ( this.max.x < this.min.x ) || ( this.max.y < this.min.y ) || ( this.max.z < this.min.z ); | |
| }, | |
| getCenter: function ( optionalTarget ) { | |
| var result = optionalTarget || new Vector3(); | |
| return this.isEmpty() ? result.set( 0, 0, 0 ) : result.addVectors( this.min, this.max ).multiplyScalar( 0.5 ); | |
| }, | |
| getSize: function ( optionalTarget ) { | |
| var result = optionalTarget || new Vector3(); | |
| return this.isEmpty() ? result.set( 0, 0, 0 ) : result.subVectors( this.max, this.min ); | |
| }, | |
| expandByPoint: function ( point ) { | |
| this.min.min( point ); | |
| this.max.max( point ); | |
| return this; | |
| }, | |
| expandByVector: function ( vector ) { | |
| this.min.sub( vector ); | |
| this.max.add( vector ); | |
| return this; | |
| }, | |
| expandByScalar: function ( scalar ) { | |
| this.min.addScalar( - scalar ); | |
| this.max.addScalar( scalar ); | |
| return this; | |
| }, | |
| expandByObject: function () { | |
| // Computes the world-axis-aligned bounding box of an object (including its children), | |
| // accounting for both the object's, and children's, world transforms | |
| var scope, i, l; | |
| var v1 = new Vector3(); | |
| function traverse( node ) { | |
| var geometry = node.geometry; | |
| if ( geometry !== undefined ) { | |
| if ( geometry.isGeometry ) { | |
| var vertices = geometry.vertices; | |
| for ( i = 0, l = vertices.length; i < l; i ++ ) { | |
| v1.copy( vertices[ i ] ); | |
| v1.applyMatrix4( node.matrixWorld ); | |
| scope.expandByPoint( v1 ); | |
| } | |
| } else if ( geometry.isBufferGeometry ) { | |
| var attribute = geometry.attributes.position; | |
| if ( attribute !== undefined ) { | |
| for ( i = 0, l = attribute.count; i < l; i ++ ) { | |
| v1.fromBufferAttribute( attribute, i ).applyMatrix4( node.matrixWorld ); | |
| scope.expandByPoint( v1 ); | |
| } | |
| } | |
| } | |
| } | |
| } | |
| return function expandByObject( object ) { | |
| scope = this; | |
| object.updateMatrixWorld( true ); | |
| object.traverse( traverse ); | |
| return this; | |
| }; | |
| }(), | |
| containsPoint: function ( point ) { | |
| return point.x < this.min.x || point.x > this.max.x || | |
| point.y < this.min.y || point.y > this.max.y || | |
| point.z < this.min.z || point.z > this.max.z ? false : true; | |
| }, | |
| containsBox: function ( box ) { | |
| return this.min.x <= box.min.x && box.max.x <= this.max.x && | |
| this.min.y <= box.min.y && box.max.y <= this.max.y && | |
| this.min.z <= box.min.z && box.max.z <= this.max.z; | |
| }, | |
| getParameter: function ( point, optionalTarget ) { | |
| // This can potentially have a divide by zero if the box | |
| // has a size dimension of 0. | |
| var result = optionalTarget || new Vector3(); | |
| return result.set( | |
| ( point.x - this.min.x ) / ( this.max.x - this.min.x ), | |
| ( point.y - this.min.y ) / ( this.max.y - this.min.y ), | |
| ( point.z - this.min.z ) / ( this.max.z - this.min.z ) | |
| ); | |
| }, | |
| intersectsBox: function ( box ) { | |
| // using 6 splitting planes to rule out intersections. | |
| return box.max.x < this.min.x || box.min.x > this.max.x || | |
| box.max.y < this.min.y || box.min.y > this.max.y || | |
| box.max.z < this.min.z || box.min.z > this.max.z ? false : true; | |
| }, | |
| intersectsSphere: ( function () { | |
| var closestPoint = new Vector3(); | |
| return function intersectsSphere( sphere ) { | |
| // Find the point on the AABB closest to the sphere center. | |
| this.clampPoint( sphere.center, closestPoint ); | |
| // If that point is inside the sphere, the AABB and sphere intersect. | |
| return closestPoint.distanceToSquared( sphere.center ) <= ( sphere.radius * sphere.radius ); | |
| }; | |
| } )(), | |
| intersectsPlane: function ( plane ) { | |
| // We compute the minimum and maximum dot product values. If those values | |
| // are on the same side (back or front) of the plane, then there is no intersection. | |
| var min, max; | |
| if ( plane.normal.x > 0 ) { | |
| min = plane.normal.x * this.min.x; | |
| max = plane.normal.x * this.max.x; | |
| } else { | |
| min = plane.normal.x * this.max.x; | |
| max = plane.normal.x * this.min.x; | |
| } | |
| if ( plane.normal.y > 0 ) { | |
| min += plane.normal.y * this.min.y; | |
| max += plane.normal.y * this.max.y; | |
| } else { | |
| min += plane.normal.y * this.max.y; | |
| max += plane.normal.y * this.min.y; | |
| } | |
| if ( plane.normal.z > 0 ) { | |
| min += plane.normal.z * this.min.z; | |
| max += plane.normal.z * this.max.z; | |
| } else { | |
| min += plane.normal.z * this.max.z; | |
| max += plane.normal.z * this.min.z; | |
| } | |
| return ( min <= plane.constant && max >= plane.constant ); | |
| }, | |
| intersectsTriangle: ( function () { | |
| // triangle centered vertices | |
| var v0 = new Vector3(); | |
| var v1 = new Vector3(); | |
| var v2 = new Vector3(); | |
| // triangle edge vectors | |
| var f0 = new Vector3(); | |
| var f1 = new Vector3(); | |
| var f2 = new Vector3(); | |
| var testAxis = new Vector3(); | |
| var center = new Vector3(); | |
| var extents = new Vector3(); | |
| var triangleNormal = new Vector3(); | |
| function satForAxes( axes ) { | |
| var i, j; | |
| for ( i = 0, j = axes.length - 3; i <= j; i += 3 ) { | |
| testAxis.fromArray( axes, i ); | |
| // project the aabb onto the seperating axis | |
| var r = extents.x * Math.abs( testAxis.x ) + extents.y * Math.abs( testAxis.y ) + extents.z * Math.abs( testAxis.z ); | |
| // project all 3 vertices of the triangle onto the seperating axis | |
| var p0 = v0.dot( testAxis ); | |
| var p1 = v1.dot( testAxis ); | |
| var p2 = v2.dot( testAxis ); | |
| // actual test, basically see if either of the most extreme of the triangle points intersects r | |
| if ( Math.max( - Math.max( p0, p1, p2 ), Math.min( p0, p1, p2 ) ) > r ) { | |
| // points of the projected triangle are outside the projected half-length of the aabb | |
| // the axis is seperating and we can exit | |
| return false; | |
| } | |
| } | |
| return true; | |
| } | |
| return function intersectsTriangle( triangle ) { | |
| if ( this.isEmpty() ) { | |
| return false; | |
| } | |
| // compute box center and extents | |
| this.getCenter( center ); | |
| extents.subVectors( this.max, center ); | |
| // translate triangle to aabb origin | |
| v0.subVectors( triangle.a, center ); | |
| v1.subVectors( triangle.b, center ); | |
| v2.subVectors( triangle.c, center ); | |
| // compute edge vectors for triangle | |
| f0.subVectors( v1, v0 ); | |
| f1.subVectors( v2, v1 ); | |
| f2.subVectors( v0, v2 ); | |
| // test against axes that are given by cross product combinations of the edges of the triangle and the edges of the aabb | |
| // make an axis testing of each of the 3 sides of the aabb against each of the 3 sides of the triangle = 9 axis of separation | |
| // axis_ij = u_i x f_j (u0, u1, u2 = face normals of aabb = x,y,z axes vectors since aabb is axis aligned) | |
| var axes = [ | |
| 0, - f0.z, f0.y, 0, - f1.z, f1.y, 0, - f2.z, f2.y, | |
| f0.z, 0, - f0.x, f1.z, 0, - f1.x, f2.z, 0, - f2.x, | |
| - f0.y, f0.x, 0, - f1.y, f1.x, 0, - f2.y, f2.x, 0 | |
| ]; | |
| if ( ! satForAxes( axes ) ) { | |
| return false; | |
| } | |
| // test 3 face normals from the aabb | |
| axes = [ 1, 0, 0, 0, 1, 0, 0, 0, 1 ]; | |
| if ( ! satForAxes( axes ) ) { | |
| return false; | |
| } | |
| // finally testing the face normal of the triangle | |
| // use already existing triangle edge vectors here | |
| triangleNormal.crossVectors( f0, f1 ); | |
| axes = [ triangleNormal.x, triangleNormal.y, triangleNormal.z ]; | |
| return satForAxes( axes ); | |
| }; | |
| } )(), | |
| clampPoint: function ( point, optionalTarget ) { | |
| var result = optionalTarget || new Vector3(); | |
| return result.copy( point ).clamp( this.min, this.max ); | |
| }, | |
| distanceToPoint: function () { | |
| var v1 = new Vector3(); | |
| return function distanceToPoint( point ) { | |
| var clampedPoint = v1.copy( point ).clamp( this.min, this.max ); | |
| return clampedPoint.sub( point ).length(); | |
| }; | |
| }(), | |
| getBoundingSphere: function () { | |
| var v1 = new Vector3(); | |
| return function getBoundingSphere( optionalTarget ) { | |
| var result = optionalTarget || new Sphere(); | |
| this.getCenter( result.center ); | |
| result.radius = this.getSize( v1 ).length() * 0.5; | |
| return result; | |
| }; | |
| }(), | |
| intersect: function ( box ) { | |
| this.min.max( box.min ); | |
| this.max.min( box.max ); | |
| // ensure that if there is no overlap, the result is fully empty, not slightly empty with non-inf/+inf values that will cause subsequence intersects to erroneously return valid values. | |
| if ( this.isEmpty() ) this.makeEmpty(); | |
| return this; | |
| }, | |
| union: function ( box ) { | |
| this.min.min( box.min ); | |
| this.max.max( box.max ); | |
| return this; | |
| }, | |
| applyMatrix4: function () { | |
| var points = [ | |
| new Vector3(), | |
| new Vector3(), | |
| new Vector3(), | |
| new Vector3(), | |
| new Vector3(), | |
| new Vector3(), | |
| new Vector3(), | |
| new Vector3() | |
| ]; | |
| return function applyMatrix4( matrix ) { | |
| // transform of empty box is an empty box. | |
| if ( this.isEmpty() ) return this; | |
| // NOTE: I am using a binary pattern to specify all 2^3 combinations below | |
| points[ 0 ].set( this.min.x, this.min.y, this.min.z ).applyMatrix4( matrix ); // 000 | |
| points[ 1 ].set( this.min.x, this.min.y, this.max.z ).applyMatrix4( matrix ); // 001 | |
| points[ 2 ].set( this.min.x, this.max.y, this.min.z ).applyMatrix4( matrix ); // 010 | |
| points[ 3 ].set( this.min.x, this.max.y, this.max.z ).applyMatrix4( matrix ); // 011 | |
| points[ 4 ].set( this.max.x, this.min.y, this.min.z ).applyMatrix4( matrix ); // 100 | |
| points[ 5 ].set( this.max.x, this.min.y, this.max.z ).applyMatrix4( matrix ); // 101 | |
| points[ 6 ].set( this.max.x, this.max.y, this.min.z ).applyMatrix4( matrix ); // 110 | |
| points[ 7 ].set( this.max.x, this.max.y, this.max.z ).applyMatrix4( matrix ); // 111 | |
| this.setFromPoints( points ); | |
| return this; | |
| }; | |
| }(), | |
| translate: function ( offset ) { | |
| this.min.add( offset ); | |
| this.max.add( offset ); | |
| return this; | |
| }, | |
| equals: function ( box ) { | |
| return box.min.equals( this.min ) && box.max.equals( this.max ); | |
| } | |
| } ); | |
| /** | |
| * @author bhouston / http://clara.io | |
| * @author mrdoob / http://mrdoob.com/ | |
| */ | |
| function Sphere( center, radius ) { | |
| this.center = ( center !== undefined ) ? center : new Vector3(); | |
| this.radius = ( radius !== undefined ) ? radius : 0; | |
| } | |
| Object.assign( Sphere.prototype, { | |
| set: function ( center, radius ) { | |
| this.center.copy( center ); | |
| this.radius = radius; | |
| return this; | |
| }, | |
| setFromPoints: function () { | |
| var box = new Box3(); | |
| return function setFromPoints( points, optionalCenter ) { | |
| var center = this.center; | |
| if ( optionalCenter !== undefined ) { | |
| center.copy( optionalCenter ); | |
| } else { | |
| box.setFromPoints( points ).getCenter( center ); | |
| } | |
| var maxRadiusSq = 0; | |
| for ( var i = 0, il = points.length; i < il; i ++ ) { | |
| maxRadiusSq = Math.max( maxRadiusSq, center.distanceToSquared( points[ i ] ) ); | |
| } | |
| this.radius = Math.sqrt( maxRadiusSq ); | |
| return this; | |
| }; | |
| }(), | |
| clone: function () { | |
| return new this.constructor().copy( this ); | |
| }, | |
| copy: function ( sphere ) { | |
| this.center.copy( sphere.center ); | |
| this.radius = sphere.radius; | |
| return this; | |
| }, | |
| empty: function () { | |
| return ( this.radius <= 0 ); | |
| }, | |
| containsPoint: function ( point ) { | |
| return ( point.distanceToSquared( this.center ) <= ( this.radius * this.radius ) ); | |
| }, | |
| distanceToPoint: function ( point ) { | |
| return ( point.distanceTo( this.center ) - this.radius ); | |
| }, | |
| intersectsSphere: function ( sphere ) { | |
| var radiusSum = this.radius + sphere.radius; | |
| return sphere.center.distanceToSquared( this.center ) <= ( radiusSum * radiusSum ); | |
| }, | |
| intersectsBox: function ( box ) { | |
| return box.intersectsSphere( this ); | |
| }, | |
| intersectsPlane: function ( plane ) { | |
| return Math.abs( plane.distanceToPoint( this.center ) ) <= this.radius; | |
| }, | |
| clampPoint: function ( point, optionalTarget ) { | |
| var deltaLengthSq = this.center.distanceToSquared( point ); | |
| var result = optionalTarget || new Vector3(); | |
| result.copy( point ); | |
| if ( deltaLengthSq > ( this.radius * this.radius ) ) { | |
| result.sub( this.center ).normalize(); | |
| result.multiplyScalar( this.radius ).add( this.center ); | |
| } | |
| return result; | |
| }, | |
| getBoundingBox: function ( optionalTarget ) { | |
| var box = optionalTarget || new Box3(); | |
| box.set( this.center, this.center ); | |
| box.expandByScalar( this.radius ); | |
| return box; | |
| }, | |
| applyMatrix4: function ( matrix ) { | |
| this.center.applyMatrix4( matrix ); | |
| this.radius = this.radius * matrix.getMaxScaleOnAxis(); | |
| return this; | |
| }, | |
| translate: function ( offset ) { | |
| this.center.add( offset ); | |
| return this; | |
| }, | |
| equals: function ( sphere ) { | |
| return sphere.center.equals( this.center ) && ( sphere.radius === this.radius ); | |
| } | |
| } ); | |
| /** | |
| * @author bhouston / http://clara.io | |
| */ | |
| function Plane( normal, constant ) { | |
| // normal is assumed to be normalized | |
| this.normal = ( normal !== undefined ) ? normal : new Vector3( 1, 0, 0 ); | |
| this.constant = ( constant !== undefined ) ? constant : 0; | |
| } | |
| Object.assign( Plane.prototype, { | |
| set: function ( normal, constant ) { | |
| this.normal.copy( normal ); | |
| this.constant = constant; | |
| return this; | |
| }, | |
| setComponents: function ( x, y, z, w ) { | |
| this.normal.set( x, y, z ); | |
| this.constant = w; | |
| return this; | |
| }, | |
| setFromNormalAndCoplanarPoint: function ( normal, point ) { | |
| this.normal.copy( normal ); | |
| this.constant = - point.dot( this.normal ); | |
| return this; | |
| }, | |
| setFromCoplanarPoints: function () { | |
| var v1 = new Vector3(); | |
| var v2 = new Vector3(); | |
| return function setFromCoplanarPoints( a, b, c ) { | |
| var normal = v1.subVectors( c, b ).cross( v2.subVectors( a, b ) ).normalize(); | |
| // Q: should an error be thrown if normal is zero (e.g. degenerate plane)? | |
| this.setFromNormalAndCoplanarPoint( normal, a ); | |
| return this; | |
| }; | |
| }(), | |
| clone: function () { | |
| return new this.constructor().copy( this ); | |
| }, | |
| copy: function ( plane ) { | |
| this.normal.copy( plane.normal ); | |
| this.constant = plane.constant; | |
| return this; | |
| }, | |
| normalize: function () { | |
| // Note: will lead to a divide by zero if the plane is invalid. | |
| var inverseNormalLength = 1.0 / this.normal.length(); | |
| this.normal.multiplyScalar( inverseNormalLength ); | |
| this.constant *= inverseNormalLength; | |
| return this; | |
| }, | |
| negate: function () { | |
| this.constant *= - 1; | |
| this.normal.negate(); | |
| return this; | |
| }, | |
| distanceToPoint: function ( point ) { | |
| return this.normal.dot( point ) + this.constant; | |
| }, | |
| distanceToSphere: function ( sphere ) { | |
| return this.distanceToPoint( sphere.center ) - sphere.radius; | |
| }, | |
| projectPoint: function ( point, optionalTarget ) { | |
| var result = optionalTarget || new Vector3(); | |
| return result.copy( this.normal ).multiplyScalar( - this.distanceToPoint( point ) ).add( point ); | |
| }, | |
| intersectLine: function () { | |
| var v1 = new Vector3(); | |
| return function intersectLine( line, optionalTarget ) { | |
| var result = optionalTarget || new Vector3(); | |
| var direction = line.delta( v1 ); | |
| var denominator = this.normal.dot( direction ); | |
| if ( denominator === 0 ) { | |
| // line is coplanar, return origin | |
| if ( this.distanceToPoint( line.start ) === 0 ) { | |
| return result.copy( line.start ); | |
| } | |
| // Unsure if this is the correct method to handle this case. | |
| return undefined; | |
| } | |
| var t = - ( line.start.dot( this.normal ) + this.constant ) / denominator; | |
| if ( t < 0 || t > 1 ) { | |
| return undefined; | |
| } | |
| return result.copy( direction ).multiplyScalar( t ).add( line.start ); | |
| }; | |
| }(), | |
| intersectsLine: function ( line ) { | |
| // Note: this tests if a line intersects the plane, not whether it (or its end-points) are coplanar with it. | |
| var startSign = this.distanceToPoint( line.start ); | |
| var endSign = this.distanceToPoint( line.end ); | |
| return ( startSign < 0 && endSign > 0 ) || ( endSign < 0 && startSign > 0 ); | |
| }, | |
| intersectsBox: function ( box ) { | |
| return box.intersectsPlane( this ); | |
| }, | |
| intersectsSphere: function ( sphere ) { | |
| return sphere.intersectsPlane( this ); | |
| }, | |
| coplanarPoint: function ( optionalTarget ) { | |
| var result = optionalTarget || new Vector3(); | |
| return result.copy( this.normal ).multiplyScalar( - this.constant ); | |
| }, | |
| applyMatrix4: function () { | |
| var v1 = new Vector3(); | |
| var m1 = new Matrix3(); | |
| return function applyMatrix4( matrix, optionalNormalMatrix ) { | |
| var normalMatrix = optionalNormalMatrix || m1.getNormalMatrix( matrix ); | |
| var referencePoint = this.coplanarPoint( v1 ).applyMatrix4( matrix ); | |
| var normal = this.normal.applyMatrix3( normalMatrix ).normalize(); | |
| this.constant = - referencePoint.dot( normal ); | |
| return this; | |
| }; | |
| }(), | |
| translate: function ( offset ) { | |
| this.constant -= offset.dot( this.normal ); | |
| return this; | |
| }, | |
| equals: function ( plane ) { | |
| return plane.normal.equals( this.normal ) && ( plane.constant === this.constant ); | |
| } | |
| } ); | |
| /** | |
| * @author mrdoob / http://mrdoob.com/ | |
| * @author alteredq / http://alteredqualia.com/ | |
| * @author bhouston / http://clara.io | |
| */ | |
| function Frustum( p0, p1, p2, p3, p4, p5 ) { | |
| this.planes = [ | |
| ( p0 !== undefined ) ? p0 : new Plane(), | |
| ( p1 !== undefined ) ? p1 : new Plane(), | |
| ( p2 !== undefined ) ? p2 : new Plane(), | |
| ( p3 !== undefined ) ? p3 : new Plane(), | |
| ( p4 !== undefined ) ? p4 : new Plane(), | |
| ( p5 !== undefined ) ? p5 : new Plane() | |
| ]; | |
| } | |
| Object.assign( Frustum.prototype, { | |
| set: function ( p0, p1, p2, p3, p4, p5 ) { | |
| var planes = this.planes; | |
| planes[ 0 ].copy( p0 ); | |
| planes[ 1 ].copy( p1 ); | |
| planes[ 2 ].copy( p2 ); | |
| planes[ 3 ].copy( p3 ); | |
| planes[ 4 ].copy( p4 ); | |
| planes[ 5 ].copy( p5 ); | |
| return this; | |
| }, | |
| clone: function () { | |
| return new this.constructor().copy( this ); | |
| }, | |
| copy: function ( frustum ) { | |
| var planes = this.planes; | |
| for ( var i = 0; i < 6; i ++ ) { | |
| planes[ i ].copy( frustum.planes[ i ] ); | |
| } | |
| return this; | |
| }, | |
| setFromMatrix: function ( m ) { | |
| var planes = this.planes; | |
| var me = m.elements; | |
| var me0 = me[ 0 ], me1 = me[ 1 ], me2 = me[ 2 ], me3 = me[ 3 ]; | |
| var me4 = me[ 4 ], me5 = me[ 5 ], me6 = me[ 6 ], me7 = me[ 7 ]; | |
| var me8 = me[ 8 ], me9 = me[ 9 ], me10 = me[ 10 ], me11 = me[ 11 ]; | |
| var me12 = me[ 12 ], me13 = me[ 13 ], me14 = me[ 14 ], me15 = me[ 15 ]; | |
| planes[ 0 ].setComponents( me3 - me0, me7 - me4, me11 - me8, me15 - me12 ).normalize(); | |
| planes[ 1 ].setComponents( me3 + me0, me7 + me4, me11 + me8, me15 + me12 ).normalize(); | |
| planes[ 2 ].setComponents( me3 + me1, me7 + me5, me11 + me9, me15 + me13 ).normalize(); | |
| planes[ 3 ].setComponents( me3 - me1, me7 - me5, me11 - me9, me15 - me13 ).normalize(); | |
| planes[ 4 ].setComponents( me3 - me2, me7 - me6, me11 - me10, me15 - me14 ).normalize(); | |
| planes[ 5 ].setComponents( me3 + me2, me7 + me6, me11 + me10, me15 + me14 ).normalize(); | |
| return this; | |
| }, | |
| intersectsObject: function () { | |
| var sphere = new Sphere(); | |
| return function intersectsObject( object ) { | |
| var geometry = object.geometry; | |
| if ( geometry.boundingSphere === null ) | |
| geometry.computeBoundingSphere(); | |
| sphere.copy( geometry.boundingSphere ) | |
| .applyMatrix4( object.matrixWorld ); | |
| return this.intersectsSphere( sphere ); | |
| }; | |
| }(), | |
| intersectsSprite: function () { | |
| var sphere = new Sphere(); | |
| return function intersectsSprite( sprite ) { | |
| sphere.center.set( 0, 0, 0 ); | |
| sphere.radius = 0.7071067811865476; | |
| sphere.applyMatrix4( sprite.matrixWorld ); | |
| return this.intersectsSphere( sphere ); | |
| }; | |
| }(), | |
| intersectsSphere: function ( sphere ) { | |
| var planes = this.planes; | |
| var center = sphere.center; | |
| var negRadius = - sphere.radius; | |
| for ( var i = 0; i < 6; i ++ ) { | |
| var distance = planes[ i ].distanceToPoint( center ); | |
| if ( distance < negRadius ) { | |
| return false; | |
| } | |
| } | |
| return true; | |
| }, | |
| intersectsBox: function () { | |
| var p1 = new Vector3(), | |
| p2 = new Vector3(); | |
| return function intersectsBox( box ) { | |
| var planes = this.planes; | |
| for ( var i = 0; i < 6; i ++ ) { | |
| var plane = planes[ i ]; | |
| p1.x = plane.normal.x > 0 ? box.min.x : box.max.x; | |
| p2.x = plane.normal.x > 0 ? box.max.x : box.min.x; | |
| p1.y = plane.normal.y > 0 ? box.min.y : box.max.y; | |
| p2.y = plane.normal.y > 0 ? box.max.y : box.min.y; | |
| p1.z = plane.normal.z > 0 ? box.min.z : box.max.z; | |
| p2.z = plane.normal.z > 0 ? box.max.z : box.min.z; | |
| var d1 = plane.distanceToPoint( p1 ); | |
| var d2 = plane.distanceToPoint( p2 ); | |
| // if both outside plane, no intersection | |
| if ( d1 < 0 && d2 < 0 ) { | |
| return false; | |
| } | |
| } | |
| return true; | |
| }; | |
| }(), | |
| containsPoint: function ( point ) { | |
| var planes = this.planes; | |
| for ( var i = 0; i < 6; i ++ ) { | |
| if ( planes[ i ].distanceToPoint( point ) < 0 ) { | |
| return false; | |
| } | |
| } | |
| return true; | |
| } | |
| } ); | |
| var alphamap_fragment = "#ifdef USE_ALPHAMAP\n\tdiffuseColor.a *= texture2D( alphaMap, vUv ).g;\n#endif\n"; | |
| var alphamap_pars_fragment = "#ifdef USE_ALPHAMAP\n\tuniform sampler2D alphaMap;\n#endif\n"; | |
| var alphatest_fragment = "#ifdef ALPHATEST\n\tif ( diffuseColor.a < ALPHATEST ) discard;\n#endif\n"; | |
| var aomap_fragment = "#ifdef USE_AOMAP\n\tfloat ambientOcclusion = ( texture2D( aoMap, vUv2 ).r - 1.0 ) * aoMapIntensity + 1.0;\n\treflectedLight.indirectDiffuse *= ambientOcclusion;\n\t#if defined( USE_ENVMAP ) && defined( PHYSICAL )\n\t\tfloat dotNV = saturate( dot( geometry.normal, geometry.viewDir ) );\n\t\treflectedLight.indirectSpecular *= computeSpecularOcclusion( dotNV, ambientOcclusion, material.specularRoughness );\n\t#endif\n#endif\n"; | |
| var aomap_pars_fragment = "#ifdef USE_AOMAP\n\tuniform sampler2D aoMap;\n\tuniform float aoMapIntensity;\n#endif"; | |
| var begin_vertex = "\nvec3 transformed = vec3( position );\n"; | |
| var beginnormal_vertex = "\nvec3 objectNormal = vec3( normal );\n"; | |
| var bsdfs = "float punctualLightIntensityToIrradianceFactor( const in float lightDistance, const in float cutoffDistance, const in float decayExponent ) {\n\tif( decayExponent > 0.0 ) {\n#if defined ( PHYSICALLY_CORRECT_LIGHTS )\n\t\tfloat distanceFalloff = 1.0 / max( pow( lightDistance, decayExponent ), 0.01 );\n\t\tfloat maxDistanceCutoffFactor = pow2( saturate( 1.0 - pow4( lightDistance / cutoffDistance ) ) );\n\t\treturn distanceFalloff * maxDistanceCutoffFactor;\n#else\n\t\treturn pow( saturate( -lightDistance / cutoffDistance + 1.0 ), decayExponent );\n#endif\n\t}\n\treturn 1.0;\n}\nvec3 BRDF_Diffuse_Lambert( const in vec3 diffuseColor ) {\n\treturn RECIPROCAL_PI * diffuseColor;\n}\nvec3 F_Schlick( const in vec3 specularColor, const in float dotLH ) {\n\tfloat fresnel = exp2( ( -5.55473 * dotLH - 6.98316 ) * dotLH );\n\treturn ( 1.0 - specularColor ) * fresnel + specularColor;\n}\nfloat G_GGX_Smith( const in float alpha, const in float dotNL, const in float dotNV ) {\n\tfloat a2 = pow2( alpha );\n\tfloat gl = dotNL + sqrt( a2 + ( 1.0 - a2 ) * pow2( dotNL ) );\n\tfloat gv = dotNV + sqrt( a2 + ( 1.0 - a2 ) * pow2( dotNV ) );\n\treturn 1.0 / ( gl * gv );\n}\nfloat G_GGX_SmithCorrelated( const in float alpha, const in float dotNL, const in float dotNV ) {\n\tfloat a2 = pow2( alpha );\n\tfloat gv = dotNL * sqrt( a2 + ( 1.0 - a2 ) * pow2( dotNV ) );\n\tfloat gl = dotNV * sqrt( a2 + ( 1.0 - a2 ) * pow2( dotNL ) );\n\treturn 0.5 / max( gv + gl, EPSILON );\n}\nfloat D_GGX( const in float alpha, const in float dotNH ) {\n\tfloat a2 = pow2( alpha );\n\tfloat denom = pow2( dotNH ) * ( a2 - 1.0 ) + 1.0;\n\treturn RECIPROCAL_PI * a2 / pow2( denom );\n}\nvec3 BRDF_Specular_GGX( const in IncidentLight incidentLight, const in GeometricContext geometry, const in vec3 specularColor, const in float roughness ) {\n\tfloat alpha = pow2( roughness );\n\tvec3 halfDir = normalize( incidentLight.direction + geometry.viewDir );\n\tfloat dotNL = saturate( dot( geometry.normal, incidentLight.direction ) );\n\tfloat dotNV = saturate( dot( geometry.normal, geometry.viewDir ) );\n\tfloat dotNH = saturate( dot( geometry.normal, halfDir ) );\n\tfloat dotLH = saturate( dot( incidentLight.direction, halfDir ) );\n\tvec3 F = F_Schlick( specularColor, dotLH );\n\tfloat G = G_GGX_SmithCorrelated( alpha, dotNL, dotNV );\n\tfloat D = D_GGX( alpha, dotNH );\n\treturn F * ( G * D );\n}\nvec2 LTC_Uv( const in vec3 N, const in vec3 V, const in float roughness ) {\n\tconst float LUT_SIZE = 64.0;\n\tconst float LUT_SCALE = ( LUT_SIZE - 1.0 ) / LUT_SIZE;\n\tconst float LUT_BIAS = 0.5 / LUT_SIZE;\n\tfloat dotNV = saturate( dot( N, V ) );\n\tvec2 uv = vec2( roughness, sqrt( 1.0 - dotNV ) );\n\tuv = uv * LUT_SCALE + LUT_BIAS;\n\treturn uv;\n}\nfloat LTC_ClippedSphereFormFactor( const in vec3 f ) {\n\tfloat l = length( f );\n\treturn max( ( l * l + f.z ) / ( l + 1.0 ), 0.0 );\n}\nvec3 LTC_EdgeVectorFormFactor( const in vec3 v1, const in vec3 v2 ) {\n\tfloat x = dot( v1, v2 );\n\tfloat y = abs( x );\n\tfloat a = 0.8543985 + ( 0.4965155 + 0.0145206 * y ) * y;\n\tfloat b = 3.4175940 + ( 4.1616724 + y ) * y;\n\tfloat v = a / b;\n\tfloat theta_sintheta = ( x > 0.0 ) ? v : 0.5 * inversesqrt( max( 1.0 - x * x, 1e-7 ) ) - v;\n\treturn cross( v1, v2 ) * theta_sintheta;\n}\nvec3 LTC_Evaluate( const in vec3 N, const in vec3 V, const in vec3 P, const in mat3 mInv, const in vec3 rectCoords[ 4 ] ) {\n\tvec3 v1 = rectCoords[ 1 ] - rectCoords[ 0 ];\n\tvec3 v2 = rectCoords[ 3 ] - rectCoords[ 0 ];\n\tvec3 lightNormal = cross( v1, v2 );\n\tif( dot( lightNormal, P - rectCoords[ 0 ] ) < 0.0 ) return vec3( 0.0 );\n\tvec3 T1, T2;\n\tT1 = normalize( V - N * dot( V, N ) );\n\tT2 = - cross( N, T1 );\n\tmat3 mat = mInv * transposeMat3( mat3( T1, T2, N ) );\n\tvec3 coords[ 4 ];\n\tcoords[ 0 ] = mat * ( rectCoords[ 0 ] - P );\n\tcoords[ 1 ] = mat * ( rectCoords[ 1 ] - P );\n\tcoords[ 2 ] = mat * ( rectCoords[ 2 ] - P );\n\tcoords[ 3 ] = mat * ( rectCoords[ 3 ] - P );\n\tcoords[ 0 ] = normalize( coords[ 0 ] );\n\tcoords[ 1 ] = normalize( coords[ 1 ] );\n\tcoords[ 2 ] = normalize( coords[ 2 ] );\n\tcoords[ 3 ] = normalize( coords[ 3 ] );\n\tvec3 vectorFormFactor = vec3( 0.0 );\n\tvectorFormFactor += LTC_EdgeVectorFormFactor( coords[ 0 ], coords[ 1 ] );\n\tvectorFormFactor += LTC_EdgeVectorFormFactor( coords[ 1 ], coords[ 2 ] );\n\tvectorFormFactor += LTC_EdgeVectorFormFactor( coords[ 2 ], coords[ 3 ] );\n\tvectorFormFactor += LTC_EdgeVectorFormFactor( coords[ 3 ], coords[ 0 ] );\n\tfloat result = LTC_ClippedSphereFormFactor( vectorFormFactor );\n\treturn vec3( result );\n}\nvec3 BRDF_Specular_GGX_Environment( const in GeometricContext geometry, const in vec3 specularColor, const in float roughness ) {\n\tfloat dotNV = saturate( dot( geometry.normal, geometry.viewDir ) );\n\tconst vec4 c0 = vec4( - 1, - 0.0275, - 0.572, 0.022 );\n\tconst vec4 c1 = vec4( 1, 0.0425, 1.04, - 0.04 );\n\tvec4 r = roughness * c0 + c1;\n\tfloat a004 = min( r.x * r.x, exp2( - 9.28 * dotNV ) ) * r.x + r.y;\n\tvec2 AB = vec2( -1.04, 1.04 ) * a004 + r.zw;\n\treturn specularColor * AB.x + AB.y;\n}\nfloat G_BlinnPhong_Implicit( ) {\n\treturn 0.25;\n}\nfloat D_BlinnPhong( const in float shininess, const in float dotNH ) {\n\treturn RECIPROCAL_PI * ( shininess * 0.5 + 1.0 ) * pow( dotNH, shininess );\n}\nvec3 BRDF_Specular_BlinnPhong( const in IncidentLight incidentLight, const in GeometricContext geometry, const in vec3 specularColor, const in float shininess ) {\n\tvec3 halfDir = normalize( incidentLight.direction + geometry.viewDir );\n\tfloat dotNH = saturate( dot( geometry.normal, halfDir ) );\n\tfloat dotLH = saturate( dot( incidentLight.direction, halfDir ) );\n\tvec3 F = F_Schlick( specularColor, dotLH );\n\tfloat G = G_BlinnPhong_Implicit( );\n\tfloat D = D_BlinnPhong( shininess, dotNH );\n\treturn F * ( G * D );\n}\nfloat GGXRoughnessToBlinnExponent( const in float ggxRoughness ) {\n\treturn ( 2.0 / pow2( ggxRoughness + 0.0001 ) - 2.0 );\n}\nfloat BlinnExponentToGGXRoughness( const in float blinnExponent ) {\n\treturn sqrt( 2.0 / ( blinnExponent + 2.0 ) );\n}\n"; | |
| var bumpmap_pars_fragment = "#ifdef USE_BUMPMAP\n\tuniform sampler2D bumpMap;\n\tuniform float bumpScale;\n\tvec2 dHdxy_fwd() {\n\t\tvec2 dSTdx = dFdx( vUv );\n\t\tvec2 dSTdy = dFdy( vUv );\n\t\tfloat Hll = bumpScale * texture2D( bumpMap, vUv ).x;\n\t\tfloat dBx = bumpScale * texture2D( bumpMap, vUv + dSTdx ).x - Hll;\n\t\tfloat dBy = bumpScale * texture2D( bumpMap, vUv + dSTdy ).x - Hll;\n\t\treturn vec2( dBx, dBy );\n\t}\n\tvec3 perturbNormalArb( vec3 surf_pos, vec3 surf_norm, vec2 dHdxy ) {\n\t\tvec3 vSigmaX = vec3( dFdx( surf_pos.x ), dFdx( surf_pos.y ), dFdx( surf_pos.z ) );\n\t\tvec3 vSigmaY = vec3( dFdy( surf_pos.x ), dFdy( surf_pos.y ), dFdy( surf_pos.z ) );\n\t\tvec3 vN = surf_norm;\n\t\tvec3 R1 = cross( vSigmaY, vN );\n\t\tvec3 R2 = cross( vN, vSigmaX );\n\t\tfloat fDet = dot( vSigmaX, R1 );\n\t\tvec3 vGrad = sign( fDet ) * ( dHdxy.x * R1 + dHdxy.y * R2 );\n\t\treturn normalize( abs( fDet ) * surf_norm - vGrad );\n\t}\n#endif\n"; | |
| var clipping_planes_fragment = "#if NUM_CLIPPING_PLANES > 0\n\tvec4 plane;\n\t#pragma unroll_loop\n\tfor ( int i = 0; i < UNION_CLIPPING_PLANES; i ++ ) {\n\t\tplane = clippingPlanes[ i ];\n\t\tif ( dot( vViewPosition, plane.xyz ) > plane.w ) discard;\n\t}\n\t#if UNION_CLIPPING_PLANES < NUM_CLIPPING_PLANES\n\t\tbool clipped = true;\n\t\t#pragma unroll_loop\n\t\tfor ( int i = UNION_CLIPPING_PLANES; i < NUM_CLIPPING_PLANES; i ++ ) {\n\t\t\tplane = clippingPlanes[ i ];\n\t\t\tclipped = ( dot( vViewPosition, plane.xyz ) > plane.w ) && clipped;\n\t\t}\n\t\tif ( clipped ) discard;\n\t#endif\n#endif\n"; | |
| var clipping_planes_pars_fragment = "#if NUM_CLIPPING_PLANES > 0\n\t#if ! defined( PHYSICAL ) && ! defined( PHONG )\n\t\tvarying vec3 vViewPosition;\n\t#endif\n\tuniform vec4 clippingPlanes[ NUM_CLIPPING_PLANES ];\n#endif\n"; | |
| var clipping_planes_pars_vertex = "#if NUM_CLIPPING_PLANES > 0 && ! defined( PHYSICAL ) && ! defined( PHONG )\n\tvarying vec3 vViewPosition;\n#endif\n"; | |
| var clipping_planes_vertex = "#if NUM_CLIPPING_PLANES > 0 && ! defined( PHYSICAL ) && ! defined( PHONG )\n\tvViewPosition = - mvPosition.xyz;\n#endif\n"; | |
| var color_fragment = "#ifdef USE_COLOR\n\tdiffuseColor.rgb *= vColor;\n#endif"; | |
| var color_pars_fragment = "#ifdef USE_COLOR\n\tvarying vec3 vColor;\n#endif\n"; | |
| var color_pars_vertex = "#ifdef USE_COLOR\n\tvarying vec3 vColor;\n#endif"; | |
| var color_vertex = "#ifdef USE_COLOR\n\tvColor.xyz = color.xyz;\n#endif"; | |
| var common = "#define PI 3.14159265359\n#define PI2 6.28318530718\n#define PI_HALF 1.5707963267949\n#define RECIPROCAL_PI 0.31830988618\n#define RECIPROCAL_PI2 0.15915494\n#define LOG2 1.442695\n#define EPSILON 1e-6\n#define saturate(a) clamp( a, 0.0, 1.0 )\n#define whiteCompliment(a) ( 1.0 - saturate( a ) )\nfloat pow2( const in float x ) { return x*x; }\nfloat pow3( const in float x ) { return x*x*x; }\nfloat pow4( const in float x ) { float x2 = x*x; return x2*x2; }\nfloat average( const in vec3 color ) { return dot( color, vec3( 0.3333 ) ); }\nhighp float rand( const in vec2 uv ) {\n\tconst highp float a = 12.9898, b = 78.233, c = 43758.5453;\n\thighp float dt = dot( uv.xy, vec2( a,b ) ), sn = mod( dt, PI );\n\treturn fract(sin(sn) * c);\n}\nstruct IncidentLight {\n\tvec3 color;\n\tvec3 direction;\n\tbool visible;\n};\nstruct ReflectedLight {\n\tvec3 directDiffuse;\n\tvec3 directSpecular;\n\tvec3 indirectDiffuse;\n\tvec3 indirectSpecular;\n};\nstruct GeometricContext {\n\tvec3 position;\n\tvec3 normal;\n\tvec3 viewDir;\n};\nvec3 transformDirection( in vec3 dir, in mat4 matrix ) {\n\treturn normalize( ( matrix * vec4( dir, 0.0 ) ).xyz );\n}\nvec3 inverseTransformDirection( in vec3 dir, in mat4 matrix ) {\n\treturn normalize( ( vec4( dir, 0.0 ) * matrix ).xyz );\n}\nvec3 projectOnPlane(in vec3 point, in vec3 pointOnPlane, in vec3 planeNormal ) {\n\tfloat distance = dot( planeNormal, point - pointOnPlane );\n\treturn - distance * planeNormal + point;\n}\nfloat sideOfPlane( in vec3 point, in vec3 pointOnPlane, in vec3 planeNormal ) {\n\treturn sign( dot( point - pointOnPlane, planeNormal ) );\n}\nvec3 linePlaneIntersect( in vec3 pointOnLine, in vec3 lineDirection, in vec3 pointOnPlane, in vec3 planeNormal ) {\n\treturn lineDirection * ( dot( planeNormal, pointOnPlane - pointOnLine ) / dot( planeNormal, lineDirection ) ) + pointOnLine;\n}\nmat3 transposeMat3( const in mat3 m ) {\n\tmat3 tmp;\n\ttmp[ 0 ] = vec3( m[ 0 ].x, m[ 1 ].x, m[ 2 ].x );\n\ttmp[ 1 ] = vec3( m[ 0 ].y, m[ 1 ].y, m[ 2 ].y );\n\ttmp[ 2 ] = vec3( m[ 0 ].z, m[ 1 ].z, m[ 2 ].z );\n\treturn tmp;\n}\nfloat linearToRelativeLuminance( const in vec3 color ) {\n\tvec3 weights = vec3( 0.2126, 0.7152, 0.0722 );\n\treturn dot( weights, color.rgb );\n}\n"; | |
| var cube_uv_reflection_fragment = "#ifdef ENVMAP_TYPE_CUBE_UV\n#define cubeUV_textureSize (1024.0)\nint getFaceFromDirection(vec3 direction) {\n\tvec3 absDirection = abs(direction);\n\tint face = -1;\n\tif( absDirection.x > absDirection.z ) {\n\t\tif(absDirection.x > absDirection.y )\n\t\t\tface = direction.x > 0.0 ? 0 : 3;\n\t\telse\n\t\t\tface = direction.y > 0.0 ? 1 : 4;\n\t}\n\telse {\n\t\tif(absDirection.z > absDirection.y )\n\t\t\tface = direction.z > 0.0 ? 2 : 5;\n\t\telse\n\t\t\tface = direction.y > 0.0 ? 1 : 4;\n\t}\n\treturn face;\n}\n#define cubeUV_maxLods1 (log2(cubeUV_textureSize*0.25) - 1.0)\n#define cubeUV_rangeClamp (exp2((6.0 - 1.0) * 2.0))\nvec2 MipLevelInfo( vec3 vec, float roughnessLevel, float roughness ) {\n\tfloat scale = exp2(cubeUV_maxLods1 - roughnessLevel);\n\tfloat dxRoughness = dFdx(roughness);\n\tfloat dyRoughness = dFdy(roughness);\n\tvec3 dx = dFdx( vec * scale * dxRoughness );\n\tvec3 dy = dFdy( vec * scale * dyRoughness );\n\tfloat d = max( dot( dx, dx ), dot( dy, dy ) );\n\td = clamp(d, 1.0, cubeUV_rangeClamp);\n\tfloat mipLevel = 0.5 * log2(d);\n\treturn vec2(floor(mipLevel), fract(mipLevel));\n}\n#define cubeUV_maxLods2 (log2(cubeUV_textureSize*0.25) - 2.0)\n#define cubeUV_rcpTextureSize (1.0 / cubeUV_textureSize)\nvec2 getCubeUV(vec3 direction, float roughnessLevel, float mipLevel) {\n\tmipLevel = roughnessLevel > cubeUV_maxLods2 - 3.0 ? 0.0 : mipLevel;\n\tfloat a = 16.0 * cubeUV_rcpTextureSize;\n\tvec2 exp2_packed = exp2( vec2( roughnessLevel, mipLevel ) );\n\tvec2 rcp_exp2_packed = vec2( 1.0 ) / exp2_packed;\n\tfloat powScale = exp2_packed.x * exp2_packed.y;\n\tfloat scale = rcp_exp2_packed.x * rcp_exp2_packed.y * 0.25;\n\tfloat mipOffset = 0.75*(1.0 - rcp_exp2_packed.y) * rcp_exp2_packed.x;\n\tbool bRes = mipLevel == 0.0;\n\tscale = bRes && (scale < a) ? a : scale;\n\tvec3 r;\n\tvec2 offset;\n\tint face = getFaceFromDirection(direction);\n\tfloat rcpPowScale = 1.0 / powScale;\n\tif( face == 0) {\n\t\tr = vec3(direction.x, -direction.z, direction.y);\n\t\toffset = vec2(0.0+mipOffset,0.75 * rcpPowScale);\n\t\toffset.y = bRes && (offset.y < 2.0*a) ? a : offset.y;\n\t}\n\telse if( face == 1) {\n\t\tr = vec3(direction.y, direction.x, direction.z);\n\t\toffset = vec2(scale+mipOffset, 0.75 * rcpPowScale);\n\t\toffset.y = bRes && (offset.y < 2.0*a) ? a : offset.y;\n\t}\n\telse if( face == 2) {\n\t\tr = vec3(direction.z, direction.x, direction.y);\n\t\toffset = vec2(2.0*scale+mipOffset, 0.75 * rcpPowScale);\n\t\toffset.y = bRes && (offset.y < 2.0*a) ? a : offset.y;\n\t}\n\telse if( face == 3) {\n\t\tr = vec3(direction.x, direction.z, direction.y);\n\t\toffset = vec2(0.0+mipOffset,0.5 * rcpPowScale);\n\t\toffset.y = bRes && (offset.y < 2.0*a) ? 0.0 : offset.y;\n\t}\n\telse if( face == 4) {\n\t\tr = vec3(direction.y, direction.x, -direction.z);\n\t\toffset = vec2(scale+mipOffset, 0.5 * rcpPowScale);\n\t\toffset.y = bRes && (offset.y < 2.0*a) ? 0.0 : offset.y;\n\t}\n\telse {\n\t\tr = vec3(direction.z, -direction.x, direction.y);\n\t\toffset = vec2(2.0*scale+mipOffset, 0.5 * rcpPowScale);\n\t\toffset.y = bRes && (offset.y < 2.0*a) ? 0.0 : offset.y;\n\t}\n\tr = normalize(r);\n\tfloat texelOffset = 0.5 * cubeUV_rcpTextureSize;\n\tvec2 s = ( r.yz / abs( r.x ) + vec2( 1.0 ) ) * 0.5;\n\tvec2 base = offset + vec2( texelOffset );\n\treturn base + s * ( scale - 2.0 * texelOffset );\n}\n#define cubeUV_maxLods3 (log2(cubeUV_textureSize*0.25) - 3.0)\nvec4 textureCubeUV(vec3 reflectedDirection, float roughness ) {\n\tfloat roughnessVal = roughness* cubeUV_maxLods3;\n\tfloat r1 = floor(roughnessVal);\n\tfloat r2 = r1 + 1.0;\n\tfloat t = fract(roughnessVal);\n\tvec2 mipInfo = MipLevelInfo(reflectedDirection, r1, roughness);\n\tfloat s = mipInfo.y;\n\tfloat level0 = mipInfo.x;\n\tfloat level1 = level0 + 1.0;\n\tlevel1 = level1 > 5.0 ? 5.0 : level1;\n\tlevel0 += min( floor( s + 0.5 ), 5.0 );\n\tvec2 uv_10 = getCubeUV(reflectedDirection, r1, level0);\n\tvec4 color10 = envMapTexelToLinear(texture2D(envMap, uv_10));\n\tvec2 uv_20 = getCubeUV(reflectedDirection, r2, level0);\n\tvec4 color20 = envMapTexelToLinear(texture2D(envMap, uv_20));\n\tvec4 result = mix(color10, color20, t);\n\treturn vec4(result.rgb, 1.0);\n}\n#endif\n"; | |
| var defaultnormal_vertex = "vec3 transformedNormal = normalMatrix * objectNormal;\n#ifdef FLIP_SIDED\n\ttransformedNormal = - transformedNormal;\n#endif\n"; | |
| var displacementmap_pars_vertex = "#ifdef USE_DISPLACEMENTMAP\n\tuniform sampler2D displacementMap;\n\tuniform float displacementScale;\n\tuniform float displacementBias;\n#endif\n"; | |
| var displacementmap_vertex = "#ifdef USE_DISPLACEMENTMAP\n\ttransformed += normalize( objectNormal ) * ( texture2D( displacementMap, uv ).x * displacementScale + displacementBias );\n#endif\n"; | |
| var emissivemap_fragment = "#ifdef USE_EMISSIVEMAP\n\tvec4 emissiveColor = texture2D( emissiveMap, vUv );\n\temissiveColor.rgb = emissiveMapTexelToLinear( emissiveColor ).rgb;\n\ttotalEmissiveRadiance *= emissiveColor.rgb;\n#endif\n"; | |
| var emissivemap_pars_fragment = "#ifdef USE_EMISSIVEMAP\n\tuniform sampler2D emissiveMap;\n#endif\n"; | |
| var encodings_fragment = " gl_FragColor = linearToOutputTexel( gl_FragColor );\n"; | |
| var encodings_pars_fragment = "\nvec4 LinearToLinear( in vec4 value ) {\n\treturn value;\n}\nvec4 GammaToLinear( in vec4 value, in float gammaFactor ) {\n\treturn vec4( pow( value.xyz, vec3( gammaFactor ) ), value.w );\n}\nvec4 LinearToGamma( in vec4 value, in float gammaFactor ) {\n\treturn vec4( pow( value.xyz, vec3( 1.0 / gammaFactor ) ), value.w );\n}\nvec4 sRGBToLinear( in vec4 value ) {\n\treturn vec4( mix( pow( value.rgb * 0.9478672986 + vec3( 0.0521327014 ), vec3( 2.4 ) ), value.rgb * 0.0773993808, vec3( lessThanEqual( value.rgb, vec3( 0.04045 ) ) ) ), value.w );\n}\nvec4 LinearTosRGB( in vec4 value ) {\n\treturn vec4( mix( pow( value.rgb, vec3( 0.41666 ) ) * 1.055 - vec3( 0.055 ), value.rgb * 12.92, vec3( lessThanEqual( value.rgb, vec3( 0.0031308 ) ) ) ), value.w );\n}\nvec4 RGBEToLinear( in vec4 value ) {\n\treturn vec4( value.rgb * exp2( value.a * 255.0 - 128.0 ), 1.0 );\n}\nvec4 LinearToRGBE( in vec4 value ) {\n\tfloat maxComponent = max( max( value.r, value.g ), value.b );\n\tfloat fExp = clamp( ceil( log2( maxComponent ) ), -128.0, 127.0 );\n\treturn vec4( value.rgb / exp2( fExp ), ( fExp + 128.0 ) / 255.0 );\n}\nvec4 RGBMToLinear( in vec4 value, in float maxRange ) {\n\treturn vec4( value.xyz * value.w * maxRange, 1.0 );\n}\nvec4 LinearToRGBM( in vec4 value, in float maxRange ) {\n\tfloat maxRGB = max( value.x, max( value.g, value.b ) );\n\tfloat M = clamp( maxRGB / maxRange, 0.0, 1.0 );\n\tM = ceil( M * 255.0 ) / 255.0;\n\treturn vec4( value.rgb / ( M * maxRange ), M );\n}\nvec4 RGBDToLinear( in vec4 value, in float maxRange ) {\n\treturn vec4( value.rgb * ( ( maxRange / 255.0 ) / value.a ), 1.0 );\n}\nvec4 LinearToRGBD( in vec4 value, in float maxRange ) {\n\tfloat maxRGB = max( value.x, max( value.g, value.b ) );\n\tfloat D = max( maxRange / maxRGB, 1.0 );\n\tD = min( floor( D ) / 255.0, 1.0 );\n\treturn vec4( value.rgb * ( D * ( 255.0 / maxRange ) ), D );\n}\nconst mat3 cLogLuvM = mat3( 0.2209, 0.3390, 0.4184, 0.1138, 0.6780, 0.7319, 0.0102, 0.1130, 0.2969 );\nvec4 LinearToLogLuv( in vec4 value ) {\n\tvec3 Xp_Y_XYZp = value.rgb * cLogLuvM;\n\tXp_Y_XYZp = max(Xp_Y_XYZp, vec3(1e-6, 1e-6, 1e-6));\n\tvec4 vResult;\n\tvResult.xy = Xp_Y_XYZp.xy / Xp_Y_XYZp.z;\n\tfloat Le = 2.0 * log2(Xp_Y_XYZp.y) + 127.0;\n\tvResult.w = fract(Le);\n\tvResult.z = (Le - (floor(vResult.w*255.0))/255.0)/255.0;\n\treturn vResult;\n}\nconst mat3 cLogLuvInverseM = mat3( 6.0014, -2.7008, -1.7996, -1.3320, 3.1029, -5.7721, 0.3008, -1.0882, 5.6268 );\nvec4 LogLuvToLinear( in vec4 value ) {\n\tfloat Le = value.z * 255.0 + value.w;\n\tvec3 Xp_Y_XYZp;\n\tXp_Y_XYZp.y = exp2((Le - 127.0) / 2.0);\n\tXp_Y_XYZp.z = Xp_Y_XYZp.y / value.y;\n\tXp_Y_XYZp.x = value.x * Xp_Y_XYZp.z;\n\tvec3 vRGB = Xp_Y_XYZp.rgb * cLogLuvInverseM;\n\treturn vec4( max(vRGB, 0.0), 1.0 );\n}\n"; | |
| var envmap_fragment = "#ifdef USE_ENVMAP\n\t#if defined( USE_BUMPMAP ) || defined( USE_NORMALMAP ) || defined( PHONG )\n\t\tvec3 cameraToVertex = normalize( vWorldPosition - cameraPosition );\n\t\tvec3 worldNormal = inverseTransformDirection( normal, viewMatrix );\n\t\t#ifdef ENVMAP_MODE_REFLECTION\n\t\t\tvec3 reflectVec = reflect( cameraToVertex, worldNormal );\n\t\t#else\n\t\t\tvec3 reflectVec = refract( cameraToVertex, worldNormal, refractionRatio );\n\t\t#endif\n\t#else\n\t\tvec3 reflectVec = vReflect;\n\t#endif\n\t#ifdef ENVMAP_TYPE_CUBE\n\t\tvec4 envColor = textureCube( envMap, vec3( flipEnvMap * reflectVec.x, reflectVec.yz ) );\n\t#elif defined( ENVMAP_TYPE_EQUIREC )\n\t\tvec2 sampleUV;\n\t\treflectVec = normalize( reflectVec );\n\t\tsampleUV.y = asin( clamp( reflectVec.y, - 1.0, 1.0 ) ) * RECIPROCAL_PI + 0.5;\n\t\tsampleUV.x = atan( reflectVec.z, reflectVec.x ) * RECIPROCAL_PI2 + 0.5;\n\t\tvec4 envColor = texture2D( envMap, sampleUV );\n\t#elif defined( ENVMAP_TYPE_SPHERE )\n\t\treflectVec = normalize( reflectVec );\n\t\tvec3 reflectView = normalize( ( viewMatrix * vec4( reflectVec, 0.0 ) ).xyz + vec3( 0.0, 0.0, 1.0 ) );\n\t\tvec4 envColor = texture2D( envMap, reflectView.xy * 0.5 + 0.5 );\n\t#else\n\t\tvec4 envColor = vec4( 0.0 );\n\t#endif\n\tenvColor = envMapTexelToLinear( envColor );\n\t#ifdef ENVMAP_BLENDING_MULTIPLY\n\t\toutgoingLight = mix( outgoingLight, outgoingLight * envColor.xyz, specularStrength * reflectivity );\n\t#elif defined( ENVMAP_BLENDING_MIX )\n\t\toutgoingLight = mix( outgoingLight, envColor.xyz, specularStrength * reflectivity );\n\t#elif defined( ENVMAP_BLENDING_ADD )\n\t\toutgoingLight += envColor.xyz * specularStrength * reflectivity;\n\t#endif\n#endif\n"; | |
| var envmap_pars_fragment = "#if defined( USE_ENVMAP ) || defined( PHYSICAL )\n\tuniform float reflectivity;\n\tuniform float envMapIntensity;\n#endif\n#ifdef USE_ENVMAP\n\t#if ! defined( PHYSICAL ) && ( defined( USE_BUMPMAP ) || defined( USE_NORMALMAP ) || defined( PHONG ) )\n\t\tvarying vec3 vWorldPosition;\n\t#endif\n\t#ifdef ENVMAP_TYPE_CUBE\n\t\tuniform samplerCube envMap;\n\t#else\n\t\tuniform sampler2D envMap;\n\t#endif\n\tuniform float flipEnvMap;\n\t#if defined( USE_BUMPMAP ) || defined( USE_NORMALMAP ) || defined( PHONG ) || defined( PHYSICAL )\n\t\tuniform float refractionRatio;\n\t#else\n\t\tvarying vec3 vReflect;\n\t#endif\n#endif\n"; | |
| var envmap_pars_vertex = "#ifdef USE_ENVMAP\n\t#if defined( USE_BUMPMAP ) || defined( USE_NORMALMAP ) || defined( PHONG )\n\t\tvarying vec3 vWorldPosition;\n\t#else\n\t\tvarying vec3 vReflect;\n\t\tuniform float refractionRatio;\n\t#endif\n#endif\n"; | |
| var envmap_vertex = "#ifdef USE_ENVMAP\n\t#if defined( USE_BUMPMAP ) || defined( USE_NORMALMAP ) || defined( PHONG )\n\t\tvWorldPosition = worldPosition.xyz;\n\t#else\n\t\tvec3 cameraToVertex = normalize( worldPosition.xyz - cameraPosition );\n\t\tvec3 worldNormal = inverseTransformDirection( transformedNormal, viewMatrix );\n\t\t#ifdef ENVMAP_MODE_REFLECTION\n\t\t\tvReflect = reflect( cameraToVertex, worldNormal );\n\t\t#else\n\t\t\tvReflect = refract( cameraToVertex, worldNormal, refractionRatio );\n\t\t#endif\n\t#endif\n#endif\n"; | |
| var fog_vertex = "\n#ifdef USE_FOG\nfogDepth = -mvPosition.z;\n#endif"; | |
| var fog_pars_vertex = "#ifdef USE_FOG\n varying float fogDepth;\n#endif\n"; | |
| var fog_fragment = "#ifdef USE_FOG\n\t#ifdef FOG_EXP2\n\t\tfloat fogFactor = whiteCompliment( exp2( - fogDensity * fogDensity * fogDepth * fogDepth * LOG2 ) );\n\t#else\n\t\tfloat fogFactor = smoothstep( fogNear, fogFar, fogDepth );\n\t#endif\n\tgl_FragColor.rgb = mix( gl_FragColor.rgb, fogColor, fogFactor );\n#endif\n"; | |
| var fog_pars_fragment = "#ifdef USE_FOG\n\tuniform vec3 fogColor;\n\tvarying float fogDepth;\n\t#ifdef FOG_EXP2\n\t\tuniform float fogDensity;\n\t#else\n\t\tuniform float fogNear;\n\t\tuniform float fogFar;\n\t#endif\n#endif\n"; | |
| var gradientmap_pars_fragment = "#ifdef TOON\n\tuniform sampler2D gradientMap;\n\tvec3 getGradientIrradiance( vec3 normal, vec3 lightDirection ) {\n\t\tfloat dotNL = dot( normal, lightDirection );\n\t\tvec2 coord = vec2( dotNL * 0.5 + 0.5, 0.0 );\n\t\t#ifdef USE_GRADIENTMAP\n\t\t\treturn texture2D( gradientMap, coord ).rgb;\n\t\t#else\n\t\t\treturn ( coord.x < 0.7 ) ? vec3( 0.7 ) : vec3( 1.0 );\n\t\t#endif\n\t}\n#endif\n"; | |
| var lightmap_fragment = "#ifdef USE_LIGHTMAP\n\treflectedLight.indirectDiffuse += PI * texture2D( lightMap, vUv2 ).xyz * lightMapIntensity;\n#endif\n"; | |
| var lightmap_pars_fragment = "#ifdef USE_LIGHTMAP\n\tuniform sampler2D lightMap;\n\tuniform float lightMapIntensity;\n#endif"; | |
| var lights_lambert_vertex = "vec3 diffuse = vec3( 1.0 );\nGeometricContext geometry;\ngeometry.position = mvPosition.xyz;\ngeometry.normal = normalize( transformedNormal );\ngeometry.viewDir = normalize( -mvPosition.xyz );\nGeometricContext backGeometry;\nbackGeometry.position = geometry.position;\nbackGeometry.normal = -geometry.normal;\nbackGeometry.viewDir = geometry.viewDir;\nvLightFront = vec3( 0.0 );\n#ifdef DOUBLE_SIDED\n\tvLightBack = vec3( 0.0 );\n#endif\nIncidentLight directLight;\nfloat dotNL;\nvec3 directLightColor_Diffuse;\n#if NUM_POINT_LIGHTS > 0\n\t#pragma unroll_loop\n\tfor ( int i = 0; i < NUM_POINT_LIGHTS; i ++ ) {\n\t\tgetPointDirectLightIrradiance( pointLights[ i ], geometry, directLight );\n\t\tdotNL = dot( geometry.normal, directLight.direction );\n\t\tdirectLightColor_Diffuse = PI * directLight.color;\n\t\tvLightFront += saturate( dotNL ) * directLightColor_Diffuse;\n\t\t#ifdef DOUBLE_SIDED\n\t\t\tvLightBack += saturate( -dotNL ) * directLightColor_Diffuse;\n\t\t#endif\n\t}\n#endif\n#if NUM_SPOT_LIGHTS > 0\n\t#pragma unroll_loop\n\tfor ( int i = 0; i < NUM_SPOT_LIGHTS; i ++ ) {\n\t\tgetSpotDirectLightIrradiance( spotLights[ i ], geometry, directLight );\n\t\tdotNL = dot( geometry.normal, directLight.direction );\n\t\tdirectLightColor_Diffuse = PI * directLight.color;\n\t\tvLightFront += saturate( dotNL ) * directLightColor_Diffuse;\n\t\t#ifdef DOUBLE_SIDED\n\t\t\tvLightBack += saturate( -dotNL ) * directLightColor_Diffuse;\n\t\t#endif\n\t}\n#endif\n#if NUM_DIR_LIGHTS > 0\n\t#pragma unroll_loop\n\tfor ( int i = 0; i < NUM_DIR_LIGHTS; i ++ ) {\n\t\tgetDirectionalDirectLightIrradiance( directionalLights[ i ], geometry, directLight );\n\t\tdotNL = dot( geometry.normal, directLight.direction );\n\t\tdirectLightColor_Diffuse = PI * directLight.color;\n\t\tvLightFront += saturate( dotNL ) * directLightColor_Diffuse;\n\t\t#ifdef DOUBLE_SIDED\n\t\t\tvLightBack += saturate( -dotNL ) * directLightColor_Diffuse;\n\t\t#endif\n\t}\n#endif\n#if NUM_HEMI_LIGHTS > 0\n\t#pragma unroll_loop\n\tfor ( int i = 0; i < NUM_HEMI_LIGHTS; i ++ ) {\n\t\tvLightFront += getHemisphereLightIrradiance( hemisphereLights[ i ], geometry );\n\t\t#ifdef DOUBLE_SIDED\n\t\t\tvLightBack += getHemisphereLightIrradiance( hemisphereLights[ i ], backGeometry );\n\t\t#endif\n\t}\n#endif\n"; | |
| var lights_pars_begin = "uniform vec3 ambientLightColor;\nvec3 getAmbientLightIrradiance( const in vec3 ambientLightColor ) {\n\tvec3 irradiance = ambientLightColor;\n\t#ifndef PHYSICALLY_CORRECT_LIGHTS\n\t\tirradiance *= PI;\n\t#endif\n\treturn irradiance;\n}\n#if NUM_DIR_LIGHTS > 0\n\tstruct DirectionalLight {\n\t\tvec3 direction;\n\t\tvec3 color;\n\t\tint shadow;\n\t\tfloat shadowBias;\n\t\tfloat shadowRadius;\n\t\tvec2 shadowMapSize;\n\t};\n\tuniform DirectionalLight directionalLights[ NUM_DIR_LIGHTS ];\n\tvoid getDirectionalDirectLightIrradiance( const in DirectionalLight directionalLight, const in GeometricContext geometry, out IncidentLight directLight ) {\n\t\tdirectLight.color = directionalLight.color;\n\t\tdirectLight.direction = directionalLight.direction;\n\t\tdirectLight.visible = true;\n\t}\n#endif\n#if NUM_POINT_LIGHTS > 0\n\tstruct PointLight {\n\t\tvec3 position;\n\t\tvec3 color;\n\t\tfloat distance;\n\t\tfloat decay;\n\t\tint shadow;\n\t\tfloat shadowBias;\n\t\tfloat shadowRadius;\n\t\tvec2 shadowMapSize;\n\t\tfloat shadowCameraNear;\n\t\tfloat shadowCameraFar;\n\t};\n\tuniform PointLight pointLights[ NUM_POINT_LIGHTS ];\n\tvoid getPointDirectLightIrradiance( const in PointLight pointLight, const in GeometricContext geometry, out IncidentLight directLight ) {\n\t\tvec3 lVector = pointLight.position - geometry.position;\n\t\tdirectLight.direction = normalize( lVector );\n\t\tfloat lightDistance = length( lVector );\n\t\tdirectLight.color = pointLight.color;\n\t\tdirectLight.color *= punctualLightIntensityToIrradianceFactor( lightDistance, pointLight.distance, pointLight.decay );\n\t\tdirectLight.visible = ( directLight.color != vec3( 0.0 ) );\n\t}\n#endif\n#if NUM_SPOT_LIGHTS > 0\n\tstruct SpotLight {\n\t\tvec3 position;\n\t\tvec3 direction;\n\t\tvec3 color;\n\t\tfloat distance;\n\t\tfloat decay;\n\t\tfloat coneCos;\n\t\tfloat penumbraCos;\n\t\tint shadow;\n\t\tfloat shadowBias;\n\t\tfloat shadowRadius;\n\t\tvec2 shadowMapSize;\n\t};\n\tuniform SpotLight spotLights[ NUM_SPOT_LIGHTS ];\n\tvoid getSpotDirectLightIrradiance( const in SpotLight spotLight, const in GeometricContext geometry, out IncidentLight directLight ) {\n\t\tvec3 lVector = spotLight.position - geometry.position;\n\t\tdirectLight.direction = normalize( lVector );\n\t\tfloat lightDistance = length( lVector );\n\t\tfloat angleCos = dot( directLight.direction, spotLight.direction );\n\t\tif ( angleCos > spotLight.coneCos ) {\n\t\t\tfloat spotEffect = smoothstep( spotLight.coneCos, spotLight.penumbraCos, angleCos );\n\t\t\tdirectLight.color = spotLight.color;\n\t\t\tdirectLight.color *= spotEffect * punctualLightIntensityToIrradianceFactor( lightDistance, spotLight.distance, spotLight.decay );\n\t\t\tdirectLight.visible = true;\n\t\t} else {\n\t\t\tdirectLight.color = vec3( 0.0 );\n\t\t\tdirectLight.visible = false;\n\t\t}\n\t}\n#endif\n#if NUM_RECT_AREA_LIGHTS > 0\n\tstruct RectAreaLight {\n\t\tvec3 color;\n\t\tvec3 position;\n\t\tvec3 halfWidth;\n\t\tvec3 halfHeight;\n\t};\n\tuniform sampler2D ltc_1;\tuniform sampler2D ltc_2;\n\tuniform RectAreaLight rectAreaLights[ NUM_RECT_AREA_LIGHTS ];\n#endif\n#if NUM_HEMI_LIGHTS > 0\n\tstruct HemisphereLight {\n\t\tvec3 direction;\n\t\tvec3 skyColor;\n\t\tvec3 groundColor;\n\t};\n\tuniform HemisphereLight hemisphereLights[ NUM_HEMI_LIGHTS ];\n\tvec3 getHemisphereLightIrradiance( const in HemisphereLight hemiLight, const in GeometricContext geometry ) {\n\t\tfloat dotNL = dot( geometry.normal, hemiLight.direction );\n\t\tfloat hemiDiffuseWeight = 0.5 * dotNL + 0.5;\n\t\tvec3 irradiance = mix( hemiLight.groundColor, hemiLight.skyColor, hemiDiffuseWeight );\n\t\t#ifndef PHYSICALLY_CORRECT_LIGHTS\n\t\t\tirradiance *= PI;\n\t\t#endif\n\t\treturn irradiance;\n\t}\n#endif\n"; | |
| var lights_pars_maps = "#if defined( USE_ENVMAP ) && defined( PHYSICAL )\n\tvec3 getLightProbeIndirectIrradiance( const in GeometricContext geometry, const in int maxMIPLevel ) {\n\t\tvec3 worldNormal = inverseTransformDirection( geometry.normal, viewMatrix );\n\t\t#ifdef ENVMAP_TYPE_CUBE\n\t\t\tvec3 queryVec = vec3( flipEnvMap * worldNormal.x, worldNormal.yz );\n\t\t\t#ifdef TEXTURE_LOD_EXT\n\t\t\t\tvec4 envMapColor = textureCubeLodEXT( envMap, queryVec, float( maxMIPLevel ) );\n\t\t\t#else\n\t\t\t\tvec4 envMapColor = textureCube( envMap, queryVec, float( maxMIPLevel ) );\n\t\t\t#endif\n\t\t\tenvMapColor.rgb = envMapTexelToLinear( envMapColor ).rgb;\n\t\t#elif defined( ENVMAP_TYPE_CUBE_UV )\n\t\t\tvec3 queryVec = vec3( flipEnvMap * worldNormal.x, worldNormal.yz );\n\t\t\tvec4 envMapColor = textureCubeUV( queryVec, 1.0 );\n\t\t#else\n\t\t\tvec4 envMapColor = vec4( 0.0 );\n\t\t#endif\n\t\treturn PI * envMapColor.rgb * envMapIntensity;\n\t}\n\tfloat getSpecularMIPLevel( const in float blinnShininessExponent, const in int maxMIPLevel ) {\n\t\tfloat maxMIPLevelScalar = float( maxMIPLevel );\n\t\tfloat desiredMIPLevel = maxMIPLevelScalar + 0.79248 - 0.5 * log2( pow2( blinnShininessExponent ) + 1.0 );\n\t\treturn clamp( desiredMIPLevel, 0.0, maxMIPLevelScalar );\n\t}\n\tvec3 getLightProbeIndirectRadiance( const in GeometricContext geometry, const in float blinnShininessExponent, const in int maxMIPLevel ) {\n\t\t#ifdef ENVMAP_MODE_REFLECTION\n\t\t\tvec3 reflectVec = reflect( -geometry.viewDir, geometry.normal );\n\t\t#else\n\t\t\tvec3 reflectVec = refract( -geometry.viewDir, geometry.normal, refractionRatio );\n\t\t#endif\n\t\treflectVec = inverseTransformDirection( reflectVec, viewMatrix );\n\t\tfloat specularMIPLevel = getSpecularMIPLevel( blinnShininessExponent, maxMIPLevel );\n\t\t#ifdef ENVMAP_TYPE_CUBE\n\t\t\tvec3 queryReflectVec = vec3( flipEnvMap * reflectVec.x, reflectVec.yz );\n\t\t\t#ifdef TEXTURE_LOD_EXT\n\t\t\t\tvec4 envMapColor = textureCubeLodEXT( envMap, queryReflectVec, specularMIPLevel );\n\t\t\t#else\n\t\t\t\tvec4 envMapColor = textureCube( envMap, queryReflectVec, specularMIPLevel );\n\t\t\t#endif\n\t\t\tenvMapColor.rgb = envMapTexelToLinear( envMapColor ).rgb;\n\t\t#elif defined( ENVMAP_TYPE_CUBE_UV )\n\t\t\tvec3 queryReflectVec = vec3( flipEnvMap * reflectVec.x, reflectVec.yz );\n\t\t\tvec4 envMapColor = textureCubeUV(queryReflectVec, BlinnExponentToGGXRoughness(blinnShininessExponent));\n\t\t#elif defined( ENVMAP_TYPE_EQUIREC )\n\t\t\tvec2 sampleUV;\n\t\t\tsampleUV.y = asin( clamp( reflectVec.y, - 1.0, 1.0 ) ) * RECIPROCAL_PI + 0.5;\n\t\t\tsampleUV.x = atan( reflectVec.z, reflectVec.x ) * RECIPROCAL_PI2 + 0.5;\n\t\t\t#ifdef TEXTURE_LOD_EXT\n\t\t\t\tvec4 envMapColor = texture2DLodEXT( envMap, sampleUV, specularMIPLevel );\n\t\t\t#else\n\t\t\t\tvec4 envMapColor = texture2D( envMap, sampleUV, specularMIPLevel );\n\t\t\t#endif\n\t\t\tenvMapColor.rgb = envMapTexelToLinear( envMapColor ).rgb;\n\t\t#elif defined( ENVMAP_TYPE_SPHERE )\n\t\t\tvec3 reflectView = normalize( ( viewMatrix * vec4( reflectVec, 0.0 ) ).xyz + vec3( 0.0,0.0,1.0 ) );\n\t\t\t#ifdef TEXTURE_LOD_EXT\n\t\t\t\tvec4 envMapColor = texture2DLodEXT( envMap, reflectView.xy * 0.5 + 0.5, specularMIPLevel );\n\t\t\t#else\n\t\t\t\tvec4 envMapColor = texture2D( envMap, reflectView.xy * 0.5 + 0.5, specularMIPLevel );\n\t\t\t#endif\n\t\t\tenvMapColor.rgb = envMapTexelToLinear( envMapColor ).rgb;\n\t\t#endif\n\t\treturn envMapColor.rgb * envMapIntensity;\n\t}\n#endif\n"; | |
| var lights_phong_fragment = "BlinnPhongMaterial material;\nmaterial.diffuseColor = diffuseColor.rgb;\nmaterial.specularColor = specular;\nmaterial.specularShininess = shininess;\nmaterial.specularStrength = specularStrength;\n"; | |
| var lights_phong_pars_fragment = "varying vec3 vViewPosition;\n#ifndef FLAT_SHADED\n\tvarying vec3 vNormal;\n#endif\nstruct BlinnPhongMaterial {\n\tvec3\tdiffuseColor;\n\tvec3\tspecularColor;\n\tfloat\tspecularShininess;\n\tfloat\tspecularStrength;\n};\nvoid RE_Direct_BlinnPhong( const in IncidentLight directLight, const in GeometricContext geometry, const in BlinnPhongMaterial material, inout ReflectedLight reflectedLight ) {\n\t#ifdef TOON\n\t\tvec3 irradiance = getGradientIrradiance( geometry.normal, directLight.direction ) * directLight.color;\n\t#else\n\t\tfloat dotNL = saturate( dot( geometry.normal, directLight.direction ) );\n\t\tvec3 irradiance = dotNL * directLight.color;\n\t#endif\n\t#ifndef PHYSICALLY_CORRECT_LIGHTS\n\t\tirradiance *= PI;\n\t#endif\n\treflectedLight.directDiffuse += irradiance * BRDF_Diffuse_Lambert( material.diffuseColor );\n\treflectedLight.directSpecular += irradiance * BRDF_Specular_BlinnPhong( directLight, geometry, material.specularColor, material.specularShininess ) * material.specularStrength;\n}\nvoid RE_IndirectDiffuse_BlinnPhong( const in vec3 irradiance, const in GeometricContext geometry, const in BlinnPhongMaterial material, inout ReflectedLight reflectedLight ) {\n\treflectedLight.indirectDiffuse += irradiance * BRDF_Diffuse_Lambert( material.diffuseColor );\n}\n#define RE_Direct\t\t\t\tRE_Direct_BlinnPhong\n#define RE_IndirectDiffuse\t\tRE_IndirectDiffuse_BlinnPhong\n#define Material_LightProbeLOD( material )\t(0)\n"; | |
| var lights_physical_fragment = "PhysicalMaterial material;\nmaterial.diffuseColor = diffuseColor.rgb * ( 1.0 - metalnessFactor );\nmaterial.specularRoughness = clamp( roughnessFactor, 0.04, 1.0 );\n#ifdef STANDARD\n\tmaterial.specularColor = mix( vec3( DEFAULT_SPECULAR_COEFFICIENT ), diffuseColor.rgb, metalnessFactor );\n#else\n\tmaterial.specularColor = mix( vec3( MAXIMUM_SPECULAR_COEFFICIENT * pow2( reflectivity ) ), diffuseColor.rgb, metalnessFactor );\n\tmaterial.clearCoat = saturate( clearCoat );\tmaterial.clearCoatRoughness = clamp( clearCoatRoughness, 0.04, 1.0 );\n#endif\n"; | |
| var lights_physical_pars_fragment = "struct PhysicalMaterial {\n\tvec3\tdiffuseColor;\n\tfloat\tspecularRoughness;\n\tvec3\tspecularColor;\n\t#ifndef STANDARD\n\t\tfloat clearCoat;\n\t\tfloat clearCoatRoughness;\n\t#endif\n};\n#define MAXIMUM_SPECULAR_COEFFICIENT 0.16\n#define DEFAULT_SPECULAR_COEFFICIENT 0.04\nfloat clearCoatDHRApprox( const in float roughness, const in float dotNL ) {\n\treturn DEFAULT_SPECULAR_COEFFICIENT + ( 1.0 - DEFAULT_SPECULAR_COEFFICIENT ) * ( pow( 1.0 - dotNL, 5.0 ) * pow( 1.0 - roughness, 2.0 ) );\n}\n#if NUM_RECT_AREA_LIGHTS > 0\n\tvoid RE_Direct_RectArea_Physical( const in RectAreaLight rectAreaLight, const in GeometricContext geometry, const in PhysicalMaterial material, inout ReflectedLight reflectedLight ) {\n\t\tvec3 normal = geometry.normal;\n\t\tvec3 viewDir = geometry.viewDir;\n\t\tvec3 position = geometry.position;\n\t\tvec3 lightPos = rectAreaLight.position;\n\t\tvec3 halfWidth = rectAreaLight.halfWidth;\n\t\tvec3 halfHeight = rectAreaLight.halfHeight;\n\t\tvec3 lightColor = rectAreaLight.color;\n\t\tfloat roughness = material.specularRoughness;\n\t\tvec3 rectCoords[ 4 ];\n\t\trectCoords[ 0 ] = lightPos - halfWidth - halfHeight;\t\trectCoords[ 1 ] = lightPos + halfWidth - halfHeight;\n\t\trectCoords[ 2 ] = lightPos + halfWidth + halfHeight;\n\t\trectCoords[ 3 ] = lightPos - halfWidth + halfHeight;\n\t\tvec2 uv = LTC_Uv( normal, viewDir, roughness );\n\t\tvec4 t1 = texture2D( ltc_1, uv );\n\t\tvec4 t2 = texture2D( ltc_2, uv );\n\t\tmat3 mInv = mat3(\n\t\t\tvec3( t1.x, 0, t1.y ),\n\t\t\tvec3( 0, 1, 0 ),\n\t\t\tvec3( t1.z, 0, t1.w )\n\t\t);\n\t\tvec3 fresnel = ( material.specularColor * t2.x + ( vec3( 1.0 ) - material.specularColor ) * t2.y );\n\t\treflectedLight.directSpecular += lightColor * fresnel * LTC_Evaluate( normal, viewDir, position, mInv, rectCoords );\n\t\treflectedLight.directDiffuse += lightColor * material.diffuseColor * LTC_Evaluate( normal, viewDir, position, mat3( 1.0 ), rectCoords );\n\t}\n#endif\nvoid RE_Direct_Physical( const in IncidentLight directLight, const in GeometricContext geometry, const in PhysicalMaterial material, inout ReflectedLight reflectedLight ) {\n\tfloat dotNL = saturate( dot( geometry.normal, directLight.direction ) );\n\tvec3 irradiance = dotNL * directLight.color;\n\t#ifndef PHYSICALLY_CORRECT_LIGHTS\n\t\tirradiance *= PI;\n\t#endif\n\t#ifndef STANDARD\n\t\tfloat clearCoatDHR = material.clearCoat * clearCoatDHRApprox( material.clearCoatRoughness, dotNL );\n\t#else\n\t\tfloat clearCoatDHR = 0.0;\n\t#endif\n\treflectedLight.directSpecular += ( 1.0 - clearCoatDHR ) * irradiance * BRDF_Specular_GGX( directLight, geometry, material.specularColor, material.specularRoughness );\n\treflectedLight.directDiffuse += ( 1.0 - clearCoatDHR ) * irradiance * BRDF_Diffuse_Lambert( material.diffuseColor );\n\t#ifndef STANDARD\n\t\treflectedLight.directSpecular += irradiance * material.clearCoat * BRDF_Specular_GGX( directLight, geometry, vec3( DEFAULT_SPECULAR_COEFFICIENT ), material.clearCoatRoughness );\n\t#endif\n}\nvoid RE_IndirectDiffuse_Physical( const in vec3 irradiance, const in GeometricContext geometry, const in PhysicalMaterial material, inout ReflectedLight reflectedLight ) {\n\treflectedLight.indirectDiffuse += irradiance * BRDF_Diffuse_Lambert( material.diffuseColor );\n}\nvoid RE_IndirectSpecular_Physical( const in vec3 radiance, const in vec3 clearCoatRadiance, const in GeometricContext geometry, const in PhysicalMaterial material, inout ReflectedLight reflectedLight ) {\n\t#ifndef STANDARD\n\t\tfloat dotNV = saturate( dot( geometry.normal, geometry.viewDir ) );\n\t\tfloat dotNL = dotNV;\n\t\tfloat clearCoatDHR = material.clearCoat * clearCoatDHRApprox( material.clearCoatRoughness, dotNL );\n\t#else\n\t\tfloat clearCoatDHR = 0.0;\n\t#endif\n\treflectedLight.indirectSpecular += ( 1.0 - clearCoatDHR ) * radiance * BRDF_Specular_GGX_Environment( geometry, material.specularColor, material.specularRoughness );\n\t#ifndef STANDARD\n\t\treflectedLight.indirectSpecular += clearCoatRadiance * material.clearCoat * BRDF_Specular_GGX_Environment( geometry, vec3( DEFAULT_SPECULAR_COEFFICIENT ), material.clearCoatRoughness );\n\t#endif\n}\n#define RE_Direct\t\t\t\tRE_Direct_Physical\n#define RE_Direct_RectArea\t\tRE_Direct_RectArea_Physical\n#define RE_IndirectDiffuse\t\tRE_IndirectDiffuse_Physical\n#define RE_IndirectSpecular\t\tRE_IndirectSpecular_Physical\n#define Material_BlinnShininessExponent( material ) GGXRoughnessToBlinnExponent( material.specularRoughness )\n#define Material_ClearCoat_BlinnShininessExponent( material ) GGXRoughnessToBlinnExponent( material.clearCoatRoughness )\nfloat computeSpecularOcclusion( const in float dotNV, const in float ambientOcclusion, const in float roughness ) {\n\treturn saturate( pow( dotNV + ambientOcclusion, exp2( - 16.0 * roughness - 1.0 ) ) - 1.0 + ambientOcclusion );\n}\n"; | |
| var lights_fragment_begin = "\nGeometricContext geometry;\ngeometry.position = - vViewPosition;\ngeometry.normal = normal;\ngeometry.viewDir = normalize( vViewPosition );\nIncidentLight directLight;\n#if ( NUM_POINT_LIGHTS > 0 ) && defined( RE_Direct )\n\tPointLight pointLight;\n\t#pragma unroll_loop\n\tfor ( int i = 0; i < NUM_POINT_LIGHTS; i ++ ) {\n\t\tpointLight = pointLights[ i ];\n\t\tgetPointDirectLightIrradiance( pointLight, geometry, directLight );\n\t\t#ifdef USE_SHADOWMAP\n\t\tdirectLight.color *= all( bvec2( pointLight.shadow, directLight.visible ) ) ? getPointShadow( pointShadowMap[ i ], pointLight.shadowMapSize, pointLight.shadowBias, pointLight.shadowRadius, vPointShadowCoord[ i ], pointLight.shadowCameraNear, pointLight.shadowCameraFar ) : 1.0;\n\t\t#endif\n\t\tRE_Direct( directLight, geometry, material, reflectedLight );\n\t}\n#endif\n#if ( NUM_SPOT_LIGHTS > 0 ) && defined( RE_Direct )\n\tSpotLight spotLight;\n\t#pragma unroll_loop\n\tfor ( int i = 0; i < NUM_SPOT_LIGHTS; i ++ ) {\n\t\tspotLight = spotLights[ i ];\n\t\tgetSpotDirectLightIrradiance( spotLight, geometry, directLight );\n\t\t#ifdef USE_SHADOWMAP\n\t\tdirectLight.color *= all( bvec2( spotLight.shadow, directLight.visible ) ) ? getShadow( spotShadowMap[ i ], spotLight.shadowMapSize, spotLight.shadowBias, spotLight.shadowRadius, vSpotShadowCoord[ i ] ) : 1.0;\n\t\t#endif\n\t\tRE_Direct( directLight, geometry, material, reflectedLight );\n\t}\n#endif\n#if ( NUM_DIR_LIGHTS > 0 ) && defined( RE_Direct )\n\tDirectionalLight directionalLight;\n\t#pragma unroll_loop\n\tfor ( int i = 0; i < NUM_DIR_LIGHTS; i ++ ) {\n\t\tdirectionalLight = directionalLights[ i ];\n\t\tgetDirectionalDirectLightIrradiance( directionalLight, geometry, directLight );\n\t\t#ifdef USE_SHADOWMAP\n\t\tdirectLight.color *= all( bvec2( directionalLight.shadow, directLight.visible ) ) ? getShadow( directionalShadowMap[ i ], directionalLight.shadowMapSize, directionalLight.shadowBias, directionalLight.shadowRadius, vDirectionalShadowCoord[ i ] ) : 1.0;\n\t\t#endif\n\t\tRE_Direct( directLight, geometry, material, reflectedLight );\n\t}\n#endif\n#if ( NUM_RECT_AREA_LIGHTS > 0 ) && defined( RE_Direct_RectArea )\n\tRectAreaLight rectAreaLight;\n\t#pragma unroll_loop\n\tfor ( int i = 0; i < NUM_RECT_AREA_LIGHTS; i ++ ) {\n\t\trectAreaLight = rectAreaLights[ i ];\n\t\tRE_Direct_RectArea( rectAreaLight, geometry, material, reflectedLight );\n\t}\n#endif\n#if defined( RE_IndirectDiffuse )\n\tvec3 irradiance = getAmbientLightIrradiance( ambientLightColor );\n\t#if ( NUM_HEMI_LIGHTS > 0 )\n\t\t#pragma unroll_loop\n\t\tfor ( int i = 0; i < NUM_HEMI_LIGHTS; i ++ ) {\n\t\t\tirradiance += getHemisphereLightIrradiance( hemisphereLights[ i ], geometry );\n\t\t}\n\t#endif\n#endif\n#if defined( RE_IndirectSpecular )\n\tvec3 radiance = vec3( 0.0 );\n\tvec3 clearCoatRadiance = vec3( 0.0 );\n#endif\n"; | |
| var lights_fragment_maps = "#if defined( RE_IndirectDiffuse )\n\t#ifdef USE_LIGHTMAP\n\t\tvec3 lightMapIrradiance = texture2D( lightMap, vUv2 ).xyz * lightMapIntensity;\n\t\t#ifndef PHYSICALLY_CORRECT_LIGHTS\n\t\t\tlightMapIrradiance *= PI;\n\t\t#endif\n\t\tirradiance += lightMapIrradiance;\n\t#endif\n\t#if defined( USE_ENVMAP ) && defined( PHYSICAL ) && defined( ENVMAP_TYPE_CUBE_UV )\n\t\tirradiance += getLightProbeIndirectIrradiance( geometry, 8 );\n\t#endif\n#endif\n#if defined( USE_ENVMAP ) && defined( RE_IndirectSpecular )\n\tradiance += getLightProbeIndirectRadiance( geometry, Material_BlinnShininessExponent( material ), 8 );\n\t#ifndef STANDARD\n\t\tclearCoatRadiance += getLightProbeIndirectRadiance( geometry, Material_ClearCoat_BlinnShininessExponent( material ), 8 );\n\t#endif\n#endif\n"; | |
| var lights_fragment_end = "#if defined( RE_IndirectDiffuse )\n\tRE_IndirectDiffuse( irradiance, geometry, material, reflectedLight );\n#endif\n#if defined( RE_IndirectSpecular )\n\tRE_IndirectSpecular( radiance, clearCoatRadiance, geometry, material, reflectedLight );\n#endif\n"; | |
| var logdepthbuf_fragment = "#if defined( USE_LOGDEPTHBUF ) && defined( USE_LOGDEPTHBUF_EXT )\n\tgl_FragDepthEXT = log2( vFragDepth ) * logDepthBufFC * 0.5;\n#endif"; | |
| var logdepthbuf_pars_fragment = "#ifdef USE_LOGDEPTHBUF\n\tuniform float logDepthBufFC;\n\t#ifdef USE_LOGDEPTHBUF_EXT\n\t\tvarying float vFragDepth;\n\t#endif\n#endif\n"; | |
| var logdepthbuf_pars_vertex = "#ifdef USE_LOGDEPTHBUF\n\t#ifdef USE_LOGDEPTHBUF_EXT\n\t\tvarying float vFragDepth;\n\t#endif\n\tuniform float logDepthBufFC;\n#endif"; | |
| var logdepthbuf_vertex = "#ifdef USE_LOGDEPTHBUF\n\t#ifdef USE_LOGDEPTHBUF_EXT\n\t\tvFragDepth = 1.0 + gl_Position.w;\n\t#else\n\t\tgl_Position.z = log2( max( EPSILON, gl_Position.w + 1.0 ) ) * logDepthBufFC - 1.0;\n\t\tgl_Position.z *= gl_Position.w;\n\t#endif\n#endif\n"; | |
| var map_fragment = "#ifdef USE_MAP\n\tvec4 texelColor = texture2D( map, vUv );\n\ttexelColor = mapTexelToLinear( texelColor );\n\tdiffuseColor *= texelColor;\n#endif\n"; | |
| var map_pars_fragment = "#ifdef USE_MAP\n\tuniform sampler2D map;\n#endif\n"; | |
| var map_particle_fragment = "#ifdef USE_MAP\n\tvec2 uv = ( uvTransform * vec3( gl_PointCoord.x, 1.0 - gl_PointCoord.y, 1 ) ).xy;\n\tvec4 mapTexel = texture2D( map, uv );\n\tdiffuseColor *= mapTexelToLinear( mapTexel );\n#endif\n"; | |
| var map_particle_pars_fragment = "#ifdef USE_MAP\n\tuniform mat3 uvTransform;\n\tuniform sampler2D map;\n#endif\n"; | |
| var metalnessmap_fragment = "float metalnessFactor = metalness;\n#ifdef USE_METALNESSMAP\n\tvec4 texelMetalness = texture2D( metalnessMap, vUv );\n\tmetalnessFactor *= texelMetalness.b;\n#endif\n"; | |
| var metalnessmap_pars_fragment = "#ifdef USE_METALNESSMAP\n\tuniform sampler2D metalnessMap;\n#endif"; | |
| var morphnormal_vertex = "#ifdef USE_MORPHNORMALS\n\tobjectNormal += ( morphNormal0 - normal ) * morphTargetInfluences[ 0 ];\n\tobjectNormal += ( morphNormal1 - normal ) * morphTargetInfluences[ 1 ];\n\tobjectNormal += ( morphNormal2 - normal ) * morphTargetInfluences[ 2 ];\n\tobjectNormal += ( morphNormal3 - normal ) * morphTargetInfluences[ 3 ];\n#endif\n"; | |
| var morphtarget_pars_vertex = "#ifdef USE_MORPHTARGETS\n\t#ifndef USE_MORPHNORMALS\n\tuniform float morphTargetInfluences[ 8 ];\n\t#else\n\tuniform float morphTargetInfluences[ 4 ];\n\t#endif\n#endif"; | |
| var morphtarget_vertex = "#ifdef USE_MORPHTARGETS\n\ttransformed += ( morphTarget0 - position ) * morphTargetInfluences[ 0 ];\n\ttransformed += ( morphTarget1 - position ) * morphTargetInfluences[ 1 ];\n\ttransformed += ( morphTarget2 - position ) * morphTargetInfluences[ 2 ];\n\ttransformed += ( morphTarget3 - position ) * morphTargetInfluences[ 3 ];\n\t#ifndef USE_MORPHNORMALS\n\ttransformed += ( morphTarget4 - position ) * morphTargetInfluences[ 4 ];\n\ttransformed += ( morphTarget5 - position ) * morphTargetInfluences[ 5 ];\n\ttransformed += ( morphTarget6 - position ) * morphTargetInfluences[ 6 ];\n\ttransformed += ( morphTarget7 - position ) * morphTargetInfluences[ 7 ];\n\t#endif\n#endif\n"; | |
| var normal_fragment_begin = "#ifdef FLAT_SHADED\n\tvec3 fdx = vec3( dFdx( vViewPosition.x ), dFdx( vViewPosition.y ), dFdx( vViewPosition.z ) );\n\tvec3 fdy = vec3( dFdy( vViewPosition.x ), dFdy( vViewPosition.y ), dFdy( vViewPosition.z ) );\n\tvec3 normal = normalize( cross( fdx, fdy ) );\n#else\n\tvec3 normal = normalize( vNormal );\n\t#ifdef DOUBLE_SIDED\n\t\tnormal = normal * ( float( gl_FrontFacing ) * 2.0 - 1.0 );\n\t#endif\n#endif\n"; | |
| var normal_fragment_maps = "#ifdef USE_NORMALMAP\n\tnormal = perturbNormal2Arb( -vViewPosition, normal );\n#elif defined( USE_BUMPMAP )\n\tnormal = perturbNormalArb( -vViewPosition, normal, dHdxy_fwd() );\n#endif\n"; | |
| var normalmap_pars_fragment = "#ifdef USE_NORMALMAP\n\tuniform sampler2D normalMap;\n\tuniform vec2 normalScale;\n\tvec3 perturbNormal2Arb( vec3 eye_pos, vec3 surf_norm ) {\n\t\tvec3 q0 = vec3( dFdx( eye_pos.x ), dFdx( eye_pos.y ), dFdx( eye_pos.z ) );\n\t\tvec3 q1 = vec3( dFdy( eye_pos.x ), dFdy( eye_pos.y ), dFdy( eye_pos.z ) );\n\t\tvec2 st0 = dFdx( vUv.st );\n\t\tvec2 st1 = dFdy( vUv.st );\n\t\tvec3 S = normalize( q0 * st1.t - q1 * st0.t );\n\t\tvec3 T = normalize( -q0 * st1.s + q1 * st0.s );\n\t\tvec3 N = normalize( surf_norm );\n\t\tvec3 mapN = texture2D( normalMap, vUv ).xyz * 2.0 - 1.0;\n\t\tmapN.xy = normalScale * mapN.xy;\n\t\tmat3 tsn = mat3( S, T, N );\n\t\treturn normalize( tsn * mapN );\n\t}\n#endif\n"; | |
| var packing = "vec3 packNormalToRGB( const in vec3 normal ) {\n\treturn normalize( normal ) * 0.5 + 0.5;\n}\nvec3 unpackRGBToNormal( const in vec3 rgb ) {\n\treturn 2.0 * rgb.xyz - 1.0;\n}\nconst float PackUpscale = 256. / 255.;const float UnpackDownscale = 255. / 256.;\nconst vec3 PackFactors = vec3( 256. * 256. * 256., 256. * 256., 256. );\nconst vec4 UnpackFactors = UnpackDownscale / vec4( PackFactors, 1. );\nconst float ShiftRight8 = 1. / 256.;\nvec4 packDepthToRGBA( const in float v ) {\n\tvec4 r = vec4( fract( v * PackFactors ), v );\n\tr.yzw -= r.xyz * ShiftRight8;\treturn r * PackUpscale;\n}\nfloat unpackRGBAToDepth( const in vec4 v ) {\n\treturn dot( v, UnpackFactors );\n}\nfloat viewZToOrthographicDepth( const in float viewZ, const in float near, const in float far ) {\n\treturn ( viewZ + near ) / ( near - far );\n}\nfloat orthographicDepthToViewZ( const in float linearClipZ, const in float near, const in float far ) {\n\treturn linearClipZ * ( near - far ) - near;\n}\nfloat viewZToPerspectiveDepth( const in float viewZ, const in float near, const in float far ) {\n\treturn (( near + viewZ ) * far ) / (( far - near ) * viewZ );\n}\nfloat perspectiveDepthToViewZ( const in float invClipZ, const in float near, const in float far ) {\n\treturn ( near * far ) / ( ( far - near ) * invClipZ - far );\n}\n"; | |
| var premultiplied_alpha_fragment = "#ifdef PREMULTIPLIED_ALPHA\n\tgl_FragColor.rgb *= gl_FragColor.a;\n#endif\n"; | |
| var project_vertex = "vec4 mvPosition = modelViewMatrix * vec4( transformed, 1.0 );\ngl_Position = projectionMatrix * mvPosition;\n"; | |
| var dithering_fragment = "#if defined( DITHERING )\n gl_FragColor.rgb = dithering( gl_FragColor.rgb );\n#endif\n"; | |
| var dithering_pars_fragment = "#if defined( DITHERING )\n\tvec3 dithering( vec3 color ) {\n\t\tfloat grid_position = rand( gl_FragCoord.xy );\n\t\tvec3 dither_shift_RGB = vec3( 0.25 / 255.0, -0.25 / 255.0, 0.25 / 255.0 );\n\t\tdither_shift_RGB = mix( 2.0 * dither_shift_RGB, -2.0 * dither_shift_RGB, grid_position );\n\t\treturn color + dither_shift_RGB;\n\t}\n#endif\n"; | |
| var roughnessmap_fragment = "float roughnessFactor = roughness;\n#ifdef USE_ROUGHNESSMAP\n\tvec4 texelRoughness = texture2D( roughnessMap, vUv );\n\troughnessFactor *= texelRoughness.g;\n#endif\n"; | |
| var roughnessmap_pars_fragment = "#ifdef USE_ROUGHNESSMAP\n\tuniform sampler2D roughnessMap;\n#endif"; | |
| var shadowmap_pars_fragment = "#ifdef USE_SHADOWMAP\n\t#if NUM_DIR_LIGHTS > 0\n\t\tuniform sampler2D directionalShadowMap[ NUM_DIR_LIGHTS ];\n\t\tvarying vec4 vDirectionalShadowCoord[ NUM_DIR_LIGHTS ];\n\t#endif\n\t#if NUM_SPOT_LIGHTS > 0\n\t\tuniform sampler2D spotShadowMap[ NUM_SPOT_LIGHTS ];\n\t\tvarying vec4 vSpotShadowCoord[ NUM_SPOT_LIGHTS ];\n\t#endif\n\t#if NUM_POINT_LIGHTS > 0\n\t\tuniform sampler2D pointShadowMap[ NUM_POINT_LIGHTS ];\n\t\tvarying vec4 vPointShadowCoord[ NUM_POINT_LIGHTS ];\n\t#endif\n\tfloat texture2DCompare( sampler2D depths, vec2 uv, float compare ) {\n\t\treturn step( compare, unpackRGBAToDepth( texture2D( depths, uv ) ) );\n\t}\n\tfloat texture2DShadowLerp( sampler2D depths, vec2 size, vec2 uv, float compare ) {\n\t\tconst vec2 offset = vec2( 0.0, 1.0 );\n\t\tvec2 texelSize = vec2( 1.0 ) / size;\n\t\tvec2 centroidUV = floor( uv * size + 0.5 ) / size;\n\t\tfloat lb = texture2DCompare( depths, centroidUV + texelSize * offset.xx, compare );\n\t\tfloat lt = texture2DCompare( depths, centroidUV + texelSize * offset.xy, compare );\n\t\tfloat rb = texture2DCompare( depths, centroidUV + texelSize * offset.yx, compare );\n\t\tfloat rt = texture2DCompare( depths, centroidUV + texelSize * offset.yy, compare );\n\t\tvec2 f = fract( uv * size + 0.5 );\n\t\tfloat a = mix( lb, lt, f.y );\n\t\tfloat b = mix( rb, rt, f.y );\n\t\tfloat c = mix( a, b, f.x );\n\t\treturn c;\n\t}\n\tfloat getShadow( sampler2D shadowMap, vec2 shadowMapSize, float shadowBias, float shadowRadius, vec4 shadowCoord ) {\n\t\tfloat shadow = 1.0;\n\t\tshadowCoord.xyz /= shadowCoord.w;\n\t\tshadowCoord.z += shadowBias;\n\t\tbvec4 inFrustumVec = bvec4 ( shadowCoord.x >= 0.0, shadowCoord.x <= 1.0, shadowCoord.y >= 0.0, shadowCoord.y <= 1.0 );\n\t\tbool inFrustum = all( inFrustumVec );\n\t\tbvec2 frustumTestVec = bvec2( inFrustum, shadowCoord.z <= 1.0 );\n\t\tbool frustumTest = all( frustumTestVec );\n\t\tif ( frustumTest ) {\n\t\t#if defined( SHADOWMAP_TYPE_PCF )\n\t\t\tvec2 texelSize = vec2( 1.0 ) / shadowMapSize;\n\t\t\tfloat dx0 = - texelSize.x * shadowRadius;\n\t\t\tfloat dy0 = - texelSize.y * shadowRadius;\n\t\t\tfloat dx1 = + texelSize.x * shadowRadius;\n\t\t\tfloat dy1 = + texelSize.y * shadowRadius;\n\t\t\tshadow = (\n\t\t\t\ttexture2DCompare( shadowMap, shadowCoord.xy + vec2( dx0, dy0 ), shadowCoord.z ) +\n\t\t\t\ttexture2DCompare( shadowMap, shadowCoord.xy + vec2( 0.0, dy0 ), shadowCoord.z ) +\n\t\t\t\ttexture2DCompare( shadowMap, shadowCoord.xy + vec2( dx1, dy0 ), shadowCoord.z ) +\n\t\t\t\ttexture2DCompare( shadowMap, shadowCoord.xy + vec2( dx0, 0.0 ), shadowCoord.z ) +\n\t\t\t\ttexture2DCompare( shadowMap, shadowCoord.xy, shadowCoord.z ) +\n\t\t\t\ttexture2DCompare( shadowMap, shadowCoord.xy + vec2( dx1, 0.0 ), shadowCoord.z ) +\n\t\t\t\ttexture2DCompare( shadowMap, shadowCoord.xy + vec2( dx0, dy1 ), shadowCoord.z ) +\n\t\t\t\ttexture2DCompare( shadowMap, shadowCoord.xy + vec2( 0.0, dy1 ), shadowCoord.z ) +\n\t\t\t\ttexture2DCompare( shadowMap, shadowCoord.xy + vec2( dx1, dy1 ), shadowCoord.z )\n\t\t\t) * ( 1.0 / 9.0 );\n\t\t#elif defined( SHADOWMAP_TYPE_PCF_SOFT )\n\t\t\tvec2 texelSize = vec2( 1.0 ) / shadowMapSize;\n\t\t\tfloat dx0 = - texelSize.x * shadowRadius;\n\t\t\tfloat dy0 = - texelSize.y * shadowRadius;\n\t\t\tfloat dx1 = + texelSize.x * shadowRadius;\n\t\t\tfloat dy1 = + texelSize.y * shadowRadius;\n\t\t\tshadow = (\n\t\t\t\ttexture2DShadowLerp( shadowMap, shadowMapSize, shadowCoord.xy + vec2( dx0, dy0 ), shadowCoord.z ) +\n\t\t\t\ttexture2DShadowLerp( shadowMap, shadowMapSize, shadowCoord.xy + vec2( 0.0, dy0 ), shadowCoord.z ) +\n\t\t\t\ttexture2DShadowLerp( shadowMap, shadowMapSize, shadowCoord.xy + vec2( dx1, dy0 ), shadowCoord.z ) +\n\t\t\t\ttexture2DShadowLerp( shadowMap, shadowMapSize, shadowCoord.xy + vec2( dx0, 0.0 ), shadowCoord.z ) +\n\t\t\t\ttexture2DShadowLerp( shadowMap, shadowMapSize, shadowCoord.xy, shadowCoord.z ) +\n\t\t\t\ttexture2DShadowLerp( shadowMap, shadowMapSize, shadowCoord.xy + vec2( dx1, 0.0 ), shadowCoord.z ) +\n\t\t\t\ttexture2DShadowLerp( shadowMap, shadowMapSize, shadowCoord.xy + vec2( dx0, dy1 ), shadowCoord.z ) +\n\t\t\t\ttexture2DShadowLerp( shadowMap, shadowMapSize, shadowCoord.xy + vec2( 0.0, dy1 ), shadowCoord.z ) +\n\t\t\t\ttexture2DShadowLerp( shadowMap, shadowMapSize, shadowCoord.xy + vec2( dx1, dy1 ), shadowCoord.z )\n\t\t\t) * ( 1.0 / 9.0 );\n\t\t#else\n\t\t\tshadow = texture2DCompare( shadowMap, shadowCoord.xy, shadowCoord.z );\n\t\t#endif\n\t\t}\n\t\treturn shadow;\n\t}\n\tvec2 cubeToUV( vec3 v, float texelSizeY ) {\n\t\tvec3 absV = abs( v );\n\t\tfloat scaleToCube = 1.0 / max( absV.x, max( absV.y, absV.z ) );\n\t\tabsV *= scaleToCube;\n\t\tv *= scaleToCube * ( 1.0 - 2.0 * texelSizeY );\n\t\tvec2 planar = v.xy;\n\t\tfloat almostATexel = 1.5 * texelSizeY;\n\t\tfloat almostOne = 1.0 - almostATexel;\n\t\tif ( absV.z >= almostOne ) {\n\t\t\tif ( v.z > 0.0 )\n\t\t\t\tplanar.x = 4.0 - v.x;\n\t\t} else if ( absV.x >= almostOne ) {\n\t\t\tfloat signX = sign( v.x );\n\t\t\tplanar.x = v.z * signX + 2.0 * signX;\n\t\t} else if ( absV.y >= almostOne ) {\n\t\t\tfloat signY = sign( v.y );\n\t\t\tplanar.x = v.x + 2.0 * signY + 2.0;\n\t\t\tplanar.y = v.z * signY - 2.0;\n\t\t}\n\t\treturn vec2( 0.125, 0.25 ) * planar + vec2( 0.375, 0.75 );\n\t}\n\tfloat getPointShadow( sampler2D shadowMap, vec2 shadowMapSize, float shadowBias, float shadowRadius, vec4 shadowCoord, float shadowCameraNear, float shadowCameraFar ) {\n\t\tvec2 texelSize = vec2( 1.0 ) / ( shadowMapSize * vec2( 4.0, 2.0 ) );\n\t\tvec3 lightToPosition = shadowCoord.xyz;\n\t\tfloat dp = ( length( lightToPosition ) - shadowCameraNear ) / ( shadowCameraFar - shadowCameraNear );\t\tdp += shadowBias;\n\t\tvec3 bd3D = normalize( lightToPosition );\n\t\t#if defined( SHADOWMAP_TYPE_PCF ) || defined( SHADOWMAP_TYPE_PCF_SOFT )\n\t\t\tvec2 offset = vec2( - 1, 1 ) * shadowRadius * texelSize.y;\n\t\t\treturn (\n\t\t\t\ttexture2DCompare( shadowMap, cubeToUV( bd3D + offset.xyy, texelSize.y ), dp ) +\n\t\t\t\ttexture2DCompare( shadowMap, cubeToUV( bd3D + offset.yyy, texelSize.y ), dp ) +\n\t\t\t\ttexture2DCompare( shadowMap, cubeToUV( bd3D + offset.xyx, texelSize.y ), dp ) +\n\t\t\t\ttexture2DCompare( shadowMap, cubeToUV( bd3D + offset.yyx, texelSize.y ), dp ) +\n\t\t\t\ttexture2DCompare( shadowMap, cubeToUV( bd3D, texelSize.y ), dp ) +\n\t\t\t\ttexture2DCompare( shadowMap, cubeToUV( bd3D + offset.xxy, texelSize.y ), dp ) +\n\t\t\t\ttexture2DCompare( shadowMap, cubeToUV( bd3D + offset.yxy, texelSize.y ), dp ) +\n\t\t\t\ttexture2DCompare( shadowMap, cubeToUV( bd3D + offset.xxx, texelSize.y ), dp ) +\n\t\t\t\ttexture2DCompare( shadowMap, cubeToUV( bd3D + offset.yxx, texelSize.y ), dp )\n\t\t\t) * ( 1.0 / 9.0 );\n\t\t#else\n\t\t\treturn texture2DCompare( shadowMap, cubeToUV( bd3D, texelSize.y ), dp );\n\t\t#endif\n\t}\n#endif\n"; | |
| var shadowmap_pars_vertex = "#ifdef USE_SHADOWMAP\n\t#if NUM_DIR_LIGHTS > 0\n\t\tuniform mat4 directionalShadowMatrix[ NUM_DIR_LIGHTS ];\n\t\tvarying vec4 vDirectionalShadowCoord[ NUM_DIR_LIGHTS ];\n\t#endif\n\t#if NUM_SPOT_LIGHTS > 0\n\t\tuniform mat4 spotShadowMatrix[ NUM_SPOT_LIGHTS ];\n\t\tvarying vec4 vSpotShadowCoord[ NUM_SPOT_LIGHTS ];\n\t#endif\n\t#if NUM_POINT_LIGHTS > 0\n\t\tuniform mat4 pointShadowMatrix[ NUM_POINT_LIGHTS ];\n\t\tvarying vec4 vPointShadowCoord[ NUM_POINT_LIGHTS ];\n\t#endif\n#endif\n"; | |
| var shadowmap_vertex = "#ifdef USE_SHADOWMAP\n\t#if NUM_DIR_LIGHTS > 0\n\t#pragma unroll_loop\n\tfor ( int i = 0; i < NUM_DIR_LIGHTS; i ++ ) {\n\t\tvDirectionalShadowCoord[ i ] = directionalShadowMatrix[ i ] * worldPosition;\n\t}\n\t#endif\n\t#if NUM_SPOT_LIGHTS > 0\n\t#pragma unroll_loop\n\tfor ( int i = 0; i < NUM_SPOT_LIGHTS; i ++ ) {\n\t\tvSpotShadowCoord[ i ] = spotShadowMatrix[ i ] * worldPosition;\n\t}\n\t#endif\n\t#if NUM_POINT_LIGHTS > 0\n\t#pragma unroll_loop\n\tfor ( int i = 0; i < NUM_POINT_LIGHTS; i ++ ) {\n\t\tvPointShadowCoord[ i ] = pointShadowMatrix[ i ] * worldPosition;\n\t}\n\t#endif\n#endif\n"; | |
| var shadowmask_pars_fragment = "float getShadowMask() {\n\tfloat shadow = 1.0;\n\t#ifdef USE_SHADOWMAP\n\t#if NUM_DIR_LIGHTS > 0\n\tDirectionalLight directionalLight;\n\t#pragma unroll_loop\n\tfor ( int i = 0; i < NUM_DIR_LIGHTS; i ++ ) {\n\t\tdirectionalLight = directionalLights[ i ];\n\t\tshadow *= bool( directionalLight.shadow ) ? getShadow( directionalShadowMap[ i ], directionalLight.shadowMapSize, directionalLight.shadowBias, directionalLight.shadowRadius, vDirectionalShadowCoord[ i ] ) : 1.0;\n\t}\n\t#endif\n\t#if NUM_SPOT_LIGHTS > 0\n\tSpotLight spotLight;\n\t#pragma unroll_loop\n\tfor ( int i = 0; i < NUM_SPOT_LIGHTS; i ++ ) {\n\t\tspotLight = spotLights[ i ];\n\t\tshadow *= bool( spotLight.shadow ) ? getShadow( spotShadowMap[ i ], spotLight.shadowMapSize, spotLight.shadowBias, spotLight.shadowRadius, vSpotShadowCoord[ i ] ) : 1.0;\n\t}\n\t#endif\n\t#if NUM_POINT_LIGHTS > 0\n\tPointLight pointLight;\n\t#pragma unroll_loop\n\tfor ( int i = 0; i < NUM_POINT_LIGHTS; i ++ ) {\n\t\tpointLight = pointLights[ i ];\n\t\tshadow *= bool( pointLight.shadow ) ? getPointShadow( pointShadowMap[ i ], pointLight.shadowMapSize, pointLight.shadowBias, pointLight.shadowRadius, vPointShadowCoord[ i ], pointLight.shadowCameraNear, pointLight.shadowCameraFar ) : 1.0;\n\t}\n\t#endif\n\t#endif\n\treturn shadow;\n}\n"; | |
| var skinbase_vertex = "#ifdef USE_SKINNING\n\tmat4 boneMatX = getBoneMatrix( skinIndex.x );\n\tmat4 boneMatY = getBoneMatrix( skinIndex.y );\n\tmat4 boneMatZ = getBoneMatrix( skinIndex.z );\n\tmat4 boneMatW = getBoneMatrix( skinIndex.w );\n#endif"; | |
| var skinning_pars_vertex = "#ifdef USE_SKINNING\n\tuniform mat4 bindMatrix;\n\tuniform mat4 bindMatrixInverse;\n\t#ifdef BONE_TEXTURE\n\t\tuniform sampler2D boneTexture;\n\t\tuniform int boneTextureSize;\n\t\tmat4 getBoneMatrix( const in float i ) {\n\t\t\tfloat j = i * 4.0;\n\t\t\tfloat x = mod( j, float( boneTextureSize ) );\n\t\t\tfloat y = floor( j / float( boneTextureSize ) );\n\t\t\tfloat dx = 1.0 / float( boneTextureSize );\n\t\t\tfloat dy = 1.0 / float( boneTextureSize );\n\t\t\ty = dy * ( y + 0.5 );\n\t\t\tvec4 v1 = texture2D( boneTexture, vec2( dx * ( x + 0.5 ), y ) );\n\t\t\tvec4 v2 = texture2D( boneTexture, vec2( dx * ( x + 1.5 ), y ) );\n\t\t\tvec4 v3 = texture2D( boneTexture, vec2( dx * ( x + 2.5 ), y ) );\n\t\t\tvec4 v4 = texture2D( boneTexture, vec2( dx * ( x + 3.5 ), y ) );\n\t\t\tmat4 bone = mat4( v1, v2, v3, v4 );\n\t\t\treturn bone;\n\t\t}\n\t#else\n\t\tuniform mat4 boneMatrices[ MAX_BONES ];\n\t\tmat4 getBoneMatrix( const in float i ) {\n\t\t\tmat4 bone = boneMatrices[ int(i) ];\n\t\t\treturn bone;\n\t\t}\n\t#endif\n#endif\n"; | |
| var skinning_vertex = "#ifdef USE_SKINNING\n\tvec4 skinVertex = bindMatrix * vec4( transformed, 1.0 );\n\tvec4 skinned = vec4( 0.0 );\n\tskinned += boneMatX * skinVertex * skinWeight.x;\n\tskinned += boneMatY * skinVertex * skinWeight.y;\n\tskinned += boneMatZ * skinVertex * skinWeight.z;\n\tskinned += boneMatW * skinVertex * skinWeight.w;\n\ttransformed = ( bindMatrixInverse * skinned ).xyz;\n#endif\n"; | |
| var skinnormal_vertex = "#ifdef USE_SKINNING\n\tmat4 skinMatrix = mat4( 0.0 );\n\tskinMatrix += skinWeight.x * boneMatX;\n\tskinMatrix += skinWeight.y * boneMatY;\n\tskinMatrix += skinWeight.z * boneMatZ;\n\tskinMatrix += skinWeight.w * boneMatW;\n\tskinMatrix = bindMatrixInverse * skinMatrix * bindMatrix;\n\tobjectNormal = vec4( skinMatrix * vec4( objectNormal, 0.0 ) ).xyz;\n#endif\n"; | |
| var specularmap_fragment = "float specularStrength;\n#ifdef USE_SPECULARMAP\n\tvec4 texelSpecular = texture2D( specularMap, vUv );\n\tspecularStrength = texelSpecular.r;\n#else\n\tspecularStrength = 1.0;\n#endif"; | |
| var specularmap_pars_fragment = "#ifdef USE_SPECULARMAP\n\tuniform sampler2D specularMap;\n#endif"; | |
| var tonemapping_fragment = "#if defined( TONE_MAPPING )\n gl_FragColor.rgb = toneMapping( gl_FragColor.rgb );\n#endif\n"; | |
| var tonemapping_pars_fragment = "#ifndef saturate\n\t#define saturate(a) clamp( a, 0.0, 1.0 )\n#endif\nuniform float toneMappingExposure;\nuniform float toneMappingWhitePoint;\nvec3 LinearToneMapping( vec3 color ) {\n\treturn toneMappingExposure * color;\n}\nvec3 ReinhardToneMapping( vec3 color ) {\n\tcolor *= toneMappingExposure;\n\treturn saturate( color / ( vec3( 1.0 ) + color ) );\n}\n#define Uncharted2Helper( x ) max( ( ( x * ( 0.15 * x + 0.10 * 0.50 ) + 0.20 * 0.02 ) / ( x * ( 0.15 * x + 0.50 ) + 0.20 * 0.30 ) ) - 0.02 / 0.30, vec3( 0.0 ) )\nvec3 Uncharted2ToneMapping( vec3 color ) {\n\tcolor *= toneMappingExposure;\n\treturn saturate( Uncharted2Helper( color ) / Uncharted2Helper( vec3( toneMappingWhitePoint ) ) );\n}\nvec3 OptimizedCineonToneMapping( vec3 color ) {\n\tcolor *= toneMappingExposure;\n\tcolor = max( vec3( 0.0 ), color - 0.004 );\n\treturn pow( ( color * ( 6.2 * color + 0.5 ) ) / ( color * ( 6.2 * color + 1.7 ) + 0.06 ), vec3( 2.2 ) );\n}\n"; | |
| var uv_pars_fragment = "#if defined( USE_MAP ) || defined( USE_BUMPMAP ) || defined( USE_NORMALMAP ) || defined( USE_SPECULARMAP ) || defined( USE_ALPHAMAP ) || defined( USE_EMISSIVEMAP ) || defined( USE_ROUGHNESSMAP ) || defined( USE_METALNESSMAP )\n\tvarying vec2 vUv;\n#endif"; | |
| var uv_pars_vertex = "#if defined( USE_MAP ) || defined( USE_BUMPMAP ) || defined( USE_NORMALMAP ) || defined( USE_SPECULARMAP ) || defined( USE_ALPHAMAP ) || defined( USE_EMISSIVEMAP ) || defined( USE_ROUGHNESSMAP ) || defined( USE_METALNESSMAP )\n\tvarying vec2 vUv;\n\tuniform mat3 uvTransform;\n#endif\n"; | |
| var uv_vertex = "#if defined( USE_MAP ) || defined( USE_BUMPMAP ) || defined( USE_NORMALMAP ) || defined( USE_SPECULARMAP ) || defined( USE_ALPHAMAP ) || defined( USE_EMISSIVEMAP ) || defined( USE_ROUGHNESSMAP ) || defined( USE_METALNESSMAP )\n\tvUv = ( uvTransform * vec3( uv, 1 ) ).xy;\n#endif"; | |
| var uv2_pars_fragment = "#if defined( USE_LIGHTMAP ) || defined( USE_AOMAP )\n\tvarying vec2 vUv2;\n#endif"; | |
| var uv2_pars_vertex = "#if defined( USE_LIGHTMAP ) || defined( USE_AOMAP )\n\tattribute vec2 uv2;\n\tvarying vec2 vUv2;\n#endif"; | |
| var uv2_vertex = "#if defined( USE_LIGHTMAP ) || defined( USE_AOMAP )\n\tvUv2 = uv2;\n#endif"; | |
| var worldpos_vertex = "#if defined( USE_ENVMAP ) || defined( DISTANCE ) || defined ( USE_SHADOWMAP )\n\tvec4 worldPosition = modelMatrix * vec4( transformed, 1.0 );\n#endif\n"; | |
| var cube_frag = "uniform samplerCube tCube;\nuniform float tFlip;\nuniform float opacity;\nvarying vec3 vWorldPosition;\nvoid main() {\n\tgl_FragColor = textureCube( tCube, vec3( tFlip * vWorldPosition.x, vWorldPosition.yz ) );\n\tgl_FragColor.a *= opacity;\n}\n"; | |
| var cube_vert = "varying vec3 vWorldPosition;\n#include <common>\nvoid main() {\n\tvWorldPosition = transformDirection( position, modelMatrix );\n\t#include <begin_vertex>\n\t#include <project_vertex>\n\tgl_Position.z = gl_Position.w;\n}\n"; | |
| var depth_frag = "#if DEPTH_PACKING == 3200\n\tuniform float opacity;\n#endif\n#include <common>\n#include <packing>\n#include <uv_pars_fragment>\n#include <map_pars_fragment>\n#include <alphamap_pars_fragment>\n#include <logdepthbuf_pars_fragment>\n#include <clipping_planes_pars_fragment>\nvoid main() {\n\t#include <clipping_planes_fragment>\n\tvec4 diffuseColor = vec4( 1.0 );\n\t#if DEPTH_PACKING == 3200\n\t\tdiffuseColor.a = opacity;\n\t#endif\n\t#include <map_fragment>\n\t#include <alphamap_fragment>\n\t#include <alphatest_fragment>\n\t#include <logdepthbuf_fragment>\n\t#if DEPTH_PACKING == 3200\n\t\tgl_FragColor = vec4( vec3( gl_FragCoord.z ), opacity );\n\t#elif DEPTH_PACKING == 3201\n\t\tgl_FragColor = packDepthToRGBA( gl_FragCoord.z );\n\t#endif\n}\n"; | |
| var depth_vert = "#include <common>\n#include <uv_pars_vertex>\n#include <displacementmap_pars_vertex>\n#include <morphtarget_pars_vertex>\n#include <skinning_pars_vertex>\n#include <logdepthbuf_pars_vertex>\n#include <clipping_planes_pars_vertex>\nvoid main() {\n\t#include <uv_vertex>\n\t#include <skinbase_vertex>\n\t#ifdef USE_DISPLACEMENTMAP\n\t\t#include <beginnormal_vertex>\n\t\t#include <morphnormal_vertex>\n\t\t#include <skinnormal_vertex>\n\t#endif\n\t#include <begin_vertex>\n\t#include <morphtarget_vertex>\n\t#include <skinning_vertex>\n\t#include <displacementmap_vertex>\n\t#include <project_vertex>\n\t#include <logdepthbuf_vertex>\n\t#include <clipping_planes_vertex>\n}\n"; | |
| var distanceRGBA_frag = "#define DISTANCE\nuniform vec3 referencePosition;\nuniform float nearDistance;\nuniform float farDistance;\nvarying vec3 vWorldPosition;\n#include <common>\n#include <packing>\n#include <uv_pars_fragment>\n#include <map_pars_fragment>\n#include <alphamap_pars_fragment>\n#include <clipping_planes_pars_fragment>\nvoid main () {\n\t#include <clipping_planes_fragment>\n\tvec4 diffuseColor = vec4( 1.0 );\n\t#include <map_fragment>\n\t#include <alphamap_fragment>\n\t#include <alphatest_fragment>\n\tfloat dist = length( vWorldPosition - referencePosition );\n\tdist = ( dist - nearDistance ) / ( farDistance - nearDistance );\n\tdist = saturate( dist );\n\tgl_FragColor = packDepthToRGBA( dist );\n}\n"; | |
| var distanceRGBA_vert = "#define DISTANCE\nvarying vec3 vWorldPosition;\n#include <common>\n#include <uv_pars_vertex>\n#include <displacementmap_pars_vertex>\n#include <morphtarget_pars_vertex>\n#include <skinning_pars_vertex>\n#include <clipping_planes_pars_vertex>\nvoid main() {\n\t#include <uv_vertex>\n\t#include <skinbase_vertex>\n\t#ifdef USE_DISPLACEMENTMAP\n\t\t#include <beginnormal_vertex>\n\t\t#include <morphnormal_vertex>\n\t\t#include <skinnormal_vertex>\n\t#endif\n\t#include <begin_vertex>\n\t#include <morphtarget_vertex>\n\t#include <skinning_vertex>\n\t#include <displacementmap_vertex>\n\t#include <project_vertex>\n\t#include <worldpos_vertex>\n\t#include <clipping_planes_vertex>\n\tvWorldPosition = worldPosition.xyz;\n}\n"; | |
| var equirect_frag = "uniform sampler2D tEquirect;\nvarying vec3 vWorldPosition;\n#include <common>\nvoid main() {\n\tvec3 direction = normalize( vWorldPosition );\n\tvec2 sampleUV;\n\tsampleUV.y = asin( clamp( direction.y, - 1.0, 1.0 ) ) * RECIPROCAL_PI + 0.5;\n\tsampleUV.x = atan( direction.z, direction.x ) * RECIPROCAL_PI2 + 0.5;\n\tgl_FragColor = texture2D( tEquirect, sampleUV );\n}\n"; | |
| var equirect_vert = "varying vec3 vWorldPosition;\n#include <common>\nvoid main() {\n\tvWorldPosition = transformDirection( position, modelMatrix );\n\t#include <begin_vertex>\n\t#include <project_vertex>\n}\n"; | |
| var linedashed_frag = "uniform vec3 diffuse;\nuniform float opacity;\nuniform float dashSize;\nuniform float totalSize;\nvarying float vLineDistance;\n#include <common>\n#include <color_pars_fragment>\n#include <fog_pars_fragment>\n#include <logdepthbuf_pars_fragment>\n#include <clipping_planes_pars_fragment>\nvoid main() {\n\t#include <clipping_planes_fragment>\n\tif ( mod( vLineDistance, totalSize ) > dashSize ) {\n\t\tdiscard;\n\t}\n\tvec3 outgoingLight = vec3( 0.0 );\n\tvec4 diffuseColor = vec4( diffuse, opacity );\n\t#include <logdepthbuf_fragment>\n\t#include <color_fragment>\n\toutgoingLight = diffuseColor.rgb;\n\tgl_FragColor = vec4( outgoingLight, diffuseColor.a );\n\t#include <premultiplied_alpha_fragment>\n\t#include <tonemapping_fragment>\n\t#include <encodings_fragment>\n\t#include <fog_fragment>\n}\n"; | |
| var linedashed_vert = "uniform float scale;\nattribute float lineDistance;\nvarying float vLineDistance;\n#include <common>\n#include <color_pars_vertex>\n#include <fog_pars_vertex>\n#include <logdepthbuf_pars_vertex>\n#include <clipping_planes_pars_vertex>\nvoid main() {\n\t#include <color_vertex>\n\tvLineDistance = scale * lineDistance;\n\tvec4 mvPosition = modelViewMatrix * vec4( position, 1.0 );\n\tgl_Position = projectionMatrix * mvPosition;\n\t#include <logdepthbuf_vertex>\n\t#include <clipping_planes_vertex>\n\t#include <fog_vertex>\n}\n"; | |
| var meshbasic_frag = "uniform vec3 diffuse;\nuniform float opacity;\n#ifndef FLAT_SHADED\n\tvarying vec3 vNormal;\n#endif\n#include <common>\n#include <color_pars_fragment>\n#include <uv_pars_fragment>\n#include <uv2_pars_fragment>\n#include <map_pars_fragment>\n#include <alphamap_pars_fragment>\n#include <aomap_pars_fragment>\n#include <lightmap_pars_fragment>\n#include <envmap_pars_fragment>\n#include <fog_pars_fragment>\n#include <specularmap_pars_fragment>\n#include <logdepthbuf_pars_fragment>\n#include <clipping_planes_pars_fragment>\nvoid main() {\n\t#include <clipping_planes_fragment>\n\tvec4 diffuseColor = vec4( diffuse, opacity );\n\t#include <logdepthbuf_fragment>\n\t#include <map_fragment>\n\t#include <color_fragment>\n\t#include <alphamap_fragment>\n\t#include <alphatest_fragment>\n\t#include <specularmap_fragment>\n\tReflectedLight reflectedLight = ReflectedLight( vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ) );\n\t#ifdef USE_LIGHTMAP\n\t\treflectedLight.indirectDiffuse += texture2D( lightMap, vUv2 ).xyz * lightMapIntensity;\n\t#else\n\t\treflectedLight.indirectDiffuse += vec3( 1.0 );\n\t#endif\n\t#include <aomap_fragment>\n\treflectedLight.indirectDiffuse *= diffuseColor.rgb;\n\tvec3 outgoingLight = reflectedLight.indirectDiffuse;\n\t#include <envmap_fragment>\n\tgl_FragColor = vec4( outgoingLight, diffuseColor.a );\n\t#include <premultiplied_alpha_fragment>\n\t#include <tonemapping_fragment>\n\t#include <encodings_fragment>\n\t#include <fog_fragment>\n}\n"; | |
| var meshbasic_vert = "#include <common>\n#include <uv_pars_vertex>\n#include <uv2_pars_vertex>\n#include <envmap_pars_vertex>\n#include <color_pars_vertex>\n#include <fog_pars_vertex>\n#include <morphtarget_pars_vertex>\n#include <skinning_pars_vertex>\n#include <logdepthbuf_pars_vertex>\n#include <clipping_planes_pars_vertex>\nvoid main() {\n\t#include <uv_vertex>\n\t#include <uv2_vertex>\n\t#include <color_vertex>\n\t#include <skinbase_vertex>\n\t#ifdef USE_ENVMAP\n\t#include <beginnormal_vertex>\n\t#include <morphnormal_vertex>\n\t#include <skinnormal_vertex>\n\t#include <defaultnormal_vertex>\n\t#endif\n\t#include <begin_vertex>\n\t#include <morphtarget_vertex>\n\t#include <skinning_vertex>\n\t#include <project_vertex>\n\t#include <logdepthbuf_vertex>\n\t#include <worldpos_vertex>\n\t#include <clipping_planes_vertex>\n\t#include <envmap_vertex>\n\t#include <fog_vertex>\n}\n"; | |
| var meshlambert_frag = "uniform vec3 diffuse;\nuniform vec3 emissive;\nuniform float opacity;\nvarying vec3 vLightFront;\n#ifdef DOUBLE_SIDED\n\tvarying vec3 vLightBack;\n#endif\n#include <common>\n#include <packing>\n#include <dithering_pars_fragment>\n#include <color_pars_fragment>\n#include <uv_pars_fragment>\n#include <uv2_pars_fragment>\n#include <map_pars_fragment>\n#include <alphamap_pars_fragment>\n#include <aomap_pars_fragment>\n#include <lightmap_pars_fragment>\n#include <emissivemap_pars_fragment>\n#include <envmap_pars_fragment>\n#include <bsdfs>\n#include <lights_pars_begin>\n#include <lights_pars_maps>\n#include <fog_pars_fragment>\n#include <shadowmap_pars_fragment>\n#include <shadowmask_pars_fragment>\n#include <specularmap_pars_fragment>\n#include <logdepthbuf_pars_fragment>\n#include <clipping_planes_pars_fragment>\nvoid main() {\n\t#include <clipping_planes_fragment>\n\tvec4 diffuseColor = vec4( diffuse, opacity );\n\tReflectedLight reflectedLight = ReflectedLight( vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ) );\n\tvec3 totalEmissiveRadiance = emissive;\n\t#include <logdepthbuf_fragment>\n\t#include <map_fragment>\n\t#include <color_fragment>\n\t#include <alphamap_fragment>\n\t#include <alphatest_fragment>\n\t#include <specularmap_fragment>\n\t#include <emissivemap_fragment>\n\treflectedLight.indirectDiffuse = getAmbientLightIrradiance( ambientLightColor );\n\t#include <lightmap_fragment>\n\treflectedLight.indirectDiffuse *= BRDF_Diffuse_Lambert( diffuseColor.rgb );\n\t#ifdef DOUBLE_SIDED\n\t\treflectedLight.directDiffuse = ( gl_FrontFacing ) ? vLightFront : vLightBack;\n\t#else\n\t\treflectedLight.directDiffuse = vLightFront;\n\t#endif\n\treflectedLight.directDiffuse *= BRDF_Diffuse_Lambert( diffuseColor.rgb ) * getShadowMask();\n\t#include <aomap_fragment>\n\tvec3 outgoingLight = reflectedLight.directDiffuse + reflectedLight.indirectDiffuse + totalEmissiveRadiance;\n\t#include <envmap_fragment>\n\tgl_FragColor = vec4( outgoingLight, diffuseColor.a );\n\t#include <tonemapping_fragment>\n\t#include <encodings_fragment>\n\t#include <fog_fragment>\n\t#include <premultiplied_alpha_fragment>\n\t#include <dithering_fragment>\n}\n"; | |
| var meshlambert_vert = "#define LAMBERT\nvarying vec3 vLightFront;\n#ifdef DOUBLE_SIDED\n\tvarying vec3 vLightBack;\n#endif\n#include <common>\n#include <uv_pars_vertex>\n#include <uv2_pars_vertex>\n#include <envmap_pars_vertex>\n#include <bsdfs>\n#include <lights_pars_begin>\n#include <lights_pars_maps>\n#include <color_pars_vertex>\n#include <fog_pars_vertex>\n#include <morphtarget_pars_vertex>\n#include <skinning_pars_vertex>\n#include <shadowmap_pars_vertex>\n#include <logdepthbuf_pars_vertex>\n#include <clipping_planes_pars_vertex>\nvoid main() {\n\t#include <uv_vertex>\n\t#include <uv2_vertex>\n\t#include <color_vertex>\n\t#include <beginnormal_vertex>\n\t#include <morphnormal_vertex>\n\t#include <skinbase_vertex>\n\t#include <skinnormal_vertex>\n\t#include <defaultnormal_vertex>\n\t#include <begin_vertex>\n\t#include <morphtarget_vertex>\n\t#include <skinning_vertex>\n\t#include <project_vertex>\n\t#include <logdepthbuf_vertex>\n\t#include <clipping_planes_vertex>\n\t#include <worldpos_vertex>\n\t#include <envmap_vertex>\n\t#include <lights_lambert_vertex>\n\t#include <shadowmap_vertex>\n\t#include <fog_vertex>\n}\n"; | |
| var meshphong_frag = "#define PHONG\nuniform vec3 diffuse;\nuniform vec3 emissive;\nuniform vec3 specular;\nuniform float shininess;\nuniform float opacity;\n#include <common>\n#include <packing>\n#include <dithering_pars_fragment>\n#include <color_pars_fragment>\n#include <uv_pars_fragment>\n#include <uv2_pars_fragment>\n#include <map_pars_fragment>\n#include <alphamap_pars_fragment>\n#include <aomap_pars_fragment>\n#include <lightmap_pars_fragment>\n#include <emissivemap_pars_fragment>\n#include <envmap_pars_fragment>\n#include <gradientmap_pars_fragment>\n#include <fog_pars_fragment>\n#include <bsdfs>\n#include <lights_pars_begin>\n#include <lights_pars_maps>\n#include <lights_phong_pars_fragment>\n#include <shadowmap_pars_fragment>\n#include <bumpmap_pars_fragment>\n#include <normalmap_pars_fragment>\n#include <specularmap_pars_fragment>\n#include <logdepthbuf_pars_fragment>\n#include <clipping_planes_pars_fragment>\nvoid main() {\n\t#include <clipping_planes_fragment>\n\tvec4 diffuseColor = vec4( diffuse, opacity );\n\tReflectedLight reflectedLight = ReflectedLight( vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ) );\n\tvec3 totalEmissiveRadiance = emissive;\n\t#include <logdepthbuf_fragment>\n\t#include <map_fragment>\n\t#include <color_fragment>\n\t#include <alphamap_fragment>\n\t#include <alphatest_fragment>\n\t#include <specularmap_fragment>\n\t#include <normal_fragment_begin>\n\t#include <normal_fragment_maps>\n\t#include <emissivemap_fragment>\n\t#include <lights_phong_fragment>\n\t#include <lights_fragment_begin>\n\t#include <lights_fragment_maps>\n\t#include <lights_fragment_end>\n\t#include <aomap_fragment>\n\tvec3 outgoingLight = reflectedLight.directDiffuse + reflectedLight.indirectDiffuse + reflectedLight.directSpecular + reflectedLight.indirectSpecular + totalEmissiveRadiance;\n\t#include <envmap_fragment>\n\tgl_FragColor = vec4( outgoingLight, diffuseColor.a );\n\t#include <tonemapping_fragment>\n\t#include <encodings_fragment>\n\t#include <fog_fragment>\n\t#include <premultiplied_alpha_fragment>\n\t#include <dithering_fragment>\n}\n"; | |
| var meshphong_vert = "#define PHONG\nvarying vec3 vViewPosition;\n#ifndef FLAT_SHADED\n\tvarying vec3 vNormal;\n#endif\n#include <common>\n#include <uv_pars_vertex>\n#include <uv2_pars_vertex>\n#include <displacementmap_pars_vertex>\n#include <envmap_pars_vertex>\n#include <color_pars_vertex>\n#include <fog_pars_vertex>\n#include <morphtarget_pars_vertex>\n#include <skinning_pars_vertex>\n#include <shadowmap_pars_vertex>\n#include <logdepthbuf_pars_vertex>\n#include <clipping_planes_pars_vertex>\nvoid main() {\n\t#include <uv_vertex>\n\t#include <uv2_vertex>\n\t#include <color_vertex>\n\t#include <beginnormal_vertex>\n\t#include <morphnormal_vertex>\n\t#include <skinbase_vertex>\n\t#include <skinnormal_vertex>\n\t#include <defaultnormal_vertex>\n#ifndef FLAT_SHADED\n\tvNormal = normalize( transformedNormal );\n#endif\n\t#include <begin_vertex>\n\t#include <morphtarget_vertex>\n\t#include <skinning_vertex>\n\t#include <displacementmap_vertex>\n\t#include <project_vertex>\n\t#include <logdepthbuf_vertex>\n\t#include <clipping_planes_vertex>\n\tvViewPosition = - mvPosition.xyz;\n\t#include <worldpos_vertex>\n\t#include <envmap_vertex>\n\t#include <shadowmap_vertex>\n\t#include <fog_vertex>\n}\n"; | |
| var meshphysical_frag = "#define PHYSICAL\nuniform vec3 diffuse;\nuniform vec3 emissive;\nuniform float roughness;\nuniform float metalness;\nuniform float opacity;\n#ifndef STANDARD\n\tuniform float clearCoat;\n\tuniform float clearCoatRoughness;\n#endif\nvarying vec3 vViewPosition;\n#ifndef FLAT_SHADED\n\tvarying vec3 vNormal;\n#endif\n#include <common>\n#include <packing>\n#include <dithering_pars_fragment>\n#include <color_pars_fragment>\n#include <uv_pars_fragment>\n#include <uv2_pars_fragment>\n#include <map_pars_fragment>\n#include <alphamap_pars_fragment>\n#include <aomap_pars_fragment>\n#include <lightmap_pars_fragment>\n#include <emissivemap_pars_fragment>\n#include <envmap_pars_fragment>\n#include <fog_pars_fragment>\n#include <bsdfs>\n#include <cube_uv_reflection_fragment>\n#include <lights_pars_begin>\n#include <lights_pars_maps>\n#include <lights_physical_pars_fragment>\n#include <shadowmap_pars_fragment>\n#include <bumpmap_pars_fragment>\n#include <normalmap_pars_fragment>\n#include <roughnessmap_pars_fragment>\n#include <metalnessmap_pars_fragment>\n#include <logdepthbuf_pars_fragment>\n#include <clipping_planes_pars_fragment>\nvoid main() {\n\t#include <clipping_planes_fragment>\n\tvec4 diffuseColor = vec4( diffuse, opacity );\n\tReflectedLight reflectedLight = ReflectedLight( vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ) );\n\tvec3 totalEmissiveRadiance = emissive;\n\t#include <logdepthbuf_fragment>\n\t#include <map_fragment>\n\t#include <color_fragment>\n\t#include <alphamap_fragment>\n\t#include <alphatest_fragment>\n\t#include <roughnessmap_fragment>\n\t#include <metalnessmap_fragment>\n\t#include <normal_fragment_begin>\n\t#include <normal_fragment_maps>\n\t#include <emissivemap_fragment>\n\t#include <lights_physical_fragment>\n\t#include <lights_fragment_begin>\n\t#include <lights_fragment_maps>\n\t#include <lights_fragment_end>\n\t#include <aomap_fragment>\n\tvec3 outgoingLight = reflectedLight.directDiffuse + reflectedLight.indirectDiffuse + reflectedLight.directSpecular + reflectedLight.indirectSpecular + totalEmissiveRadiance;\n\tgl_FragColor = vec4( outgoingLight, diffuseColor.a );\n\t#include <tonemapping_fragment>\n\t#include <encodings_fragment>\n\t#include <fog_fragment>\n\t#include <premultiplied_alpha_fragment>\n\t#include <dithering_fragment>\n}\n"; | |
| var meshphysical_vert = "#define PHYSICAL\nvarying vec3 vViewPosition;\n#ifndef FLAT_SHADED\n\tvarying vec3 vNormal;\n#endif\n#include <common>\n#include <uv_pars_vertex>\n#include <uv2_pars_vertex>\n#include <displacementmap_pars_vertex>\n#include <color_pars_vertex>\n#include <fog_pars_vertex>\n#include <morphtarget_pars_vertex>\n#include <skinning_pars_vertex>\n#include <shadowmap_pars_vertex>\n#include <logdepthbuf_pars_vertex>\n#include <clipping_planes_pars_vertex>\nvoid main() {\n\t#include <uv_vertex>\n\t#include <uv2_vertex>\n\t#include <color_vertex>\n\t#include <beginnormal_vertex>\n\t#include <morphnormal_vertex>\n\t#include <skinbase_vertex>\n\t#include <skinnormal_vertex>\n\t#include <defaultnormal_vertex>\n#ifndef FLAT_SHADED\n\tvNormal = normalize( transformedNormal );\n#endif\n\t#include <begin_vertex>\n\t#include <morphtarget_vertex>\n\t#include <skinning_vertex>\n\t#include <displacementmap_vertex>\n\t#include <project_vertex>\n\t#include <logdepthbuf_vertex>\n\t#include <clipping_planes_vertex>\n\tvViewPosition = - mvPosition.xyz;\n\t#include <worldpos_vertex>\n\t#include <shadowmap_vertex>\n\t#include <fog_vertex>\n}\n"; | |
| var normal_frag = "#define NORMAL\nuniform float opacity;\n#if defined( FLAT_SHADED ) || defined( USE_BUMPMAP ) || defined( USE_NORMALMAP )\n\tvarying vec3 vViewPosition;\n#endif\n#ifndef FLAT_SHADED\n\tvarying vec3 vNormal;\n#endif\n#include <packing>\n#include <uv_pars_fragment>\n#include <bumpmap_pars_fragment>\n#include <normalmap_pars_fragment>\n#include <logdepthbuf_pars_fragment>\nvoid main() {\n\t#include <logdepthbuf_fragment>\n\t#include <normal_fragment_begin>\n\t#include <normal_fragment_maps>\n\tgl_FragColor = vec4( packNormalToRGB( normal ), opacity );\n}\n"; | |
| var normal_vert = "#define NORMAL\n#if defined( FLAT_SHADED ) || defined( USE_BUMPMAP ) || defined( USE_NORMALMAP )\n\tvarying vec3 vViewPosition;\n#endif\n#ifndef FLAT_SHADED\n\tvarying vec3 vNormal;\n#endif\n#include <uv_pars_vertex>\n#include <displacementmap_pars_vertex>\n#include <morphtarget_pars_vertex>\n#include <skinning_pars_vertex>\n#include <logdepthbuf_pars_vertex>\nvoid main() {\n\t#include <uv_vertex>\n\t#include <beginnormal_vertex>\n\t#include <morphnormal_vertex>\n\t#include <skinbase_vertex>\n\t#include <skinnormal_vertex>\n\t#include <defaultnormal_vertex>\n#ifndef FLAT_SHADED\n\tvNormal = normalize( transformedNormal );\n#endif\n\t#include <begin_vertex>\n\t#include <morphtarget_vertex>\n\t#include <skinning_vertex>\n\t#include <displacementmap_vertex>\n\t#include <project_vertex>\n\t#include <logdepthbuf_vertex>\n#if defined( FLAT_SHADED ) || defined( USE_BUMPMAP ) || defined( USE_NORMALMAP )\n\tvViewPosition = - mvPosition.xyz;\n#endif\n}\n"; | |
| var points_frag = "uniform vec3 diffuse;\nuniform float opacity;\n#include <common>\n#include <packing>\n#include <color_pars_fragment>\n#include <map_particle_pars_fragment>\n#include <fog_pars_fragment>\n#include <shadowmap_pars_fragment>\n#include <logdepthbuf_pars_fragment>\n#include <clipping_planes_pars_fragment>\nvoid main() {\n\t#include <clipping_planes_fragment>\n\tvec3 outgoingLight = vec3( 0.0 );\n\tvec4 diffuseColor = vec4( diffuse, opacity );\n\t#include <logdepthbuf_fragment>\n\t#include <map_particle_fragment>\n\t#include <color_fragment>\n\t#include <alphatest_fragment>\n\toutgoingLight = diffuseColor.rgb;\n\tgl_FragColor = vec4( outgoingLight, diffuseColor.a );\n\t#include <premultiplied_alpha_fragment>\n\t#include <tonemapping_fragment>\n\t#include <encodings_fragment>\n\t#include <fog_fragment>\n}\n"; | |
| var points_vert = "uniform float size;\nuniform float scale;\n#include <common>\n#include <color_pars_vertex>\n#include <fog_pars_vertex>\n#include <shadowmap_pars_vertex>\n#include <logdepthbuf_pars_vertex>\n#include <clipping_planes_pars_vertex>\nvoid main() {\n\t#include <color_vertex>\n\t#include <begin_vertex>\n\t#include <project_vertex>\n\t#ifdef USE_SIZEATTENUATION\n\t\tgl_PointSize = size * ( scale / - mvPosition.z );\n\t#else\n\t\tgl_PointSize = size;\n\t#endif\n\t#include <logdepthbuf_vertex>\n\t#include <clipping_planes_vertex>\n\t#include <worldpos_vertex>\n\t#include <shadowmap_vertex>\n\t#include <fog_vertex>\n}\n"; | |
| var shadow_frag = "uniform vec3 color;\nuniform float opacity;\n#include <common>\n#include <packing>\n#include <fog_pars_fragment>\n#include <bsdfs>\n#include <lights_pars_begin>\n#include <shadowmap_pars_fragment>\n#include <shadowmask_pars_fragment>\nvoid main() {\n\tgl_FragColor = vec4( color, opacity * ( 1.0 - getShadowMask() ) );\n\t#include <fog_fragment>\n}\n"; | |
| var shadow_vert = "#include <fog_pars_vertex>\n#include <shadowmap_pars_vertex>\nvoid main() {\n\t#include <begin_vertex>\n\t#include <project_vertex>\n\t#include <worldpos_vertex>\n\t#include <shadowmap_vertex>\n\t#include <fog_vertex>\n}\n"; | |
| var ShaderChunk = { | |
| alphamap_fragment: alphamap_fragment, | |
| alphamap_pars_fragment: alphamap_pars_fragment, | |
| alphatest_fragment: alphatest_fragment, | |
| aomap_fragment: aomap_fragment, | |
| aomap_pars_fragment: aomap_pars_fragment, | |
| begin_vertex: begin_vertex, | |
| beginnormal_vertex: beginnormal_vertex, | |
| bsdfs: bsdfs, | |
| bumpmap_pars_fragment: bumpmap_pars_fragment, | |
| clipping_planes_fragment: clipping_planes_fragment, | |
| clipping_planes_pars_fragment: clipping_planes_pars_fragment, | |
| clipping_planes_pars_vertex: clipping_planes_pars_vertex, | |
| clipping_planes_vertex: clipping_planes_vertex, | |
| color_fragment: color_fragment, | |
| color_pars_fragment: color_pars_fragment, | |
| color_pars_vertex: color_pars_vertex, | |
| color_vertex: color_vertex, | |
| common: common, | |
| cube_uv_reflection_fragment: cube_uv_reflection_fragment, | |
| defaultnormal_vertex: defaultnormal_vertex, | |
| displacementmap_pars_vertex: displacementmap_pars_vertex, | |
| displacementmap_vertex: displacementmap_vertex, | |
| emissivemap_fragment: emissivemap_fragment, | |
| emissivemap_pars_fragment: emissivemap_pars_fragment, | |
| encodings_fragment: encodings_fragment, | |
| encodings_pars_fragment: encodings_pars_fragment, | |
| envmap_fragment: envmap_fragment, | |
| envmap_pars_fragment: envmap_pars_fragment, | |
| envmap_pars_vertex: envmap_pars_vertex, | |
| envmap_vertex: envmap_vertex, | |
| fog_vertex: fog_vertex, | |
| fog_pars_vertex: fog_pars_vertex, | |
| fog_fragment: fog_fragment, | |
| fog_pars_fragment: fog_pars_fragment, | |
| gradientmap_pars_fragment: gradientmap_pars_fragment, | |
| lightmap_fragment: lightmap_fragment, | |
| lightmap_pars_fragment: lightmap_pars_fragment, | |
| lights_lambert_vertex: lights_lambert_vertex, | |
| lights_pars_begin: lights_pars_begin, | |
| lights_pars_maps: lights_pars_maps, | |
| lights_phong_fragment: lights_phong_fragment, | |
| lights_phong_pars_fragment: lights_phong_pars_fragment, | |
| lights_physical_fragment: lights_physical_fragment, | |
| lights_physical_pars_fragment: lights_physical_pars_fragment, | |
| lights_fragment_begin: lights_fragment_begin, | |
| lights_fragment_maps: lights_fragment_maps, | |
| lights_fragment_end: lights_fragment_end, | |
| logdepthbuf_fragment: logdepthbuf_fragment, | |
| logdepthbuf_pars_fragment: logdepthbuf_pars_fragment, | |
| logdepthbuf_pars_vertex: logdepthbuf_pars_vertex, | |
| logdepthbuf_vertex: logdepthbuf_vertex, | |
| map_fragment: map_fragment, | |
| map_pars_fragment: map_pars_fragment, | |
| map_particle_fragment: map_particle_fragment, | |
| map_particle_pars_fragment: map_particle_pars_fragment, | |
| metalnessmap_fragment: metalnessmap_fragment, | |
| metalnessmap_pars_fragment: metalnessmap_pars_fragment, | |
| morphnormal_vertex: morphnormal_vertex, | |
| morphtarget_pars_vertex: morphtarget_pars_vertex, | |
| morphtarget_vertex: morphtarget_vertex, | |
| normal_fragment_begin: normal_fragment_begin, | |
| normal_fragment_maps: normal_fragment_maps, | |
| normalmap_pars_fragment: normalmap_pars_fragment, | |
| packing: packing, | |
| premultiplied_alpha_fragment: premultiplied_alpha_fragment, | |
| project_vertex: project_vertex, | |
| dithering_fragment: dithering_fragment, | |
| dithering_pars_fragment: dithering_pars_fragment, | |
| roughnessmap_fragment: roughnessmap_fragment, | |
| roughnessmap_pars_fragment: roughnessmap_pars_fragment, | |
| shadowmap_pars_fragment: shadowmap_pars_fragment, | |
| shadowmap_pars_vertex: shadowmap_pars_vertex, | |
| shadowmap_vertex: shadowmap_vertex, | |
| shadowmask_pars_fragment: shadowmask_pars_fragment, | |
| skinbase_vertex: skinbase_vertex, | |
| skinning_pars_vertex: skinning_pars_vertex, | |
| skinning_vertex: skinning_vertex, | |
| skinnormal_vertex: skinnormal_vertex, | |
| specularmap_fragment: specularmap_fragment, | |
| specularmap_pars_fragment: specularmap_pars_fragment, | |
| tonemapping_fragment: tonemapping_fragment, | |
| tonemapping_pars_fragment: tonemapping_pars_fragment, | |
| uv_pars_fragment: uv_pars_fragment, | |
| uv_pars_vertex: uv_pars_vertex, | |
| uv_vertex: uv_vertex, | |
| uv2_pars_fragment: uv2_pars_fragment, | |
| uv2_pars_vertex: uv2_pars_vertex, | |
| uv2_vertex: uv2_vertex, | |
| worldpos_vertex: worldpos_vertex, | |
| cube_frag: cube_frag, | |
| cube_vert: cube_vert, | |
| depth_frag: depth_frag, | |
| depth_vert: depth_vert, | |
| distanceRGBA_frag: distanceRGBA_frag, | |
| distanceRGBA_vert: distanceRGBA_vert, | |
| equirect_frag: equirect_frag, | |
| equirect_vert: equirect_vert, | |
| linedashed_frag: linedashed_frag, | |
| linedashed_vert: linedashed_vert, | |
| meshbasic_frag: meshbasic_frag, | |
| meshbasic_vert: meshbasic_vert, | |
| meshlambert_frag: meshlambert_frag, | |
| meshlambert_vert: meshlambert_vert, | |
| meshphong_frag: meshphong_frag, | |
| meshphong_vert: meshphong_vert, | |
| meshphysical_frag: meshphysical_frag, | |
| meshphysical_vert: meshphysical_vert, | |
| normal_frag: normal_frag, | |
| normal_vert: normal_vert, | |
| points_frag: points_frag, | |
| points_vert: points_vert, | |
| shadow_frag: shadow_frag, | |
| shadow_vert: shadow_vert | |
| }; | |
| /** | |
| * Uniform Utilities | |
| */ | |
| var UniformsUtils = { | |
| merge: function ( uniforms ) { | |
| var merged = {}; | |
| for ( var u = 0; u < uniforms.length; u ++ ) { | |
| var tmp = this.clone( uniforms[ u ] ); | |
| for ( var p in tmp ) { | |
| merged[ p ] = tmp[ p ]; | |
| } | |
| } | |
| return merged; | |
| }, | |
| clone: function ( uniforms_src ) { | |
| var uniforms_dst = {}; | |
| for ( var u in uniforms_src ) { | |
| uniforms_dst[ u ] = {}; | |
| for ( var p in uniforms_src[ u ] ) { | |
| var parameter_src = uniforms_src[ u ][ p ]; | |
| if ( parameter_src && ( parameter_src.isColor || | |
| parameter_src.isMatrix3 || parameter_src.isMatrix4 || | |
| parameter_src.isVector2 || parameter_src.isVector3 || parameter_src.isVector4 || | |
| parameter_src.isTexture ) ) { | |
| uniforms_dst[ u ][ p ] = parameter_src.clone(); | |
| } else if ( Array.isArray( parameter_src ) ) { | |
| uniforms_dst[ u ][ p ] = parameter_src.slice(); | |
| } else { | |
| uniforms_dst[ u ][ p ] = parameter_src; | |
| } | |
| } | |
| } | |
| return uniforms_dst; | |
| } | |
| }; | |
| /** | |
| * @author mrdoob / http://mrdoob.com/ | |
| */ | |
| var ColorKeywords = { 'aliceblue': 0xF0F8FF, 'antiquewhite': 0xFAEBD7, 'aqua': 0x00FFFF, 'aquamarine': 0x7FFFD4, 'azure': 0xF0FFFF, | |
| 'beige': 0xF5F5DC, 'bisque': 0xFFE4C4, 'black': 0x000000, 'blanchedalmond': 0xFFEBCD, 'blue': 0x0000FF, 'blueviolet': 0x8A2BE2, | |
| 'brown': 0xA52A2A, 'burlywood': 0xDEB887, 'cadetblue': 0x5F9EA0, 'chartreuse': 0x7FFF00, 'chocolate': 0xD2691E, 'coral': 0xFF7F50, | |
| 'cornflowerblue': 0x6495ED, 'cornsilk': 0xFFF8DC, 'crimson': 0xDC143C, 'cyan': 0x00FFFF, 'darkblue': 0x00008B, 'darkcyan': 0x008B8B, | |
| 'darkgoldenrod': 0xB8860B, 'darkgray': 0xA9A9A9, 'darkgreen': 0x006400, 'darkgrey': 0xA9A9A9, 'darkkhaki': 0xBDB76B, 'darkmagenta': 0x8B008B, | |
| 'darkolivegreen': 0x556B2F, 'darkorange': 0xFF8C00, 'darkorchid': 0x9932CC, 'darkred': 0x8B0000, 'darksalmon': 0xE9967A, 'darkseagreen': 0x8FBC8F, | |
| 'darkslateblue': 0x483D8B, 'darkslategray': 0x2F4F4F, 'darkslategrey': 0x2F4F4F, 'darkturquoise': 0x00CED1, 'darkviolet': 0x9400D3, | |
| 'deeppink': 0xFF1493, 'deepskyblue': 0x00BFFF, 'dimgray': 0x696969, 'dimgrey': 0x696969, 'dodgerblue': 0x1E90FF, 'firebrick': 0xB22222, | |
| 'floralwhite': 0xFFFAF0, 'forestgreen': 0x228B22, 'fuchsia': 0xFF00FF, 'gainsboro': 0xDCDCDC, 'ghostwhite': 0xF8F8FF, 'gold': 0xFFD700, | |
| 'goldenrod': 0xDAA520, 'gray': 0x808080, 'green': 0x008000, 'greenyellow': 0xADFF2F, 'grey': 0x808080, 'honeydew': 0xF0FFF0, 'hotpink': 0xFF69B4, | |
| 'indianred': 0xCD5C5C, 'indigo': 0x4B0082, 'ivory': 0xFFFFF0, 'khaki': 0xF0E68C, 'lavender': 0xE6E6FA, 'lavenderblush': 0xFFF0F5, 'lawngreen': 0x7CFC00, | |
| 'lemonchiffon': 0xFFFACD, 'lightblue': 0xADD8E6, 'lightcoral': 0xF08080, 'lightcyan': 0xE0FFFF, 'lightgoldenrodyellow': 0xFAFAD2, 'lightgray': 0xD3D3D3, | |
| 'lightgreen': 0x90EE90, 'lightgrey': 0xD3D3D3, 'lightpink': 0xFFB6C1, 'lightsalmon': 0xFFA07A, 'lightseagreen': 0x20B2AA, 'lightskyblue': 0x87CEFA, | |
| 'lightslategray': 0x778899, 'lightslategrey': 0x778899, 'lightsteelblue': 0xB0C4DE, 'lightyellow': 0xFFFFE0, 'lime': 0x00FF00, 'limegreen': 0x32CD32, | |
| 'linen': 0xFAF0E6, 'magenta': 0xFF00FF, 'maroon': 0x800000, 'mediumaquamarine': 0x66CDAA, 'mediumblue': 0x0000CD, 'mediumorchid': 0xBA55D3, | |
| 'mediumpurple': 0x9370DB, 'mediumseagreen': 0x3CB371, 'mediumslateblue': 0x7B68EE, 'mediumspringgreen': 0x00FA9A, 'mediumturquoise': 0x48D1CC, | |
| 'mediumvioletred': 0xC71585, 'midnightblue': 0x191970, 'mintcream': 0xF5FFFA, 'mistyrose': 0xFFE4E1, 'moccasin': 0xFFE4B5, 'navajowhite': 0xFFDEAD, | |
| 'navy': 0x000080, 'oldlace': 0xFDF5E6, 'olive': 0x808000, 'olivedrab': 0x6B8E23, 'orange': 0xFFA500, 'orangered': 0xFF4500, 'orchid': 0xDA70D6, | |
| 'palegoldenrod': 0xEEE8AA, 'palegreen': 0x98FB98, 'paleturquoise': 0xAFEEEE, 'palevioletred': 0xDB7093, 'papayawhip': 0xFFEFD5, 'peachpuff': 0xFFDAB9, | |
| 'peru': 0xCD853F, 'pink': 0xFFC0CB, 'plum': 0xDDA0DD, 'powderblue': 0xB0E0E6, 'purple': 0x800080, 'rebeccapurple': 0x663399, 'red': 0xFF0000, 'rosybrown': 0xBC8F8F, | |
| 'royalblue': 0x4169E1, 'saddlebrown': 0x8B4513, 'salmon': 0xFA8072, 'sandybrown': 0xF4A460, 'seagreen': 0x2E8B57, 'seashell': 0xFFF5EE, | |
| 'sienna': 0xA0522D, 'silver': 0xC0C0C0, 'skyblue': 0x87CEEB, 'slateblue': 0x6A5ACD, 'slategray': 0x708090, 'slategrey': 0x708090, 'snow': 0xFFFAFA, | |
| 'springgreen': 0x00FF7F, 'steelblue': 0x4682B4, 'tan': 0xD2B48C, 'teal': 0x008080, 'thistle': 0xD8BFD8, 'tomato': 0xFF6347, 'turquoise': 0x40E0D0, | |
| 'violet': 0xEE82EE, 'wheat': 0xF5DEB3, 'white': 0xFFFFFF, 'whitesmoke': 0xF5F5F5, 'yellow': 0xFFFF00, 'yellowgreen': 0x9ACD32 }; | |
| function Color( r, g, b ) { | |
| if ( g === undefined && b === undefined ) { | |
| // r is THREE.Color, hex or string | |
| return this.set( r ); | |
| } | |
| return this.setRGB( r, g, b ); | |
| } | |
| Object.assign( Color.prototype, { | |
| isColor: true, | |
| r: 1, g: 1, b: 1, | |
| set: function ( value ) { | |
| if ( value && value.isColor ) { | |
| this.copy( value ); | |
| } else if ( typeof value === 'number' ) { | |
| this.setHex( value ); | |
| } else if ( typeof value === 'string' ) { | |
| this.setStyle( value ); | |
| } | |
| return this; | |
| }, | |
| setScalar: function ( scalar ) { | |
| this.r = scalar; | |
| this.g = scalar; | |
| this.b = scalar; | |
| return this; | |
| }, | |
| setHex: function ( hex ) { | |
| hex = Math.floor( hex ); | |
| this.r = ( hex >> 16 & 255 ) / 255; | |
| this.g = ( hex >> 8 & 255 ) / 255; | |
| this.b = ( hex & 255 ) / 255; | |
| return this; | |
| }, | |
| setRGB: function ( r, g, b ) { | |
| this.r = r; | |
| this.g = g; | |
| this.b = b; | |
| return this; | |
| }, | |
| setHSL: function () { | |
| function hue2rgb( p, q, t ) { | |
| if ( t < 0 ) t += 1; | |
| if ( t > 1 ) t -= 1; | |
| if ( t < 1 / 6 ) return p + ( q - p ) * 6 * t; | |
| if ( t < 1 / 2 ) return q; | |
| if ( t < 2 / 3 ) return p + ( q - p ) * 6 * ( 2 / 3 - t ); | |
| return p; | |
| } | |
| return function setHSL( h, s, l ) { | |
| // h,s,l ranges are in 0.0 - 1.0 | |
| h = _Math.euclideanModulo( h, 1 ); | |
| s = _Math.clamp( s, 0, 1 ); | |
| l = _Math.clamp( l, 0, 1 ); | |
| if ( s === 0 ) { | |
| this.r = this.g = this.b = l; | |
| } else { | |
| var p = l <= 0.5 ? l * ( 1 + s ) : l + s - ( l * s ); | |
| var q = ( 2 * l ) - p; | |
| this.r = hue2rgb( q, p, h + 1 / 3 ); | |
| this.g = hue2rgb( q, p, h ); | |
| this.b = hue2rgb( q, p, h - 1 / 3 ); | |
| } | |
| return this; | |
| }; | |
| }(), | |
| setStyle: function ( style ) { | |
| function handleAlpha( string ) { | |
| if ( string === undefined ) return; | |
| if ( parseFloat( string ) < 1 ) { | |
| console.warn( 'THREE.Color: Alpha component of ' + style + ' will be ignored.' ); | |
| } | |
| } | |
| var m; | |
| if ( m = /^((?:rgb|hsl)a?)\(\s*([^\)]*)\)/.exec( style ) ) { | |
| // rgb / hsl | |
| var color; | |
| var name = m[ 1 ]; | |
| var components = m[ 2 ]; | |
| switch ( name ) { | |
| case 'rgb': | |
| case 'rgba': | |
| if ( color = /^(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*(,\s*([0-9]*\.?[0-9]+)\s*)?$/.exec( components ) ) { | |
| // rgb(255,0,0) rgba(255,0,0,0.5) | |
| this.r = Math.min( 255, parseInt( color[ 1 ], 10 ) ) / 255; | |
| this.g = Math.min( 255, parseInt( color[ 2 ], 10 ) ) / 255; | |
| this.b = Math.min( 255, parseInt( color[ 3 ], 10 ) ) / 255; | |
| handleAlpha( color[ 5 ] ); | |
| return this; | |
| } | |
| if ( color = /^(\d+)\%\s*,\s*(\d+)\%\s*,\s*(\d+)\%\s*(,\s*([0-9]*\.?[0-9]+)\s*)?$/.exec( components ) ) { | |
| // rgb(100%,0%,0%) rgba(100%,0%,0%,0.5) | |
| this.r = Math.min( 100, parseInt( color[ 1 ], 10 ) ) / 100; | |
| this.g = Math.min( 100, parseInt( color[ 2 ], 10 ) ) / 100; | |
| this.b = Math.min( 100, parseInt( color[ 3 ], 10 ) ) / 100; | |
| handleAlpha( color[ 5 ] ); | |
| return this; | |
| } | |
| break; | |
| case 'hsl': | |
| case 'hsla': | |
| if ( color = /^([0-9]*\.?[0-9]+)\s*,\s*(\d+)\%\s*,\s*(\d+)\%\s*(,\s*([0-9]*\.?[0-9]+)\s*)?$/.exec( components ) ) { | |
| // hsl(120,50%,50%) hsla(120,50%,50%,0.5) | |
| var h = parseFloat( color[ 1 ] ) / 360; | |
| var s = parseInt( color[ 2 ], 10 ) / 100; | |
| var l = parseInt( color[ 3 ], 10 ) / 100; | |
| handleAlpha( color[ 5 ] ); | |
| return this.setHSL( h, s, l ); | |
| } | |
| break; | |
| } | |
| } else if ( m = /^\#([A-Fa-f0-9]+)$/.exec( style ) ) { | |
| // hex color | |
| var hex = m[ 1 ]; | |
| var size = hex.length; | |
| if ( size === 3 ) { | |
| // #ff0 | |
| this.r = parseInt( hex.charAt( 0 ) + hex.charAt( 0 ), 16 ) / 255; | |
| this.g = parseInt( hex.charAt( 1 ) + hex.charAt( 1 ), 16 ) / 255; | |
| this.b = parseInt( hex.charAt( 2 ) + hex.charAt( 2 ), 16 ) / 255; | |
| return this; | |
| } else if ( size === 6 ) { | |
| // #ff0000 | |
| this.r = parseInt( hex.charAt( 0 ) + hex.charAt( 1 ), 16 ) / 255; | |
| this.g = parseInt( hex.charAt( 2 ) + hex.charAt( 3 ), 16 ) / 255; | |
| this.b = parseInt( hex.charAt( 4 ) + hex.charAt( 5 ), 16 ) / 255; | |
| return this; | |
| } | |
| } | |
| if ( style && style.length > 0 ) { | |
| // color keywords | |
| var hex = ColorKeywords[ style ]; | |
| if ( hex !== undefined ) { | |
| // red | |
| this.setHex( hex ); | |
| } else { | |
| // unknown color | |
| console.warn( 'THREE.Color: Unknown color ' + style ); | |
| } | |
| } | |
| return this; | |
| }, | |
| clone: function () { | |
| return new this.constructor( this.r, this.g, this.b ); | |
| }, | |
| copy: function ( color ) { | |
| this.r = color.r; | |
| this.g = color.g; | |
| this.b = color.b; | |
| return this; | |
| }, | |
| copyGammaToLinear: function ( color, gammaFactor ) { | |
| if ( gammaFactor === undefined ) gammaFactor = 2.0; | |
| this.r = Math.pow( color.r, gammaFactor ); | |
| this.g = Math.pow( color.g, gammaFactor ); | |
| this.b = Math.pow( color.b, gammaFactor ); | |
| return this; | |
| }, | |
| copyLinearToGamma: function ( color, gammaFactor ) { | |
| if ( gammaFactor === undefined ) gammaFactor = 2.0; | |
| var safeInverse = ( gammaFactor > 0 ) ? ( 1.0 / gammaFactor ) : 1.0; | |
| this.r = Math.pow( color.r, safeInverse ); | |
| this.g = Math.pow( color.g, safeInverse ); | |
| this.b = Math.pow( color.b, safeInverse ); | |
| return this; | |
| }, | |
| convertGammaToLinear: function () { | |
| var r = this.r, g = this.g, b = this.b; | |
| this.r = r * r; | |
| this.g = g * g; | |
| this.b = b * b; | |
| return this; | |
| }, | |
| convertLinearToGamma: function () { | |
| this.r = Math.sqrt( this.r ); | |
| this.g = Math.sqrt( this.g ); | |
| this.b = Math.sqrt( this.b ); | |
| return this; | |
| }, | |
| getHex: function () { | |
| return ( this.r * 255 ) << 16 ^ ( this.g * 255 ) << 8 ^ ( this.b * 255 ) << 0; | |
| }, | |
| getHexString: function () { | |
| return ( '000000' + this.getHex().toString( 16 ) ).slice( - 6 ); | |
| }, | |
| getHSL: function ( optionalTarget ) { | |
| // h,s,l ranges are in 0.0 - 1.0 | |
| var hsl = optionalTarget || { h: 0, s: 0, l: 0 }; | |
| var r = this.r, g = this.g, b = this.b; | |
| var max = Math.max( r, g, b ); | |
| var min = Math.min( r, g, b ); | |
| var hue, saturation; | |
| var lightness = ( min + max ) / 2.0; | |
| if ( min === max ) { | |
| hue = 0; | |
| saturation = 0; | |
| } else { | |
| var delta = max - min; | |
| saturation = lightness <= 0.5 ? delta / ( max + min ) : delta / ( 2 - max - min ); | |
| switch ( max ) { | |
| case r: hue = ( g - b ) / delta + ( g < b ? 6 : 0 ); break; | |
| case g: hue = ( b - r ) / delta + 2; break; | |
| case b: hue = ( r - g ) / delta + 4; break; | |
| } | |
| hue /= 6; | |
| } | |
| hsl.h = hue; | |
| hsl.s = saturation; | |
| hsl.l = lightness; | |
| return hsl; | |
| }, | |
| getStyle: function () { | |
| return 'rgb(' + ( ( this.r * 255 ) | 0 ) + ',' + ( ( this.g * 255 ) | 0 ) + ',' + ( ( this.b * 255 ) | 0 ) + ')'; | |
| }, | |
| offsetHSL: function ( h, s, l ) { | |
| var hsl = this.getHSL(); | |
| hsl.h += h; hsl.s += s; hsl.l += l; | |
| this.setHSL( hsl.h, hsl.s, hsl.l ); | |
| return this; | |
| }, | |
| add: function ( color ) { | |
| this.r += color.r; | |
| this.g += color.g; | |
| this.b += color.b; | |
| return this; | |
| }, | |
| addColors: function ( color1, color2 ) { | |
| this.r = color1.r + color2.r; | |
| this.g = color1.g + color2.g; | |
| this.b = color1.b + color2.b; | |
| return this; | |
| }, | |
| addScalar: function ( s ) { | |
| this.r += s; | |
| this.g += s; | |
| this.b += s; | |
| return this; | |
| }, | |
| sub: function ( color ) { | |
| this.r = Math.max( 0, this.r - color.r ); | |
| this.g = Math.max( 0, this.g - color.g ); | |
| this.b = Math.max( 0, this.b - color.b ); | |
| return this; | |
| }, | |
| multiply: function ( color ) { | |
| this.r *= color.r; | |
| this.g *= color.g; | |
| this.b *= color.b; | |
| return this; | |
| }, | |
| multiplyScalar: function ( s ) { | |
| this.r *= s; | |
| this.g *= s; | |
| this.b *= s; | |
| return this; | |
| }, | |
| lerp: function ( color, alpha ) { | |
| this.r += ( color.r - this.r ) * alpha; | |
| this.g += ( color.g - this.g ) * alpha; | |
| this.b += ( color.b - this.b ) * alpha; | |
| return this; | |
| }, | |
| equals: function ( c ) { | |
| return ( c.r === this.r ) && ( c.g === this.g ) && ( c.b === this.b ); | |
| }, | |
| fromArray: function ( array, offset ) { | |
| if ( offset === undefined ) offset = 0; | |
| this.r = array[ offset ]; | |
| this.g = array[ offset + 1 ]; | |
| this.b = array[ offset + 2 ]; | |
| return this; | |
| }, | |
| toArray: function ( array, offset ) { | |
| if ( array === undefined ) array = []; | |
| if ( offset === undefined ) offset = 0; | |
| array[ offset ] = this.r; | |
| array[ offset + 1 ] = this.g; | |
| array[ offset + 2 ] = this.b; | |
| return array; | |
| }, | |
| toJSON: function () { | |
| return this.getHex(); | |
| } | |
| } ); | |
| /** | |
| * Uniforms library for shared webgl shaders | |
| */ | |
| var UniformsLib = { | |
| common: { | |
| diffuse: { value: new Color( 0xeeeeee ) }, | |
| opacity: { value: 1.0 }, | |
| map: { value: null }, | |
| uvTransform: { value: new Matrix3() }, | |
| alphaMap: { value: null }, | |
| }, | |
| specularmap: { | |
| specularMap: { value: null }, | |
| }, | |
| envmap: { | |
| envMap: { value: null }, | |
| flipEnvMap: { value: - 1 }, | |
| reflectivity: { value: 1.0 }, | |
| refractionRatio: { value: 0.98 } | |
| }, | |
| aomap: { | |
| aoMap: { value: null }, | |
| aoMapIntensity: { value: 1 } | |
| }, | |
| lightmap: { | |
| lightMap: { value: null }, | |
| lightMapIntensity: { value: 1 } | |
| }, | |
| emissivemap: { | |
| emissiveMap: { value: null } | |
| }, | |
| bumpmap: { | |
| bumpMap: { value: null }, | |
| bumpScale: { value: 1 } | |
| }, | |
| normalmap: { | |
| normalMap: { value: null }, | |
| normalScale: { value: new Vector2( 1, 1 ) } | |
| }, | |
| displacementmap: { | |
| displacementMap: { value: null }, | |
| displacementScale: { value: 1 }, | |
| displacementBias: { value: 0 } | |
| }, | |
| roughnessmap: { | |
| roughnessMap: { value: null } | |
| }, | |
| metalnessmap: { | |
| metalnessMap: { value: null } | |
| }, | |
| gradientmap: { | |
| gradientMap: { value: null } | |
| }, | |
| fog: { | |
| fogDensity: { value: 0.00025 }, | |
| fogNear: { value: 1 }, | |
| fogFar: { value: 2000 }, | |
| fogColor: { value: new Color( 0xffffff ) } | |
| }, | |
| lights: { | |
| ambientLightColor: { value: [] }, | |
| directionalLights: { value: [], properties: { | |
| direction: {}, | |
| color: {}, | |
| shadow: {}, | |
| shadowBias: {}, | |
| shadowRadius: {}, | |
| shadowMapSize: {} | |
| } }, | |
| directionalShadowMap: { value: [] }, | |
| directionalShadowMatrix: { value: [] }, | |
| spotLights: { value: [], properties: { | |
| color: {}, | |
| position: {}, | |
| direction: {}, | |
| distance: {}, | |
| coneCos: {}, | |
| penumbraCos: {}, | |
| decay: {}, | |
| shadow: {}, | |
| shadowBias: {}, | |
| shadowRadius: {}, | |
| shadowMapSize: {} | |
| } }, | |
| spotShadowMap: { value: [] }, | |
| spotShadowMatrix: { value: [] }, | |
| pointLights: { value: [], properties: { | |
| color: {}, | |
| position: {}, | |
| decay: {}, | |
| distance: {}, | |
| shadow: {}, | |
| shadowBias: {}, | |
| shadowRadius: {}, | |
| shadowMapSize: {}, | |
| shadowCameraNear: {}, | |
| shadowCameraFar: {} | |
| } }, | |
| pointShadowMap: { value: [] }, | |
| pointShadowMatrix: { value: [] }, | |
| hemisphereLights: { value: [], properties: { | |
| direction: {}, | |
| skyColor: {}, | |
| groundColor: {} | |
| } }, | |
| // TODO (abelnation): RectAreaLight BRDF data needs to be moved from example to main src | |
| rectAreaLights: { value: [], properties: { | |
| color: {}, | |
| position: {}, | |
| width: {}, | |
| height: {} | |
| } } | |
| }, | |
| points: { | |
| diffuse: { value: new Color( 0xeeeeee ) }, | |
| opacity: { value: 1.0 }, | |
| size: { value: 1.0 }, | |
| scale: { value: 1.0 }, | |
| map: { value: null }, | |
| uvTransform: { value: new Matrix3() } | |
| } | |
| }; | |
| /** | |
| * @author alteredq / http://alteredqualia.com/ | |
| * @author mrdoob / http://mrdoob.com/ | |
| * @author mikael emtinger / http://gomo.se/ | |
| */ | |
| var ShaderLib = { | |
| basic: { | |
| uniforms: UniformsUtils.merge( [ | |
| UniformsLib.common, | |
| UniformsLib.specularmap, | |
| UniformsLib.envmap, | |
| UniformsLib.aomap, | |
| UniformsLib.lightmap, | |
| UniformsLib.fog | |
| ] ), | |
| vertexShader: ShaderChunk.meshbasic_vert, | |
| fragmentShader: ShaderChunk.meshbasic_frag | |
| }, | |
| lambert: { | |
| uniforms: UniformsUtils.merge( [ | |
| UniformsLib.common, | |
| UniformsLib.specularmap, | |
| UniformsLib.envmap, | |
| UniformsLib.aomap, | |
| UniformsLib.lightmap, | |
| UniformsLib.emissivemap, | |
| UniformsLib.fog, | |
| UniformsLib.lights, | |
| { | |
| emissive: { value: new Color( 0x000000 ) } | |
| } | |
| ] ), | |
| vertexShader: ShaderChunk.meshlambert_vert, | |
| fragmentShader: ShaderChunk.meshlambert_frag | |
| }, | |
| phong: { | |
| uniforms: UniformsUtils.merge( [ | |
| UniformsLib.common, | |
| UniformsLib.specularmap, | |
| UniformsLib.envmap, | |
| UniformsLib.aomap, | |
| UniformsLib.lightmap, | |
| UniformsLib.emissivemap, | |
| UniformsLib.bumpmap, | |
| UniformsLib.normalmap, | |
| UniformsLib.displacementmap, | |
| UniformsLib.gradientmap, | |
| UniformsLib.fog, | |
| UniformsLib.lights, | |
| { | |
| emissive: { value: new Color( 0x000000 ) }, | |
| specular: { value: new Color( 0x111111 ) }, | |
| shininess: { value: 30 } | |
| } | |
| ] ), | |
| vertexShader: ShaderChunk.meshphong_vert, | |
| fragmentShader: ShaderChunk.meshphong_frag | |
| }, | |
| standard: { | |
| uniforms: UniformsUtils.merge( [ | |
| UniformsLib.common, | |
| UniformsLib.envmap, | |
| UniformsLib.aomap, | |
| UniformsLib.lightmap, | |
| UniformsLib.emissivemap, | |
| UniformsLib.bumpmap, | |
| UniformsLib.normalmap, | |
| UniformsLib.displacementmap, | |
| UniformsLib.roughnessmap, | |
| UniformsLib.metalnessmap, | |
| UniformsLib.fog, | |
| UniformsLib.lights, | |
| { | |
| emissive: { value: new Color( 0x000000 ) }, | |
| roughness: { value: 0.5 }, | |
| metalness: { value: 0.5 }, | |
| envMapIntensity: { value: 1 } // temporary | |
| } | |
| ] ), | |
| vertexShader: ShaderChunk.meshphysical_vert, | |
| fragmentShader: ShaderChunk.meshphysical_frag | |
| }, | |
| points: { | |
| uniforms: UniformsUtils.merge( [ | |
| UniformsLib.points, | |
| UniformsLib.fog | |
| ] ), | |
| vertexShader: ShaderChunk.points_vert, | |
| fragmentShader: ShaderChunk.points_frag | |
| }, | |
| dashed: { | |
| uniforms: UniformsUtils.merge( [ | |
| UniformsLib.common, | |
| UniformsLib.fog, | |
| { | |
| scale: { value: 1 }, | |
| dashSize: { value: 1 }, | |
| totalSize: { value: 2 } | |
| } | |
| ] ), | |
| vertexShader: ShaderChunk.linedashed_vert, | |
| fragmentShader: ShaderChunk.linedashed_frag | |
| }, | |
| depth: { | |
| uniforms: UniformsUtils.merge( [ | |
| UniformsLib.common, | |
| UniformsLib.displacementmap | |
| ] ), | |
| vertexShader: ShaderChunk.depth_vert, | |
| fragmentShader: ShaderChunk.depth_frag | |
| }, | |
| normal: { | |
| uniforms: UniformsUtils.merge( [ | |
| UniformsLib.common, | |
| UniformsLib.bumpmap, | |
| UniformsLib.normalmap, | |
| UniformsLib.displacementmap, | |
| { | |
| opacity: { value: 1.0 } | |
| } | |
| ] ), | |
| vertexShader: ShaderChunk.normal_vert, | |
| fragmentShader: ShaderChunk.normal_frag | |
| }, | |
| /* ------------------------------------------------------------------------- | |
| // Cube map shader | |
| ------------------------------------------------------------------------- */ | |
| cube: { | |
| uniforms: { | |
| tCube: { value: null }, | |
| tFlip: { value: - 1 }, | |
| opacity: { value: 1.0 } | |
| }, | |
| vertexShader: ShaderChunk.cube_vert, | |
| fragmentShader: ShaderChunk.cube_frag | |
| }, | |
| equirect: { | |
| uniforms: { | |
| tEquirect: { value: null }, | |
| }, | |
| vertexShader: ShaderChunk.equirect_vert, | |
| fragmentShader: ShaderChunk.equirect_frag | |
| }, | |
| distanceRGBA: { | |
| uniforms: UniformsUtils.merge( [ | |
| UniformsLib.common, | |
| UniformsLib.displacementmap, | |
| { | |
| referencePosition: { value: new Vector3() }, | |
| nearDistance: { value: 1 }, | |
| farDistance: { value: 1000 } | |
| } | |
| ] ), | |
| vertexShader: ShaderChunk.distanceRGBA_vert, | |
| fragmentShader: ShaderChunk.distanceRGBA_frag | |
| }, | |
| shadow: { | |
| uniforms: UniformsUtils.merge( [ | |
| UniformsLib.lights, | |
| UniformsLib.fog, | |
| { | |
| color: { value: new Color( 0x00000 ) }, | |
| opacity: { value: 1.0 } | |
| }, | |
| ] ), | |
| vertexShader: ShaderChunk.shadow_vert, | |
| fragmentShader: ShaderChunk.shadow_frag | |
| } | |
| }; | |
| ShaderLib.physical = { | |
| uniforms: UniformsUtils.merge( [ | |
| ShaderLib.standard.uniforms, | |
| { | |
| clearCoat: { value: 0 }, | |
| clearCoatRoughness: { value: 0 } | |
| } | |
| ] ), | |
| vertexShader: ShaderChunk.meshphysical_vert, | |
| fragmentShader: ShaderChunk.meshphysical_frag | |
| }; | |
| /** | |
| * @author mrdoob / http://mrdoob.com/ | |
| */ | |
| function WebGLAttributes( gl ) { | |
| var buffers = new WeakMap(); | |
| function createBuffer( attribute, bufferType ) { | |
| var array = attribute.array; | |
| var usage = attribute.dynamic ? gl.DYNAMIC_DRAW : gl.STATIC_DRAW; | |
| var buffer = gl.createBuffer(); | |
| gl.bindBuffer( bufferType, buffer ); | |
| gl.bufferData( bufferType, array, usage ); | |
| attribute.onUploadCallback(); | |
| var type = gl.FLOAT; | |
| if ( array instanceof Float32Array ) { | |
| type = gl.FLOAT; | |
| } else if ( array instanceof Float64Array ) { | |
| console.warn( 'THREE.WebGLAttributes: Unsupported data buffer format: Float64Array.' ); | |
| } else if ( array instanceof Uint16Array ) { | |
| type = gl.UNSIGNED_SHORT; | |
| } else if ( array instanceof Int16Array ) { | |
| type = gl.SHORT; | |
| } else if ( array instanceof Uint32Array ) { | |
| type = gl.UNSIGNED_INT; | |
| } else if ( array instanceof Int32Array ) { | |
| type = gl.INT; | |
| } else if ( array instanceof Int8Array ) { | |
| type = gl.BYTE; | |
| } else if ( array instanceof Uint8Array ) { | |
| type = gl.UNSIGNED_BYTE; | |
| } | |
| return { | |
| buffer: buffer, | |
| type: type, | |
| bytesPerElement: array.BYTES_PER_ELEMENT, | |
| version: attribute.version | |
| }; | |
| } | |
| function updateBuffer( buffer, attribute, bufferType ) { | |
| var array = attribute.array; | |
| var updateRange = attribute.updateRange; | |
| gl.bindBuffer( bufferType, buffer ); | |
| if ( attribute.dynamic === false ) { | |
| gl.bufferData( bufferType, array, gl.STATIC_DRAW ); | |
| } else if ( updateRange.count === - 1 ) { | |
| // Not using update ranges | |
| gl.bufferSubData( bufferType, 0, array ); | |
| } else if ( updateRange.count === 0 ) { | |
| console.error( 'THREE.WebGLObjects.updateBuffer: dynamic THREE.BufferAttribute marked as needsUpdate but updateRange.count is 0, ensure you are using set methods or updating manually.' ); | |
| } else { | |
| gl.bufferSubData( bufferType, updateRange.offset * array.BYTES_PER_ELEMENT, | |
| array.subarray( updateRange.offset, updateRange.offset + updateRange.count ) ); | |
| updateRange.count = - 1; // reset range | |
| } | |
| } | |
| // | |
| function get( attribute ) { | |
| if ( attribute.isInterleavedBufferAttribute ) attribute = attribute.data; | |
| return buffers.get( attribute ); | |
| } | |
| function remove( attribute ) { | |
| if ( attribute.isInterleavedBufferAttribute ) attribute = attribute.data; | |
| var data = buffers.get( attribute ); | |
| if ( data ) { | |
| gl.deleteBuffer( data.buffer ); | |
| buffers.delete( attribute ); | |
| } | |
| } | |
| function update( attribute, bufferType ) { | |
| if ( attribute.isInterleavedBufferAttribute ) attribute = attribute.data; | |
| var data = buffers.get( attribute ); | |
| if ( data === undefined ) { | |
| buffers.set( attribute, createBuffer( attribute, bufferType ) ); | |
| } else if ( data.version < attribute.version ) { | |
| updateBuffer( data.buffer, attribute, bufferType ); | |
| data.version = attribute.version; | |
| } | |
| } | |
| return { | |
| get: get, | |
| remove: remove, | |
| update: update | |
| }; | |
| } | |
| /** | |
| * @author mrdoob / http://mrdoob.com/ | |
| * @author WestLangley / http://github.com/WestLangley | |
| * @author bhouston / http://clara.io | |
| */ | |
| function Euler( x, y, z, order ) { | |
| this._x = x || 0; | |
| this._y = y || 0; | |
| this._z = z || 0; | |
| this._order = order || Euler.DefaultOrder; | |
| } | |
| Euler.RotationOrders = [ 'XYZ', 'YZX', 'ZXY', 'XZY', 'YXZ', 'ZYX' ]; | |
| Euler.DefaultOrder = 'XYZ'; | |
| Object.defineProperties( Euler.prototype, { | |
| x: { | |
| get: function () { | |
| return this._x; | |
| }, | |
| set: function ( value ) { | |
| this._x = value; | |
| this.onChangeCallback(); | |
| } | |
| }, | |
| y: { | |
| get: function () { | |
| return this._y; | |
| }, | |
| set: function ( value ) { | |
| this._y = value; | |
| this.onChangeCallback(); | |
| } | |
| }, | |
| z: { | |
| get: function () { | |
| return this._z; | |
| }, | |
| set: function ( value ) { | |
| this._z = value; | |
| this.onChangeCallback(); | |
| } | |
| }, | |
| order: { | |
| get: function () { | |
| return this._order; | |
| }, | |
| set: function ( value ) { | |
| this._order = value; | |
| this.onChangeCallback(); | |
| } | |
| } | |
| } ); | |
| Object.assign( Euler.prototype, { | |
| isEuler: true, | |
| set: function ( x, y, z, order ) { | |
| this._x = x; | |
| this._y = y; | |
| this._z = z; | |
| this._order = order || this._order; | |
| this.onChangeCallback(); | |
| return this; | |
| }, | |
| clone: function () { | |
| return new this.constructor( this._x, this._y, this._z, this._order ); | |
| }, | |
| copy: function ( euler ) { | |
| this._x = euler._x; | |
| this._y = euler._y; | |
| this._z = euler._z; | |
| this._order = euler._order; | |
| this.onChangeCallback(); | |
| return this; | |
| }, | |
| setFromRotationMatrix: function ( m, order, update ) { | |
| var clamp = _Math.clamp; | |
| // assumes the upper 3x3 of m is a pure rotation matrix (i.e, unscaled) | |
| var te = m.elements; | |
| var m11 = te[ 0 ], m12 = te[ 4 ], m13 = te[ 8 ]; | |
| var m21 = te[ 1 ], m22 = te[ 5 ], m23 = te[ 9 ]; | |
| var m31 = te[ 2 ], m32 = te[ 6 ], m33 = te[ 10 ]; | |
| order = order || this._order; | |
| if ( order === 'XYZ' ) { | |
| this._y = Math.asin( clamp( m13, - 1, 1 ) ); | |
| if ( Math.abs( m13 ) < 0.99999 ) { | |
| this._x = Math.atan2( - m23, m33 ); | |
| this._z = Math.atan2( - m12, m11 ); | |
| } else { | |
| this._x = Math.atan2( m32, m22 ); | |
| this._z = 0; | |
| } | |
| } else if ( order === 'YXZ' ) { | |
| this._x = Math.asin( - clamp( m23, - 1, 1 ) ); | |
| if ( Math.abs( m23 ) < 0.99999 ) { | |
| this._y = Math.atan2( m13, m33 ); | |
| this._z = Math.atan2( m21, m22 ); | |
| } else { | |
| this._y = Math.atan2( - m31, m11 ); | |
| this._z = 0; | |
| } | |
| } else if ( order === 'ZXY' ) { | |
| this._x = Math.asin( clamp( m32, - 1, 1 ) ); | |
| if ( Math.abs( m32 ) < 0.99999 ) { | |
| this._y = Math.atan2( - m31, m33 ); | |
| this._z = Math.atan2( - m12, m22 ); | |
| } else { | |
| this._y = 0; | |
| this._z = Math.atan2( m21, m11 ); | |
| } | |
| } else if ( order === 'ZYX' ) { | |
| this._y = Math.asin( - clamp( m31, - 1, 1 ) ); | |
| if ( Math.abs( m31 ) < 0.99999 ) { | |
| this._x = Math.atan2( m32, m33 ); | |
| this._z = Math.atan2( m21, m11 ); | |
| } else { | |
| this._x = 0; | |
| this._z = Math.atan2( - m12, m22 ); | |
| } | |
| } else if ( order === 'YZX' ) { | |
| this._z = Math.asin( clamp( m21, - 1, 1 ) ); | |
| if ( Math.abs( m21 ) < 0.99999 ) { | |
| this._x = Math.atan2( - m23, m22 ); | |
| this._y = Math.atan2( - m31, m11 ); | |
| } else { | |
| this._x = 0; | |
| this._y = Math.atan2( m13, m33 ); | |
| } | |
| } else if ( order === 'XZY' ) { | |
| this._z = Math.asin( - clamp( m12, - 1, 1 ) ); | |
| if ( Math.abs( m12 ) < 0.99999 ) { | |
| this._x = Math.atan2( m32, m22 ); | |
| this._y = Math.atan2( m13, m11 ); | |
| } else { | |
| this._x = Math.atan2( - m23, m33 ); | |
| this._y = 0; | |
| } | |
| } else { | |
| console.warn( 'THREE.Euler: .setFromRotationMatrix() given unsupported order: ' + order ); | |
| } | |
| this._order = order; | |
| if ( update !== false ) this.onChangeCallback(); | |
| return this; | |
| }, | |
| setFromQuaternion: function () { | |
| var matrix = new Matrix4(); | |
| return function setFromQuaternion( q, order, update ) { | |
| matrix.makeRotationFromQuaternion( q ); | |
| return this.setFromRotationMatrix( matrix, order, update ); | |
| }; | |
| }(), | |
| setFromVector3: function ( v, order ) { | |
| return this.set( v.x, v.y, v.z, order || this._order ); | |
| }, | |
| reorder: function () { | |
| // WARNING: this discards revolution information -bhouston | |
| var q = new Quaternion(); | |
| return function reorder( newOrder ) { | |
| q.setFromEuler( this ); | |
| return this.setFromQuaternion( q, newOrder ); | |
| }; | |
| }(), | |
| equals: function ( euler ) { | |
| return ( euler._x === this._x ) && ( euler._y === this._y ) && ( euler._z === this._z ) && ( euler._order === this._order ); | |
| }, | |
| fromArray: function ( array ) { | |
| this._x = array[ 0 ]; | |
| this._y = array[ 1 ]; | |
| this._z = array[ 2 ]; | |
| if ( array[ 3 ] !== undefined ) this._order = array[ 3 ]; | |
| this.onChangeCallback(); | |
| return this; | |
| }, | |
| toArray: function ( array, offset ) { | |
| if ( array === undefined ) array = []; | |
| if ( offset === undefined ) offset = 0; | |
| array[ offset ] = this._x; | |
| array[ offset + 1 ] = this._y; | |
| array[ offset + 2 ] = this._z; | |
| array[ offset + 3 ] = this._order; | |
| return array; | |
| }, | |
| toVector3: function ( optionalResult ) { | |
| if ( optionalResult ) { | |
| return optionalResult.set( this._x, this._y, this._z ); | |
| } else { | |
| return new Vector3( this._x, this._y, this._z ); | |
| } | |
| }, | |
| onChange: function ( callback ) { | |
| this.onChangeCallback = callback; | |
| return this; | |
| }, | |
| onChangeCallback: function () {} | |
| } ); | |
| /** | |
| * @author mrdoob / http://mrdoob.com/ | |
| */ | |
| function Layers() { | |
| this.mask = 1 | 0; | |
| } | |
| Object.assign( Layers.prototype, { | |
| set: function ( channel ) { | |
| this.mask = 1 << channel | 0; | |
| }, | |
| enable: function ( channel ) { | |
| this.mask |= 1 << channel | 0; | |
| }, | |
| toggle: function ( channel ) { | |
| this.mask ^= 1 << channel | 0; | |
| }, | |
| disable: function ( channel ) { | |
| this.mask &= ~ ( 1 << channel | 0 ); | |
| }, | |
| test: function ( layers ) { | |
| return ( this.mask & layers.mask ) !== 0; | |
| } | |
| } ); | |
| /** | |
| * @author mrdoob / http://mrdoob.com/ | |
| * @author mikael emtinger / http://gomo.se/ | |
| * @author alteredq / http://alteredqualia.com/ | |
| * @author WestLangley / http://github.com/WestLangley | |
| * @author elephantatwork / www.elephantatwork.ch | |
| */ | |
| var object3DId = 0; | |
| function Object3D() { | |
| Object.defineProperty( this, 'id', { value: object3DId ++ } ); | |
| this.uuid = _Math.generateUUID(); | |
| this.name = ''; | |
| this.type = 'Object3D'; | |
| this.parent = null; | |
| this.children = []; | |
| this.up = Object3D.DefaultUp.clone(); | |
| var position = new Vector3(); | |
| var rotation = new Euler(); | |
| var quaternion = new Quaternion(); | |
| var scale = new Vector3( 1, 1, 1 ); | |
| function onRotationChange() { | |
| quaternion.setFromEuler( rotation, false ); | |
| } | |
| function onQuaternionChange() { | |
| rotation.setFromQuaternion( quaternion, undefined, false ); | |
| } | |
| rotation.onChange( onRotationChange ); | |
| quaternion.onChange( onQuaternionChange ); | |
| Object.defineProperties( this, { | |
| position: { | |
| enumerable: true, | |
| value: position | |
| }, | |
| rotation: { | |
| enumerable: true, | |
| value: rotation | |
| }, | |
| quaternion: { | |
| enumerable: true, | |
| value: quaternion | |
| }, | |
| scale: { | |
| enumerable: true, | |
| value: scale | |
| }, | |
| modelViewMatrix: { | |
| value: new Matrix4() | |
| }, | |
| normalMatrix: { | |
| value: new Matrix3() | |
| } | |
| } ); | |
| this.matrix = new Matrix4(); | |
| this.matrixWorld = new Matrix4(); | |
| this.matrixAutoUpdate = Object3D.DefaultMatrixAutoUpdate; | |
| this.matrixWorldNeedsUpdate = false; | |
| this.layers = new Layers(); | |
| this.visible = true; | |
| this.castShadow = false; | |
| this.receiveShadow = false; | |
| this.frustumCulled = true; | |
| this.renderOrder = 0; | |
| this.userData = {}; | |
| } | |
| Object3D.DefaultUp = new Vector3( 0, 1, 0 ); | |
| Object3D.DefaultMatrixAutoUpdate = true; | |
| Object3D.prototype = Object.assign( Object.create( EventDispatcher.prototype ), { | |
| constructor: Object3D, | |
| isObject3D: true, | |
| onBeforeRender: function () {}, | |
| onAfterRender: function () {}, | |
| applyMatrix: function ( matrix ) { | |
| this.matrix.multiplyMatrices( matrix, this.matrix ); | |
| this.matrix.decompose( this.position, this.quaternion, this.scale ); | |
| }, | |
| applyQuaternion: function ( q ) { | |
| this.quaternion.premultiply( q ); | |
| return this; | |
| }, | |
| setRotationFromAxisAngle: function ( axis, angle ) { | |
| // assumes axis is normalized | |
| this.quaternion.setFromAxisAngle( axis, angle ); | |
| }, | |
| setRotationFromEuler: function ( euler ) { | |
| this.quaternion.setFromEuler( euler, true ); | |
| }, | |
| setRotationFromMatrix: function ( m ) { | |
| // assumes the upper 3x3 of m is a pure rotation matrix (i.e, unscaled) | |
| this.quaternion.setFromRotationMatrix( m ); | |
| }, | |
| setRotationFromQuaternion: function ( q ) { | |
| // assumes q is normalized | |
| this.quaternion.copy( q ); | |
| }, | |
| rotateOnAxis: function () { | |
| // rotate object on axis in object space | |
| // axis is assumed to be normalized | |
| var q1 = new Quaternion(); | |
| return function rotateOnAxis( axis, angle ) { | |
| q1.setFromAxisAngle( axis, angle ); | |
| this.quaternion.multiply( q1 ); | |
| return this; | |
| }; | |
| }(), | |
| rotateOnWorldAxis: function () { | |
| // rotate object on axis in world space | |
| // axis is assumed to be normalized | |
| // method assumes no rotated parent | |
| var q1 = new Quaternion(); | |
| return function rotateOnWorldAxis( axis, angle ) { | |
| q1.setFromAxisAngle( axis, angle ); | |
| this.quaternion.premultiply( q1 ); | |
| return this; | |
| }; | |
| }(), | |
| rotateX: function () { | |
| var v1 = new Vector3( 1, 0, 0 ); | |
| return function rotateX( angle ) { | |
| return this.rotateOnAxis( v1, angle ); | |
| }; | |
| }(), | |
| rotateY: function () { | |
| var v1 = new Vector3( 0, 1, 0 ); | |
| return function rotateY( angle ) { | |
| return this.rotateOnAxis( v1, angle ); | |
| }; | |
| }(), | |
| rotateZ: function () { | |
| var v1 = new Vector3( 0, 0, 1 ); | |
| return function rotateZ( angle ) { | |
| return this.rotateOnAxis( v1, angle ); | |
| }; | |
| }(), | |
| translateOnAxis: function () { | |
| // translate object by distance along axis in object space | |
| // axis is assumed to be normalized | |
| var v1 = new Vector3(); | |
| return function translateOnAxis( axis, distance ) { | |
| v1.copy( axis ).applyQuaternion( this.quaternion ); | |
| this.position.add( v1.multiplyScalar( distance ) ); | |
| return this; | |
| }; | |
| }(), | |
| translateX: function () { | |
| var v1 = new Vector3( 1, 0, 0 ); | |
| return function translateX( distance ) { | |
| return this.translateOnAxis( v1, distance ); | |
| }; | |
| }(), | |
| translateY: function () { | |
| var v1 = new Vector3( 0, 1, 0 ); | |
| return function translateY( distance ) { | |
| return this.translateOnAxis( v1, distance ); | |
| }; | |
| }(), | |
| translateZ: function () { | |
| var v1 = new Vector3( 0, 0, 1 ); | |
| return function translateZ( distance ) { | |
| return this.translateOnAxis( v1, distance ); | |
| }; | |
| }(), | |
| localToWorld: function ( vector ) { | |
| return vector.applyMatrix4( this.matrixWorld ); | |
| }, | |
| worldToLocal: function () { | |
| var m1 = new Matrix4(); | |
| return function worldToLocal( vector ) { | |
| return vector.applyMatrix4( m1.getInverse( this.matrixWorld ) ); | |
| }; | |
| }(), | |
| lookAt: function () { | |
| // This method does not support objects with rotated and/or translated parent(s) | |
| var m1 = new Matrix4(); | |
| var vector = new Vector3(); | |
| return function lookAt( x, y, z ) { | |
| if ( x.isVector3 ) { | |
| vector.copy( x ); | |
| } else { | |
| vector.set( x, y, z ); | |
| } | |
| if ( this.isCamera ) { | |
| m1.lookAt( this.position, vector, this.up ); | |
| } else { | |
| m1.lookAt( vector, this.position, this.up ); | |
| } | |
| this.quaternion.setFromRotationMatrix( m1 ); | |
| }; | |
| }(), | |
| add: function ( object ) { | |
| if ( arguments.length > 1 ) { | |
| for ( var i = 0; i < arguments.length; i ++ ) { | |
| this.add( arguments[ i ] ); | |
| } | |
| return this; | |
| } | |
| if ( object === this ) { | |
| console.error( "THREE.Object3D.add: object can't be added as a child of itself.", object ); | |
| return this; | |
| } | |
| if ( ( object && object.isObject3D ) ) { | |
| if ( object.parent !== null ) { | |
| object.parent.remove( object ); | |
| } | |
| object.parent = this; | |
| object.dispatchEvent( { type: 'added' } ); | |
| this.children.push( object ); | |
| } else { | |
| console.error( "THREE.Object3D.add: object not an instance of THREE.Object3D.", object ); | |
| } | |
| return this; | |
| }, | |
| remove: function ( object ) { | |
| if ( arguments.length > 1 ) { | |
| for ( var i = 0; i < arguments.length; i ++ ) { | |
| this.remove( arguments[ i ] ); | |
| } | |
| return this; | |
| } | |
| var index = this.children.indexOf( object ); | |
| if ( index !== - 1 ) { | |
| object.parent = null; | |
| object.dispatchEvent( { type: 'removed' } ); | |
| this.children.splice( index, 1 ); | |
| } | |
| return this; | |
| }, | |
| getObjectById: function ( id ) { | |
| return this.getObjectByProperty( 'id', id ); | |
| }, | |
| getObjectByName: function ( name ) { | |
| return this.getObjectByProperty( 'name', name ); | |
| }, | |
| getObjectByProperty: function ( name, value ) { | |
| if ( this[ name ] === value ) return this; | |
| for ( var i = 0, l = this.children.length; i < l; i ++ ) { | |
| var child = this.children[ i ]; | |
| var object = child.getObjectByProperty( name, value ); | |
| if ( object !== undefined ) { | |
| return object; | |
| } | |
| } | |
| return undefined; | |
| }, | |
| getWorldPosition: function ( optionalTarget ) { | |
| var result = optionalTarget || new Vector3(); | |
| this.updateMatrixWorld( true ); | |
| return result.setFromMatrixPosition( this.matrixWorld ); | |
| }, | |
| getWorldQuaternion: function () { | |
| var position = new Vector3(); | |
| var scale = new Vector3(); | |
| return function getWorldQuaternion( optionalTarget ) { | |
| var result = optionalTarget || new Quaternion(); | |
| this.updateMatrixWorld( true ); | |
| this.matrixWorld.decompose( position, result, scale ); | |
| return result; | |
| }; | |
| }(), | |
| getWorldRotation: function () { | |
| var quaternion = new Quaternion(); | |
| return function getWorldRotation( optionalTarget ) { | |
| var result = optionalTarget || new Euler(); | |
| this.getWorldQuaternion( quaternion ); | |
| return result.setFromQuaternion( quaternion, this.rotation.order, false ); | |
| }; | |
| }(), | |
| getWorldScale: function () { | |
| var position = new Vector3(); | |
| var quaternion = new Quaternion(); | |
| return function getWorldScale( optionalTarget ) { | |
| var result = optionalTarget || new Vector3(); | |
| this.updateMatrixWorld( true ); | |
| this.matrixWorld.decompose( position, quaternion, result ); | |
| return result; | |
| }; | |
| }(), | |
| getWorldDirection: function () { | |
| var quaternion = new Quaternion(); | |
| return function getWorldDirection( optionalTarget ) { | |
| var result = optionalTarget || new Vector3(); | |
| this.getWorldQuaternion( quaternion ); | |
| return result.set( 0, 0, 1 ).applyQuaternion( quaternion ); | |
| }; | |
| }(), | |
| raycast: function () {}, | |
| traverse: function ( callback ) { | |
| callback( this ); | |
| var children = this.children; | |
| for ( var i = 0, l = children.length; i < l; i ++ ) { | |
| children[ i ].traverse( callback ); | |
| } | |
| }, | |
| traverseVisible: function ( callback ) { | |
| if ( this.visible === false ) return; | |
| callback( this ); | |
| var children = this.children; | |
| for ( var i = 0, l = children.length; i < l; i ++ ) { | |
| children[ i ].traverseVisible( callback ); | |
| } | |
| }, | |
| traverseAncestors: function ( callback ) { | |
| var parent = this.parent; | |
| if ( parent !== null ) { | |
| callback( parent ); | |
| parent.traverseAncestors( callback ); | |
| } | |
| }, | |
| updateMatrix: function () { | |
| this.matrix.compose( this.position, this.quaternion, this.scale ); | |
| this.matrixWorldNeedsUpdate = true; | |
| }, | |
| updateMatrixWorld: function ( force ) { | |
| if ( this.matrixAutoUpdate ) this.updateMatrix(); | |
| if ( this.matrixWorldNeedsUpdate || force ) { | |
| if ( this.parent === null ) { | |
| this.matrixWorld.copy( this.matrix ); | |
| } else { | |
| this.matrixWorld.multiplyMatrices( this.parent.matrixWorld, this.matrix ); | |
| } | |
| this.matrixWorldNeedsUpdate = false; | |
| force = true; | |
| } | |
| // update children | |
| var children = this.children; | |
| for ( var i = 0, l = children.length; i < l; i ++ ) { | |
| children[ i ].updateMatrixWorld( force ); | |
| } | |
| }, | |
| toJSON: function ( meta ) { | |
| // meta is a string when called from JSON.stringify | |
| var isRootObject = ( meta === undefined || typeof meta === 'string' ); | |
| var output = {}; | |
| // meta is a hash used to collect geometries, materials. | |
| // not providing it implies that this is the root object | |
| // being serialized. | |
| if ( isRootObject ) { | |
| // initialize meta obj | |
| meta = { | |
| geometries: {}, | |
| materials: {}, | |
| textures: {}, | |
| images: {}, | |
| shapes: {} | |
| }; | |
| output.metadata = { | |
| version: 4.5, | |
| type: 'Object', | |
| generator: 'Object3D.toJSON' | |
| }; | |
| } | |
| // standard Object3D serialization | |
| var object = {}; | |
| object.uuid = this.uuid; | |
| object.type = this.type; | |
| if ( this.name !== '' ) object.name = this.name; | |
| if ( this.castShadow === true ) object.castShadow = true; | |
| if ( this.receiveShadow === true ) object.receiveShadow = true; | |
| if ( this.visible === false ) object.visible = false; | |
| if ( JSON.stringify( this.userData ) !== '{}' ) object.userData = this.userData; | |
| object.matrix = this.matrix.toArray(); | |
| // | |
| function serialize( library, element ) { | |
| if ( library[ element.uuid ] === undefined ) { | |
| library[ element.uuid ] = element.toJSON( meta ); | |
| } | |
| return element.uuid; | |
| } | |
| if ( this.geometry !== undefined ) { | |
| object.geometry = serialize( meta.geometries, this.geometry ); | |
| var parameters = this.geometry.parameters; | |
| if ( parameters !== undefined && parameters.shapes !== undefined ) { | |
| var shapes = parameters.shapes; | |
| if ( Array.isArray( shapes ) ) { | |
| for ( var i = 0, l = shapes.length; i < l; i ++ ) { | |
| var shape = shapes[ i ]; | |
| serialize( meta.shapes, shape ); | |
| } | |
| } else { | |
| serialize( meta.shapes, shapes ); | |
| } | |
| } | |
| } | |
| if ( this.material !== undefined ) { | |
| if ( Array.isArray( this.material ) ) { | |
| var uuids = []; | |
| for ( var i = 0, l = this.material.length; i < l; i ++ ) { | |
| uuids.push( serialize( meta.materials, this.material[ i ] ) ); | |
| } | |
| object.material = uuids; | |
| } else { | |
| object.material = serialize( meta.materials, this.material ); | |
| } | |
| } | |
| // | |
| if ( this.children.length > 0 ) { | |
| object.children = []; | |
| for ( var i = 0; i < this.children.length; i ++ ) { | |
| object.children.push( this.children[ i ].toJSON( meta ).object ); | |
| } | |
| } | |
| if ( isRootObject ) { | |
| var geometries = extractFromCache( meta.geometries ); | |
| var materials = extractFromCache( meta.materials ); | |
| var textures = extractFromCache( meta.textures ); | |
| var images = extractFromCache( meta.images ); | |
| var shapes = extractFromCache( meta.shapes ); | |
| if ( geometries.length > 0 ) output.geometries = geometries; | |
| if ( materials.length > 0 ) output.materials = materials; | |
| if ( textures.length > 0 ) output.textures = textures; | |
| if ( images.length > 0 ) output.images = images; | |
| if ( shapes.length > 0 ) output.shapes = shapes; | |
| } | |
| output.object = object; | |
| return output; | |
| // extract data from the cache hash | |
| // remove metadata on each item | |
| // and return as array | |
| function extractFromCache( cache ) { | |
| var values = []; | |
| for ( var key in cache ) { | |
| var data = cache[ key ]; | |
| delete data.metadata; | |
| values.push( data ); | |
| } | |
| return values; | |
| } | |
| }, | |
| clone: function ( recursive ) { | |
| return new this.constructor().copy( this, recursive ); | |
| }, | |
| copy: function ( source, recursive ) { | |
| if ( recursive === undefined ) recursive = true; | |
| this.name = source.name; | |
| this.up.copy( source.up ); | |
| this.position.copy( source.position ); | |
| this.quaternion.copy( source.quaternion ); | |
| this.scale.copy( source.scale ); | |
| this.matrix.copy( source.matrix ); | |
| this.matrixWorld.copy( source.matrixWorld ); | |
| this.matrixAutoUpdate = source.matrixAutoUpdate; | |
| this.matrixWorldNeedsUpdate = source.matrixWorldNeedsUpdate; | |
| this.layers.mask = source.layers.mask; | |
| this.visible = source.visible; | |
| this.castShadow = source.castShadow; | |
| this.receiveShadow = source.receiveShadow; | |
| this.frustumCulled = source.frustumCulled; | |
| this.renderOrder = source.renderOrder; | |
| this.userData = JSON.parse( JSON.stringify( source.userData ) ); | |
| if ( recursive === true ) { | |
| for ( var i = 0; i < source.children.length; i ++ ) { | |
| var child = source.children[ i ]; | |
| this.add( child.clone() ); | |
| } | |
| } | |
| return this; | |
| } | |
| } ); | |
| /** | |
| * @author mrdoob / http://mrdoob.com/ | |
| * @author mikael emtinger / http://gomo.se/ | |
| * @author WestLangley / http://github.com/WestLangley | |
| */ | |
| function Camera() { | |
| Object3D.call( this ); | |
| this.type = 'Camera'; | |
| this.matrixWorldInverse = new Matrix4(); | |
| this.projectionMatrix = new Matrix4(); | |
| } | |
| Camera.prototype = Object.assign( Object.create( Object3D.prototype ), { | |
| constructor: Camera, | |
| isCamera: true, | |
| copy: function ( source, recursive ) { | |
| Object3D.prototype.copy.call( this, source, recursive ); | |
| this.matrixWorldInverse.copy( source.matrixWorldInverse ); | |
| this.projectionMatrix.copy( source.projectionMatrix ); | |
| return this; | |
| }, | |
| getWorldDirection: function () { | |
| var quaternion = new Quaternion(); | |
| return function getWorldDirection( optionalTarget ) { | |
| var result = optionalTarget || new Vector3(); | |
| this.getWorldQuaternion( quaternion ); | |
| return result.set( 0, 0, - 1 ).applyQuaternion( quaternion ); | |
| }; | |
| }(), | |
| updateMatrixWorld: function ( force ) { | |
| Object3D.prototype.updateMatrixWorld.call( this, force ); | |
| this.matrixWorldInverse.getInverse( this.matrixWorld ); | |
| }, | |
| clone: function () { | |
| return new this.constructor().copy( this ); | |
| } | |
| } ); | |
| /** | |
| * @author alteredq / http://alteredqualia.com/ | |
| * @author arose / http://github.com/arose | |
| */ | |
| function OrthographicCamera( left, right, top, bottom, near, far ) { | |
| Camera.call( this ); | |
| this.type = 'OrthographicCamera'; | |
| this.zoom = 1; | |
| this.view = null; | |
| this.left = left; | |
| this.right = right; | |
| this.top = top; | |
| this.bottom = bottom; | |
| this.near = ( near !== undefined ) ? near : 0.1; | |
| this.far = ( far !== undefined ) ? far : 2000; | |
| this.updateProjectionMatrix(); | |
| } | |
| OrthographicCamera.prototype = Object.assign( Object.create( Camera.prototype ), { | |
| constructor: OrthographicCamera, | |
| isOrthographicCamera: true, | |
| copy: function ( source, recursive ) { | |
| Camera.prototype.copy.call( this, source, recursive ); | |
| this.left = source.left; | |
| this.right = source.right; | |
| this.top = source.top; | |
| this.bottom = source.bottom; | |
| this.near = source.near; | |
| this.far = source.far; | |
| this.zoom = source.zoom; | |
| this.view = source.view === null ? null : Object.assign( {}, source.view ); | |
| return this; | |
| }, | |
| setViewOffset: function ( fullWidth, fullHeight, x, y, width, height ) { | |
| if ( this.view === null ) { | |
| this.view = { | |
| enabled: true, | |
| fullWidth: 1, | |
| fullHeight: 1, | |
| offsetX: 0, | |
| offsetY: 0, | |
| width: 1, | |
| height: 1 | |
| }; | |
| } | |
| this.view.enabled = true; | |
| this.view.fullWidth = fullWidth; | |
| this.view.fullHeight = fullHeight; | |
| this.view.offsetX = x; | |
| this.view.offsetY = y; | |
| this.view.width = width; | |
| this.view.height = height; | |
| this.updateProjectionMatrix(); | |
| }, | |
| clearViewOffset: function () { | |
| if ( this.view !== null ) { | |
| this.view.enabled = false; | |
| } | |
| this.updateProjectionMatrix(); | |
| }, | |
| updateProjectionMatrix: function () { | |
| var dx = ( this.right - this.left ) / ( 2 * this.zoom ); | |
| var dy = ( this.top - this.bottom ) / ( 2 * this.zoom ); | |
| var cx = ( this.right + this.left ) / 2; | |
| var cy = ( this.top + this.bottom ) / 2; | |
| var left = cx - dx; | |
| var right = cx + dx; | |
| var top = cy + dy; | |
| var bottom = cy - dy; | |
| if ( this.view !== null && this.view.enabled ) { | |
| var zoomW = this.zoom / ( this.view.width / this.view.fullWidth ); | |
| var zoomH = this.zoom / ( this.view.height / this.view.fullHeight ); | |
| var scaleW = ( this.right - this.left ) / this.view.width; | |
| var scaleH = ( this.top - this.bottom ) / this.view.height; | |
| left += scaleW * ( this.view.offsetX / zoomW ); | |
| right = left + scaleW * ( this.view.width / zoomW ); | |
| top -= scaleH * ( this.view.offsetY / zoomH ); | |
| bottom = top - scaleH * ( this.view.height / zoomH ); | |
| } | |
| this.projectionMatrix.makeOrthographic( left, right, top, bottom, this.near, this.far ); | |
| }, | |
| toJSON: function ( meta ) { | |
| var data = Object3D.prototype.toJSON.call( this, meta ); | |
| data.object.zoom = this.zoom; | |
| data.object.left = this.left; | |
| data.object.right = this.right; | |
| data.object.top = this.top; | |
| data.object.bottom = this.bottom; | |
| data.object.near = this.near; | |
| data.object.far = this.far; | |
| if ( this.view !== null ) data.object.view = Object.assign( {}, this.view ); | |
| return data; | |
| } | |
| } ); | |
| /** | |
| * @author mrdoob / http://mrdoob.com/ | |
| * @author alteredq / http://alteredqualia.com/ | |
| */ | |
| function Face3( a, b, c, normal, color, materialIndex ) { | |
| this.a = a; | |
| this.b = b; | |
| this.c = c; | |
| this.normal = ( normal && normal.isVector3 ) ? normal : new Vector3(); | |
| this.vertexNormals = Array.isArray( normal ) ? normal : []; | |
| this.color = ( color && color.isColor ) ? color : new Color(); | |
| this.vertexColors = Array.isArray( color ) ? color : []; | |
| this.materialIndex = materialIndex !== undefined ? materialIndex : 0; | |
| } | |
| Object.assign( Face3.prototype, { | |
| clone: function () { | |
| return new this.constructor().copy( this ); | |
| }, | |
| copy: function ( source ) { | |
| this.a = source.a; | |
| this.b = source.b; | |
| this.c = source.c; | |
| this.normal.copy( source.normal ); | |
| this.color.copy( source.color ); | |
| this.materialIndex = source.materialIndex; | |
| for ( var i = 0, il = source.vertexNormals.length; i < il; i ++ ) { | |
| this.vertexNormals[ i ] = source.vertexNormals[ i ].clone(); | |
| } | |
| for ( var i = 0, il = source.vertexColors.length; i < il; i ++ ) { | |
| this.vertexColors[ i ] = source.vertexColors[ i ].clone(); | |
| } | |
| return this; | |
| } | |
| } ); | |
| /** | |
| * @author mrdoob / http://mrdoob.com/ | |
| * @author kile / http://kile.stravaganza.org/ | |
| * @author alteredq / http://alteredqualia.com/ | |
| * @author mikael emtinger / http://gomo.se/ | |
| * @author zz85 / http://www.lab4games.net/zz85/blog | |
| * @author bhouston / http://clara.io | |
| */ | |
| var geometryId = 0; // Geometry uses even numbers as Id | |
| function Geometry() { | |
| Object.defineProperty( this, 'id', { value: geometryId += 2 } ); | |
| this.uuid = _Math.generateUUID(); | |
| this.name = ''; | |
| this.type = 'Geometry'; | |
| this.vertices = []; | |
| this.colors = []; | |
| this.faces = []; | |
| this.faceVertexUvs = [[]]; | |
| this.morphTargets = []; | |
| this.morphNormals = []; | |
| this.skinWeights = []; | |
| this.skinIndices = []; | |
| this.lineDistances = []; | |
| this.boundingBox = null; | |
| this.boundingSphere = null; | |
| // update flags | |
| this.elementsNeedUpdate = false; | |
| this.verticesNeedUpdate = false; | |
| this.uvsNeedUpdate = false; | |
| this.normalsNeedUpdate = false; | |
| this.colorsNeedUpdate = false; | |
| this.lineDistancesNeedUpdate = false; | |
| this.groupsNeedUpdate = false; | |
| } | |
| Geometry.prototype = Object.assign( Object.create( EventDispatcher.prototype ), { | |
| constructor: Geometry, | |
| isGeometry: true, | |
| applyMatrix: function ( matrix ) { | |
| var normalMatrix = new Matrix3().getNormalMatrix( matrix ); | |
| for ( var i = 0, il = this.vertices.length; i < il; i ++ ) { | |
| var vertex = this.vertices[ i ]; | |
| vertex.applyMatrix4( matrix ); | |
| } | |
| for ( var i = 0, il = this.faces.length; i < il; i ++ ) { | |
| var face = this.faces[ i ]; | |
| face.normal.applyMatrix3( normalMatrix ).normalize(); | |
| for ( var j = 0, jl = face.vertexNormals.length; j < jl; j ++ ) { | |
| face.vertexNormals[ j ].applyMatrix3( normalMatrix ).normalize(); | |
| } | |
| } | |
| if ( this.boundingBox !== null ) { | |
| this.computeBoundingBox(); | |
| } | |
| if ( this.boundingSphere !== null ) { | |
| this.computeBoundingSphere(); | |
| } | |
| this.verticesNeedUpdate = true; | |
| this.normalsNeedUpdate = true; | |
| return this; | |
| }, | |
| rotateX: function () { | |
| // rotate geometry around world x-axis | |
| var m1 = new Matrix4(); | |
| return function rotateX( angle ) { | |
| m1.makeRotationX( angle ); | |
| this.applyMatrix( m1 ); | |
| return this; | |
| }; | |
| }(), | |
| rotateY: function () { | |
| // rotate geometry around world y-axis | |
| var m1 = new Matrix4(); | |
| return function rotateY( angle ) { | |
| m1.makeRotationY( angle ); | |
| this.applyMatrix( m1 ); | |
| return this; | |
| }; | |
| }(), | |
| rotateZ: function () { | |
| // rotate geometry around world z-axis | |
| var m1 = new Matrix4(); | |
| return function rotateZ( angle ) { | |
| m1.makeRotationZ( angle ); | |
| this.applyMatrix( m1 ); | |
| return this; | |
| }; | |
| }(), | |
| translate: function () { | |
| // translate geometry | |
| var m1 = new Matrix4(); | |
| return function translate( x, y, z ) { | |
| m1.makeTranslation( x, y, z ); | |
| this.applyMatrix( m1 ); | |
| return this; | |
| }; | |
| }(), | |
| scale: function () { | |
| // scale geometry | |
| var m1 = new Matrix4(); | |
| return function scale( x, y, z ) { | |
| m1.makeScale( x, y, z ); | |
| this.applyMatrix( m1 ); | |
| return this; | |
| }; | |
| }(), | |
| lookAt: function () { | |
| var obj = new Object3D(); | |
| return function lookAt( vector ) { | |
| obj.lookAt( vector ); | |
| obj.updateMatrix(); | |
| this.applyMatrix( obj.matrix ); | |
| }; | |
| }(), | |
| fromBufferGeometry: function ( geometry ) { | |
| var scope = this; | |
| var indices = geometry.index !== null ? geometry.index.array : undefined; | |
| var attributes = geometry.attributes; | |
| var positions = attributes.position.array; | |
| var normals = attributes.normal !== undefined ? attributes.normal.array : undefined; | |
| var colors = attributes.color !== undefined ? attributes.color.array : undefined; | |
| var uvs = attributes.uv !== undefined ? attributes.uv.array : undefined; | |
| var uvs2 = attributes.uv2 !== undefined ? attributes.uv2.array : undefined; | |
| if ( uvs2 !== undefined ) this.faceVertexUvs[ 1 ] = []; | |
| var tempNormals = []; | |
| var tempUVs = []; | |
| var tempUVs2 = []; | |
| for ( var i = 0, j = 0; i < positions.length; i += 3, j += 2 ) { | |
| scope.vertices.push( new Vector3( positions[ i ], positions[ i + 1 ], positions[ i + 2 ] ) ); | |
| if ( normals !== undefined ) { | |
| tempNormals.push( new Vector3( normals[ i ], normals[ i + 1 ], normals[ i + 2 ] ) ); | |
| } | |
| if ( colors !== undefined ) { | |
| scope.colors.push( new Color( colors[ i ], colors[ i + 1 ], colors[ i + 2 ] ) ); | |
| } | |
| if ( uvs !== undefined ) { | |
| tempUVs.push( new Vector2( uvs[ j ], uvs[ j + 1 ] ) ); | |
| } | |
| if ( uvs2 !== undefined ) { | |
| tempUVs2.push( new Vector2( uvs2[ j ], uvs2[ j + 1 ] ) ); | |
| } | |
| } | |
| function addFace( a, b, c, materialIndex ) { | |
| var vertexNormals = normals !== undefined ? [ tempNormals[ a ].clone(), tempNormals[ b ].clone(), tempNormals[ c ].clone() ] : []; | |
| var vertexColors = colors !== undefined ? [ scope.colors[ a ].clone(), scope.colors[ b ].clone(), scope.colors[ c ].clone() ] : []; | |
| var face = new Face3( a, b, c, vertexNormals, vertexColors, materialIndex ); | |
| scope.faces.push( face ); | |
| if ( uvs !== undefined ) { | |
| scope.faceVertexUvs[ 0 ].push( [ tempUVs[ a ].clone(), tempUVs[ b ].clone(), tempUVs[ c ].clone() ] ); | |
| } | |
| if ( uvs2 !== undefined ) { | |
| scope.faceVertexUvs[ 1 ].push( [ tempUVs2[ a ].clone(), tempUVs2[ b ].clone(), tempUVs2[ c ].clone() ] ); | |
| } | |
| } | |
| var groups = geometry.groups; | |
| if ( groups.length > 0 ) { | |
| for ( var i = 0; i < groups.length; i ++ ) { | |
| var group = groups[ i ]; | |
| var start = group.start; | |
| var count = group.count; | |
| for ( var j = start, jl = start + count; j < jl; j += 3 ) { | |
| if ( indices !== undefined ) { | |
| addFace( indices[ j ], indices[ j + 1 ], indices[ j + 2 ], group.materialIndex ); | |
| } else { | |
| addFace( j, j + 1, j + 2, group.materialIndex ); | |
| } | |
| } | |
| } | |
| } else { | |
| if ( indices !== undefined ) { | |
| for ( var i = 0; i < indices.length; i += 3 ) { | |
| addFace( indices[ i ], indices[ i + 1 ], indices[ i + 2 ] ); | |
| } | |
| } else { | |
| for ( var i = 0; i < positions.length / 3; i += 3 ) { | |
| addFace( i, i + 1, i + 2 ); | |
| } | |
| } | |
| } | |
| this.computeFaceNormals(); | |
| if ( geometry.boundingBox !== null ) { | |
| this.boundingBox = geometry.boundingBox.clone(); | |
| } | |
| if ( geometry.boundingSphere !== null ) { | |
| this.boundingSphere = geometry.boundingSphere.clone(); | |
| } | |
| return this; | |
| }, | |
| center: function () { | |
| this.computeBoundingBox(); | |
| var offset = this.boundingBox.getCenter().negate(); | |
| this.translate( offset.x, offset.y, offset.z ); | |
| return offset; | |
| }, | |
| normalize: function () { | |
| this.computeBoundingSphere(); | |
| var center = this.boundingSphere.center; | |
| var radius = this.boundingSphere.radius; | |
| var s = radius === 0 ? 1 : 1.0 / radius; | |
| var matrix = new Matrix4(); | |
| matrix.set( | |
| s, 0, 0, - s * center.x, | |
| 0, s, 0, - s * center.y, | |
| 0, 0, s, - s * center.z, | |
| 0, 0, 0, 1 | |
| ); | |
| this.applyMatrix( matrix ); | |
| return this; | |
| }, | |
| computeFaceNormals: function () { | |
| var cb = new Vector3(), ab = new Vector3(); | |
| for ( var f = 0, fl = this.faces.length; f < fl; f ++ ) { | |
| var face = this.faces[ f ]; | |
| var vA = this.vertices[ face.a ]; | |
| var vB = this.vertices[ face.b ]; | |
| var vC = this.vertices[ face.c ]; | |
| cb.subVectors( vC, vB ); | |
| ab.subVectors( vA, vB ); | |
| cb.cross( ab ); | |
| cb.normalize(); | |
| face.normal.copy( cb ); | |
| } | |
| }, | |
| computeVertexNormals: function ( areaWeighted ) { | |
| if ( areaWeighted === undefined ) areaWeighted = true; | |
| var v, vl, f, fl, face, vertices; | |
| vertices = new Array( this.vertices.length ); | |
| for ( v = 0, vl = this.vertices.length; v < vl; v ++ ) { | |
| vertices[ v ] = new Vector3(); | |
| } | |
| if ( areaWeighted ) { | |
| // vertex normals weighted by triangle areas | |
| // http://www.iquilezles.org/www/articles/normals/normals.htm | |
| var vA, vB, vC; | |
| var cb = new Vector3(), ab = new Vector3(); | |
| for ( f = 0, fl = this.faces.length; f < fl; f ++ ) { | |
| face = this.faces[ f ]; | |
| vA = this.vertices[ face.a ]; | |
| vB = this.vertices[ face.b ]; | |
| vC = this.vertices[ face.c ]; | |
| cb.subVectors( vC, vB ); | |
| ab.subVectors( vA, vB ); | |
| cb.cross( ab ); | |
| vertices[ face.a ].add( cb ); | |
| vertices[ face.b ].add( cb ); | |
| vertices[ face.c ].add( cb ); | |
| } | |
| } else { | |
| this.computeFaceNormals(); | |
| for ( f = 0, fl = this.faces.length; f < fl; f ++ ) { | |
| face = this.faces[ f ]; | |
| vertices[ face.a ].add( face.normal ); | |
| vertices[ face.b ].add( face.normal ); | |
| vertices[ face.c ].add( face.normal ); | |
| } | |
| } | |
| for ( v = 0, vl = this.vertices.length; v < vl; v ++ ) { | |
| vertices[ v ].normalize(); | |
| } | |
| for ( f = 0, fl = this.faces.length; f < fl; f ++ ) { | |
| face = this.faces[ f ]; | |
| var vertexNormals = face.vertexNormals; | |
| if ( vertexNormals.length === 3 ) { | |
| vertexNormals[ 0 ].copy( vertices[ face.a ] ); | |
| vertexNormals[ 1 ].copy( vertices[ face.b ] ); | |
| vertexNormals[ 2 ].copy( vertices[ face.c ] ); | |
| } else { | |
| vertexNormals[ 0 ] = vertices[ face.a ].clone(); | |
| vertexNormals[ 1 ] = vertices[ face.b ].clone(); | |
| vertexNormals[ 2 ] = vertices[ face.c ].clone(); | |
| } | |
| } | |
| if ( this.faces.length > 0 ) { | |
| this.normalsNeedUpdate = true; | |
| } | |
| }, | |
| computeFlatVertexNormals: function () { | |
| var f, fl, face; | |
| this.computeFaceNormals(); | |
| for ( f = 0, fl = this.faces.length; f < fl; f ++ ) { | |
| face = this.faces[ f ]; | |
| var vertexNormals = face.vertexNormals; | |
| if ( vertexNormals.length === 3 ) { | |
| vertexNormals[ 0 ].copy( face.normal ); | |
| vertexNormals[ 1 ].copy( face.normal ); | |
| vertexNormals[ 2 ].copy( face.normal ); | |
| } else { | |
| vertexNormals[ 0 ] = face.normal.clone(); | |
| vertexNormals[ 1 ] = face.normal.clone(); | |
| vertexNormals[ 2 ] = face.normal.clone(); | |
| } | |
| } | |
| if ( this.faces.length > 0 ) { | |
| this.normalsNeedUpdate = true; | |
| } | |
| }, | |
| computeMorphNormals: function () { | |
| var i, il, f, fl, face; | |
| // save original normals | |
| // - create temp variables on first access | |
| // otherwise just copy (for faster repeated calls) | |
| for ( f = 0, fl = this.faces.length; f < fl; f ++ ) { | |
| face = this.faces[ f ]; | |
| if ( ! face.__originalFaceNormal ) { | |
| face.__originalFaceNormal = face.normal.clone(); | |
| } else { | |
| face.__originalFaceNormal.copy( face.normal ); | |
| } | |
| if ( ! face.__originalVertexNormals ) face.__originalVertexNormals = []; | |
| for ( i = 0, il = face.vertexNormals.length; i < il; i ++ ) { | |
| if ( ! face.__originalVertexNormals[ i ] ) { | |
| face.__originalVertexNormals[ i ] = face.vertexNormals[ i ].clone(); | |
| } else { | |
| face.__originalVertexNormals[ i ].copy( face.vertexNormals[ i ] ); | |
| } | |
| } | |
| } | |
| // use temp geometry to compute face and vertex normals for each morph | |
| var tmpGeo = new Geometry(); | |
| tmpGeo.faces = this.faces; | |
| for ( i = 0, il = this.morphTargets.length; i < il; i ++ ) { | |
| // create on first access | |
| if ( ! this.morphNormals[ i ] ) { | |
| this.morphNormals[ i ] = {}; | |
| this.morphNormals[ i ].faceNormals = []; | |
| this.morphNormals[ i ].vertexNormals = []; | |
| var dstNormalsFace = this.morphNormals[ i ].faceNormals; | |
| var dstNormalsVertex = this.morphNormals[ i ].vertexNormals; | |
| var faceNormal, vertexNormals; | |
| for ( f = 0, fl = this.faces.length; f < fl; f ++ ) { | |
| faceNormal = new Vector3(); | |
| vertexNormals = { a: new Vector3(), b: new Vector3(), c: new Vector3() }; | |
| dstNormalsFace.push( faceNormal ); | |
| dstNormalsVertex.push( vertexNormals ); | |
| } | |
| } | |
| var morphNormals = this.morphNormals[ i ]; | |
| // set vertices to morph target | |
| tmpGeo.vertices = this.morphTargets[ i ].vertices; | |
| // compute morph normals | |
| tmpGeo.computeFaceNormals(); | |
| tmpGeo.computeVertexNormals(); | |
| // store morph normals | |
| var faceNormal, vertexNormals; | |
| for ( f = 0, fl = this.faces.length; f < fl; f ++ ) { | |
| face = this.faces[ f ]; | |
| faceNormal = morphNormals.faceNormals[ f ]; | |
| vertexNormals = morphNormals.vertexNormals[ f ]; | |
| faceNormal.copy( face.normal ); | |
| vertexNormals.a.copy( face.vertexNormals[ 0 ] ); | |
| vertexNormals.b.copy( face.vertexNormals[ 1 ] ); | |
| vertexNormals.c.copy( face.vertexNormals[ 2 ] ); | |
| } | |
| } | |
| // restore original normals | |
| for ( f = 0, fl = this.faces.length; f < fl; f ++ ) { | |
| face = this.faces[ f ]; | |
| face.normal = face.__originalFaceNormal; | |
| face.vertexNormals = face.__originalVertexNormals; | |
| } | |
| }, | |
| computeBoundingBox: function () { | |
| if ( this.boundingBox === null ) { | |
| this.boundingBox = new Box3(); | |
| } | |
| this.boundingBox.setFromPoints( this.vertices ); | |
| }, | |
| computeBoundingSphere: function () { | |
| if ( this.boundingSphere === null ) { | |
| this.boundingSphere = new Sphere(); | |
| } | |
| this.boundingSphere.setFromPoints( this.vertices ); | |
| }, | |
| merge: function ( geometry, matrix, materialIndexOffset ) { | |
| if ( ! ( geometry && geometry.isGeometry ) ) { | |
| console.error( 'THREE.Geometry.merge(): geometry not an instance of THREE.Geometry.', geometry ); | |
| return; | |
| } | |
| var normalMatrix, | |
| vertexOffset = this.vertices.length, | |
| vertices1 = this.vertices, | |
| vertices2 = geometry.vertices, | |
| faces1 = this.faces, | |
| faces2 = geometry.faces, | |
| uvs1 = this.faceVertexUvs[ 0 ], | |
| uvs2 = geometry.faceVertexUvs[ 0 ], | |
| colors1 = this.colors, | |
| colors2 = geometry.colors; | |
| if ( materialIndexOffset === undefined ) materialIndexOffset = 0; | |
| if ( matrix !== undefined ) { | |
| normalMatrix = new Matrix3().getNormalMatrix( matrix ); | |
| } | |
| // vertices | |
| for ( var i = 0, il = vertices2.length; i < il; i ++ ) { | |
| var vertex = vertices2[ i ]; | |
| var vertexCopy = vertex.clone(); | |
| if ( matrix !== undefined ) vertexCopy.applyMatrix4( matrix ); | |
| vertices1.push( vertexCopy ); | |
| } | |
| // colors | |
| for ( var i = 0, il = colors2.length; i < il; i ++ ) { | |
| colors1.push( colors2[ i ].clone() ); | |
| } | |
| // faces | |
| for ( i = 0, il = faces2.length; i < il; i ++ ) { | |
| var face = faces2[ i ], faceCopy, normal, color, | |
| faceVertexNormals = face.vertexNormals, | |
| faceVertexColors = face.vertexColors; | |
| faceCopy = new Face3( face.a + vertexOffset, face.b + vertexOffset, face.c + vertexOffset ); | |
| faceCopy.normal.copy( face.normal ); | |
| if ( normalMatrix !== undefined ) { | |
| faceCopy.normal.applyMatrix3( normalMatrix ).normalize(); | |
| } | |
| for ( var j = 0, jl = faceVertexNormals.length; j < jl; j ++ ) { | |
| normal = faceVertexNormals[ j ].clone(); | |
| if ( normalMatrix !== undefined ) { | |
| normal.applyMatrix3( normalMatrix ).normalize(); | |
| } | |
| faceCopy.vertexNormals.push( normal ); | |
| } | |
| faceCopy.color.copy( face.color ); | |
| for ( var j = 0, jl = faceVertexColors.length; j < jl; j ++ ) { | |
| color = faceVertexColors[ j ]; | |
| faceCopy.vertexColors.push( color.clone() ); | |
| } | |
| faceCopy.materialIndex = face.materialIndex + materialIndexOffset; | |
| faces1.push( faceCopy ); | |
| } | |
| // uvs | |
| for ( i = 0, il = uvs2.length; i < il; i ++ ) { | |
| var uv = uvs2[ i ], uvCopy = []; | |
| if ( uv === undefined ) { | |
| continue; | |
| } | |
| for ( var j = 0, jl = uv.length; j < jl; j ++ ) { | |
| uvCopy.push( uv[ j ].clone() ); | |
| } | |
| uvs1.push( uvCopy ); | |
| } | |
| }, | |
| mergeMesh: function ( mesh ) { | |
| if ( ! ( mesh && mesh.isMesh ) ) { | |
| console.error( 'THREE.Geometry.mergeMesh(): mesh not an instance of THREE.Mesh.', mesh ); | |
| return; | |
| } | |
| mesh.matrixAutoUpdate && mesh.updateMatrix(); | |
| this.merge( mesh.geometry, mesh.matrix ); | |
| }, | |
| /* | |
| * Checks for duplicate vertices with hashmap. | |
| * Duplicated vertices are removed | |
| * and faces' vertices are updated. | |
| */ | |
| mergeVertices: function () { | |
| var verticesMap = {}; // Hashmap for looking up vertices by position coordinates (and making sure they are unique) | |
| var unique = [], changes = []; | |
| var v, key; | |
| var precisionPoints = 4; // number of decimal points, e.g. 4 for epsilon of 0.0001 | |
| var precision = Math.pow( 10, precisionPoints ); | |
| var i, il, face; | |
| var indices, j, jl; | |
| for ( i = 0, il = this.vertices.length; i < il; i ++ ) { | |
| v = this.vertices[ i ]; | |
| key = Math.round( v.x * precision ) + '_' + Math.round( v.y * precision ) + '_' + Math.round( v.z * precision ); | |
| if ( verticesMap[ key ] === undefined ) { | |
| verticesMap[ key ] = i; | |
| unique.push( this.vertices[ i ] ); | |
| changes[ i ] = unique.length - 1; | |
| } else { | |
| //console.log('Duplicate vertex found. ', i, ' could be using ', verticesMap[key]); | |
| changes[ i ] = changes[ verticesMap[ key ] ]; | |
| } | |
| } | |
| // if faces are completely degenerate after merging vertices, we | |
| // have to remove them from the geometry. | |
| var faceIndicesToRemove = []; | |
| for ( i = 0, il = this.faces.length; i < il; i ++ ) { | |
| face = this.faces[ i ]; | |
| face.a = changes[ face.a ]; | |
| face.b = changes[ face.b ]; | |
| face.c = changes[ face.c ]; | |
| indices = [ face.a, face.b, face.c ]; | |
| // if any duplicate vertices are found in a Face3 | |
| // we have to remove the face as nothing can be saved | |
| for ( var n = 0; n < 3; n ++ ) { | |
| if ( indices[ n ] === indices[ ( n + 1 ) % 3 ] ) { | |
| faceIndicesToRemove.push( i ); | |
| break; | |
| } | |
| } | |
| } | |
| for ( i = faceIndicesToRemove.length - 1; i >= 0; i -- ) { | |
| var idx = faceIndicesToRemove[ i ]; | |
| this.faces.splice( idx, 1 ); | |
| for ( j = 0, jl = this.faceVertexUvs.length; j < jl; j ++ ) { | |
| this.faceVertexUvs[ j ].splice( idx, 1 ); | |
| } | |
| } | |
| // Use unique set of vertices | |
| var diff = this.vertices.length - unique.length; | |
| this.vertices = unique; | |
| return diff; | |
| }, | |
| setFromPoints: function ( points ) { | |
| this.vertices = []; | |
| for ( var i = 0, l = points.length; i < l; i ++ ) { | |
| var point = points[ i ]; | |
| this.vertices.push( new Vector3( point.x, point.y, point.z || 0 ) ); | |
| } | |
| return this; | |
| }, | |
| sortFacesByMaterialIndex: function () { | |
| var faces = this.faces; | |
| var length = faces.length; | |
| // tag faces | |
| for ( var i = 0; i < length; i ++ ) { | |
| faces[ i ]._id = i; | |
| } | |
| // sort faces | |
| function materialIndexSort( a, b ) { | |
| return a.materialIndex - b.materialIndex; | |
| } | |
| faces.sort( materialIndexSort ); | |
| // sort uvs | |
| var uvs1 = this.faceVertexUvs[ 0 ]; | |
| var uvs2 = this.faceVertexUvs[ 1 ]; | |
| var newUvs1, newUvs2; | |
| if ( uvs1 && uvs1.length === length ) newUvs1 = []; | |
| if ( uvs2 && uvs2.length === length ) newUvs2 = []; | |
| for ( var i = 0; i < length; i ++ ) { | |
| var id = faces[ i ]._id; | |
| if ( newUvs1 ) newUvs1.push( uvs1[ id ] ); | |
| if ( newUvs2 ) newUvs2.push( uvs2[ id ] ); | |
| } | |
| if ( newUvs1 ) this.faceVertexUvs[ 0 ] = newUvs1; | |
| if ( newUvs2 ) this.faceVertexUvs[ 1 ] = newUvs2; | |
| }, | |
| toJSON: function () { | |
| var data = { | |
| metadata: { | |
| version: 4.5, | |
| type: 'Geometry', | |
| generator: 'Geometry.toJSON' | |
| } | |
| }; | |
| // standard Geometry serialization | |
| data.uuid = this.uuid; | |
| data.type = this.type; | |
| if ( this.name !== '' ) data.name = this.name; | |
| if ( this.parameters !== undefined ) { | |
| var parameters = this.parameters; | |
| for ( var key in parameters ) { | |
| if ( parameters[ key ] !== undefined ) data[ key ] = parameters[ key ]; | |
| } | |
| return data; | |
| } | |
| var vertices = []; | |
| for ( var i = 0; i < this.vertices.length; i ++ ) { | |
| var vertex = this.vertices[ i ]; | |
| vertices.push( vertex.x, vertex.y, vertex.z ); | |
| } | |
| var faces = []; | |
| var normals = []; | |
| var normalsHash = {}; | |
| var colors = []; | |
| var colorsHash = {}; | |
| var uvs = []; | |
| var uvsHash = {}; | |
| for ( var i = 0; i < this.faces.length; i ++ ) { | |
| var face = this.faces[ i ]; | |
| var hasMaterial = true; | |
| var hasFaceUv = false; // deprecated | |
| var hasFaceVertexUv = this.faceVertexUvs[ 0 ][ i ] !== undefined; | |
| var hasFaceNormal = face.normal.length() > 0; | |
| var hasFaceVertexNormal = face.vertexNormals.length > 0; | |
| var hasFaceColor = face.color.r !== 1 || face.color.g !== 1 || face.color.b !== 1; | |
| var hasFaceVertexColor = face.vertexColors.length > 0; | |
| var faceType = 0; | |
| faceType = setBit( faceType, 0, 0 ); // isQuad | |
| faceType = setBit( faceType, 1, hasMaterial ); | |
| faceType = setBit( faceType, 2, hasFaceUv ); | |
| faceType = setBit( faceType, 3, hasFaceVertexUv ); | |
| faceType = setBit( faceType, 4, hasFaceNormal ); | |
| faceType = setBit( faceType, 5, hasFaceVertexNormal ); | |
| faceType = setBit( faceType, 6, hasFaceColor ); | |
| faceType = setBit( faceType, 7, hasFaceVertexColor ); | |
| faces.push( faceType ); | |
| faces.push( face.a, face.b, face.c ); | |
| faces.push( face.materialIndex ); | |
| if ( hasFaceVertexUv ) { | |
| var faceVertexUvs = this.faceVertexUvs[ 0 ][ i ]; | |
| faces.push( | |
| getUvIndex( faceVertexUvs[ 0 ] ), | |
| getUvIndex( faceVertexUvs[ 1 ] ), | |
| getUvIndex( faceVertexUvs[ 2 ] ) | |
| ); | |
| } | |
| if ( hasFaceNormal ) { | |
| faces.push( getNormalIndex( face.normal ) ); | |
| } | |
| if ( hasFaceVertexNormal ) { | |
| var vertexNormals = face.vertexNormals; | |
| faces.push( | |
| getNormalIndex( vertexNormals[ 0 ] ), | |
| getNormalIndex( vertexNormals[ 1 ] ), | |
| getNormalIndex( vertexNormals[ 2 ] ) | |
| ); | |
| } | |
| if ( hasFaceColor ) { | |
| faces.push( getColorIndex( face.color ) ); | |
| } | |
| if ( hasFaceVertexColor ) { | |
| var vertexColors = face.vertexColors; | |
| faces.push( | |
| getColorIndex( vertexColors[ 0 ] ), | |
| getColorIndex( vertexColors[ 1 ] ), | |
| getColorIndex( vertexColors[ 2 ] ) | |
| ); | |
| } | |
| } | |
| function setBit( value, position, enabled ) { | |
| return enabled ? value | ( 1 << position ) : value & ( ~ ( 1 << position ) ); | |
| } | |
| function getNormalIndex( normal ) { | |
| var hash = normal.x.toString() + normal.y.toString() + normal.z.toString(); | |
| if ( normalsHash[ hash ] !== undefined ) { | |
| return normalsHash[ hash ]; | |
| } | |
| normalsHash[ hash ] = normals.length / 3; | |
| normals.push( normal.x, normal.y, normal.z ); | |
| return normalsHash[ hash ]; | |
| } | |
| function getColorIndex( color ) { | |
| var hash = color.r.toString() + color.g.toString() + color.b.toString(); | |
| if ( colorsHash[ hash ] !== undefined ) { | |
| return colorsHash[ hash ]; | |
| } | |
| colorsHash[ hash ] = colors.length; | |
| colors.push( color.getHex() ); | |
| return colorsHash[ hash ]; | |
| } | |
| function getUvIndex( uv ) { | |
| var hash = uv.x.toString() + uv.y.toString(); | |
| if ( uvsHash[ hash ] !== undefined ) { | |
| return uvsHash[ hash ]; | |
| } | |
| uvsHash[ hash ] = uvs.length / 2; | |
| uvs.push( uv.x, uv.y ); | |
| return uvsHash[ hash ]; | |
| } | |
| data.data = {}; | |
| data.data.vertices = vertices; | |
| data.data.normals = normals; | |
| if ( colors.length > 0 ) data.data.colors = colors; | |
| if ( uvs.length > 0 ) data.data.uvs = [ uvs ]; // temporal backward compatibility | |
| data.data.faces = faces; | |
| return data; | |
| }, | |
| clone: function () { | |
| /* | |
| // Handle primitives | |
| var parameters = this.parameters; | |
| if ( parameters !== undefined ) { | |
| var values = []; | |
| for ( var key in parameters ) { | |
| values.push( parameters[ key ] ); | |
| } | |
| var geometry = Object.create( this.constructor.prototype ); | |
| this.constructor.apply( geometry, values ); | |
| return geometry; | |
| } | |
| return new this.constructor().copy( this ); | |
| */ | |
| return new Geometry().copy( this ); | |
| }, | |
| copy: function ( source ) { | |
| var i, il, j, jl, k, kl; | |
| // reset | |
| this.vertices = []; | |
| this.colors = []; | |
| this.faces = []; | |
| this.faceVertexUvs = [[]]; | |
| this.morphTargets = []; | |
| this.morphNormals = []; | |
| this.skinWeights = []; | |
| this.skinIndices = []; | |
| this.lineDistances = []; | |
| this.boundingBox = null; | |
| this.boundingSphere = null; | |
| // name | |
| this.name = source.name; | |
| // vertices | |
| var vertices = source.vertices; | |
| for ( i = 0, il = vertices.length; i < il; i ++ ) { | |
| this.vertices.push( vertices[ i ].clone() ); | |
| } | |
| // colors | |
| var colors = source.colors; | |
| for ( i = 0, il = colors.length; i < il; i ++ ) { | |
| this.colors.push( colors[ i ].clone() ); | |
| } | |
| // faces | |
| var faces = source.faces; | |
| for ( i = 0, il = faces.length; i < il; i ++ ) { | |
| this.faces.push( faces[ i ].clone() ); | |
| } | |
| // face vertex uvs | |
| for ( i = 0, il = source.faceVertexUvs.length; i < il; i ++ ) { | |
| var faceVertexUvs = source.faceVertexUvs[ i ]; | |
| if ( this.faceVertexUvs[ i ] === undefined ) { | |
| this.faceVertexUvs[ i ] = []; | |
| } | |
| for ( j = 0, jl = faceVertexUvs.length; j < jl; j ++ ) { | |
| var uvs = faceVertexUvs[ j ], uvsCopy = []; | |
| for ( k = 0, kl = uvs.length; k < kl; k ++ ) { | |
| var uv = uvs[ k ]; | |
| uvsCopy.push( uv.clone() ); | |
| } | |
| this.faceVertexUvs[ i ].push( uvsCopy ); | |
| } | |
| } | |
| // morph targets | |
| var morphTargets = source.morphTargets; | |
| for ( i = 0, il = morphTargets.length; i < il; i ++ ) { | |
| var morphTarget = {}; | |
| morphTarget.name = morphTargets[ i ].name; | |
| // vertices | |
| if ( morphTargets[ i ].vertices !== undefined ) { | |
| morphTarget.vertices = []; | |
| for ( j = 0, jl = morphTargets[ i ].vertices.length; j < jl; j ++ ) { | |
| morphTarget.vertices.push( morphTargets[ i ].vertices[ j ].clone() ); | |
| } | |
| } | |
| // normals | |
| if ( morphTargets[ i ].normals !== undefined ) { | |
| morphTarget.normals = []; | |
| for ( j = 0, jl = morphTargets[ i ].normals.length; j < jl; j ++ ) { | |
| morphTarget.normals.push( morphTargets[ i ].normals[ j ].clone() ); | |
| } | |
| } | |
| this.morphTargets.push( morphTarget ); | |
| } | |
| // morph normals | |
| var morphNormals = source.morphNormals; | |
| for ( i = 0, il = morphNormals.length; i < il; i ++ ) { | |
| var morphNormal = {}; | |
| // vertex normals | |
| if ( morphNormals[ i ].vertexNormals !== undefined ) { | |
| morphNormal.vertexNormals = []; | |
| for ( j = 0, jl = morphNormals[ i ].vertexNormals.length; j < jl; j ++ ) { | |
| var srcVertexNormal = morphNormals[ i ].vertexNormals[ j ]; | |
| var destVertexNormal = {}; | |
| destVertexNormal.a = srcVertexNormal.a.clone(); | |
| destVertexNormal.b = srcVertexNormal.b.clone(); | |
| destVertexNormal.c = srcVertexNormal.c.clone(); | |
| morphNormal.vertexNormals.push( destVertexNormal ); | |
| } | |
| } | |
| // face normals | |
| if ( morphNormals[ i ].faceNormals !== undefined ) { | |
| morphNormal.faceNormals = []; | |
| for ( j = 0, jl = morphNormals[ i ].faceNormals.length; j < jl; j ++ ) { | |
| morphNormal.faceNormals.push( morphNormals[ i ].faceNormals[ j ].clone() ); | |
| } | |
| } | |
| this.morphNormals.push( morphNormal ); | |
| } | |
| // skin weights | |
| var skinWeights = source.skinWeights; | |
| for ( i = 0, il = skinWeights.length; i < il; i ++ ) { | |
| this.skinWeights.push( skinWeights[ i ].clone() ); | |
| } | |
| // skin indices | |
| var skinIndices = source.skinIndices; | |
| for ( i = 0, il = skinIndices.length; i < il; i ++ ) { | |
| this.skinIndices.push( skinIndices[ i ].clone() ); | |
| } | |
| // line distances | |
| var lineDistances = source.lineDistances; | |
| for ( i = 0, il = lineDistances.length; i < il; i ++ ) { | |
| this.lineDistances.push( lineDistances[ i ] ); | |
| } | |
| // bounding box | |
| var boundingBox = source.boundingBox; | |
| if ( boundingBox !== null ) { | |
| this.boundingBox = boundingBox.clone(); | |
| } | |
| // bounding sphere | |
| var boundingSphere = source.boundingSphere; | |
| if ( boundingSphere !== null ) { | |
| this.boundingSphere = boundingSphere.clone(); | |
| } | |
| // update flags | |
| this.elementsNeedUpdate = source.elementsNeedUpdate; | |
| this.verticesNeedUpdate = source.verticesNeedUpdate; | |
| this.uvsNeedUpdate = source.uvsNeedUpdate; | |
| this.normalsNeedUpdate = source.normalsNeedUpdate; | |
| this.colorsNeedUpdate = source.colorsNeedUpdate; | |
| this.lineDistancesNeedUpdate = source.lineDistancesNeedUpdate; | |
| this.groupsNeedUpdate = source.groupsNeedUpdate; | |
| return this; | |
| }, | |
| dispose: function () { | |
| this.dispatchEvent( { type: 'dispose' } ); | |
| } | |
| } ); | |
| /** | |
| * @author mrdoob / http://mrdoob.com/ | |
| */ | |
| function BufferAttribute( array, itemSize, normalized ) { | |
| if ( Array.isArray( array ) ) { | |
| throw new TypeError( 'THREE.BufferAttribute: array should be a Typed Array.' ); | |
| } | |
| this.uuid = _Math.generateUUID(); | |
| this.name = ''; | |
| this.array = array; | |
| this.itemSize = itemSize; | |
| this.count = array !== undefined ? array.length / itemSize : 0; | |
| this.normalized = normalized === true; | |
| this.dynamic = false; | |
| this.updateRange = { offset: 0, count: - 1 }; | |
| this.onUploadCallback = function () {}; | |
| this.version = 0; | |
| } | |
| Object.defineProperty( BufferAttribute.prototype, 'needsUpdate', { | |
| set: function ( value ) { | |
| if ( value === true ) this.version ++; | |
| } | |
| } ); | |
| Object.assign( BufferAttribute.prototype, { | |
| isBufferAttribute: true, | |
| setArray: function ( array ) { | |
| if ( Array.isArray( array ) ) { | |
| throw new TypeError( 'THREE.BufferAttribute: array should be a Typed Array.' ); | |
| } | |
| this.count = array !== undefined ? array.length / this.itemSize : 0; | |
| this.array = array; | |
| }, | |
| setDynamic: function ( value ) { | |
| this.dynamic = value; | |
| return this; | |
| }, | |
| copy: function ( source ) { | |
| this.array = new source.array.constructor( source.array ); | |
| this.itemSize = source.itemSize; | |
| this.count = source.count; | |
| this.normalized = source.normalized; | |
| this.dynamic = source.dynamic; | |
| return this; | |
| }, | |
| copyAt: function ( index1, attribute, index2 ) { | |
| index1 *= this.itemSize; | |
| index2 *= attribute.itemSize; | |
| for ( var i = 0, l = this.itemSize; i < l; i ++ ) { | |
| this.array[ index1 + i ] = attribute.array[ index2 + i ]; | |
| } | |
| return this; | |
| }, | |
| copyArray: function ( array ) { | |
| this.array.set( array ); | |
| return this; | |
| }, | |
| copyColorsArray: function ( colors ) { | |
| var array = this.array, offset = 0; | |
| for ( var i = 0, l = colors.length; i < l; i ++ ) { | |
| var color = colors[ i ]; | |
| if ( color === undefined ) { | |
| console.warn( 'THREE.BufferAttribute.copyColorsArray(): color is undefined', i ); | |
| color = new Color(); | |
| } | |
| array[ offset ++ ] = color.r; | |
| array[ offset ++ ] = color.g; | |
| array[ offset ++ ] = color.b; | |
| } | |
| return this; | |
| }, | |
| copyVector2sArray: function ( vectors ) { | |
| var array = this.array, offset = 0; | |
| for ( var i = 0, l = vectors.length; i < l; i ++ ) { | |
| var vector = vectors[ i ]; | |
| if ( vector === undefined ) { | |
| console.warn( 'THREE.BufferAttribute.copyVector2sArray(): vector is undefined', i ); | |
| vector = new Vector2(); | |
| } | |
| array[ offset ++ ] = vector.x; | |
| array[ offset ++ ] = vector.y; | |
| } | |
| return this; | |
| }, | |
| copyVector3sArray: function ( vectors ) { | |
| var array = this.array, offset = 0; | |
| for ( var i = 0, l = vectors.length; i < l; i ++ ) { | |
| var vector = vectors[ i ]; | |
| if ( vector === undefined ) { | |
| console.warn( 'THREE.BufferAttribute.copyVector3sArray(): vector is undefined', i ); | |
| vector = new Vector3(); | |
| } | |
| array[ offset ++ ] = vector.x; | |
| array[ offset ++ ] = vector.y; | |
| array[ offset ++ ] = vector.z; | |
| } | |
| return this; | |
| }, | |
| copyVector4sArray: function ( vectors ) { | |
| var array = this.array, offset = 0; | |
| for ( var i = 0, l = vectors.length; i < l; i ++ ) { | |
| var vector = vectors[ i ]; | |
| if ( vector === undefined ) { | |
| console.warn( 'THREE.BufferAttribute.copyVector4sArray(): vector is undefined', i ); | |
| vector = new Vector4(); | |
| } | |
| array[ offset ++ ] = vector.x; | |
| array[ offset ++ ] = vector.y; | |
| array[ offset ++ ] = vector.z; | |
| array[ offset ++ ] = vector.w; | |
| } | |
| return this; | |
| }, | |
| set: function ( value, offset ) { | |
| if ( offset === undefined ) offset = 0; | |
| this.array.set( value, offset ); | |
| return this; | |
| }, | |
| getX: function ( index ) { | |
| return this.array[ index * this.itemSize ]; | |
| }, | |
| setX: function ( index, x ) { | |
| this.array[ index * this.itemSize ] = x; | |
| return this; | |
| }, | |
| getY: function ( index ) { | |
| return this.array[ index * this.itemSize + 1 ]; | |
| }, | |
| setY: function ( index, y ) { | |
| this.array[ index * this.itemSize + 1 ] = y; | |
| return this; | |
| }, | |
| getZ: function ( index ) { | |
| return this.array[ index * this.itemSize + 2 ]; | |
| }, | |
| setZ: function ( index, z ) { | |
| this.array[ index * this.itemSize + 2 ] = z; | |
| return this; | |
| }, | |
| getW: function ( index ) { | |
| return this.array[ index * this.itemSize + 3 ]; | |
| }, | |
| setW: function ( index, w ) { | |
| this.array[ index * this.itemSize + 3 ] = w; | |
| return this; | |
| }, | |
| setXY: function ( index, x, y ) { | |
| index *= this.itemSize; | |
| this.array[ index + 0 ] = x; | |
| this.array[ index + 1 ] = y; | |
| return this; | |
| }, | |
| setXYZ: function ( index, x, y, z ) { | |
| index *= this.itemSize; | |
| this.array[ index + 0 ] = x; | |
| this.array[ index + 1 ] = y; | |
| this.array[ index + 2 ] = z; | |
| return this; | |
| }, | |
| setXYZW: function ( index, x, y, z, w ) { | |
| index *= this.itemSize; | |
| this.array[ index + 0 ] = x; | |
| this.array[ index + 1 ] = y; | |
| this.array[ index + 2 ] = z; | |
| this.array[ index + 3 ] = w; | |
| return this; | |
| }, | |
| onUpload: function ( callback ) { | |
| this.onUploadCallback = callback; | |
| return this; | |
| }, | |
| clone: function () { | |
| return new this.constructor( this.array, this.itemSize ).copy( this ); | |
| } | |
| } ); | |
| // | |
| function Int8BufferAttribute( array, itemSize, normalized ) { | |
| BufferAttribute.call( this, new Int8Array( array ), itemSize, normalized ); | |
| } | |
| Int8BufferAttribute.prototype = Object.create( BufferAttribute.prototype ); | |
| Int8BufferAttribute.prototype.constructor = Int8BufferAttribute; | |
| function Uint8BufferAttribute( array, itemSize, normalized ) { | |
| BufferAttribute.call( this, new Uint8Array( array ), itemSize, normalized ); | |
| } | |
| Uint8BufferAttribute.prototype = Object.create( BufferAttribute.prototype ); | |
| Uint8BufferAttribute.prototype.constructor = Uint8BufferAttribute; | |
| function Uint8ClampedBufferAttribute( array, itemSize, normalized ) { | |
| BufferAttribute.call( this, new Uint8ClampedArray( array ), itemSize, normalized ); | |
| } | |
| Uint8ClampedBufferAttribute.prototype = Object.create( BufferAttribute.prototype ); | |
| Uint8ClampedBufferAttribute.prototype.constructor = Uint8ClampedBufferAttribute; | |
| function Int16BufferAttribute( array, itemSize, normalized ) { | |
| BufferAttribute.call( this, new Int16Array( array ), itemSize, normalized ); | |
| } | |
| Int16BufferAttribute.prototype = Object.create( BufferAttribute.prototype ); | |
| Int16BufferAttribute.prototype.constructor = Int16BufferAttribute; | |
| function Uint16BufferAttribute( array, itemSize, normalized ) { | |
| BufferAttribute.call( this, new Uint16Array( array ), itemSize, normalized ); | |
| } | |
| Uint16BufferAttribute.prototype = Object.create( BufferAttribute.prototype ); | |
| Uint16BufferAttribute.prototype.constructor = Uint16BufferAttribute; | |
| function Int32BufferAttribute( array, itemSize, normalized ) { | |
| BufferAttribute.call( this, new Int32Array( array ), itemSize, normalized ); | |
| } | |
| Int32BufferAttribute.prototype = Object.create( BufferAttribute.prototype ); | |
| Int32BufferAttribute.prototype.constructor = Int32BufferAttribute; | |
| function Uint32BufferAttribute( array, itemSize, normalized ) { | |
| BufferAttribute.call( this, new Uint32Array( array ), itemSize, normalized ); | |
| } | |
| Uint32BufferAttribute.prototype = Object.create( BufferAttribute.prototype ); | |
| Uint32BufferAttribute.prototype.constructor = Uint32BufferAttribute; | |
| function Float32BufferAttribute( array, itemSize, normalized ) { | |
| BufferAttribute.call( this, new Float32Array( array ), itemSize, normalized ); | |
| } | |
| Float32BufferAttribute.prototype = Object.create( BufferAttribute.prototype ); | |
| Float32BufferAttribute.prototype.constructor = Float32BufferAttribute; | |
| function Float64BufferAttribute( array, itemSize, normalized ) { | |
| BufferAttribute.call( this, new Float64Array( array ), itemSize, normalized ); | |
| } | |
| Float64BufferAttribute.prototype = Object.create( BufferAttribute.prototype ); | |
| Float64BufferAttribute.prototype.constructor = Float64BufferAttribute; | |
| /** | |
| * @author mrdoob / http://mrdoob.com/ | |
| */ | |
| function DirectGeometry() { | |
| this.vertices = []; | |
| this.normals = []; | |
| this.colors = []; | |
| this.uvs = []; | |
| this.uvs2 = []; | |
| this.groups = []; | |
| this.morphTargets = {}; | |
| this.skinWeights = []; | |
| this.skinIndices = []; | |
| // this.lineDistances = []; | |
| this.boundingBox = null; | |
| this.boundingSphere = null; | |
| // update flags | |
| this.verticesNeedUpdate = false; | |
| this.normalsNeedUpdate = false; | |
| this.colorsNeedUpdate = false; | |
| this.uvsNeedUpdate = false; | |
| this.groupsNeedUpdate = false; | |
| } | |
| Object.assign( DirectGeometry.prototype, { | |
| computeGroups: function ( geometry ) { | |
| var group; | |
| var groups = []; | |
| var materialIndex = undefined; | |
| var faces = geometry.faces; | |
| for ( var i = 0; i < faces.length; i ++ ) { | |
| var face = faces[ i ]; | |
| // materials | |
| if ( face.materialIndex !== materialIndex ) { | |
| materialIndex = face.materialIndex; | |
| if ( group !== undefined ) { | |
| group.count = ( i * 3 ) - group.start; | |
| groups.push( group ); | |
| } | |
| group = { | |
| start: i * 3, | |
| materialIndex: materialIndex | |
| }; | |
| } | |
| } | |
| if ( group !== undefined ) { | |
| group.count = ( i * 3 ) - group.start; | |
| groups.push( group ); | |
| } | |
| this.groups = groups; | |
| }, | |
| fromGeometry: function ( geometry ) { | |
| var faces = geometry.faces; | |
| var vertices = geometry.vertices; | |
| var faceVertexUvs = geometry.faceVertexUvs; | |
| var hasFaceVertexUv = faceVertexUvs[ 0 ] && faceVertexUvs[ 0 ].length > 0; | |
| var hasFaceVertexUv2 = faceVertexUvs[ 1 ] && faceVertexUvs[ 1 ].length > 0; | |
| // morphs | |
| var morphTargets = geometry.morphTargets; | |
| var morphTargetsLength = morphTargets.length; | |
| var morphTargetsPosition; | |
| if ( morphTargetsLength > 0 ) { | |
| morphTargetsPosition = []; | |
| for ( var i = 0; i < morphTargetsLength; i ++ ) { | |
| morphTargetsPosition[ i ] = []; | |
| } | |
| this.morphTargets.position = morphTargetsPosition; | |
| } | |
| var morphNormals = geometry.morphNormals; | |
| var morphNormalsLength = morphNormals.length; | |
| var morphTargetsNormal; | |
| if ( morphNormalsLength > 0 ) { | |
| morphTargetsNormal = []; | |
| for ( var i = 0; i < morphNormalsLength; i ++ ) { | |
| morphTargetsNormal[ i ] = []; | |
| } | |
| this.morphTargets.normal = morphTargetsNormal; | |
| } | |
| // skins | |
| var skinIndices = geometry.skinIndices; | |
| var skinWeights = geometry.skinWeights; | |
| var hasSkinIndices = skinIndices.length === vertices.length; | |
| var hasSkinWeights = skinWeights.length === vertices.length; | |
| // | |
| for ( var i = 0; i < faces.length; i ++ ) { | |
| var face = faces[ i ]; | |
| this.vertices.push( vertices[ face.a ], vertices[ face.b ], vertices[ face.c ] ); | |
| var vertexNormals = face.vertexNormals; | |
| if ( vertexNormals.length === 3 ) { | |
| this.normals.push( vertexNormals[ 0 ], vertexNormals[ 1 ], vertexNormals[ 2 ] ); | |
| } else { | |
| var normal = face.normal; | |
| this.normals.push( normal, normal, normal ); | |
| } | |
| var vertexColors = face.vertexColors; | |
| if ( vertexColors.length === 3 ) { | |
| this.colors.push( vertexColors[ 0 ], vertexColors[ 1 ], vertexColors[ 2 ] ); | |
| } else { | |
| var color = face.color; | |
| this.colors.push( color, color, color ); | |
| } | |
| if ( hasFaceVertexUv === true ) { | |
| var vertexUvs = faceVertexUvs[ 0 ][ i ]; | |
| if ( vertexUvs !== undefined ) { | |
| this.uvs.push( vertexUvs[ 0 ], vertexUvs[ 1 ], vertexUvs[ 2 ] ); | |
| } else { | |
| console.warn( 'THREE.DirectGeometry.fromGeometry(): Undefined vertexUv ', i ); | |
| this.uvs.push( new Vector2(), new Vector2(), new Vector2() ); | |
| } | |
| } | |
| if ( hasFaceVertexUv2 === true ) { | |
| var vertexUvs = faceVertexUvs[ 1 ][ i ]; | |
| if ( vertexUvs !== undefined ) { | |
| this.uvs2.push( vertexUvs[ 0 ], vertexUvs[ 1 ], vertexUvs[ 2 ] ); | |
| } else { | |
| console.warn( 'THREE.DirectGeometry.fromGeometry(): Undefined vertexUv2 ', i ); | |
| this.uvs2.push( new Vector2(), new Vector2(), new Vector2() ); | |
| } | |
| } | |
| // morphs | |
| for ( var j = 0; j < morphTargetsLength; j ++ ) { | |
| var morphTarget = morphTargets[ j ].vertices; | |
| morphTargetsPosition[ j ].push( morphTarget[ face.a ], morphTarget[ face.b ], morphTarget[ face.c ] ); | |
| } | |
| for ( var j = 0; j < morphNormalsLength; j ++ ) { | |
| var morphNormal = morphNormals[ j ].vertexNormals[ i ]; | |
| morphTargetsNormal[ j ].push( morphNormal.a, morphNormal.b, morphNormal.c ); | |
| } | |
| // skins | |
| if ( hasSkinIndices ) { | |
| this.skinIndices.push( skinIndices[ face.a ], skinIndices[ face.b ], skinIndices[ face.c ] ); | |
| } | |
| if ( hasSkinWeights ) { | |
| this.skinWeights.push( skinWeights[ face.a ], skinWeights[ face.b ], skinWeights[ face.c ] ); | |
| } | |
| } | |
| this.computeGroups( geometry ); | |
| this.verticesNeedUpdate = geometry.verticesNeedUpdate; | |
| this.normalsNeedUpdate = geometry.normalsNeedUpdate; | |
| this.colorsNeedUpdate = geometry.colorsNeedUpdate; | |
| this.uvsNeedUpdate = geometry.uvsNeedUpdate; | |
| this.groupsNeedUpdate = geometry.groupsNeedUpdate; | |
| return this; | |
| } | |
| } ); | |
| /** | |
| * @author mrdoob / http://mrdoob.com/ | |
| */ | |
| function arrayMax( array ) { | |
| if ( array.length === 0 ) return - Infinity; | |
| var max = array[ 0 ]; | |
| for ( var i = 1, l = array.length; i < l; ++ i ) { | |
| if ( array[ i ] > max ) max = array[ i ]; | |
| } | |
| return max; | |
| } | |
| /** | |
| * @author alteredq / http://alteredqualia.com/ | |
| * @author mrdoob / http://mrdoob.com/ | |
| */ | |
| var bufferGeometryId = 1; // BufferGeometry uses odd numbers as Id | |
| function BufferGeometry() { | |
| Object.defineProperty( this, 'id', { value: bufferGeometryId += 2 } ); | |
| this.uuid = _Math.generateUUID(); | |
| this.name = ''; | |
| this.type = 'BufferGeometry'; | |
| this.index = null; | |
| this.attributes = {}; | |
| this.morphAttributes = {}; | |
| this.groups = []; | |
| this.boundingBox = null; | |
| this.boundingSphere = null; | |
| this.drawRange = { start: 0, count: Infinity }; | |
| } | |
| BufferGeometry.prototype = Object.assign( Object.create( EventDispatcher.prototype ), { | |
| constructor: BufferGeometry, | |
| isBufferGeometry: true, | |
| getIndex: function () { | |
| return this.index; | |
| }, | |
| setIndex: function ( index ) { | |
| if ( Array.isArray( index ) ) { | |
| this.index = new ( arrayMax( index ) > 65535 ? Uint32BufferAttribute : Uint16BufferAttribute )( index, 1 ); | |
| } else { | |
| this.index = index; | |
| } | |
| }, | |
| addAttribute: function ( name, attribute ) { | |
| if ( ! ( attribute && attribute.isBufferAttribute ) && ! ( attribute && attribute.isInterleavedBufferAttribute ) ) { | |
| console.warn( 'THREE.BufferGeometry: .addAttribute() now expects ( name, attribute ).' ); | |
| this.addAttribute( name, new BufferAttribute( arguments[ 1 ], arguments[ 2 ] ) ); | |
| return; | |
| } | |
| if ( name === 'index' ) { | |
| console.warn( 'THREE.BufferGeometry.addAttribute: Use .setIndex() for index attribute.' ); | |
| this.setIndex( attribute ); | |
| return; | |
| } | |
| this.attributes[ name ] = attribute; | |
| return this; | |
| }, | |
| getAttribute: function ( name ) { | |
| return this.attributes[ name ]; | |
| }, | |
| removeAttribute: function ( name ) { | |
| delete this.attributes[ name ]; | |
| return this; | |
| }, | |
| addGroup: function ( start, count, materialIndex ) { | |
| this.groups.push( { | |
| start: start, | |
| count: count, | |
| materialIndex: materialIndex !== undefined ? materialIndex : 0 | |
| } ); | |
| }, | |
| clearGroups: function () { | |
| this.groups = []; | |
| }, | |
| setDrawRange: function ( start, count ) { | |
| this.drawRange.start = start; | |
| this.drawRange.count = count; | |
| }, | |
| applyMatrix: function ( matrix ) { | |
| var position = this.attributes.position; | |
| if ( position !== undefined ) { | |
| matrix.applyToBufferAttribute( position ); | |
| position.needsUpdate = true; | |
| } | |
| var normal = this.attributes.normal; | |
| if ( normal !== undefined ) { | |
| var normalMatrix = new Matrix3().getNormalMatrix( matrix ); | |
| normalMatrix.applyToBufferAttribute( normal ); | |
| normal.needsUpdate = true; | |
| } | |
| if ( this.boundingBox !== null ) { | |
| this.computeBoundingBox(); | |
| } | |
| if ( this.boundingSphere !== null ) { | |
| this.computeBoundingSphere(); | |
| } | |
| return this; | |
| }, | |
| rotateX: function () { | |
| // rotate geometry around world x-axis | |
| var m1 = new Matrix4(); | |
| return function rotateX( angle ) { | |
| m1.makeRotationX( angle ); | |
| this.applyMatrix( m1 ); | |
| return this; | |
| }; | |
| }(), | |
| rotateY: function () { | |
| // rotate geometry around world y-axis | |
| var m1 = new Matrix4(); | |
| return function rotateY( angle ) { | |
| m1.makeRotationY( angle ); | |
| this.applyMatrix( m1 ); | |
| return this; | |
| }; | |
| }(), | |
| rotateZ: function () { | |
| // rotate geometry around world z-axis | |
| var m1 = new Matrix4(); | |
| return function rotateZ( angle ) { | |
| m1.makeRotationZ( angle ); | |
| this.applyMatrix( m1 ); | |
| return this; | |
| }; | |
| }(), | |
| translate: function () { | |
| // translate geometry | |
| var m1 = new Matrix4(); | |
| return function translate( x, y, z ) { | |
| m1.makeTranslation( x, y, z ); | |
| this.applyMatrix( m1 ); | |
| return this; | |
| }; | |
| }(), | |
| scale: function () { | |
| // scale geometry | |
| var m1 = new Matrix4(); | |
| return function scale( x, y, z ) { | |
| m1.makeScale( x, y, z ); | |
| this.applyMatrix( m1 ); | |
| return this; | |
| }; | |
| }(), | |
| lookAt: function () { | |
| var obj = new Object3D(); | |
| return function lookAt( vector ) { | |
| obj.lookAt( vector ); | |
| obj.updateMatrix(); | |
| this.applyMatrix( obj.matrix ); | |
| }; | |
| }(), | |
| center: function () { | |
| this.computeBoundingBox(); | |
| var offset = this.boundingBox.getCenter().negate(); | |
| this.translate( offset.x, offset.y, offset.z ); | |
| return offset; | |
| }, | |
| setFromObject: function ( object ) { | |
| // console.log( 'THREE.BufferGeometry.setFromObject(). Converting', object, this ); | |
| var geometry = object.geometry; | |
| if ( object.isPoints || object.isLine ) { | |
| var positions = new Float32BufferAttribute( geometry.vertices.length * 3, 3 ); | |
| var colors = new Float32BufferAttribute( geometry.colors.length * 3, 3 ); | |
| this.addAttribute( 'position', positions.copyVector3sArray( geometry.vertices ) ); | |
| this.addAttribute( 'color', colors.copyColorsArray( geometry.colors ) ); | |
| if ( geometry.lineDistances && geometry.lineDistances.length === geometry.vertices.length ) { | |
| var lineDistances = new Float32BufferAttribute( geometry.lineDistances.length, 1 ); | |
| this.addAttribute( 'lineDistance', lineDistances.copyArray( geometry.lineDistances ) ); | |
| } | |
| if ( geometry.boundingSphere !== null ) { | |
| this.boundingSphere = geometry.boundingSphere.clone(); | |
| } | |
| if ( geometry.boundingBox !== null ) { | |
| this.boundingBox = geometry.boundingBox.clone(); | |
| } | |
| } else if ( object.isMesh ) { | |
| if ( geometry && geometry.isGeometry ) { | |
| this.fromGeometry( geometry ); | |
| } | |
| } | |
| return this; | |
| }, | |
| setFromPoints: function ( points ) { | |
| var position = []; | |
| for ( var i = 0, l = points.length; i < l; i ++ ) { | |
| var point = points[ i ]; | |
| position.push( point.x, point.y, point.z || 0 ); | |
| } | |
| this.addAttribute( 'position', new Float32BufferAttribute( position, 3 ) ); | |
| return this; | |
| }, | |
| updateFromObject: function ( object ) { | |
| var geometry = object.geometry; | |
| if ( object.isMesh ) { | |
| var direct = geometry.__directGeometry; | |
| if ( geometry.elementsNeedUpdate === true ) { | |
| direct = undefined; | |
| geometry.elementsNeedUpdate = false; | |
| } | |
| if ( direct === undefined ) { | |
| return this.fromGeometry( geometry ); | |
| } | |
| direct.verticesNeedUpdate = geometry.verticesNeedUpdate; | |
| direct.normalsNeedUpdate = geometry.normalsNeedUpdate; | |
| direct.colorsNeedUpdate = geometry.colorsNeedUpdate; | |
| direct.uvsNeedUpdate = geometry.uvsNeedUpdate; | |
| direct.groupsNeedUpdate = geometry.groupsNeedUpdate; | |
| geometry.verticesNeedUpdate = false; | |
| geometry.normalsNeedUpdate = false; | |
| geometry.colorsNeedUpdate = false; | |
| geometry.uvsNeedUpdate = false; | |
| geometry.groupsNeedUpdate = false; | |
| geometry = direct; | |
| } | |
| var attribute; | |
| if ( geometry.verticesNeedUpdate === true ) { | |
| attribute = this.attributes.position; | |
| if ( attribute !== undefined ) { | |
| attribute.copyVector3sArray( geometry.vertices ); | |
| attribute.needsUpdate = true; | |
| } | |
| geometry.verticesNeedUpdate = false; | |
| } | |
| if ( geometry.normalsNeedUpdate === true ) { | |
| attribute = this.attributes.normal; | |
| if ( attribute !== undefined ) { | |
| attribute.copyVector3sArray( geometry.normals ); | |
| attribute.needsUpdate = true; | |
| } | |
| geometry.normalsNeedUpdate = false; | |
| } | |
| if ( geometry.colorsNeedUpdate === true ) { | |
| attribute = this.attributes.color; | |
| if ( attribute !== undefined ) { | |
| attribute.copyColorsArray( geometry.colors ); | |
| attribute.needsUpdate = true; | |
| } | |
| geometry.colorsNeedUpdate = false; | |
| } | |
| if ( geometry.uvsNeedUpdate ) { | |
| attribute = this.attributes.uv; | |
| if ( attribute !== undefined ) { | |
| attribute.copyVector2sArray( geometry.uvs ); | |
| attribute.needsUpdate = true; | |
| } | |
| geometry.uvsNeedUpdate = false; | |
| } | |
| if ( geometry.lineDistancesNeedUpdate ) { | |
| attribute = this.attributes.lineDistance; | |
| if ( attribute !== undefined ) { | |
| attribute.copyArray( geometry.lineDistances ); | |
| attribute.needsUpdate = true; | |
| } | |
| geometry.lineDistancesNeedUpdate = false; | |
| } | |
| if ( geometry.groupsNeedUpdate ) { | |
| geometry.computeGroups( object.geometry ); | |
| this.groups = geometry.groups; | |
| geometry.groupsNeedUpdate = false; | |
| } | |
| return this; | |
| }, | |
| fromGeometry: function ( geometry ) { | |
| geometry.__directGeometry = new DirectGeometry().fromGeometry( geometry ); | |
| return this.fromDirectGeometry( geometry.__directGeometry ); | |
| }, | |
| fromDirectGeometry: function ( geometry ) { | |
| var positions = new Float32Array( geometry.vertices.length * 3 ); | |
| this.addAttribute( 'position', new BufferAttribute( positions, 3 ).copyVector3sArray( geometry.vertices ) ); | |
| if ( geometry.normals.length > 0 ) { | |
| var normals = new Float32Array( geometry.normals.length * 3 ); | |
| this.addAttribute( 'normal', new BufferAttribute( normals, 3 ).copyVector3sArray( geometry.normals ) ); | |
| } | |
| if ( geometry.colors.length > 0 ) { | |
| var colors = new Float32Array( geometry.colors.length * 3 ); | |
| this.addAttribute( 'color', new BufferAttribute( colors, 3 ).copyColorsArray( geometry.colors ) ); | |
| } | |
| if ( geometry.uvs.length > 0 ) { | |
| var uvs = new Float32Array( geometry.uvs.length * 2 ); | |
| this.addAttribute( 'uv', new BufferAttribute( uvs, 2 ).copyVector2sArray( geometry.uvs ) ); | |
| } | |
| if ( geometry.uvs2.length > 0 ) { | |
| var uvs2 = new Float32Array( geometry.uvs2.length * 2 ); | |
| this.addAttribute( 'uv2', new BufferAttribute( uvs2, 2 ).copyVector2sArray( geometry.uvs2 ) ); | |
| } | |
| // groups | |
| this.groups = geometry.groups; | |
| // morphs | |
| for ( var name in geometry.morphTargets ) { | |
| var array = []; | |
| var morphTargets = geometry.morphTargets[ name ]; | |
| for ( var i = 0, l = morphTargets.length; i < l; i ++ ) { | |
| var morphTarget = morphTargets[ i ]; | |
| var attribute = new Float32BufferAttribute( morphTarget.length * 3, 3 ); | |
| array.push( attribute.copyVector3sArray( morphTarget ) ); | |
| } | |
| this.morphAttributes[ name ] = array; | |
| } | |
| // skinning | |
| if ( geometry.skinIndices.length > 0 ) { | |
| var skinIndices = new Float32BufferAttribute( geometry.skinIndices.length * 4, 4 ); | |
| this.addAttribute( 'skinIndex', skinIndices.copyVector4sArray( geometry.skinIndices ) ); | |
| } | |
| if ( geometry.skinWeights.length > 0 ) { | |
| var skinWeights = new Float32BufferAttribute( geometry.skinWeights.length * 4, 4 ); | |
| this.addAttribute( 'skinWeight', skinWeights.copyVector4sArray( geometry.skinWeights ) ); | |
| } | |
| // | |
| if ( geometry.boundingSphere !== null ) { | |
| this.boundingSphere = geometry.boundingSphere.clone(); | |
| } | |
| if ( geometry.boundingBox !== null ) { | |
| this.boundingBox = geometry.boundingBox.clone(); | |
| } | |
| return this; | |
| }, | |
| computeBoundingBox: function () { | |
| if ( this.boundingBox === null ) { | |
| this.boundingBox = new Box3(); | |
| } | |
| var position = this.attributes.position; | |
| if ( position !== undefined ) { | |
| this.boundingBox.setFromBufferAttribute( position ); | |
| } else { | |
| this.boundingBox.makeEmpty(); | |
| } | |
| if ( isNaN( this.boundingBox.min.x ) || isNaN( this.boundingBox.min.y ) || isNaN( this.boundingBox.min.z ) ) { | |
| console.error( 'THREE.BufferGeometry.computeBoundingBox: Computed min/max have NaN values. The "position" attribute is likely to have NaN values.', this ); | |
| } | |
| }, | |
| computeBoundingSphere: function () { | |
| var box = new Box3(); | |
| var vector = new Vector3(); | |
| return function computeBoundingSphere() { | |
| if ( this.boundingSphere === null ) { | |
| this.boundingSphere = new Sphere(); | |
| } | |
| var position = this.attributes.position; | |
| if ( position ) { | |
| var center = this.boundingSphere.center; | |
| box.setFromBufferAttribute( position ); | |
| box.getCenter( center ); | |
| // hoping to find a boundingSphere with a radius smaller than the | |
| // boundingSphere of the boundingBox: sqrt(3) smaller in the best case | |
| var maxRadiusSq = 0; | |
| for ( var i = 0, il = position.count; i < il; i ++ ) { | |
| vector.x = position.getX( i ); | |
| vector.y = position.getY( i ); | |
| vector.z = position.getZ( i ); | |
| maxRadiusSq = Math.max( maxRadiusSq, center.distanceToSquared( vector ) ); | |
| } | |
| this.boundingSphere.radius = Math.sqrt( maxRadiusSq ); | |
| if ( isNaN( this.boundingSphere.radius ) ) { | |
| console.error( 'THREE.BufferGeometry.computeBoundingSphere(): Computed radius is NaN. The "position" attribute is likely to have NaN values.', this ); | |
| } | |
| } | |
| }; | |
| }(), | |
| computeFaceNormals: function () { | |
| // backwards compatibility | |
| }, | |
| computeVertexNormals: function () { | |
| var index = this.index; | |
| var attributes = this.attributes; | |
| var groups = this.groups; | |
| if ( attributes.position ) { | |
| var positions = attributes.position.array; | |
| if ( attributes.normal === undefined ) { | |
| this.addAttribute( 'normal', new BufferAttribute( new Float32Array( positions.length ), 3 ) ); | |
| } else { | |
| // reset existing normals to zero | |
| var array = attributes.normal.array; | |
| for ( var i = 0, il = array.length; i < il; i ++ ) { | |
| array[ i ] = 0; | |
| } | |
| } | |
| var normals = attributes.normal.array; | |
| var vA, vB, vC; | |
| var pA = new Vector3(), pB = new Vector3(), pC = new Vector3(); | |
| var cb = new Vector3(), ab = new Vector3(); | |
| // indexed elements | |
| if ( index ) { | |
| var indices = index.array; | |
| if ( groups.length === 0 ) { | |
| this.addGroup( 0, indices.length ); | |
| } | |
| for ( var j = 0, jl = groups.length; j < jl; ++ j ) { | |
| var group = groups[ j ]; | |
| var start = group.start; | |
| var count = group.count; | |
| for ( var i = start, il = start + count; i < il; i += 3 ) { | |
| vA = indices[ i + 0 ] * 3; | |
| vB = indices[ i + 1 ] * 3; | |
| vC = indices[ i + 2 ] * 3; | |
| pA.fromArray( positions, vA ); | |
| pB.fromArray( positions, vB ); | |
| pC.fromArray( positions, vC ); | |
| cb.subVectors( pC, pB ); | |
| ab.subVectors( pA, pB ); | |
| cb.cross( ab ); | |
| normals[ vA ] += cb.x; | |
| normals[ vA + 1 ] += cb.y; | |
| normals[ vA + 2 ] += cb.z; | |
| normals[ vB ] += cb.x; | |
| normals[ vB + 1 ] += cb.y; | |
| normals[ vB + 2 ] += cb.z; | |
| normals[ vC ] += cb.x; | |
| normals[ vC + 1 ] += cb.y; | |
| normals[ vC + 2 ] += cb.z; | |
| } | |
| } | |
| } else { | |
| // non-indexed elements (unconnected triangle soup) | |
| for ( var i = 0, il = positions.length; i < il; i += 9 ) { | |
| pA.fromArray( positions, i ); | |
| pB.fromArray( positions, i + 3 ); | |
| pC.fromArray( positions, i + 6 ); | |
| cb.subVectors( pC, pB ); | |
| ab.subVectors( pA, pB ); | |
| cb.cross( ab ); | |
| normals[ i ] = cb.x; | |
| normals[ i + 1 ] = cb.y; | |
| normals[ i + 2 ] = cb.z; | |
| normals[ i + 3 ] = cb.x; | |
| normals[ i + 4 ] = cb.y; | |
| normals[ i + 5 ] = cb.z; | |
| normals[ i + 6 ] = cb.x; | |
| normals[ i + 7 ] = cb.y; | |
| normals[ i + 8 ] = cb.z; | |
| } | |
| } | |
| this.normalizeNormals(); | |
| attributes.normal.needsUpdate = true; | |
| } | |
| }, | |
| merge: function ( geometry, offset ) { | |
| if ( ! ( geometry && geometry.isBufferGeometry ) ) { | |
| console.error( 'THREE.BufferGeometry.merge(): geometry not an instance of THREE.BufferGeometry.', geometry ); | |
| return; | |
| } | |
| if ( offset === undefined ) offset = 0; | |
| var attributes = this.attributes; | |
| for ( var key in attributes ) { | |
| if ( geometry.attributes[ key ] === undefined ) continue; | |
| var attribute1 = attributes[ key ]; | |
| var attributeArray1 = attribute1.array; | |
| var attribute2 = geometry.attributes[ key ]; | |
| var attributeArray2 = attribute2.array; | |
| var attributeSize = attribute2.itemSize; | |
| for ( var i = 0, j = attributeSize * offset; i < attributeArray2.length; i ++, j ++ ) { | |
| attributeArray1[ j ] = attributeArray2[ i ]; | |
| } | |
| } | |
| return this; | |
| }, | |
| normalizeNormals: function () { | |
| var vector = new Vector3(); | |
| return function normalizeNormals() { | |
| var normals = this.attributes.normal; | |
| for ( var i = 0, il = normals.count; i < il; i ++ ) { | |
| vector.x = normals.getX( i ); | |
| vector.y = normals.getY( i ); | |
| vector.z = normals.getZ( i ); | |
| vector.normalize(); | |
| normals.setXYZ( i, vector.x, vector.y, vector.z ); | |
| } | |
| }; | |
| }(), | |
| toNonIndexed: function () { | |
| if ( this.index === null ) { | |
| console.warn( 'THREE.BufferGeometry.toNonIndexed(): Geometry is already non-indexed.' ); | |
| return this; | |
| } | |
| var geometry2 = new BufferGeometry(); | |
| var indices = this.index.array; | |
| var attributes = this.attributes; | |
| for ( var name in attributes ) { | |
| var attribute = attributes[ name ]; | |
| var array = attribute.array; | |
| var itemSize = attribute.itemSize; | |
| var array2 = new array.constructor( indices.length * itemSize ); | |
| var index = 0, index2 = 0; | |
| for ( var i = 0, l = indices.length; i < l; i ++ ) { | |
| index = indices[ i ] * itemSize; | |
| for ( var j = 0; j < itemSize; j ++ ) { | |
| array2[ index2 ++ ] = array[ index ++ ]; | |
| } | |
| } | |
| geometry2.addAttribute( name, new BufferAttribute( array2, itemSize ) ); | |
| } | |
| return geometry2; | |
| }, | |
| toJSON: function () { | |
| var data = { | |
| metadata: { | |
| version: 4.5, | |
| type: 'BufferGeometry', | |
| generator: 'BufferGeometry.toJSON' | |
| } | |
| }; | |
| // standard BufferGeometry serialization | |
| data.uuid = this.uuid; | |
| data.type = this.type; | |
| if ( this.name !== '' ) data.name = this.name; | |
| if ( this.parameters !== undefined ) { | |
| var parameters = this.parameters; | |
| for ( var key in parameters ) { | |
| if ( parameters[ key ] !== undefined ) data[ key ] = parameters[ key ]; | |
| } | |
| return data; | |
| } | |
| data.data = { attributes: {} }; | |
| var index = this.index; | |
| if ( index !== null ) { | |
| var array = Array.prototype.slice.call( index.array ); | |
| data.data.index = { | |
| type: index.array.constructor.name, | |
| array: array | |
| }; | |
| } | |
| var attributes = this.attributes; | |
| for ( var key in attributes ) { | |
| var attribute = attributes[ key ]; | |
| var array = Array.prototype.slice.call( attribute.array ); | |
| data.data.attributes[ key ] = { | |
| itemSize: attribute.itemSize, | |
| type: attribute.array.constructor.name, | |
| array: array, | |
| normalized: attribute.normalized | |
| }; | |
| } | |
| var groups = this.groups; | |
| if ( groups.length > 0 ) { | |
| data.data.groups = JSON.parse( JSON.stringify( groups ) ); | |
| } | |
| var boundingSphere = this.boundingSphere; | |
| if ( boundingSphere !== null ) { | |
| data.data.boundingSphere = { | |
| center: boundingSphere.center.toArray(), | |
| radius: boundingSphere.radius | |
| }; | |
| } | |
| return data; | |
| }, | |
| clone: function () { | |
| /* | |
| // Handle primitives | |
| var parameters = this.parameters; | |
| if ( parameters !== undefined ) { | |
| var values = []; | |
| for ( var key in parameters ) { | |
| values.push( parameters[ key ] ); | |
| } | |
| var geometry = Object.create( this.constructor.prototype ); | |
| this.constructor.apply( geometry, values ); | |
| return geometry; | |
| } | |
| return new this.constructor().copy( this ); | |
| */ | |
| return new BufferGeometry().copy( this ); | |
| }, | |
| copy: function ( source ) { | |
| var name, i, l; | |
| // reset | |
| this.index = null; | |
| this.attributes = {}; | |
| this.morphAttributes = {}; | |
| this.groups = []; | |
| this.boundingBox = null; | |
| this.boundingSphere = null; | |
| // name | |
| this.name = source.name; | |
| // index | |
| var index = source.index; | |
| if ( index !== null ) { | |
| this.setIndex( index.clone() ); | |
| } | |
| // attributes | |
| var attributes = source.attributes; | |
| for ( name in attributes ) { | |
| var attribute = attributes[ name ]; | |
| this.addAttribute( name, attribute.clone() ); | |
| } | |
| // morph attributes | |
| var morphAttributes = source.morphAttributes; | |
| for ( name in morphAttributes ) { | |
| var array = []; | |
| var morphAttribute = morphAttributes[ name ]; // morphAttribute: array of Float32BufferAttributes | |
| for ( i = 0, l = morphAttribute.length; i < l; i ++ ) { | |
| array.push( morphAttribute[ i ].clone() ); | |
| } | |
| this.morphAttributes[ name ] = array; | |
| } | |
| // groups | |
| var groups = source.groups; | |
| for ( i = 0, l = groups.length; i < l; i ++ ) { | |
| var group = groups[ i ]; | |
| this.addGroup( group.start, group.count, group.materialIndex ); | |
| } | |
| // bounding box | |
| var boundingBox = source.boundingBox; | |
| if ( boundingBox !== null ) { | |
| this.boundingBox = boundingBox.clone(); | |
| } | |
| // bounding sphere | |
| var boundingSphere = source.boundingSphere; | |
| if ( boundingSphere !== null ) { | |
| this.boundingSphere = boundingSphere.clone(); | |
| } | |
| // draw range | |
| this.drawRange.start = source.drawRange.start; | |
| this.drawRange.count = source.drawRange.count; | |
| return this; | |
| }, | |
| dispose: function () { | |
| this.dispatchEvent( { type: 'dispose' } ); | |
| } | |
| } ); | |
| /** | |
| * @author mrdoob / http://mrdoob.com/ | |
| * @author Mugen87 / https://github.com/Mugen87 | |
| */ | |
| // BoxGeometry | |
| function BoxGeometry( width, height, depth, widthSegments, heightSegments, depthSegments ) { | |
| Geometry.call( this ); | |
| this.type = 'BoxGeometry'; | |
| this.parameters = { | |
| width: width, | |
| height: height, | |
| depth: depth, | |
| widthSegments: widthSegments, | |
| heightSegments: heightSegments, | |
| depthSegments: depthSegments | |
| }; | |
| this.fromBufferGeometry( new BoxBufferGeometry( width, height, depth, widthSegments, heightSegments, depthSegments ) ); | |
| this.mergeVertices(); | |
| } | |
| BoxGeometry.prototype = Object.create( Geometry.prototype ); | |
| BoxGeometry.prototype.constructor = BoxGeometry; | |
| // BoxBufferGeometry | |
| function BoxBufferGeometry( width, height, depth, widthSegments, heightSegments, depthSegments ) { | |
| BufferGeometry.call( this ); | |
| this.type = 'BoxBufferGeometry'; | |
| this.parameters = { | |
| width: width, | |
| height: height, | |
| depth: depth, | |
| widthSegments: widthSegments, | |
| heightSegments: heightSegments, | |
| depthSegments: depthSegments | |
| }; | |
| var scope = this; | |
| width = width || 1; | |
| height = height || 1; | |
| depth = depth || 1; | |
| // segments | |
| widthSegments = Math.floor( widthSegments ) || 1; | |
| heightSegments = Math.floor( heightSegments ) || 1; | |
| depthSegments = Math.floor( depthSegments ) || 1; | |
| // buffers | |
| var indices = []; | |
| var vertices = []; | |
| var normals = []; | |
| var uvs = []; | |
| // helper variables | |
| var numberOfVertices = 0; | |
| var groupStart = 0; | |
| // build each side of the box geometry | |
| buildPlane( 'z', 'y', 'x', - 1, - 1, depth, height, width, depthSegments, heightSegments, 0 ); // px | |
| buildPlane( 'z', 'y', 'x', 1, - 1, depth, height, - width, depthSegments, heightSegments, 1 ); // nx | |
| buildPlane( 'x', 'z', 'y', 1, 1, width, depth, height, widthSegments, depthSegments, 2 ); // py | |
| buildPlane( 'x', 'z', 'y', 1, - 1, width, depth, - height, widthSegments, depthSegments, 3 ); // ny | |
| buildPlane( 'x', 'y', 'z', 1, - 1, width, height, depth, widthSegments, heightSegments, 4 ); // pz | |
| buildPlane( 'x', 'y', 'z', - 1, - 1, width, height, - depth, widthSegments, heightSegments, 5 ); // nz | |
| // build geometry | |
| this.setIndex( indices ); | |
| this.addAttribute( 'position', new Float32BufferAttribute( vertices, 3 ) ); | |
| this.addAttribute( 'normal', new Float32BufferAttribute( normals, 3 ) ); | |
| this.addAttribute( 'uv', new Float32BufferAttribute( uvs, 2 ) ); | |
| function buildPlane( u, v, w, udir, vdir, width, height, depth, gridX, gridY, materialIndex ) { | |
| var segmentWidth = width / gridX; | |
| var segmentHeight = height / gridY; | |
| var widthHalf = width / 2; | |
| var heightHalf = height / 2; | |
| var depthHalf = depth / 2; | |
| var gridX1 = gridX + 1; | |
| var gridY1 = gridY + 1; | |
| var vertexCounter = 0; | |
| var groupCount = 0; | |
| var ix, iy; | |
| var vector = new Vector3(); | |
| // generate vertices, normals and uvs | |
| for ( iy = 0; iy < gridY1; iy ++ ) { | |
| var y = iy * segmentHeight - heightHalf; | |
| for ( ix = 0; ix < gridX1; ix ++ ) { | |
| var x = ix * segmentWidth - widthHalf; | |
| // set values to correct vector component | |
| vector[ u ] = x * udir; | |
| vector[ v ] = y * vdir; | |
| vector[ w ] = depthHalf; | |
| // now apply vector to vertex buffer | |
| vertices.push( vector.x, vector.y, vector.z ); | |
| // set values to correct vector component | |
| vector[ u ] = 0; | |
| vector[ v ] = 0; | |
| vector[ w ] = depth > 0 ? 1 : - 1; | |
| // now apply vector to normal buffer | |
| normals.push( vector.x, vector.y, vector.z ); | |
| // uvs | |
| uvs.push( ix / gridX ); | |
| uvs.push( 1 - ( iy / gridY ) ); | |
| // counters | |
| vertexCounter += 1; | |
| } | |
| } | |
| // indices | |
| // 1. you need three indices to draw a single face | |
| // 2. a single segment consists of two faces | |
| // 3. so we need to generate six (2*3) indices per segment | |
| for ( iy = 0; iy < gridY; iy ++ ) { | |
| for ( ix = 0; ix < gridX; ix ++ ) { | |
| var a = numberOfVertices + ix + gridX1 * iy; | |
| var b = numberOfVertices + ix + gridX1 * ( iy + 1 ); | |
| var c = numberOfVertices + ( ix + 1 ) + gridX1 * ( iy + 1 ); | |
| var d = numberOfVertices + ( ix + 1 ) + gridX1 * iy; | |
| // faces | |
| indices.push( a, b, d ); | |
| indices.push( b, c, d ); | |
| // increase counter | |
| groupCount += 6; | |
| } | |
| } | |
| // add a group to the geometry. this will ensure multi material support | |
| scope.addGroup( groupStart, groupCount, materialIndex ); | |
| // calculate new start value for groups | |
| groupStart += groupCount; | |
| // update total number of vertices | |
| numberOfVertices += vertexCounter; | |
| } | |
| } | |
| BoxBufferGeometry.prototype = Object.create( BufferGeometry.prototype ); | |
| BoxBufferGeometry.prototype.constructor = BoxBufferGeometry; | |
| /** | |
| * @author mrdoob / http://mrdoob.com/ | |
| * @author Mugen87 / https://github.com/Mugen87 | |
| */ | |
| // PlaneGeometry | |
| function PlaneGeometry( width, height, widthSegments, heightSegments ) { | |
| Geometry.call( this ); | |
| this.type = 'PlaneGeometry'; | |
| this.parameters = { | |
| width: width, | |
| height: height, | |
| widthSegments: widthSegments, | |
| heightSegments: heightSegments | |
| }; | |
| this.fromBufferGeometry( new PlaneBufferGeometry( width, height, widthSegments, heightSegments ) ); | |
| this.mergeVertices(); | |
| } | |
| PlaneGeometry.prototype = Object.create( Geometry.prototype ); | |
| PlaneGeometry.prototype.constructor = PlaneGeometry; | |
| // PlaneBufferGeometry | |
| function PlaneBufferGeometry( width, height, widthSegments, heightSegments ) { | |
| BufferGeometry.call( this ); | |
| this.type = 'PlaneBufferGeometry'; | |
| this.parameters = { | |
| width: width, | |
| height: height, | |
| widthSegments: widthSegments, | |
| heightSegments: heightSegments | |
| }; | |
| width = width || 1; | |
| height = height || 1; | |
| var width_half = width / 2; | |
| var height_half = height / 2; | |
| var gridX = Math.floor( widthSegments ) || 1; | |
| var gridY = Math.floor( heightSegments ) || 1; | |
| var gridX1 = gridX + 1; | |
| var gridY1 = gridY + 1; | |
| var segment_width = width / gridX; | |
| var segment_height = height / gridY; | |
| var ix, iy; | |
| // buffers | |
| var indices = []; | |
| var vertices = []; | |
| var normals = []; | |
| var uvs = []; | |
| // generate vertices, normals and uvs | |
| for ( iy = 0; iy < gridY1; iy ++ ) { | |
| var y = iy * segment_height - height_half; | |
| for ( ix = 0; ix < gridX1; ix ++ ) { | |
| var x = ix * segment_width - width_half; | |
| vertices.push( x, - y, 0 ); | |
| normals.push( 0, 0, 1 ); | |
| uvs.push( ix / gridX ); | |
| uvs.push( 1 - ( iy / gridY ) ); | |
| } | |
| } | |
| // indices | |
| for ( iy = 0; iy < gridY; iy ++ ) { | |
| for ( ix = 0; ix < gridX; ix ++ ) { | |
| var a = ix + gridX1 * iy; | |
| var b = ix + gridX1 * ( iy + 1 ); | |
| var c = ( ix + 1 ) + gridX1 * ( iy + 1 ); | |
| var d = ( ix + 1 ) + gridX1 * iy; | |
| // faces | |
| indices.push( a, b, d ); | |
| indices.push( b, c, d ); | |
| } | |
| } | |
| // build geometry | |
| this.setIndex( indices ); | |
| this.addAttribute( 'position', new Float32BufferAttribute( vertices, 3 ) ); | |
| this.addAttribute( 'normal', new Float32BufferAttribute( normals, 3 ) ); | |
| this.addAttribute( 'uv', new Float32BufferAttribute( uvs, 2 ) ); | |
| } | |
| PlaneBufferGeometry.prototype = Object.create( BufferGeometry.prototype ); | |
| PlaneBufferGeometry.prototype.constructor = PlaneBufferGeometry; | |
| /** | |
| * @author mrdoob / http://mrdoob.com/ | |
| * @author alteredq / http://alteredqualia.com/ | |
| */ | |
| var materialId = 0; | |
| function Material() { | |
| Object.defineProperty( this, 'id', { value: materialId ++ } ); | |
| this.uuid = _Math.generateUUID(); | |
| this.name = ''; | |
| this.type = 'Material'; | |
| this.fog = true; | |
| this.lights = true; | |
| this.blending = NormalBlending; | |
| this.side = FrontSide; | |
| this.flatShading = false; | |
| this.vertexColors = NoColors; // THREE.NoColors, THREE.VertexColors, THREE.FaceColors | |
| this.opacity = 1; | |
| this.transparent = false; | |
| this.blendSrc = SrcAlphaFactor; | |
| this.blendDst = OneMinusSrcAlphaFactor; | |
| this.blendEquation = AddEquation; | |
| this.blendSrcAlpha = null; | |
| this.blendDstAlpha = null; | |
| this.blendEquationAlpha = null; | |
| this.depthFunc = LessEqualDepth; | |
| this.depthTest = true; | |
| this.depthWrite = true; | |
| this.clippingPlanes = null; | |
| this.clipIntersection = false; | |
| this.clipShadows = false; | |
| this.shadowSide = null; | |
| this.colorWrite = true; | |
| this.precision = null; // override the renderer's default precision for this material | |
| this.polygonOffset = false; | |
| this.polygonOffsetFactor = 0; | |
| this.polygonOffsetUnits = 0; | |
| this.dithering = false; | |
| this.alphaTest = 0; | |
| this.premultipliedAlpha = false; | |
| this.overdraw = 0; // Overdrawn pixels (typically between 0 and 1) for fixing antialiasing gaps in CanvasRenderer | |
| this.visible = true; | |
| this.userData = {}; | |
| this.needsUpdate = true; | |
| } | |
| Material.prototype = Object.assign( Object.create( EventDispatcher.prototype ), { | |
| constructor: Material, | |
| isMaterial: true, | |
| onBeforeCompile: function () {}, | |
| setValues: function ( values ) { | |
| if ( values === undefined ) return; | |
| for ( var key in values ) { | |
| var newValue = values[ key ]; | |
| if ( newValue === undefined ) { | |
| console.warn( "THREE.Material: '" + key + "' parameter is undefined." ); | |
| continue; | |
| } | |
| // for backward compatability if shading is set in the constructor | |
| if ( key === 'shading' ) { | |
| console.warn( 'THREE.' + this.type + ': .shading has been removed. Use the boolean .flatShading instead.' ); | |
| this.flatShading = ( newValue === FlatShading ) ? true : false; | |
| continue; | |
| } | |
| var currentValue = this[ key ]; | |
| if ( currentValue === undefined ) { | |
| console.warn( "THREE." + this.type + ": '" + key + "' is not a property of this material." ); | |
| continue; | |
| } | |
| if ( currentValue && currentValue.isColor ) { | |
| currentValue.set( newValue ); | |
| } else if ( ( currentValue && currentValue.isVector3 ) && ( newValue && newValue.isVector3 ) ) { | |
| currentValue.copy( newValue ); | |
| } else if ( key === 'overdraw' ) { | |
| // ensure overdraw is backwards-compatible with legacy boolean type | |
| this[ key ] = Number( newValue ); | |
| } else { | |
| this[ key ] = newValue; | |
| } | |
| } | |
| }, | |
| toJSON: function ( meta ) { | |
| var isRoot = ( meta === undefined || typeof meta === 'string' ); | |
| if ( isRoot ) { | |
| meta = { | |
| textures: {}, | |
| images: {} | |
| }; | |
| } | |
| var data = { | |
| metadata: { | |
| version: 4.5, | |
| type: 'Material', | |
| generator: 'Material.toJSON' | |
| } | |
| }; | |
| // standard Material serialization | |
| data.uuid = this.uuid; | |
| data.type = this.type; | |
| if ( this.name !== '' ) data.name = this.name; | |
| if ( this.color && this.color.isColor ) data.color = this.color.getHex(); | |
| if ( this.roughness !== undefined ) data.roughness = this.roughness; | |
| if ( this.metalness !== undefined ) data.metalness = this.metalness; | |
| if ( this.emissive && this.emissive.isColor ) data.emissive = this.emissive.getHex(); | |
| if ( this.emissiveIntensity !== 1 ) data.emissiveIntensity = this.emissiveIntensity; | |
| if ( this.specular && this.specular.isColor ) data.specular = this.specular.getHex(); | |
| if ( this.shininess !== undefined ) data.shininess = this.shininess; | |
| if ( this.clearCoat !== undefined ) data.clearCoat = this.clearCoat; | |
| if ( this.clearCoatRoughness !== undefined ) data.clearCoatRoughness = this.clearCoatRoughness; | |
| if ( this.map && this.map.isTexture ) data.map = this.map.toJSON( meta ).uuid; | |
| if ( this.alphaMap && this.alphaMap.isTexture ) data.alphaMap = this.alphaMap.toJSON( meta ).uuid; | |
| if ( this.lightMap && this.lightMap.isTexture ) data.lightMap = this.lightMap.toJSON( meta ).uuid; | |
| if ( this.bumpMap && this.bumpMap.isTexture ) { | |
| data.bumpMap = this.bumpMap.toJSON( meta ).uuid; | |
| data.bumpScale = this.bumpScale; | |
| } | |
| if ( this.normalMap && this.normalMap.isTexture ) { | |
| data.normalMap = this.normalMap.toJSON( meta ).uuid; | |
| data.normalScale = this.normalScale.toArray(); | |
| } | |
| if ( this.displacementMap && this.displacementMap.isTexture ) { | |
| data.displacementMap = this.displacementMap.toJSON( meta ).uuid; | |
| data.displacementScale = this.displacementScale; | |
| data.displacementBias = this.displacementBias; | |
| } | |
| if ( this.roughnessMap && this.roughnessMap.isTexture ) data.roughnessMap = this.roughnessMap.toJSON( meta ).uuid; | |
| if ( this.metalnessMap && this.metalnessMap.isTexture ) data.metalnessMap = this.metalnessMap.toJSON( meta ).uuid; | |
| if ( this.emissiveMap && this.emissiveMap.isTexture ) data.emissiveMap = this.emissiveMap.toJSON( meta ).uuid; | |
| if ( this.specularMap && this.specularMap.isTexture ) data.specularMap = this.specularMap.toJSON( meta ).uuid; | |
| if ( this.envMap && this.envMap.isTexture ) { | |
| data.envMap = this.envMap.toJSON( meta ).uuid; | |
| data.reflectivity = this.reflectivity; // Scale behind envMap | |
| } | |
| if ( this.gradientMap && this.gradientMap.isTexture ) { | |
| data.gradientMap = this.gradientMap.toJSON( meta ).uuid; | |
| } | |
| if ( this.size !== undefined ) data.size = this.size; | |
| if ( this.sizeAttenuation !== undefined ) data.sizeAttenuation = this.sizeAttenuation; | |
| if ( this.blending !== NormalBlending ) data.blending = this.blending; | |
| if ( this.flatShading === true ) data.flatShading = this.flatShading; | |
| if ( this.side !== FrontSide ) data.side = this.side; | |
| if ( this.vertexColors !== NoColors ) data.vertexColors = this.vertexColors; | |
| if ( this.opacity < 1 ) data.opacity = this.opacity; | |
| if ( this.transparent === true ) data.transparent = this.transparent; | |
| data.depthFunc = this.depthFunc; | |
| data.depthTest = this.depthTest; | |
| data.depthWrite = this.depthWrite; | |
| // rotation (SpriteMaterial) | |
| if ( this.rotation !== 0 ) data.rotation = this.rotation; | |
| if ( this.linewidth !== 1 ) data.linewidth = this.linewidth; | |
| if ( this.dashSize !== undefined ) data.dashSize = this.dashSize; | |
| if ( this.gapSize !== undefined ) data.gapSize = this.gapSize; | |
| if ( this.scale !== undefined ) data.scale = this.scale; | |
| if ( this.dithering === true ) data.dithering = true; | |
| if ( this.alphaTest > 0 ) data.alphaTest = this.alphaTest; | |
| if ( this.premultipliedAlpha === true ) data.premultipliedAlpha = this.premultipliedAlpha; | |
| if ( this.wireframe === true ) data.wireframe = this.wireframe; | |
| if ( this.wireframeLinewidth > 1 ) data.wireframeLinewidth = this.wireframeLinewidth; | |
| if ( this.wireframeLinecap !== 'round' ) data.wireframeLinecap = this.wireframeLinecap; | |
| if ( this.wireframeLinejoin !== 'round' ) data.wireframeLinejoin = this.wireframeLinejoin; | |
| if ( this.morphTargets === true ) data.morphTargets = true; | |
| if ( this.skinning === true ) data.skinning = true; | |
| if ( this.visible === false ) data.visible = false; | |
| if ( JSON.stringify( this.userData ) !== '{}' ) data.userData = this.userData; | |
| // TODO: Copied from Object3D.toJSON | |
| function extractFromCache( cache ) { | |
| var values = []; | |
| for ( var key in cache ) { | |
| var data = cache[ key ]; | |
| delete data.metadata; | |
| values.push( data ); | |
| } | |
| return values; | |
| } | |
| if ( isRoot ) { | |
| var textures = extractFromCache( meta.textures ); | |
| var images = extractFromCache( meta.images ); | |
| if ( textures.length > 0 ) data.textures = textures; | |
| if ( images.length > 0 ) data.images = images; | |
| } | |
| return data; | |
| }, | |
| clone: function () { | |
| return new this.constructor().copy( this ); | |
| }, | |
| copy: function ( source ) { | |
| this.name = source.name; | |
| this.fog = source.fog; | |
| this.lights = source.lights; | |
| this.blending = source.blending; | |
| this.side = source.side; | |
| this.flatShading = source.flatShading; | |
| this.vertexColors = source.vertexColors; | |
| this.opacity = source.opacity; | |
| this.transparent = source.transparent; | |
| this.blendSrc = source.blendSrc; | |
| this.blendDst = source.blendDst; | |
| this.blendEquation = source.blendEquation; | |
| this.blendSrcAlpha = source.blendSrcAlpha; | |
| this.blendDstAlpha = source.blendDstAlpha; | |
| this.blendEquationAlpha = source.blendEquationAlpha; | |
| this.depthFunc = source.depthFunc; | |
| this.depthTest = source.depthTest; | |
| this.depthWrite = source.depthWrite; | |
| this.colorWrite = source.colorWrite; | |
| this.precision = source.precision; | |
| this.polygonOffset = source.polygonOffset; | |
| this.polygonOffsetFactor = source.polygonOffsetFactor; | |
| this.polygonOffsetUnits = source.polygonOffsetUnits; | |
| this.dithering = source.dithering; | |
| this.alphaTest = source.alphaTest; | |
| this.premultipliedAlpha = source.premultipliedAlpha; | |
| this.overdraw = source.overdraw; | |
| this.visible = source.visible; | |
| this.userData = JSON.parse( JSON.stringify( source.userData ) ); | |
| this.clipShadows = source.clipShadows; | |
| this.clipIntersection = source.clipIntersection; | |
| var srcPlanes = source.clippingPlanes, | |
| dstPlanes = null; | |
| if ( srcPlanes !== null ) { | |
| var n = srcPlanes.length; | |
| dstPlanes = new Array( n ); | |
| for ( var i = 0; i !== n; ++ i ) | |
| dstPlanes[ i ] = srcPlanes[ i ].clone(); | |
| } | |
| this.clippingPlanes = dstPlanes; | |
| this.shadowSide = source.shadowSide; | |
| return this; | |
| }, | |
| dispose: function () { | |
| this.dispatchEvent( { type: 'dispose' } ); | |
| } | |
| } ); | |
| /** | |
| * @author mrdoob / http://mrdoob.com/ | |
| * @author alteredq / http://alteredqualia.com/ | |
| * | |
| * parameters = { | |
| * color: <hex>, | |
| * opacity: <float>, | |
| * map: new THREE.Texture( <Image> ), | |
| * | |
| * lightMap: new THREE.Texture( <Image> ), | |
| * lightMapIntensity: <float> | |
| * | |
| * aoMap: new THREE.Texture( <Image> ), | |
| * aoMapIntensity: <float> | |
| * | |
| * specularMap: new THREE.Texture( <Image> ), | |
| * | |
| * alphaMap: new THREE.Texture( <Image> ), | |
| * | |
| * envMap: new THREE.CubeTexture( [posx, negx, posy, negy, posz, negz] ), | |
| * combine: THREE.Multiply, | |
| * reflectivity: <float>, | |
| * refractionRatio: <float>, | |
| * | |
| * depthTest: <bool>, | |
| * depthWrite: <bool>, | |
| * | |
| * wireframe: <boolean>, | |
| * wireframeLinewidth: <float>, | |
| * | |
| * skinning: <bool>, | |
| * morphTargets: <bool> | |
| * } | |
| */ | |
| function MeshBasicMaterial( parameters ) { | |
| Material.call( this ); | |
| this.type = 'MeshBasicMaterial'; | |
| this.color = new Color( 0xffffff ); // emissive | |
| this.map = null; | |
| this.lightMap = null; | |
| this.lightMapIntensity = 1.0; | |
| this.aoMap = null; | |
| this.aoMapIntensity = 1.0; | |
| this.specularMap = null; | |
| this.alphaMap = null; | |
| this.envMap = null; | |
| this.combine = MultiplyOperation; | |
| this.reflectivity = 1; | |
| this.refractionRatio = 0.98; | |
| this.wireframe = false; | |
| this.wireframeLinewidth = 1; | |
| this.wireframeLinecap = 'round'; | |
| this.wireframeLinejoin = 'round'; | |
| this.skinning = false; | |
| this.morphTargets = false; | |
| this.lights = false; | |
| this.setValues( parameters ); | |
| } | |
| MeshBasicMaterial.prototype = Object.create( Material.prototype ); | |
| MeshBasicMaterial.prototype.constructor = MeshBasicMaterial; | |
| MeshBasicMaterial.prototype.isMeshBasicMaterial = true; | |
| MeshBasicMaterial.prototype.copy = function ( source ) { | |
| Material.prototype.copy.call( this, source ); | |
| this.color.copy( source.color ); | |
| this.map = source.map; | |
| this.lightMap = source.lightMap; | |
| this.lightMapIntensity = source.lightMapIntensity; | |
| this.aoMap = source.aoMap; | |
| this.aoMapIntensity = source.aoMapIntensity; | |
| this.specularMap = source.specularMap; | |
| this.alphaMap = source.alphaMap; | |
| this.envMap = source.envMap; | |
| this.combine = source.combine; | |
| this.reflectivity = source.reflectivity; | |
| this.refractionRatio = source.refractionRatio; | |
| this.wireframe = source.wireframe; | |
| this.wireframeLinewidth = source.wireframeLinewidth; | |
| this.wireframeLinecap = source.wireframeLinecap; | |
| this.wireframeLinejoin = source.wireframeLinejoin; | |
| this.skinning = source.skinning; | |
| this.morphTargets = source.morphTargets; | |
| return this; | |
| }; | |
| /** | |
| * @author alteredq / http://alteredqualia.com/ | |
| * | |
| * parameters = { | |
| * defines: { "label" : "value" }, | |
| * uniforms: { "parameter1": { value: 1.0 }, "parameter2": { value2: 2 } }, | |
| * | |
| * fragmentShader: <string>, | |
| * vertexShader: <string>, | |
| * | |
| * wireframe: <boolean>, | |
| * wireframeLinewidth: <float>, | |
| * | |
| * lights: <bool>, | |
| * | |
| * skinning: <bool>, | |
| * morphTargets: <bool>, | |
| * morphNormals: <bool> | |
| * } | |
| */ | |
| function ShaderMaterial( parameters ) { | |
| Material.call( this ); | |
| this.type = 'ShaderMaterial'; | |
| this.defines = {}; | |
| this.uniforms = {}; | |
| this.vertexShader = 'void main() {\n\tgl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );\n}'; | |
| this.fragmentShader = 'void main() {\n\tgl_FragColor = vec4( 1.0, 0.0, 0.0, 1.0 );\n}'; | |
| this.linewidth = 1; | |
| this.wireframe = false; | |
| this.wireframeLinewidth = 1; | |
| this.fog = false; // set to use scene fog | |
| this.lights = false; // set to use scene lights | |
| this.clipping = false; // set to use user-defined clipping planes | |
| this.skinning = false; // set to use skinning attribute streams | |
| this.morphTargets = false; // set to use morph targets | |
| this.morphNormals = false; // set to use morph normals | |
| this.extensions = { | |
| derivatives: false, // set to use derivatives | |
| fragDepth: false, // set to use fragment depth values | |
| drawBuffers: false, // set to use draw buffers | |
| shaderTextureLOD: false // set to use shader texture LOD | |
| }; | |
| // When rendered geometry doesn't include these attributes but the material does, | |
| // use these default values in WebGL. This avoids errors when buffer data is missing. | |
| this.defaultAttributeValues = { | |
| 'color': [ 1, 1, 1 ], | |
| 'uv': [ 0, 0 ], | |
| 'uv2': [ 0, 0 ] | |
| }; | |
| this.index0AttributeName = undefined; | |
| this.uniformsNeedUpdate = false; | |
| if ( parameters !== undefined ) { | |
| if ( parameters.attributes !== undefined ) { | |
| console.error( 'THREE.ShaderMaterial: attributes should now be defined in THREE.BufferGeometry instead.' ); | |
| } | |
| this.setValues( parameters ); | |
| } | |
| } | |
| ShaderMaterial.prototype = Object.create( Material.prototype ); | |
| ShaderMaterial.prototype.constructor = ShaderMaterial; | |
| ShaderMaterial.prototype.isShaderMaterial = true; | |
| ShaderMaterial.prototype.copy = function ( source ) { | |
| Material.prototype.copy.call( this, source ); | |
| this.fragmentShader = source.fragmentShader; | |
| this.vertexShader = source.vertexShader; | |
| this.uniforms = UniformsUtils.clone( source.uniforms ); | |
| this.defines = source.defines; | |
| this.wireframe = source.wireframe; | |
| this.wireframeLinewidth = source.wireframeLinewidth; | |
| this.lights = source.lights; | |
| this.clipping = source.clipping; | |
| this.skinning = source.skinning; | |
| this.morphTargets = source.morphTargets; | |
| this.morphNormals = source.morphNormals; | |
| this.extensions = source.extensions; | |
| return this; | |
| }; | |
| ShaderMaterial.prototype.toJSON = function ( meta ) { | |
| var data = Material.prototype.toJSON.call( this, meta ); | |
| data.uniforms = this.uniforms; | |
| data.vertexShader = this.vertexShader; | |
| data.fragmentShader = this.fragmentShader; | |
| return data; | |
| }; | |
| /** | |
| * @author bhouston / http://clara.io | |
| */ | |
| function Ray( origin, direction ) { | |
| this.origin = ( origin !== undefined ) ? origin : new Vector3(); | |
| this.direction = ( direction !== undefined ) ? direction : new Vector3(); | |
| } | |
| Object.assign( Ray.prototype, { | |
| set: function ( origin, direction ) { | |
| this.origin.copy( origin ); | |
| this.direction.copy( direction ); | |
| return this; | |
| }, | |
| clone: function () { | |
| return new this.constructor().copy( this ); | |
| }, | |
| copy: function ( ray ) { | |
| this.origin.copy( ray.origin ); | |
| this.direction.copy( ray.direction ); | |
| return this; | |
| }, | |
| at: function ( t, optionalTarget ) { | |
| var result = optionalTarget || new Vector3(); | |
| return result.copy( this.direction ).multiplyScalar( t ).add( this.origin ); | |
| }, | |
| lookAt: function ( v ) { | |
| this.direction.copy( v ).sub( this.origin ).normalize(); | |
| return this; | |
| }, | |
| recast: function () { | |
| var v1 = new Vector3(); | |
| return function recast( t ) { | |
| this.origin.copy( this.at( t, v1 ) ); | |
| return this; | |
| }; | |
| }(), | |
| closestPointToPoint: function ( point, optionalTarget ) { | |
| var result = optionalTarget || new Vector3(); | |
| result.subVectors( point, this.origin ); | |
| var directionDistance = result.dot( this.direction ); | |
| if ( directionDistance < 0 ) { | |
| return result.copy( this.origin ); | |
| } | |
| return result.copy( this.direction ).multiplyScalar( directionDistance ).add( this.origin ); | |
| }, | |
| distanceToPoint: function ( point ) { | |
| return Math.sqrt( this.distanceSqToPoint( point ) ); | |
| }, | |
| distanceSqToPoint: function () { | |
| var v1 = new Vector3(); | |
| return function distanceSqToPoint( point ) { | |
| var directionDistance = v1.subVectors( point, this.origin ).dot( this.direction ); | |
| // point behind the ray | |
| if ( directionDistance < 0 ) { | |
| return this.origin.distanceToSquared( point ); | |
| } | |
| v1.copy( this.direction ).multiplyScalar( directionDistance ).add( this.origin ); | |
| return v1.distanceToSquared( point ); | |
| }; | |
| }(), | |
| distanceSqToSegment: function () { | |
| var segCenter = new Vector3(); | |
| var segDir = new Vector3(); | |
| var diff = new Vector3(); | |
| return function distanceSqToSegment( v0, v1, optionalPointOnRay, optionalPointOnSegment ) { | |
| // from http://www.geometrictools.com/GTEngine/Include/Mathematics/GteDistRaySegment.h | |
| // It returns the min distance between the ray and the segment | |
| // defined by v0 and v1 | |
| // It can also set two optional targets : | |
| // - The closest point on the ray | |
| // - The closest point on the segment | |
| segCenter.copy( v0 ).add( v1 ).multiplyScalar( 0.5 ); | |
| segDir.copy( v1 ).sub( v0 ).normalize(); | |
| diff.copy( this.origin ).sub( segCenter ); | |
| var segExtent = v0.distanceTo( v1 ) * 0.5; | |
| var a01 = - this.direction.dot( segDir ); | |
| var b0 = diff.dot( this.direction ); | |
| var b1 = - diff.dot( segDir ); | |
| var c = diff.lengthSq(); | |
| var det = Math.abs( 1 - a01 * a01 ); | |
| var s0, s1, sqrDist, extDet; | |
| if ( det > 0 ) { | |
| // The ray and segment are not parallel. | |
| s0 = a01 * b1 - b0; | |
| s1 = a01 * b0 - b1; | |
| extDet = segExtent * det; | |
| if ( s0 >= 0 ) { | |
| if ( s1 >= - extDet ) { | |
| if ( s1 <= extDet ) { | |
| // region 0 | |
| // Minimum at interior points of ray and segment. | |
| var invDet = 1 / det; | |
| s0 *= invDet; | |
| s1 *= invDet; | |
| sqrDist = s0 * ( s0 + a01 * s1 + 2 * b0 ) + s1 * ( a01 * s0 + s1 + 2 * b1 ) + c; | |
| } else { | |
| // region 1 | |
| s1 = segExtent; | |
| s0 = Math.max( 0, - ( a01 * s1 + b0 ) ); | |
| sqrDist = - s0 * s0 + s1 * ( s1 + 2 * b1 ) + c; | |
| } | |
| } else { | |
| // region 5 | |
| s1 = - segExtent; | |
| s0 = Math.max( 0, - ( a01 * s1 + b0 ) ); | |
| sqrDist = - s0 * s0 + s1 * ( s1 + 2 * b1 ) + c; | |
| } | |
| } else { | |
| if ( s1 <= - extDet ) { | |
| // region 4 | |
| s0 = Math.max( 0, - ( - a01 * segExtent + b0 ) ); | |
| s1 = ( s0 > 0 ) ? - segExtent : Math.min( Math.max( - segExtent, - b1 ), segExtent ); | |
| sqrDist = - s0 * s0 + s1 * ( s1 + 2 * b1 ) + c; | |
| } else if ( s1 <= extDet ) { | |
| // region 3 | |
| s0 = 0; | |
| s1 = Math.min( Math.max( - segExtent, - b1 ), segExtent ); | |
| sqrDist = s1 * ( s1 + 2 * b1 ) + c; | |
| } else { | |
| // region 2 | |
| s0 = Math.max( 0, - ( a01 * segExtent + b0 ) ); | |
| s1 = ( s0 > 0 ) ? segExtent : Math.min( Math.max( - segExtent, - b1 ), segExtent ); | |
| sqrDist = - s0 * s0 + s1 * ( s1 + 2 * b1 ) + c; | |
| } | |
| } | |
| } else { | |
| // Ray and segment are parallel. | |
| s1 = ( a01 > 0 ) ? - segExtent : segExtent; | |
| s0 = Math.max( 0, - ( a01 * s1 + b0 ) ); | |
| sqrDist = - s0 * s0 + s1 * ( s1 + 2 * b1 ) + c; | |
| } | |
| if ( optionalPointOnRay ) { | |
| optionalPointOnRay.copy( this.direction ).multiplyScalar( s0 ).add( this.origin ); | |
| } | |
| if ( optionalPointOnSegment ) { | |
| optionalPointOnSegment.copy( segDir ).multiplyScalar( s1 ).add( segCenter ); | |
| } | |
| return sqrDist; | |
| }; | |
| }(), | |
| intersectSphere: function () { | |
| var v1 = new Vector3(); | |
| return function intersectSphere( sphere, optionalTarget ) { | |
| v1.subVectors( sphere.center, this.origin ); | |
| var tca = v1.dot( this.direction ); | |
| var d2 = v1.dot( v1 ) - tca * tca; | |
| var radius2 = sphere.radius * sphere.radius; | |
| if ( d2 > radius2 ) return null; | |
| var thc = Math.sqrt( radius2 - d2 ); | |
| // t0 = first intersect point - entrance on front of sphere | |
| var t0 = tca - thc; | |
| // t1 = second intersect point - exit point on back of sphere | |
| var t1 = tca + thc; | |
| // test to see if both t0 and t1 are behind the ray - if so, return null | |
| if ( t0 < 0 && t1 < 0 ) return null; | |
| // test to see if t0 is behind the ray: | |
| // if it is, the ray is inside the sphere, so return the second exit point scaled by t1, | |
| // in order to always return an intersect point that is in front of the ray. | |
| if ( t0 < 0 ) return this.at( t1, optionalTarget ); | |
| // else t0 is in front of the ray, so return the first collision point scaled by t0 | |
| return this.at( t0, optionalTarget ); | |
| }; | |
| }(), | |
| intersectsSphere: function ( sphere ) { | |
| return this.distanceToPoint( sphere.center ) <= sphere.radius; | |
| }, | |
| distanceToPlane: function ( plane ) { | |
| var denominator = plane.normal.dot( this.direction ); | |
| if ( denominator === 0 ) { | |
| // line is coplanar, return origin | |
| if ( plane.distanceToPoint( this.origin ) === 0 ) { | |
| return 0; | |
| } | |
| // Null is preferable to undefined since undefined means.... it is undefined | |
| return null; | |
| } | |
| var t = - ( this.origin.dot( plane.normal ) + plane.constant ) / denominator; | |
| // Return if the ray never intersects the plane | |
| return t >= 0 ? t : null; | |
| }, | |
| intersectPlane: function ( plane, optionalTarget ) { | |
| var t = this.distanceToPlane( plane ); | |
| if ( t === null ) { | |
| return null; | |
| } | |
| return this.at( t, optionalTarget ); | |
| }, | |
| intersectsPlane: function ( plane ) { | |
| // check if the ray lies on the plane first | |
| var distToPoint = plane.distanceToPoint( this.origin ); | |
| if ( distToPoint === 0 ) { | |
| return true; | |
| } | |
| var denominator = plane.normal.dot( this.direction ); | |
| if ( denominator * distToPoint < 0 ) { | |
| return true; | |
| } | |
| // ray origin is behind the plane (and is pointing behind it) | |
| return false; | |
| }, | |
| intersectBox: function ( box, optionalTarget ) { | |
| var tmin, tmax, tymin, tymax, tzmin, tzmax; | |
| var invdirx = 1 / this.direction.x, | |
| invdiry = 1 / this.direction.y, | |
| invdirz = 1 / this.direction.z; | |
| var origin = this.origin; | |
| if ( invdirx >= 0 ) { | |
| tmin = ( box.min.x - origin.x ) * invdirx; | |
| tmax = ( box.max.x - origin.x ) * invdirx; | |
| } else { | |
| tmin = ( box.max.x - origin.x ) * invdirx; | |
| tmax = ( box.min.x - origin.x ) * invdirx; | |
| } | |
| if ( invdiry >= 0 ) { | |
| tymin = ( box.min.y - origin.y ) * invdiry; | |
| tymax = ( box.max.y - origin.y ) * invdiry; | |
| } else { | |
| tymin = ( box.max.y - origin.y ) * invdiry; | |
| tymax = ( box.min.y - origin.y ) * invdiry; | |
| } | |
| if ( ( tmin > tymax ) || ( tymin > tmax ) ) return null; | |
| // These lines also handle the case where tmin or tmax is NaN | |
| // (result of 0 * Infinity). x !== x returns true if x is NaN | |
| if ( tymin > tmin || tmin !== tmin ) tmin = tymin; | |
| if ( tymax < tmax || tmax !== tmax ) tmax = tymax; | |
| if ( invdirz >= 0 ) { | |
| tzmin = ( box.min.z - origin.z ) * invdirz; | |
| tzmax = ( box.max.z - origin.z ) * invdirz; | |
| } else { | |
| tzmin = ( box.max.z - origin.z ) * invdirz; | |
| tzmax = ( box.min.z - origin.z ) * invdirz; | |
| } | |
| if ( ( tmin > tzmax ) || ( tzmin > tmax ) ) return null; | |
| if ( tzmin > tmin || tmin !== tmin ) tmin = tzmin; | |
| if ( tzmax < tmax || tmax !== tmax ) tmax = tzmax; | |
| //return point closest to the ray (positive side) | |
| if ( tmax < 0 ) return null; | |
| return this.at( tmin >= 0 ? tmin : tmax, optionalTarget ); | |
| }, | |
| intersectsBox: ( function () { | |
| var v = new Vector3(); | |
| return function intersectsBox( box ) { | |
| return this.intersectBox( box, v ) !== null; | |
| }; | |
| } )(), | |
| intersectTriangle: function () { | |
| // Compute the offset origin, edges, and normal. | |
| var diff = new Vector3(); | |
| var edge1 = new Vector3(); | |
| var edge2 = new Vector3(); | |
| var normal = new Vector3(); | |
| return function intersectTriangle( a, b, c, backfaceCulling, optionalTarget ) { | |
| // from http://www.geometrictools.com/GTEngine/Include/Mathematics/GteIntrRay3Triangle3.h | |
| edge1.subVectors( b, a ); | |
| edge2.subVectors( c, a ); | |
| normal.crossVectors( edge1, edge2 ); | |
| // Solve Q + t*D = b1*E1 + b2*E2 (Q = kDiff, D = ray direction, | |
| // E1 = kEdge1, E2 = kEdge2, N = Cross(E1,E2)) by | |
| // |Dot(D,N)|*b1 = sign(Dot(D,N))*Dot(D,Cross(Q,E2)) | |
| // |Dot(D,N)|*b2 = sign(Dot(D,N))*Dot(D,Cross(E1,Q)) | |
| // |Dot(D,N)|*t = -sign(Dot(D,N))*Dot(Q,N) | |
| var DdN = this.direction.dot( normal ); | |
| var sign; | |
| if ( DdN > 0 ) { | |
| if ( backfaceCulling ) return null; | |
| sign = 1; | |
| } else if ( DdN < 0 ) { | |
| sign = - 1; | |
| DdN = - DdN; | |
| } else { | |
| return null; | |
| } | |
| diff.subVectors( this.origin, a ); | |
| var DdQxE2 = sign * this.direction.dot( edge2.crossVectors( diff, edge2 ) ); | |
| // b1 < 0, no intersection | |
| if ( DdQxE2 < 0 ) { | |
| return null; | |
| } | |
| var DdE1xQ = sign * this.direction.dot( edge1.cross( diff ) ); | |
| // b2 < 0, no intersection | |
| if ( DdE1xQ < 0 ) { | |
| return null; | |
| } | |
| // b1+b2 > 1, no intersection | |
| if ( DdQxE2 + DdE1xQ > DdN ) { | |
| return null; | |
| } | |
| // Line intersects triangle, check if ray does. | |
| var QdN = - sign * diff.dot( normal ); | |
| // t < 0, no intersection | |
| if ( QdN < 0 ) { | |
| return null; | |
| } | |
| // Ray intersects triangle. | |
| return this.at( QdN / DdN, optionalTarget ); | |
| }; | |
| }(), | |
| applyMatrix4: function ( matrix4 ) { | |
| this.origin.applyMatrix4( matrix4 ); | |
| this.direction.transformDirection( matrix4 ); | |
| return this; | |
| }, | |
| equals: function ( ray ) { | |
| return ray.origin.equals( this.origin ) && ray.direction.equals( this.direction ); | |
| } | |
| } ); | |
| /** | |
| * @author bhouston / http://clara.io | |
| */ | |
| function Line3( start, end ) { | |
| this.start = ( start !== undefined ) ? start : new Vector3(); | |
| this.end = ( end !== undefined ) ? end : new Vector3(); | |
| } | |
| Object.assign( Line3.prototype, { | |
| set: function ( start, end ) { | |
| this.start.copy( start ); | |
| this.end.copy( end ); | |
| return this; | |
| }, | |
| clone: function () { | |
| return new this.constructor().copy( this ); | |
| }, | |
| copy: function ( line ) { | |
| this.start.copy( line.start ); | |
| this.end.copy( line.end ); | |
| return this; | |
| }, | |
| getCenter: function ( optionalTarget ) { | |
| var result = optionalTarget || new Vector3(); | |
| return result.addVectors( this.start, this.end ).multiplyScalar( 0.5 ); | |
| }, | |
| delta: function ( optionalTarget ) { | |
| var result = optionalTarget || new Vector3(); | |
| return result.subVectors( this.end, this.start ); | |
| }, | |
| distanceSq: function () { | |
| return this.start.distanceToSquared( this.end ); | |
| }, | |
| distance: function () { | |
| return this.start.distanceTo( this.end ); | |
| }, | |
| at: function ( t, optionalTarget ) { | |
| var result = optionalTarget || new Vector3(); | |
| return this.delta( result ).multiplyScalar( t ).add( this.start ); | |
| }, | |
| closestPointToPointParameter: function () { | |
| var startP = new Vector3(); | |
| var startEnd = new Vector3(); | |
| return function closestPointToPointParameter( point, clampToLine ) { | |
| startP.subVectors( point, this.start ); | |
| startEnd.subVectors( this.end, this.start ); | |
| var startEnd2 = startEnd.dot( startEnd ); | |
| var startEnd_startP = startEnd.dot( startP ); | |
| var t = startEnd_startP / startEnd2; | |
| if ( clampToLine ) { | |
| t = _Math.clamp( t, 0, 1 ); | |
| } | |
| return t; | |
| }; | |
| }(), | |
| closestPointToPoint: function ( point, clampToLine, optionalTarget ) { | |
| var t = this.closestPointToPointParameter( point, clampToLine ); | |
| var result = optionalTarget || new Vector3(); | |
| return this.delta( result ).multiplyScalar( t ).add( this.start ); | |
| }, | |
| applyMatrix4: function ( matrix ) { | |
| this.start.applyMatrix4( matrix ); | |
| this.end.applyMatrix4( matrix ); | |
| return this; | |
| }, | |
| equals: function ( line ) { | |
| return line.start.equals( this.start ) && line.end.equals( this.end ); | |
| } | |
| } ); | |
| /** | |
| * @author bhouston / http://clara.io | |
| * @author mrdoob / http://mrdoob.com/ | |
| */ | |
| function Triangle( a, b, c ) { | |
| this.a = ( a !== undefined ) ? a : new Vector3(); | |
| this.b = ( b !== undefined ) ? b : new Vector3(); | |
| this.c = ( c !== undefined ) ? c : new Vector3(); | |
| } | |
| Object.assign( Triangle, { | |
| normal: function () { | |
| var v0 = new Vector3(); | |
| return function normal( a, b, c, optionalTarget ) { | |
| var result = optionalTarget || new Vector3(); | |
| result.subVectors( c, b ); | |
| v0.subVectors( a, b ); | |
| result.cross( v0 ); | |
| var resultLengthSq = result.lengthSq(); | |
| if ( resultLengthSq > 0 ) { | |
| return result.multiplyScalar( 1 / Math.sqrt( resultLengthSq ) ); | |
| } | |
| return result.set( 0, 0, 0 ); | |
| }; | |
| }(), | |
| // static/instance method to calculate barycentric coordinates | |
| // based on: http://www.blackpawn.com/texts/pointinpoly/default.html | |
| barycoordFromPoint: function () { | |
| var v0 = new Vector3(); | |
| var v1 = new Vector3(); | |
| var v2 = new Vector3(); | |
| return function barycoordFromPoint( point, a, b, c, optionalTarget ) { | |
| v0.subVectors( c, a ); | |
| v1.subVectors( b, a ); | |
| v2.subVectors( point, a ); | |
| var dot00 = v0.dot( v0 ); | |
| var dot01 = v0.dot( v1 ); | |
| var dot02 = v0.dot( v2 ); | |
| var dot11 = v1.dot( v1 ); | |
| var dot12 = v1.dot( v2 ); | |
| var denom = ( dot00 * dot11 - dot01 * dot01 ); | |
| var result = optionalTarget || new Vector3(); | |
| // collinear or singular triangle | |
| if ( denom === 0 ) { | |
| // arbitrary location outside of triangle? | |
| // not sure if this is the best idea, maybe should be returning undefined | |
| return result.set( - 2, - 1, - 1 ); | |
| } | |
| var invDenom = 1 / denom; | |
| var u = ( dot11 * dot02 - dot01 * dot12 ) * invDenom; | |
| var v = ( dot00 * dot12 - dot01 * dot02 ) * invDenom; | |
| // barycentric coordinates must always sum to 1 | |
| return result.set( 1 - u - v, v, u ); | |
| }; | |
| }(), | |
| containsPoint: function () { | |
| var v1 = new Vector3(); | |
| return function containsPoint( point, a, b, c ) { | |
| var result = Triangle.barycoordFromPoint( point, a, b, c, v1 ); | |
| return ( result.x >= 0 ) && ( result.y >= 0 ) && ( ( result.x + result.y ) <= 1 ); | |
| }; | |
| }() | |
| } ); | |
| Object.assign( Triangle.prototype, { | |
| set: function ( a, b, c ) { | |
| this.a.copy( a ); | |
| this.b.copy( b ); | |
| this.c.copy( c ); | |
| return this; | |
| }, | |
| setFromPointsAndIndices: function ( points, i0, i1, i2 ) { | |
| this.a.copy( points[ i0 ] ); | |
| this.b.copy( points[ i1 ] ); | |
| this.c.copy( points[ i2 ] ); | |
| return this; | |
| }, | |
| clone: function () { | |
| return new this.constructor().copy( this ); | |
| }, | |
| copy: function ( triangle ) { | |
| this.a.copy( triangle.a ); | |
| this.b.copy( triangle.b ); | |
| this.c.copy( triangle.c ); | |
| return this; | |
| }, | |
| area: function () { | |
| var v0 = new Vector3(); | |
| var v1 = new Vector3(); | |
| return function area() { | |
| v0.subVectors( this.c, this.b ); | |
| v1.subVectors( this.a, this.b ); | |
| return v0.cross( v1 ).length() * 0.5; | |
| }; | |
| }(), | |
| midpoint: function ( optionalTarget ) { | |
| var result = optionalTarget || new Vector3(); | |
| return result.addVectors( this.a, this.b ).add( this.c ).multiplyScalar( 1 / 3 ); | |
| }, | |
| normal: function ( optionalTarget ) { | |
| return Triangle.normal( this.a, this.b, this.c, optionalTarget ); | |
| }, | |
| plane: function ( optionalTarget ) { | |
| var result = optionalTarget || new Plane(); | |
| return result.setFromCoplanarPoints( this.a, this.b, this.c ); | |
| }, | |
| barycoordFromPoint: function ( point, optionalTarget ) { | |
| return Triangle.barycoordFromPoint( point, this.a, this.b, this.c, optionalTarget ); | |
| }, | |
| containsPoint: function ( point ) { | |
| return Triangle.containsPoint( point, this.a, this.b, this.c ); | |
| }, | |
| intersectsBox: function ( box ) { | |
| return box.intersectsTriangle( this ); | |
| }, | |
| closestPointToPoint: function () { | |
| var plane = new Plane(); | |
| var edgeList = [ new Line3(), new Line3(), new Line3() ]; | |
| var projectedPoint = new Vector3(); | |
| var closestPoint = new Vector3(); | |
| return function closestPointToPoint( point, optionalTarget ) { | |
| var result = optionalTarget || new Vector3(); | |
| var minDistance = Infinity; | |
| // project the point onto the plane of the triangle | |
| plane.setFromCoplanarPoints( this.a, this.b, this.c ); | |
| plane.projectPoint( point, projectedPoint ); | |
| // check if the projection lies within the triangle | |
| if ( this.containsPoint( projectedPoint ) === true ) { | |
| // if so, this is the closest point | |
| result.copy( projectedPoint ); | |
| } else { | |
| // if not, the point falls outside the triangle. the result is the closest point to the triangle's edges or vertices | |
| edgeList[ 0 ].set( this.a, this.b ); | |
| edgeList[ 1 ].set( this.b, this.c ); | |
| edgeList[ 2 ].set( this.c, this.a ); | |
| for ( var i = 0; i < edgeList.length; i ++ ) { | |
| edgeList[ i ].closestPointToPoint( projectedPoint, true, closestPoint ); | |
| var distance = projectedPoint.distanceToSquared( closestPoint ); | |
| if ( distance < minDistance ) { | |
| minDistance = distance; | |
| result.copy( closestPoint ); | |
| } | |
| } | |
| } | |
| return result; | |
| }; | |
| }(), | |
| equals: function ( triangle ) { | |
| return triangle.a.equals( this.a ) && triangle.b.equals( this.b ) && triangle.c.equals( this.c ); | |
| } | |
| } ); | |
| /** | |
| * @author mrdoob / http://mrdoob.com/ | |
| * @author alteredq / http://alteredqualia.com/ | |
| * @author mikael emtinger / http://gomo.se/ | |
| * @author jonobr1 / http://jonobr1.com/ | |
| */ | |
| function Mesh( geometry, material ) { | |
| Object3D.call( this ); | |
| this.type = 'Mesh'; | |
| this.geometry = geometry !== undefined ? geometry : new BufferGeometry(); | |
| this.material = material !== undefined ? material : new MeshBasicMaterial( { color: Math.random() * 0xffffff } ); | |
| this.drawMode = TrianglesDrawMode; | |
| this.updateMorphTargets(); | |
| } | |
| Mesh.prototype = Object.assign( Object.create( Object3D.prototype ), { | |
| constructor: Mesh, | |
| isMesh: true, | |
| setDrawMode: function ( value ) { | |
| this.drawMode = value; | |
| }, | |
| copy: function ( source ) { | |
| Object3D.prototype.copy.call( this, source ); | |
| this.drawMode = source.drawMode; | |
| if ( source.morphTargetInfluences !== undefined ) { | |
| this.morphTargetInfluences = source.morphTargetInfluences.slice(); | |
| } | |
| if ( source.morphTargetDictionary !== undefined ) { | |
| this.morphTargetDictionary = Object.assign( {}, source.morphTargetDictionary ); | |
| } | |
| return this; | |
| }, | |
| updateMorphTargets: function () { | |
| var geometry = this.geometry; | |
| var m, ml, name; | |
| if ( geometry.isBufferGeometry ) { | |
| var morphAttributes = geometry.morphAttributes; | |
| var keys = Object.keys( morphAttributes ); | |
| if ( keys.length > 0 ) { | |
| var morphAttribute = morphAttributes[ keys[ 0 ] ]; | |
| if ( morphAttribute !== undefined ) { | |
| this.morphTargetInfluences = []; | |
| this.morphTargetDictionary = {}; | |
| for ( m = 0, ml = morphAttribute.length; m < ml; m ++ ) { | |
| name = morphAttribute[ m ].name || String( m ); | |
| this.morphTargetInfluences.push( 0 ); | |
| this.morphTargetDictionary[ name ] = m; | |
| } | |
| } | |
| } | |
| } else { | |
| var morphTargets = geometry.morphTargets; | |
| if ( morphTargets !== undefined && morphTargets.length > 0 ) { | |
| this.morphTargetInfluences = []; | |
| this.morphTargetDictionary = {}; | |
| for ( m = 0, ml = morphTargets.length; m < ml; m ++ ) { | |
| name = morphTargets[ m ].name || String( m ); | |
| this.morphTargetInfluences.push( 0 ); | |
| this.morphTargetDictionary[ name ] = m; | |
| } | |
| } | |
| } | |
| }, | |
| raycast: ( function () { | |
| var inverseMatrix = new Matrix4(); | |
| var ray = new Ray(); | |
| var sphere = new Sphere(); | |
| var vA = new Vector3(); | |
| var vB = new Vector3(); | |
| var vC = new Vector3(); | |
| var tempA = new Vector3(); | |
| var tempB = new Vector3(); | |
| var tempC = new Vector3(); | |
| var uvA = new Vector2(); | |
| var uvB = new Vector2(); | |
| var uvC = new Vector2(); | |
| var barycoord = new Vector3(); | |
| var intersectionPoint = new Vector3(); | |
| var intersectionPointWorld = new Vector3(); | |
| function uvIntersection( point, p1, p2, p3, uv1, uv2, uv3 ) { | |
| Triangle.barycoordFromPoint( point, p1, p2, p3, barycoord ); | |
| uv1.multiplyScalar( barycoord.x ); | |
| uv2.multiplyScalar( barycoord.y ); | |
| uv3.multiplyScalar( barycoord.z ); | |
| uv1.add( uv2 ).add( uv3 ); | |
| return uv1.clone(); | |
| } | |
| function checkIntersection( object, material, raycaster, ray, pA, pB, pC, point ) { | |
| var intersect; | |
| if ( material.side === BackSide ) { | |
| intersect = ray.intersectTriangle( pC, pB, pA, true, point ); | |
| } else { | |
| intersect = ray.intersectTriangle( pA, pB, pC, material.side !== DoubleSide, point ); | |
| } | |
| if ( intersect === null ) return null; | |
| intersectionPointWorld.copy( point ); | |
| intersectionPointWorld.applyMatrix4( object.matrixWorld ); | |
| var distance = raycaster.ray.origin.distanceTo( intersectionPointWorld ); | |
| if ( distance < raycaster.near || distance > raycaster.far ) return null; | |
| return { | |
| distance: distance, | |
| point: intersectionPointWorld.clone(), | |
| object: object | |
| }; | |
| } | |
| function checkBufferGeometryIntersection( object, raycaster, ray, position, uv, a, b, c ) { | |
| vA.fromBufferAttribute( position, a ); | |
| vB.fromBufferAttribute( position, b ); | |
| vC.fromBufferAttribute( position, c ); | |
| var intersection = checkIntersection( object, object.material, raycaster, ray, vA, vB, vC, intersectionPoint ); | |
| if ( intersection ) { | |
| if ( uv ) { | |
| uvA.fromBufferAttribute( uv, a ); | |
| uvB.fromBufferAttribute( uv, b ); | |
| uvC.fromBufferAttribute( uv, c ); | |
| intersection.uv = uvIntersection( intersectionPoint, vA, vB, vC, uvA, uvB, uvC ); | |
| } | |
| intersection.face = new Face3( a, b, c, Triangle.normal( vA, vB, vC ) ); | |
| intersection.faceIndex = a; | |
| } | |
| return intersection; | |
| } | |
| return function raycast( raycaster, intersects ) { | |
| var geometry = this.geometry; | |
| var material = this.material; | |
| var matrixWorld = this.matrixWorld; | |
| if ( material === undefined ) return; | |
| // Checking boundingSphere distance to ray | |
| if ( geometry.boundingSphere === null ) geometry.computeBoundingSphere(); | |
| sphere.copy( geometry.boundingSphere ); | |
| sphere.applyMatrix4( matrixWorld ); | |
| if ( raycaster.ray.intersectsSphere( sphere ) === false ) return; | |
| // | |
| inverseMatrix.getInverse( matrixWorld ); | |
| ray.copy( raycaster.ray ).applyMatrix4( inverseMatrix ); | |
| // Check boundingBox before continuing | |
| if ( geometry.boundingBox !== null ) { | |
| if ( ray.intersectsBox( geometry.boundingBox ) === false ) return; | |
| } | |
| var intersection; | |
| if ( geometry.isBufferGeometry ) { | |
| var a, b, c; | |
| var index = geometry.index; | |
| var position = geometry.attributes.position; | |
| var uv = geometry.attributes.uv; | |
| var i, l; | |
| if ( index !== null ) { | |
| // indexed buffer geometry | |
| for ( i = 0, l = index.count; i < l; i += 3 ) { | |
| a = index.getX( i ); | |
| b = index.getX( i + 1 ); | |
| c = index.getX( i + 2 ); | |
| intersection = checkBufferGeometryIntersection( this, raycaster, ray, position, uv, a, b, c ); | |
| if ( intersection ) { | |
| intersection.faceIndex = Math.floor( i / 3 ); // triangle number in indices buffer semantics | |
| intersects.push( intersection ); | |
| } | |
| } | |
| } else if ( position !== undefined ) { | |
| // non-indexed buffer geometry | |
| for ( i = 0, l = position.count; i < l; i += 3 ) { | |
| a = i; | |
| b = i + 1; | |
| c = i + 2; | |
| intersection = checkBufferGeometryIntersection( this, raycaster, ray, position, uv, a, b, c ); | |
| if ( intersection ) { | |
| intersection.index = a; // triangle number in positions buffer semantics | |
| intersects.push( intersection ); | |
| } | |
| } | |
| } | |
| } else if ( geometry.isGeometry ) { | |
| var fvA, fvB, fvC; | |
| var isMultiMaterial = Array.isArray( material ); | |
| var vertices = geometry.vertices; | |
| var faces = geometry.faces; | |
| var uvs; | |
| var faceVertexUvs = geometry.faceVertexUvs[ 0 ]; | |
| if ( faceVertexUvs.length > 0 ) uvs = faceVertexUvs; | |
| for ( var f = 0, fl = faces.length; f < fl; f ++ ) { | |
| var face = faces[ f ]; | |
| var faceMaterial = isMultiMaterial ? material[ face.materialIndex ] : material; | |
| if ( faceMaterial === undefined ) continue; | |
| fvA = vertices[ face.a ]; | |
| fvB = vertices[ face.b ]; | |
| fvC = vertices[ face.c ]; | |
| if ( faceMaterial.morphTargets === true ) { | |
| var morphTargets = geometry.morphTargets; | |
| var morphInfluences = this.morphTargetInfluences; | |
| vA.set( 0, 0, 0 ); | |
| vB.set( 0, 0, 0 ); | |
| vC.set( 0, 0, 0 ); | |
| for ( var t = 0, tl = morphTargets.length; t < tl; t ++ ) { | |
| var influence = morphInfluences[ t ]; | |
| if ( influence === 0 ) continue; | |
| var targets = morphTargets[ t ].vertices; | |
| vA.addScaledVector( tempA.subVectors( targets[ face.a ], fvA ), influence ); | |
| vB.addScaledVector( tempB.subVectors( targets[ face.b ], fvB ), influence ); | |
| vC.addScaledVector( tempC.subVectors( targets[ face.c ], fvC ), influence ); | |
| } | |
| vA.add( fvA ); | |
| vB.add( fvB ); | |
| vC.add( fvC ); | |
| fvA = vA; | |
| fvB = vB; | |
| fvC = vC; | |
| } | |
| intersection = checkIntersection( this, faceMaterial, raycaster, ray, fvA, fvB, fvC, intersectionPoint ); | |
| if ( intersection ) { | |
| if ( uvs && uvs[ f ] ) { | |
| var uvs_f = uvs[ f ]; | |
| uvA.copy( uvs_f[ 0 ] ); | |
| uvB.copy( uvs_f[ 1 ] ); | |
| uvC.copy( uvs_f[ 2 ] ); | |
| intersection.uv = uvIntersection( intersectionPoint, fvA, fvB, fvC, uvA, uvB, uvC ); | |
| } | |
| intersection.face = face; | |
| intersection.faceIndex = f; | |
| intersects.push( intersection ); | |
| } | |
| } | |
| } | |
| }; | |
| }() ), | |
| clone: function () { | |
| return new this.constructor( this.geometry, this.material ).copy( this ); | |
| } | |
| } ); | |
| /** | |
| * @author mrdoob / http://mrdoob.com/ | |
| */ | |
| function WebGLBackground( renderer, state, geometries, premultipliedAlpha ) { | |
| var clearColor = new Color( 0x000000 ); | |
| var clearAlpha = 0; | |
| var planeCamera, planeMesh; | |
| var boxMesh; | |
| function render( renderList, scene, camera, forceClear ) { | |
| var background = scene.background; | |
| if ( background === null ) { | |
| setClear( clearColor, clearAlpha ); | |
| } else if ( background && background.isColor ) { | |
| setClear( background, 1 ); | |
| forceClear = true; | |
| } | |
| if ( renderer.autoClear || forceClear ) { | |
| renderer.clear( renderer.autoClearColor, renderer.autoClearDepth, renderer.autoClearStencil ); | |
| } | |
| if ( background && background.isCubeTexture ) { | |
| if ( boxMesh === undefined ) { | |
| boxMesh = new Mesh( | |
| new BoxBufferGeometry( 1, 1, 1 ), | |
| new ShaderMaterial( { | |
| uniforms: ShaderLib.cube.uniforms, | |
| vertexShader: ShaderLib.cube.vertexShader, | |
| fragmentShader: ShaderLib.cube.fragmentShader, | |
| side: BackSide, | |
| depthTest: true, | |
| depthWrite: false, | |
| fog: false | |
| } ) | |
| ); | |
| boxMesh.geometry.removeAttribute( 'normal' ); | |
| boxMesh.geometry.removeAttribute( 'uv' ); | |
| boxMesh.onBeforeRender = function ( renderer, scene, camera ) { | |
| this.matrixWorld.copyPosition( camera.matrixWorld ); | |
| }; | |
| geometries.update( boxMesh.geometry ); | |
| } | |
| boxMesh.material.uniforms.tCube.value = background; | |
| renderList.push( boxMesh, boxMesh.geometry, boxMesh.material, 0, null ); | |
| } else if ( background && background.isTexture ) { | |
| if ( planeCamera === undefined ) { | |
| planeCamera = new OrthographicCamera( - 1, 1, 1, - 1, 0, 1 ); | |
| planeMesh = new Mesh( | |
| new PlaneBufferGeometry( 2, 2 ), | |
| new MeshBasicMaterial( { depthTest: false, depthWrite: false, fog: false } ) | |
| ); | |
| geometries.update( planeMesh.geometry ); | |
| } | |
| planeMesh.material.map = background; | |
| // TODO Push this to renderList | |
| renderer.renderBufferDirect( planeCamera, null, planeMesh.geometry, planeMesh.material, planeMesh, null ); | |
| } | |
| } | |
| function setClear( color, alpha ) { | |
| state.buffers.color.setClear( color.r, color.g, color.b, alpha, premultipliedAlpha ); | |
| } | |
| return { | |
| getClearColor: function () { | |
| return clearColor; | |
| }, | |
| setClearColor: function ( color, alpha ) { | |
| clearColor.set( color ); | |
| clearAlpha = alpha !== undefined ? alpha : 1; | |
| setClear( clearColor, clearAlpha ); | |
| }, | |
| getClearAlpha: function () { | |
| return clearAlpha; | |
| }, | |
| setClearAlpha: function ( alpha ) { | |
| clearAlpha = alpha; | |
| setClear( clearColor, clearAlpha ); | |
| }, | |
| render: render | |
| }; | |
| } | |
| /** | |
| * @author mrdoob / http://mrdoob.com/ | |
| */ | |
| function WebGLBufferRenderer( gl, extensions, info ) { | |
| var mode; | |
| function setMode( value ) { | |
| mode = value; | |
| } | |
| function render( start, count ) { | |
| gl.drawArrays( mode, start, count ); | |
| info.update( count, mode ); | |
| } | |
| function renderInstances( geometry, start, count ) { | |
| var extension = extensions.get( 'ANGLE_instanced_arrays' ); | |
| if ( extension === null ) { | |
| console.error( 'THREE.WebGLBufferRenderer: using THREE.InstancedBufferGeometry but hardware does not support extension ANGLE_instanced_arrays.' ); | |
| return; | |
| } | |
| var position = geometry.attributes.position; | |
| if ( position.isInterleavedBufferAttribute ) { | |
| count = position.data.count; | |
| extension.drawArraysInstancedANGLE( mode, 0, count, geometry.maxInstancedCount ); | |
| } else { | |
| extension.drawArraysInstancedANGLE( mode, start, count, geometry.maxInstancedCount ); | |
| } | |
| info.update( count, mode, geometry.maxInstancedCount ); | |
| } | |
| // | |
| this.setMode = setMode; | |
| this.render = render; | |
| this.renderInstances = renderInstances; | |
| } | |
| /** | |
| * @author mrdoob / http://mrdoob.com/ | |
| */ | |
| function WebGLCapabilities( gl, extensions, parameters ) { | |
| var maxAnisotropy; | |
| function getMaxAnisotropy() { | |
| if ( maxAnisotropy !== undefined ) return maxAnisotropy; | |
| var extension = extensions.get( 'EXT_texture_filter_anisotropic' ); | |
| if ( extension !== null ) { | |
| maxAnisotropy = gl.getParameter( extension.MAX_TEXTURE_MAX_ANISOTROPY_EXT ); | |
| } else { | |
| maxAnisotropy = 0; | |
| } | |
| return maxAnisotropy; | |
| } | |
| function getMaxPrecision( precision ) { | |
| if ( precision === 'highp' ) { | |
| if ( gl.getShaderPrecisionFormat( gl.VERTEX_SHADER, gl.HIGH_FLOAT ).precision > 0 && | |
| gl.getShaderPrecisionFormat( gl.FRAGMENT_SHADER, gl.HIGH_FLOAT ).precision > 0 ) { | |
| return 'highp'; | |
| } | |
| precision = 'mediump'; | |
| } | |
| if ( precision === 'mediump' ) { | |
| if ( gl.getShaderPrecisionFormat( gl.VERTEX_SHADER, gl.MEDIUM_FLOAT ).precision > 0 && | |
| gl.getShaderPrecisionFormat( gl.FRAGMENT_SHADER, gl.MEDIUM_FLOAT ).precision > 0 ) { | |
| return 'mediump'; | |
| } | |
| } | |
| return 'lowp'; | |
| } | |
| var precision = parameters.precision !== undefined ? parameters.precision : 'highp'; | |
| var maxPrecision = getMaxPrecision( precision ); | |
| if ( maxPrecision !== precision ) { | |
| console.warn( 'THREE.WebGLRenderer:', precision, 'not supported, using', maxPrecision, 'instead.' ); | |
| precision = maxPrecision; | |
| } | |
| var logarithmicDepthBuffer = parameters.logarithmicDepthBuffer === true; | |
| var maxTextures = gl.getParameter( gl.MAX_TEXTURE_IMAGE_UNITS ); | |
| var maxVertexTextures = gl.getParameter( gl.MAX_VERTEX_TEXTURE_IMAGE_UNITS ); | |
| var maxTextureSize = gl.getParameter( gl.MAX_TEXTURE_SIZE ); | |
| var maxCubemapSize = gl.getParameter( gl.MAX_CUBE_MAP_TEXTURE_SIZE ); | |
| var maxAttributes = gl.getParameter( gl.MAX_VERTEX_ATTRIBS ); | |
| var maxVertexUniforms = gl.getParameter( gl.MAX_VERTEX_UNIFORM_VECTORS ); | |
| var maxVaryings = gl.getParameter( gl.MAX_VARYING_VECTORS ); | |
| var maxFragmentUniforms = gl.getParameter( gl.MAX_FRAGMENT_UNIFORM_VECTORS ); | |
| var vertexTextures = maxVertexTextures > 0; | |
| var floatFragmentTextures = !! extensions.get( 'OES_texture_float' ); | |
| var floatVertexTextures = vertexTextures && floatFragmentTextures; | |
| return { | |
| getMaxAnisotropy: getMaxAnisotropy, | |
| getMaxPrecision: getMaxPrecision, | |
| precision: precision, | |
| logarithmicDepthBuffer: logarithmicDepthBuffer, | |
| maxTextures: maxTextures, | |
| maxVertexTextures: maxVertexTextures, | |
| maxTextureSize: maxTextureSize, | |
| maxCubemapSize: maxCubemapSize, | |
| maxAttributes: maxAttributes, | |
| maxVertexUniforms: maxVertexUniforms, | |
| maxVaryings: maxVaryings, | |
| maxFragmentUniforms: maxFragmentUniforms, | |
| vertexTextures: vertexTextures, | |
| floatFragmentTextures: floatFragmentTextures, | |
| floatVertexTextures: floatVertexTextures | |
| }; | |
| } | |
| /** | |
| * @author tschw | |
| */ | |
| function WebGLClipping() { | |
| var scope = this, | |
| globalState = null, | |
| numGlobalPlanes = 0, | |
| localClippingEnabled = false, | |
| renderingShadows = false, | |
| plane = new Plane(), | |
| viewNormalMatrix = new Matrix3(), | |
| uniform = { value: null, needsUpdate: false }; | |
| this.uniform = uniform; | |
| this.numPlanes = 0; | |
| this.numIntersection = 0; | |
| this.init = function ( planes, enableLocalClipping, camera ) { | |
| var enabled = | |
| planes.length !== 0 || | |
| enableLocalClipping || | |
| // enable state of previous frame - the clipping code has to | |
| // run another frame in order to reset the state: | |
| numGlobalPlanes !== 0 || | |
| localClippingEnabled; | |
| localClippingEnabled = enableLocalClipping; | |
| globalState = projectPlanes( planes, camera, 0 ); | |
| numGlobalPlanes = planes.length; | |
| return enabled; | |
| }; | |
| this.beginShadows = function () { | |
| renderingShadows = true; | |
| projectPlanes( null ); | |
| }; | |
| this.endShadows = function () { | |
| renderingShadows = false; | |
| resetGlobalState(); | |
| }; | |
| this.setState = function ( planes, clipIntersection, clipShadows, camera, cache, fromCache ) { | |
| if ( ! localClippingEnabled || planes === null || planes.length === 0 || renderingShadows && ! clipShadows ) { | |
| // there's no local clipping | |
| if ( renderingShadows ) { | |
| // there's no global clipping | |
| projectPlanes( null ); | |
| } else { | |
| resetGlobalState(); | |
| } | |
| } else { | |
| var nGlobal = renderingShadows ? 0 : numGlobalPlanes, | |
| lGlobal = nGlobal * 4, | |
| dstArray = cache.clippingState || null; | |
| uniform.value = dstArray; // ensure unique state | |
| dstArray = projectPlanes( planes, camera, lGlobal, fromCache ); | |
| for ( var i = 0; i !== lGlobal; ++ i ) { | |
| dstArray[ i ] = globalState[ i ]; | |
| } | |
| cache.clippingState = dstArray; | |
| this.numIntersection = clipIntersection ? this.numPlanes : 0; | |
| this.numPlanes += nGlobal; | |
| } | |
| }; | |
| function resetGlobalState() { | |
| if ( uniform.value !== globalState ) { | |
| uniform.value = globalState; | |
| uniform.needsUpdate = numGlobalPlanes > 0; | |
| } | |
| scope.numPlanes = numGlobalPlanes; | |
| scope.numIntersection = 0; | |
| } | |
| function projectPlanes( planes, camera, dstOffset, skipTransform ) { | |
| var nPlanes = planes !== null ? planes.length : 0, | |
| dstArray = null; | |
| if ( nPlanes !== 0 ) { | |
| dstArray = uniform.value; | |
| if ( skipTransform !== true || dstArray === null ) { | |
| var flatSize = dstOffset + nPlanes * 4, | |
| viewMatrix = camera.matrixWorldInverse; | |
| viewNormalMatrix.getNormalMatrix( viewMatrix ); | |
| if ( dstArray === null || dstArray.length < flatSize ) { | |
| dstArray = new Float32Array( flatSize ); | |
| } | |
| for ( var i = 0, i4 = dstOffset; i !== nPlanes; ++ i, i4 += 4 ) { | |
| plane.copy( planes[ i ] ).applyMatrix4( viewMatrix, viewNormalMatrix ); | |
| plane.normal.toArray( dstArray, i4 ); | |
| dstArray[ i4 + 3 ] = plane.constant; | |
| } | |
| } | |
| uniform.value = dstArray; | |
| uniform.needsUpdate = true; | |
| } | |
| scope.numPlanes = nPlanes; | |
| return dstArray; | |
| } | |
| } | |
| /** | |
| * @author mrdoob / http://mrdoob.com/ | |
| */ | |
| function WebGLExtensions( gl ) { | |
| var extensions = {}; | |
| return { | |
| get: function ( name ) { | |
| if ( extensions[ name ] !== undefined ) { | |
| return extensions[ name ]; | |
| } | |
| var extension; | |
| switch ( name ) { | |
| case 'WEBGL_depth_texture': | |
| extension = gl.getExtension( 'WEBGL_depth_texture' ) || gl.getExtension( 'MOZ_WEBGL_depth_texture' ) || gl.getExtension( 'WEBKIT_WEBGL_depth_texture' ); | |
| break; | |
| case 'EXT_texture_filter_anisotropic': | |
| extension = gl.getExtension( 'EXT_texture_filter_anisotropic' ) || gl.getExtension( 'MOZ_EXT_texture_filter_anisotropic' ) || gl.getExtension( 'WEBKIT_EXT_texture_filter_anisotropic' ); | |
| break; | |
| case 'WEBGL_compressed_texture_s3tc': | |
| extension = gl.getExtension( 'WEBGL_compressed_texture_s3tc' ) || gl.getExtension( 'MOZ_WEBGL_compressed_texture_s3tc' ) || gl.getExtension( 'WEBKIT_WEBGL_compressed_texture_s3tc' ); | |
| break; | |
| case 'WEBGL_compressed_texture_pvrtc': | |
| extension = gl.getExtension( 'WEBGL_compressed_texture_pvrtc' ) || gl.getExtension( 'WEBKIT_WEBGL_compressed_texture_pvrtc' ); | |
| break; | |
| case 'WEBGL_compressed_texture_etc1': | |
| extension = gl.getExtension( 'WEBGL_compressed_texture_etc1' ); | |
| break; | |
| default: | |
| extension = gl.getExtension( name ); | |
| } | |
| if ( extension === null ) { | |
| console.warn( 'THREE.WebGLRenderer: ' + name + ' extension not supported.' ); | |
| } | |
| extensions[ name ] = extension; | |
| return extension; | |
| } | |
| }; | |
| } | |
| /** | |
| * @author mrdoob / http://mrdoob.com/ | |
| */ | |
| function WebGLGeometries( gl, attributes, info ) { | |
| var geometries = {}; | |
| var wireframeAttributes = {}; | |
| function onGeometryDispose( event ) { | |
| var geometry = event.target; | |
| var buffergeometry = geometries[ geometry.id ]; | |
| if ( buffergeometry.index !== null ) { | |
| attributes.remove( buffergeometry.index ); | |
| } | |
| for ( var name in buffergeometry.attributes ) { | |
| attributes.remove( buffergeometry.attributes[ name ] ); | |
| } | |
| geometry.removeEventListener( 'dispose', onGeometryDispose ); | |
| delete geometries[ geometry.id ]; | |
| // TODO Remove duplicate code | |
| var attribute = wireframeAttributes[ geometry.id ]; | |
| if ( attribute ) { | |
| attributes.remove( attribute ); | |
| delete wireframeAttributes[ geometry.id ]; | |
| } | |
| attribute = wireframeAttributes[ buffergeometry.id ]; | |
| if ( attribute ) { | |
| attributes.remove( attribute ); | |
| delete wireframeAttributes[ buffergeometry.id ]; | |
| } | |
| // | |
| info.memory.geometries --; | |
| } | |
| function get( object, geometry ) { | |
| var buffergeometry = geometries[ geometry.id ]; | |
| if ( buffergeometry ) return buffergeometry; | |
| geometry.addEventListener( 'dispose', onGeometryDispose ); | |
| if ( geometry.isBufferGeometry ) { | |
| buffergeometry = geometry; | |
| } else if ( geometry.isGeometry ) { | |
| if ( geometry._bufferGeometry === undefined ) { | |
| geometry._bufferGeometry = new BufferGeometry().setFromObject( object ); | |
| } | |
| buffergeometry = geometry._bufferGeometry; | |
| } | |
| geometries[ geometry.id ] = buffergeometry; | |
| info.memory.geometries ++; | |
| return buffergeometry; | |
| } | |
| function update( geometry ) { | |
| var index = geometry.index; | |
| var geometryAttributes = geometry.attributes; | |
| if ( index !== null ) { | |
| attributes.update( index, gl.ELEMENT_ARRAY_BUFFER ); | |
| } | |
| for ( var name in geometryAttributes ) { | |
| attributes.update( geometryAttributes[ name ], gl.ARRAY_BUFFER ); | |
| } | |
| // morph targets | |
| var morphAttributes = geometry.morphAttributes; | |
| for ( var name in morphAttributes ) { | |
| var array = morphAttributes[ name ]; | |
| for ( var i = 0, l = array.length; i < l; i ++ ) { | |
| attributes.update( array[ i ], gl.ARRAY_BUFFER ); | |
| } | |
| } | |
| } | |
| function getWireframeAttribute( geometry ) { | |
| var attribute = wireframeAttributes[ geometry.id ]; | |
| if ( attribute ) return attribute; | |
| var indices = []; | |
| var geometryIndex = geometry.index; | |
| var geometryAttributes = geometry.attributes; | |
| // console.time( 'wireframe' ); | |
| if ( geometryIndex !== null ) { | |
| var array = geometryIndex.array; | |
| for ( var i = 0, l = array.length; i < l; i += 3 ) { | |
| var a = array[ i + 0 ]; | |
| var b = array[ i + 1 ]; | |
| var c = array[ i + 2 ]; | |
| indices.push( a, b, b, c, c, a ); | |
| } | |
| } else { | |
| var array = geometryAttributes.position.array; | |
| for ( var i = 0, l = ( array.length / 3 ) - 1; i < l; i += 3 ) { | |
| var a = i + 0; | |
| var b = i + 1; | |
| var c = i + 2; | |
| indices.push( a, b, b, c, c, a ); | |
| } | |
| } | |
| // console.timeEnd( 'wireframe' ); | |
| attribute = new ( arrayMax( indices ) > 65535 ? Uint32BufferAttribute : Uint16BufferAttribute )( indices, 1 ); | |
| attributes.update( attribute, gl.ELEMENT_ARRAY_BUFFER ); | |
| wireframeAttributes[ geometry.id ] = attribute; | |
| return attribute; | |
| } | |
| return { | |
| get: get, | |
| update: update, | |
| getWireframeAttribute: getWireframeAttribute | |
| }; | |
| } | |
| /** | |
| * @author mrdoob / http://mrdoob.com/ | |
| */ | |
| function WebGLIndexedBufferRenderer( gl, extensions, info ) { | |
| var mode; | |
| function setMode( value ) { | |
| mode = value; | |
| } | |
| var type, bytesPerElement; | |
| function setIndex( value ) { | |
| type = value.type; | |
| bytesPerElement = value.bytesPerElement; | |
| } | |
| function render( start, count ) { | |
| gl.drawElements( mode, count, type, start * bytesPerElement ); | |
| info.update( count, mode ); | |
| } | |
| function renderInstances( geometry, start, count ) { | |
| var extension = extensions.get( 'ANGLE_instanced_arrays' ); | |
| if ( extension === null ) { | |
| console.error( 'THREE.WebGLIndexedBufferRenderer: using THREE.InstancedBufferGeometry but hardware does not support extension ANGLE_instanced_arrays.' ); | |
| return; | |
| } | |
| extension.drawElementsInstancedANGLE( mode, count, type, start * bytesPerElement, geometry.maxInstancedCount ); | |
| info.update( count, mode, geometry.maxInstancedCount ); | |
| } | |
| // | |
| this.setMode = setMode; | |
| this.setIndex = setIndex; | |
| this.render = render; | |
| this.renderInstances = renderInstances; | |
| } | |
| /** | |
| * @author Mugen87 / https://github.com/Mugen87 | |
| */ | |
| function WebGLInfo( gl ) { | |
| var memory = { | |
| geometries: 0, | |
| textures: 0 | |
| }; | |
| var render = { | |
| frame: 0, | |
| calls: 0, | |
| triangles: 0, | |
| points: 0, | |
| lines: 0 | |
| }; | |
| function update( count, mode, instanceCount ) { | |
| instanceCount = instanceCount || 1; | |
| render.calls ++; | |
| switch ( mode ) { | |
| case gl.TRIANGLES: | |
| render.triangles += instanceCount * ( count / 3 ); | |
| break; | |
| case gl.TRIANGLE_STRIP: | |
| case gl.TRIANGLE_FAN: | |
| render.triangles += instanceCount * ( count - 2 ); | |
| break; | |
| case gl.LINES: | |
| render.lines += instanceCount * ( count / 2 ); | |
| break; | |
| case gl.LINE_STRIP: | |
| render.lines += instanceCount * ( count - 1 ); | |
| break; | |
| case gl.LINE_LOOP: | |
| render.lines += instanceCount * count; | |
| break; | |
| case gl.POINTS: | |
| render.points += instanceCount * count; | |
| break; | |
| default: | |
| console.error( 'THREE.WebGLInfo: Unknown draw mode:', mode ); | |
| break; | |
| } | |
| } | |
| function reset() { | |
| render.frame ++; | |
| render.calls = 0; | |
| render.triangles = 0; | |
| render.points = 0; | |
| render.lines = 0; | |
| } | |
| return { | |
| memory: memory, | |
| render: render, | |
| programs: null, | |
| autoReset: true, | |
| reset: reset, | |
| update: update | |
| }; | |
| } | |
| /** | |
| * @author mrdoob / http://mrdoob.com/ | |
| */ | |
| function absNumericalSort( a, b ) { | |
| return Math.abs( b[ 1 ] ) - Math.abs( a[ 1 ] ); | |
| } | |
| function WebGLMorphtargets( gl ) { | |
| var influencesList = {}; | |
| var morphInfluences = new Float32Array( 8 ); | |
| function update( object, geometry, material, program ) { | |
| var objectInfluences = object.morphTargetInfluences; | |
| var length = objectInfluences.length; | |
| var influences = influencesList[ geometry.id ]; | |
| if ( influences === undefined ) { | |
| // initialise list | |
| influences = []; | |
| for ( var i = 0; i < length; i ++ ) { | |
| influences[ i ] = [ i, 0 ]; | |
| } | |
| influencesList[ geometry.id ] = influences; | |
| } | |
| var morphTargets = material.morphTargets && geometry.morphAttributes.position; | |
| var morphNormals = material.morphNormals && geometry.morphAttributes.normal; | |
| // Remove current morphAttributes | |
| for ( var i = 0; i < length; i ++ ) { | |
| var influence = influences[ i ]; | |
| if ( influence[ 1 ] !== 0 ) { | |
| if ( morphTargets ) geometry.removeAttribute( 'morphTarget' + i ); | |
| if ( morphNormals ) geometry.removeAttribute( 'morphNormal' + i ); | |
| } | |
| } | |
| // Collect influences | |
| for ( var i = 0; i < length; i ++ ) { | |
| var influence = influences[ i ]; | |
| influence[ 0 ] = i; | |
| influence[ 1 ] = objectInfluences[ i ]; | |
| } | |
| influences.sort( absNumericalSort ); | |
| // Add morphAttributes | |
| for ( var i = 0; i < 8; i ++ ) { | |
| var influence = influences[ i ]; | |
| if ( influence ) { | |
| var index = influence[ 0 ]; | |
| var value = influence[ 1 ]; | |
| if ( value ) { | |
| if ( morphTargets ) geometry.addAttribute( 'morphTarget' + i, morphTargets[ index ] ); | |
| if ( morphNormals ) geometry.addAttribute( 'morphNormal' + i, morphNormals[ index ] ); | |
| morphInfluences[ i ] = value; | |
| continue; | |
| } | |
| } | |
| morphInfluences[ i ] = 0; | |
| } | |
| program.getUniforms().setValue( gl, 'morphTargetInfluences', morphInfluences ); | |
| } | |
| return { | |
| update: update | |
| }; | |
| } | |
| /** | |
| * @author mrdoob / http://mrdoob.com/ | |
| */ | |
| function WebGLObjects( geometries, info ) { | |
| var updateList = {}; | |
| function update( object ) { | |
| var frame = info.render.frame; | |
| var geometry = object.geometry; | |
| var buffergeometry = geometries.get( object, geometry ); | |
| // Update once per frame | |
| if ( updateList[ buffergeometry.id ] !== frame ) { | |
| if ( geometry.isGeometry ) { | |
| buffergeometry.updateFromObject( object ); | |
| } | |
| geometries.update( buffergeometry ); | |
| updateList[ buffergeometry.id ] = frame; | |
| } | |
| return buffergeometry; | |
| } | |
| function dispose() { | |
| updateList = {}; | |
| } | |
| return { | |
| update: update, | |
| dispose: dispose | |
| }; | |
| } | |
| /** | |
| * @author mrdoob / http://mrdoob.com/ | |
| */ | |
| function CubeTexture( images, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy, encoding ) { | |
| images = images !== undefined ? images : []; | |
| mapping = mapping !== undefined ? mapping : CubeReflectionMapping; | |
| Texture.call( this, images, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy, encoding ); | |
| this.flipY = false; | |
| } | |
| CubeTexture.prototype = Object.create( Texture.prototype ); | |
| CubeTexture.prototype.constructor = CubeTexture; | |
| CubeTexture.prototype.isCubeTexture = true; | |
| Object.defineProperty( CubeTexture.prototype, 'images', { | |
| get: function () { | |
| return this.image; | |
| }, | |
| set: function ( value ) { | |
| this.image = value; | |
| } | |
| } ); | |
| /** | |
| * @author tschw | |
| * | |
| * Uniforms of a program. | |
| * Those form a tree structure with a special top-level container for the root, | |
| * which you get by calling 'new WebGLUniforms( gl, program, renderer )'. | |
| * | |
| * | |
| * Properties of inner nodes including the top-level container: | |
| * | |
| * .seq - array of nested uniforms | |
| * .map - nested uniforms by name | |
| * | |
| * | |
| * Methods of all nodes except the top-level container: | |
| * | |
| * .setValue( gl, value, [renderer] ) | |
| * | |
| * uploads a uniform value(s) | |
| * the 'renderer' parameter is needed for sampler uniforms | |
| * | |
| * | |
| * Static methods of the top-level container (renderer factorizations): | |
| * | |
| * .upload( gl, seq, values, renderer ) | |
| * | |
| * sets uniforms in 'seq' to 'values[id].value' | |
| * | |
| * .seqWithValue( seq, values ) : filteredSeq | |
| * | |
| * filters 'seq' entries with corresponding entry in values | |
| * | |
| * | |
| * Methods of the top-level container (renderer factorizations): | |
| * | |
| * .setValue( gl, name, value ) | |
| * | |
| * sets uniform with name 'name' to 'value' | |
| * | |
| * .set( gl, obj, prop ) | |
| * | |
| * sets uniform from object and property with same name than uniform | |
| * | |
| * .setOptional( gl, obj, prop ) | |
| * | |
| * like .set for an optional property of the object | |
| * | |
| */ | |
| var emptyTexture = new Texture(); | |
| var emptyCubeTexture = new CubeTexture(); | |
| // --- Base for inner nodes (including the root) --- | |
| function UniformContainer() { | |
| this.seq = []; | |
| this.map = {}; | |
| } | |
| // --- Utilities --- | |
| // Array Caches (provide typed arrays for temporary by size) | |
| var arrayCacheF32 = []; | |
| var arrayCacheI32 = []; | |
| // Float32Array caches used for uploading Matrix uniforms | |
| var mat4array = new Float32Array( 16 ); | |
| var mat3array = new Float32Array( 9 ); | |
| // Flattening for arrays of vectors and matrices | |
| function flatten( array, nBlocks, blockSize ) { | |
| var firstElem = array[ 0 ]; | |
| if ( firstElem <= 0 || firstElem > 0 ) return array; | |
| // unoptimized: ! isNaN( firstElem ) | |
| // see http://jacksondunstan.com/articles/983 | |
| var n = nBlocks * blockSize, | |
| r = arrayCacheF32[ n ]; | |
| if ( r === undefined ) { | |
| r = new Float32Array( n ); | |
| arrayCacheF32[ n ] = r; | |
| } | |
| if ( nBlocks !== 0 ) { | |
| firstElem.toArray( r, 0 ); | |
| for ( var i = 1, offset = 0; i !== nBlocks; ++ i ) { | |
| offset += blockSize; | |
| array[ i ].toArray( r, offset ); | |
| } | |
| } | |
| return r; | |
| } | |
| // Texture unit allocation | |
| function allocTexUnits( renderer, n ) { | |
| var r = arrayCacheI32[ n ]; | |
| if ( r === undefined ) { | |
| r = new Int32Array( n ); | |
| arrayCacheI32[ n ] = r; | |
| } | |
| for ( var i = 0; i !== n; ++ i ) | |
| r[ i ] = renderer.allocTextureUnit(); | |
| return r; | |
| } | |
| // --- Setters --- | |
| // Note: Defining these methods externally, because they come in a bunch | |
| // and this way their names minify. | |
| // Single scalar | |
| function setValue1f( gl, v ) { | |
| gl.uniform1f( this.addr, v ); | |
| } | |
| function setValue1i( gl, v ) { | |
| gl.uniform1i( this.addr, v ); | |
| } | |
| // Single float vector (from flat array or THREE.VectorN) | |
| function setValue2fv( gl, v ) { | |
| if ( v.x === undefined ) { | |
| gl.uniform2fv( this.addr, v ); | |
| } else { | |
| gl.uniform2f( this.addr, v.x, v.y ); | |
| } | |
| } | |
| function setValue3fv( gl, v ) { | |
| if ( v.x !== undefined ) { | |
| gl.uniform3f( this.addr, v.x, v.y, v.z ); | |
| } else if ( v.r !== undefined ) { | |
| gl.uniform3f( this.addr, v.r, v.g, v.b ); | |
| } else { | |
| gl.uniform3fv( this.addr, v ); | |
| } | |
| } | |
| function setValue4fv( gl, v ) { | |
| if ( v.x === undefined ) { | |
| gl.uniform4fv( this.addr, v ); | |
| } else { | |
| gl.uniform4f( this.addr, v.x, v.y, v.z, v.w ); | |
| } | |
| } | |
| // Single matrix (from flat array or MatrixN) | |
| function setValue2fm( gl, v ) { | |
| gl.uniformMatrix2fv( this.addr, false, v.elements || v ); | |
| } | |
| function setValue3fm( gl, v ) { | |
| if ( v.elements === undefined ) { | |
| gl.uniformMatrix3fv( this.addr, false, v ); | |
| } else { | |
| mat3array.set( v.elements ); | |
| gl.uniformMatrix3fv( this.addr, false, mat3array ); | |
| } | |
| } | |
| function setValue4fm( gl, v ) { | |
| if ( v.elements === undefined ) { | |
| gl.uniformMatrix4fv( this.addr, false, v ); | |
| } else { | |
| mat4array.set( v.elements ); | |
| gl.uniformMatrix4fv( this.addr, false, mat4array ); | |
| } | |
| } | |
| // Single texture (2D / Cube) | |
| function setValueT1( gl, v, renderer ) { | |
| var unit = renderer.allocTextureUnit(); | |
| gl.uniform1i( this.addr, unit ); | |
| renderer.setTexture2D( v || emptyTexture, unit ); | |
| } | |
| function setValueT6( gl, v, renderer ) { | |
| var unit = renderer.allocTextureUnit(); | |
| gl.uniform1i( this.addr, unit ); | |
| renderer.setTextureCube( v || emptyCubeTexture, unit ); | |
| } | |
| // Integer / Boolean vectors or arrays thereof (always flat arrays) | |
| function setValue2iv( gl, v ) { | |
| gl.uniform2iv( this.addr, v ); | |
| } | |
| function setValue3iv( gl, v ) { | |
| gl.uniform3iv( this.addr, v ); | |
| } | |
| function setValue4iv( gl, v ) { | |
| gl.uniform4iv( this.addr, v ); | |
| } | |
| // Helper to pick the right setter for the singular case | |
| function getSingularSetter( type ) { | |
| switch ( type ) { | |
| case 0x1406: return setValue1f; // FLOAT | |
| case 0x8b50: return setValue2fv; // _VEC2 | |
| case 0x8b51: return setValue3fv; // _VEC3 | |
| case 0x8b52: return setValue4fv; // _VEC4 | |
| case 0x8b5a: return setValue2fm; // _MAT2 | |
| case 0x8b5b: return setValue3fm; // _MAT3 | |
| case 0x8b5c: return setValue4fm; // _MAT4 | |
| case 0x8b5e: case 0x8d66: return setValueT1; // SAMPLER_2D, SAMPLER_EXTERNAL_OES | |
| case 0x8b60: return setValueT6; // SAMPLER_CUBE | |
| case 0x1404: case 0x8b56: return setValue1i; // INT, BOOL | |
| case 0x8b53: case 0x8b57: return setValue2iv; // _VEC2 | |
| case 0x8b54: case 0x8b58: return setValue3iv; // _VEC3 | |
| case 0x8b55: case 0x8b59: return setValue4iv; // _VEC4 | |
| } | |
| } | |
| // Array of scalars | |
| function setValue1fv( gl, v ) { | |
| gl.uniform1fv( this.addr, v ); | |
| } | |
| function setValue1iv( gl, v ) { | |
| gl.uniform1iv( this.addr, v ); | |
| } | |
| // Array of vectors (flat or from THREE classes) | |
| function setValueV2a( gl, v ) { | |
| gl.uniform2fv( this.addr, flatten( v, this.size, 2 ) ); | |
| } | |
| function setValueV3a( gl, v ) { | |
| gl.uniform3fv( this.addr, flatten( v, this.size, 3 ) ); | |
| } | |
| function setValueV4a( gl, v ) { | |
| gl.uniform4fv( this.addr, flatten( v, this.size, 4 ) ); | |
| } | |
| // Array of matrices (flat or from THREE clases) | |
| function setValueM2a( gl, v ) { | |
| gl.uniformMatrix2fv( this.addr, false, flatten( v, this.size, 4 ) ); | |
| } | |
| function setValueM3a( gl, v ) { | |
| gl.uniformMatrix3fv( this.addr, false, flatten( v, this.size, 9 ) ); | |
| } | |
| function setValueM4a( gl, v ) { | |
| gl.uniformMatrix4fv( this.addr, false, flatten( v, this.size, 16 ) ); | |
| } | |
| // Array of textures (2D / Cube) | |
| function setValueT1a( gl, v, renderer ) { | |
| var n = v.length, | |
| units = allocTexUnits( renderer, n ); | |
| gl.uniform1iv( this.addr, units ); | |
| for ( var i = 0; i !== n; ++ i ) { | |
| renderer.setTexture2D( v[ i ] || emptyTexture, units[ i ] ); | |
| } | |
| } | |
| function setValueT6a( gl, v, renderer ) { | |
| var n = v.length, | |
| units = allocTexUnits( renderer, n ); | |
| gl.uniform1iv( this.addr, units ); | |
| for ( var i = 0; i !== n; ++ i ) { | |
| renderer.setTextureCube( v[ i ] || emptyCubeTexture, units[ i ] ); | |
| } | |
| } | |
| // Helper to pick the right setter for a pure (bottom-level) array | |
| function getPureArraySetter( type ) { | |
| switch ( type ) { | |
| case 0x1406: return setValue1fv; // FLOAT | |
| case 0x8b50: return setValueV2a; // _VEC2 | |
| case 0x8b51: return setValueV3a; // _VEC3 | |
| case 0x8b52: return setValueV4a; // _VEC4 | |
| case 0x8b5a: return setValueM2a; // _MAT2 | |
| case 0x8b5b: return setValueM3a; // _MAT3 | |
| case 0x8b5c: return setValueM4a; // _MAT4 | |
| case 0x8b5e: return setValueT1a; // SAMPLER_2D | |
| case 0x8b60: return setValueT6a; // SAMPLER_CUBE | |
| case 0x1404: case 0x8b56: return setValue1iv; // INT, BOOL | |
| case 0x8b53: case 0x8b57: return setValue2iv; // _VEC2 | |
| case 0x8b54: case 0x8b58: return setValue3iv; // _VEC3 | |
| case 0x8b55: case 0x8b59: return setValue4iv; // _VEC4 | |
| } | |
| } | |
| // --- Uniform Classes --- | |
| function SingleUniform( id, activeInfo, addr ) { | |
| this.id = id; | |
| this.addr = addr; | |
| this.setValue = getSingularSetter( activeInfo.type ); | |
| // this.path = activeInfo.name; // DEBUG | |
| } | |
| function PureArrayUniform( id, activeInfo, addr ) { | |
| this.id = id; | |
| this.addr = addr; | |
| this.size = activeInfo.size; | |
| this.setValue = getPureArraySetter( activeInfo.type ); | |
| // this.path = activeInfo.name; // DEBUG | |
| } | |
| function StructuredUniform( id ) { | |
| this.id = id; | |
| UniformContainer.call( this ); // mix-in | |
| } | |
| StructuredUniform.prototype.setValue = function ( gl, value ) { | |
| // Note: Don't need an extra 'renderer' parameter, since samplers | |
| // are not allowed in structured uniforms. | |
| var seq = this.seq; | |
| for ( var i = 0, n = seq.length; i !== n; ++ i ) { | |
| var u = seq[ i ]; | |
| u.setValue( gl, value[ u.id ] ); | |
| } | |
| }; | |
| // --- Top-level --- | |
| // Parser - builds up the property tree from the path strings | |
| var RePathPart = /([\w\d_]+)(\])?(\[|\.)?/g; | |
| // extracts | |
| // - the identifier (member name or array index) | |
| // - followed by an optional right bracket (found when array index) | |
| // - followed by an optional left bracket or dot (type of subscript) | |
| // | |
| // Note: These portions can be read in a non-overlapping fashion and | |
| // allow straightforward parsing of the hierarchy that WebGL encodes | |
| // in the uniform names. | |
| function addUniform( container, uniformObject ) { | |
| container.seq.push( uniformObject ); | |
| container.map[ uniformObject.id ] = uniformObject; | |
| } | |
| function parseUniform( activeInfo, addr, container ) { | |
| var path = activeInfo.name, | |
| pathLength = path.length; | |
| // reset RegExp object, because of the early exit of a previous run | |
| RePathPart.lastIndex = 0; | |
| for ( ; ; ) { | |
| var match = RePathPart.exec( path ), | |
| matchEnd = RePathPart.lastIndex, | |
| id = match[ 1 ], | |
| idIsIndex = match[ 2 ] === ']', | |
| subscript = match[ 3 ]; | |
| if ( idIsIndex ) id = id | 0; // convert to integer | |
| if ( subscript === undefined || subscript === '[' && matchEnd + 2 === pathLength ) { | |
| // bare name or "pure" bottom-level array "[0]" suffix | |
| addUniform( container, subscript === undefined ? | |
| new SingleUniform( id, activeInfo, addr ) : | |
| new PureArrayUniform( id, activeInfo, addr ) ); | |
| break; | |
| } else { | |
| // step into inner node / create it in case it doesn't exist | |
| var map = container.map, next = map[ id ]; | |
| if ( next === undefined ) { | |
| next = new StructuredUniform( id ); | |
| addUniform( container, next ); | |
| } | |
| container = next; | |
| } | |
| } | |
| } | |
| // Root Container | |
| function WebGLUniforms( gl, program, renderer ) { | |
| UniformContainer.call( this ); | |
| this.renderer = renderer; | |
| var n = gl.getProgramParameter( program, gl.ACTIVE_UNIFORMS ); | |
| for ( var i = 0; i < n; ++ i ) { | |
| var info = gl.getActiveUniform( program, i ), | |
| path = info.name, | |
| addr = gl.getUniformLocation( program, path ); | |
| parseUniform( info, addr, this ); | |
| } | |
| } | |
| WebGLUniforms.prototype.setValue = function ( gl, name, value ) { | |
| var u = this.map[ name ]; | |
| if ( u !== undefined ) u.setValue( gl, value, this.renderer ); | |
| }; | |
| WebGLUniforms.prototype.setOptional = function ( gl, object, name ) { | |
| var v = object[ name ]; | |
| if ( v !== undefined ) this.setValue( gl, name, v ); | |
| }; | |
| // Static interface | |
| WebGLUniforms.upload = function ( gl, seq, values, renderer ) { | |
| for ( var i = 0, n = seq.length; i !== n; ++ i ) { | |
| var u = seq[ i ], | |
| v = values[ u.id ]; | |
| if ( v.needsUpdate !== false ) { | |
| // note: always updating when .needsUpdate is undefined | |
| u.setValue( gl, v.value, renderer ); | |
| } | |
| } | |
| }; | |
| WebGLUniforms.seqWithValue = function ( seq, values ) { | |
| var r = []; | |
| for ( var i = 0, n = seq.length; i !== n; ++ i ) { | |
| var u = seq[ i ]; | |
| if ( u.id in values ) r.push( u ); | |
| } | |
| return r; | |
| }; | |
| /** | |
| * @author mrdoob / http://mrdoob.com/ | |
| */ | |
| function addLineNumbers( string ) { | |
| var lines = string.split( '\n' ); | |
| for ( var i = 0; i < lines.length; i ++ ) { | |
| lines[ i ] = ( i + 1 ) + ': ' + lines[ i ]; | |
| } | |
| return lines.join( '\n' ); | |
| } | |
| function WebGLShader( gl, type, string ) { | |
| var shader = gl.createShader( type ); | |
| gl.shaderSource( shader, string ); | |
| gl.compileShader( shader ); | |
| if ( gl.getShaderParameter( shader, gl.COMPILE_STATUS ) === false ) { | |
| console.error( 'THREE.WebGLShader: Shader couldn\'t compile.' ); | |
| } | |
| if ( gl.getShaderInfoLog( shader ) !== '' ) { | |
| console.warn( 'THREE.WebGLShader: gl.getShaderInfoLog()', type === gl.VERTEX_SHADER ? 'vertex' : 'fragment', gl.getShaderInfoLog( shader ), addLineNumbers( string ) ); | |
| } | |
| // --enable-privileged-webgl-extension | |
| // console.log( type, gl.getExtension( 'WEBGL_debug_shaders' ).getTranslatedShaderSource( shader ) ); | |
| return shader; | |
| } | |
| /** | |
| * @author mrdoob / http://mrdoob.com/ | |
| */ | |
| var programIdCount = 0; | |
| function getEncodingComponents( encoding ) { | |
| switch ( encoding ) { | |
| case LinearEncoding: | |
| return [ 'Linear', '( value )' ]; | |
| case sRGBEncoding: | |
| return [ 'sRGB', '( value )' ]; | |
| case RGBEEncoding: | |
| return [ 'RGBE', '( value )' ]; | |
| case RGBM7Encoding: | |
| return [ 'RGBM', '( value, 7.0 )' ]; | |
| case RGBM16Encoding: | |
| return [ 'RGBM', '( value, 16.0 )' ]; | |
| case RGBDEncoding: | |
| return [ 'RGBD', '( value, 256.0 )' ]; | |
| case GammaEncoding: | |
| return [ 'Gamma', '( value, float( GAMMA_FACTOR ) )' ]; | |
| default: | |
| throw new Error( 'unsupported encoding: ' + encoding ); | |
| } | |
| } | |
| function getTexelDecodingFunction( functionName, encoding ) { | |
| var components = getEncodingComponents( encoding ); | |
| return 'vec4 ' + functionName + '( vec4 value ) { return ' + components[ 0 ] + 'ToLinear' + components[ 1 ] + '; }'; | |
| } | |
| function getTexelEncodingFunction( functionName, encoding ) { | |
| var components = getEncodingComponents( encoding ); | |
| return 'vec4 ' + functionName + '( vec4 value ) { return LinearTo' + components[ 0 ] + components[ 1 ] + '; }'; | |
| } | |
| function getToneMappingFunction( functionName, toneMapping ) { | |
| var toneMappingName; | |
| switch ( toneMapping ) { | |
| case LinearToneMapping: | |
| toneMappingName = 'Linear'; | |
| break; | |
| case ReinhardToneMapping: | |
| toneMappingName = 'Reinhard'; | |
| break; | |
| case Uncharted2ToneMapping: | |
| toneMappingName = 'Uncharted2'; | |
| break; | |
| case CineonToneMapping: | |
| toneMappingName = 'OptimizedCineon'; | |
| break; | |
| default: | |
| throw new Error( 'unsupported toneMapping: ' + toneMapping ); | |
| } | |
| return 'vec3 ' + functionName + '( vec3 color ) { return ' + toneMappingName + 'ToneMapping( color ); }'; | |
| } | |
| function generateExtensions( extensions, parameters, rendererExtensions ) { | |
| extensions = extensions || {}; | |
| var chunks = [ | |
| ( extensions.derivatives || parameters.envMapCubeUV || parameters.bumpMap || parameters.normalMap || parameters.flatShading ) ? '#extension GL_OES_standard_derivatives : enable' : '', | |
| ( extensions.fragDepth || parameters.logarithmicDepthBuffer ) && rendererExtensions.get( 'EXT_frag_depth' ) ? '#extension GL_EXT_frag_depth : enable' : '', | |
| ( extensions.drawBuffers ) && rendererExtensions.get( 'WEBGL_draw_buffers' ) ? '#extension GL_EXT_draw_buffers : require' : '', | |
| ( extensions.shaderTextureLOD || parameters.envMap ) && rendererExtensions.get( 'EXT_shader_texture_lod' ) ? '#extension GL_EXT_shader_texture_lod : enable' : '' | |
| ]; | |
| return chunks.filter( filterEmptyLine ).join( '\n' ); | |
| } | |
| function generateDefines( defines ) { | |
| var chunks = []; | |
| for ( var name in defines ) { | |
| var value = defines[ name ]; | |
| if ( value === false ) continue; | |
| chunks.push( '#define ' + name + ' ' + value ); | |
| } | |
| return chunks.join( '\n' ); | |
| } | |
| function fetchAttributeLocations( gl, program ) { | |
| var attributes = {}; | |
| var n = gl.getProgramParameter( program, gl.ACTIVE_ATTRIBUTES ); | |
| for ( var i = 0; i < n; i ++ ) { | |
| var info = gl.getActiveAttrib( program, i ); | |
| var name = info.name; | |
| // console.log( 'THREE.WebGLProgram: ACTIVE VERTEX ATTRIBUTE:', name, i ); | |
| attributes[ name ] = gl.getAttribLocation( program, name ); | |
| } | |
| return attributes; | |
| } | |
| function filterEmptyLine( string ) { | |
| return string !== ''; | |
| } | |
| function replaceLightNums( string, parameters ) { | |
| return string | |
| .replace( /NUM_DIR_LIGHTS/g, parameters.numDirLights ) | |
| .replace( /NUM_SPOT_LIGHTS/g, parameters.numSpotLights ) | |
| .replace( /NUM_RECT_AREA_LIGHTS/g, parameters.numRectAreaLights ) | |
| .replace( /NUM_POINT_LIGHTS/g, parameters.numPointLights ) | |
| .replace( /NUM_HEMI_LIGHTS/g, parameters.numHemiLights ); | |
| } | |
| function replaceClippingPlaneNums( string, parameters ) { | |
| return string | |
| .replace( /NUM_CLIPPING_PLANES/g, parameters.numClippingPlanes ) | |
| .replace( /UNION_CLIPPING_PLANES/g, ( parameters.numClippingPlanes - parameters.numClipIntersection ) ); | |
| } | |
| function parseIncludes( string ) { | |
| var pattern = /^[ \t]*#include +<([\w\d.]+)>/gm; | |
| function replace( match, include ) { | |
| var replace = ShaderChunk[ include ]; | |
| if ( replace === undefined ) { | |
| throw new Error( 'Can not resolve #include <' + include + '>' ); | |
| } | |
| return parseIncludes( replace ); | |
| } | |
| return string.replace( pattern, replace ); | |
| } | |
| function unrollLoops( string ) { | |
| var pattern = /#pragma unroll_loop[\s]+?for \( int i \= (\d+)\; i < (\d+)\; i \+\+ \) \{([\s\S]+?)(?=\})\}/g; | |
| function replace( match, start, end, snippet ) { | |
| var unroll = ''; | |
| for ( var i = parseInt( start ); i < parseInt( end ); i ++ ) { | |
| unroll += snippet.replace( /\[ i \]/g, '[ ' + i + ' ]' ); | |
| } | |
| return unroll; | |
| } | |
| return string.replace( pattern, replace ); | |
| } | |
| function WebGLProgram( renderer, extensions, code, material, shader, parameters ) { | |
| var gl = renderer.context; | |
| var defines = material.defines; | |
| var vertexShader = shader.vertexShader; | |
| var fragmentShader = shader.fragmentShader; | |
| var shadowMapTypeDefine = 'SHADOWMAP_TYPE_BASIC'; | |
| if ( parameters.shadowMapType === PCFShadowMap ) { | |
| shadowMapTypeDefine = 'SHADOWMAP_TYPE_PCF'; | |
| } else if ( parameters.shadowMapType === PCFSoftShadowMap ) { | |
| shadowMapTypeDefine = 'SHADOWMAP_TYPE_PCF_SOFT'; | |
| } | |
| var envMapTypeDefine = 'ENVMAP_TYPE_CUBE'; | |
| var envMapModeDefine = 'ENVMAP_MODE_REFLECTION'; | |
| var envMapBlendingDefine = 'ENVMAP_BLENDING_MULTIPLY'; | |
| if ( parameters.envMap ) { | |
| switch ( material.envMap.mapping ) { | |
| case CubeReflectionMapping: | |
| case CubeRefractionMapping: | |
| envMapTypeDefine = 'ENVMAP_TYPE_CUBE'; | |
| break; | |
| case CubeUVReflectionMapping: | |
| case CubeUVRefractionMapping: | |
| envMapTypeDefine = 'ENVMAP_TYPE_CUBE_UV'; | |
| break; | |
| case EquirectangularReflectionMapping: | |
| case EquirectangularRefractionMapping: | |
| envMapTypeDefine = 'ENVMAP_TYPE_EQUIREC'; | |
| break; | |
| case SphericalReflectionMapping: | |
| envMapTypeDefine = 'ENVMAP_TYPE_SPHERE'; | |
| break; | |
| } | |
| switch ( material.envMap.mapping ) { | |
| case CubeRefractionMapping: | |
| case EquirectangularRefractionMapping: | |
| envMapModeDefine = 'ENVMAP_MODE_REFRACTION'; | |
| break; | |
| } | |
| switch ( material.combine ) { | |
| case MultiplyOperation: | |
| envMapBlendingDefine = 'ENVMAP_BLENDING_MULTIPLY'; | |
| break; | |
| case MixOperation: | |
| envMapBlendingDefine = 'ENVMAP_BLENDING_MIX'; | |
| break; | |
| case AddOperation: | |
| envMapBlendingDefine = 'ENVMAP_BLENDING_ADD'; | |
| break; | |
| } | |
| } | |
| var gammaFactorDefine = ( renderer.gammaFactor > 0 ) ? renderer.gammaFactor : 1.0; | |
| // console.log( 'building new program ' ); | |
| // | |
| var customExtensions = generateExtensions( material.extensions, parameters, extensions ); | |
| var customDefines = generateDefines( defines ); | |
| // | |
| var program = gl.createProgram(); | |
| var prefixVertex, prefixFragment; | |
| if ( material.isRawShaderMaterial ) { | |
| prefixVertex = [ | |
| customDefines | |
| ].filter( filterEmptyLine ).join( '\n' ); | |
| if ( prefixVertex.length > 0 ) { | |
| prefixVertex += '\n'; | |
| } | |
| prefixFragment = [ | |
| customExtensions, | |
| customDefines | |
| ].filter( filterEmptyLine ).join( '\n' ); | |
| if ( prefixFragment.length > 0 ) { | |
| prefixFragment += '\n'; | |
| } | |
| } else { | |
| prefixVertex = [ | |
| 'precision ' + parameters.precision + ' float;', | |
| 'precision ' + parameters.precision + ' int;', | |
| '#define SHADER_NAME ' + shader.name, | |
| customDefines, | |
| parameters.supportsVertexTextures ? '#define VERTEX_TEXTURES' : '', | |
| '#define GAMMA_FACTOR ' + gammaFactorDefine, | |
| '#define MAX_BONES ' + parameters.maxBones, | |
| ( parameters.useFog && parameters.fog ) ? '#define USE_FOG' : '', | |
| ( parameters.useFog && parameters.fogExp ) ? '#define FOG_EXP2' : '', | |
| parameters.map ? '#define USE_MAP' : '', | |
| parameters.envMap ? '#define USE_ENVMAP' : '', | |
| parameters.envMap ? '#define ' + envMapModeDefine : '', | |
| parameters.lightMap ? '#define USE_LIGHTMAP' : '', | |
| parameters.aoMap ? '#define USE_AOMAP' : '', | |
| parameters.emissiveMap ? '#define USE_EMISSIVEMAP' : '', | |
| parameters.bumpMap ? '#define USE_BUMPMAP' : '', | |
| parameters.normalMap ? '#define USE_NORMALMAP' : '', | |
| parameters.displacementMap && parameters.supportsVertexTextures ? '#define USE_DISPLACEMENTMAP' : '', | |
| parameters.specularMap ? '#define USE_SPECULARMAP' : '', | |
| parameters.roughnessMap ? '#define USE_ROUGHNESSMAP' : '', | |
| parameters.metalnessMap ? '#define USE_METALNESSMAP' : '', | |
| parameters.alphaMap ? '#define USE_ALPHAMAP' : '', | |
| parameters.vertexColors ? '#define USE_COLOR' : '', | |
| parameters.flatShading ? '#define FLAT_SHADED' : '', | |
| parameters.skinning ? '#define USE_SKINNING' : '', | |
| parameters.useVertexTexture ? '#define BONE_TEXTURE' : '', | |
| parameters.morphTargets ? '#define USE_MORPHTARGETS' : '', | |
| parameters.morphNormals && parameters.flatShading === false ? '#define USE_MORPHNORMALS' : '', | |
| parameters.doubleSided ? '#define DOUBLE_SIDED' : '', | |
| parameters.flipSided ? '#define FLIP_SIDED' : '', | |
| parameters.shadowMapEnabled ? '#define USE_SHADOWMAP' : '', | |
| parameters.shadowMapEnabled ? '#define ' + shadowMapTypeDefine : '', | |
| parameters.sizeAttenuation ? '#define USE_SIZEATTENUATION' : '', | |
| parameters.logarithmicDepthBuffer ? '#define USE_LOGDEPTHBUF' : '', | |
| parameters.logarithmicDepthBuffer && extensions.get( 'EXT_frag_depth' ) ? '#define USE_LOGDEPTHBUF_EXT' : '', | |
| 'uniform mat4 modelMatrix;', | |
| 'uniform mat4 modelViewMatrix;', | |
| 'uniform mat4 projectionMatrix;', | |
| 'uniform mat4 viewMatrix;', | |
| 'uniform mat3 normalMatrix;', | |
| 'uniform vec3 cameraPosition;', | |
| 'attribute vec3 position;', | |
| 'attribute vec3 normal;', | |
| 'attribute vec2 uv;', | |
| '#ifdef USE_COLOR', | |
| ' attribute vec3 color;', | |
| '#endif', | |
| '#ifdef USE_MORPHTARGETS', | |
| ' attribute vec3 morphTarget0;', | |
| ' attribute vec3 morphTarget1;', | |
| ' attribute vec3 morphTarget2;', | |
| ' attribute vec3 morphTarget3;', | |
| ' #ifdef USE_MORPHNORMALS', | |
| ' attribute vec3 morphNormal0;', | |
| ' attribute vec3 morphNormal1;', | |
| ' attribute vec3 morphNormal2;', | |
| ' attribute vec3 morphNormal3;', | |
| ' #else', | |
| ' attribute vec3 morphTarget4;', | |
| ' attribute vec3 morphTarget5;', | |
| ' attribute vec3 morphTarget6;', | |
| ' attribute vec3 morphTarget7;', | |
| ' #endif', | |
| '#endif', | |
| '#ifdef USE_SKINNING', | |
| ' attribute vec4 skinIndex;', | |
| ' attribute vec4 skinWeight;', | |
| '#endif', | |
| '\n' | |
| ].filter( filterEmptyLine ).join( '\n' ); | |
| prefixFragment = [ | |
| customExtensions, | |
| 'precision ' + parameters.precision + ' float;', | |
| 'precision ' + parameters.precision + ' int;', | |
| '#define SHADER_NAME ' + shader.name, | |
| customDefines, | |
| parameters.alphaTest ? '#define ALPHATEST ' + parameters.alphaTest : '', | |
| '#define GAMMA_FACTOR ' + gammaFactorDefine, | |
| ( parameters.useFog && parameters.fog ) ? '#define USE_FOG' : '', | |
| ( parameters.useFog && parameters.fogExp ) ? '#define FOG_EXP2' : '', | |
| parameters.map ? '#define USE_MAP' : '', | |
| parameters.envMap ? '#define USE_ENVMAP' : '', | |
| parameters.envMap ? '#define ' + envMapTypeDefine : '', | |
| parameters.envMap ? '#define ' + envMapModeDefine : '', | |
| parameters.envMap ? '#define ' + envMapBlendingDefine : '', | |
| parameters.lightMap ? '#define USE_LIGHTMAP' : '', | |
| parameters.aoMap ? '#define USE_AOMAP' : '', | |
| parameters.emissiveMap ? '#define USE_EMISSIVEMAP' : '', | |
| parameters.bumpMap ? '#define USE_BUMPMAP' : '', | |
| parameters.normalMap ? '#define USE_NORMALMAP' : '', | |
| parameters.specularMap ? '#define USE_SPECULARMAP' : '', | |
| parameters.roughnessMap ? '#define USE_ROUGHNESSMAP' : '', | |
| parameters.metalnessMap ? '#define USE_METALNESSMAP' : '', | |
| parameters.alphaMap ? '#define USE_ALPHAMAP' : '', | |
| parameters.vertexColors ? '#define USE_COLOR' : '', | |
| parameters.gradientMap ? '#define USE_GRADIENTMAP' : '', | |
| parameters.flatShading ? '#define FLAT_SHADED' : '', | |
| parameters.doubleSided ? '#define DOUBLE_SIDED' : '', | |
| parameters.flipSided ? '#define FLIP_SIDED' : '', | |
| parameters.shadowMapEnabled ? '#define USE_SHADOWMAP' : '', | |
| parameters.shadowMapEnabled ? '#define ' + shadowMapTypeDefine : '', | |
| parameters.premultipliedAlpha ? '#define PREMULTIPLIED_ALPHA' : '', | |
| parameters.physicallyCorrectLights ? '#define PHYSICALLY_CORRECT_LIGHTS' : '', | |
| parameters.logarithmicDepthBuffer ? '#define USE_LOGDEPTHBUF' : '', | |
| parameters.logarithmicDepthBuffer && extensions.get( 'EXT_frag_depth' ) ? '#define USE_LOGDEPTHBUF_EXT' : '', | |
| parameters.envMap && extensions.get( 'EXT_shader_texture_lod' ) ? '#define TEXTURE_LOD_EXT' : '', | |
| 'uniform mat4 viewMatrix;', | |
| 'uniform vec3 cameraPosition;', | |
| ( parameters.toneMapping !== NoToneMapping ) ? '#define TONE_MAPPING' : '', | |
| ( parameters.toneMapping !== NoToneMapping ) ? ShaderChunk[ 'tonemapping_pars_fragment' ] : '', // this code is required here because it is used by the toneMapping() function defined below | |
| ( parameters.toneMapping !== NoToneMapping ) ? getToneMappingFunction( 'toneMapping', parameters.toneMapping ) : '', | |
| parameters.dithering ? '#define DITHERING' : '', | |
| ( parameters.outputEncoding || parameters.mapEncoding || parameters.envMapEncoding || parameters.emissiveMapEncoding ) ? ShaderChunk[ 'encodings_pars_fragment' ] : '', // this code is required here because it is used by the various encoding/decoding function defined below | |
| parameters.mapEncoding ? getTexelDecodingFunction( 'mapTexelToLinear', parameters.mapEncoding ) : '', | |
| parameters.envMapEncoding ? getTexelDecodingFunction( 'envMapTexelToLinear', parameters.envMapEncoding ) : '', | |
| parameters.emissiveMapEncoding ? getTexelDecodingFunction( 'emissiveMapTexelToLinear', parameters.emissiveMapEncoding ) : '', | |
| parameters.outputEncoding ? getTexelEncodingFunction( 'linearToOutputTexel', parameters.outputEncoding ) : '', | |
| parameters.depthPacking ? '#define DEPTH_PACKING ' + material.depthPacking : '', | |
| '\n' | |
| ].filter( filterEmptyLine ).join( '\n' ); | |
| } | |
| vertexShader = parseIncludes( vertexShader ); | |
| vertexShader = replaceLightNums( vertexShader, parameters ); | |
| vertexShader = replaceClippingPlaneNums( vertexShader, parameters ); | |
| fragmentShader = parseIncludes( fragmentShader ); | |
| fragmentShader = replaceLightNums( fragmentShader, parameters ); | |
| fragmentShader = replaceClippingPlaneNums( fragmentShader, parameters ); | |
| vertexShader = unrollLoops( vertexShader ); | |
| fragmentShader = unrollLoops( fragmentShader ); | |
| var vertexGlsl = prefixVertex + vertexShader; | |
| var fragmentGlsl = prefixFragment + fragmentShader; | |
| // console.log( '*VERTEX*', vertexGlsl ); | |
| // console.log( '*FRAGMENT*', fragmentGlsl ); | |
| var glVertexShader = WebGLShader( gl, gl.VERTEX_SHADER, vertexGlsl ); | |
| var glFragmentShader = WebGLShader( gl, gl.FRAGMENT_SHADER, fragmentGlsl ); | |
| gl.attachShader( program, glVertexShader ); | |
| gl.attachShader( program, glFragmentShader ); | |
| // Force a particular attribute to index 0. | |
| if ( material.index0AttributeName !== undefined ) { | |
| gl.bindAttribLocation( program, 0, material.index0AttributeName ); | |
| } else if ( parameters.morphTargets === true ) { | |
| // programs with morphTargets displace position out of attribute 0 | |
| gl.bindAttribLocation( program, 0, 'position' ); | |
| } | |
| gl.linkProgram( program ); | |
| var programLog = gl.getProgramInfoLog( program ).trim(); | |
| var vertexLog = gl.getShaderInfoLog( glVertexShader ).trim(); | |
| var fragmentLog = gl.getShaderInfoLog( glFragmentShader ).trim(); | |
| var runnable = true; | |
| var haveDiagnostics = true; | |
| // console.log( '**VERTEX**', gl.getExtension( 'WEBGL_debug_shaders' ).getTranslatedShaderSource( glVertexShader ) ); | |
| // console.log( '**FRAGMENT**', gl.getExtension( 'WEBGL_debug_shaders' ).getTranslatedShaderSource( glFragmentShader ) ); | |
| if ( gl.getProgramParameter( program, gl.LINK_STATUS ) === false ) { | |
| runnable = false; | |
| console.error( 'THREE.WebGLProgram: shader error: ', gl.getError(), 'gl.VALIDATE_STATUS', gl.getProgramParameter( program, gl.VALIDATE_STATUS ), 'gl.getProgramInfoLog', programLog, vertexLog, fragmentLog ); | |
| } else if ( programLog !== '' ) { | |
| console.warn( 'THREE.WebGLProgram: gl.getProgramInfoLog()', programLog ); | |
| } else if ( vertexLog === '' || fragmentLog === '' ) { | |
| haveDiagnostics = false; | |
| } | |
| if ( haveDiagnostics ) { | |
| this.diagnostics = { | |
| runnable: runnable, | |
| material: material, | |
| programLog: programLog, | |
| vertexShader: { | |
| log: vertexLog, | |
| prefix: prefixVertex | |
| }, | |
| fragmentShader: { | |
| log: fragmentLog, | |
| prefix: prefixFragment | |
| } | |
| }; | |
| } | |
| // clean up | |
| gl.deleteShader( glVertexShader ); | |
| gl.deleteShader( glFragmentShader ); | |
| // set up caching for uniform locations | |
| var cachedUniforms; | |
| this.getUniforms = function () { | |
| if ( cachedUniforms === undefined ) { | |
| cachedUniforms = new WebGLUniforms( gl, program, renderer ); | |
| } | |
| return cachedUniforms; | |
| }; | |
| // set up caching for attribute locations | |
| var cachedAttributes; | |
| this.getAttributes = function () { | |
| if ( cachedAttributes === undefined ) { | |
| cachedAttributes = fetchAttributeLocations( gl, program ); | |
| } | |
| return cachedAttributes; | |
| }; | |
| // free resource | |
| this.destroy = function () { | |
| gl.deleteProgram( program ); | |
| this.program = undefined; | |
| }; | |
| // DEPRECATED | |
| Object.defineProperties( this, { | |
| uniforms: { | |
| get: function () { | |
| console.warn( 'THREE.WebGLProgram: .uniforms is now .getUniforms().' ); | |
| return this.getUniforms(); | |
| } | |
| }, | |
| attributes: { | |
| get: function () { | |
| console.warn( 'THREE.WebGLProgram: .attributes is now .getAttributes().' ); | |
| return this.getAttributes(); | |
| } | |
| } | |
| } ); | |
| // | |
| this.id = programIdCount ++; | |
| this.code = code; | |
| this.usedTimes = 1; | |
| this.program = program; | |
| this.vertexShader = glVertexShader; | |
| this.fragmentShader = glFragmentShader; | |
| return this; | |
| } | |
| /** | |
| * @author mrdoob / http://mrdoob.com/ | |
| */ | |
| function WebGLPrograms( renderer, extensions, capabilities ) { | |
| var programs = []; | |
| var shaderIDs = { | |
| MeshDepthMaterial: 'depth', | |
| MeshDistanceMaterial: 'distanceRGBA', | |
| MeshNormalMaterial: 'normal', | |
| MeshBasicMaterial: 'basic', | |
| MeshLambertMaterial: 'lambert', | |
| MeshPhongMaterial: 'phong', | |
| MeshToonMaterial: 'phong', | |
| MeshStandardMaterial: 'physical', | |
| MeshPhysicalMaterial: 'physical', | |
| LineBasicMaterial: 'basic', | |
| LineDashedMaterial: 'dashed', | |
| PointsMaterial: 'points', | |
| ShadowMaterial: 'shadow' | |
| }; | |
| var parameterNames = [ | |
| "precision", "supportsVertexTextures", "map", "mapEncoding", "envMap", "envMapMode", "envMapEncoding", | |
| "lightMap", "aoMap", "emissiveMap", "emissiveMapEncoding", "bumpMap", "normalMap", "displacementMap", "specularMap", | |
| "roughnessMap", "metalnessMap", "gradientMap", | |
| "alphaMap", "combine", "vertexColors", "fog", "useFog", "fogExp", | |
| "flatShading", "sizeAttenuation", "logarithmicDepthBuffer", "skinning", | |
| "maxBones", "useVertexTexture", "morphTargets", "morphNormals", | |
| "maxMorphTargets", "maxMorphNormals", "premultipliedAlpha", | |
| "numDirLights", "numPointLights", "numSpotLights", "numHemiLights", "numRectAreaLights", | |
| "shadowMapEnabled", "shadowMapType", "toneMapping", 'physicallyCorrectLights', | |
| "alphaTest", "doubleSided", "flipSided", "numClippingPlanes", "numClipIntersection", "depthPacking", "dithering" | |
| ]; | |
| function allocateBones( object ) { | |
| var skeleton = object.skeleton; | |
| var bones = skeleton.bones; | |
| if ( capabilities.floatVertexTextures ) { | |
| return 1024; | |
| } else { | |
| // default for when object is not specified | |
| // ( for example when prebuilding shader to be used with multiple objects ) | |
| // | |
| // - leave some extra space for other uniforms | |
| // - limit here is ANGLE's 254 max uniform vectors | |
| // (up to 54 should be safe) | |
| var nVertexUniforms = capabilities.maxVertexUniforms; | |
| var nVertexMatrices = Math.floor( ( nVertexUniforms - 20 ) / 4 ); | |
| var maxBones = Math.min( nVertexMatrices, bones.length ); | |
| if ( maxBones < bones.length ) { | |
| console.warn( 'THREE.WebGLRenderer: Skeleton has ' + bones.length + ' bones. This GPU supports ' + maxBones + '.' ); | |
| return 0; | |
| } | |
| return maxBones; | |
| } | |
| } | |
| function getTextureEncodingFromMap( map, gammaOverrideLinear ) { | |
| var encoding; | |
| if ( ! map ) { | |
| encoding = LinearEncoding; | |
| } else if ( map.isTexture ) { | |
| encoding = map.encoding; | |
| } else if ( map.isWebGLRenderTarget ) { | |
| console.warn( "THREE.WebGLPrograms.getTextureEncodingFromMap: don't use render targets as textures. Use their .texture property instead." ); | |
| encoding = map.texture.encoding; | |
| } | |
| // add backwards compatibility for WebGLRenderer.gammaInput/gammaOutput parameter, should probably be removed at some point. | |
| if ( encoding === LinearEncoding && gammaOverrideLinear ) { | |
| encoding = GammaEncoding; | |
| } | |
| return encoding; | |
| } | |
| this.getParameters = function ( material, lights, shadows, fog, nClipPlanes, nClipIntersection, object ) { | |
| var shaderID = shaderIDs[ material.type ]; | |
| // heuristics to create shader parameters according to lights in the scene | |
| // (not to blow over maxLights budget) | |
| var maxBones = object.isSkinnedMesh ? allocateBones( object ) : 0; | |
| var precision = capabilities.precision; | |
| if ( material.precision !== null ) { | |
| precision = capabilities.getMaxPrecision( material.precision ); | |
| if ( precision !== material.precision ) { | |
| console.warn( 'THREE.WebGLProgram.getParameters:', material.precision, 'not supported, using', precision, 'instead.' ); | |
| } | |
| } | |
| var currentRenderTarget = renderer.getRenderTarget(); | |
| var parameters = { | |
| shaderID: shaderID, | |
| precision: precision, | |
| supportsVertexTextures: capabilities.vertexTextures, | |
| outputEncoding: getTextureEncodingFromMap( ( ! currentRenderTarget ) ? null : currentRenderTarget.texture, renderer.gammaOutput ), | |
| map: !! material.map, | |
| mapEncoding: getTextureEncodingFromMap( material.map, renderer.gammaInput ), | |
| envMap: !! material.envMap, | |
| envMapMode: material.envMap && material.envMap.mapping, | |
| envMapEncoding: getTextureEncodingFromMap( material.envMap, renderer.gammaInput ), | |
| envMapCubeUV: ( !! material.envMap ) && ( ( material.envMap.mapping === CubeUVReflectionMapping ) || ( material.envMap.mapping === CubeUVRefractionMapping ) ), | |
| lightMap: !! material.lightMap, | |
| aoMap: !! material.aoMap, | |
| emissiveMap: !! material.emissiveMap, | |
| emissiveMapEncoding: getTextureEncodingFromMap( material.emissiveMap, renderer.gammaInput ), | |
| bumpMap: !! material.bumpMap, | |
| normalMap: !! material.normalMap, | |
| displacementMap: !! material.displacementMap, | |
| roughnessMap: !! material.roughnessMap, | |
| metalnessMap: !! material.metalnessMap, | |
| specularMap: !! material.specularMap, | |
| alphaMap: !! material.alphaMap, | |
| gradientMap: !! material.gradientMap, | |
| combine: material.combine, | |
| vertexColors: material.vertexColors, | |
| fog: !! fog, | |
| useFog: material.fog, | |
| fogExp: ( fog && fog.isFogExp2 ), | |
| flatShading: material.flatShading, | |
| sizeAttenuation: material.sizeAttenuation, | |
| logarithmicDepthBuffer: capabilities.logarithmicDepthBuffer, | |
| skinning: material.skinning && maxBones > 0, | |
| maxBones: maxBones, | |
| useVertexTexture: capabilities.floatVertexTextures, | |
| morphTargets: material.morphTargets, | |
| morphNormals: material.morphNormals, | |
| maxMorphTargets: renderer.maxMorphTargets, | |
| maxMorphNormals: renderer.maxMorphNormals, | |
| numDirLights: lights.directional.length, | |
| numPointLights: lights.point.length, | |
| numSpotLights: lights.spot.length, | |
| numRectAreaLights: lights.rectArea.length, | |
| numHemiLights: lights.hemi.length, | |
| numClippingPlanes: nClipPlanes, | |
| numClipIntersection: nClipIntersection, | |
| dithering: material.dithering, | |
| shadowMapEnabled: renderer.shadowMap.enabled && object.receiveShadow && shadows.length > 0, | |
| shadowMapType: renderer.shadowMap.type, | |
| toneMapping: renderer.toneMapping, | |
| physicallyCorrectLights: renderer.physicallyCorrectLights, | |
| premultipliedAlpha: material.premultipliedAlpha, | |
| alphaTest: material.alphaTest, | |
| doubleSided: material.side === DoubleSide, | |
| flipSided: material.side === BackSide, | |
| depthPacking: ( material.depthPacking !== undefined ) ? material.depthPacking : false | |
| }; | |
| return parameters; | |
| }; | |
| this.getProgramCode = function ( material, parameters ) { | |
| var array = []; | |
| if ( parameters.shaderID ) { | |
| array.push( parameters.shaderID ); | |
| } else { | |
| array.push( material.fragmentShader ); | |
| array.push( material.vertexShader ); | |
| } | |
| if ( material.defines !== undefined ) { | |
| for ( var name in material.defines ) { | |
| array.push( name ); | |
| array.push( material.defines[ name ] ); | |
| } | |
| } | |
| for ( var i = 0; i < parameterNames.length; i ++ ) { | |
| array.push( parameters[ parameterNames[ i ] ] ); | |
| } | |
| array.push( material.onBeforeCompile.toString() ); | |
| array.push( renderer.gammaOutput ); | |
| return array.join(); | |
| }; | |
| this.acquireProgram = function ( material, shader, parameters, code ) { | |
| var program; | |
| // Check if code has been already compiled | |
| for ( var p = 0, pl = programs.length; p < pl; p ++ ) { | |
| var programInfo = programs[ p ]; | |
| if ( programInfo.code === code ) { | |
| program = programInfo; | |
| ++ program.usedTimes; | |
| break; | |
| } | |
| } | |
| if ( program === undefined ) { | |
| program = new WebGLProgram( renderer, extensions, code, material, shader, parameters ); | |
| programs.push( program ); | |
| } | |
| return program; | |
| }; | |
| this.releaseProgram = function ( program ) { | |
| if ( -- program.usedTimes === 0 ) { | |
| // Remove from unordered set | |
| var i = programs.indexOf( program ); | |
| programs[ i ] = programs[ programs.length - 1 ]; | |
| programs.pop(); | |
| // Free WebGL resources | |
| program.destroy(); | |
| } | |
| }; | |
| // Exposed for resource monitoring & error feedback via renderer.info: | |
| this.programs = programs; | |
| } | |
| /** | |
| * @author fordacious / fordacious.github.io | |
| */ | |
| function WebGLProperties() { | |
| var properties = new WeakMap(); | |
| function get( object ) { | |
| if ( properties.has( object ) === false ) { | |
| properties.set( object, {} ); | |
| } | |
| return properties.get( object ); | |
| } | |
| function remove( object ) { | |
| properties.delete( object ); | |
| } | |
| function update( object, key, value ) { | |
| properties.get( object )[ key ] = value; | |
| } | |
| function dispose() { | |
| properties = new WeakMap(); | |
| } | |
| return { | |
| get: get, | |
| remove: remove, | |
| update: update, | |
| dispose: dispose | |
| }; | |
| } | |
| /** | |
| * @author mrdoob / http://mrdoob.com/ | |
| */ | |
| function painterSortStable( a, b ) { | |
| if ( a.renderOrder !== b.renderOrder ) { | |
| return a.renderOrder - b.renderOrder; | |
| } else if ( a.program && b.program && a.program !== b.program ) { | |
| return a.program.id - b.program.id; | |
| } else if ( a.material.id !== b.material.id ) { | |
| return a.material.id - b.material.id; | |
| } else if ( a.z !== b.z ) { | |
| return a.z - b.z; | |
| } else { | |
| return a.id - b.id; | |
| } | |
| } | |
| function reversePainterSortStable( a, b ) { | |
| if ( a.renderOrder !== b.renderOrder ) { | |
| return a.renderOrder - b.renderOrder; | |
| } if ( a.z !== b.z ) { | |
| return b.z - a.z; | |
| } else { | |
| return a.id - b.id; | |
| } | |
| } | |
| function WebGLRenderList() { | |
| var renderItems = []; | |
| var renderItemsIndex = 0; | |
| var opaque = []; | |
| var transparent = []; | |
| function init() { | |
| renderItemsIndex = 0; | |
| opaque.length = 0; | |
| transparent.length = 0; | |
| } | |
| function push( object, geometry, material, z, group ) { | |
| var renderItem = renderItems[ renderItemsIndex ]; | |
| if ( renderItem === undefined ) { | |
| renderItem = { | |
| id: object.id, | |
| object: object, | |
| geometry: geometry, | |
| material: material, | |
| program: material.program, | |
| renderOrder: object.renderOrder, | |
| z: z, | |
| group: group | |
| }; | |
| renderItems[ renderItemsIndex ] = renderItem; | |
| } else { | |
| renderItem.id = object.id; | |
| renderItem.object = object; | |
| renderItem.geometry = geometry; | |
| renderItem.material = material; | |
| renderItem.program = material.program; | |
| renderItem.renderOrder = object.renderOrder; | |
| renderItem.z = z; | |
| renderItem.group = group; | |
| } | |
| ( material.transparent === true ? transparent : opaque ).push( renderItem ); | |
| renderItemsIndex ++; | |
| } | |
| function sort() { | |
| if ( opaque.length > 1 ) opaque.sort( painterSortStable ); | |
| if ( transparent.length > 1 ) transparent.sort( reversePainterSortStable ); | |
| } | |
| return { | |
| opaque: opaque, | |
| transparent: transparent, | |
| init: init, | |
| push: push, | |
| sort: sort | |
| }; | |
| } | |
| function WebGLRenderLists() { | |
| var lists = {}; | |
| function get( scene, camera ) { | |
| var hash = scene.id + ',' + camera.id; | |
| var list = lists[ hash ]; | |
| if ( list === undefined ) { | |
| // console.log( 'THREE.WebGLRenderLists:', hash ); | |
| list = new WebGLRenderList(); | |
| lists[ hash ] = list; | |
| } | |
| return list; | |
| } | |
| function dispose() { | |
| lists = {}; | |
| } | |
| return { | |
| get: get, | |
| dispose: dispose | |
| }; | |
| } | |
| /** | |
| * @author mrdoob / http://mrdoob.com/ | |
| */ | |
| function UniformsCache() { | |
| var lights = {}; | |
| return { | |
| get: function ( light ) { | |
| if ( lights[ light.id ] !== undefined ) { | |
| return lights[ light.id ]; | |
| } | |
| var uniforms; | |
| switch ( light.type ) { | |
| case 'DirectionalLight': | |
| uniforms = { | |
| direction: new Vector3(), | |
| color: new Color(), | |
| shadow: false, | |
| shadowBias: 0, | |
| shadowRadius: 1, | |
| shadowMapSize: new Vector2() | |
| }; | |
| break; | |
| case 'SpotLight': | |
| uniforms = { | |
| position: new Vector3(), | |
| direction: new Vector3(), | |
| color: new Color(), | |
| distance: 0, | |
| coneCos: 0, | |
| penumbraCos: 0, | |
| decay: 0, | |
| shadow: false, | |
| shadowBias: 0, | |
| shadowRadius: 1, | |
| shadowMapSize: new Vector2() | |
| }; | |
| break; | |
| case 'PointLight': | |
| uniforms = { | |
| position: new Vector3(), | |
| color: new Color(), | |
| distance: 0, | |
| decay: 0, | |
| shadow: false, | |
| shadowBias: 0, | |
| shadowRadius: 1, | |
| shadowMapSize: new Vector2(), | |
| shadowCameraNear: 1, | |
| shadowCameraFar: 1000 | |
| }; | |
| break; | |
| case 'HemisphereLight': | |
| uniforms = { | |
| direction: new Vector3(), | |
| skyColor: new Color(), | |
| groundColor: new Color() | |
| }; | |
| break; | |
| case 'RectAreaLight': | |
| uniforms = { | |
| color: new Color(), | |
| position: new Vector3(), | |
| halfWidth: new Vector3(), | |
| halfHeight: new Vector3() | |
| // TODO (abelnation): set RectAreaLight shadow uniforms | |
| }; | |
| break; | |
| } | |
| lights[ light.id ] = uniforms; | |
| return uniforms; | |
| } | |
| }; | |
| } | |
| var count = 0; | |
| function WebGLLights() { | |
| var cache = new UniformsCache(); | |
| var state = { | |
| id: count ++, | |
| hash: '', | |
| ambient: [ 0, 0, 0 ], | |
| directional: [], | |
| directionalShadowMap: [], | |
| directionalShadowMatrix: [], | |
| spot: [], | |
| spotShadowMap: [], | |
| spotShadowMatrix: [], | |
| rectArea: [], | |
| point: [], | |
| pointShadowMap: [], | |
| pointShadowMatrix: [], | |
| hemi: [] | |
| }; | |
| var vector3 = new Vector3(); | |
| var matrix4 = new Matrix4(); | |
| var matrix42 = new Matrix4(); | |
| function setup( lights, shadows, camera ) { | |
| var r = 0, g = 0, b = 0; | |
| var directionalLength = 0; | |
| var pointLength = 0; | |
| var spotLength = 0; | |
| var rectAreaLength = 0; | |
| var hemiLength = 0; | |
| var viewMatrix = camera.matrixWorldInverse; | |
| for ( var i = 0, l = lights.length; i < l; i ++ ) { | |
| var light = lights[ i ]; | |
| var color = light.color; | |
| var intensity = light.intensity; | |
| var distance = light.distance; | |
| var shadowMap = ( light.shadow && light.shadow.map ) ? light.shadow.map.texture : null; | |
| if ( light.isAmbientLight ) { | |
| r += color.r * intensity; | |
| g += color.g * intensity; | |
| b += color.b * intensity; | |
| } else if ( light.isDirectionalLight ) { | |
| var uniforms = cache.get( light ); | |
| uniforms.color.copy( light.color ).multiplyScalar( light.intensity ); | |
| uniforms.direction.setFromMatrixPosition( light.matrixWorld ); | |
| vector3.setFromMatrixPosition( light.target.matrixWorld ); | |
| uniforms.direction.sub( vector3 ); | |
| uniforms.direction.transformDirection( viewMatrix ); | |
| uniforms.shadow = light.castShadow; | |
| if ( light.castShadow ) { | |
| var shadow = light.shadow; | |
| uniforms.shadowBias = shadow.bias; | |
| uniforms.shadowRadius = shadow.radius; | |
| uniforms.shadowMapSize = shadow.mapSize; | |
| } | |
| state.directionalShadowMap[ directionalLength ] = shadowMap; | |
| state.directionalShadowMatrix[ directionalLength ] = light.shadow.matrix; | |
| state.directional[ directionalLength ] = uniforms; | |
| directionalLength ++; | |
| } else if ( light.isSpotLight ) { | |
| var uniforms = cache.get( light ); | |
| uniforms.position.setFromMatrixPosition( light.matrixWorld ); | |
| uniforms.position.applyMatrix4( viewMatrix ); | |
| uniforms.color.copy( color ).multiplyScalar( intensity ); | |
| uniforms.distance = distance; | |
| uniforms.direction.setFromMatrixPosition( light.matrixWorld ); | |
| vector3.setFromMatrixPosition( light.target.matrixWorld ); | |
| uniforms.direction.sub( vector3 ); | |
| uniforms.direction.transformDirection( viewMatrix ); | |
| uniforms.coneCos = Math.cos( light.angle ); | |
| uniforms.penumbraCos = Math.cos( light.angle * ( 1 - light.penumbra ) ); | |
| uniforms.decay = ( light.distance === 0 ) ? 0.0 : light.decay; | |
| uniforms.shadow = light.castShadow; | |
| if ( light.castShadow ) { | |
| var shadow = light.shadow; | |
| uniforms.shadowBias = shadow.bias; | |
| uniforms.shadowRadius = shadow.radius; | |
| uniforms.shadowMapSize = shadow.mapSize; | |
| } | |
| state.spotShadowMap[ spotLength ] = shadowMap; | |
| state.spotShadowMatrix[ spotLength ] = light.shadow.matrix; | |
| state.spot[ spotLength ] = uniforms; | |
| spotLength ++; | |
| } else if ( light.isRectAreaLight ) { | |
| var uniforms = cache.get( light ); | |
| // (a) intensity is the total visible light emitted | |
| //uniforms.color.copy( color ).multiplyScalar( intensity / ( light.width * light.height * Math.PI ) ); | |
| // (b) intensity is the brightness of the light | |
| uniforms.color.copy( color ).multiplyScalar( intensity ); | |
| uniforms.position.setFromMatrixPosition( light.matrixWorld ); | |
| uniforms.position.applyMatrix4( viewMatrix ); | |
| // extract local rotation of light to derive width/height half vectors | |
| matrix42.identity(); | |
| matrix4.copy( light.matrixWorld ); | |
| matrix4.premultiply( viewMatrix ); | |
| matrix42.extractRotation( matrix4 ); | |
| uniforms.halfWidth.set( light.width * 0.5, 0.0, 0.0 ); | |
| uniforms.halfHeight.set( 0.0, light.height * 0.5, 0.0 ); | |
| uniforms.halfWidth.applyMatrix4( matrix42 ); | |
| uniforms.halfHeight.applyMatrix4( matrix42 ); | |
| // TODO (abelnation): RectAreaLight distance? | |
| // uniforms.distance = distance; | |
| state.rectArea[ rectAreaLength ] = uniforms; | |
| rectAreaLength ++; | |
| } else if ( light.isPointLight ) { | |
| var uniforms = cache.get( light ); | |
| uniforms.position.setFromMatrixPosition( light.matrixWorld ); | |
| uniforms.position.applyMatrix4( viewMatrix ); | |
| uniforms.color.copy( light.color ).multiplyScalar( light.intensity ); | |
| uniforms.distance = light.distance; | |
| uniforms.decay = ( light.distance === 0 ) ? 0.0 : light.decay; | |
| uniforms.shadow = light.castShadow; | |
| if ( light.castShadow ) { | |
| var shadow = light.shadow; | |
| uniforms.shadowBias = shadow.bias; | |
| uniforms.shadowRadius = shadow.radius; | |
| uniforms.shadowMapSize = shadow.mapSize; | |
| uniforms.shadowCameraNear = shadow.camera.near; | |
| uniforms.shadowCameraFar = shadow.camera.far; | |
| } | |
| state.pointShadowMap[ pointLength ] = shadowMap; | |
| state.pointShadowMatrix[ pointLength ] = light.shadow.matrix; | |
| state.point[ pointLength ] = uniforms; | |
| pointLength ++; | |
| } else if ( light.isHemisphereLight ) { | |
| var uniforms = cache.get( light ); | |
| uniforms.direction.setFromMatrixPosition( light.matrixWorld ); | |
| uniforms.direction.transformDirection( viewMatrix ); | |
| uniforms.direction.normalize(); | |
| uniforms.skyColor.copy( light.color ).multiplyScalar( intensity ); | |
| uniforms.groundColor.copy( light.groundColor ).multiplyScalar( intensity ); | |
| state.hemi[ hemiLength ] = uniforms; | |
| hemiLength ++; | |
| } | |
| } | |
| state.ambient[ 0 ] = r; | |
| state.ambient[ 1 ] = g; | |
| state.ambient[ 2 ] = b; | |
| state.directional.length = directionalLength; | |
| state.spot.length = spotLength; | |
| state.rectArea.length = rectAreaLength; | |
| state.point.length = pointLength; | |
| state.hemi.length = hemiLength; | |
| state.hash = state.id + ',' + directionalLength + ',' + pointLength + ',' + spotLength + ',' + rectAreaLength + ',' + hemiLength + ',' + shadows.length; | |
| } | |
| return { | |
| setup: setup, | |
| state: state | |
| }; | |
| } | |
| /** | |
| * @author Mugen87 / https://github.com/Mugen87 | |
| */ | |
| function WebGLRenderState() { | |
| var lights = new WebGLLights(); | |
| var lightsArray = []; | |
| var shadowsArray = []; | |
| var spritesArray = []; | |
| function init() { | |
| lightsArray.length = 0; | |
| shadowsArray.length = 0; | |
| spritesArray.length = 0; | |
| } | |
| function pushLight( light ) { | |
| lightsArray.push( light ); | |
| } | |
| function pushShadow( shadowLight ) { | |
| shadowsArray.push( shadowLight ); | |
| } | |
| function pushSprite( shadowLight ) { | |
| spritesArray.push( shadowLight ); | |
| } | |
| function setupLights( camera ) { | |
| lights.setup( lightsArray, shadowsArray, camera ); | |
| } | |
| var state = { | |
| lightsArray: lightsArray, | |
| shadowsArray: shadowsArray, | |
| spritesArray: spritesArray, | |
| lights: lights | |
| }; | |
| return { | |
| init: init, | |
| state: state, | |
| setupLights: setupLights, | |
| pushLight: pushLight, | |
| pushShadow: pushShadow, | |
| pushSprite: pushSprite | |
| }; | |
| } | |
| function WebGLRenderStates() { | |
| var renderStates = {}; | |
| function get( scene, camera ) { | |
| var hash = scene.id + ',' + camera.id; | |
| var renderState = renderStates[ hash ]; | |
| if ( renderState === undefined ) { | |
| renderState = new WebGLRenderState(); | |
| renderStates[ hash ] = renderState; | |
| } | |
| return renderState; | |
| } | |
| function dispose() { | |
| renderStates = {}; | |
| } | |
| return { | |
| get: get, | |
| dispose: dispose | |
| }; | |
| } | |
| /** | |
| * @author mrdoob / http://mrdoob.com/ | |
| * @author alteredq / http://alteredqualia.com/ | |
| * @author bhouston / https://clara.io | |
| * @author WestLangley / http://github.com/WestLangley | |
| * | |
| * parameters = { | |
| * | |
| * opacity: <float>, | |
| * | |
| * map: new THREE.Texture( <Image> ), | |
| * | |
| * alphaMap: new THREE.Texture( <Image> ), | |
| * | |
| * displacementMap: new THREE.Texture( <Image> ), | |
| * displacementScale: <float>, | |
| * displacementBias: <float>, | |
| * | |
| * wireframe: <boolean>, | |
| * wireframeLinewidth: <float> | |
| * } | |
| */ | |
| function MeshDepthMaterial( parameters ) { | |
| Material.call( this ); | |
| this.type = 'MeshDepthMaterial'; | |
| this.depthPacking = BasicDepthPacking; | |
| this.skinning = false; | |
| this.morphTargets = false; | |
| this.map = null; | |
| this.alphaMap = null; | |
| this.displacementMap = null; | |
| this.displacementScale = 1; | |
| this.displacementBias = 0; | |
| this.wireframe = false; | |
| this.wireframeLinewidth = 1; | |
| this.fog = false; | |
| this.lights = false; | |
| this.setValues( parameters ); | |
| } | |
| MeshDepthMaterial.prototype = Object.create( Material.prototype ); | |
| MeshDepthMaterial.prototype.constructor = MeshDepthMaterial; | |
| MeshDepthMaterial.prototype.isMeshDepthMaterial = true; | |
| MeshDepthMaterial.prototype.copy = function ( source ) { | |
| Material.prototype.copy.call( this, source ); | |
| this.depthPacking = source.depthPacking; | |
| this.skinning = source.skinning; | |
| this.morphTargets = source.morphTargets; | |
| this.map = source.map; | |
| this.alphaMap = source.alphaMap; | |
| this.displacementMap = source.displacementMap; | |
| this.displacementScale = source.displacementScale; | |
| this.displacementBias = source.displacementBias; | |
| this.wireframe = source.wireframe; | |
| this.wireframeLinewidth = source.wireframeLinewidth; | |
| return this; | |
| }; | |
| /** | |
| * @author WestLangley / http://github.com/WestLangley | |
| * | |
| * parameters = { | |
| * | |
| * referencePosition: <float>, | |
| * nearDistance: <float>, | |
| * farDistance: <float>, | |
| * | |
| * skinning: <bool>, | |
| * morphTargets: <bool>, | |
| * | |
| * map: new THREE.Texture( <Image> ), | |
| * | |
| * alphaMap: new THREE.Texture( <Image> ), | |
| * | |
| * displacementMap: new THREE.Texture( <Image> ), | |
| * displacementScale: <float>, | |
| * displacementBias: <float> | |
| * | |
| * } | |
| */ | |
| function MeshDistanceMaterial( parameters ) { | |
| Material.call( this ); | |
| this.type = 'MeshDistanceMaterial'; | |
| this.referencePosition = new Vector3(); | |
| this.nearDistance = 1; | |
| this.farDistance = 1000; | |
| this.skinning = false; | |
| this.morphTargets = false; | |
| this.map = null; | |
| this.alphaMap = null; | |
| this.displacementMap = null; | |
| this.displacementScale = 1; | |
| this.displacementBias = 0; | |
| this.fog = false; | |
| this.lights = false; | |
| this.setValues( parameters ); | |
| } | |
| MeshDistanceMaterial.prototype = Object.create( Material.prototype ); | |
| MeshDistanceMaterial.prototype.constructor = MeshDistanceMaterial; | |
| MeshDistanceMaterial.prototype.isMeshDistanceMaterial = true; | |
| MeshDistanceMaterial.prototype.copy = function ( source ) { | |
| Material.prototype.copy.call( this, source ); | |
| this.referencePosition.copy( source.referencePosition ); | |
| this.nearDistance = source.nearDistance; | |
| this.farDistance = source.farDistance; | |
| this.skinning = source.skinning; | |
| this.morphTargets = source.morphTargets; | |
| this.map = source.map; | |
| this.alphaMap = source.alphaMap; | |
| this.displacementMap = source.displacementMap; | |
| this.displacementScale = source.displacementScale; | |
| this.displacementBias = source.displacementBias; | |
| return this; | |
| }; | |
| /** | |
| * @author alteredq / http://alteredqualia.com/ | |
| * @author mrdoob / http://mrdoob.com/ | |
| */ | |
| function WebGLShadowMap( _renderer, _objects, maxTextureSize ) { | |
| var _frustum = new Frustum(), | |
| _projScreenMatrix = new Matrix4(), | |
| _shadowMapSize = new Vector2(), | |
| _maxShadowMapSize = new Vector2( maxTextureSize, maxTextureSize ), | |
| _lookTarget = new Vector3(), | |
| _lightPositionWorld = new Vector3(), | |
| _MorphingFlag = 1, | |
| _SkinningFlag = 2, | |
| _NumberOfMaterialVariants = ( _MorphingFlag | _SkinningFlag ) + 1, | |
| _depthMaterials = new Array( _NumberOfMaterialVariants ), | |
| _distanceMaterials = new Array( _NumberOfMaterialVariants ), | |
| _materialCache = {}; | |
| var shadowSide = { 0: BackSide, 1: FrontSide, 2: DoubleSide }; | |
| var cubeDirections = [ | |
| new Vector3( 1, 0, 0 ), new Vector3( - 1, 0, 0 ), new Vector3( 0, 0, 1 ), | |
| new Vector3( 0, 0, - 1 ), new Vector3( 0, 1, 0 ), new Vector3( 0, - 1, 0 ) | |
| ]; | |
| var cubeUps = [ | |
| new Vector3( 0, 1, 0 ), new Vector3( 0, 1, 0 ), new Vector3( 0, 1, 0 ), | |
| new Vector3( 0, 1, 0 ), new Vector3( 0, 0, 1 ), new Vector3( 0, 0, - 1 ) | |
| ]; | |
| var cube2DViewPorts = [ | |
| new Vector4(), new Vector4(), new Vector4(), | |
| new Vector4(), new Vector4(), new Vector4() | |
| ]; | |
| // init | |
| for ( var i = 0; i !== _NumberOfMaterialVariants; ++ i ) { | |
| var useMorphing = ( i & _MorphingFlag ) !== 0; | |
| var useSkinning = ( i & _SkinningFlag ) !== 0; | |
| var depthMaterial = new MeshDepthMaterial( { | |
| depthPacking: RGBADepthPacking, | |
| morphTargets: useMorphing, | |
| skinning: useSkinning | |
| } ); | |
| _depthMaterials[ i ] = depthMaterial; | |
| // | |
| var distanceMaterial = new MeshDistanceMaterial( { | |
| morphTargets: useMorphing, | |
| skinning: useSkinning | |
| } ); | |
| _distanceMaterials[ i ] = distanceMaterial; | |
| } | |
| // | |
| var scope = this; | |
| this.enabled = false; | |
| this.autoUpdate = true; | |
| this.needsUpdate = false; | |
| this.type = PCFShadowMap; | |
| this.render = function ( lights, scene, camera ) { | |
| if ( scope.enabled === false ) return; | |
| if ( scope.autoUpdate === false && scope.needsUpdate === false ) return; | |
| if ( lights.length === 0 ) return; | |
| // TODO Clean up (needed in case of contextlost) | |
| var _gl = _renderer.context; | |
| var _state = _renderer.state; | |
| // Set GL state for depth map. | |
| _state.disable( _gl.BLEND ); | |
| _state.buffers.color.setClear( 1, 1, 1, 1 ); | |
| _state.buffers.depth.setTest( true ); | |
| _state.setScissorTest( false ); | |
| // render depth map | |
| var faceCount; | |
| for ( var i = 0, il = lights.length; i < il; i ++ ) { | |
| var light = lights[ i ]; | |
| var shadow = light.shadow; | |
| var isPointLight = light && light.isPointLight; | |
| if ( shadow === undefined ) { | |
| console.warn( 'THREE.WebGLShadowMap:', light, 'has no shadow.' ); | |
| continue; | |
| } | |
| var shadowCamera = shadow.camera; | |
| _shadowMapSize.copy( shadow.mapSize ); | |
| _shadowMapSize.min( _maxShadowMapSize ); | |
| if ( isPointLight ) { | |
| var vpWidth = _shadowMapSize.x; | |
| var vpHeight = _shadowMapSize.y; | |
| // These viewports map a cube-map onto a 2D texture with the | |
| // following orientation: | |
| // | |
| // xzXZ | |
| // y Y | |
| // | |
| // X - Positive x direction | |
| // x - Negative x direction | |
| // Y - Positive y direction | |
| // y - Negative y direction | |
| // Z - Positive z direction | |
| // z - Negative z direction | |
| // positive X | |
| cube2DViewPorts[ 0 ].set( vpWidth * 2, vpHeight, vpWidth, vpHeight ); | |
| // negative X | |
| cube2DViewPorts[ 1 ].set( 0, vpHeight, vpWidth, vpHeight ); | |
| // positive Z | |
| cube2DViewPorts[ 2 ].set( vpWidth * 3, vpHeight, vpWidth, vpHeight ); | |
| // negative Z | |
| cube2DViewPorts[ 3 ].set( vpWidth, vpHeight, vpWidth, vpHeight ); | |
| // positive Y | |
| cube2DViewPorts[ 4 ].set( vpWidth * 3, 0, vpWidth, vpHeight ); | |
| // negative Y | |
| cube2DViewPorts[ 5 ].set( vpWidth, 0, vpWidth, vpHeight ); | |
| _shadowMapSize.x *= 4.0; | |
| _shadowMapSize.y *= 2.0; | |
| } | |
| if ( shadow.map === null ) { | |
| var pars = { minFilter: NearestFilter, magFilter: NearestFilter, format: RGBAFormat }; | |
| shadow.map = new WebGLRenderTarget( _shadowMapSize.x, _shadowMapSize.y, pars ); | |
| shadow.map.texture.name = light.name + ".shadowMap"; | |
| shadowCamera.updateProjectionMatrix(); | |
| } | |
| if ( shadow.isSpotLightShadow ) { | |
| shadow.update( light ); | |
| } | |
| var shadowMap = shadow.map; | |
| var shadowMatrix = shadow.matrix; | |
| _lightPositionWorld.setFromMatrixPosition( light.matrixWorld ); | |
| shadowCamera.position.copy( _lightPositionWorld ); | |
| if ( isPointLight ) { | |
| faceCount = 6; | |
| // for point lights we set the shadow matrix to be a translation-only matrix | |
| // equal to inverse of the light's position | |
| shadowMatrix.makeTranslation( - _lightPositionWorld.x, - _lightPositionWorld.y, - _lightPositionWorld.z ); | |
| } else { | |
| faceCount = 1; | |
| _lookTarget.setFromMatrixPosition( light.target.matrixWorld ); | |
| shadowCamera.lookAt( _lookTarget ); | |
| shadowCamera.updateMatrixWorld(); | |
| // compute shadow matrix | |
| shadowMatrix.set( | |
| 0.5, 0.0, 0.0, 0.5, | |
| 0.0, 0.5, 0.0, 0.5, | |
| 0.0, 0.0, 0.5, 0.5, | |
| 0.0, 0.0, 0.0, 1.0 | |
| ); | |
| shadowMatrix.multiply( shadowCamera.projectionMatrix ); | |
| shadowMatrix.multiply( shadowCamera.matrixWorldInverse ); | |
| } | |
| _renderer.setRenderTarget( shadowMap ); | |
| _renderer.clear(); | |
| // render shadow map for each cube face (if omni-directional) or | |
| // run a single pass if not | |
| for ( var face = 0; face < faceCount; face ++ ) { | |
| if ( isPointLight ) { | |
| _lookTarget.copy( shadowCamera.position ); | |
| _lookTarget.add( cubeDirections[ face ] ); | |
| shadowCamera.up.copy( cubeUps[ face ] ); | |
| shadowCamera.lookAt( _lookTarget ); | |
| shadowCamera.updateMatrixWorld(); | |
| var vpDimensions = cube2DViewPorts[ face ]; | |
| _state.viewport( vpDimensions ); | |
| } | |
| // update camera matrices and frustum | |
| _projScreenMatrix.multiplyMatrices( shadowCamera.projectionMatrix, shadowCamera.matrixWorldInverse ); | |
| _frustum.setFromMatrix( _projScreenMatrix ); | |
| // set object matrices & frustum culling | |
| renderObject( scene, camera, shadowCamera, isPointLight ); | |
| } | |
| } | |
| scope.needsUpdate = false; | |
| }; | |
| function getDepthMaterial( object, material, isPointLight, lightPositionWorld, shadowCameraNear, shadowCameraFar ) { | |
| var geometry = object.geometry; | |
| var result = null; | |
| var materialVariants = _depthMaterials; | |
| var customMaterial = object.customDepthMaterial; | |
| if ( isPointLight ) { | |
| materialVariants = _distanceMaterials; | |
| customMaterial = object.customDistanceMaterial; | |
| } | |
| if ( ! customMaterial ) { | |
| var useMorphing = false; | |
| if ( material.morphTargets ) { | |
| if ( geometry && geometry.isBufferGeometry ) { | |
| useMorphing = geometry.morphAttributes && geometry.morphAttributes.position && geometry.morphAttributes.position.length > 0; | |
| } else if ( geometry && geometry.isGeometry ) { | |
| useMorphing = geometry.morphTargets && geometry.morphTargets.length > 0; | |
| } | |
| } | |
| if ( object.isSkinnedMesh && material.skinning === false ) { | |
| console.warn( 'THREE.WebGLShadowMap: THREE.SkinnedMesh with material.skinning set to false:', object ); | |
| } | |
| var useSkinning = object.isSkinnedMesh && material.skinning; | |
| var variantIndex = 0; | |
| if ( useMorphing ) variantIndex |= _MorphingFlag; | |
| if ( useSkinning ) variantIndex |= _SkinningFlag; | |
| result = materialVariants[ variantIndex ]; | |
| } else { | |
| result = customMaterial; | |
| } | |
| if ( _renderer.localClippingEnabled && | |
| material.clipShadows === true && | |
| material.clippingPlanes.length !== 0 ) { | |
| // in this case we need a unique material instance reflecting the | |
| // appropriate state | |
| var keyA = result.uuid, keyB = material.uuid; | |
| var materialsForVariant = _materialCache[ keyA ]; | |
| if ( materialsForVariant === undefined ) { | |
| materialsForVariant = {}; | |
| _materialCache[ keyA ] = materialsForVariant; | |
| } | |
| var cachedMaterial = materialsForVariant[ keyB ]; | |
| if ( cachedMaterial === undefined ) { | |
| cachedMaterial = result.clone(); | |
| materialsForVariant[ keyB ] = cachedMaterial; | |
| } | |
| result = cachedMaterial; | |
| } | |
| result.visible = material.visible; | |
| result.wireframe = material.wireframe; | |
| result.side = ( material.shadowSide != null ) ? material.shadowSide : shadowSide[ material.side ]; | |
| result.clipShadows = material.clipShadows; | |
| result.clippingPlanes = material.clippingPlanes; | |
| result.clipIntersection = material.clipIntersection; | |
| result.wireframeLinewidth = material.wireframeLinewidth; | |
| result.linewidth = material.linewidth; | |
| if ( isPointLight && result.isMeshDistanceMaterial ) { | |
| result.referencePosition.copy( lightPositionWorld ); | |
| result.nearDistance = shadowCameraNear; | |
| result.farDistance = shadowCameraFar; | |
| } | |
| return result; | |
| } | |
| function renderObject( object, camera, shadowCamera, isPointLight ) { | |
| if ( object.visible === false ) return; | |
| var visible = object.layers.test( camera.layers ); | |
| if ( visible && ( object.isMesh || object.isLine || object.isPoints ) ) { | |
| if ( object.castShadow && ( ! object.frustumCulled || _frustum.intersectsObject( object ) ) ) { | |
| object.modelViewMatrix.multiplyMatrices( shadowCamera.matrixWorldInverse, object.matrixWorld ); | |
| var geometry = _objects.update( object ); | |
| var material = object.material; | |
| if ( Array.isArray( material ) ) { | |
| var groups = geometry.groups; | |
| for ( var k = 0, kl = groups.length; k < kl; k ++ ) { | |
| var group = groups[ k ]; | |
| var groupMaterial = material[ group.materialIndex ]; | |
| if ( groupMaterial && groupMaterial.visible ) { | |
| var depthMaterial = getDepthMaterial( object, groupMaterial, isPointLight, _lightPositionWorld, shadowCamera.near, shadowCamera.far ); | |
| _renderer.renderBufferDirect( shadowCamera, null, geometry, depthMaterial, object, group ); | |
| } | |
| } | |
| } else if ( material.visible ) { | |
| var depthMaterial = getDepthMaterial( object, material, isPointLight, _lightPositionWorld, shadowCamera.near, shadowCamera.far ); | |
| _renderer.renderBufferDirect( shadowCamera, null, geometry, depthMaterial, object, null ); | |
| } | |
| } | |
| } | |
| var children = object.children; | |
| for ( var i = 0, l = children.length; i < l; i ++ ) { | |
| renderObject( children[ i ], camera, shadowCamera, isPointLight ); | |
| } | |
| } | |
| } | |
| /** | |
| * @author mrdoob / http://mrdoob.com/ | |
| */ | |
| function CanvasTexture( canvas, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy ) { | |
| Texture.call( this, canvas, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy ); | |
| this.needsUpdate = true; | |
| } | |
| CanvasTexture.prototype = Object.create( Texture.prototype ); | |
| CanvasTexture.prototype.constructor = CanvasTexture; | |
| /** | |
| * @author mikael emtinger / http://gomo.se/ | |
| * @author alteredq / http://alteredqualia.com/ | |
| */ | |
| function WebGLSpriteRenderer( renderer, gl, state, textures, capabilities ) { | |
| var vertexBuffer, elementBuffer; | |
| var program, attributes, uniforms; | |
| var texture; | |
| // decompose matrixWorld | |
| var spritePosition = new Vector3(); | |
| var spriteRotation = new Quaternion(); | |
| var spriteScale = new Vector3(); | |
| function init() { | |
| var vertices = new Float32Array( [ | |
| - 0.5, - 0.5, 0, 0, | |
| 0.5, - 0.5, 1, 0, | |
| 0.5, 0.5, 1, 1, | |
| - 0.5, 0.5, 0, 1 | |
| ] ); | |
| var faces = new Uint16Array( [ | |
| 0, 1, 2, | |
| 0, 2, 3 | |
| ] ); | |
| vertexBuffer = gl.createBuffer(); | |
| elementBuffer = gl.createBuffer(); | |
| gl.bindBuffer( gl.ARRAY_BUFFER, vertexBuffer ); | |
| gl.bufferData( gl.ARRAY_BUFFER, vertices, gl.STATIC_DRAW ); | |
| gl.bindBuffer( gl.ELEMENT_ARRAY_BUFFER, elementBuffer ); | |
| gl.bufferData( gl.ELEMENT_ARRAY_BUFFER, faces, gl.STATIC_DRAW ); | |
| program = createProgram(); | |
| attributes = { | |
| position: gl.getAttribLocation( program, 'position' ), | |
| uv: gl.getAttribLocation( program, 'uv' ) | |
| }; | |
| uniforms = { | |
| uvOffset: gl.getUniformLocation( program, 'uvOffset' ), | |
| uvScale: gl.getUniformLocation( program, 'uvScale' ), | |
| rotation: gl.getUniformLocation( program, 'rotation' ), | |
| center: gl.getUniformLocation( program, 'center' ), | |
| scale: gl.getUniformLocation( program, 'scale' ), | |
| color: gl.getUniformLocation( program, 'color' ), | |
| map: gl.getUniformLocation( program, 'map' ), | |
| opacity: gl.getUniformLocation( program, 'opacity' ), | |
| modelViewMatrix: gl.getUniformLocation( program, 'modelViewMatrix' ), | |
| projectionMatrix: gl.getUniformLocation( program, 'projectionMatrix' ), | |
| fogType: gl.getUniformLocation( program, 'fogType' ), | |
| fogDensity: gl.getUniformLocation( program, 'fogDensity' ), | |
| fogNear: gl.getUniformLocation( program, 'fogNear' ), | |
| fogFar: gl.getUniformLocation( program, 'fogFar' ), | |
| fogColor: gl.getUniformLocation( program, 'fogColor' ), | |
| fogDepth: gl.getUniformLocation( program, 'fogDepth' ), | |
| alphaTest: gl.getUniformLocation( program, 'alphaTest' ) | |
| }; | |
| var canvas = document.createElementNS( 'http://www.w3.org/1999/xhtml', 'canvas' ); | |
| canvas.width = 8; | |
| canvas.height = 8; | |
| var context = canvas.getContext( '2d' ); | |
| context.fillStyle = 'white'; | |
| context.fillRect( 0, 0, 8, 8 ); | |
| texture = new CanvasTexture( canvas ); | |
| } | |
| this.render = function ( sprites, scene, camera ) { | |
| if ( sprites.length === 0 ) return; | |
| // setup gl | |
| if ( program === undefined ) { | |
| init(); | |
| } | |
| state.useProgram( program ); | |
| state.initAttributes(); | |
| state.enableAttribute( attributes.position ); | |
| state.enableAttribute( attributes.uv ); | |
| state.disableUnusedAttributes(); | |
| state.disable( gl.CULL_FACE ); | |
| state.enable( gl.BLEND ); | |
| gl.bindBuffer( gl.ARRAY_BUFFER, vertexBuffer ); | |
| gl.vertexAttribPointer( attributes.position, 2, gl.FLOAT, false, 2 * 8, 0 ); | |
| gl.vertexAttribPointer( attributes.uv, 2, gl.FLOAT, false, 2 * 8, 8 ); | |
| gl.bindBuffer( gl.ELEMENT_ARRAY_BUFFER, elementBuffer ); | |
| gl.uniformMatrix4fv( uniforms.projectionMatrix, false, camera.projectionMatrix.elements ); | |
| state.activeTexture( gl.TEXTURE0 ); | |
| gl.uniform1i( uniforms.map, 0 ); | |
| var oldFogType = 0; | |
| var sceneFogType = 0; | |
| var fog = scene.fog; | |
| if ( fog ) { | |
| gl.uniform3f( uniforms.fogColor, fog.color.r, fog.color.g, fog.color.b ); | |
| if ( fog.isFog ) { | |
| gl.uniform1f( uniforms.fogNear, fog.near ); | |
| gl.uniform1f( uniforms.fogFar, fog.far ); | |
| gl.uniform1i( uniforms.fogType, 1 ); | |
| oldFogType = 1; | |
| sceneFogType = 1; | |
| } else if ( fog.isFogExp2 ) { | |
| gl.uniform1f( uniforms.fogDensity, fog.density ); | |
| gl.uniform1i( uniforms.fogType, 2 ); | |
| oldFogType = 2; | |
| sceneFogType = 2; | |
| } | |
| } else { | |
| gl.uniform1i( uniforms.fogType, 0 ); | |
| oldFogType = 0; | |
| sceneFogType = 0; | |
| } | |
| // update positions and sort | |
| for ( var i = 0, l = sprites.length; i < l; i ++ ) { | |
| var sprite = sprites[ i ]; | |
| sprite.modelViewMatrix.multiplyMatrices( camera.matrixWorldInverse, sprite.matrixWorld ); | |
| sprite.z = - sprite.modelViewMatrix.elements[ 14 ]; | |
| } | |
| sprites.sort( painterSortStable ); | |
| // render all sprites | |
| var scale = []; | |
| var center = []; | |
| for ( var i = 0, l = sprites.length; i < l; i ++ ) { | |
| var sprite = sprites[ i ]; | |
| var material = sprite.material; | |
| if ( material.visible === false ) continue; | |
| sprite.onBeforeRender( renderer, scene, camera, undefined, material, undefined ); | |
| gl.uniform1f( uniforms.alphaTest, material.alphaTest ); | |
| gl.uniformMatrix4fv( uniforms.modelViewMatrix, false, sprite.modelViewMatrix.elements ); | |
| sprite.matrixWorld.decompose( spritePosition, spriteRotation, spriteScale ); | |
| scale[ 0 ] = spriteScale.x; | |
| scale[ 1 ] = spriteScale.y; | |
| center[ 0 ] = sprite.center.x - 0.5; | |
| center[ 1 ] = sprite.center.y - 0.5; | |
| var fogType = 0; | |
| if ( scene.fog && material.fog ) { | |
| fogType = sceneFogType; | |
| } | |
| if ( oldFogType !== fogType ) { | |
| gl.uniform1i( uniforms.fogType, fogType ); | |
| oldFogType = fogType; | |
| } | |
| if ( material.map !== null ) { | |
| gl.uniform2f( uniforms.uvOffset, material.map.offset.x, material.map.offset.y ); | |
| gl.uniform2f( uniforms.uvScale, material.map.repeat.x, material.map.repeat.y ); | |
| } else { | |
| gl.uniform2f( uniforms.uvOffset, 0, 0 ); | |
| gl.uniform2f( uniforms.uvScale, 1, 1 ); | |
| } | |
| gl.uniform1f( uniforms.opacity, material.opacity ); | |
| gl.uniform3f( uniforms.color, material.color.r, material.color.g, material.color.b ); | |
| gl.uniform1f( uniforms.rotation, material.rotation ); | |
| gl.uniform2fv( uniforms.center, center ); | |
| gl.uniform2fv( uniforms.scale, scale ); | |
| state.setBlending( material.blending, material.blendEquation, material.blendSrc, material.blendDst, material.blendEquationAlpha, material.blendSrcAlpha, material.blendDstAlpha, material.premultipliedAlpha ); | |
| state.buffers.depth.setTest( material.depthTest ); | |
| state.buffers.depth.setMask( material.depthWrite ); | |
| state.buffers.color.setMask( material.colorWrite ); | |
| textures.setTexture2D( material.map || texture, 0 ); | |
| gl.drawElements( gl.TRIANGLES, 6, gl.UNSIGNED_SHORT, 0 ); | |
| sprite.onAfterRender( renderer, scene, camera, undefined, material, undefined ); | |
| } | |
| // restore gl | |
| state.enable( gl.CULL_FACE ); | |
| state.reset(); | |
| }; | |
| function createProgram() { | |
| var program = gl.createProgram(); | |
| var vertexShader = gl.createShader( gl.VERTEX_SHADER ); | |
| var fragmentShader = gl.createShader( gl.FRAGMENT_SHADER ); | |
| gl.shaderSource( vertexShader, [ | |
| 'precision ' + capabilities.precision + ' float;', | |
| '#define SHADER_NAME ' + 'SpriteMaterial', | |
| 'uniform mat4 modelViewMatrix;', | |
| 'uniform mat4 projectionMatrix;', | |
| 'uniform float rotation;', | |
| 'uniform vec2 center;', | |
| 'uniform vec2 scale;', | |
| 'uniform vec2 uvOffset;', | |
| 'uniform vec2 uvScale;', | |
| 'attribute vec2 position;', | |
| 'attribute vec2 uv;', | |
| 'varying vec2 vUV;', | |
| 'varying float fogDepth;', | |
| 'void main() {', | |
| ' vUV = uvOffset + uv * uvScale;', | |
| ' vec2 alignedPosition = ( position - center ) * scale;', | |
| ' vec2 rotatedPosition;', | |
| ' rotatedPosition.x = cos( rotation ) * alignedPosition.x - sin( rotation ) * alignedPosition.y;', | |
| ' rotatedPosition.y = sin( rotation ) * alignedPosition.x + cos( rotation ) * alignedPosition.y;', | |
| ' vec4 mvPosition;', | |
| ' mvPosition = modelViewMatrix * vec4( 0.0, 0.0, 0.0, 1.0 );', | |
| ' mvPosition.xy += rotatedPosition;', | |
| ' gl_Position = projectionMatrix * mvPosition;', | |
| ' fogDepth = - mvPosition.z;', | |
| '}' | |
| ].join( '\n' ) ); | |
| gl.shaderSource( fragmentShader, [ | |
| 'precision ' + capabilities.precision + ' float;', | |
| '#define SHADER_NAME ' + 'SpriteMaterial', | |
| 'uniform vec3 color;', | |
| 'uniform sampler2D map;', | |
| 'uniform float opacity;', | |
| 'uniform int fogType;', | |
| 'uniform vec3 fogColor;', | |
| 'uniform float fogDensity;', | |
| 'uniform float fogNear;', | |
| 'uniform float fogFar;', | |
| 'uniform float alphaTest;', | |
| 'varying vec2 vUV;', | |
| 'varying float fogDepth;', | |
| 'void main() {', | |
| ' vec4 texture = texture2D( map, vUV );', | |
| ' gl_FragColor = vec4( color * texture.xyz, texture.a * opacity );', | |
| ' if ( gl_FragColor.a < alphaTest ) discard;', | |
| ' if ( fogType > 0 ) {', | |
| ' float fogFactor = 0.0;', | |
| ' if ( fogType == 1 ) {', | |
| ' fogFactor = smoothstep( fogNear, fogFar, fogDepth );', | |
| ' } else {', | |
| ' const float LOG2 = 1.442695;', | |
| ' fogFactor = exp2( - fogDensity * fogDensity * fogDepth * fogDepth * LOG2 );', | |
| ' fogFactor = 1.0 - clamp( fogFactor, 0.0, 1.0 );', | |
| ' }', | |
| ' gl_FragColor.rgb = mix( gl_FragColor.rgb, fogColor, fogFactor );', | |
| ' }', | |
| '}' | |
| ].join( '\n' ) ); | |
| gl.compileShader( vertexShader ); | |
| gl.compileShader( fragmentShader ); | |
| gl.attachShader( program, vertexShader ); | |
| gl.attachShader( program, fragmentShader ); | |
| gl.linkProgram( program ); | |
| return program; | |
| } | |
| function painterSortStable( a, b ) { | |
| if ( a.renderOrder !== b.renderOrder ) { | |
| return a.renderOrder - b.renderOrder; | |
| } else if ( a.z !== b.z ) { | |
| return b.z - a.z; | |
| } else { | |
| return b.id - a.id; | |
| } | |
| } | |
| } | |
| /** | |
| * @author mrdoob / http://mrdoob.com/ | |
| */ | |
| function WebGLState( gl, extensions, utils ) { | |
| function ColorBuffer() { | |
| var locked = false; | |
| var color = new Vector4(); | |
| var currentColorMask = null; | |
| var currentColorClear = new Vector4( 0, 0, 0, 0 ); | |
| return { | |
| setMask: function ( colorMask ) { | |
| if ( currentColorMask !== colorMask && ! locked ) { | |
| gl.colorMask( colorMask, colorMask, colorMask, colorMask ); | |
| currentColorMask = colorMask; | |
| } | |
| }, | |
| setLocked: function ( lock ) { | |
| locked = lock; | |
| }, | |
| setClear: function ( r, g, b, a, premultipliedAlpha ) { | |
| if ( premultipliedAlpha === true ) { | |
| r *= a; g *= a; b *= a; | |
| } | |
| color.set( r, g, b, a ); | |
| if ( currentColorClear.equals( color ) === false ) { | |
| gl.clearColor( r, g, b, a ); | |
| currentColorClear.copy( color ); | |
| } | |
| }, | |
| reset: function () { | |
| locked = false; | |
| currentColorMask = null; | |
| currentColorClear.set( - 1, 0, 0, 0 ); // set to invalid state | |
| } | |
| }; | |
| } | |
| function DepthBuffer() { | |
| var locked = false; | |
| var currentDepthMask = null; | |
| var currentDepthFunc = null; | |
| var currentDepthClear = null; | |
| return { | |
| setTest: function ( depthTest ) { | |
| if ( depthTest ) { | |
| enable( gl.DEPTH_TEST ); | |
| } else { | |
| disable( gl.DEPTH_TEST ); | |
| } | |
| }, | |
| setMask: function ( depthMask ) { | |
| if ( currentDepthMask !== depthMask && ! locked ) { | |
| gl.depthMask( depthMask ); | |
| currentDepthMask = depthMask; | |
| } | |
| }, | |
| setFunc: function ( depthFunc ) { | |
| if ( currentDepthFunc !== depthFunc ) { | |
| if ( depthFunc ) { | |
| switch ( depthFunc ) { | |
| case NeverDepth: | |
| gl.depthFunc( gl.NEVER ); | |
| break; | |
| case AlwaysDepth: | |
| gl.depthFunc( gl.ALWAYS ); | |
| break; | |
| case LessDepth: | |
| gl.depthFunc( gl.LESS ); | |
| break; | |
| case LessEqualDepth: | |
| gl.depthFunc( gl.LEQUAL ); | |
| break; | |
| case EqualDepth: | |
| gl.depthFunc( gl.EQUAL ); | |
| break; | |
| case GreaterEqualDepth: | |
| gl.depthFunc( gl.GEQUAL ); | |
| break; | |
| case GreaterDepth: | |
| gl.depthFunc( gl.GREATER ); | |
| break; | |
| case NotEqualDepth: | |
| gl.depthFunc( gl.NOTEQUAL ); | |
| break; | |
| default: | |
| gl.depthFunc( gl.LEQUAL ); | |
| } | |
| } else { | |
| gl.depthFunc( gl.LEQUAL ); | |
| } | |
| currentDepthFunc = depthFunc; | |
| } | |
| }, | |
| setLocked: function ( lock ) { | |
| locked = lock; | |
| }, | |
| setClear: function ( depth ) { | |
| if ( currentDepthClear !== depth ) { | |
| gl.clearDepth( depth ); | |
| currentDepthClear = depth; | |
| } | |
| }, | |
| reset: function () { | |
| locked = false; | |
| currentDepthMask = null; | |
| currentDepthFunc = null; | |
| currentDepthClear = null; | |
| } | |
| }; | |
| } | |
| function StencilBuffer() { | |
| var locked = false; | |
| var currentStencilMask = null; | |
| var currentStencilFunc = null; | |
| var currentStencilRef = null; | |
| var currentStencilFuncMask = null; | |
| var currentStencilFail = null; | |
| var currentStencilZFail = null; | |
| var currentStencilZPass = null; | |
| var currentStencilClear = null; | |
| return { | |
| setTest: function ( stencilTest ) { | |
| if ( stencilTest ) { | |
| enable( gl.STENCIL_TEST ); | |
| } else { | |
| disable( gl.STENCIL_TEST ); | |
| } | |
| }, | |
| setMask: function ( stencilMask ) { | |
| if ( currentStencilMask !== stencilMask && ! locked ) { | |
| gl.stencilMask( stencilMask ); | |
| currentStencilMask = stencilMask; | |
| } | |
| }, | |
| setFunc: function ( stencilFunc, stencilRef, stencilMask ) { | |
| if ( currentStencilFunc !== stencilFunc || | |
| currentStencilRef !== stencilRef || | |
| currentStencilFuncMask !== stencilMask ) { | |
| gl.stencilFunc( stencilFunc, stencilRef, stencilMask ); | |
| currentStencilFunc = stencilFunc; | |
| currentStencilRef = stencilRef; | |
| currentStencilFuncMask = stencilMask; | |
| } | |
| }, | |
| setOp: function ( stencilFail, stencilZFail, stencilZPass ) { | |
| if ( currentStencilFail !== stencilFail || | |
| currentStencilZFail !== stencilZFail || | |
| currentStencilZPass !== stencilZPass ) { | |
| gl.stencilOp( stencilFail, stencilZFail, stencilZPass ); | |
| currentStencilFail = stencilFail; | |
| currentStencilZFail = stencilZFail; | |
| currentStencilZPass = stencilZPass; | |
| } | |
| }, | |
| setLocked: function ( lock ) { | |
| locked = lock; | |
| }, | |
| setClear: function ( stencil ) { | |
| if ( currentStencilClear !== stencil ) { | |
| gl.clearStencil( stencil ); | |
| currentStencilClear = stencil; | |
| } | |
| }, | |
| reset: function () { | |
| locked = false; | |
| currentStencilMask = null; | |
| currentStencilFunc = null; | |
| currentStencilRef = null; | |
| currentStencilFuncMask = null; | |
| currentStencilFail = null; | |
| currentStencilZFail = null; | |
| currentStencilZPass = null; | |
| currentStencilClear = null; | |
| } | |
| }; | |
| } | |
| // | |
| var colorBuffer = new ColorBuffer(); | |
| var depthBuffer = new DepthBuffer(); | |
| var stencilBuffer = new StencilBuffer(); | |
| var maxVertexAttributes = gl.getParameter( gl.MAX_VERTEX_ATTRIBS ); | |
| var newAttributes = new Uint8Array( maxVertexAttributes ); | |
| var enabledAttributes = new Uint8Array( maxVertexAttributes ); | |
| var attributeDivisors = new Uint8Array( maxVertexAttributes ); | |
| var capabilities = {}; | |
| var compressedTextureFormats = null; | |
| var currentProgram = null; | |
| var currentBlending = null; | |
| var currentBlendEquation = null; | |
| var currentBlendSrc = null; | |
| var currentBlendDst = null; | |
| var currentBlendEquationAlpha = null; | |
| var currentBlendSrcAlpha = null; | |
| var currentBlendDstAlpha = null; | |
| var currentPremultipledAlpha = false; | |
| var currentFlipSided = null; | |
| var currentCullFace = null; | |
| var currentLineWidth = null; | |
| var currentPolygonOffsetFactor = null; | |
| var currentPolygonOffsetUnits = null; | |
| var maxTextures = gl.getParameter( gl.MAX_COMBINED_TEXTURE_IMAGE_UNITS ); | |
| var lineWidthAvailable = false; | |
| var version = 0; | |
| var glVersion = gl.getParameter( gl.VERSION ); | |
| if ( glVersion.indexOf( 'WebGL' ) !== - 1 ) { | |
| version = parseFloat( /^WebGL\ ([0-9])/.exec( glVersion )[ 1 ] ); | |
| lineWidthAvailable = ( version >= 1.0 ); | |
| } else if ( glVersion.indexOf( 'OpenGL ES' ) !== - 1 ) { | |
| version = parseFloat( /^OpenGL\ ES\ ([0-9])/.exec( glVersion )[ 1 ] ); | |
| lineWidthAvailable = ( version >= 2.0 ); | |
| } | |
| var currentTextureSlot = null; | |
| var currentBoundTextures = {}; | |
| var currentScissor = new Vector4(); | |
| var currentViewport = new Vector4(); | |
| function createTexture( type, target, count ) { | |
| var data = new Uint8Array( 4 ); // 4 is required to match default unpack alignment of 4. | |
| var texture = gl.createTexture(); | |
| gl.bindTexture( type, texture ); | |
| gl.texParameteri( type, gl.TEXTURE_MIN_FILTER, gl.NEAREST ); | |
| gl.texParameteri( type, gl.TEXTURE_MAG_FILTER, gl.NEAREST ); | |
| for ( var i = 0; i < count; i ++ ) { | |
| gl.texImage2D( target + i, 0, gl.RGBA, 1, 1, 0, gl.RGBA, gl.UNSIGNED_BYTE, data ); | |
| } | |
| return texture; | |
| } | |
| var emptyTextures = {}; | |
| emptyTextures[ gl.TEXTURE_2D ] = createTexture( gl.TEXTURE_2D, gl.TEXTURE_2D, 1 ); | |
| emptyTextures[ gl.TEXTURE_CUBE_MAP ] = createTexture( gl.TEXTURE_CUBE_MAP, gl.TEXTURE_CUBE_MAP_POSITIVE_X, 6 ); | |
| // init | |
| colorBuffer.setClear( 0, 0, 0, 1 ); | |
| depthBuffer.setClear( 1 ); | |
| stencilBuffer.setClear( 0 ); | |
| enable( gl.DEPTH_TEST ); | |
| depthBuffer.setFunc( LessEqualDepth ); | |
| setFlipSided( false ); | |
| setCullFace( CullFaceBack ); | |
| enable( gl.CULL_FACE ); | |
| enable( gl.BLEND ); | |
| setBlending( NormalBlending ); | |
| // | |
| function initAttributes() { | |
| for ( var i = 0, l = newAttributes.length; i < l; i ++ ) { | |
| newAttributes[ i ] = 0; | |
| } | |
| } | |
| function enableAttribute( attribute ) { | |
| newAttributes[ attribute ] = 1; | |
| if ( enabledAttributes[ attribute ] === 0 ) { | |
| gl.enableVertexAttribArray( attribute ); | |
| enabledAttributes[ attribute ] = 1; | |
| } | |
| if ( attributeDivisors[ attribute ] !== 0 ) { | |
| var extension = extensions.get( 'ANGLE_instanced_arrays' ); | |
| extension.vertexAttribDivisorANGLE( attribute, 0 ); | |
| attributeDivisors[ attribute ] = 0; | |
| } | |
| } | |
| function enableAttributeAndDivisor( attribute, meshPerAttribute ) { | |
| newAttributes[ attribute ] = 1; | |
| if ( enabledAttributes[ attribute ] === 0 ) { | |
| gl.enableVertexAttribArray( attribute ); | |
| enabledAttributes[ attribute ] = 1; | |
| } | |
| if ( attributeDivisors[ attribute ] !== meshPerAttribute ) { | |
| var extension = extensions.get( 'ANGLE_instanced_arrays' ); | |
| extension.vertexAttribDivisorANGLE( attribute, meshPerAttribute ); | |
| attributeDivisors[ attribute ] = meshPerAttribute; | |
| } | |
| } | |
| function disableUnusedAttributes() { | |
| for ( var i = 0, l = enabledAttributes.length; i !== l; ++ i ) { | |
| if ( enabledAttributes[ i ] !== newAttributes[ i ] ) { | |
| gl.disableVertexAttribArray( i ); | |
| enabledAttributes[ i ] = 0; | |
| } | |
| } | |
| } | |
| function enable( id ) { | |
| if ( capabilities[ id ] !== true ) { | |
| gl.enable( id ); | |
| capabilities[ id ] = true; | |
| } | |
| } | |
| function disable( id ) { | |
| if ( capabilities[ id ] !== false ) { | |
| gl.disable( id ); | |
| capabilities[ id ] = false; | |
| } | |
| } | |
| function getCompressedTextureFormats() { | |
| if ( compressedTextureFormats === null ) { | |
| compressedTextureFormats = []; | |
| if ( extensions.get( 'WEBGL_compressed_texture_pvrtc' ) || | |
| extensions.get( 'WEBGL_compressed_texture_s3tc' ) || | |
| extensions.get( 'WEBGL_compressed_texture_etc1' ) || | |
| extensions.get( 'WEBGL_compressed_texture_astc' ) ) { | |
| var formats = gl.getParameter( gl.COMPRESSED_TEXTURE_FORMATS ); | |
| for ( var i = 0; i < formats.length; i ++ ) { | |
| compressedTextureFormats.push( formats[ i ] ); | |
| } | |
| } | |
| } | |
| return compressedTextureFormats; | |
| } | |
| function useProgram( program ) { | |
| if ( currentProgram !== program ) { | |
| gl.useProgram( program ); | |
| currentProgram = program; | |
| return true; | |
| } | |
| return false; | |
| } | |
| function setBlending( blending, blendEquation, blendSrc, blendDst, blendEquationAlpha, blendSrcAlpha, blendDstAlpha, premultipliedAlpha ) { | |
| if ( blending !== NoBlending ) { | |
| enable( gl.BLEND ); | |
| } else { | |
| disable( gl.BLEND ); | |
| } | |
| if ( blending !== CustomBlending ) { | |
| if ( blending !== currentBlending || premultipliedAlpha !== currentPremultipledAlpha ) { | |
| switch ( blending ) { | |
| case AdditiveBlending: | |
| if ( premultipliedAlpha ) { | |
| gl.blendEquationSeparate( gl.FUNC_ADD, gl.FUNC_ADD ); | |
| gl.blendFuncSeparate( gl.ONE, gl.ONE, gl.ONE, gl.ONE ); | |
| } else { | |
| gl.blendEquation( gl.FUNC_ADD ); | |
| gl.blendFunc( gl.SRC_ALPHA, gl.ONE ); | |
| } | |
| break; | |
| case SubtractiveBlending: | |
| if ( premultipliedAlpha ) { | |
| gl.blendEquationSeparate( gl.FUNC_ADD, gl.FUNC_ADD ); | |
| gl.blendFuncSeparate( gl.ZERO, gl.ZERO, gl.ONE_MINUS_SRC_COLOR, gl.ONE_MINUS_SRC_ALPHA ); | |
| } else { | |
| gl.blendEquation( gl.FUNC_ADD ); | |
| gl.blendFunc( gl.ZERO, gl.ONE_MINUS_SRC_COLOR ); | |
| } | |
| break; | |
| case MultiplyBlending: | |
| if ( premultipliedAlpha ) { | |
| gl.blendEquationSeparate( gl.FUNC_ADD, gl.FUNC_ADD ); | |
| gl.blendFuncSeparate( gl.ZERO, gl.SRC_COLOR, gl.ZERO, gl.SRC_ALPHA ); | |
| } else { | |
| gl.blendEquation( gl.FUNC_ADD ); | |
| gl.blendFunc( gl.ZERO, gl.SRC_COLOR ); | |
| } | |
| break; | |
| default: | |
| if ( premultipliedAlpha ) { | |
| gl.blendEquationSeparate( gl.FUNC_ADD, gl.FUNC_ADD ); | |
| gl.blendFuncSeparate( gl.ONE, gl.ONE_MINUS_SRC_ALPHA, gl.ONE, gl.ONE_MINUS_SRC_ALPHA ); | |
| } else { | |
| gl.blendEquationSeparate( gl.FUNC_ADD, gl.FUNC_ADD ); | |
| gl.blendFuncSeparate( gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA, gl.ONE, gl.ONE_MINUS_SRC_ALPHA ); | |
| } | |
| } | |
| } | |
| currentBlendEquation = null; | |
| currentBlendSrc = null; | |
| currentBlendDst = null; | |
| currentBlendEquationAlpha = null; | |
| currentBlendSrcAlpha = null; | |
| currentBlendDstAlpha = null; | |
| } else { | |
| blendEquationAlpha = blendEquationAlpha || blendEquation; | |
| blendSrcAlpha = blendSrcAlpha || blendSrc; | |
| blendDstAlpha = blendDstAlpha || blendDst; | |
| if ( blendEquation !== currentBlendEquation || blendEquationAlpha !== currentBlendEquationAlpha ) { | |
| gl.blendEquationSeparate( utils.convert( blendEquation ), utils.convert( blendEquationAlpha ) ); | |
| currentBlendEquation = blendEquation; | |
| currentBlendEquationAlpha = blendEquationAlpha; | |
| } | |
| if ( blendSrc !== currentBlendSrc || blendDst !== currentBlendDst || blendSrcAlpha !== currentBlendSrcAlpha || blendDstAlpha !== currentBlendDstAlpha ) { | |
| gl.blendFuncSeparate( utils.convert( blendSrc ), utils.convert( blendDst ), utils.convert( blendSrcAlpha ), utils.convert( blendDstAlpha ) ); | |
| currentBlendSrc = blendSrc; | |
| currentBlendDst = blendDst; | |
| currentBlendSrcAlpha = blendSrcAlpha; | |
| currentBlendDstAlpha = blendDstAlpha; | |
| } | |
| } | |
| currentBlending = blending; | |
| currentPremultipledAlpha = premultipliedAlpha; | |
| } | |
| function setMaterial( material, frontFaceCW ) { | |
| material.side === DoubleSide | |
| ? disable( gl.CULL_FACE ) | |
| : enable( gl.CULL_FACE ); | |
| var flipSided = ( material.side === BackSide ); | |
| if ( frontFaceCW ) flipSided = ! flipSided; | |
| setFlipSided( flipSided ); | |
| material.transparent === true | |
| ? setBlending( material.blending, material.blendEquation, material.blendSrc, material.blendDst, material.blendEquationAlpha, material.blendSrcAlpha, material.blendDstAlpha, material.premultipliedAlpha ) | |
| : setBlending( NoBlending ); | |
| depthBuffer.setFunc( material.depthFunc ); | |
| depthBuffer.setTest( material.depthTest ); | |
| depthBuffer.setMask( material.depthWrite ); | |
| colorBuffer.setMask( material.colorWrite ); | |
| setPolygonOffset( material.polygonOffset, material.polygonOffsetFactor, material.polygonOffsetUnits ); | |
| } | |
| // | |
| function setFlipSided( flipSided ) { | |
| if ( currentFlipSided !== flipSided ) { | |
| if ( flipSided ) { | |
| gl.frontFace( gl.CW ); | |
| } else { | |
| gl.frontFace( gl.CCW ); | |
| } | |
| currentFlipSided = flipSided; | |
| } | |
| } | |
| function setCullFace( cullFace ) { | |
| if ( cullFace !== CullFaceNone ) { | |
| enable( gl.CULL_FACE ); | |
| if ( cullFace !== currentCullFace ) { | |
| if ( cullFace === CullFaceBack ) { | |
| gl.cullFace( gl.BACK ); | |
| } else if ( cullFace === CullFaceFront ) { | |
| gl.cullFace( gl.FRONT ); | |
| } else { | |
| gl.cullFace( gl.FRONT_AND_BACK ); | |
| } | |
| } | |
| } else { | |
| disable( gl.CULL_FACE ); | |
| } | |
| currentCullFace = cullFace; | |
| } | |
| function setLineWidth( width ) { | |
| if ( width !== currentLineWidth ) { | |
| if ( lineWidthAvailable ) gl.lineWidth( width ); | |
| currentLineWidth = width; | |
| } | |
| } | |
| function setPolygonOffset( polygonOffset, factor, units ) { | |
| if ( polygonOffset ) { | |
| enable( gl.POLYGON_OFFSET_FILL ); | |
| if ( currentPolygonOffsetFactor !== factor || currentPolygonOffsetUnits !== units ) { | |
| gl.polygonOffset( factor, units ); | |
| currentPolygonOffsetFactor = factor; | |
| currentPolygonOffsetUnits = units; | |
| } | |
| } else { | |
| disable( gl.POLYGON_OFFSET_FILL ); | |
| } | |
| } | |
| function setScissorTest( scissorTest ) { | |
| if ( scissorTest ) { | |
| enable( gl.SCISSOR_TEST ); | |
| } else { | |
| disable( gl.SCISSOR_TEST ); | |
| } | |
| } | |
| // texture | |
| function activeTexture( webglSlot ) { | |
| if ( webglSlot === undefined ) webglSlot = gl.TEXTURE0 + maxTextures - 1; | |
| if ( currentTextureSlot !== webglSlot ) { | |
| gl.activeTexture( webglSlot ); | |
| currentTextureSlot = webglSlot; | |
| } | |
| } | |
| function bindTexture( webglType, webglTexture ) { | |
| if ( currentTextureSlot === null ) { | |
| activeTexture(); | |
| } | |
| var boundTexture = currentBoundTextures[ currentTextureSlot ]; | |
| if ( boundTexture === undefined ) { | |
| boundTexture = { type: undefined, texture: undefined }; | |
| currentBoundTextures[ currentTextureSlot ] = boundTexture; | |
| } | |
| if ( boundTexture.type !== webglType || boundTexture.texture !== webglTexture ) { | |
| gl.bindTexture( webglType, webglTexture || emptyTextures[ webglType ] ); | |
| boundTexture.type = webglType; | |
| boundTexture.texture = webglTexture; | |
| } | |
| } | |
| function compressedTexImage2D() { | |
| try { | |
| gl.compressedTexImage2D.apply( gl, arguments ); | |
| } catch ( error ) { | |
| console.error( 'THREE.WebGLState:', error ); | |
| } | |
| } | |
| function texImage2D() { | |
| try { | |
| gl.texImage2D.apply( gl, arguments ); | |
| } catch ( error ) { | |
| console.error( 'THREE.WebGLState:', error ); | |
| } | |
| } | |
| // | |
| function scissor( scissor ) { | |
| if ( currentScissor.equals( scissor ) === false ) { | |
| gl.scissor( scissor.x, scissor.y, scissor.z, scissor.w ); | |
| currentScissor.copy( scissor ); | |
| } | |
| } | |
| function viewport( viewport ) { | |
| if ( currentViewport.equals( viewport ) === false ) { | |
| gl.viewport( viewport.x, viewport.y, viewport.z, viewport.w ); | |
| currentViewport.copy( viewport ); | |
| } | |
| } | |
| // | |
| function reset() { | |
| for ( var i = 0; i < enabledAttributes.length; i ++ ) { | |
| if ( enabledAttributes[ i ] === 1 ) { | |
| gl.disableVertexAttribArray( i ); | |
| enabledAttributes[ i ] = 0; | |
| } | |
| } | |
| capabilities = {}; | |
| compressedTextureFormats = null; | |
| currentTextureSlot = null; | |
| currentBoundTextures = {}; | |
| currentProgram = null; | |
| currentBlending = null; | |
| currentFlipSided = null; | |
| currentCullFace = null; | |
| colorBuffer.reset(); | |
| depthBuffer.reset(); | |
| stencilBuffer.reset(); | |
| } | |
| return { | |
| buffers: { | |
| color: colorBuffer, | |
| depth: depthBuffer, | |
| stencil: stencilBuffer | |
| }, | |
| initAttributes: initAttributes, | |
| enableAttribute: enableAttribute, | |
| enableAttributeAndDivisor: enableAttributeAndDivisor, | |
| disableUnusedAttributes: disableUnusedAttributes, | |
| enable: enable, | |
| disable: disable, | |
| getCompressedTextureFormats: getCompressedTextureFormats, | |
| useProgram: useProgram, | |
| setBlending: setBlending, | |
| setMaterial: setMaterial, | |
| setFlipSided: setFlipSided, | |
| setCullFace: setCullFace, | |
| setLineWidth: setLineWidth, | |
| setPolygonOffset: setPolygonOffset, | |
| setScissorTest: setScissorTest, | |
| activeTexture: activeTexture, | |
| bindTexture: bindTexture, | |
| compressedTexImage2D: compressedTexImage2D, | |
| texImage2D: texImage2D, | |
| scissor: scissor, | |
| viewport: viewport, | |
| reset: reset | |
| }; | |
| } | |
| /** | |
| * @author mrdoob / http://mrdoob.com/ | |
| */ | |
| function WebGLTextures( _gl, extensions, state, properties, capabilities, utils, info ) { | |
| var _isWebGL2 = ( typeof WebGL2RenderingContext !== 'undefined' && _gl instanceof WebGL2RenderingContext ); | |
| var _videoTextures = {}; | |
| var _canvas; | |
| // | |
| function clampToMaxSize( image, maxSize ) { | |
| if ( image.width > maxSize || image.height > maxSize ) { | |
| // Warning: Scaling through the canvas will only work with images that use | |
| // premultiplied alpha. | |
| var scale = maxSize / Math.max( image.width, image.height ); | |
| var canvas = document.createElementNS( 'http://www.w3.org/1999/xhtml', 'canvas' ); | |
| canvas.width = Math.floor( image.width * scale ); | |
| canvas.height = Math.floor( image.height * scale ); | |
| var context = canvas.getContext( '2d' ); | |
| context.drawImage( image, 0, 0, image.width, image.height, 0, 0, canvas.width, canvas.height ); | |
| console.warn( 'THREE.WebGLRenderer: image is too big (' + image.width + 'x' + image.height + '). Resized to ' + canvas.width + 'x' + canvas.height, image ); | |
| return canvas; | |
| } | |
| return image; | |
| } | |
| function isPowerOfTwo( image ) { | |
| return _Math.isPowerOfTwo( image.width ) && _Math.isPowerOfTwo( image.height ); | |
| } | |
| function makePowerOfTwo( image ) { | |
| if ( image instanceof HTMLImageElement || image instanceof HTMLCanvasElement || image instanceof ImageBitmap ) { | |
| if ( _canvas === undefined ) _canvas = document.createElementNS( 'http://www.w3.org/1999/xhtml', 'canvas' ); | |
| _canvas.width = _Math.floorPowerOfTwo( image.width ); | |
| _canvas.height = _Math.floorPowerOfTwo( image.height ); | |
| var context = _canvas.getContext( '2d' ); | |
| context.drawImage( image, 0, 0, _canvas.width, _canvas.height ); | |
| console.warn( 'THREE.WebGLRenderer: image is not power of two (' + image.width + 'x' + image.height + '). Resized to ' + _canvas.width + 'x' + _canvas.height, image ); | |
| return _canvas; | |
| } | |
| return image; | |
| } | |
| function textureNeedsPowerOfTwo( texture ) { | |
| return ( texture.wrapS !== ClampToEdgeWrapping || texture.wrapT !== ClampToEdgeWrapping ) || | |
| ( texture.minFilter !== NearestFilter && texture.minFilter !== LinearFilter ); | |
| } | |
| function textureNeedsGenerateMipmaps( texture, isPowerOfTwo ) { | |
| return texture.generateMipmaps && isPowerOfTwo && | |
| texture.minFilter !== NearestFilter && texture.minFilter !== LinearFilter; | |
| } | |
| // Fallback filters for non-power-of-2 textures | |
| function filterFallback( f ) { | |
| if ( f === NearestFilter || f === NearestMipMapNearestFilter || f === NearestMipMapLinearFilter ) { | |
| return _gl.NEAREST; | |
| } | |
| return _gl.LINEAR; | |
| } | |
| // | |
| function onTextureDispose( event ) { | |
| var texture = event.target; | |
| texture.removeEventListener( 'dispose', onTextureDispose ); | |
| deallocateTexture( texture ); | |
| if ( texture.isVideoTexture ) { | |
| delete _videoTextures[ texture.id ]; | |
| } | |
| info.memory.textures --; | |
| } | |
| function onRenderTargetDispose( event ) { | |
| var renderTarget = event.target; | |
| renderTarget.removeEventListener( 'dispose', onRenderTargetDispose ); | |
| deallocateRenderTarget( renderTarget ); | |
| info.memory.textures --; | |
| } | |
| // | |
| function deallocateTexture( texture ) { | |
| var textureProperties = properties.get( texture ); | |
| if ( texture.image && textureProperties.__image__webglTextureCube ) { | |
| // cube texture | |
| _gl.deleteTexture( textureProperties.__image__webglTextureCube ); | |
| } else { | |
| // 2D texture | |
| if ( textureProperties.__webglInit === undefined ) return; | |
| _gl.deleteTexture( textureProperties.__webglTexture ); | |
| } | |
| // remove all webgl properties | |
| properties.remove( texture ); | |
| } | |
| function deallocateRenderTarget( renderTarget ) { | |
| var renderTargetProperties = properties.get( renderTarget ); | |
| var textureProperties = properties.get( renderTarget.texture ); | |
| if ( ! renderTarget ) return; | |
| if ( textureProperties.__webglTexture !== undefined ) { | |
| _gl.deleteTexture( textureProperties.__webglTexture ); | |
| } | |
| if ( renderTarget.depthTexture ) { | |
| renderTarget.depthTexture.dispose(); | |
| } | |
| if ( renderTarget.isWebGLRenderTargetCube ) { | |
| for ( var i = 0; i < 6; i ++ ) { | |
| _gl.deleteFramebuffer( renderTargetProperties.__webglFramebuffer[ i ] ); | |
| if ( renderTargetProperties.__webglDepthbuffer ) _gl.deleteRenderbuffer( renderTargetProperties.__webglDepthbuffer[ i ] ); | |
| } | |
| } else { | |
| _gl.deleteFramebuffer( renderTargetProperties.__webglFramebuffer ); | |
| if ( renderTargetProperties.__webglDepthbuffer ) _gl.deleteRenderbuffer( renderTargetProperties.__webglDepthbuffer ); | |
| } | |
| properties.remove( renderTarget.texture ); | |
| properties.remove( renderTarget ); | |
| } | |
| // | |
| function setTexture2D( texture, slot ) { | |
| var textureProperties = properties.get( texture ); | |
| if ( texture.isVideoTexture ) updateVideoTexture( texture ); | |
| if ( texture.version > 0 && textureProperties.__version !== texture.version ) { | |
| var image = texture.image; | |
| if ( image === undefined ) { | |
| console.warn( 'THREE.WebGLRenderer: Texture marked for update but image is undefined', texture ); | |
| } else if ( image.complete === false ) { | |
| console.warn( 'THREE.WebGLRenderer: Texture marked for update but image is incomplete', texture ); | |
| } else { | |
| uploadTexture( textureProperties, texture, slot ); | |
| return; | |
| } | |
| } | |
| state.activeTexture( _gl.TEXTURE0 + slot ); | |
| state.bindTexture( _gl.TEXTURE_2D, textureProperties.__webglTexture ); | |
| } | |
| function setTextureCube( texture, slot ) { | |
| var textureProperties = properties.get( texture ); | |
| if ( texture.image.length === 6 ) { | |
| if ( texture.version > 0 && textureProperties.__version !== texture.version ) { | |
| if ( ! textureProperties.__image__webglTextureCube ) { | |
| texture.addEventListener( 'dispose', onTextureDispose ); | |
| textureProperties.__image__webglTextureCube = _gl.createTexture(); | |
| info.memory.textures ++; | |
| } | |
| state.activeTexture( _gl.TEXTURE0 + slot ); | |
| state.bindTexture( _gl.TEXTURE_CUBE_MAP, textureProperties.__image__webglTextureCube ); | |
| _gl.pixelStorei( _gl.UNPACK_FLIP_Y_WEBGL, texture.flipY ); | |
| var isCompressed = ( texture && texture.isCompressedTexture ); | |
| var isDataTexture = ( texture.image[ 0 ] && texture.image[ 0 ].isDataTexture ); | |
| var cubeImage = []; | |
| for ( var i = 0; i < 6; i ++ ) { | |
| if ( ! isCompressed && ! isDataTexture ) { | |
| cubeImage[ i ] = clampToMaxSize( texture.image[ i ], capabilities.maxCubemapSize ); | |
| } else { | |
| cubeImage[ i ] = isDataTexture ? texture.image[ i ].image : texture.image[ i ]; | |
| } | |
| } | |
| var image = cubeImage[ 0 ], | |
| isPowerOfTwoImage = isPowerOfTwo( image ), | |
| glFormat = utils.convert( texture.format ), | |
| glType = utils.convert( texture.type ); | |
| setTextureParameters( _gl.TEXTURE_CUBE_MAP, texture, isPowerOfTwoImage ); | |
| for ( var i = 0; i < 6; i ++ ) { | |
| if ( ! isCompressed ) { | |
| if ( isDataTexture ) { | |
| state.texImage2D( _gl.TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, glFormat, cubeImage[ i ].width, cubeImage[ i ].height, 0, glFormat, glType, cubeImage[ i ].data ); | |
| } else { | |
| state.texImage2D( _gl.TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, glFormat, glFormat, glType, cubeImage[ i ] ); | |
| } | |
| } else { | |
| var mipmap, mipmaps = cubeImage[ i ].mipmaps; | |
| for ( var j = 0, jl = mipmaps.length; j < jl; j ++ ) { | |
| mipmap = mipmaps[ j ]; | |
| if ( texture.format !== RGBAFormat && texture.format !== RGBFormat ) { | |
| if ( state.getCompressedTextureFormats().indexOf( glFormat ) > - 1 ) { | |
| state.compressedTexImage2D( _gl.TEXTURE_CUBE_MAP_POSITIVE_X + i, j, glFormat, mipmap.width, mipmap.height, 0, mipmap.data ); | |
| } else { | |
| console.warn( 'THREE.WebGLRenderer: Attempt to load unsupported compressed texture format in .setTextureCube()' ); | |
| } | |
| } else { | |
| state.texImage2D( _gl.TEXTURE_CUBE_MAP_POSITIVE_X + i, j, glFormat, mipmap.width, mipmap.height, 0, glFormat, glType, mipmap.data ); | |
| } | |
| } | |
| } | |
| } | |
| if ( textureNeedsGenerateMipmaps( texture, isPowerOfTwoImage ) ) { | |
| _gl.generateMipmap( _gl.TEXTURE_CUBE_MAP ); | |
| } | |
| textureProperties.__version = texture.version; | |
| if ( texture.onUpdate ) texture.onUpdate( texture ); | |
| } else { | |
| state.activeTexture( _gl.TEXTURE0 + slot ); | |
| state.bindTexture( _gl.TEXTURE_CUBE_MAP, textureProperties.__image__webglTextureCube ); | |
| } | |
| } | |
| } | |
| function setTextureCubeDynamic( texture, slot ) { | |
| state.activeTexture( _gl.TEXTURE0 + slot ); | |
| state.bindTexture( _gl.TEXTURE_CUBE_MAP, properties.get( texture ).__webglTexture ); | |
| } | |
| function setTextureParameters( textureType, texture, isPowerOfTwoImage ) { | |
| var extension; | |
| if ( isPowerOfTwoImage ) { | |
| _gl.texParameteri( textureType, _gl.TEXTURE_WRAP_S, utils.convert( texture.wrapS ) ); | |
| _gl.texParameteri( textureType, _gl.TEXTURE_WRAP_T, utils.convert( texture.wrapT ) ); | |
| _gl.texParameteri( textureType, _gl.TEXTURE_MAG_FILTER, utils.convert( texture.magFilter ) ); | |
| _gl.texParameteri( textureType, _gl.TEXTURE_MIN_FILTER, utils.convert( texture.minFilter ) ); | |
| } else { | |
| _gl.texParameteri( textureType, _gl.TEXTURE_WRAP_S, _gl.CLAMP_TO_EDGE ); | |
| _gl.texParameteri( textureType, _gl.TEXTURE_WRAP_T, _gl.CLAMP_TO_EDGE ); | |
| if ( texture.wrapS !== ClampToEdgeWrapping || texture.wrapT !== ClampToEdgeWrapping ) { | |
| console.warn( 'THREE.WebGLRenderer: Texture is not power of two. Texture.wrapS and Texture.wrapT should be set to THREE.ClampToEdgeWrapping.', texture ); | |
| } | |
| _gl.texParameteri( textureType, _gl.TEXTURE_MAG_FILTER, filterFallback( texture.magFilter ) ); | |
| _gl.texParameteri( textureType, _gl.TEXTURE_MIN_FILTER, filterFallback( texture.minFilter ) ); | |
| if ( texture.minFilter !== NearestFilter && texture.minFilter !== LinearFilter ) { | |
| console.warn( 'THREE.WebGLRenderer: Texture is not power of two. Texture.minFilter should be set to THREE.NearestFilter or THREE.LinearFilter.', texture ); | |
| } | |
| } | |
| extension = extensions.get( 'EXT_texture_filter_anisotropic' ); | |
| if ( extension ) { | |
| if ( texture.type === FloatType && extensions.get( 'OES_texture_float_linear' ) === null ) return; | |
| if ( texture.type === HalfFloatType && extensions.get( 'OES_texture_half_float_linear' ) === null ) return; | |
| if ( texture.anisotropy > 1 || properties.get( texture ).__currentAnisotropy ) { | |
| _gl.texParameterf( textureType, extension.TEXTURE_MAX_ANISOTROPY_EXT, Math.min( texture.anisotropy, capabilities.getMaxAnisotropy() ) ); | |
| properties.get( texture ).__currentAnisotropy = texture.anisotropy; | |
| } | |
| } | |
| } | |
| function uploadTexture( textureProperties, texture, slot ) { | |
| if ( textureProperties.__webglInit === undefined ) { | |
| textureProperties.__webglInit = true; | |
| texture.addEventListener( 'dispose', onTextureDispose ); | |
| textureProperties.__webglTexture = _gl.createTexture(); | |
| info.memory.textures ++; | |
| } | |
| state.activeTexture( _gl.TEXTURE0 + slot ); | |
| state.bindTexture( _gl.TEXTURE_2D, textureProperties.__webglTexture ); | |
| _gl.pixelStorei( _gl.UNPACK_FLIP_Y_WEBGL, texture.flipY ); | |
| _gl.pixelStorei( _gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, texture.premultiplyAlpha ); | |
| _gl.pixelStorei( _gl.UNPACK_ALIGNMENT, texture.unpackAlignment ); | |
| var image = clampToMaxSize( texture.image, capabilities.maxTextureSize ); | |
| if ( textureNeedsPowerOfTwo( texture ) && isPowerOfTwo( image ) === false ) { | |
| image = makePowerOfTwo( image ); | |
| } | |
| var isPowerOfTwoImage = isPowerOfTwo( image ), | |
| glFormat = utils.convert( texture.format ), | |
| glType = utils.convert( texture.type ); | |
| setTextureParameters( _gl.TEXTURE_2D, texture, isPowerOfTwoImage ); | |
| var mipmap, mipmaps = texture.mipmaps; | |
| if ( texture.isDepthTexture ) { | |
| // populate depth texture with dummy data | |
| var internalFormat = _gl.DEPTH_COMPONENT; | |
| if ( texture.type === FloatType ) { | |
| if ( ! _isWebGL2 ) throw new Error( 'Float Depth Texture only supported in WebGL2.0' ); | |
| internalFormat = _gl.DEPTH_COMPONENT32F; | |
| } else if ( _isWebGL2 ) { | |
| // WebGL 2.0 requires signed internalformat for glTexImage2D | |
| internalFormat = _gl.DEPTH_COMPONENT16; | |
| } | |
| if ( texture.format === DepthFormat && internalFormat === _gl.DEPTH_COMPONENT ) { | |
| // The error INVALID_OPERATION is generated by texImage2D if format and internalformat are | |
| // DEPTH_COMPONENT and type is not UNSIGNED_SHORT or UNSIGNED_INT | |
| // (https://www.khronos.org/registry/webgl/extensions/WEBGL_depth_texture/) | |
| if ( texture.type !== UnsignedShortType && texture.type !== UnsignedIntType ) { | |
| console.warn( 'THREE.WebGLRenderer: Use UnsignedShortType or UnsignedIntType for DepthFormat DepthTexture.' ); | |
| texture.type = UnsignedShortType; | |
| glType = utils.convert( texture.type ); | |
| } | |
| } | |
| // Depth stencil textures need the DEPTH_STENCIL internal format | |
| // (https://www.khronos.org/registry/webgl/extensions/WEBGL_depth_texture/) | |
| if ( texture.format === DepthStencilFormat ) { | |
| internalFormat = _gl.DEPTH_STENCIL; | |
| // The error INVALID_OPERATION is generated by texImage2D if format and internalformat are | |
| // DEPTH_STENCIL and type is not UNSIGNED_INT_24_8_WEBGL. | |
| // (https://www.khronos.org/registry/webgl/extensions/WEBGL_depth_texture/) | |
| if ( texture.type !== UnsignedInt248Type ) { | |
| console.warn( 'THREE.WebGLRenderer: Use UnsignedInt248Type for DepthStencilFormat DepthTexture.' ); | |
| texture.type = UnsignedInt248Type; | |
| glType = utils.convert( texture.type ); | |
| } | |
| } | |
| state.texImage2D( _gl.TEXTURE_2D, 0, internalFormat, image.width, image.height, 0, glFormat, glType, null ); | |
| } else if ( texture.isDataTexture ) { | |
| // use manually created mipmaps if available | |
| // if there are no manual mipmaps | |
| // set 0 level mipmap and then use GL to generate other mipmap levels | |
| if ( mipmaps.length > 0 && isPowerOfTwoImage ) { | |
| for ( var i = 0, il = mipmaps.length; i < il; i ++ ) { | |
| mipmap = mipmaps[ i ]; | |
| state.texImage2D( _gl.TEXTURE_2D, i, glFormat, mipmap.width, mipmap.height, 0, glFormat, glType, mipmap.data ); | |
| } | |
| texture.generateMipmaps = false; | |
| } else { | |
| state.texImage2D( _gl.TEXTURE_2D, 0, glFormat, image.width, image.height, 0, glFormat, glType, image.data ); | |
| } | |
| } else if ( texture.isCompressedTexture ) { | |
| for ( var i = 0, il = mipmaps.length; i < il; i ++ ) { | |
| mipmap = mipmaps[ i ]; | |
| if ( texture.format !== RGBAFormat && texture.format !== RGBFormat ) { | |
| if ( state.getCompressedTextureFormats().indexOf( glFormat ) > - 1 ) { | |
| state.compressedTexImage2D( _gl.TEXTURE_2D, i, glFormat, mipmap.width, mipmap.height, 0, mipmap.data ); | |
| } else { | |
| console.warn( 'THREE.WebGLRenderer: Attempt to load unsupported compressed texture format in .uploadTexture()' ); | |
| } | |
| } else { | |
| state.texImage2D( _gl.TEXTURE_2D, i, glFormat, mipmap.width, mipmap.height, 0, glFormat, glType, mipmap.data ); | |
| } | |
| } | |
| } else { | |
| // regular Texture (image, video, canvas) | |
| // use manually created mipmaps if available | |
| // if there are no manual mipmaps | |
| // set 0 level mipmap and then use GL to generate other mipmap levels | |
| if ( mipmaps.length > 0 && isPowerOfTwoImage ) { | |
| for ( var i = 0, il = mipmaps.length; i < il; i ++ ) { | |
| mipmap = mipmaps[ i ]; | |
| state.texImage2D( _gl.TEXTURE_2D, i, glFormat, glFormat, glType, mipmap ); | |
| } | |
| texture.generateMipmaps = false; | |
| } else { | |
| state.texImage2D( _gl.TEXTURE_2D, 0, glFormat, glFormat, glType, image ); | |
| } | |
| } | |
| if ( textureNeedsGenerateMipmaps( texture, isPowerOfTwoImage ) ) _gl.generateMipmap( _gl.TEXTURE_2D ); | |
| textureProperties.__version = texture.version; | |
| if ( texture.onUpdate ) texture.onUpdate( texture ); | |
| } | |
| // Render targets | |
| // Setup storage for target texture and bind it to correct framebuffer | |
| function setupFrameBufferTexture( framebuffer, renderTarget, attachment, textureTarget ) { | |
| var glFormat = utils.convert( renderTarget.texture.format ); | |
| var glType = utils.convert( renderTarget.texture.type ); | |
| state.texImage2D( textureTarget, 0, glFormat, renderTarget.width, renderTarget.height, 0, glFormat, glType, null ); | |
| _gl.bindFramebuffer( _gl.FRAMEBUFFER, framebuffer ); | |
| _gl.framebufferTexture2D( _gl.FRAMEBUFFER, attachment, textureTarget, properties.get( renderTarget.texture ).__webglTexture, 0 ); | |
| _gl.bindFramebuffer( _gl.FRAMEBUFFER, null ); | |
| } | |
| // Setup storage for internal depth/stencil buffers and bind to correct framebuffer | |
| function setupRenderBufferStorage( renderbuffer, renderTarget ) { | |
| _gl.bindRenderbuffer( _gl.RENDERBUFFER, renderbuffer ); | |
| if ( renderTarget.depthBuffer && ! renderTarget.stencilBuffer ) { | |
| _gl.renderbufferStorage( _gl.RENDERBUFFER, _gl.DEPTH_COMPONENT16, renderTarget.width, renderTarget.height ); | |
| _gl.framebufferRenderbuffer( _gl.FRAMEBUFFER, _gl.DEPTH_ATTACHMENT, _gl.RENDERBUFFER, renderbuffer ); | |
| } else if ( renderTarget.depthBuffer && renderTarget.stencilBuffer ) { | |
| _gl.renderbufferStorage( _gl.RENDERBUFFER, _gl.DEPTH_STENCIL, renderTarget.width, renderTarget.height ); | |
| _gl.framebufferRenderbuffer( _gl.FRAMEBUFFER, _gl.DEPTH_STENCIL_ATTACHMENT, _gl.RENDERBUFFER, renderbuffer ); | |
| } else { | |
| // FIXME: We don't support !depth !stencil | |
| _gl.renderbufferStorage( _gl.RENDERBUFFER, _gl.RGBA4, renderTarget.width, renderTarget.height ); | |
| } | |
| _gl.bindRenderbuffer( _gl.RENDERBUFFER, null ); | |
| } | |
| // Setup resources for a Depth Texture for a FBO (needs an extension) | |
| function setupDepthTexture( framebuffer, renderTarget ) { | |
| var isCube = ( renderTarget && renderTarget.isWebGLRenderTargetCube ); | |
| if ( isCube ) throw new Error( 'Depth Texture with cube render targets is not supported' ); | |
| _gl.bindFramebuffer( _gl.FRAMEBUFFER, framebuffer ); | |
| if ( ! ( renderTarget.depthTexture && renderTarget.depthTexture.isDepthTexture ) ) { | |
| throw new Error( 'renderTarget.depthTexture must be an instance of THREE.DepthTexture' ); | |
| } | |
| // upload an empty depth texture with framebuffer size | |
| if ( ! properties.get( renderTarget.depthTexture ).__webglTexture || | |
| renderTarget.depthTexture.image.width !== renderTarget.width || | |
| renderTarget.depthTexture.image.height !== renderTarget.height ) { | |
| renderTarget.depthTexture.image.width = renderTarget.width; | |
| renderTarget.depthTexture.image.height = renderTarget.height; | |
| renderTarget.depthTexture.needsUpdate = true; | |
| } | |
| setTexture2D( renderTarget.depthTexture, 0 ); | |
| var webglDepthTexture = properties.get( renderTarget.depthTexture ).__webglTexture; | |
| if ( renderTarget.depthTexture.format === DepthFormat ) { | |
| _gl.framebufferTexture2D( _gl.FRAMEBUFFER, _gl.DEPTH_ATTACHMENT, _gl.TEXTURE_2D, webglDepthTexture, 0 ); | |
| } else if ( renderTarget.depthTexture.format === DepthStencilFormat ) { | |
| _gl.framebufferTexture2D( _gl.FRAMEBUFFER, _gl.DEPTH_STENCIL_ATTACHMENT, _gl.TEXTURE_2D, webglDepthTexture, 0 ); | |
| } else { | |
| throw new Error( 'Unknown depthTexture format' ); | |
| } | |
| } | |
| // Setup GL resources for a non-texture depth buffer | |
| function setupDepthRenderbuffer( renderTarget ) { | |
| var renderTargetProperties = properties.get( renderTarget ); | |
| var isCube = ( renderTarget.isWebGLRenderTargetCube === true ); | |
| if ( renderTarget.depthTexture ) { | |
| if ( isCube ) throw new Error( 'target.depthTexture not supported in Cube render targets' ); | |
| setupDepthTexture( renderTargetProperties.__webglFramebuffer, renderTarget ); | |
| } else { | |
| if ( isCube ) { | |
| renderTargetProperties.__webglDepthbuffer = []; | |
| for ( var i = 0; i < 6; i ++ ) { | |
| _gl.bindFramebuffer( _gl.FRAMEBUFFER, renderTargetProperties.__webglFramebuffer[ i ] ); | |
| renderTargetProperties.__webglDepthbuffer[ i ] = _gl.createRenderbuffer(); | |
| setupRenderBufferStorage( renderTargetProperties.__webglDepthbuffer[ i ], renderTarget ); | |
| } | |
| } else { | |
| _gl.bindFramebuffer( _gl.FRAMEBUFFER, renderTargetProperties.__webglFramebuffer ); | |
| renderTargetProperties.__webglDepthbuffer = _gl.createRenderbuffer(); | |
| setupRenderBufferStorage( renderTargetProperties.__webglDepthbuffer, renderTarget ); | |
| } | |
| } | |
| _gl.bindFramebuffer( _gl.FRAMEBUFFER, null ); | |
| } | |
| // Set up GL resources for the render target | |
| function setupRenderTarget( renderTarget ) { | |
| var renderTargetProperties = properties.get( renderTarget ); | |
| var textureProperties = properties.get( renderTarget.texture ); | |
| renderTarget.addEventListener( 'dispose', onRenderTargetDispose ); | |
| textureProperties.__webglTexture = _gl.createTexture(); | |
| info.memory.textures ++; | |
| var isCube = ( renderTarget.isWebGLRenderTargetCube === true ); | |
| var isTargetPowerOfTwo = isPowerOfTwo( renderTarget ); | |
| // Setup framebuffer | |
| if ( isCube ) { | |
| renderTargetProperties.__webglFramebuffer = []; | |
| for ( var i = 0; i < 6; i ++ ) { | |
| renderTargetProperties.__webglFramebuffer[ i ] = _gl.createFramebuffer(); | |
| } | |
| } else { | |
| renderTargetProperties.__webglFramebuffer = _gl.createFramebuffer(); | |
| } | |
| // Setup color buffer | |
| if ( isCube ) { | |
| state.bindTexture( _gl.TEXTURE_CUBE_MAP, textureProperties.__webglTexture ); | |
| setTextureParameters( _gl.TEXTURE_CUBE_MAP, renderTarget.texture, isTargetPowerOfTwo ); | |
| for ( var i = 0; i < 6; i ++ ) { | |
| setupFrameBufferTexture( renderTargetProperties.__webglFramebuffer[ i ], renderTarget, _gl.COLOR_ATTACHMENT0, _gl.TEXTURE_CUBE_MAP_POSITIVE_X + i ); | |
| } | |
| if ( textureNeedsGenerateMipmaps( renderTarget.texture, isTargetPowerOfTwo ) ) _gl.generateMipmap( _gl.TEXTURE_CUBE_MAP ); | |
| state.bindTexture( _gl.TEXTURE_CUBE_MAP, null ); | |
| } else { | |
| state.bindTexture( _gl.TEXTURE_2D, textureProperties.__webglTexture ); | |
| setTextureParameters( _gl.TEXTURE_2D, renderTarget.texture, isTargetPowerOfTwo ); | |
| setupFrameBufferTexture( renderTargetProperties.__webglFramebuffer, renderTarget, _gl.COLOR_ATTACHMENT0, _gl.TEXTURE_2D ); | |
| if ( textureNeedsGenerateMipmaps( renderTarget.texture, isTargetPowerOfTwo ) ) _gl.generateMipmap( _gl.TEXTURE_2D ); | |
| state.bindTexture( _gl.TEXTURE_2D, null ); | |
| } | |
| // Setup depth and stencil buffers | |
| if ( renderTarget.depthBuffer ) { | |
| setupDepthRenderbuffer( renderTarget ); | |
| } | |
| } | |
| function updateRenderTargetMipmap( renderTarget ) { | |
| var texture = renderTarget.texture; | |
| var isTargetPowerOfTwo = isPowerOfTwo( renderTarget ); | |
| if ( textureNeedsGenerateMipmaps( texture, isTargetPowerOfTwo ) ) { | |
| var target = renderTarget.isWebGLRenderTargetCube ? _gl.TEXTURE_CUBE_MAP : _gl.TEXTURE_2D; | |
| var webglTexture = properties.get( texture ).__webglTexture; | |
| state.bindTexture( target, webglTexture ); | |
| _gl.generateMipmap( target ); | |
| state.bindTexture( target, null ); | |
| } | |
| } | |
| function updateVideoTexture( texture ) { | |
| var id = texture.id; | |
| var frame = info.render.frame; | |
| // Check the last frame we updated the VideoTexture | |
| if ( _videoTextures[ id ] !== frame ) { | |
| _videoTextures[ id ] = frame; | |
| texture.update(); | |
| } | |
| } | |
| this.setTexture2D = setTexture2D; | |
| this.setTextureCube = setTextureCube; | |
| this.setTextureCubeDynamic = setTextureCubeDynamic; | |
| this.setupRenderTarget = setupRenderTarget; | |
| this.updateRenderTargetMipmap = updateRenderTargetMipmap; | |
| } | |
| /** | |
| * @author thespite / http://www.twitter.com/thespite | |
| */ | |
| function WebGLUtils( gl, extensions ) { | |
| function convert( p ) { | |
| var extension; | |
| if ( p === RepeatWrapping ) return gl.REPEAT; | |
| if ( p === ClampToEdgeWrapping ) return gl.CLAMP_TO_EDGE; | |
| if ( p === MirroredRepeatWrapping ) return gl.MIRRORED_REPEAT; | |
| if ( p === NearestFilter ) return gl.NEAREST; | |
| if ( p === NearestMipMapNearestFilter ) return gl.NEAREST_MIPMAP_NEAREST; | |
| if ( p === NearestMipMapLinearFilter ) return gl.NEAREST_MIPMAP_LINEAR; | |
| if ( p === LinearFilter ) return gl.LINEAR; | |
| if ( p === LinearMipMapNearestFilter ) return gl.LINEAR_MIPMAP_NEAREST; | |
| if ( p === LinearMipMapLinearFilter ) return gl.LINEAR_MIPMAP_LINEAR; | |
| if ( p === UnsignedByteType ) return gl.UNSIGNED_BYTE; | |
| if ( p === UnsignedShort4444Type ) return gl.UNSIGNED_SHORT_4_4_4_4; | |
| if ( p === UnsignedShort5551Type ) return gl.UNSIGNED_SHORT_5_5_5_1; | |
| if ( p === UnsignedShort565Type ) return gl.UNSIGNED_SHORT_5_6_5; | |
| if ( p === ByteType ) return gl.BYTE; | |
| if ( p === ShortType ) return gl.SHORT; | |
| if ( p === UnsignedShortType ) return gl.UNSIGNED_SHORT; | |
| if ( p === IntType ) return gl.INT; | |
| if ( p === UnsignedIntType ) return gl.UNSIGNED_INT; | |
| if ( p === FloatType ) return gl.FLOAT; | |
| if ( p === HalfFloatType ) { | |
| extension = extensions.get( 'OES_texture_half_float' ); | |
| if ( extension !== null ) return extension.HALF_FLOAT_OES; | |
| } | |
| if ( p === AlphaFormat ) return gl.ALPHA; | |
| if ( p === RGBFormat ) return gl.RGB; | |
| if ( p === RGBAFormat ) return gl.RGBA; | |
| if ( p === LuminanceFormat ) return gl.LUMINANCE; | |
| if ( p === LuminanceAlphaFormat ) return gl.LUMINANCE_ALPHA; | |
| if ( p === DepthFormat ) return gl.DEPTH_COMPONENT; | |
| if ( p === DepthStencilFormat ) return gl.DEPTH_STENCIL; | |
| if ( p === AddEquation ) return gl.FUNC_ADD; | |
| if ( p === SubtractEquation ) return gl.FUNC_SUBTRACT; | |
| if ( p === ReverseSubtractEquation ) return gl.FUNC_REVERSE_SUBTRACT; | |
| if ( p === ZeroFactor ) return gl.ZERO; | |
| if ( p === OneFactor ) return gl.ONE; | |
| if ( p === SrcColorFactor ) return gl.SRC_COLOR; | |
| if ( p === OneMinusSrcColorFactor ) return gl.ONE_MINUS_SRC_COLOR; | |
| if ( p === SrcAlphaFactor ) return gl.SRC_ALPHA; | |
| if ( p === OneMinusSrcAlphaFactor ) return gl.ONE_MINUS_SRC_ALPHA; | |
| if ( p === DstAlphaFactor ) return gl.DST_ALPHA; | |
| if ( p === OneMinusDstAlphaFactor ) return gl.ONE_MINUS_DST_ALPHA; | |
| if ( p === DstColorFactor ) return gl.DST_COLOR; | |
| if ( p === OneMinusDstColorFactor ) return gl.ONE_MINUS_DST_COLOR; | |
| if ( p === SrcAlphaSaturateFactor ) return gl.SRC_ALPHA_SATURATE; | |
| if ( p === RGB_S3TC_DXT1_Format || p === RGBA_S3TC_DXT1_Format || | |
| p === RGBA_S3TC_DXT3_Format || p === RGBA_S3TC_DXT5_Format ) { | |
| extension = extensions.get( 'WEBGL_compressed_texture_s3tc' ); | |
| if ( extension !== null ) { | |
| if ( p === RGB_S3TC_DXT1_Format ) return extension.COMPRESSED_RGB_S3TC_DXT1_EXT; | |
| if ( p === RGBA_S3TC_DXT1_Format ) return extension.COMPRESSED_RGBA_S3TC_DXT1_EXT; | |
| if ( p === RGBA_S3TC_DXT3_Format ) return extension.COMPRESSED_RGBA_S3TC_DXT3_EXT; | |
| if ( p === RGBA_S3TC_DXT5_Format ) return extension.COMPRESSED_RGBA_S3TC_DXT5_EXT; | |
| } | |
| } | |
| if ( p === RGB_PVRTC_4BPPV1_Format || p === RGB_PVRTC_2BPPV1_Format || | |
| p === RGBA_PVRTC_4BPPV1_Format || p === RGBA_PVRTC_2BPPV1_Format ) { | |
| extension = extensions.get( 'WEBGL_compressed_texture_pvrtc' ); | |
| if ( extension !== null ) { | |
| if ( p === RGB_PVRTC_4BPPV1_Format ) return extension.COMPRESSED_RGB_PVRTC_4BPPV1_IMG; | |
| if ( p === RGB_PVRTC_2BPPV1_Format ) return extension.COMPRESSED_RGB_PVRTC_2BPPV1_IMG; | |
| if ( p === RGBA_PVRTC_4BPPV1_Format ) return extension.COMPRESSED_RGBA_PVRTC_4BPPV1_IMG; | |
| if ( p === RGBA_PVRTC_2BPPV1_Format ) return extension.COMPRESSED_RGBA_PVRTC_2BPPV1_IMG; | |
| } | |
| } | |
| if ( p === RGB_ETC1_Format ) { | |
| extension = extensions.get( 'WEBGL_compressed_texture_etc1' ); | |
| if ( extension !== null ) return extension.COMPRESSED_RGB_ETC1_WEBGL; | |
| } | |
| if ( p === RGBA_ASTC_4x4_Format || p === RGBA_ASTC_5x4_Format || p === RGBA_ASTC_5x5_Format || | |
| p === RGBA_ASTC_6x5_Format || p === RGBA_ASTC_6x6_Format || p === RGBA_ASTC_8x5_Format || | |
| p === RGBA_ASTC_8x6_Format || p === RGBA_ASTC_8x8_Format || p === RGBA_ASTC_10x5_Format || | |
| p === RGBA_ASTC_10x6_Format || p === RGBA_ASTC_10x8_Format || p === RGBA_ASTC_10x10_Format || | |
| p === RGBA_ASTC_12x10_Format || p === RGBA_ASTC_12x12_Format ) { | |
| extension = extensions.get( 'WEBGL_compressed_texture_astc' ); | |
| if ( extension !== null ) { | |
| return p; | |
| } | |
| } | |
| if ( p === MinEquation || p === MaxEquation ) { | |
| extension = extensions.get( 'EXT_blend_minmax' ); | |
| if ( extension !== null ) { | |
| if ( p === MinEquation ) return extension.MIN_EXT; | |
| if ( p === MaxEquation ) return extension.MAX_EXT; | |
| } | |
| } | |
| if ( p === UnsignedInt248Type ) { | |
| extension = extensions.get( 'WEBGL_depth_texture' ); | |
| if ( extension !== null ) return extension.UNSIGNED_INT_24_8_WEBGL; | |
| } | |
| return 0; | |
| } | |
| return { convert: convert }; | |
| } | |
| /** | |
| * @author mrdoob / http://mrdoob.com/ | |
| * @author greggman / http://games.greggman.com/ | |
| * @author zz85 / http://www.lab4games.net/zz85/blog | |
| * @author tschw | |
| */ | |
| function PerspectiveCamera( fov, aspect, near, far ) { | |
| Camera.call( this ); | |
| this.type = 'PerspectiveCamera'; | |
| this.fov = fov !== undefined ? fov : 50; | |
| this.zoom = 1; | |
| this.near = near !== undefined ? near : 0.1; | |
| this.far = far !== undefined ? far : 2000; | |
| this.focus = 10; | |
| this.aspect = aspect !== undefined ? aspect : 1; | |
| this.view = null; | |
| this.filmGauge = 35; // width of the film (default in millimeters) | |
| this.filmOffset = 0; // horizontal film offset (same unit as gauge) | |
| this.updateProjectionMatrix(); | |
| } | |
| PerspectiveCamera.prototype = Object.assign( Object.create( Camera.prototype ), { | |
| constructor: PerspectiveCamera, | |
| isPerspectiveCamera: true, | |
| copy: function ( source, recursive ) { | |
| Camera.prototype.copy.call( this, source, recursive ); | |
| this.fov = source.fov; | |
| this.zoom = source.zoom; | |
| this.near = source.near; | |
| this.far = source.far; | |
| this.focus = source.focus; | |
| this.aspect = source.aspect; | |
| this.view = source.view === null ? null : Object.assign( {}, source.view ); | |
| this.filmGauge = source.filmGauge; | |
| this.filmOffset = source.filmOffset; | |
| return this; | |
| }, | |
| /** | |
| * Sets the FOV by focal length in respect to the current .filmGauge. | |
| * | |
| * The default film gauge is 35, so that the focal length can be specified for | |
| * a 35mm (full frame) camera. | |
| * | |
| * Values for focal length and film gauge must have the same unit. | |
| */ | |
| setFocalLength: function ( focalLength ) { | |
| // see http://www.bobatkins.com/photography/technical/field_of_view.html | |
| var vExtentSlope = 0.5 * this.getFilmHeight() / focalLength; | |
| this.fov = _Math.RAD2DEG * 2 * Math.atan( vExtentSlope ); | |
| this.updateProjectionMatrix(); | |
| }, | |
| /** | |
| * Calculates the focal length from the current .fov and .filmGauge. | |
| */ | |
| getFocalLength: function () { | |
| var vExtentSlope = Math.tan( _Math.DEG2RAD * 0.5 * this.fov ); | |
| return 0.5 * this.getFilmHeight() / vExtentSlope; | |
| }, | |
| getEffectiveFOV: function () { | |
| return _Math.RAD2DEG * 2 * Math.atan( | |
| Math.tan( _Math.DEG2RAD * 0.5 * this.fov ) / this.zoom ); | |
| }, | |
| getFilmWidth: function () { | |
| // film not completely covered in portrait format (aspect < 1) | |
| return this.filmGauge * Math.min( this.aspect, 1 ); | |
| }, | |
| getFilmHeight: function () { | |
| // film not completely covered in landscape format (aspect > 1) | |
| return this.filmGauge / Math.max( this.aspect, 1 ); | |
| }, | |
| /** | |
| * Sets an offset in a larger frustum. This is useful for multi-window or | |
| * multi-monitor/multi-machine setups. | |
| * | |
| * For example, if you have 3x2 monitors and each monitor is 1920x1080 and | |
| * the monitors are in grid like this | |
| * | |
| * +---+---+---+ | |
| * | A | B | C | | |
| * +---+---+---+ | |
| * | D | E | F | | |
| * +---+---+---+ | |
| * | |
| * then for each monitor you would call it like this | |
| * | |
| * var w = 1920; | |
| * var h = 1080; | |
| * var fullWidth = w * 3; | |
| * var fullHeight = h * 2; | |
| * | |
| * --A-- | |
| * camera.setOffset( fullWidth, fullHeight, w * 0, h * 0, w, h ); | |
| * --B-- | |
| * camera.setOffset( fullWidth, fullHeight, w * 1, h * 0, w, h ); | |
| * --C-- | |
| * camera.setOffset( fullWidth, fullHeight, w * 2, h * 0, w, h ); | |
| * --D-- | |
| * camera.setOffset( fullWidth, fullHeight, w * 0, h * 1, w, h ); | |
| * --E-- | |
| * camera.setOffset( fullWidth, fullHeight, w * 1, h * 1, w, h ); | |
| * --F-- | |
| * camera.setOffset( fullWidth, fullHeight, w * 2, h * 1, w, h ); | |
| * | |
| * Note there is no reason monitors have to be the same size or in a grid. | |
| */ | |
| setViewOffset: function ( fullWidth, fullHeight, x, y, width, height ) { | |
| this.aspect = fullWidth / fullHeight; | |
| if ( this.view === null ) { | |
| this.view = { | |
| enabled: true, | |
| fullWidth: 1, | |
| fullHeight: 1, | |
| offsetX: 0, | |
| offsetY: 0, | |
| width: 1, | |
| height: 1 | |
| }; | |
| } | |
| this.view.enabled = true; | |
| this.view.fullWidth = fullWidth; | |
| this.view.fullHeight = fullHeight; | |
| this.view.offsetX = x; | |
| this.view.offsetY = y; | |
| this.view.width = width; | |
| this.view.height = height; | |
| this.updateProjectionMatrix(); | |
| }, | |
| clearViewOffset: function () { | |
| if ( this.view !== null ) { | |
| this.view.enabled = false; | |
| } | |
| this.updateProjectionMatrix(); | |
| }, | |
| updateProjectionMatrix: function () { | |
| var near = this.near, | |
| top = near * Math.tan( | |
| _Math.DEG2RAD * 0.5 * this.fov ) / this.zoom, | |
| height = 2 * top, | |
| width = this.aspect * height, | |
| left = - 0.5 * width, | |
| view = this.view; | |
| if ( this.view !== null && this.view.enabled ) { | |
| var fullWidth = view.fullWidth, | |
| fullHeight = view.fullHeight; | |
| left += view.offsetX * width / fullWidth; | |
| top -= view.offsetY * height / fullHeight; | |
| width *= view.width / fullWidth; | |
| height *= view.height / fullHeight; | |
| } | |
| var skew = this.filmOffset; | |
| if ( skew !== 0 ) left += near * skew / this.getFilmWidth(); | |
| this.projectionMatrix.makePerspective( left, left + width, top, top - height, near, this.far ); | |
| }, | |
| toJSON: function ( meta ) { | |
| var data = Object3D.prototype.toJSON.call( this, meta ); | |
| data.object.fov = this.fov; | |
| data.object.zoom = this.zoom; | |
| data.object.near = this.near; | |
| data.object.far = this.far; | |
| data.object.focus = this.focus; | |
| data.object.aspect = this.aspect; | |
| if ( this.view !== null ) data.object.view = Object.assign( {}, this.view ); | |
| data.object.filmGauge = this.filmGauge; | |
| data.object.filmOffset = this.filmOffset; | |
| return data; | |
| } | |
| } ); | |
| /** | |
| * @author mrdoob / http://mrdoob.com/ | |
| */ | |
| function ArrayCamera( array ) { | |
| PerspectiveCamera.call( this ); | |
| this.cameras = array || []; | |
| } | |
| ArrayCamera.prototype = Object.assign( Object.create( PerspectiveCamera.prototype ), { | |
| constructor: ArrayCamera, | |
| isArrayCamera: true | |
| } ); | |
| /** | |
| * @author mrdoob / http://mrdoob.com/ | |
| */ | |
| function WebVRManager( renderer ) { | |
| var scope = this; | |
| var device = null; | |
| var frameData = null; | |
| var poseTarget = null; | |
| var standingMatrix = new Matrix4(); | |
| var standingMatrixInverse = new Matrix4(); | |
| if ( typeof window !== 'undefined' && 'VRFrameData' in window ) { | |
| frameData = new window.VRFrameData(); | |
| } | |
| var matrixWorldInverse = new Matrix4(); | |
| var cameraL = new PerspectiveCamera(); | |
| cameraL.bounds = new Vector4( 0.0, 0.0, 0.5, 1.0 ); | |
| cameraL.layers.enable( 1 ); | |
| var cameraR = new PerspectiveCamera(); | |
| cameraR.bounds = new Vector4( 0.5, 0.0, 0.5, 1.0 ); | |
| cameraR.layers.enable( 2 ); | |
| var cameraVR = new ArrayCamera( [ cameraL, cameraR ] ); | |
| cameraVR.layers.enable( 1 ); | |
| cameraVR.layers.enable( 2 ); | |
| // | |
| var currentSize, currentPixelRatio; | |
| function onVRDisplayPresentChange() { | |
| if ( device !== null && device.isPresenting ) { | |
| var eyeParameters = device.getEyeParameters( 'left' ); | |
| var renderWidth = eyeParameters.renderWidth; | |
| var renderHeight = eyeParameters.renderHeight; | |
| currentPixelRatio = renderer.getPixelRatio(); | |
| currentSize = renderer.getSize(); | |
| renderer.setDrawingBufferSize( renderWidth * 2, renderHeight, 1 ); | |
| } else if ( scope.enabled ) { | |
| renderer.setDrawingBufferSize( currentSize.width, currentSize.height, currentPixelRatio ); | |
| } | |
| } | |
| if ( typeof window !== 'undefined' ) { | |
| window.addEventListener( 'vrdisplaypresentchange', onVRDisplayPresentChange, false ); | |
| } | |
| // | |
| this.enabled = false; | |
| this.userHeight = 1.6; | |
| this.getDevice = function () { | |
| return device; | |
| }; | |
| this.setDevice = function ( value ) { | |
| if ( value !== undefined ) device = value; | |
| }; | |
| this.setPoseTarget = function ( object ) { | |
| if ( object !== undefined ) poseTarget = object; | |
| }; | |
| this.getCamera = function ( camera ) { | |
| if ( device === null ) return camera; | |
| device.depthNear = camera.near; | |
| device.depthFar = camera.far; | |
| device.getFrameData( frameData ); | |
| // | |
| var pose = frameData.pose; | |
| var poseObject = poseTarget !== null ? poseTarget : camera; | |
| if ( pose.position !== null ) { | |
| poseObject.position.fromArray( pose.position ); | |
| } else { | |
| poseObject.position.set( 0, 0, 0 ); | |
| } | |
| if ( pose.orientation !== null ) { | |
| poseObject.quaternion.fromArray( pose.orientation ); | |
| } | |
| var stageParameters = device.stageParameters; | |
| if ( stageParameters ) { | |
| standingMatrix.fromArray( stageParameters.sittingToStandingTransform ); | |
| } else { | |
| standingMatrix.makeTranslation( 0, scope.userHeight, 0 ); | |
| } | |
| poseObject.position.applyMatrix4( standingMatrix ); | |
| poseObject.updateMatrixWorld(); | |
| if ( device.isPresenting === false ) return camera; | |
| // | |
| cameraL.near = camera.near; | |
| cameraR.near = camera.near; | |
| cameraL.far = camera.far; | |
| cameraR.far = camera.far; | |
| cameraVR.matrixWorld.copy( camera.matrixWorld ); | |
| cameraVR.matrixWorldInverse.copy( camera.matrixWorldInverse ); | |
| cameraL.matrixWorldInverse.fromArray( frameData.leftViewMatrix ); | |
| cameraR.matrixWorldInverse.fromArray( frameData.rightViewMatrix ); | |
| // TODO (mrdoob) Double check this code | |
| standingMatrixInverse.getInverse( standingMatrix ); | |
| cameraL.matrixWorldInverse.multiply( standingMatrixInverse ); | |
| cameraR.matrixWorldInverse.multiply( standingMatrixInverse ); | |
| var parent = poseObject.parent; | |
| if ( parent !== null ) { | |
| matrixWorldInverse.getInverse( parent.matrixWorld ); | |
| cameraL.matrixWorldInverse.multiply( matrixWorldInverse ); | |
| cameraR.matrixWorldInverse.multiply( matrixWorldInverse ); | |
| } | |
| // envMap and Mirror needs camera.matrixWorld | |
| cameraL.matrixWorld.getInverse( cameraL.matrixWorldInverse ); | |
| cameraR.matrixWorld.getInverse( cameraR.matrixWorldInverse ); | |
| cameraL.projectionMatrix.fromArray( frameData.leftProjectionMatrix ); | |
| cameraR.projectionMatrix.fromArray( frameData.rightProjectionMatrix ); | |
| // HACK (mrdoob) | |
| // https://github.com/w3c/webvr/issues/203 | |
| cameraVR.projectionMatrix.copy( cameraL.projectionMatrix ); | |
| // | |
| var layers = device.getLayers(); | |
| if ( layers.length ) { | |
| var layer = layers[ 0 ]; | |
| if ( layer.leftBounds !== null && layer.leftBounds.length === 4 ) { | |
| cameraL.bounds.fromArray( layer.leftBounds ); | |
| } | |
| if ( layer.rightBounds !== null && layer.rightBounds.length === 4 ) { | |
| cameraR.bounds.fromArray( layer.rightBounds ); | |
| } | |
| } | |
| return cameraVR; | |
| }; | |
| this.getStandingMatrix = function () { | |
| return standingMatrix; | |
| }; | |
| this.submitFrame = function () { | |
| if ( device && device.isPresenting ) device.submitFrame(); | |
| }; | |
| this.dispose = function () { | |
| if ( typeof window !== 'undefined' ) { | |
| window.removeEventListener( 'vrdisplaypresentchange', onVRDisplayPresentChange ); | |
| } | |
| }; | |
| } | |
| /** | |
| * @author supereggbert / http://www.paulbrunt.co.uk/ | |
| * @author mrdoob / http://mrdoob.com/ | |
| * @author alteredq / http://alteredqualia.com/ | |
| * @author szimek / https://github.com/szimek/ | |
| * @author tschw | |
| */ | |
| function WebGLRenderer( parameters ) { | |
| console.log( 'THREE.WebGLRenderer', REVISION ); | |
| parameters = parameters || {}; | |
| var _canvas = parameters.canvas !== undefined ? parameters.canvas : document.createElementNS( 'http://www.w3.org/1999/xhtml', 'canvas' ), | |
| _context = parameters.context !== undefined ? parameters.context : null, | |
| _alpha = parameters.alpha !== undefined ? parameters.alpha : false, | |
| _depth = parameters.depth !== undefined ? parameters.depth : true, | |
| _stencil = parameters.stencil !== undefined ? parameters.stencil : true, | |
| _antialias = parameters.antialias !== undefined ? parameters.antialias : false, | |
| _premultipliedAlpha = parameters.premultipliedAlpha !== undefined ? parameters.premultipliedAlpha : true, | |
| _preserveDrawingBuffer = parameters.preserveDrawingBuffer !== undefined ? parameters.preserveDrawingBuffer : false, | |
| _powerPreference = parameters.powerPreference !== undefined ? parameters.powerPreference : 'default'; | |
| var currentRenderList = null; | |
| var currentRenderState = null; | |
| // public properties | |
| this.domElement = _canvas; | |
| this.context = null; | |
| // clearing | |
| this.autoClear = true; | |
| this.autoClearColor = true; | |
| this.autoClearDepth = true; | |
| this.autoClearStencil = true; | |
| // scene graph | |
| this.sortObjects = true; | |
| // user-defined clipping | |
| this.clippingPlanes = []; | |
| this.localClippingEnabled = false; | |
| // physically based shading | |
| this.gammaFactor = 2.0; // for backwards compatibility | |
| this.gammaInput = false; | |
| this.gammaOutput = false; | |
| // physical lights | |
| this.physicallyCorrectLights = false; | |
| // tone mapping | |
| this.toneMapping = LinearToneMapping; | |
| this.toneMappingExposure = 1.0; | |
| this.toneMappingWhitePoint = 1.0; | |
| // morphs | |
| this.maxMorphTargets = 8; | |
| this.maxMorphNormals = 4; | |
| // internal properties | |
| var _this = this, | |
| _isContextLost = false, | |
| // internal state cache | |
| _currentRenderTarget = null, | |
| _currentFramebuffer = null, | |
| _currentMaterialId = - 1, | |
| _currentGeometryProgram = '', | |
| _currentCamera = null, | |
| _currentArrayCamera = null, | |
| _currentViewport = new Vector4(), | |
| _currentScissor = new Vector4(), | |
| _currentScissorTest = null, | |
| // | |
| _usedTextureUnits = 0, | |
| // | |
| _width = _canvas.width, | |
| _height = _canvas.height, | |
| _pixelRatio = 1, | |
| _viewport = new Vector4( 0, 0, _width, _height ), | |
| _scissor = new Vector4( 0, 0, _width, _height ), | |
| _scissorTest = false, | |
| // frustum | |
| _frustum = new Frustum(), | |
| // clipping | |
| _clipping = new WebGLClipping(), | |
| _clippingEnabled = false, | |
| _localClippingEnabled = false, | |
| // camera matrices cache | |
| _projScreenMatrix = new Matrix4(), | |
| _vector3 = new Vector3(); | |
| function getTargetPixelRatio() { | |
| return _currentRenderTarget === null ? _pixelRatio : 1; | |
| } | |
| // initialize | |
| var _gl; | |
| try { | |
| var contextAttributes = { | |
| alpha: _alpha, | |
| depth: _depth, | |
| stencil: _stencil, | |
| antialias: _antialias, | |
| premultipliedAlpha: _premultipliedAlpha, | |
| preserveDrawingBuffer: _preserveDrawingBuffer, | |
| powerPreference: _powerPreference | |
| }; | |
| // event listeners must be registered before WebGL context is created, see #12753 | |
| _canvas.addEventListener( 'webglcontextlost', onContextLost, false ); | |
| _canvas.addEventListener( 'webglcontextrestored', onContextRestore, false ); | |
| _gl = _context || _canvas.getContext( 'webgl', contextAttributes ) || _canvas.getContext( 'experimental-webgl', contextAttributes ); | |
| if ( _gl === null ) { | |
| if ( _canvas.getContext( 'webgl' ) !== null ) { | |
| throw new Error( 'Error creating WebGL context with your selected attributes.' ); | |
| } else { | |
| throw new Error( 'Error creating WebGL context.' ); | |
| } | |
| } | |
| // Some experimental-webgl implementations do not have getShaderPrecisionFormat | |
| if ( _gl.getShaderPrecisionFormat === undefined ) { | |
| _gl.getShaderPrecisionFormat = function () { | |
| return { 'rangeMin': 1, 'rangeMax': 1, 'precision': 1 }; | |
| }; | |
| } | |
| } catch ( error ) { | |
| console.error( 'THREE.WebGLRenderer: ' + error.message ); | |
| } | |
| var extensions, capabilities, state, info; | |
| var properties, textures, attributes, geometries, objects; | |
| var programCache, renderLists, renderStates; | |
| var background, morphtargets, bufferRenderer, indexedBufferRenderer; | |
| var spriteRenderer; | |
| var utils; | |
| function initGLContext() { | |
| extensions = new WebGLExtensions( _gl ); | |
| extensions.get( 'WEBGL_depth_texture' ); | |
| extensions.get( 'OES_texture_float' ); | |
| extensions.get( 'OES_texture_float_linear' ); | |
| extensions.get( 'OES_texture_half_float' ); | |
| extensions.get( 'OES_texture_half_float_linear' ); | |
| extensions.get( 'OES_standard_derivatives' ); | |
| extensions.get( 'OES_element_index_uint' ); | |
| extensions.get( 'ANGLE_instanced_arrays' ); | |
| utils = new WebGLUtils( _gl, extensions ); | |
| capabilities = new WebGLCapabilities( _gl, extensions, parameters ); | |
| state = new WebGLState( _gl, extensions, utils ); | |
| state.scissor( _currentScissor.copy( _scissor ).multiplyScalar( _pixelRatio ) ); | |
| state.viewport( _currentViewport.copy( _viewport ).multiplyScalar( _pixelRatio ) ); | |
| info = new WebGLInfo( _gl ); | |
| properties = new WebGLProperties(); | |
| textures = new WebGLTextures( _gl, extensions, state, properties, capabilities, utils, info ); | |
| attributes = new WebGLAttributes( _gl ); | |
| geometries = new WebGLGeometries( _gl, attributes, info ); | |
| objects = new WebGLObjects( geometries, info ); | |
| morphtargets = new WebGLMorphtargets( _gl ); | |
| programCache = new WebGLPrograms( _this, extensions, capabilities ); | |
| renderLists = new WebGLRenderLists(); | |
| renderStates = new WebGLRenderStates(); | |
| background = new WebGLBackground( _this, state, geometries, _premultipliedAlpha ); | |
| bufferRenderer = new WebGLBufferRenderer( _gl, extensions, info ); | |
| indexedBufferRenderer = new WebGLIndexedBufferRenderer( _gl, extensions, info ); | |
| spriteRenderer = new WebGLSpriteRenderer( _this, _gl, state, textures, capabilities ); | |
| info.programs = programCache.programs; | |
| _this.context = _gl; | |
| _this.capabilities = capabilities; | |
| _this.extensions = extensions; | |
| _this.properties = properties; | |
| _this.renderLists = renderLists; | |
| _this.state = state; | |
| _this.info = info; | |
| } | |
| initGLContext(); | |
| // vr | |
| var vr = new WebVRManager( _this ); | |
| this.vr = vr; | |
| // shadow map | |
| var shadowMap = new WebGLShadowMap( _this, objects, capabilities.maxTextureSize ); | |
| this.shadowMap = shadowMap; | |
| // API | |
| this.getContext = function () { | |
| return _gl; | |
| }; | |
| this.getContextAttributes = function () { | |
| return _gl.getContextAttributes(); | |
| }; | |
| this.forceContextLoss = function () { | |
| var extension = extensions.get( 'WEBGL_lose_context' ); | |
| if ( extension ) extension.loseContext(); | |
| }; | |
| this.forceContextRestore = function () { | |
| var extension = extensions.get( 'WEBGL_lose_context' ); | |
| if ( extension ) extension.restoreContext(); | |
| }; | |
| this.getPixelRatio = function () { | |
| return _pixelRatio; | |
| }; | |
| this.setPixelRatio = function ( value ) { | |
| if ( value === undefined ) return; | |
| _pixelRatio = value; | |
| this.setSize( _width, _height, false ); | |
| }; | |
| this.getSize = function () { | |
| return { | |
| width: _width, | |
| height: _height | |
| }; | |
| }; | |
| this.setSize = function ( width, height, updateStyle ) { | |
| var device = vr.getDevice(); | |
| if ( device && device.isPresenting ) { | |
| console.warn( 'THREE.WebGLRenderer: Can\'t change size while VR device is presenting.' ); | |
| return; | |
| } | |
| _width = width; | |
| _height = height; | |
| _canvas.width = width * _pixelRatio; | |
| _canvas.height = height * _pixelRatio; | |
| if ( updateStyle !== false ) { | |
| _canvas.style.width = width + 'px'; | |
| _canvas.style.height = height + 'px'; | |
| } | |
| this.setViewport( 0, 0, width, height ); | |
| }; | |
| this.getDrawingBufferSize = function () { | |
| return { | |
| width: _width * _pixelRatio, | |
| height: _height * _pixelRatio | |
| }; | |
| }; | |
| this.setDrawingBufferSize = function ( width, height, pixelRatio ) { | |
| _width = width; | |
| _height = height; | |
| _pixelRatio = pixelRatio; | |
| _canvas.width = width * pixelRatio; | |
| _canvas.height = height * pixelRatio; | |
| this.setViewport( 0, 0, width, height ); | |
| }; | |
| this.getCurrentViewport = function () { | |
| return _currentViewport; | |
| }; | |
| this.setViewport = function ( x, y, width, height ) { | |
| _viewport.set( x, _height - y - height, width, height ); | |
| state.viewport( _currentViewport.copy( _viewport ).multiplyScalar( _pixelRatio ) ); | |
| }; | |
| this.setScissor = function ( x, y, width, height ) { | |
| _scissor.set( x, _height - y - height, width, height ); | |
| state.scissor( _currentScissor.copy( _scissor ).multiplyScalar( _pixelRatio ) ); | |
| }; | |
| this.setScissorTest = function ( boolean ) { | |
| state.setScissorTest( _scissorTest = boolean ); | |
| }; | |
| // Clearing | |
| this.getClearColor = function () { | |
| return background.getClearColor(); | |
| }; | |
| this.setClearColor = function () { | |
| background.setClearColor.apply( background, arguments ); | |
| }; | |
| this.getClearAlpha = function () { | |
| return background.getClearAlpha(); | |
| }; | |
| this.setClearAlpha = function () { | |
| background.setClearAlpha.apply( background, arguments ); | |
| }; | |
| this.clear = function ( color, depth, stencil ) { | |
| var bits = 0; | |
| if ( color === undefined || color ) bits |= _gl.COLOR_BUFFER_BIT; | |
| if ( depth === undefined || depth ) bits |= _gl.DEPTH_BUFFER_BIT; | |
| if ( stencil === undefined || stencil ) bits |= _gl.STENCIL_BUFFER_BIT; | |
| _gl.clear( bits ); | |
| }; | |
| this.clearColor = function () { | |
| this.clear( true, false, false ); | |
| }; | |
| this.clearDepth = function () { | |
| this.clear( false, true, false ); | |
| }; | |
| this.clearStencil = function () { | |
| this.clear( false, false, true ); | |
| }; | |
| this.clearTarget = function ( renderTarget, color, depth, stencil ) { | |
| this.setRenderTarget( renderTarget ); | |
| this.clear( color, depth, stencil ); | |
| }; | |
| // | |
| this.dispose = function () { | |
| _canvas.removeEventListener( 'webglcontextlost', onContextLost, false ); | |
| _canvas.removeEventListener( 'webglcontextrestored', onContextRestore, false ); | |
| renderLists.dispose(); | |
| renderStates.dispose(); | |
| properties.dispose(); | |
| objects.dispose(); | |
| vr.dispose(); | |
| stopAnimation(); | |
| }; | |
| // Events | |
| function onContextLost( event ) { | |
| event.preventDefault(); | |
| console.log( 'THREE.WebGLRenderer: Context Lost.' ); | |
| _isContextLost = true; | |
| } | |
| function onContextRestore( /* event */ ) { | |
| console.log( 'THREE.WebGLRenderer: Context Restored.' ); | |
| _isContextLost = false; | |
| initGLContext(); | |
| } | |
| function onMaterialDispose( event ) { | |
| var material = event.target; | |
| material.removeEventListener( 'dispose', onMaterialDispose ); | |
| deallocateMaterial( material ); | |
| } | |
| // Buffer deallocation | |
| function deallocateMaterial( material ) { | |
| releaseMaterialProgramReference( material ); | |
| properties.remove( material ); | |
| } | |
| function releaseMaterialProgramReference( material ) { | |
| var programInfo = properties.get( material ).program; | |
| material.program = undefined; | |
| if ( programInfo !== undefined ) { | |
| programCache.releaseProgram( programInfo ); | |
| } | |
| } | |
| // Buffer rendering | |
| function renderObjectImmediate( object, program, material ) { | |
| object.render( function ( object ) { | |
| _this.renderBufferImmediate( object, program, material ); | |
| } ); | |
| } | |
| this.renderBufferImmediate = function ( object, program, material ) { | |
| state.initAttributes(); | |
| var buffers = properties.get( object ); | |
| if ( object.hasPositions && ! buffers.position ) buffers.position = _gl.createBuffer(); | |
| if ( object.hasNormals && ! buffers.normal ) buffers.normal = _gl.createBuffer(); | |
| if ( object.hasUvs && ! buffers.uv ) buffers.uv = _gl.createBuffer(); | |
| if ( object.hasColors && ! buffers.color ) buffers.color = _gl.createBuffer(); | |
| var programAttributes = program.getAttributes(); | |
| if ( object.hasPositions ) { | |
| _gl.bindBuffer( _gl.ARRAY_BUFFER, buffers.position ); | |
| _gl.bufferData( _gl.ARRAY_BUFFER, object.positionArray, _gl.DYNAMIC_DRAW ); | |
| state.enableAttribute( programAttributes.position ); | |
| _gl.vertexAttribPointer( programAttributes.position, 3, _gl.FLOAT, false, 0, 0 ); | |
| } | |
| if ( object.hasNormals ) { | |
| _gl.bindBuffer( _gl.ARRAY_BUFFER, buffers.normal ); | |
| if ( ! material.isMeshPhongMaterial && | |
| ! material.isMeshStandardMaterial && | |
| ! material.isMeshNormalMaterial && | |
| material.flatShading === true ) { | |
| for ( var i = 0, l = object.count * 3; i < l; i += 9 ) { | |
| var array = object.normalArray; | |
| var nx = ( array[ i + 0 ] + array[ i + 3 ] + array[ i + 6 ] ) / 3; | |
| var ny = ( array[ i + 1 ] + array[ i + 4 ] + array[ i + 7 ] ) / 3; | |
| var nz = ( array[ i + 2 ] + array[ i + 5 ] + array[ i + 8 ] ) / 3; | |
| array[ i + 0 ] = nx; | |
| array[ i + 1 ] = ny; | |
| array[ i + 2 ] = nz; | |
| array[ i + 3 ] = nx; | |
| array[ i + 4 ] = ny; | |
| array[ i + 5 ] = nz; | |
| array[ i + 6 ] = nx; | |
| array[ i + 7 ] = ny; | |
| array[ i + 8 ] = nz; | |
| } | |
| } | |
| _gl.bufferData( _gl.ARRAY_BUFFER, object.normalArray, _gl.DYNAMIC_DRAW ); | |
| state.enableAttribute( programAttributes.normal ); | |
| _gl.vertexAttribPointer( programAttributes.normal, 3, _gl.FLOAT, false, 0, 0 ); | |
| } | |
| if ( object.hasUvs && material.map ) { | |
| _gl.bindBuffer( _gl.ARRAY_BUFFER, buffers.uv ); | |
| _gl.bufferData( _gl.ARRAY_BUFFER, object.uvArray, _gl.DYNAMIC_DRAW ); | |
| state.enableAttribute( programAttributes.uv ); | |
| _gl.vertexAttribPointer( programAttributes.uv, 2, _gl.FLOAT, false, 0, 0 ); | |
| } | |
| if ( object.hasColors && material.vertexColors !== NoColors ) { | |
| _gl.bindBuffer( _gl.ARRAY_BUFFER, buffers.color ); | |
| _gl.bufferData( _gl.ARRAY_BUFFER, object.colorArray, _gl.DYNAMIC_DRAW ); | |
| state.enableAttribute( programAttributes.color ); | |
| _gl.vertexAttribPointer( programAttributes.color, 3, _gl.FLOAT, false, 0, 0 ); | |
| } | |
| state.disableUnusedAttributes(); | |
| _gl.drawArrays( _gl.TRIANGLES, 0, object.count ); | |
| object.count = 0; | |
| }; | |
| this.renderBufferDirect = function ( camera, fog, geometry, material, object, group ) { | |
| var frontFaceCW = ( object.isMesh && object.matrixWorld.determinant() < 0 ); | |
| state.setMaterial( material, frontFaceCW ); | |
| var program = setProgram( camera, fog, material, object ); | |
| var geometryProgram = geometry.id + '_' + program.id + '_' + ( material.wireframe === true ); | |
| var updateBuffers = false; | |
| if ( geometryProgram !== _currentGeometryProgram ) { | |
| _currentGeometryProgram = geometryProgram; | |
| updateBuffers = true; | |
| } | |
| if ( object.morphTargetInfluences ) { | |
| morphtargets.update( object, geometry, material, program ); | |
| updateBuffers = true; | |
| } | |
| // | |
| var index = geometry.index; | |
| var position = geometry.attributes.position; | |
| var rangeFactor = 1; | |
| if ( material.wireframe === true ) { | |
| index = geometries.getWireframeAttribute( geometry ); | |
| rangeFactor = 2; | |
| } | |
| var attribute; | |
| var renderer = bufferRenderer; | |
| if ( index !== null ) { | |
| attribute = attributes.get( index ); | |
| renderer = indexedBufferRenderer; | |
| renderer.setIndex( attribute ); | |
| } | |
| if ( updateBuffers ) { | |
| setupVertexAttributes( material, program, geometry ); | |
| if ( index !== null ) { | |
| _gl.bindBuffer( _gl.ELEMENT_ARRAY_BUFFER, attribute.buffer ); | |
| } | |
| } | |
| // | |
| var dataCount = Infinity; | |
| if ( index !== null ) { | |
| dataCount = index.count; | |
| } else if ( position !== undefined ) { | |
| dataCount = position.count; | |
| } | |
| var rangeStart = geometry.drawRange.start * rangeFactor; | |
| var rangeCount = geometry.drawRange.count * rangeFactor; | |
| var groupStart = group !== null ? group.start * rangeFactor : 0; | |
| var groupCount = group !== null ? group.count * rangeFactor : Infinity; | |
| var drawStart = Math.max( rangeStart, groupStart ); | |
| var drawEnd = Math.min( dataCount, rangeStart + rangeCount, groupStart + groupCount ) - 1; | |
| var drawCount = Math.max( 0, drawEnd - drawStart + 1 ); | |
| if ( drawCount === 0 ) return; | |
| // | |
| if ( object.isMesh ) { | |
| if ( material.wireframe === true ) { | |
| state.setLineWidth( material.wireframeLinewidth * getTargetPixelRatio() ); | |
| renderer.setMode( _gl.LINES ); | |
| } else { | |
| switch ( object.drawMode ) { | |
| case TrianglesDrawMode: | |
| renderer.setMode( _gl.TRIANGLES ); | |
| break; | |
| case TriangleStripDrawMode: | |
| renderer.setMode( _gl.TRIANGLE_STRIP ); | |
| break; | |
| case TriangleFanDrawMode: | |
| renderer.setMode( _gl.TRIANGLE_FAN ); | |
| break; | |
| } | |
| } | |
| } else if ( object.isLine ) { | |
| var lineWidth = material.linewidth; | |
| if ( lineWidth === undefined ) lineWidth = 1; // Not using Line*Material | |
| state.setLineWidth( lineWidth * getTargetPixelRatio() ); | |
| if ( object.isLineSegments ) { | |
| renderer.setMode( _gl.LINES ); | |
| } else if ( object.isLineLoop ) { | |
| renderer.setMode( _gl.LINE_LOOP ); | |
| } else { | |
| renderer.setMode( _gl.LINE_STRIP ); | |
| } | |
| } else if ( object.isPoints ) { | |
| renderer.setMode( _gl.POINTS ); | |
| } | |
| if ( geometry && geometry.isInstancedBufferGeometry ) { | |
| if ( geometry.maxInstancedCount > 0 ) { | |
| renderer.renderInstances( geometry, drawStart, drawCount ); | |
| } | |
| } else { | |
| renderer.render( drawStart, drawCount ); | |
| } | |
| }; | |
| function setupVertexAttributes( material, program, geometry, startIndex ) { | |
| if ( geometry && geometry.isInstancedBufferGeometry ) { | |
| if ( extensions.get( 'ANGLE_instanced_arrays' ) === null ) { | |
| console.error( 'THREE.WebGLRenderer.setupVertexAttributes: using THREE.InstancedBufferGeometry but hardware does not support extension ANGLE_instanced_arrays.' ); | |
| return; | |
| } | |
| } | |
| if ( startIndex === undefined ) startIndex = 0; | |
| state.initAttributes(); | |
| var geometryAttributes = geometry.attributes; | |
| var programAttributes = program.getAttributes(); | |
| var materialDefaultAttributeValues = material.defaultAttributeValues; | |
| for ( var name in programAttributes ) { | |
| var programAttribute = programAttributes[ name ]; | |
| if ( programAttribute >= 0 ) { | |
| var geometryAttribute = geometryAttributes[ name ]; | |
| if ( geometryAttribute !== undefined ) { | |
| var normalized = geometryAttribute.normalized; | |
| var size = geometryAttribute.itemSize; | |
| var attribute = attributes.get( geometryAttribute ); | |
| // TODO Attribute may not be available on context restore | |
| if ( attribute === undefined ) continue; | |
| var buffer = attribute.buffer; | |
| var type = attribute.type; | |
| var bytesPerElement = attribute.bytesPerElement; | |
| if ( geometryAttribute.isInterleavedBufferAttribute ) { | |
| var data = geometryAttribute.data; | |
| var stride = data.stride; | |
| var offset = geometryAttribute.offset; | |
| if ( data && data.isInstancedInterleavedBuffer ) { | |
| state.enableAttributeAndDivisor( programAttribute, data.meshPerAttribute ); | |
| if ( geometry.maxInstancedCount === undefined ) { | |
| geometry.maxInstancedCount = data.meshPerAttribute * data.count; | |
| } | |
| } else { | |
| state.enableAttribute( programAttribute ); | |
| } | |
| _gl.bindBuffer( _gl.ARRAY_BUFFER, buffer ); | |
| _gl.vertexAttribPointer( programAttribute, size, type, normalized, stride * bytesPerElement, ( startIndex * stride + offset ) * bytesPerElement ); | |
| } else { | |
| if ( geometryAttribute.isInstancedBufferAttribute ) { | |
| state.enableAttributeAndDivisor( programAttribute, geometryAttribute.meshPerAttribute ); | |
| if ( geometry.maxInstancedCount === undefined ) { | |
| geometry.maxInstancedCount = geometryAttribute.meshPerAttribute * geometryAttribute.count; | |
| } | |
| } else { | |
| state.enableAttribute( programAttribute ); | |
| } | |
| _gl.bindBuffer( _gl.ARRAY_BUFFER, buffer ); | |
| _gl.vertexAttribPointer( programAttribute, size, type, normalized, 0, startIndex * size * bytesPerElement ); | |
| } | |
| } else if ( materialDefaultAttributeValues !== undefined ) { | |
| var value = materialDefaultAttributeValues[ name ]; | |
| if ( value !== undefined ) { | |
| switch ( value.length ) { | |
| case 2: | |
| _gl.vertexAttrib2fv( programAttribute, value ); | |
| break; | |
| case 3: | |
| _gl.vertexAttrib3fv( programAttribute, value ); | |
| break; | |
| case 4: | |
| _gl.vertexAttrib4fv( programAttribute, value ); | |
| break; | |
| default: | |
| _gl.vertexAttrib1fv( programAttribute, value ); | |
| } | |
| } | |
| } | |
| } | |
| } | |
| state.disableUnusedAttributes(); | |
| } | |
| // Compile | |
| this.compile = function ( scene, camera ) { | |
| currentRenderState = renderStates.get( scene, camera ); | |
| currentRenderState.init(); | |
| scene.traverse( function ( object ) { | |
| if ( object.isLight ) { | |
| currentRenderState.pushLight( object ); | |
| if ( object.castShadow ) { | |
| currentRenderState.pushShadow( object ); | |
| } | |
| } | |
| } ); | |
| currentRenderState.setupLights( camera ); | |
| scene.traverse( function ( object ) { | |
| if ( object.material ) { | |
| if ( Array.isArray( object.material ) ) { | |
| for ( var i = 0; i < object.material.length; i ++ ) { | |
| initMaterial( object.material[ i ], scene.fog, object ); | |
| } | |
| } else { | |
| initMaterial( object.material, scene.fog, object ); | |
| } | |
| } | |
| } ); | |
| }; | |
| // Animation Loop | |
| var isAnimating = false; | |
| var onAnimationFrame = null; | |
| function startAnimation() { | |
| if ( isAnimating ) return; | |
| requestAnimationLoopFrame(); | |
| isAnimating = true; | |
| } | |
| function stopAnimation() { | |
| isAnimating = false; | |
| } | |
| function requestAnimationLoopFrame() { | |
| var device = vr.getDevice(); | |
| if ( device && device.isPresenting ) { | |
| device.requestAnimationFrame( animationLoop ); | |
| } else { | |
| window.requestAnimationFrame( animationLoop ); | |
| } | |
| } | |
| function animationLoop( time ) { | |
| if ( isAnimating === false ) return; | |
| onAnimationFrame( time ); | |
| requestAnimationLoopFrame(); | |
| } | |
| this.animate = function ( callback ) { | |
| onAnimationFrame = callback; | |
| onAnimationFrame !== null ? startAnimation() : stopAnimation(); | |
| }; | |
| // Rendering | |
| this.render = function ( scene, camera, renderTarget, forceClear ) { | |
| if ( ! ( camera && camera.isCamera ) ) { | |
| console.error( 'THREE.WebGLRenderer.render: camera is not an instance of THREE.Camera.' ); | |
| return; | |
| } | |
| if ( _isContextLost ) return; | |
| // reset caching for this frame | |
| _currentGeometryProgram = ''; | |
| _currentMaterialId = - 1; | |
| _currentCamera = null; | |
| // update scene graph | |
| if ( scene.autoUpdate === true ) scene.updateMatrixWorld(); | |
| // update camera matrices and frustum | |
| if ( camera.parent === null ) camera.updateMatrixWorld(); | |
| if ( vr.enabled ) { | |
| camera = vr.getCamera( camera ); | |
| } | |
| // | |
| currentRenderState = renderStates.get( scene, camera ); | |
| currentRenderState.init(); | |
| scene.onBeforeRender( _this, scene, camera, renderTarget ); | |
| _projScreenMatrix.multiplyMatrices( camera.projectionMatrix, camera.matrixWorldInverse ); | |
| _frustum.setFromMatrix( _projScreenMatrix ); | |
| _localClippingEnabled = this.localClippingEnabled; | |
| _clippingEnabled = _clipping.init( this.clippingPlanes, _localClippingEnabled, camera ); | |
| currentRenderList = renderLists.get( scene, camera ); | |
| currentRenderList.init(); | |
| projectObject( scene, camera, _this.sortObjects ); | |
| if ( _this.sortObjects === true ) { | |
| currentRenderList.sort(); | |
| } | |
| // | |
| if ( _clippingEnabled ) _clipping.beginShadows(); | |
| var shadowsArray = currentRenderState.state.shadowsArray; | |
| shadowMap.render( shadowsArray, scene, camera ); | |
| currentRenderState.setupLights( camera ); | |
| if ( _clippingEnabled ) _clipping.endShadows(); | |
| // | |
| if ( this.info.autoReset ) this.info.reset(); | |
| if ( renderTarget === undefined ) { | |
| renderTarget = null; | |
| } | |
| this.setRenderTarget( renderTarget ); | |
| // | |
| background.render( currentRenderList, scene, camera, forceClear ); | |
| // render scene | |
| var opaqueObjects = currentRenderList.opaque; | |
| var transparentObjects = currentRenderList.transparent; | |
| if ( scene.overrideMaterial ) { | |
| var overrideMaterial = scene.overrideMaterial; | |
| if ( opaqueObjects.length ) renderObjects( opaqueObjects, scene, camera, overrideMaterial ); | |
| if ( transparentObjects.length ) renderObjects( transparentObjects, scene, camera, overrideMaterial ); | |
| } else { | |
| // opaque pass (front-to-back order) | |
| if ( opaqueObjects.length ) renderObjects( opaqueObjects, scene, camera ); | |
| // transparent pass (back-to-front order) | |
| if ( transparentObjects.length ) renderObjects( transparentObjects, scene, camera ); | |
| } | |
| // custom renderers | |
| var spritesArray = currentRenderState.state.spritesArray; | |
| spriteRenderer.render( spritesArray, scene, camera ); | |
| // Generate mipmap if we're using any kind of mipmap filtering | |
| if ( renderTarget ) { | |
| textures.updateRenderTargetMipmap( renderTarget ); | |
| } | |
| // Ensure depth buffer writing is enabled so it can be cleared on next render | |
| state.buffers.depth.setTest( true ); | |
| state.buffers.depth.setMask( true ); | |
| state.buffers.color.setMask( true ); | |
| state.setPolygonOffset( false ); | |
| scene.onAfterRender( _this, scene, camera ); | |
| if ( vr.enabled ) { | |
| vr.submitFrame(); | |
| } | |
| // _gl.finish(); | |
| currentRenderList = null; | |
| currentRenderState = null; | |
| }; | |
| /* | |
| // TODO Duplicated code (Frustum) | |
| var _sphere = new Sphere(); | |
| function isObjectViewable( object ) { | |
| var geometry = object.geometry; | |
| if ( geometry.boundingSphere === null ) | |
| geometry.computeBoundingSphere(); | |
| _sphere.copy( geometry.boundingSphere ). | |
| applyMatrix4( object.matrixWorld ); | |
| return isSphereViewable( _sphere ); | |
| } | |
| function isSpriteViewable( sprite ) { | |
| _sphere.center.set( 0, 0, 0 ); | |
| _sphere.radius = 0.7071067811865476; | |
| _sphere.applyMatrix4( sprite.matrixWorld ); | |
| return isSphereViewable( _sphere ); | |
| } | |
| function isSphereViewable( sphere ) { | |
| if ( ! _frustum.intersectsSphere( sphere ) ) return false; | |
| var numPlanes = _clipping.numPlanes; | |
| if ( numPlanes === 0 ) return true; | |
| var planes = _this.clippingPlanes, | |
| center = sphere.center, | |
| negRad = - sphere.radius, | |
| i = 0; | |
| do { | |
| // out when deeper than radius in the negative halfspace | |
| if ( planes[ i ].distanceToPoint( center ) < negRad ) return false; | |
| } while ( ++ i !== numPlanes ); | |
| return true; | |
| } | |
| */ | |
| function projectObject( object, camera, sortObjects ) { | |
| if ( object.visible === false ) return; | |
| var visible = object.layers.test( camera.layers ); | |
| if ( visible ) { | |
| if ( object.isLight ) { | |
| currentRenderState.pushLight( object ); | |
| if ( object.castShadow ) { | |
| currentRenderState.pushShadow( object ); | |
| } | |
| } else if ( object.isSprite ) { | |
| if ( ! object.frustumCulled || _frustum.intersectsSprite( object ) ) { | |
| currentRenderState.pushSprite( object ); | |
| } | |
| } else if ( object.isImmediateRenderObject ) { | |
| if ( sortObjects ) { | |
| _vector3.setFromMatrixPosition( object.matrixWorld ) | |
| .applyMatrix4( _projScreenMatrix ); | |
| } | |
| currentRenderList.push( object, null, object.material, _vector3.z, null ); | |
| } else if ( object.isMesh || object.isLine || object.isPoints ) { | |
| if ( object.isSkinnedMesh ) { | |
| object.skeleton.update(); | |
| } | |
| if ( ! object.frustumCulled || _frustum.intersectsObject( object ) ) { | |
| if ( sortObjects ) { | |
| _vector3.setFromMatrixPosition( object.matrixWorld ) | |
| .applyMatrix4( _projScreenMatrix ); | |
| } | |
| var geometry = objects.update( object ); | |
| var material = object.material; | |
| if ( Array.isArray( material ) ) { | |
| var groups = geometry.groups; | |
| for ( var i = 0, l = groups.length; i < l; i ++ ) { | |
| var group = groups[ i ]; | |
| var groupMaterial = material[ group.materialIndex ]; | |
| if ( groupMaterial && groupMaterial.visible ) { | |
| currentRenderList.push( object, geometry, groupMaterial, _vector3.z, group ); | |
| } | |
| } | |
| } else if ( material.visible ) { | |
| currentRenderList.push( object, geometry, material, _vector3.z, null ); | |
| } | |
| } | |
| } | |
| } | |
| var children = object.children; | |
| for ( var i = 0, l = children.length; i < l; i ++ ) { | |
| projectObject( children[ i ], camera, sortObjects ); | |
| } | |
| } | |
| function renderObjects( renderList, scene, camera, overrideMaterial ) { | |
| for ( var i = 0, l = renderList.length; i < l; i ++ ) { | |
| var renderItem = renderList[ i ]; | |
| var object = renderItem.object; | |
| var geometry = renderItem.geometry; | |
| var material = overrideMaterial === undefined ? renderItem.material : overrideMaterial; | |
| var group = renderItem.group; | |
| if ( camera.isArrayCamera ) { | |
| _currentArrayCamera = camera; | |
| var cameras = camera.cameras; | |
| for ( var j = 0, jl = cameras.length; j < jl; j ++ ) { | |
| var camera2 = cameras[ j ]; | |
| if ( object.layers.test( camera2.layers ) ) { | |
| var bounds = camera2.bounds; | |
| var x = bounds.x * _width; | |
| var y = bounds.y * _height; | |
| var width = bounds.z * _width; | |
| var height = bounds.w * _height; | |
| state.viewport( _currentViewport.set( x, y, width, height ).multiplyScalar( _pixelRatio ) ); | |
| renderObject( object, scene, camera2, geometry, material, group ); | |
| } | |
| } | |
| } else { | |
| _currentArrayCamera = null; | |
| renderObject( object, scene, camera, geometry, material, group ); | |
| } | |
| } | |
| } | |
| function renderObject( object, scene, camera, geometry, material, group ) { | |
| object.onBeforeRender( _this, scene, camera, geometry, material, group ); | |
| currentRenderState = renderStates.get( scene, _currentArrayCamera || camera ); | |
| object.modelViewMatrix.multiplyMatrices( camera.matrixWorldInverse, object.matrixWorld ); | |
| object.normalMatrix.getNormalMatrix( object.modelViewMatrix ); | |
| if ( object.isImmediateRenderObject ) { | |
| var frontFaceCW = ( object.isMesh && object.matrixWorld.determinant() < 0 ); | |
| state.setMaterial( material, frontFaceCW ); | |
| var program = setProgram( camera, scene.fog, material, object ); | |
| _currentGeometryProgram = ''; | |
| renderObjectImmediate( object, program, material ); | |
| } else { | |
| _this.renderBufferDirect( camera, scene.fog, geometry, material, object, group ); | |
| } | |
| object.onAfterRender( _this, scene, camera, geometry, material, group ); | |
| currentRenderState = renderStates.get( scene, _currentArrayCamera || camera ); | |
| } | |
| function initMaterial( material, fog, object ) { | |
| var materialProperties = properties.get( material ); | |
| var lights = currentRenderState.state.lights; | |
| var shadowsArray = currentRenderState.state.shadowsArray; | |
| var parameters = programCache.getParameters( | |
| material, lights.state, shadowsArray, fog, _clipping.numPlanes, _clipping.numIntersection, object ); | |
| var code = programCache.getProgramCode( material, parameters ); | |
| var program = materialProperties.program; | |
| var programChange = true; | |
| if ( program === undefined ) { | |
| // new material | |
| material.addEventListener( 'dispose', onMaterialDispose ); | |
| } else if ( program.code !== code ) { | |
| // changed glsl or parameters | |
| releaseMaterialProgramReference( material ); | |
| } else if ( materialProperties.lightsHash !== lights.state.hash ) { | |
| properties.update( material, 'lightsHash', lights.state.hash ); | |
| programChange = false; | |
| } else if ( parameters.shaderID !== undefined ) { | |
| // same glsl and uniform list | |
| return; | |
| } else { | |
| // only rebuild uniform list | |
| programChange = false; | |
| } | |
| if ( programChange ) { | |
| if ( parameters.shaderID ) { | |
| var shader = ShaderLib[ parameters.shaderID ]; | |
| materialProperties.shader = { | |
| name: material.type, | |
| uniforms: UniformsUtils.clone( shader.uniforms ), | |
| vertexShader: shader.vertexShader, | |
| fragmentShader: shader.fragmentShader | |
| }; | |
| } else { | |
| materialProperties.shader = { | |
| name: material.type, | |
| uniforms: material.uniforms, | |
| vertexShader: material.vertexShader, | |
| fragmentShader: material.fragmentShader | |
| }; | |
| } | |
| material.onBeforeCompile( materialProperties.shader ); | |
| program = programCache.acquireProgram( material, materialProperties.shader, parameters, code ); | |
| materialProperties.program = program; | |
| material.program = program; | |
| } | |
| var programAttributes = program.getAttributes(); | |
| if ( material.morphTargets ) { | |
| material.numSupportedMorphTargets = 0; | |
| for ( var i = 0; i < _this.maxMorphTargets; i ++ ) { | |
| if ( programAttributes[ 'morphTarget' + i ] >= 0 ) { | |
| material.numSupportedMorphTargets ++; | |
| } | |
| } | |
| } | |
| if ( material.morphNormals ) { | |
| material.numSupportedMorphNormals = 0; | |
| for ( var i = 0; i < _this.maxMorphNormals; i ++ ) { | |
| if ( programAttributes[ 'morphNormal' + i ] >= 0 ) { | |
| material.numSupportedMorphNormals ++; | |
| } | |
| } | |
| } | |
| var uniforms = materialProperties.shader.uniforms; | |
| if ( ! material.isShaderMaterial && | |
| ! material.isRawShaderMaterial || | |
| material.clipping === true ) { | |
| materialProperties.numClippingPlanes = _clipping.numPlanes; | |
| materialProperties.numIntersection = _clipping.numIntersection; | |
| uniforms.clippingPlanes = _clipping.uniform; | |
| } | |
| materialProperties.fog = fog; | |
| // store the light setup it was created for | |
| materialProperties.lightsHash = lights.state.hash; | |
| if ( material.lights ) { | |
| // wire up the material to this renderer's lighting state | |
| uniforms.ambientLightColor.value = lights.state.ambient; | |
| uniforms.directionalLights.value = lights.state.directional; | |
| uniforms.spotLights.value = lights.state.spot; | |
| uniforms.rectAreaLights.value = lights.state.rectArea; | |
| uniforms.pointLights.value = lights.state.point; | |
| uniforms.hemisphereLights.value = lights.state.hemi; | |
| uniforms.directionalShadowMap.value = lights.state.directionalShadowMap; | |
| uniforms.directionalShadowMatrix.value = lights.state.directionalShadowMatrix; | |
| uniforms.spotShadowMap.value = lights.state.spotShadowMap; | |
| uniforms.spotShadowMatrix.value = lights.state.spotShadowMatrix; | |
| uniforms.pointShadowMap.value = lights.state.pointShadowMap; | |
| uniforms.pointShadowMatrix.value = lights.state.pointShadowMatrix; | |
| // TODO (abelnation): add area lights shadow info to uniforms | |
| } | |
| var progUniforms = materialProperties.program.getUniforms(), | |
| uniformsList = | |
| WebGLUniforms.seqWithValue( progUniforms.seq, uniforms ); | |
| materialProperties.uniformsList = uniformsList; | |
| } | |
| function setProgram( camera, fog, material, object ) { | |
| _usedTextureUnits = 0; | |
| var materialProperties = properties.get( material ); | |
| var lights = currentRenderState.state.lights; | |
| if ( _clippingEnabled ) { | |
| if ( _localClippingEnabled || camera !== _currentCamera ) { | |
| var useCache = | |
| camera === _currentCamera && | |
| material.id === _currentMaterialId; | |
| // we might want to call this function with some ClippingGroup | |
| // object instead of the material, once it becomes feasible | |
| // (#8465, #8379) | |
| _clipping.setState( | |
| material.clippingPlanes, material.clipIntersection, material.clipShadows, | |
| camera, materialProperties, useCache ); | |
| } | |
| } | |
| if ( material.needsUpdate === false ) { | |
| if ( materialProperties.program === undefined ) { | |
| material.needsUpdate = true; | |
| } else if ( material.fog && materialProperties.fog !== fog ) { | |
| material.needsUpdate = true; | |
| } else if ( material.lights && materialProperties.lightsHash !== lights.state.hash ) { | |
| material.needsUpdate = true; | |
| } else if ( materialProperties.numClippingPlanes !== undefined && | |
| ( materialProperties.numClippingPlanes !== _clipping.numPlanes || | |
| materialProperties.numIntersection !== _clipping.numIntersection ) ) { | |
| material.needsUpdate = true; | |
| } | |
| } | |
| if ( material.needsUpdate ) { | |
| initMaterial( material, fog, object ); | |
| material.needsUpdate = false; | |
| } | |
| var refreshProgram = false; | |
| var refreshMaterial = false; | |
| var refreshLights = false; | |
| var program = materialProperties.program, | |
| p_uniforms = program.getUniforms(), | |
| m_uniforms = materialProperties.shader.uniforms; | |
| if ( state.useProgram( program.program ) ) { | |
| refreshProgram = true; | |
| refreshMaterial = true; | |
| refreshLights = true; | |
| } | |
| if ( material.id !== _currentMaterialId ) { | |
| _currentMaterialId = material.id; | |
| refreshMaterial = true; | |
| } | |
| if ( refreshProgram || camera !== _currentCamera ) { | |
| p_uniforms.setValue( _gl, 'projectionMatrix', camera.projectionMatrix ); | |
| if ( capabilities.logarithmicDepthBuffer ) { | |
| p_uniforms.setValue( _gl, 'logDepthBufFC', | |
| 2.0 / ( Math.log( camera.far + 1.0 ) / Math.LN2 ) ); | |
| } | |
| // Avoid unneeded uniform updates per ArrayCamera's sub-camera | |
| if ( _currentCamera !== ( _currentArrayCamera || camera ) ) { | |
| _currentCamera = ( _currentArrayCamera || camera ); | |
| // lighting uniforms depend on the camera so enforce an update | |
| // now, in case this material supports lights - or later, when | |
| // the next material that does gets activated: | |
| refreshMaterial = true; // set to true on material change | |
| refreshLights = true; // remains set until update done | |
| } | |
| // load material specific uniforms | |
| // (shader material also gets them for the sake of genericity) | |
| if ( material.isShaderMaterial || | |
| material.isMeshPhongMaterial || | |
| material.isMeshStandardMaterial || | |
| material.envMap ) { | |
| var uCamPos = p_uniforms.map.cameraPosition; | |
| if ( uCamPos !== undefined ) { | |
| uCamPos.setValue( _gl, | |
| _vector3.setFromMatrixPosition( camera.matrixWorld ) ); | |
| } | |
| } | |
| if ( material.isMeshPhongMaterial || | |
| material.isMeshLambertMaterial || | |
| material.isMeshBasicMaterial || | |
| material.isMeshStandardMaterial || | |
| material.isShaderMaterial || | |
| material.skinning ) { | |
| p_uniforms.setValue( _gl, 'viewMatrix', camera.matrixWorldInverse ); | |
| } | |
| } | |
| // skinning uniforms must be set even if material didn't change | |
| // auto-setting of texture unit for bone texture must go before other textures | |
| // not sure why, but otherwise weird things happen | |
| if ( material.skinning ) { | |
| p_uniforms.setOptional( _gl, object, 'bindMatrix' ); | |
| p_uniforms.setOptional( _gl, object, 'bindMatrixInverse' ); | |
| var skeleton = object.skeleton; | |
| if ( skeleton ) { | |
| var bones = skeleton.bones; | |
| if ( capabilities.floatVertexTextures ) { | |
| if ( skeleton.boneTexture === undefined ) { | |
| // layout (1 matrix = 4 pixels) | |
| // RGBA RGBA RGBA RGBA (=> column1, column2, column3, column4) | |
| // with 8x8 pixel texture max 16 bones * 4 pixels = (8 * 8) | |
| // 16x16 pixel texture max 64 bones * 4 pixels = (16 * 16) | |
| // 32x32 pixel texture max 256 bones * 4 pixels = (32 * 32) | |
| // 64x64 pixel texture max 1024 bones * 4 pixels = (64 * 64) | |
| var size = Math.sqrt( bones.length * 4 ); // 4 pixels needed for 1 matrix | |
| size = _Math.ceilPowerOfTwo( size ); | |
| size = Math.max( size, 4 ); | |
| var boneMatrices = new Float32Array( size * size * 4 ); // 4 floats per RGBA pixel | |
| boneMatrices.set( skeleton.boneMatrices ); // copy current values | |
| var boneTexture = new DataTexture( boneMatrices, size, size, RGBAFormat, FloatType ); | |
| boneTexture.needsUpdate = true; | |
| skeleton.boneMatrices = boneMatrices; | |
| skeleton.boneTexture = boneTexture; | |
| skeleton.boneTextureSize = size; | |
| } | |
| p_uniforms.setValue( _gl, 'boneTexture', skeleton.boneTexture ); | |
| p_uniforms.setValue( _gl, 'boneTextureSize', skeleton.boneTextureSize ); | |
| } else { | |
| p_uniforms.setOptional( _gl, skeleton, 'boneMatrices' ); | |
| } | |
| } | |
| } | |
| if ( refreshMaterial ) { | |
| p_uniforms.setValue( _gl, 'toneMappingExposure', _this.toneMappingExposure ); | |
| p_uniforms.setValue( _gl, 'toneMappingWhitePoint', _this.toneMappingWhitePoint ); | |
| if ( material.lights ) { | |
| // the current material requires lighting info | |
| // note: all lighting uniforms are always set correctly | |
| // they simply reference the renderer's state for their | |
| // values | |
| // | |
| // use the current material's .needsUpdate flags to set | |
| // the GL state when required | |
| markUniformsLightsNeedsUpdate( m_uniforms, refreshLights ); | |
| } | |
| // refresh uniforms common to several materials | |
| if ( fog && material.fog ) { | |
| refreshUniformsFog( m_uniforms, fog ); | |
| } | |
| if ( material.isMeshBasicMaterial ) { | |
| refreshUniformsCommon( m_uniforms, material ); | |
| } else if ( material.isMeshLambertMaterial ) { | |
| refreshUniformsCommon( m_uniforms, material ); | |
| refreshUniformsLambert( m_uniforms, material ); | |
| } else if ( material.isMeshPhongMaterial ) { | |
| refreshUniformsCommon( m_uniforms, material ); | |
| if ( material.isMeshToonMaterial ) { | |
| refreshUniformsToon( m_uniforms, material ); | |
| } else { | |
| refreshUniformsPhong( m_uniforms, material ); | |
| } | |
| } else if ( material.isMeshStandardMaterial ) { | |
| refreshUniformsCommon( m_uniforms, material ); | |
| if ( material.isMeshPhysicalMaterial ) { | |
| refreshUniformsPhysical( m_uniforms, material ); | |
| } else { | |
| refreshUniformsStandard( m_uniforms, material ); | |
| } | |
| } else if ( material.isMeshDepthMaterial ) { | |
| refreshUniformsCommon( m_uniforms, material ); | |
| refreshUniformsDepth( m_uniforms, material ); | |
| } else if ( material.isMeshDistanceMaterial ) { | |
| refreshUniformsCommon( m_uniforms, material ); | |
| refreshUniformsDistance( m_uniforms, material ); | |
| } else if ( material.isMeshNormalMaterial ) { | |
| refreshUniformsCommon( m_uniforms, material ); | |
| refreshUniformsNormal( m_uniforms, material ); | |
| } else if ( material.isLineBasicMaterial ) { | |
| refreshUniformsLine( m_uniforms, material ); | |
| if ( material.isLineDashedMaterial ) { | |
| refreshUniformsDash( m_uniforms, material ); | |
| } | |
| } else if ( material.isPointsMaterial ) { | |
| refreshUniformsPoints( m_uniforms, material ); | |
| } else if ( material.isShadowMaterial ) { | |
| m_uniforms.color.value = material.color; | |
| m_uniforms.opacity.value = material.opacity; | |
| } | |
| // RectAreaLight Texture | |
| // TODO (mrdoob): Find a nicer implementation | |
| if ( m_uniforms.ltc_1 !== undefined ) m_uniforms.ltc_1.value = UniformsLib.LTC_1; | |
| if ( m_uniforms.ltc_2 !== undefined ) m_uniforms.ltc_2.value = UniformsLib.LTC_2; | |
| WebGLUniforms.upload( _gl, materialProperties.uniformsList, m_uniforms, _this ); | |
| } | |
| if ( material.isShaderMaterial && material.uniformsNeedUpdate === true ) { | |
| WebGLUniforms.upload( _gl, materialProperties.uniformsList, m_uniforms, _this ); | |
| material.uniformsNeedUpdate = false; | |
| } | |
| // common matrices | |
| p_uniforms.setValue( _gl, 'modelViewMatrix', object.modelViewMatrix ); | |
| p_uniforms.setValue( _gl, 'normalMatrix', object.normalMatrix ); | |
| p_uniforms.setValue( _gl, 'modelMatrix', object.matrixWorld ); | |
| return program; | |
| } | |
| // Uniforms (refresh uniforms objects) | |
| function refreshUniformsCommon( uniforms, material ) { | |
| uniforms.opacity.value = material.opacity; | |
| if ( material.color ) { | |
| uniforms.diffuse.value = material.color; | |
| } | |
| if ( material.emissive ) { | |
| uniforms.emissive.value.copy( material.emissive ).multiplyScalar( material.emissiveIntensity ); | |
| } | |
| if ( material.map ) { | |
| uniforms.map.value = material.map; | |
| } | |
| if ( material.alphaMap ) { | |
| uniforms.alphaMap.value = material.alphaMap; | |
| } | |
| if ( material.specularMap ) { | |
| uniforms.specularMap.value = material.specularMap; | |
| } | |
| if ( material.envMap ) { | |
| uniforms.envMap.value = material.envMap; | |
| // don't flip CubeTexture envMaps, flip everything else: | |
| // WebGLRenderTargetCube will be flipped for backwards compatibility | |
| // WebGLRenderTargetCube.texture will be flipped because it's a Texture and NOT a CubeTexture | |
| // this check must be handled differently, or removed entirely, if WebGLRenderTargetCube uses a CubeTexture in the future | |
| uniforms.flipEnvMap.value = ( ! ( material.envMap && material.envMap.isCubeTexture ) ) ? 1 : - 1; | |
| uniforms.reflectivity.value = material.reflectivity; | |
| uniforms.refractionRatio.value = material.refractionRatio; | |
| } | |
| if ( material.lightMap ) { | |
| uniforms.lightMap.value = material.lightMap; | |
| uniforms.lightMapIntensity.value = material.lightMapIntensity; | |
| } | |
| if ( material.aoMap ) { | |
| uniforms.aoMap.value = material.aoMap; | |
| uniforms.aoMapIntensity.value = material.aoMapIntensity; | |
| } | |
| // uv repeat and offset setting priorities | |
| // 1. color map | |
| // 2. specular map | |
| // 3. normal map | |
| // 4. bump map | |
| // 5. alpha map | |
| // 6. emissive map | |
| var uvScaleMap; | |
| if ( material.map ) { | |
| uvScaleMap = material.map; | |
| } else if ( material.specularMap ) { | |
| uvScaleMap = material.specularMap; | |
| } else if ( material.displacementMap ) { | |
| uvScaleMap = material.displacementMap; | |
| } else if ( material.normalMap ) { | |
| uvScaleMap = material.normalMap; | |
| } else if ( material.bumpMap ) { | |
| uvScaleMap = material.bumpMap; | |
| } else if ( material.roughnessMap ) { | |
| uvScaleMap = material.roughnessMap; | |
| } else if ( material.metalnessMap ) { | |
| uvScaleMap = material.metalnessMap; | |
| } else if ( material.alphaMap ) { | |
| uvScaleMap = material.alphaMap; | |
| } else if ( material.emissiveMap ) { | |
| uvScaleMap = material.emissiveMap; | |
| } | |
| if ( uvScaleMap !== undefined ) { | |
| // backwards compatibility | |
| if ( uvScaleMap.isWebGLRenderTarget ) { | |
| uvScaleMap = uvScaleMap.texture; | |
| } | |
| if ( uvScaleMap.matrixAutoUpdate === true ) { | |
| var offset = uvScaleMap.offset; | |
| var repeat = uvScaleMap.repeat; | |
| var rotation = uvScaleMap.rotation; | |
| var center = uvScaleMap.center; | |
| uvScaleMap.matrix.setUvTransform( offset.x, offset.y, repeat.x, repeat.y, rotation, center.x, center.y ); | |
| } | |
| uniforms.uvTransform.value.copy( uvScaleMap.matrix ); | |
| } | |
| } | |
| function refreshUniformsLine( uniforms, material ) { | |
| uniforms.diffuse.value = material.color; | |
| uniforms.opacity.value = material.opacity; | |
| } | |
| function refreshUniformsDash( uniforms, material ) { | |
| uniforms.dashSize.value = material.dashSize; | |
| uniforms.totalSize.value = material.dashSize + material.gapSize; | |
| uniforms.scale.value = material.scale; | |
| } | |
| function refreshUniformsPoints( uniforms, material ) { | |
| uniforms.diffuse.value = material.color; | |
| uniforms.opacity.value = material.opacity; | |
| uniforms.size.value = material.size * _pixelRatio; | |
| uniforms.scale.value = _height * 0.5; | |
| uniforms.map.value = material.map; | |
| if ( material.map !== null ) { | |
| if ( material.map.matrixAutoUpdate === true ) { | |
| var offset = material.map.offset; | |
| var repeat = material.map.repeat; | |
| var rotation = material.map.rotation; | |
| var center = material.map.center; | |
| material.map.matrix.setUvTransform( offset.x, offset.y, repeat.x, repeat.y, rotation, center.x, center.y ); | |
| } | |
| uniforms.uvTransform.value.copy( material.map.matrix ); | |
| } | |
| } | |
| function refreshUniformsFog( uniforms, fog ) { | |
| uniforms.fogColor.value = fog.color; | |
| if ( fog.isFog ) { | |
| uniforms.fogNear.value = fog.near; | |
| uniforms.fogFar.value = fog.far; | |
| } else if ( fog.isFogExp2 ) { | |
| uniforms.fogDensity.value = fog.density; | |
| } | |
| } | |
| function refreshUniformsLambert( uniforms, material ) { | |
| if ( material.emissiveMap ) { | |
| uniforms.emissiveMap.value = material.emissiveMap; | |
| } | |
| } | |
| function refreshUniformsPhong( uniforms, material ) { | |
| uniforms.specular.value = material.specular; | |
| uniforms.shininess.value = Math.max( material.shininess, 1e-4 ); // to prevent pow( 0.0, 0.0 ) | |
| if ( material.emissiveMap ) { | |
| uniforms.emissiveMap.value = material.emissiveMap; | |
| } | |
| if ( material.bumpMap ) { | |
| uniforms.bumpMap.value = material.bumpMap; | |
| uniforms.bumpScale.value = material.bumpScale; | |
| } | |
| if ( material.normalMap ) { | |
| uniforms.normalMap.value = material.normalMap; | |
| uniforms.normalScale.value.copy( material.normalScale ); | |
| } | |
| if ( material.displacementMap ) { | |
| uniforms.displacementMap.value = material.displacementMap; | |
| uniforms.displacementScale.value = material.displacementScale; | |
| uniforms.displacementBias.value = material.displacementBias; | |
| } | |
| } | |
| function refreshUniformsToon( uniforms, material ) { | |
| refreshUniformsPhong( uniforms, material ); | |
| if ( material.gradientMap ) { | |
| uniforms.gradientMap.value = material.gradientMap; | |
| } | |
| } | |
| function refreshUniformsStandard( uniforms, material ) { | |
| uniforms.roughness.value = material.roughness; | |
| uniforms.metalness.value = material.metalness; | |
| if ( material.roughnessMap ) { | |
| uniforms.roughnessMap.value = material.roughnessMap; | |
| } | |
| if ( material.metalnessMap ) { | |
| uniforms.metalnessMap.value = material.metalnessMap; | |
| } | |
| if ( material.emissiveMap ) { | |
| uniforms.emissiveMap.value = material.emissiveMap; | |
| } | |
| if ( material.bumpMap ) { | |
| uniforms.bumpMap.value = material.bumpMap; | |
| uniforms.bumpScale.value = material.bumpScale; | |
| } | |
| if ( material.normalMap ) { | |
| uniforms.normalMap.value = material.normalMap; | |
| uniforms.normalScale.value.copy( material.normalScale ); | |
| } | |
| if ( material.displacementMap ) { | |
| uniforms.displacementMap.value = material.displacementMap; | |
| uniforms.displacementScale.value = material.displacementScale; | |
| uniforms.displacementBias.value = material.displacementBias; | |
| } | |
| if ( material.envMap ) { | |
| //uniforms.envMap.value = material.envMap; // part of uniforms common | |
| uniforms.envMapIntensity.value = material.envMapIntensity; | |
| } | |
| } | |
| function refreshUniformsPhysical( uniforms, material ) { | |
| uniforms.clearCoat.value = material.clearCoat; | |
| uniforms.clearCoatRoughness.value = material.clearCoatRoughness; | |
| refreshUniformsStandard( uniforms, material ); | |
| } | |
| function refreshUniformsDepth( uniforms, material ) { | |
| if ( material.displacementMap ) { | |
| uniforms.displacementMap.value = material.displacementMap; | |
| uniforms.displacementScale.value = material.displacementScale; | |
| uniforms.displacementBias.value = material.displacementBias; | |
| } | |
| } | |
| function refreshUniformsDistance( uniforms, material ) { | |
| if ( material.displacementMap ) { | |
| uniforms.displacementMap.value = material.displacementMap; | |
| uniforms.displacementScale.value = material.displacementScale; | |
| uniforms.displacementBias.value = material.displacementBias; | |
| } | |
| uniforms.referencePosition.value.copy( material.referencePosition ); | |
| uniforms.nearDistance.value = material.nearDistance; | |
| uniforms.farDistance.value = material.farDistance; | |
| } | |
| function refreshUniformsNormal( uniforms, material ) { | |
| if ( material.bumpMap ) { | |
| uniforms.bumpMap.value = material.bumpMap; | |
| uniforms.bumpScale.value = material.bumpScale; | |
| } | |
| if ( material.normalMap ) { | |
| uniforms.normalMap.value = material.normalMap; | |
| uniforms.normalScale.value.copy( material.normalScale ); | |
| } | |
| if ( material.displacementMap ) { | |
| uniforms.displacementMap.value = material.displacementMap; | |
| uniforms.displacementScale.value = material.displacementScale; | |
| uniforms.displacementBias.value = material.displacementBias; | |
| } | |
| } | |
| // If uniforms are marked as clean, they don't need to be loaded to the GPU. | |
| function markUniformsLightsNeedsUpdate( uniforms, value ) { | |
| uniforms.ambientLightColor.needsUpdate = value; | |
| uniforms.directionalLights.needsUpdate = value; | |
| uniforms.pointLights.needsUpdate = value; | |
| uniforms.spotLights.needsUpdate = value; | |
| uniforms.rectAreaLights.needsUpdate = value; | |
| uniforms.hemisphereLights.needsUpdate = value; | |
| } | |
| // Textures | |
| function allocTextureUnit() { | |
| var textureUnit = _usedTextureUnits; | |
| if ( textureUnit >= capabilities.maxTextures ) { | |
| console.warn( 'THREE.WebGLRenderer: Trying to use ' + textureUnit + ' texture units while this GPU supports only ' + capabilities.maxTextures ); | |
| } | |
| _usedTextureUnits += 1; | |
| return textureUnit; | |
| } | |
| this.allocTextureUnit = allocTextureUnit; | |
| // this.setTexture2D = setTexture2D; | |
| this.setTexture2D = ( function () { | |
| var warned = false; | |
| // backwards compatibility: peel texture.texture | |
| return function setTexture2D( texture, slot ) { | |
| if ( texture && texture.isWebGLRenderTarget ) { | |
| if ( ! warned ) { | |
| console.warn( "THREE.WebGLRenderer.setTexture2D: don't use render targets as textures. Use their .texture property instead." ); | |
| warned = true; | |
| } | |
| texture = texture.texture; | |
| } | |
| textures.setTexture2D( texture, slot ); | |
| }; | |
| }() ); | |
| this.setTexture = ( function () { | |
| var warned = false; | |
| return function setTexture( texture, slot ) { | |
| if ( ! warned ) { | |
| console.warn( "THREE.WebGLRenderer: .setTexture is deprecated, use setTexture2D instead." ); | |
| warned = true; | |
| } | |
| textures.setTexture2D( texture, slot ); | |
| }; | |
| }() ); | |
| this.setTextureCube = ( function () { | |
| var warned = false; | |
| return function setTextureCube( texture, slot ) { | |
| // backwards compatibility: peel texture.texture | |
| if ( texture && texture.isWebGLRenderTargetCube ) { | |
| if ( ! warned ) { | |
| console.warn( "THREE.WebGLRenderer.setTextureCube: don't use cube render targets as textures. Use their .texture property instead." ); | |
| warned = true; | |
| } | |
| texture = texture.texture; | |
| } | |
| // currently relying on the fact that WebGLRenderTargetCube.texture is a Texture and NOT a CubeTexture | |
| // TODO: unify these code paths | |
| if ( ( texture && texture.isCubeTexture ) || | |
| ( Array.isArray( texture.image ) && texture.image.length === 6 ) ) { | |
| // CompressedTexture can have Array in image :/ | |
| // this function alone should take care of cube textures | |
| textures.setTextureCube( texture, slot ); | |
| } else { | |
| // assumed: texture property of THREE.WebGLRenderTargetCube | |
| textures.setTextureCubeDynamic( texture, slot ); | |
| } | |
| }; | |
| }() ); | |
| this.getRenderTarget = function () { | |
| return _currentRenderTarget; | |
| }; | |
| this.setRenderTarget = function ( renderTarget ) { | |
| _currentRenderTarget = renderTarget; | |
| if ( renderTarget && properties.get( renderTarget ).__webglFramebuffer === undefined ) { | |
| textures.setupRenderTarget( renderTarget ); | |
| } | |
| var framebuffer = null; | |
| var isCube = false; | |
| if ( renderTarget ) { | |
| var __webglFramebuffer = properties.get( renderTarget ).__webglFramebuffer; | |
| if ( renderTarget.isWebGLRenderTargetCube ) { | |
| framebuffer = __webglFramebuffer[ renderTarget.activeCubeFace ]; | |
| isCube = true; | |
| } else { | |
| framebuffer = __webglFramebuffer; | |
| } | |
| _currentViewport.copy( renderTarget.viewport ); | |
| _currentScissor.copy( renderTarget.scissor ); | |
| _currentScissorTest = renderTarget.scissorTest; | |
| } else { | |
| _currentViewport.copy( _viewport ).multiplyScalar( _pixelRatio ); | |
| _currentScissor.copy( _scissor ).multiplyScalar( _pixelRatio ); | |
| _currentScissorTest = _scissorTest; | |
| } | |
| if ( _currentFramebuffer !== framebuffer ) { | |
| _gl.bindFramebuffer( _gl.FRAMEBUFFER, framebuffer ); | |
| _currentFramebuffer = framebuffer; | |
| } | |
| state.viewport( _currentViewport ); | |
| state.scissor( _currentScissor ); | |
| state.setScissorTest( _currentScissorTest ); | |
| if ( isCube ) { | |
| var textureProperties = properties.get( renderTarget.texture ); | |
| _gl.framebufferTexture2D( _gl.FRAMEBUFFER, _gl.COLOR_ATTACHMENT0, _gl.TEXTURE_CUBE_MAP_POSITIVE_X + renderTarget.activeCubeFace, textureProperties.__webglTexture, renderTarget.activeMipMapLevel ); | |
| } | |
| }; | |
| this.readRenderTargetPixels = function ( renderTarget, x, y, width, height, buffer ) { | |
| if ( ! ( renderTarget && renderTarget.isWebGLRenderTarget ) ) { | |
| console.error( 'THREE.WebGLRenderer.readRenderTargetPixels: renderTarget is not THREE.WebGLRenderTarget.' ); | |
| return; | |
| } | |
| var framebuffer = properties.get( renderTarget ).__webglFramebuffer; | |
| if ( framebuffer ) { | |
| var restore = false; | |
| if ( framebuffer !== _currentFramebuffer ) { | |
| _gl.bindFramebuffer( _gl.FRAMEBUFFER, framebuffer ); | |
| restore = true; | |
| } | |
| try { | |
| var texture = renderTarget.texture; | |
| var textureFormat = texture.format; | |
| var textureType = texture.type; | |
| if ( textureFormat !== RGBAFormat && utils.convert( textureFormat ) !== _gl.getParameter( _gl.IMPLEMENTATION_COLOR_READ_FORMAT ) ) { | |
| console.error( 'THREE.WebGLRenderer.readRenderTargetPixels: renderTarget is not in RGBA or implementation defined format.' ); | |
| return; | |
| } | |
| if ( textureType !== UnsignedByteType && utils.convert( textureType ) !== _gl.getParameter( _gl.IMPLEMENTATION_COLOR_READ_TYPE ) && // IE11, Edge and Chrome Mac < 52 (#9513) | |
| ! ( textureType === FloatType && ( extensions.get( 'OES_texture_float' ) || extensions.get( 'WEBGL_color_buffer_float' ) ) ) && // Chrome Mac >= 52 and Firefox | |
| ! ( textureType === HalfFloatType && extensions.get( 'EXT_color_buffer_half_float' ) ) ) { | |
| console.error( 'THREE.WebGLRenderer.readRenderTargetPixels: renderTarget is not in UnsignedByteType or implementation defined type.' ); | |
| return; | |
| } | |
| if ( _gl.checkFramebufferStatus( _gl.FRAMEBUFFER ) === _gl.FRAMEBUFFER_COMPLETE ) { | |
| // the following if statement ensures valid read requests (no out-of-bounds pixels, see #8604) | |
| if ( ( x >= 0 && x <= ( renderTarget.width - width ) ) && ( y >= 0 && y <= ( renderTarget.height - height ) ) ) { | |
| _gl.readPixels( x, y, width, height, utils.convert( textureFormat ), utils.convert( textureType ), buffer ); | |
| } | |
| } else { | |
| console.error( 'THREE.WebGLRenderer.readRenderTargetPixels: readPixels from renderTarget failed. Framebuffer not complete.' ); | |
| } | |
| } finally { | |
| if ( restore ) { | |
| _gl.bindFramebuffer( _gl.FRAMEBUFFER, _currentFramebuffer ); | |
| } | |
| } | |
| } | |
| }; | |
| this.copyFramebufferToTexture = function ( position, texture, level ) { | |
| var width = texture.image.width; | |
| var height = texture.image.height; | |
| var internalFormat = utils.convert( texture.format ); | |
| this.setTexture2D( texture, 0 ); | |
| _gl.copyTexImage2D( _gl.TEXTURE_2D, level || 0, internalFormat, position.x, position.y, width, height, 0 ); | |
| }; | |
| } | |
| /** | |
| * @author mrdoob / http://mrdoob.com/ | |
| * @author alteredq / http://alteredqualia.com/ | |
| */ | |
| function FogExp2( color, density ) { | |
| this.name = ''; | |
| this.color = new Color( color ); | |
| this.density = ( density !== undefined ) ? density : 0.00025; | |
| } | |
| FogExp2.prototype.isFogExp2 = true; | |
| FogExp2.prototype.clone = function () { | |
| return new FogExp2( this.color.getHex(), this.density ); | |
| }; | |
| FogExp2.prototype.toJSON = function ( /* meta */ ) { | |
| return { | |
| type: 'FogExp2', | |
| color: this.color.getHex(), | |
| density: this.density | |
| }; | |
| }; | |
| /** | |
| * @author mrdoob / http://mrdoob.com/ | |
| * @author alteredq / http://alteredqualia.com/ | |
| */ | |
| function Fog( color, near, far ) { | |
| this.name = ''; | |
| this.color = new Color( color ); | |
| this.near = ( near !== undefined ) ? near : 1; | |
| this.far = ( far !== undefined ) ? far : 1000; | |
| } | |
| Fog.prototype.isFog = true; | |
| Fog.prototype.clone = function () { | |
| return new Fog( this.color.getHex(), this.near, this.far ); | |
| }; | |
| Fog.prototype.toJSON = function ( /* meta */ ) { | |
| return { | |
| type: 'Fog', | |
| color: this.color.getHex(), | |
| near: this.near, | |
| far: this.far | |
| }; | |
| }; | |
| /** | |
| * @author mrdoob / http://mrdoob.com/ | |
| */ | |
| function Scene() { | |
| Object3D.call( this ); | |
| this.type = 'Scene'; | |
| this.background = null; | |
| this.fog = null; | |
| this.overrideMaterial = null; | |
| this.autoUpdate = true; // checked by the renderer | |
| } | |
| Scene.prototype = Object.assign( Object.create( Object3D.prototype ), { | |
| constructor: Scene, | |
| copy: function ( source, recursive ) { | |
| Object3D.prototype.copy.call( this, source, recursive ); | |
| if ( source.background !== null ) this.background = source.background.clone(); | |
| if ( source.fog !== null ) this.fog = source.fog.clone(); | |
| if ( source.overrideMaterial !== null ) this.overrideMaterial = source.overrideMaterial.clone(); | |
| this.autoUpdate = source.autoUpdate; | |
| this.matrixAutoUpdate = source.matrixAutoUpdate; | |
| return this; | |
| }, | |
| toJSON: function ( meta ) { | |
| var data = Object3D.prototype.toJSON.call( this, meta ); | |
| if ( this.background !== null ) data.object.background = this.background.toJSON( meta ); | |
| if ( this.fog !== null ) data.object.fog = this.fog.toJSON(); | |
| return data; | |
| } | |
| } ); | |
| /** | |
| * @author alteredq / http://alteredqualia.com/ | |
| * | |
| * parameters = { | |
| * color: <hex>, | |
| * opacity: <float>, | |
| * map: new THREE.Texture( <Image> ), | |
| * | |
| * uvOffset: new THREE.Vector2(), | |
| * uvScale: new THREE.Vector2() | |
| * } | |
| */ | |
| function SpriteMaterial( parameters ) { | |
| Material.call( this ); | |
| this.type = 'SpriteMaterial'; | |
| this.color = new Color( 0xffffff ); | |
| this.map = null; | |
| this.rotation = 0; | |
| this.fog = false; | |
| this.lights = false; | |
| this.setValues( parameters ); | |
| } | |
| SpriteMaterial.prototype = Object.create( Material.prototype ); | |
| SpriteMaterial.prototype.constructor = SpriteMaterial; | |
| SpriteMaterial.prototype.isSpriteMaterial = true; | |
| SpriteMaterial.prototype.copy = function ( source ) { | |
| Material.prototype.copy.call( this, source ); | |
| this.color.copy( source.color ); | |
| this.map = source.map; | |
| this.rotation = source.rotation; | |
| return this; | |
| }; | |
| /** | |
| * @author mikael emtinger / http://gomo.se/ | |
| * @author alteredq / http://alteredqualia.com/ | |
| */ | |
| function Sprite( material ) { | |
| Object3D.call( this ); | |
| this.type = 'Sprite'; | |
| this.material = ( material !== undefined ) ? material : new SpriteMaterial(); | |
| this.center = new Vector2( 0.5, 0.5 ); | |
| } | |
| Sprite.prototype = Object.assign( Object.create( Object3D.prototype ), { | |
| constructor: Sprite, | |
| isSprite: true, | |
| raycast: ( function () { | |
| var intersectPoint = new Vector3(); | |
| var worldPosition = new Vector3(); | |
| var worldScale = new Vector3(); | |
| return function raycast( raycaster, intersects ) { | |
| worldPosition.setFromMatrixPosition( this.matrixWorld ); | |
| raycaster.ray.closestPointToPoint( worldPosition, intersectPoint ); | |
| worldScale.setFromMatrixScale( this.matrixWorld ); | |
| var guessSizeSq = worldScale.x * worldScale.y / 4; | |
| if ( worldPosition.distanceToSquared( intersectPoint ) > guessSizeSq ) return; | |
| var distance = raycaster.ray.origin.distanceTo( intersectPoint ); | |
| if ( distance < raycaster.near || distance > raycaster.far ) return; | |
| intersects.push( { | |
| distance: distance, | |
| point: intersectPoint.clone(), | |
| face: null, | |
| object: this | |
| } ); | |
| }; | |
| }() ), | |
| clone: function () { | |
| return new this.constructor( this.material ).copy( this ); | |
| }, | |
| copy: function ( source ) { | |
| Object3D.prototype.copy.call( this, source ); | |
| if ( source.center !== undefined ) this.center.copy( source.center ); | |
| return this; | |
| } | |
| } ); | |
| /** | |
| * @author mikael emtinger / http://gomo.se/ | |
| * @author alteredq / http://alteredqualia.com/ | |
| * @author mrdoob / http://mrdoob.com/ | |
| */ | |
| function LOD() { | |
| Object3D.call( this ); | |
| this.type = 'LOD'; | |
| Object.defineProperties( this, { | |
| levels: { | |
| enumerable: true, | |
| value: [] | |
| } | |
| } ); | |
| } | |
| LOD.prototype = Object.assign( Object.create( Object3D.prototype ), { | |
| constructor: LOD, | |
| copy: function ( source ) { | |
| Object3D.prototype.copy.call( this, source, false ); | |
| var levels = source.levels; | |
| for ( var i = 0, l = levels.length; i < l; i ++ ) { | |
| var level = levels[ i ]; | |
| this.addLevel( level.object.clone(), level.distance ); | |
| } | |
| return this; | |
| }, | |
| addLevel: function ( object, distance ) { | |
| if ( distance === undefined ) distance = 0; | |
| distance = Math.abs( distance ); | |
| var levels = this.levels; | |
| for ( var l = 0; l < levels.length; l ++ ) { | |
| if ( distance < levels[ l ].distance ) { | |
| break; | |
| } | |
| } | |
| levels.splice( l, 0, { distance: distance, object: object } ); | |
| this.add( object ); | |
| }, | |
| getObjectForDistance: function ( distance ) { | |
| var levels = this.levels; | |
| for ( var i = 1, l = levels.length; i < l; i ++ ) { | |
| if ( distance < levels[ i ].distance ) { | |
| break; | |
| } | |
| } | |
| return levels[ i - 1 ].object; | |
| }, | |
| raycast: ( function () { | |
| var matrixPosition = new Vector3(); | |
| return function raycast( raycaster, intersects ) { | |
| matrixPosition.setFromMatrixPosition( this.matrixWorld ); | |
| var distance = raycaster.ray.origin.distanceTo( matrixPosition ); | |
| this.getObjectForDistance( distance ).raycast( raycaster, intersects ); | |
| }; | |
| }() ), | |
| update: function () { | |
| var v1 = new Vector3(); | |
| var v2 = new Vector3(); | |
| return function update( camera ) { | |
| var levels = this.levels; | |
| if ( levels.length > 1 ) { | |
| v1.setFromMatrixPosition( camera.matrixWorld ); | |
| v2.setFromMatrixPosition( this.matrixWorld ); | |
| var distance = v1.distanceTo( v2 ); | |
| levels[ 0 ].object.visible = true; | |
| for ( var i = 1, l = levels.length; i < l; i ++ ) { | |
| if ( distance >= levels[ i ].distance ) { | |
| levels[ i - 1 ].object.visible = false; | |
| levels[ i ].object.visible = true; | |
| } else { | |
| break; | |
| } | |
| } | |
| for ( ; i < l; i ++ ) { | |
| levels[ i ].object.visible = false; | |
| } | |
| } | |
| }; | |
| }(), | |
| toJSON: function ( meta ) { | |
| var data = Object3D.prototype.toJSON.call( this, meta ); | |
| data.object.levels = []; | |
| var levels = this.levels; | |
| for ( var i = 0, l = levels.length; i < l; i ++ ) { | |
| var level = levels[ i ]; | |
| data.object.levels.push( { | |
| object: level.object.uuid, | |
| distance: level.distance | |
| } ); | |
| } | |
| return data; | |
| } | |
| } ); | |
| /** | |
| * @author mikael emtinger / http://gomo.se/ | |
| * @author alteredq / http://alteredqualia.com/ | |
| * @author michael guerrero / http://realitymeltdown.com | |
| * @author ikerr / http://verold.com | |
| */ | |
| function Skeleton( bones, boneInverses ) { | |
| // copy the bone array | |
| bones = bones || []; | |
| this.bones = bones.slice( 0 ); | |
| this.boneMatrices = new Float32Array( this.bones.length * 16 ); | |
| // use the supplied bone inverses or calculate the inverses | |
| if ( boneInverses === undefined ) { | |
| this.calculateInverses(); | |
| } else { | |
| if ( this.bones.length === boneInverses.length ) { | |
| this.boneInverses = boneInverses.slice( 0 ); | |
| } else { | |
| console.warn( 'THREE.Skeleton boneInverses is the wrong length.' ); | |
| this.boneInverses = []; | |
| for ( var i = 0, il = this.bones.length; i < il; i ++ ) { | |
| this.boneInverses.push( new Matrix4() ); | |
| } | |
| } | |
| } | |
| } | |
| Object.assign( Skeleton.prototype, { | |
| calculateInverses: function () { | |
| this.boneInverses = []; | |
| for ( var i = 0, il = this.bones.length; i < il; i ++ ) { | |
| var inverse = new Matrix4(); | |
| if ( this.bones[ i ] ) { | |
| inverse.getInverse( this.bones[ i ].matrixWorld ); | |
| } | |
| this.boneInverses.push( inverse ); | |
| } | |
| }, | |
| pose: function () { | |
| var bone, i, il; | |
| // recover the bind-time world matrices | |
| for ( i = 0, il = this.bones.length; i < il; i ++ ) { | |
| bone = this.bones[ i ]; | |
| if ( bone ) { | |
| bone.matrixWorld.getInverse( this.boneInverses[ i ] ); | |
| } | |
| } | |
| // compute the local matrices, positions, rotations and scales | |
| for ( i = 0, il = this.bones.length; i < il; i ++ ) { | |
| bone = this.bones[ i ]; | |
| if ( bone ) { | |
| if ( bone.parent && bone.parent.isBone ) { | |
| bone.matrix.getInverse( bone.parent.matrixWorld ); | |
| bone.matrix.multiply( bone.matrixWorld ); | |
| } else { | |
| bone.matrix.copy( bone.matrixWorld ); | |
| } | |
| bone.matrix.decompose( bone.position, bone.quaternion, bone.scale ); | |
| } | |
| } | |
| }, | |
| update: ( function () { | |
| var offsetMatrix = new Matrix4(); | |
| var identityMatrix = new Matrix4(); | |
| return function update() { | |
| var bones = this.bones; | |
| var boneInverses = this.boneInverses; | |
| var boneMatrices = this.boneMatrices; | |
| var boneTexture = this.boneTexture; | |
| // flatten bone matrices to array | |
| for ( var i = 0, il = bones.length; i < il; i ++ ) { | |
| // compute the offset between the current and the original transform | |
| var matrix = bones[ i ] ? bones[ i ].matrixWorld : identityMatrix; | |
| offsetMatrix.multiplyMatrices( matrix, boneInverses[ i ] ); | |
| offsetMatrix.toArray( boneMatrices, i * 16 ); | |
| } | |
| if ( boneTexture !== undefined ) { | |
| boneTexture.needsUpdate = true; | |
| } | |
| }; | |
| } )(), | |
| clone: function () { | |
| return new Skeleton( this.bones, this.boneInverses ); | |
| }, | |
| getBoneByName: function ( name ) { | |
| for ( var i = 0, il = this.bones.length; i < il; i ++ ) { | |
| var bone = this.bones[ i ]; | |
| if ( bone.name === name ) { | |
| return bone; | |
| } | |
| } | |
| return undefined; | |
| } | |
| } ); | |
| /** | |
| * @author mikael emtinger / http://gomo.se/ | |
| * @author alteredq / http://alteredqualia.com/ | |
| * @author ikerr / http://verold.com | |
| */ | |
| function Bone() { | |
| Object3D.call( this ); | |
| this.type = 'Bone'; | |
| } | |
| Bone.prototype = Object.assign( Object.create( Object3D.prototype ), { | |
| constructor: Bone, | |
| isBone: true | |
| } ); | |
| /** | |
| * @author mikael emtinger / http://gomo.se/ | |
| * @author alteredq / http://alteredqualia.com/ | |
| * @author ikerr / http://verold.com | |
| */ | |
| function SkinnedMesh( geometry, material ) { | |
| Mesh.call( this, geometry, material ); | |
| this.type = 'SkinnedMesh'; | |
| this.bindMode = 'attached'; | |
| this.bindMatrix = new Matrix4(); | |
| this.bindMatrixInverse = new Matrix4(); | |
| var bones = this.initBones(); | |
| var skeleton = new Skeleton( bones ); | |
| this.bind( skeleton, this.matrixWorld ); | |
| this.normalizeSkinWeights(); | |
| } | |
| SkinnedMesh.prototype = Object.assign( Object.create( Mesh.prototype ), { | |
| constructor: SkinnedMesh, | |
| isSkinnedMesh: true, | |
| initBones: function () { | |
| var bones = [], bone, gbone; | |
| var i, il; | |
| if ( this.geometry && this.geometry.bones !== undefined ) { | |
| // first, create array of 'Bone' objects from geometry data | |
| for ( i = 0, il = this.geometry.bones.length; i < il; i ++ ) { | |
| gbone = this.geometry.bones[ i ]; | |
| // create new 'Bone' object | |
| bone = new Bone(); | |
| bones.push( bone ); | |
| // apply values | |
| bone.name = gbone.name; | |
| bone.position.fromArray( gbone.pos ); | |
| bone.quaternion.fromArray( gbone.rotq ); | |
| if ( gbone.scl !== undefined ) bone.scale.fromArray( gbone.scl ); | |
| } | |
| // second, create bone hierarchy | |
| for ( i = 0, il = this.geometry.bones.length; i < il; i ++ ) { | |
| gbone = this.geometry.bones[ i ]; | |
| if ( ( gbone.parent !== - 1 ) && ( gbone.parent !== null ) && ( bones[ gbone.parent ] !== undefined ) ) { | |
| // subsequent bones in the hierarchy | |
| bones[ gbone.parent ].add( bones[ i ] ); | |
| } else { | |
| // topmost bone, immediate child of the skinned mesh | |
| this.add( bones[ i ] ); | |
| } | |
| } | |
| } | |
| // now the bones are part of the scene graph and children of the skinned mesh. | |
| // let's update the corresponding matrices | |
| this.updateMatrixWorld( true ); | |
| return bones; | |
| }, | |
| bind: function ( skeleton, bindMatrix ) { | |
| this.skeleton = skeleton; | |
| if ( bindMatrix === undefined ) { | |
| this.updateMatrixWorld( true ); | |
| this.skeleton.calculateInverses(); | |
| bindMatrix = this.matrixWorld; | |
| } | |
| this.bindMatrix.copy( bindMatrix ); | |
| this.bindMatrixInverse.getInverse( bindMatrix ); | |
| }, | |
| pose: function () { | |
| this.skeleton.pose(); | |
| }, | |
| normalizeSkinWeights: function () { | |
| var scale, i; | |
| if ( this.geometry && this.geometry.isGeometry ) { | |
| for ( i = 0; i < this.geometry.skinWeights.length; i ++ ) { | |
| var sw = this.geometry.skinWeights[ i ]; | |
| scale = 1.0 / sw.manhattanLength(); | |
| if ( scale !== Infinity ) { | |
| sw.multiplyScalar( scale ); | |
| } else { | |
| sw.set( 1, 0, 0, 0 ); // do something reasonable | |
| } | |
| } | |
| } else if ( this.geometry && this.geometry.isBufferGeometry ) { | |
| var vec = new Vector4(); | |
| var skinWeight = this.geometry.attributes.skinWeight; | |
| for ( i = 0; i < skinWeight.count; i ++ ) { | |
| vec.x = skinWeight.getX( i ); | |
| vec.y = skinWeight.getY( i ); | |
| vec.z = skinWeight.getZ( i ); | |
| vec.w = skinWeight.getW( i ); | |
| scale = 1.0 / vec.manhattanLength(); | |
| if ( scale !== Infinity ) { | |
| vec.multiplyScalar( scale ); | |
| } else { | |
| vec.set( 1, 0, 0, 0 ); // do something reasonable | |
| } | |
| skinWeight.setXYZW( i, vec.x, vec.y, vec.z, vec.w ); | |
| } | |
| } | |
| }, | |
| updateMatrixWorld: function ( force ) { | |
| Mesh.prototype.updateMatrixWorld.call( this, force ); | |
| if ( this.bindMode === 'attached' ) { | |
| this.bindMatrixInverse.getInverse( this.matrixWorld ); | |
| } else if ( this.bindMode === 'detached' ) { | |
| this.bindMatrixInverse.getInverse( this.bindMatrix ); | |
| } else { | |
| console.warn( 'THREE.SkinnedMesh: Unrecognized bindMode: ' + this.bindMode ); | |
| } | |
| }, | |
| clone: function () { | |
| return new this.constructor( this.geometry, this.material ).copy( this ); | |
| } | |
| } ); | |
| /** | |
| * @author mrdoob / http://mrdoob.com/ | |
| * @author alteredq / http://alteredqualia.com/ | |
| * | |
| * parameters = { | |
| * color: <hex>, | |
| * opacity: <float>, | |
| * | |
| * linewidth: <float>, | |
| * linecap: "round", | |
| * linejoin: "round" | |
| * } | |
| */ | |
| function LineBasicMaterial( parameters ) { | |
| Material.call( this ); | |
| this.type = 'LineBasicMaterial'; | |
| this.color = new Color( 0xffffff ); | |
| this.linewidth = 1; | |
| this.linecap = 'round'; | |
| this.linejoin = 'round'; | |
| this.lights = false; | |
| this.setValues( parameters ); | |
| } | |
| LineBasicMaterial.prototype = Object.create( Material.prototype ); | |
| LineBasicMaterial.prototype.constructor = LineBasicMaterial; | |
| LineBasicMaterial.prototype.isLineBasicMaterial = true; | |
| LineBasicMaterial.prototype.copy = function ( source ) { | |
| Material.prototype.copy.call( this, source ); | |
| this.color.copy( source.color ); | |
| this.linewidth = source.linewidth; | |
| this.linecap = source.linecap; | |
| this.linejoin = source.linejoin; | |
| return this; | |
| }; | |
| /** | |
| * @author mrdoob / http://mrdoob.com/ | |
| */ | |
| function Line( geometry, material, mode ) { | |
| if ( mode === 1 ) { | |
| console.warn( 'THREE.Line: parameter THREE.LinePieces no longer supported. Created THREE.LineSegments instead.' ); | |
| return new LineSegments( geometry, material ); | |
| } | |
| Object3D.call( this ); | |
| this.type = 'Line'; | |
| this.geometry = geometry !== undefined ? geometry : new BufferGeometry(); | |
| this.material = material !== undefined ? material : new LineBasicMaterial( { color: Math.random() * 0xffffff } ); | |
| } | |
| Line.prototype = Object.assign( Object.create( Object3D.prototype ), { | |
| constructor: Line, | |
| isLine: true, | |
| computeLineDistances: ( function () { | |
| var start = new Vector3(); | |
| var end = new Vector3(); | |
| return function computeLineDistances() { | |
| var geometry = this.geometry; | |
| if ( geometry.isBufferGeometry ) { | |
| // we assume non-indexed geometry | |
| if ( geometry.index === null ) { | |
| var positionAttribute = geometry.attributes.position; | |
| var lineDistances = [ 0 ]; | |
| for ( var i = 1, l = positionAttribute.count; i < l; i ++ ) { | |
| start.fromBufferAttribute( positionAttribute, i - 1 ); | |
| end.fromBufferAttribute( positionAttribute, i ); | |
| lineDistances[ i ] = lineDistances[ i - 1 ]; | |
| lineDistances[ i ] += start.distanceTo( end ); | |
| } | |
| geometry.addAttribute( 'lineDistance', new THREE.Float32BufferAttribute( lineDistances, 1 ) ); | |
| } else { | |
| console.warn( 'THREE.Line.computeLineDistances(): Computation only possible with non-indexed BufferGeometry.' ); | |
| } | |
| } else if ( geometry.isGeometry ) { | |
| var vertices = geometry.vertices; | |
| var lineDistances = geometry.lineDistances; | |
| lineDistances[ 0 ] = 0; | |
| for ( var i = 1, l = vertices.length; i < l; i ++ ) { | |
| lineDistances[ i ] = lineDistances[ i - 1 ]; | |
| lineDistances[ i ] += vertices[ i - 1 ].distanceTo( vertices[ i ] ); | |
| } | |
| } | |
| return this; | |
| }; | |
| }() ), | |
| raycast: ( function () { | |
| var inverseMatrix = new Matrix4(); | |
| var ray = new Ray(); | |
| var sphere = new Sphere(); | |
| return function raycast( raycaster, intersects ) { | |
| var precision = raycaster.linePrecision; | |
| var precisionSq = precision * precision; | |
| var geometry = this.geometry; | |
| var matrixWorld = this.matrixWorld; | |
| // Checking boundingSphere distance to ray | |
| if ( geometry.boundingSphere === null ) geometry.computeBoundingSphere(); | |
| sphere.copy( geometry.boundingSphere ); | |
| sphere.applyMatrix4( matrixWorld ); | |
| if ( raycaster.ray.intersectsSphere( sphere ) === false ) return; | |
| // | |
| inverseMatrix.getInverse( matrixWorld ); | |
| ray.copy( raycaster.ray ).applyMatrix4( inverseMatrix ); | |
| var vStart = new Vector3(); | |
| var vEnd = new Vector3(); | |
| var interSegment = new Vector3(); | |
| var interRay = new Vector3(); | |
| var step = ( this && this.isLineSegments ) ? 2 : 1; | |
| if ( geometry.isBufferGeometry ) { | |
| var index = geometry.index; | |
| var attributes = geometry.attributes; | |
| var positions = attributes.position.array; | |
| if ( index !== null ) { | |
| var indices = index.array; | |
| for ( var i = 0, l = indices.length - 1; i < l; i += step ) { | |
| var a = indices[ i ]; | |
| var b = indices[ i + 1 ]; | |
| vStart.fromArray( positions, a * 3 ); | |
| vEnd.fromArray( positions, b * 3 ); | |
| var distSq = ray.distanceSqToSegment( vStart, vEnd, interRay, interSegment ); | |
| if ( distSq > precisionSq ) continue; | |
| interRay.applyMatrix4( this.matrixWorld ); //Move back to world space for distance calculation | |
| var distance = raycaster.ray.origin.distanceTo( interRay ); | |
| if ( distance < raycaster.near || distance > raycaster.far ) continue; | |
| intersects.push( { | |
| distance: distance, | |
| // What do we want? intersection point on the ray or on the segment?? | |
| // point: raycaster.ray.at( distance ), | |
| point: interSegment.clone().applyMatrix4( this.matrixWorld ), | |
| index: i, | |
| face: null, | |
| faceIndex: null, | |
| object: this | |
| } ); | |
| } | |
| } else { | |
| for ( var i = 0, l = positions.length / 3 - 1; i < l; i += step ) { | |
| vStart.fromArray( positions, 3 * i ); | |
| vEnd.fromArray( positions, 3 * i + 3 ); | |
| var distSq = ray.distanceSqToSegment( vStart, vEnd, interRay, interSegment ); | |
| if ( distSq > precisionSq ) continue; | |
| interRay.applyMatrix4( this.matrixWorld ); //Move back to world space for distance calculation | |
| var distance = raycaster.ray.origin.distanceTo( interRay ); | |
| if ( distance < raycaster.near || distance > raycaster.far ) continue; | |
| intersects.push( { | |
| distance: distance, | |
| // What do we want? intersection point on the ray or on the segment?? | |
| // point: raycaster.ray.at( distance ), | |
| point: interSegment.clone().applyMatrix4( this.matrixWorld ), | |
| index: i, | |
| face: null, | |
| faceIndex: null, | |
| object: this | |
| } ); | |
| } | |
| } | |
| } else if ( geometry.isGeometry ) { | |
| var vertices = geometry.vertices; | |
| var nbVertices = vertices.length; | |
| for ( var i = 0; i < nbVertices - 1; i += step ) { | |
| var distSq = ray.distanceSqToSegment( vertices[ i ], vertices[ i + 1 ], interRay, interSegment ); | |
| if ( distSq > precisionSq ) continue; | |
| interRay.applyMatrix4( this.matrixWorld ); //Move back to world space for distance calculation | |
| var distance = raycaster.ray.origin.distanceTo( interRay ); | |
| if ( distance < raycaster.near || distance > raycaster.far ) continue; | |
| intersects.push( { | |
| distance: distance, | |
| // What do we want? intersection point on the ray or on the segment?? | |
| // point: raycaster.ray.at( distance ), | |
| point: interSegment.clone().applyMatrix4( this.matrixWorld ), | |
| index: i, | |
| face: null, | |
| faceIndex: null, | |
| object: this | |
| } ); | |
| } | |
| } | |
| }; | |
| }() ), | |
| clone: function () { | |
| return new this.constructor( this.geometry, this.material ).copy( this ); | |
| } | |
| } ); | |
| /** | |
| * @author mrdoob / http://mrdoob.com/ | |
| */ | |
| function LineSegments( geometry, material ) { | |
| Line.call( this, geometry, material ); | |
| this.type = 'LineSegments'; | |
| } | |
| LineSegments.prototype = Object.assign( Object.create( Line.prototype ), { | |
| constructor: LineSegments, | |
| isLineSegments: true, | |
| computeLineDistances: ( function () { | |
| var start = new Vector3(); | |
| var end = new Vector3(); | |
| return function computeLineDistances() { | |
| var geometry = this.geometry; | |
| if ( geometry.isBufferGeometry ) { | |
| // we assume non-indexed geometry | |
| if ( geometry.index === null ) { | |
| var positionAttribute = geometry.attributes.position; | |
| var lineDistances = []; | |
| for ( var i = 0, l = positionAttribute.count; i < l; i += 2 ) { | |
| start.fromBufferAttribute( positionAttribute, i ); | |
| end.fromBufferAttribute( positionAttribute, i + 1 ); | |
| lineDistances[ i ] = ( i === 0 ) ? 0 : lineDistances[ i - 1 ]; | |
| lineDistances[ i + 1 ] = lineDistances[ i ] + start.distanceTo( end ); | |
| } | |
| geometry.addAttribute( 'lineDistance', new THREE.Float32BufferAttribute( lineDistances, 1 ) ); | |
| } else { | |
| console.warn( 'THREE.LineSegments.computeLineDistances(): Computation only possible with non-indexed BufferGeometry.' ); | |
| } | |
| } else if ( geometry.isGeometry ) { | |
| var vertices = geometry.vertices; | |
| var lineDistances = geometry.lineDistances; | |
| for ( var i = 0, l = vertices.length; i < l; i += 2 ) { | |
| start.copy( vertices[ i ] ); | |
| end.copy( vertices[ i + 1 ] ); | |
| lineDistances[ i ] = ( i === 0 ) ? 0 : lineDistances[ i - 1 ]; | |
| lineDistances[ i + 1 ] = lineDistances[ i ] + start.distanceTo( end ); | |
| } | |
| } | |
| return this; | |
| }; | |
| }() ) | |
| } ); | |
| /** | |
| * @author mgreter / http://github.com/mgreter | |
| */ | |
| function LineLoop( geometry, material ) { | |
| Line.call( this, geometry, material ); | |
| this.type = 'LineLoop'; | |
| } | |
| LineLoop.prototype = Object.assign( Object.create( Line.prototype ), { | |
| constructor: LineLoop, | |
| isLineLoop: true, | |
| } ); | |
| /** | |
| * @author mrdoob / http://mrdoob.com/ | |
| * @author alteredq / http://alteredqualia.com/ | |
| * | |
| * parameters = { | |
| * color: <hex>, | |
| * opacity: <float>, | |
| * map: new THREE.Texture( <Image> ), | |
| * | |
| * size: <float>, | |
| * sizeAttenuation: <bool> | |
| * } | |
| */ | |
| function PointsMaterial( parameters ) { | |
| Material.call( this ); | |
| this.type = 'PointsMaterial'; | |
| this.color = new Color( 0xffffff ); | |
| this.map = null; | |
| this.size = 1; | |
| this.sizeAttenuation = true; | |
| this.lights = false; | |
| this.setValues( parameters ); | |
| } | |
| PointsMaterial.prototype = Object.create( Material.prototype ); | |
| PointsMaterial.prototype.constructor = PointsMaterial; | |
| PointsMaterial.prototype.isPointsMaterial = true; | |
| PointsMaterial.prototype.copy = function ( source ) { | |
| Material.prototype.copy.call( this, source ); | |
| this.color.copy( source.color ); | |
| this.map = source.map; | |
| this.size = source.size; | |
| this.sizeAttenuation = source.sizeAttenuation; | |
| return this; | |
| }; | |
| /** | |
| * @author alteredq / http://alteredqualia.com/ | |
| */ | |
| function Points( geometry, material ) { | |
| Object3D.call( this ); | |
| this.type = 'Points'; | |
| this.geometry = geometry !== undefined ? geometry : new BufferGeometry(); | |
| this.material = material !== undefined ? material : new PointsMaterial( { color: Math.random() * 0xffffff } ); | |
| } | |
| Points.prototype = Object.assign( Object.create( Object3D.prototype ), { | |
| constructor: Points, | |
| isPoints: true, | |
| raycast: ( function () { | |
| var inverseMatrix = new Matrix4(); | |
| var ray = new Ray(); | |
| var sphere = new Sphere(); | |
| return function raycast( raycaster, intersects ) { | |
| var object = this; | |
| var geometry = this.geometry; | |
| var matrixWorld = this.matrixWorld; | |
| var threshold = raycaster.params.Points.threshold; | |
| // Checking boundingSphere distance to ray | |
| if ( geometry.boundingSphere === null ) geometry.computeBoundingSphere(); | |
| sphere.copy( geometry.boundingSphere ); | |
| sphere.applyMatrix4( matrixWorld ); | |
| sphere.radius += threshold; | |
| if ( raycaster.ray.intersectsSphere( sphere ) === false ) return; | |
| // | |
| inverseMatrix.getInverse( matrixWorld ); | |
| ray.copy( raycaster.ray ).applyMatrix4( inverseMatrix ); | |
| var localThreshold = threshold / ( ( this.scale.x + this.scale.y + this.scale.z ) / 3 ); | |
| var localThresholdSq = localThreshold * localThreshold; | |
| var position = new Vector3(); | |
| function testPoint( point, index ) { | |
| var rayPointDistanceSq = ray.distanceSqToPoint( point ); | |
| if ( rayPointDistanceSq < localThresholdSq ) { | |
| var intersectPoint = ray.closestPointToPoint( point ); | |
| intersectPoint.applyMatrix4( matrixWorld ); | |
| var distance = raycaster.ray.origin.distanceTo( intersectPoint ); | |
| if ( distance < raycaster.near || distance > raycaster.far ) return; | |
| intersects.push( { | |
| distance: distance, | |
| distanceToRay: Math.sqrt( rayPointDistanceSq ), | |
| point: intersectPoint.clone(), | |
| index: index, | |
| face: null, | |
| object: object | |
| } ); | |
| } | |
| } | |
| if ( geometry.isBufferGeometry ) { | |
| var index = geometry.index; | |
| var attributes = geometry.attributes; | |
| var positions = attributes.position.array; | |
| if ( index !== null ) { | |
| var indices = index.array; | |
| for ( var i = 0, il = indices.length; i < il; i ++ ) { | |
| var a = indices[ i ]; | |
| position.fromArray( positions, a * 3 ); | |
| testPoint( position, a ); | |
| } | |
| } else { | |
| for ( var i = 0, l = positions.length / 3; i < l; i ++ ) { | |
| position.fromArray( positions, i * 3 ); | |
| testPoint( position, i ); | |
| } | |
| } | |
| } else { | |
| var vertices = geometry.vertices; | |
| for ( var i = 0, l = vertices.length; i < l; i ++ ) { | |
| testPoint( vertices[ i ], i ); | |
| } | |
| } | |
| }; | |
| }() ), | |
| clone: function () { | |
| return new this.constructor( this.geometry, this.material ).copy( this ); | |
| } | |
| } ); | |
| /** | |
| * @author mrdoob / http://mrdoob.com/ | |
| */ | |
| function Group() { | |
| Object3D.call( this ); | |
| this.type = 'Group'; | |
| } | |
| Group.prototype = Object.assign( Object.create( Object3D.prototype ), { | |
| constructor: Group, | |
| isGroup: true | |
| } ); | |
| /** | |
| * @author mrdoob / http://mrdoob.com/ | |
| */ | |
| function VideoTexture( video, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy ) { | |
| Texture.call( this, video, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy ); | |
| this.generateMipmaps = false; | |
| } | |
| VideoTexture.prototype = Object.assign( Object.create( Texture.prototype ), { | |
| constructor: VideoTexture, | |
| isVideoTexture: true, | |
| update: function () { | |
| var video = this.image; | |
| if ( video.readyState >= video.HAVE_CURRENT_DATA ) { | |
| this.needsUpdate = true; | |
| } | |
| } | |
| } ); | |
| /** | |
| * @author alteredq / http://alteredqualia.com/ | |
| */ | |
| function CompressedTexture( mipmaps, width, height, format, type, mapping, wrapS, wrapT, magFilter, minFilter, anisotropy, encoding ) { | |
| Texture.call( this, null, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy, encoding ); | |
| this.image = { width: width, height: height }; | |
| this.mipmaps = mipmaps; | |
| // no flipping for cube textures | |
| // (also flipping doesn't work for compressed textures ) | |
| this.flipY = false; | |
| // can't generate mipmaps for compressed textures | |
| // mips must be embedded in DDS files | |
| this.generateMipmaps = false; | |
| } | |
| CompressedTexture.prototype = Object.create( Texture.prototype ); | |
| CompressedTexture.prototype.constructor = CompressedTexture; | |
| CompressedTexture.prototype.isCompressedTexture = true; | |
| /** | |
| * @author Matt DesLauriers / @mattdesl | |
| * @author atix / arthursilber.de | |
| */ | |
| function DepthTexture( width, height, type, mapping, wrapS, wrapT, magFilter, minFilter, anisotropy, format ) { | |
| format = format !== undefined ? format : DepthFormat; | |
| if ( format !== DepthFormat && format !== DepthStencilFormat ) { | |
| throw new Error( 'DepthTexture format must be either THREE.DepthFormat or THREE.DepthStencilFormat' ); | |
| } | |
| if ( type === undefined && format === DepthFormat ) type = UnsignedShortType; | |
| if ( type === undefined && format === DepthStencilFormat ) type = UnsignedInt248Type; | |
| Texture.call( this, null, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy ); | |
| this.image = { width: width, height: height }; | |
| this.magFilter = magFilter !== undefined ? magFilter : NearestFilter; | |
| this.minFilter = minFilter !== undefined ? minFilter : NearestFilter; | |
| this.flipY = false; | |
| this.generateMipmaps = false; | |
| } | |
| DepthTexture.prototype = Object.create( Texture.prototype ); | |
| DepthTexture.prototype.constructor = DepthTexture; | |
| DepthTexture.prototype.isDepthTexture = true; | |
| /** | |
| * @author mrdoob / http://mrdoob.com/ | |
| * @author Mugen87 / https://github.com/Mugen87 | |
| */ | |
| function WireframeGeometry( geometry ) { | |
| BufferGeometry.call( this ); | |
| this.type = 'WireframeGeometry'; | |
| // buffer | |
| var vertices = []; | |
| // helper variables | |
| var i, j, l, o, ol; | |
| var edge = [ 0, 0 ], edges = {}, e, edge1, edge2; | |
| var key, keys = [ 'a', 'b', 'c' ]; | |
| var vertex; | |
| // different logic for Geometry and BufferGeometry | |
| if ( geometry && geometry.isGeometry ) { | |
| // create a data structure that contains all edges without duplicates | |
| var faces = geometry.faces; | |
| for ( i = 0, l = faces.length; i < l; i ++ ) { | |
| var face = faces[ i ]; | |
| for ( j = 0; j < 3; j ++ ) { | |
| edge1 = face[ keys[ j ] ]; | |
| edge2 = face[ keys[ ( j + 1 ) % 3 ] ]; | |
| edge[ 0 ] = Math.min( edge1, edge2 ); // sorting prevents duplicates | |
| edge[ 1 ] = Math.max( edge1, edge2 ); | |
| key = edge[ 0 ] + ',' + edge[ 1 ]; | |
| if ( edges[ key ] === undefined ) { | |
| edges[ key ] = { index1: edge[ 0 ], index2: edge[ 1 ] }; | |
| } | |
| } | |
| } | |
| // generate vertices | |
| for ( key in edges ) { | |
| e = edges[ key ]; | |
| vertex = geometry.vertices[ e.index1 ]; | |
| vertices.push( vertex.x, vertex.y, vertex.z ); | |
| vertex = geometry.vertices[ e.index2 ]; | |
| vertices.push( vertex.x, vertex.y, vertex.z ); | |
| } | |
| } else if ( geometry && geometry.isBufferGeometry ) { | |
| var position, indices, groups; | |
| var group, start, count; | |
| var index1, index2; | |
| vertex = new Vector3(); | |
| if ( geometry.index !== null ) { | |
| // indexed BufferGeometry | |
| position = geometry.attributes.position; | |
| indices = geometry.index; | |
| groups = geometry.groups; | |
| if ( groups.length === 0 ) { | |
| groups = [ { start: 0, count: indices.count, materialIndex: 0 } ]; | |
| } | |
| // create a data structure that contains all eges without duplicates | |
| for ( o = 0, ol = groups.length; o < ol; ++ o ) { | |
| group = groups[ o ]; | |
| start = group.start; | |
| count = group.count; | |
| for ( i = start, l = ( start + count ); i < l; i += 3 ) { | |
| for ( j = 0; j < 3; j ++ ) { | |
| edge1 = indices.getX( i + j ); | |
| edge2 = indices.getX( i + ( j + 1 ) % 3 ); | |
| edge[ 0 ] = Math.min( edge1, edge2 ); // sorting prevents duplicates | |
| edge[ 1 ] = Math.max( edge1, edge2 ); | |
| key = edge[ 0 ] + ',' + edge[ 1 ]; | |
| if ( edges[ key ] === undefined ) { | |
| edges[ key ] = { index1: edge[ 0 ], index2: edge[ 1 ] }; | |
| } | |
| } | |
| } | |
| } | |
| // generate vertices | |
| for ( key in edges ) { | |
| e = edges[ key ]; | |
| vertex.fromBufferAttribute( position, e.index1 ); | |
| vertices.push( vertex.x, vertex.y, vertex.z ); | |
| vertex.fromBufferAttribute( position, e.index2 ); | |
| vertices.push( vertex.x, vertex.y, vertex.z ); | |
| } | |
| } else { | |
| // non-indexed BufferGeometry | |
| position = geometry.attributes.position; | |
| for ( i = 0, l = ( position.count / 3 ); i < l; i ++ ) { | |
| for ( j = 0; j < 3; j ++ ) { | |
| // three edges per triangle, an edge is represented as (index1, index2) | |
| // e.g. the first triangle has the following edges: (0,1),(1,2),(2,0) | |
| index1 = 3 * i + j; | |
| vertex.fromBufferAttribute( position, index1 ); | |
| vertices.push( vertex.x, vertex.y, vertex.z ); | |
| index2 = 3 * i + ( ( j + 1 ) % 3 ); | |
| vertex.fromBufferAttribute( position, index2 ); | |
| vertices.push( vertex.x, vertex.y, vertex.z ); | |
| } | |
| } | |
| } | |
| } | |
| // build geometry | |
| this.addAttribute( 'position', new Float32BufferAttribute( vertices, 3 ) ); | |
| } | |
| WireframeGeometry.prototype = Object.create( BufferGeometry.prototype ); | |
| WireframeGeometry.prototype.constructor = WireframeGeometry; | |
| /** | |
| * @author zz85 / https://github.com/zz85 | |
| * @author Mugen87 / https://github.com/Mugen87 | |
| * | |
| * Parametric Surfaces Geometry | |
| * based on the brilliant article by @prideout http://prideout.net/blog/?p=44 | |
| */ | |
| // ParametricGeometry | |
| function ParametricGeometry( func, slices, stacks ) { | |
| Geometry.call( this ); | |
| this.type = 'ParametricGeometry'; | |
| this.parameters = { | |
| func: func, | |
| slices: slices, | |
| stacks: stacks | |
| }; | |
| this.fromBufferGeometry( new ParametricBufferGeometry( func, slices, stacks ) ); | |
| this.mergeVertices(); | |
| } | |
| ParametricGeometry.prototype = Object.create( Geometry.prototype ); | |
| ParametricGeometry.prototype.constructor = ParametricGeometry; | |
| // ParametricBufferGeometry | |
| function ParametricBufferGeometry( func, slices, stacks ) { | |
| BufferGeometry.call( this ); | |
| this.type = 'ParametricBufferGeometry'; | |
| this.parameters = { | |
| func: func, | |
| slices: slices, | |
| stacks: stacks | |
| }; | |
| // buffers | |
| var indices = []; | |
| var vertices = []; | |
| var normals = []; | |
| var uvs = []; | |
| var EPS = 0.00001; | |
| var normal = new Vector3(); | |
| var p0 = new Vector3(), p1 = new Vector3(); | |
| var pu = new Vector3(), pv = new Vector3(); | |
| var i, j; | |
| // generate vertices, normals and uvs | |
| var sliceCount = slices + 1; | |
| for ( i = 0; i <= stacks; i ++ ) { | |
| var v = i / stacks; | |
| for ( j = 0; j <= slices; j ++ ) { | |
| var u = j / slices; | |
| // vertex | |
| p0 = func( u, v, p0 ); | |
| vertices.push( p0.x, p0.y, p0.z ); | |
| // normal | |
| // approximate tangent vectors via finite differences | |
| if ( u - EPS >= 0 ) { | |
| p1 = func( u - EPS, v, p1 ); | |
| pu.subVectors( p0, p1 ); | |
| } else { | |
| p1 = func( u + EPS, v, p1 ); | |
| pu.subVectors( p1, p0 ); | |
| } | |
| if ( v - EPS >= 0 ) { | |
| p1 = func( u, v - EPS, p1 ); | |
| pv.subVectors( p0, p1 ); | |
| } else { | |
| p1 = func( u, v + EPS, p1 ); | |
| pv.subVectors( p1, p0 ); | |
| } | |
| // cross product of tangent vectors returns surface normal | |
| normal.crossVectors( pu, pv ).normalize(); | |
| normals.push( normal.x, normal.y, normal.z ); | |
| // uv | |
| uvs.push( u, v ); | |
| } | |
| } | |
| // generate indices | |
| for ( i = 0; i < stacks; i ++ ) { | |
| for ( j = 0; j < slices; j ++ ) { | |
| var a = i * sliceCount + j; | |
| var b = i * sliceCount + j + 1; | |
| var c = ( i + 1 ) * sliceCount + j + 1; | |
| var d = ( i + 1 ) * sliceCount + j; | |
| // faces one and two | |
| indices.push( a, b, d ); | |
| indices.push( b, c, d ); | |
| } | |
| } | |
| // build geometry | |
| this.setIndex( indices ); | |
| this.addAttribute( 'position', new Float32BufferAttribute( vertices, 3 ) ); | |
| this.addAttribute( 'normal', new Float32BufferAttribute( normals, 3 ) ); | |
| this.addAttribute( 'uv', new Float32BufferAttribute( uvs, 2 ) ); | |
| } | |
| ParametricBufferGeometry.prototype = Object.create( BufferGeometry.prototype ); | |
| ParametricBufferGeometry.prototype.constructor = ParametricBufferGeometry; | |
| /** | |
| * @author clockworkgeek / https://github.com/clockworkgeek | |
| * @author timothypratley / https://github.com/timothypratley | |
| * @author WestLangley / http://github.com/WestLangley | |
| * @author Mugen87 / https://github.com/Mugen87 | |
| */ | |
| // PolyhedronGeometry | |
| function PolyhedronGeometry( vertices, indices, radius, detail ) { | |
| Geometry.call( this ); | |
| this.type = 'PolyhedronGeometry'; | |
| this.parameters = { | |
| vertices: vertices, | |
| indices: indices, | |
| radius: radius, | |
| detail: detail | |
| }; | |
| this.fromBufferGeometry( new PolyhedronBufferGeometry( vertices, indices, radius, detail ) ); | |
| this.mergeVertices(); | |
| } | |
| PolyhedronGeometry.prototype = Object.create( Geometry.prototype ); | |
| PolyhedronGeometry.prototype.constructor = PolyhedronGeometry; | |
| // PolyhedronBufferGeometry | |
| function PolyhedronBufferGeometry( vertices, indices, radius, detail ) { | |
| BufferGeometry.call( this ); | |
| this.type = 'PolyhedronBufferGeometry'; | |
| this.parameters = { | |
| vertices: vertices, | |
| indices: indices, | |
| radius: radius, | |
| detail: detail | |
| }; | |
| radius = radius || 1; | |
| detail = detail || 0; | |
| // default buffer data | |
| var vertexBuffer = []; | |
| var uvBuffer = []; | |
| // the subdivision creates the vertex buffer data | |
| subdivide( detail ); | |
| // all vertices should lie on a conceptual sphere with a given radius | |
| appplyRadius( radius ); | |
| // finally, create the uv data | |
| generateUVs(); | |
| // build non-indexed geometry | |
| this.addAttribute( 'position', new Float32BufferAttribute( vertexBuffer, 3 ) ); | |
| this.addAttribute( 'normal', new Float32BufferAttribute( vertexBuffer.slice(), 3 ) ); | |
| this.addAttribute( 'uv', new Float32BufferAttribute( uvBuffer, 2 ) ); | |
| if ( detail === 0 ) { | |
| this.computeVertexNormals(); // flat normals | |
| } else { | |
| this.normalizeNormals(); // smooth normals | |
| } | |
| // helper functions | |
| function subdivide( detail ) { | |
| var a = new Vector3(); | |
| var b = new Vector3(); | |
| var c = new Vector3(); | |
| // iterate over all faces and apply a subdivison with the given detail value | |
| for ( var i = 0; i < indices.length; i += 3 ) { | |
| // get the vertices of the face | |
| getVertexByIndex( indices[ i + 0 ], a ); | |
| getVertexByIndex( indices[ i + 1 ], b ); | |
| getVertexByIndex( indices[ i + 2 ], c ); | |
| // perform subdivision | |
| subdivideFace( a, b, c, detail ); | |
| } | |
| } | |
| function subdivideFace( a, b, c, detail ) { | |
| var cols = Math.pow( 2, detail ); | |
| // we use this multidimensional array as a data structure for creating the subdivision | |
| var v = []; | |
| var i, j; | |
| // construct all of the vertices for this subdivision | |
| for ( i = 0; i <= cols; i ++ ) { | |
| v[ i ] = []; | |
| var aj = a.clone().lerp( c, i / cols ); | |
| var bj = b.clone().lerp( c, i / cols ); | |
| var rows = cols - i; | |
| for ( j = 0; j <= rows; j ++ ) { | |
| if ( j === 0 && i === cols ) { | |
| v[ i ][ j ] = aj; | |
| } else { | |
| v[ i ][ j ] = aj.clone().lerp( bj, j / rows ); | |
| } | |
| } | |
| } | |
| // construct all of the faces | |
| for ( i = 0; i < cols; i ++ ) { | |
| for ( j = 0; j < 2 * ( cols - i ) - 1; j ++ ) { | |
| var k = Math.floor( j / 2 ); | |
| if ( j % 2 === 0 ) { | |
| pushVertex( v[ i ][ k + 1 ] ); | |
| pushVertex( v[ i + 1 ][ k ] ); | |
| pushVertex( v[ i ][ k ] ); | |
| } else { | |
| pushVertex( v[ i ][ k + 1 ] ); | |
| pushVertex( v[ i + 1 ][ k + 1 ] ); | |
| pushVertex( v[ i + 1 ][ k ] ); | |
| } | |
| } | |
| } | |
| } | |
| function appplyRadius( radius ) { | |
| var vertex = new Vector3(); | |
| // iterate over the entire buffer and apply the radius to each vertex | |
| for ( var i = 0; i < vertexBuffer.length; i += 3 ) { | |
| vertex.x = vertexBuffer[ i + 0 ]; | |
| vertex.y = vertexBuffer[ i + 1 ]; | |
| vertex.z = vertexBuffer[ i + 2 ]; | |
| vertex.normalize().multiplyScalar( radius ); | |
| vertexBuffer[ i + 0 ] = vertex.x; | |
| vertexBuffer[ i + 1 ] = vertex.y; | |
| vertexBuffer[ i + 2 ] = vertex.z; | |
| } | |
| } | |
| function generateUVs() { | |
| var vertex = new Vector3(); | |
| for ( var i = 0; i < vertexBuffer.length; i += 3 ) { | |
| vertex.x = vertexBuffer[ i + 0 ]; | |
| vertex.y = vertexBuffer[ i + 1 ]; | |
| vertex.z = vertexBuffer[ i + 2 ]; | |
| var u = azimuth( vertex ) / 2 / Math.PI + 0.5; | |
| var v = inclination( vertex ) / Math.PI + 0.5; | |
| uvBuffer.push( u, 1 - v ); | |
| } | |
| correctUVs(); | |
| correctSeam(); | |
| } | |
| function correctSeam() { | |
| // handle case when face straddles the seam, see #3269 | |
| for ( var i = 0; i < uvBuffer.length; i += 6 ) { | |
| // uv data of a single face | |
| var x0 = uvBuffer[ i + 0 ]; | |
| var x1 = uvBuffer[ i + 2 ]; | |
| var x2 = uvBuffer[ i + 4 ]; | |
| var max = Math.max( x0, x1, x2 ); | |
| var min = Math.min( x0, x1, x2 ); | |
| // 0.9 is somewhat arbitrary | |
| if ( max > 0.9 && min < 0.1 ) { | |
| if ( x0 < 0.2 ) uvBuffer[ i + 0 ] += 1; | |
| if ( x1 < 0.2 ) uvBuffer[ i + 2 ] += 1; | |
| if ( x2 < 0.2 ) uvBuffer[ i + 4 ] += 1; | |
| } | |
| } | |
| } | |
| function pushVertex( vertex ) { | |
| vertexBuffer.push( vertex.x, vertex.y, vertex.z ); | |
| } | |
| function getVertexByIndex( index, vertex ) { | |
| var stride = index * 3; | |
| vertex.x = vertices[ stride + 0 ]; | |
| vertex.y = vertices[ stride + 1 ]; | |
| vertex.z = vertices[ stride + 2 ]; | |
| } | |
| function correctUVs() { | |
| var a = new Vector3(); | |
| var b = new Vector3(); | |
| var c = new Vector3(); | |
| var centroid = new Vector3(); | |
| var uvA = new Vector2(); | |
| var uvB = new Vector2(); | |
| var uvC = new Vector2(); | |
| for ( var i = 0, j = 0; i < vertexBuffer.length; i += 9, j += 6 ) { | |
| a.set( vertexBuffer[ i + 0 ], vertexBuffer[ i + 1 ], vertexBuffer[ i + 2 ] ); | |
| b.set( vertexBuffer[ i + 3 ], vertexBuffer[ i + 4 ], vertexBuffer[ i + 5 ] ); | |
| c.set( vertexBuffer[ i + 6 ], vertexBuffer[ i + 7 ], vertexBuffer[ i + 8 ] ); | |
| uvA.set( uvBuffer[ j + 0 ], uvBuffer[ j + 1 ] ); | |
| uvB.set( uvBuffer[ j + 2 ], uvBuffer[ j + 3 ] ); | |
| uvC.set( uvBuffer[ j + 4 ], uvBuffer[ j + 5 ] ); | |
| centroid.copy( a ).add( b ).add( c ).divideScalar( 3 ); | |
| var azi = azimuth( centroid ); | |
| correctUV( uvA, j + 0, a, azi ); | |
| correctUV( uvB, j + 2, b, azi ); | |
| correctUV( uvC, j + 4, c, azi ); | |
| } | |
| } | |
| function correctUV( uv, stride, vector, azimuth ) { | |
| if ( ( azimuth < 0 ) && ( uv.x === 1 ) ) { | |
| uvBuffer[ stride ] = uv.x - 1; | |
| } | |
| if ( ( vector.x === 0 ) && ( vector.z === 0 ) ) { | |
| uvBuffer[ stride ] = azimuth / 2 / Math.PI + 0.5; | |
| } | |
| } | |
| // Angle around the Y axis, counter-clockwise when looking from above. | |
| function azimuth( vector ) { | |
| return Math.atan2( vector.z, - vector.x ); | |
| } | |
| // Angle above the XZ plane. | |
| function inclination( vector ) { | |
| return Math.atan2( - vector.y, Math.sqrt( ( vector.x * vector.x ) + ( vector.z * vector.z ) ) ); | |
| } | |
| } | |
| PolyhedronBufferGeometry.prototype = Object.create( BufferGeometry.prototype ); | |
| PolyhedronBufferGeometry.prototype.constructor = PolyhedronBufferGeometry; | |
| /** | |
| * @author timothypratley / https://github.com/timothypratley | |
| * @author Mugen87 / https://github.com/Mugen87 | |
| */ | |
| // TetrahedronGeometry | |
| function TetrahedronGeometry( radius, detail ) { | |
| Geometry.call( this ); | |
| this.type = 'TetrahedronGeometry'; | |
| this.parameters = { | |
| radius: radius, | |
| detail: detail | |
| }; | |
| this.fromBufferGeometry( new TetrahedronBufferGeometry( radius, detail ) ); | |
| this.mergeVertices(); | |
| } | |
| TetrahedronGeometry.prototype = Object.create( Geometry.prototype ); | |
| TetrahedronGeometry.prototype.constructor = TetrahedronGeometry; | |
| // TetrahedronBufferGeometry | |
| function TetrahedronBufferGeometry( radius, detail ) { | |
| var vertices = [ | |
| 1, 1, 1, - 1, - 1, 1, - 1, 1, - 1, 1, - 1, - 1 | |
| ]; | |
| var indices = [ | |
| 2, 1, 0, 0, 3, 2, 1, 3, 0, 2, 3, 1 | |
| ]; | |
| PolyhedronBufferGeometry.call( this, vertices, indices, radius, detail ); | |
| this.type = 'TetrahedronBufferGeometry'; | |
| this.parameters = { | |
| radius: radius, | |
| detail: detail | |
| }; | |
| } | |
| TetrahedronBufferGeometry.prototype = Object.create( PolyhedronBufferGeometry.prototype ); | |
| TetrahedronBufferGeometry.prototype.constructor = TetrahedronBufferGeometry; | |
| /** | |
| * @author timothypratley / https://github.com/timothypratley | |
| * @author Mugen87 / https://github.com/Mugen87 | |
| */ | |
| // OctahedronGeometry | |
| function OctahedronGeometry( radius, detail ) { | |
| Geometry.call( this ); | |
| this.type = 'OctahedronGeometry'; | |
| this.parameters = { | |
| radius: radius, | |
| detail: detail | |
| }; | |
| this.fromBufferGeometry( new OctahedronBufferGeometry( radius, detail ) ); | |
| this.mergeVertices(); | |
| } | |
| OctahedronGeometry.prototype = Object.create( Geometry.prototype ); | |
| OctahedronGeometry.prototype.constructor = OctahedronGeometry; | |
| // OctahedronBufferGeometry | |
| function OctahedronBufferGeometry( radius, detail ) { | |
| var vertices = [ | |
| 1, 0, 0, - 1, 0, 0, 0, 1, 0, | |
| 0, - 1, 0, 0, 0, 1, 0, 0, - 1 | |
| ]; | |
| var indices = [ | |
| 0, 2, 4, 0, 4, 3, 0, 3, 5, | |
| 0, 5, 2, 1, 2, 5, 1, 5, 3, | |
| 1, 3, 4, 1, 4, 2 | |
| ]; | |
| PolyhedronBufferGeometry.call( this, vertices, indices, radius, detail ); | |
| this.type = 'OctahedronBufferGeometry'; | |
| this.parameters = { | |
| radius: radius, | |
| detail: detail | |
| }; | |
| } | |
| OctahedronBufferGeometry.prototype = Object.create( PolyhedronBufferGeometry.prototype ); | |
| OctahedronBufferGeometry.prototype.constructor = OctahedronBufferGeometry; | |
| /** | |
| * @author timothypratley / https://github.com/timothypratley | |
| * @author Mugen87 / https://github.com/Mugen87 | |
| */ | |
| // IcosahedronGeometry | |
| function IcosahedronGeometry( radius, detail ) { | |
| Geometry.call( this ); | |
| this.type = 'IcosahedronGeometry'; | |
| this.parameters = { | |
| radius: radius, | |
| detail: detail | |
| }; | |
| this.fromBufferGeometry( new IcosahedronBufferGeometry( radius, detail ) ); | |
| this.mergeVertices(); | |
| } | |
| IcosahedronGeometry.prototype = Object.create( Geometry.prototype ); | |
| IcosahedronGeometry.prototype.constructor = IcosahedronGeometry; | |
| // IcosahedronBufferGeometry | |
| function IcosahedronBufferGeometry( radius, detail ) { | |
| var t = ( 1 + Math.sqrt( 5 ) ) / 2; | |
| var vertices = [ | |
| - 1, t, 0, 1, t, 0, - 1, - t, 0, 1, - t, 0, | |
| 0, - 1, t, 0, 1, t, 0, - 1, - t, 0, 1, - t, | |
| t, 0, - 1, t, 0, 1, - t, 0, - 1, - t, 0, 1 | |
| ]; | |
| var indices = [ | |
| 0, 11, 5, 0, 5, 1, 0, 1, 7, 0, 7, 10, 0, 10, 11, | |
| 1, 5, 9, 5, 11, 4, 11, 10, 2, 10, 7, 6, 7, 1, 8, | |
| 3, 9, 4, 3, 4, 2, 3, 2, 6, 3, 6, 8, 3, 8, 9, | |
| 4, 9, 5, 2, 4, 11, 6, 2, 10, 8, 6, 7, 9, 8, 1 | |
| ]; | |
| PolyhedronBufferGeometry.call( this, vertices, indices, radius, detail ); | |
| this.type = 'IcosahedronBufferGeometry'; | |
| this.parameters = { | |
| radius: radius, | |
| detail: detail | |
| }; | |
| } | |
| IcosahedronBufferGeometry.prototype = Object.create( PolyhedronBufferGeometry.prototype ); | |
| IcosahedronBufferGeometry.prototype.constructor = IcosahedronBufferGeometry; | |
| /** | |
| * @author Abe Pazos / https://hamoid.com | |
| * @author Mugen87 / https://github.com/Mugen87 | |
| */ | |
| // DodecahedronGeometry | |
| function DodecahedronGeometry( radius, detail ) { | |
| Geometry.call( this ); | |
| this.type = 'DodecahedronGeometry'; | |
| this.parameters = { | |
| radius: radius, | |
| detail: detail | |
| }; | |
| this.fromBufferGeometry( new DodecahedronBufferGeometry( radius, detail ) ); | |
| this.mergeVertices(); | |
| } | |
| DodecahedronGeometry.prototype = Object.create( Geometry.prototype ); | |
| DodecahedronGeometry.prototype.constructor = DodecahedronGeometry; | |
| // DodecahedronBufferGeometry | |
| function DodecahedronBufferGeometry( radius, detail ) { | |
| var t = ( 1 + Math.sqrt( 5 ) ) / 2; | |
| var r = 1 / t; | |
| var vertices = [ | |
| // (±1, ±1, ±1) | |
| - 1, - 1, - 1, - 1, - 1, 1, | |
| - 1, 1, - 1, - 1, 1, 1, | |
| 1, - 1, - 1, 1, - 1, 1, | |
| 1, 1, - 1, 1, 1, 1, | |
| // (0, ±1/φ, ±φ) | |
| 0, - r, - t, 0, - r, t, | |
| 0, r, - t, 0, r, t, | |
| // (±1/φ, ±φ, 0) | |
| - r, - t, 0, - r, t, 0, | |
| r, - t, 0, r, t, 0, | |
| // (±φ, 0, ±1/φ) | |
| - t, 0, - r, t, 0, - r, | |
| - t, 0, r, t, 0, r | |
| ]; | |
| var indices = [ | |
| 3, 11, 7, 3, 7, 15, 3, 15, 13, | |
| 7, 19, 17, 7, 17, 6, 7, 6, 15, | |
| 17, 4, 8, 17, 8, 10, 17, 10, 6, | |
| 8, 0, 16, 8, 16, 2, 8, 2, 10, | |
| 0, 12, 1, 0, 1, 18, 0, 18, 16, | |
| 6, 10, 2, 6, 2, 13, 6, 13, 15, | |
| 2, 16, 18, 2, 18, 3, 2, 3, 13, | |
| 18, 1, 9, 18, 9, 11, 18, 11, 3, | |
| 4, 14, 12, 4, 12, 0, 4, 0, 8, | |
| 11, 9, 5, 11, 5, 19, 11, 19, 7, | |
| 19, 5, 14, 19, 14, 4, 19, 4, 17, | |
| 1, 12, 14, 1, 14, 5, 1, 5, 9 | |
| ]; | |
| PolyhedronBufferGeometry.call( this, vertices, indices, radius, detail ); | |
| this.type = 'DodecahedronBufferGeometry'; | |
| this.parameters = { | |
| radius: radius, | |
| detail: detail | |
| }; | |
| } | |
| DodecahedronBufferGeometry.prototype = Object.create( PolyhedronBufferGeometry.prototype ); | |
| DodecahedronBufferGeometry.prototype.constructor = DodecahedronBufferGeometry; | |
| /** | |
| * @author oosmoxiecode / https://github.com/oosmoxiecode | |
| * @author WestLangley / https://github.com/WestLangley | |
| * @author zz85 / https://github.com/zz85 | |
| * @author miningold / https://github.com/miningold | |
| * @author jonobr1 / https://github.com/jonobr1 | |
| * @author Mugen87 / https://github.com/Mugen87 | |
| * | |
| */ | |
| // TubeGeometry | |
| function TubeGeometry( path, tubularSegments, radius, radialSegments, closed, taper ) { | |
| Geometry.call( this ); | |
| this.type = 'TubeGeometry'; | |
| this.parameters = { | |
| path: path, | |
| tubularSegments: tubularSegments, | |
| radius: radius, | |
| radialSegments: radialSegments, | |
| closed: closed | |
| }; | |
| if ( taper !== undefined ) console.warn( 'THREE.TubeGeometry: taper has been removed.' ); | |
| var bufferGeometry = new TubeBufferGeometry( path, tubularSegments, radius, radialSegments, closed ); | |
| // expose internals | |
| this.tangents = bufferGeometry.tangents; | |
| this.normals = bufferGeometry.normals; | |
| this.binormals = bufferGeometry.binormals; | |
| // create geometry | |
| this.fromBufferGeometry( bufferGeometry ); | |
| this.mergeVertices(); | |
| } | |
| TubeGeometry.prototype = Object.create( Geometry.prototype ); | |
| TubeGeometry.prototype.constructor = TubeGeometry; | |
| // TubeBufferGeometry | |
| function TubeBufferGeometry( path, tubularSegments, radius, radialSegments, closed ) { | |
| BufferGeometry.call( this ); | |
| this.type = 'TubeBufferGeometry'; | |
| this.parameters = { | |
| path: path, | |
| tubularSegments: tubularSegments, | |
| radius: radius, | |
| radialSegments: radialSegments, | |
| closed: closed | |
| }; | |
| tubularSegments = tubularSegments || 64; | |
| radius = radius || 1; | |
| radialSegments = radialSegments || 8; | |
| closed = closed || false; | |
| var frames = path.computeFrenetFrames( tubularSegments, closed ); | |
| // expose internals | |
| this.tangents = frames.tangents; | |
| this.normals = frames.normals; | |
| this.binormals = frames.binormals; | |
| // helper variables | |
| var vertex = new Vector3(); | |
| var normal = new Vector3(); | |
| var uv = new Vector2(); | |
| var P = new Vector3(); | |
| var i, j; | |
| // buffer | |
| var vertices = []; | |
| var normals = []; | |
| var uvs = []; | |
| var indices = []; | |
| // create buffer data | |
| generateBufferData(); | |
| // build geometry | |
| this.setIndex( indices ); | |
| this.addAttribute( 'position', new Float32BufferAttribute( vertices, 3 ) ); | |
| this.addAttribute( 'normal', new Float32BufferAttribute( normals, 3 ) ); | |
| this.addAttribute( 'uv', new Float32BufferAttribute( uvs, 2 ) ); | |
| // functions | |
| function generateBufferData() { | |
| for ( i = 0; i < tubularSegments; i ++ ) { | |
| generateSegment( i ); | |
| } | |
| // if the geometry is not closed, generate the last row of vertices and normals | |
| // at the regular position on the given path | |
| // | |
| // if the geometry is closed, duplicate the first row of vertices and normals (uvs will differ) | |
| generateSegment( ( closed === false ) ? tubularSegments : 0 ); | |
| // uvs are generated in a separate function. | |
| // this makes it easy compute correct values for closed geometries | |
| generateUVs(); | |
| // finally create faces | |
| generateIndices(); | |
| } | |
| function generateSegment( i ) { | |
| // we use getPointAt to sample evenly distributed points from the given path | |
| P = path.getPointAt( i / tubularSegments, P ); | |
| // retrieve corresponding normal and binormal | |
| var N = frames.normals[ i ]; | |
| var B = frames.binormals[ i ]; | |
| // generate normals and vertices for the current segment | |
| for ( j = 0; j <= radialSegments; j ++ ) { | |
| var v = j / radialSegments * Math.PI * 2; | |
| var sin = Math.sin( v ); | |
| var cos = - Math.cos( v ); | |
| // normal | |
| normal.x = ( cos * N.x + sin * B.x ); | |
| normal.y = ( cos * N.y + sin * B.y ); | |
| normal.z = ( cos * N.z + sin * B.z ); | |
| normal.normalize(); | |
| normals.push( normal.x, normal.y, normal.z ); | |
| // vertex | |
| vertex.x = P.x + radius * normal.x; | |
| vertex.y = P.y + radius * normal.y; | |
| vertex.z = P.z + radius * normal.z; | |
| vertices.push( vertex.x, vertex.y, vertex.z ); | |
| } | |
| } | |
| function generateIndices() { | |
| for ( j = 1; j <= tubularSegments; j ++ ) { | |
| for ( i = 1; i <= radialSegments; i ++ ) { | |
| var a = ( radialSegments + 1 ) * ( j - 1 ) + ( i - 1 ); | |
| var b = ( radialSegments + 1 ) * j + ( i - 1 ); | |
| var c = ( radialSegments + 1 ) * j + i; | |
| var d = ( radialSegments + 1 ) * ( j - 1 ) + i; | |
| // faces | |
| indices.push( a, b, d ); | |
| indices.push( b, c, d ); | |
| } | |
| } | |
| } | |
| function generateUVs() { | |
| for ( i = 0; i <= tubularSegments; i ++ ) { | |
| for ( j = 0; j <= radialSegments; j ++ ) { | |
| uv.x = i / tubularSegments; | |
| uv.y = j / radialSegments; | |
| uvs.push( uv.x, uv.y ); | |
| } | |
| } | |
| } | |
| } | |
| TubeBufferGeometry.prototype = Object.create( BufferGeometry.prototype ); | |
| TubeBufferGeometry.prototype.constructor = TubeBufferGeometry; | |
| /** | |
| * @author oosmoxiecode | |
| * @author Mugen87 / https://github.com/Mugen87 | |
| * | |
| * based on http://www.blackpawn.com/texts/pqtorus/ | |
| */ | |
| // TorusKnotGeometry | |
| function TorusKnotGeometry( radius, tube, tubularSegments, radialSegments, p, q, heightScale ) { | |
| Geometry.call( this ); | |
| this.type = 'TorusKnotGeometry'; | |
| this.parameters = { | |
| radius: radius, | |
| tube: tube, | |
| tubularSegments: tubularSegments, | |
| radialSegments: radialSegments, | |
| p: p, | |
| q: q | |
| }; | |
| if ( heightScale !== undefined ) console.warn( 'THREE.TorusKnotGeometry: heightScale has been deprecated. Use .scale( x, y, z ) instead.' ); | |
| this.fromBufferGeometry( new TorusKnotBufferGeometry( radius, tube, tubularSegments, radialSegments, p, q ) ); | |
| this.mergeVertices(); | |
| } | |
| TorusKnotGeometry.prototype = Object.create( Geometry.prototype ); | |
| TorusKnotGeometry.prototype.constructor = TorusKnotGeometry; | |
| // TorusKnotBufferGeometry | |
| function TorusKnotBufferGeometry( radius, tube, tubularSegments, radialSegments, p, q ) { | |
| BufferGeometry.call( this ); | |
| this.type = 'TorusKnotBufferGeometry'; | |
| this.parameters = { | |
| radius: radius, | |
| tube: tube, | |
| tubularSegments: tubularSegments, | |
| radialSegments: radialSegments, | |
| p: p, | |
| q: q | |
| }; | |
| radius = radius || 1; | |
| tube = tube || 0.4; | |
| tubularSegments = Math.floor( tubularSegments ) || 64; | |
| radialSegments = Math.floor( radialSegments ) || 8; | |
| p = p || 2; | |
| q = q || 3; | |
| // buffers | |
| var indices = []; | |
| var vertices = []; | |
| var normals = []; | |
| var uvs = []; | |
| // helper variables | |
| var i, j; | |
| var vertex = new Vector3(); | |
| var normal = new Vector3(); | |
| var P1 = new Vector3(); | |
| var P2 = new Vector3(); | |
| var B = new Vector3(); | |
| var T = new Vector3(); | |
| var N = new Vector3(); | |
| // generate vertices, normals and uvs | |
| for ( i = 0; i <= tubularSegments; ++ i ) { | |
| // the radian "u" is used to calculate the position on the torus curve of the current tubular segement | |
| var u = i / tubularSegments * p * Math.PI * 2; | |
| // now we calculate two points. P1 is our current position on the curve, P2 is a little farther ahead. | |
| // these points are used to create a special "coordinate space", which is necessary to calculate the correct vertex positions | |
| calculatePositionOnCurve( u, p, q, radius, P1 ); | |
| calculatePositionOnCurve( u + 0.01, p, q, radius, P2 ); | |
| // calculate orthonormal basis | |
| T.subVectors( P2, P1 ); | |
| N.addVectors( P2, P1 ); | |
| B.crossVectors( T, N ); | |
| N.crossVectors( B, T ); | |
| // normalize B, N. T can be ignored, we don't use it | |
| B.normalize(); | |
| N.normalize(); | |
| for ( j = 0; j <= radialSegments; ++ j ) { | |
| // now calculate the vertices. they are nothing more than an extrusion of the torus curve. | |
| // because we extrude a shape in the xy-plane, there is no need to calculate a z-value. | |
| var v = j / radialSegments * Math.PI * 2; | |
| var cx = - tube * Math.cos( v ); | |
| var cy = tube * Math.sin( v ); | |
| // now calculate the final vertex position. | |
| // first we orient the extrusion with our basis vectos, then we add it to the current position on the curve | |
| vertex.x = P1.x + ( cx * N.x + cy * B.x ); | |
| vertex.y = P1.y + ( cx * N.y + cy * B.y ); | |
| vertex.z = P1.z + ( cx * N.z + cy * B.z ); | |
| vertices.push( vertex.x, vertex.y, vertex.z ); | |
| // normal (P1 is always the center/origin of the extrusion, thus we can use it to calculate the normal) | |
| normal.subVectors( vertex, P1 ).normalize(); | |
| normals.push( normal.x, normal.y, normal.z ); | |
| // uv | |
| uvs.push( i / tubularSegments ); | |
| uvs.push( j / radialSegments ); | |
| } | |
| } | |
| // generate indices | |
| for ( j = 1; j <= tubularSegments; j ++ ) { | |
| for ( i = 1; i <= radialSegments; i ++ ) { | |
| // indices | |
| var a = ( radialSegments + 1 ) * ( j - 1 ) + ( i - 1 ); | |
| var b = ( radialSegments + 1 ) * j + ( i - 1 ); | |
| var c = ( radialSegments + 1 ) * j + i; | |
| var d = ( radialSegments + 1 ) * ( j - 1 ) + i; | |
| // faces | |
| indices.push( a, b, d ); | |
| indices.push( b, c, d ); | |
| } | |
| } | |
| // build geometry | |
| this.setIndex( indices ); | |
| this.addAttribute( 'position', new Float32BufferAttribute( vertices, 3 ) ); | |
| this.addAttribute( 'normal', new Float32BufferAttribute( normals, 3 ) ); | |
| this.addAttribute( 'uv', new Float32BufferAttribute( uvs, 2 ) ); | |
| // this function calculates the current position on the torus curve | |
| function calculatePositionOnCurve( u, p, q, radius, position ) { | |
| var cu = Math.cos( u ); | |
| var su = Math.sin( u ); | |
| var quOverP = q / p * u; | |
| var cs = Math.cos( quOverP ); | |
| position.x = radius * ( 2 + cs ) * 0.5 * cu; | |
| position.y = radius * ( 2 + cs ) * su * 0.5; | |
| position.z = radius * Math.sin( quOverP ) * 0.5; | |
| } | |
| } | |
| TorusKnotBufferGeometry.prototype = Object.create( BufferGeometry.prototype ); | |
| TorusKnotBufferGeometry.prototype.constructor = TorusKnotBufferGeometry; | |
| /** | |
| * @author oosmoxiecode | |
| * @author mrdoob / http://mrdoob.com/ | |
| * @author Mugen87 / https://github.com/Mugen87 | |
| */ | |
| // TorusGeometry | |
| function TorusGeometry( radius, tube, radialSegments, tubularSegments, arc ) { | |
| Geometry.call( this ); | |
| this.type = 'TorusGeometry'; | |
| this.parameters = { | |
| radius: radius, | |
| tube: tube, | |
| radialSegments: radialSegments, | |
| tubularSegments: tubularSegments, | |
| arc: arc | |
| }; | |
| this.fromBufferGeometry( new TorusBufferGeometry( radius, tube, radialSegments, tubularSegments, arc ) ); | |
| this.mergeVertices(); | |
| } | |
| TorusGeometry.prototype = Object.create( Geometry.prototype ); | |
| TorusGeometry.prototype.constructor = TorusGeometry; | |
| // TorusBufferGeometry | |
| function TorusBufferGeometry( radius, tube, radialSegments, tubularSegments, arc ) { | |
| BufferGeometry.call( this ); | |
| this.type = 'TorusBufferGeometry'; | |
| this.parameters = { | |
| radius: radius, | |
| tube: tube, | |
| radialSegments: radialSegments, | |
| tubularSegments: tubularSegments, | |
| arc: arc | |
| }; | |
| radius = radius || 1; | |
| tube = tube || 0.4; | |
| radialSegments = Math.floor( radialSegments ) || 8; | |
| tubularSegments = Math.floor( tubularSegments ) || 6; | |
| arc = arc || Math.PI * 2; | |
| // buffers | |
| var indices = []; | |
| var vertices = []; | |
| var normals = []; | |
| var uvs = []; | |
| // helper variables | |
| var center = new Vector3(); | |
| var vertex = new Vector3(); | |
| var normal = new Vector3(); | |
| var j, i; | |
| // generate vertices, normals and uvs | |
| for ( j = 0; j <= radialSegments; j ++ ) { | |
| for ( i = 0; i <= tubularSegments; i ++ ) { | |
| var u = i / tubularSegments * arc; | |
| var v = j / radialSegments * Math.PI * 2; | |
| // vertex | |
| vertex.x = ( radius + tube * Math.cos( v ) ) * Math.cos( u ); | |
| vertex.y = ( radius + tube * Math.cos( v ) ) * Math.sin( u ); | |
| vertex.z = tube * Math.sin( v ); | |
| vertices.push( vertex.x, vertex.y, vertex.z ); | |
| // normal | |
| center.x = radius * Math.cos( u ); | |
| center.y = radius * Math.sin( u ); | |
| normal.subVectors( vertex, center ).normalize(); | |
| normals.push( normal.x, normal.y, normal.z ); | |
| // uv | |
| uvs.push( i / tubularSegments ); | |
| uvs.push( j / radialSegments ); | |
| } | |
| } | |
| // generate indices | |
| for ( j = 1; j <= radialSegments; j ++ ) { | |
| for ( i = 1; i <= tubularSegments; i ++ ) { | |
| // indices | |
| var a = ( tubularSegments + 1 ) * j + i - 1; | |
| var b = ( tubularSegments + 1 ) * ( j - 1 ) + i - 1; | |
| var c = ( tubularSegments + 1 ) * ( j - 1 ) + i; | |
| var d = ( tubularSegments + 1 ) * j + i; | |
| // faces | |
| indices.push( a, b, d ); | |
| indices.push( b, c, d ); | |
| } | |
| } | |
| // build geometry | |
| this.setIndex( indices ); | |
| this.addAttribute( 'position', new Float32BufferAttribute( vertices, 3 ) ); | |
| this.addAttribute( 'normal', new Float32BufferAttribute( normals, 3 ) ); | |
| this.addAttribute( 'uv', new Float32BufferAttribute( uvs, 2 ) ); | |
| } | |
| TorusBufferGeometry.prototype = Object.create( BufferGeometry.prototype ); | |
| TorusBufferGeometry.prototype.constructor = TorusBufferGeometry; | |
| /** | |
| * @author Mugen87 / https://github.com/Mugen87 | |
| * Port from https://github.com/mapbox/earcut (v2.1.2) | |
| */ | |
| var Earcut = { | |
| triangulate: function ( data, holeIndices, dim ) { | |
| dim = dim || 2; | |
| var hasHoles = holeIndices && holeIndices.length, | |
| outerLen = hasHoles ? holeIndices[ 0 ] * dim : data.length, | |
| outerNode = linkedList( data, 0, outerLen, dim, true ), | |
| triangles = []; | |
| if ( ! outerNode ) return triangles; | |
| var minX, minY, maxX, maxY, x, y, invSize; | |
| if ( hasHoles ) outerNode = eliminateHoles( data, holeIndices, outerNode, dim ); | |
| // if the shape is not too simple, we'll use z-order curve hash later; calculate polygon bbox | |
| if ( data.length > 80 * dim ) { | |
| minX = maxX = data[ 0 ]; | |
| minY = maxY = data[ 1 ]; | |
| for ( var i = dim; i < outerLen; i += dim ) { | |
| x = data[ i ]; | |
| y = data[ i + 1 ]; | |
| if ( x < minX ) minX = x; | |
| if ( y < minY ) minY = y; | |
| if ( x > maxX ) maxX = x; | |
| if ( y > maxY ) maxY = y; | |
| } | |
| // minX, minY and invSize are later used to transform coords into integers for z-order calculation | |
| invSize = Math.max( maxX - minX, maxY - minY ); | |
| invSize = invSize !== 0 ? 1 / invSize : 0; | |
| } | |
| earcutLinked( outerNode, triangles, dim, minX, minY, invSize ); | |
| return triangles; | |
| } | |
| }; | |
| // create a circular doubly linked list from polygon points in the specified winding order | |
| function linkedList( data, start, end, dim, clockwise ) { | |
| var i, last; | |
| if ( clockwise === ( signedArea( data, start, end, dim ) > 0 ) ) { | |
| for ( i = start; i < end; i += dim ) last = insertNode( i, data[ i ], data[ i + 1 ], last ); | |
| } else { | |
| for ( i = end - dim; i >= start; i -= dim ) last = insertNode( i, data[ i ], data[ i + 1 ], last ); | |
| } | |
| if ( last && equals( last, last.next ) ) { | |
| removeNode( last ); | |
| last = last.next; | |
| } | |
| return last; | |
| } | |
| // eliminate colinear or duplicate points | |
| function filterPoints( start, end ) { | |
| if ( ! start ) return start; | |
| if ( ! end ) end = start; | |
| var p = start, again; | |
| do { | |
| again = false; | |
| if ( ! p.steiner && ( equals( p, p.next ) || area( p.prev, p, p.next ) === 0 ) ) { | |
| removeNode( p ); | |
| p = end = p.prev; | |
| if ( p === p.next ) break; | |
| again = true; | |
| } else { | |
| p = p.next; | |
| } | |
| } while ( again || p !== end ); | |
| return end; | |
| } | |
| // main ear slicing loop which triangulates a polygon (given as a linked list) | |
| function earcutLinked( ear, triangles, dim, minX, minY, invSize, pass ) { | |
| if ( ! ear ) return; | |
| // interlink polygon nodes in z-order | |
| if ( ! pass && invSize ) indexCurve( ear, minX, minY, invSize ); | |
| var stop = ear, prev, next; | |
| // iterate through ears, slicing them one by one | |
| while ( ear.prev !== ear.next ) { | |
| prev = ear.prev; | |
| next = ear.next; | |
| if ( invSize ? isEarHashed( ear, minX, minY, invSize ) : isEar( ear ) ) { | |
| // cut off the triangle | |
| triangles.push( prev.i / dim ); | |
| triangles.push( ear.i / dim ); | |
| triangles.push( next.i / dim ); | |
| removeNode( ear ); | |
| // skipping the next vertice leads to less sliver triangles | |
| ear = next.next; | |
| stop = next.next; | |
| continue; | |
| } | |
| ear = next; | |
| // if we looped through the whole remaining polygon and can't find any more ears | |
| if ( ear === stop ) { | |
| // try filtering points and slicing again | |
| if ( ! pass ) { | |
| earcutLinked( filterPoints( ear ), triangles, dim, minX, minY, invSize, 1 ); | |
| // if this didn't work, try curing all small self-intersections locally | |
| } else if ( pass === 1 ) { | |
| ear = cureLocalIntersections( ear, triangles, dim ); | |
| earcutLinked( ear, triangles, dim, minX, minY, invSize, 2 ); | |
| // as a last resort, try splitting the remaining polygon into two | |
| } else if ( pass === 2 ) { | |
| splitEarcut( ear, triangles, dim, minX, minY, invSize ); | |
| } | |
| break; | |
| } | |
| } | |
| } | |
| // check whether a polygon node forms a valid ear with adjacent nodes | |
| function isEar( ear ) { | |
| var a = ear.prev, | |
| b = ear, | |
| c = ear.next; | |
| if ( area( a, b, c ) >= 0 ) return false; // reflex, can't be an ear | |
| // now make sure we don't have other points inside the potential ear | |
| var p = ear.next.next; | |
| while ( p !== ear.prev ) { | |
| if ( pointInTriangle( a.x, a.y, b.x, b.y, c.x, c.y, p.x, p.y ) && area( p.prev, p, p.next ) >= 0 ) { | |
| return false; | |
| } | |
| p = p.next; | |
| } | |
| return true; | |
| } | |
| function isEarHashed( ear, minX, minY, invSize ) { | |
| var a = ear.prev, | |
| b = ear, | |
| c = ear.next; | |
| if ( area( a, b, c ) >= 0 ) return false; // reflex, can't be an ear | |
| // triangle bbox; min & max are calculated like this for speed | |
| var minTX = a.x < b.x ? ( a.x < c.x ? a.x : c.x ) : ( b.x < c.x ? b.x : c.x ), | |
| minTY = a.y < b.y ? ( a.y < c.y ? a.y : c.y ) : ( b.y < c.y ? b.y : c.y ), | |
| maxTX = a.x > b.x ? ( a.x > c.x ? a.x : c.x ) : ( b.x > c.x ? b.x : c.x ), | |
| maxTY = a.y > b.y ? ( a.y > c.y ? a.y : c.y ) : ( b.y > c.y ? b.y : c.y ); | |
| // z-order range for the current triangle bbox; | |
| var minZ = zOrder( minTX, minTY, minX, minY, invSize ), | |
| maxZ = zOrder( maxTX, maxTY, minX, minY, invSize ); | |
| // first look for points inside the triangle in increasing z-order | |
| var p = ear.nextZ; | |
| while ( p && p.z <= maxZ ) { | |
| if ( p !== ear.prev && p !== ear.next && | |
| pointInTriangle( a.x, a.y, b.x, b.y, c.x, c.y, p.x, p.y ) && | |
| area( p.prev, p, p.next ) >= 0 ) return false; | |
| p = p.nextZ; | |
| } | |
| // then look for points in decreasing z-order | |
| p = ear.prevZ; | |
| while ( p && p.z >= minZ ) { | |
| if ( p !== ear.prev && p !== ear.next && | |
| pointInTriangle( a.x, a.y, b.x, b.y, c.x, c.y, p.x, p.y ) && | |
| area( p.prev, p, p.next ) >= 0 ) return false; | |
| p = p.prevZ; | |
| } | |
| return true; | |
| } | |
| // go through all polygon nodes and cure small local self-intersections | |
| function cureLocalIntersections( start, triangles, dim ) { | |
| var p = start; | |
| do { | |
| var a = p.prev, b = p.next.next; | |
| if ( ! equals( a, b ) && intersects( a, p, p.next, b ) && locallyInside( a, b ) && locallyInside( b, a ) ) { | |
| triangles.push( a.i / dim ); | |
| triangles.push( p.i / dim ); | |
| triangles.push( b.i / dim ); | |
| // remove two nodes involved | |
| removeNode( p ); | |
| removeNode( p.next ); | |
| p = start = b; | |
| } | |
| p = p.next; | |
| } while ( p !== start ); | |
| return p; | |
| } | |
| // try splitting polygon into two and triangulate them independently | |
| function splitEarcut( start, triangles, dim, minX, minY, invSize ) { | |
| // look for a valid diagonal that divides the polygon into two | |
| var a = start; | |
| do { | |
| var b = a.next.next; | |
| while ( b !== a.prev ) { | |
| if ( a.i !== b.i && isValidDiagonal( a, b ) ) { | |
| // split the polygon in two by the diagonal | |
| var c = splitPolygon( a, b ); | |
| // filter colinear points around the cuts | |
| a = filterPoints( a, a.next ); | |
| c = filterPoints( c, c.next ); | |
| // run earcut on each half | |
| earcutLinked( a, triangles, dim, minX, minY, invSize ); | |
| earcutLinked( c, triangles, dim, minX, minY, invSize ); | |
| return; | |
| } | |
| b = b.next; | |
| } | |
| a = a.next; | |
| } while ( a !== start ); | |
| } | |
| // link every hole into the outer loop, producing a single-ring polygon without holes | |
| function eliminateHoles( data, holeIndices, outerNode, dim ) { | |
| var queue = [], i, len, start, end, list; | |
| for ( i = 0, len = holeIndices.length; i < len; i ++ ) { | |
| start = holeIndices[ i ] * dim; | |
| end = i < len - 1 ? holeIndices[ i + 1 ] * dim : data.length; | |
| list = linkedList( data, start, end, dim, false ); | |
| if ( list === list.next ) list.steiner = true; | |
| queue.push( getLeftmost( list ) ); | |
| } | |
| queue.sort( compareX ); | |
| // process holes from left to right | |
| for ( i = 0; i < queue.length; i ++ ) { | |
| eliminateHole( queue[ i ], outerNode ); | |
| outerNode = filterPoints( outerNode, outerNode.next ); | |
| } | |
| return outerNode; | |
| } | |
| function compareX( a, b ) { | |
| return a.x - b.x; | |
| } | |
| // find a bridge between vertices that connects hole with an outer ring and and link it | |
| function eliminateHole( hole, outerNode ) { | |
| outerNode = findHoleBridge( hole, outerNode ); | |
| if ( outerNode ) { | |
| var b = splitPolygon( outerNode, hole ); | |
| filterPoints( b, b.next ); | |
| } | |
| } | |
| // David Eberly's algorithm for finding a bridge between hole and outer polygon | |
| function findHoleBridge( hole, outerNode ) { | |
| var p = outerNode, | |
| hx = hole.x, | |
| hy = hole.y, | |
| qx = - Infinity, | |
| m; | |
| // find a segment intersected by a ray from the hole's leftmost point to the left; | |
| // segment's endpoint with lesser x will be potential connection point | |
| do { | |
| if ( hy <= p.y && hy >= p.next.y && p.next.y !== p.y ) { | |
| var x = p.x + ( hy - p.y ) * ( p.next.x - p.x ) / ( p.next.y - p.y ); | |
| if ( x <= hx && x > qx ) { | |
| qx = x; | |
| if ( x === hx ) { | |
| if ( hy === p.y ) return p; | |
| if ( hy === p.next.y ) return p.next; | |
| } | |
| m = p.x < p.next.x ? p : p.next; | |
| } | |
| } | |
| p = p.next; | |
| } while ( p !== outerNode ); | |
| if ( ! m ) return null; | |
| if ( hx === qx ) return m.prev; // hole touches outer segment; pick lower endpoint | |
| // look for points inside the triangle of hole point, segment intersection and endpoint; | |
| // if there are no points found, we have a valid connection; | |
| // otherwise choose the point of the minimum angle with the ray as connection point | |
| var stop = m, | |
| mx = m.x, | |
| my = m.y, | |
| tanMin = Infinity, | |
| tan; | |
| p = m.next; | |
| while ( p !== stop ) { | |
| if ( hx >= p.x && p.x >= mx && hx !== p.x && | |
| pointInTriangle( hy < my ? hx : qx, hy, mx, my, hy < my ? qx : hx, hy, p.x, p.y ) ) { | |
| tan = Math.abs( hy - p.y ) / ( hx - p.x ); // tangential | |
| if ( ( tan < tanMin || ( tan === tanMin && p.x > m.x ) ) && locallyInside( p, hole ) ) { | |
| m = p; | |
| tanMin = tan; | |
| } | |
| } | |
| p = p.next; | |
| } | |
| return m; | |
| } | |
| // interlink polygon nodes in z-order | |
| function indexCurve( start, minX, minY, invSize ) { | |
| var p = start; | |
| do { | |
| if ( p.z === null ) p.z = zOrder( p.x, p.y, minX, minY, invSize ); | |
| p.prevZ = p.prev; | |
| p.nextZ = p.next; | |
| p = p.next; | |
| } while ( p !== start ); | |
| p.prevZ.nextZ = null; | |
| p.prevZ = null; | |
| sortLinked( p ); | |
| } | |
| // Simon Tatham's linked list merge sort algorithm | |
| // http://www.chiark.greenend.org.uk/~sgtatham/algorithms/listsort.html | |
| function sortLinked( list ) { | |
| var i, p, q, e, tail, numMerges, pSize, qSize, inSize = 1; | |
| do { | |
| p = list; | |
| list = null; | |
| tail = null; | |
| numMerges = 0; | |
| while ( p ) { | |
| numMerges ++; | |
| q = p; | |
| pSize = 0; | |
| for ( i = 0; i < inSize; i ++ ) { | |
| pSize ++; | |
| q = q.nextZ; | |
| if ( ! q ) break; | |
| } | |
| qSize = inSize; | |
| while ( pSize > 0 || ( qSize > 0 && q ) ) { | |
| if ( pSize !== 0 && ( qSize === 0 || ! q || p.z <= q.z ) ) { | |
| e = p; | |
| p = p.nextZ; | |
| pSize --; | |
| } else { | |
| e = q; | |
| q = q.nextZ; | |
| qSize --; | |
| } | |
| if ( tail ) tail.nextZ = e; | |
| else list = e; | |
| e.prevZ = tail; | |
| tail = e; | |
| } | |
| p = q; | |
| } | |
| tail.nextZ = null; | |
| inSize *= 2; | |
| } while ( numMerges > 1 ); | |
| return list; | |
| } | |
| // z-order of a point given coords and inverse of the longer side of data bbox | |
| function zOrder( x, y, minX, minY, invSize ) { | |
| // coords are transformed into non-negative 15-bit integer range | |
| x = 32767 * ( x - minX ) * invSize; | |
| y = 32767 * ( y - minY ) * invSize; | |
| x = ( x | ( x << 8 ) ) & 0x00FF00FF; | |
| x = ( x | ( x << 4 ) ) & 0x0F0F0F0F; | |
| x = ( x | ( x << 2 ) ) & 0x33333333; | |
| x = ( x | ( x << 1 ) ) & 0x55555555; | |
| y = ( y | ( y << 8 ) ) & 0x00FF00FF; | |
| y = ( y | ( y << 4 ) ) & 0x0F0F0F0F; | |
| y = ( y | ( y << 2 ) ) & 0x33333333; | |
| y = ( y | ( y << 1 ) ) & 0x55555555; | |
| return x | ( y << 1 ); | |
| } | |
| // find the leftmost node of a polygon ring | |
| function getLeftmost( start ) { | |
| var p = start, leftmost = start; | |
| do { | |
| if ( p.x < leftmost.x ) leftmost = p; | |
| p = p.next; | |
| } while ( p !== start ); | |
| return leftmost; | |
| } | |
| // check if a point lies within a convex triangle | |
| function pointInTriangle( ax, ay, bx, by, cx, cy, px, py ) { | |
| return ( cx - px ) * ( ay - py ) - ( ax - px ) * ( cy - py ) >= 0 && | |
| ( ax - px ) * ( by - py ) - ( bx - px ) * ( ay - py ) >= 0 && | |
| ( bx - px ) * ( cy - py ) - ( cx - px ) * ( by - py ) >= 0; | |
| } | |
| // check if a diagonal between two polygon nodes is valid (lies in polygon interior) | |
| function isValidDiagonal( a, b ) { | |
| return a.next.i !== b.i && a.prev.i !== b.i && ! intersectsPolygon( a, b ) && | |
| locallyInside( a, b ) && locallyInside( b, a ) && middleInside( a, b ); | |
| } | |
| // signed area of a triangle | |
| function area( p, q, r ) { | |
| return ( q.y - p.y ) * ( r.x - q.x ) - ( q.x - p.x ) * ( r.y - q.y ); | |
| } | |
| // check if two points are equal | |
| function equals( p1, p2 ) { | |
| return p1.x === p2.x && p1.y === p2.y; | |
| } | |
| // check if two segments intersect | |
| function intersects( p1, q1, p2, q2 ) { | |
| if ( ( equals( p1, q1 ) && equals( p2, q2 ) ) || | |
| ( equals( p1, q2 ) && equals( p2, q1 ) ) ) return true; | |
| return area( p1, q1, p2 ) > 0 !== area( p1, q1, q2 ) > 0 && | |
| area( p2, q2, p1 ) > 0 !== area( p2, q2, q1 ) > 0; | |
| } | |
| // check if a polygon diagonal intersects any polygon segments | |
| function intersectsPolygon( a, b ) { | |
| var p = a; | |
| do { | |
| if ( p.i !== a.i && p.next.i !== a.i && p.i !== b.i && p.next.i !== b.i && | |
| intersects( p, p.next, a, b ) ) { | |
| return true; | |
| } | |
| p = p.next; | |
| } while ( p !== a ); | |
| return false; | |
| } | |
| // check if a polygon diagonal is locally inside the polygon | |
| function locallyInside( a, b ) { | |
| return area( a.prev, a, a.next ) < 0 ? | |
| area( a, b, a.next ) >= 0 && area( a, a.prev, b ) >= 0 : | |
| area( a, b, a.prev ) < 0 || area( a, a.next, b ) < 0; | |
| } | |
| // check if the middle point of a polygon diagonal is inside the polygon | |
| function middleInside( a, b ) { | |
| var p = a, | |
| inside = false, | |
| px = ( a.x + b.x ) / 2, | |
| py = ( a.y + b.y ) / 2; | |
| do { | |
| if ( ( ( p.y > py ) !== ( p.next.y > py ) ) && p.next.y !== p.y && | |
| ( px < ( p.next.x - p.x ) * ( py - p.y ) / ( p.next.y - p.y ) + p.x ) ) { | |
| inside = ! inside; | |
| } | |
| p = p.next; | |
| } while ( p !== a ); | |
| return inside; | |
| } | |
| // link two polygon vertices with a bridge; if the vertices belong to the same ring, it splits polygon into two; | |
| // if one belongs to the outer ring and another to a hole, it merges it into a single ring | |
| function splitPolygon( a, b ) { | |
| var a2 = new Node( a.i, a.x, a.y ), | |
| b2 = new Node( b.i, b.x, b.y ), | |
| an = a.next, | |
| bp = b.prev; | |
| a.next = b; | |
| b.prev = a; | |
| a2.next = an; | |
| an.prev = a2; | |
| b2.next = a2; | |
| a2.prev = b2; | |
| bp.next = b2; | |
| b2.prev = bp; | |
| return b2; | |
| } | |
| // create a node and optionally link it with previous one (in a circular doubly linked list) | |
| function insertNode( i, x, y, last ) { | |
| var p = new Node( i, x, y ); | |
| if ( ! last ) { | |
| p.prev = p; | |
| p.next = p; | |
| } else { | |
| p.next = last.next; | |
| p.prev = last; | |
| last.next.prev = p; | |
| last.next = p; | |
| } | |
| return p; | |
| } | |
| function removeNode( p ) { | |
| p.next.prev = p.prev; | |
| p.prev.next = p.next; | |
| if ( p.prevZ ) p.prevZ.nextZ = p.nextZ; | |
| if ( p.nextZ ) p.nextZ.prevZ = p.prevZ; | |
| } | |
| function Node( i, x, y ) { | |
| // vertice index in coordinates array | |
| this.i = i; | |
| // vertex coordinates | |
| this.x = x; | |
| this.y = y; | |
| // previous and next vertice nodes in a polygon ring | |
| this.prev = null; | |
| this.next = null; | |
| // z-order curve value | |
| this.z = null; | |
| // previous and next nodes in z-order | |
| this.prevZ = null; | |
| this.nextZ = null; | |
| // indicates whether this is a steiner point | |
| this.steiner = false; | |
| } | |
| function signedArea( data, start, end, dim ) { | |
| var sum = 0; | |
| for ( var i = start, j = end - dim; i < end; i += dim ) { | |
| sum += ( data[ j ] - data[ i ] ) * ( data[ i + 1 ] + data[ j + 1 ] ); | |
| j = i; | |
| } | |
| return sum; | |
| } | |
| /** | |
| * @author zz85 / http://www.lab4games.net/zz85/blog | |
| */ | |
| var ShapeUtils = { | |
| // calculate area of the contour polygon | |
| area: function ( contour ) { | |
| var n = contour.length; | |
| var a = 0.0; | |
| for ( var p = n - 1, q = 0; q < n; p = q ++ ) { | |
| a += contour[ p ].x * contour[ q ].y - contour[ q ].x * contour[ p ].y; | |
| } | |
| return a * 0.5; | |
| }, | |
| isClockWise: function ( pts ) { | |
| return ShapeUtils.area( pts ) < 0; | |
| }, | |
| triangulateShape: function ( contour, holes ) { | |
| var vertices = []; // flat array of vertices like [ x0,y0, x1,y1, x2,y2, ... ] | |
| var holeIndices = []; // array of hole indices | |
| var faces = []; // final array of vertex indices like [ [ a,b,d ], [ b,c,d ] ] | |
| removeDupEndPts( contour ); | |
| addContour( vertices, contour ); | |
| // | |
| var holeIndex = contour.length; | |
| holes.forEach( removeDupEndPts ); | |
| for ( var i = 0; i < holes.length; i ++ ) { | |
| holeIndices.push( holeIndex ); | |
| holeIndex += holes[ i ].length; | |
| addContour( vertices, holes[ i ] ); | |
| } | |
| // | |
| var triangles = Earcut.triangulate( vertices, holeIndices ); | |
| // | |
| for ( var i = 0; i < triangles.length; i += 3 ) { | |
| faces.push( triangles.slice( i, i + 3 ) ); | |
| } | |
| return faces; | |
| } | |
| }; | |
| function removeDupEndPts( points ) { | |
| var l = points.length; | |
| if ( l > 2 && points[ l - 1 ].equals( points[ 0 ] ) ) { | |
| points.pop(); | |
| } | |
| } | |
| function addContour( vertices, contour ) { | |
| for ( var i = 0; i < contour.length; i ++ ) { | |
| vertices.push( contour[ i ].x ); | |
| vertices.push( contour[ i ].y ); | |
| } | |
| } | |
| /** | |
| * @author zz85 / http://www.lab4games.net/zz85/blog | |
| * | |
| * Creates extruded geometry from a path shape. | |
| * | |
| * parameters = { | |
| * | |
| * curveSegments: <int>, // number of points on the curves | |
| * steps: <int>, // number of points for z-side extrusions / used for subdividing segments of extrude spline too | |
| * amount: <int>, // Depth to extrude the shape | |
| * | |
| * bevelEnabled: <bool>, // turn on bevel | |
| * bevelThickness: <float>, // how deep into the original shape bevel goes | |
| * bevelSize: <float>, // how far from shape outline is bevel | |
| * bevelSegments: <int>, // number of bevel layers | |
| * | |
| * extrudePath: <THREE.Curve> // curve to extrude shape along | |
| * frames: <Object> // containing arrays of tangents, normals, binormals | |
| * | |
| * UVGenerator: <Object> // object that provides UV generator functions | |
| * | |
| * } | |
| */ | |
| // ExtrudeGeometry | |
| function ExtrudeGeometry( shapes, options ) { | |
| Geometry.call( this ); | |
| this.type = 'ExtrudeGeometry'; | |
| this.parameters = { | |
| shapes: shapes, | |
| options: options | |
| }; | |
| this.fromBufferGeometry( new ExtrudeBufferGeometry( shapes, options ) ); | |
| this.mergeVertices(); | |
| } | |
| ExtrudeGeometry.prototype = Object.create( Geometry.prototype ); | |
| ExtrudeGeometry.prototype.constructor = ExtrudeGeometry; | |
| // ExtrudeBufferGeometry | |
| function ExtrudeBufferGeometry( shapes, options ) { | |
| if ( typeof ( shapes ) === "undefined" ) { | |
| return; | |
| } | |
| BufferGeometry.call( this ); | |
| this.type = 'ExtrudeBufferGeometry'; | |
| shapes = Array.isArray( shapes ) ? shapes : [ shapes ]; | |
| this.addShapeList( shapes, options ); | |
| this.computeVertexNormals(); | |
| // can't really use automatic vertex normals | |
| // as then front and back sides get smoothed too | |
| // should do separate smoothing just for sides | |
| //this.computeVertexNormals(); | |
| //console.log( "took", ( Date.now() - startTime ) ); | |
| } | |
| ExtrudeBufferGeometry.prototype = Object.create( BufferGeometry.prototype ); | |
| ExtrudeBufferGeometry.prototype.constructor = ExtrudeBufferGeometry; | |
| ExtrudeBufferGeometry.prototype.getArrays = function () { | |
| var positionAttribute = this.getAttribute( "position" ); | |
| var verticesArray = positionAttribute ? Array.prototype.slice.call( positionAttribute.array ) : []; | |
| var uvAttribute = this.getAttribute( "uv" ); | |
| var uvArray = uvAttribute ? Array.prototype.slice.call( uvAttribute.array ) : []; | |
| var IndexAttribute = this.index; | |
| var indicesArray = IndexAttribute ? Array.prototype.slice.call( IndexAttribute.array ) : []; | |
| return { | |
| position: verticesArray, | |
| uv: uvArray, | |
| index: indicesArray | |
| }; | |
| }; | |
| ExtrudeBufferGeometry.prototype.addShapeList = function ( shapes, options ) { | |
| var sl = shapes.length; | |
| options.arrays = this.getArrays(); | |
| for ( var s = 0; s < sl; s ++ ) { | |
| var shape = shapes[ s ]; | |
| this.addShape( shape, options ); | |
| } | |
| this.setIndex( options.arrays.index ); | |
| this.addAttribute( 'position', new Float32BufferAttribute( options.arrays.position, 3 ) ); | |
| this.addAttribute( 'uv', new Float32BufferAttribute( options.arrays.uv, 2 ) ); | |
| }; | |
| ExtrudeBufferGeometry.prototype.addShape = function ( shape, options ) { | |
| var arrays = options.arrays ? options.arrays : this.getArrays(); | |
| var verticesArray = arrays.position; | |
| var indicesArray = arrays.index; | |
| var uvArray = arrays.uv; | |
| var placeholder = []; | |
| var amount = options.amount !== undefined ? options.amount : 100; | |
| var bevelThickness = options.bevelThickness !== undefined ? options.bevelThickness : 6; // 10 | |
| var bevelSize = options.bevelSize !== undefined ? options.bevelSize : bevelThickness - 2; // 8 | |
| var bevelSegments = options.bevelSegments !== undefined ? options.bevelSegments : 3; | |
| var bevelEnabled = options.bevelEnabled !== undefined ? options.bevelEnabled : true; // false | |
| var curveSegments = options.curveSegments !== undefined ? options.curveSegments : 12; | |
| var steps = options.steps !== undefined ? options.steps : 1; | |
| var extrudePath = options.extrudePath; | |
| var extrudePts, extrudeByPath = false; | |
| // Use default WorldUVGenerator if no UV generators are specified. | |
| var uvgen = options.UVGenerator !== undefined ? options.UVGenerator : ExtrudeGeometry.WorldUVGenerator; | |
| var splineTube, binormal, normal, position2; | |
| if ( extrudePath ) { | |
| extrudePts = extrudePath.getSpacedPoints( steps ); | |
| extrudeByPath = true; | |
| bevelEnabled = false; // bevels not supported for path extrusion | |
| // SETUP TNB variables | |
| // TODO1 - have a .isClosed in spline? | |
| splineTube = options.frames !== undefined ? options.frames : extrudePath.computeFrenetFrames( steps, false ); | |
| // console.log(splineTube, 'splineTube', splineTube.normals.length, 'steps', steps, 'extrudePts', extrudePts.length); | |
| binormal = new Vector3(); | |
| normal = new Vector3(); | |
| position2 = new Vector3(); | |
| } | |
| // Safeguards if bevels are not enabled | |
| if ( ! bevelEnabled ) { | |
| bevelSegments = 0; | |
| bevelThickness = 0; | |
| bevelSize = 0; | |
| } | |
| // Variables initialization | |
| var ahole, h, hl; // looping of holes | |
| var scope = this; | |
| var shapePoints = shape.extractPoints( curveSegments ); | |
| var vertices = shapePoints.shape; | |
| var holes = shapePoints.holes; | |
| var reverse = ! ShapeUtils.isClockWise( vertices ); | |
| if ( reverse ) { | |
| vertices = vertices.reverse(); | |
| // Maybe we should also check if holes are in the opposite direction, just to be safe ... | |
| for ( h = 0, hl = holes.length; h < hl; h ++ ) { | |
| ahole = holes[ h ]; | |
| if ( ShapeUtils.isClockWise( ahole ) ) { | |
| holes[ h ] = ahole.reverse(); | |
| } | |
| } | |
| } | |
| var faces = ShapeUtils.triangulateShape( vertices, holes ); | |
| /* Vertices */ | |
| var contour = vertices; // vertices has all points but contour has only points of circumference | |
| for ( h = 0, hl = holes.length; h < hl; h ++ ) { | |
| ahole = holes[ h ]; | |
| vertices = vertices.concat( ahole ); | |
| } | |
| function scalePt2( pt, vec, size ) { | |
| if ( ! vec ) console.error( "THREE.ExtrudeGeometry: vec does not exist" ); | |
| return vec.clone().multiplyScalar( size ).add( pt ); | |
| } | |
| var b, bs, t, z, | |
| vert, vlen = vertices.length, | |
| face, flen = faces.length; | |
| // Find directions for point movement | |
| function getBevelVec( inPt, inPrev, inNext ) { | |
| // computes for inPt the corresponding point inPt' on a new contour | |
| // shifted by 1 unit (length of normalized vector) to the left | |
| // if we walk along contour clockwise, this new contour is outside the old one | |
| // | |
| // inPt' is the intersection of the two lines parallel to the two | |
| // adjacent edges of inPt at a distance of 1 unit on the left side. | |
| var v_trans_x, v_trans_y, shrink_by; // resulting translation vector for inPt | |
| // good reading for geometry algorithms (here: line-line intersection) | |
| // http://geomalgorithms.com/a05-_intersect-1.html | |
| var v_prev_x = inPt.x - inPrev.x, | |
| v_prev_y = inPt.y - inPrev.y; | |
| var v_next_x = inNext.x - inPt.x, | |
| v_next_y = inNext.y - inPt.y; | |
| var v_prev_lensq = ( v_prev_x * v_prev_x + v_prev_y * v_prev_y ); | |
| // check for collinear edges | |
| var collinear0 = ( v_prev_x * v_next_y - v_prev_y * v_next_x ); | |
| if ( Math.abs( collinear0 ) > Number.EPSILON ) { | |
| // not collinear | |
| // length of vectors for normalizing | |
| var v_prev_len = Math.sqrt( v_prev_lensq ); | |
| var v_next_len = Math.sqrt( v_next_x * v_next_x + v_next_y * v_next_y ); | |
| // shift adjacent points by unit vectors to the left | |
| var ptPrevShift_x = ( inPrev.x - v_prev_y / v_prev_len ); | |
| var ptPrevShift_y = ( inPrev.y + v_prev_x / v_prev_len ); | |
| var ptNextShift_x = ( inNext.x - v_next_y / v_next_len ); | |
| var ptNextShift_y = ( inNext.y + v_next_x / v_next_len ); | |
| // scaling factor for v_prev to intersection point | |
| var sf = ( ( ptNextShift_x - ptPrevShift_x ) * v_next_y - | |
| ( ptNextShift_y - ptPrevShift_y ) * v_next_x ) / | |
| ( v_prev_x * v_next_y - v_prev_y * v_next_x ); | |
| // vector from inPt to intersection point | |
| v_trans_x = ( ptPrevShift_x + v_prev_x * sf - inPt.x ); | |
| v_trans_y = ( ptPrevShift_y + v_prev_y * sf - inPt.y ); | |
| // Don't normalize!, otherwise sharp corners become ugly | |
| // but prevent crazy spikes | |
| var v_trans_lensq = ( v_trans_x * v_trans_x + v_trans_y * v_trans_y ); | |
| if ( v_trans_lensq <= 2 ) { | |
| return new Vector2( v_trans_x, v_trans_y ); | |
| } else { | |
| shrink_by = Math.sqrt( v_trans_lensq / 2 ); | |
| } | |
| } else { | |
| // handle special case of collinear edges | |
| var direction_eq = false; // assumes: opposite | |
| if ( v_prev_x > Number.EPSILON ) { | |
| if ( v_next_x > Number.EPSILON ) { | |
| direction_eq = true; | |
| } | |
| } else { | |
| if ( v_prev_x < - Number.EPSILON ) { | |
| if ( v_next_x < - Number.EPSILON ) { | |
| direction_eq = true; | |
| } | |
| } else { | |
| if ( Math.sign( v_prev_y ) === Math.sign( v_next_y ) ) { | |
| direction_eq = true; | |
| } | |
| } | |
| } | |
| if ( direction_eq ) { | |
| // console.log("Warning: lines are a straight sequence"); | |
| v_trans_x = - v_prev_y; | |
| v_trans_y = v_prev_x; | |
| shrink_by = Math.sqrt( v_prev_lensq ); | |
| } else { | |
| // console.log("Warning: lines are a straight spike"); | |
| v_trans_x = v_prev_x; | |
| v_trans_y = v_prev_y; | |
| shrink_by = Math.sqrt( v_prev_lensq / 2 ); | |
| } | |
| } | |
| return new Vector2( v_trans_x / shrink_by, v_trans_y / shrink_by ); | |
| } | |
| var contourMovements = []; | |
| for ( var i = 0, il = contour.length, j = il - 1, k = i + 1; i < il; i ++, j ++, k ++ ) { | |
| if ( j === il ) j = 0; | |
| if ( k === il ) k = 0; | |
| // (j)---(i)---(k) | |
| // console.log('i,j,k', i, j , k) | |
| contourMovements[ i ] = getBevelVec( contour[ i ], contour[ j ], contour[ k ] ); | |
| } | |
| var holesMovements = [], | |
| oneHoleMovements, verticesMovements = contourMovements.concat(); | |
| for ( h = 0, hl = holes.length; h < hl; h ++ ) { | |
| ahole = holes[ h ]; | |
| oneHoleMovements = []; | |
| for ( i = 0, il = ahole.length, j = il - 1, k = i + 1; i < il; i ++, j ++, k ++ ) { | |
| if ( j === il ) j = 0; | |
| if ( k === il ) k = 0; | |
| // (j)---(i)---(k) | |
| oneHoleMovements[ i ] = getBevelVec( ahole[ i ], ahole[ j ], ahole[ k ] ); | |
| } | |
| holesMovements.push( oneHoleMovements ); | |
| verticesMovements = verticesMovements.concat( oneHoleMovements ); | |
| } | |
| // Loop bevelSegments, 1 for the front, 1 for the back | |
| for ( b = 0; b < bevelSegments; b ++ ) { | |
| //for ( b = bevelSegments; b > 0; b -- ) { | |
| t = b / bevelSegments; | |
| z = bevelThickness * Math.cos( t * Math.PI / 2 ); | |
| bs = bevelSize * Math.sin( t * Math.PI / 2 ); | |
| // contract shape | |
| for ( i = 0, il = contour.length; i < il; i ++ ) { | |
| vert = scalePt2( contour[ i ], contourMovements[ i ], bs ); | |
| v( vert.x, vert.y, - z ); | |
| } | |
| // expand holes | |
| for ( h = 0, hl = holes.length; h < hl; h ++ ) { | |
| ahole = holes[ h ]; | |
| oneHoleMovements = holesMovements[ h ]; | |
| for ( i = 0, il = ahole.length; i < il; i ++ ) { | |
| vert = scalePt2( ahole[ i ], oneHoleMovements[ i ], bs ); | |
| v( vert.x, vert.y, - z ); | |
| } | |
| } | |
| } | |
| bs = bevelSize; | |
| // Back facing vertices | |
| for ( i = 0; i < vlen; i ++ ) { | |
| vert = bevelEnabled ? scalePt2( vertices[ i ], verticesMovements[ i ], bs ) : vertices[ i ]; | |
| if ( ! extrudeByPath ) { | |
| v( vert.x, vert.y, 0 ); | |
| } else { | |
| // v( vert.x, vert.y + extrudePts[ 0 ].y, extrudePts[ 0 ].x ); | |
| normal.copy( splineTube.normals[ 0 ] ).multiplyScalar( vert.x ); | |
| binormal.copy( splineTube.binormals[ 0 ] ).multiplyScalar( vert.y ); | |
| position2.copy( extrudePts[ 0 ] ).add( normal ).add( binormal ); | |
| v( position2.x, position2.y, position2.z ); | |
| } | |
| } | |
| // Add stepped vertices... | |
| // Including front facing vertices | |
| var s; | |
| for ( s = 1; s <= steps; s ++ ) { | |
| for ( i = 0; i < vlen; i ++ ) { | |
| vert = bevelEnabled ? scalePt2( vertices[ i ], verticesMovements[ i ], bs ) : vertices[ i ]; | |
| if ( ! extrudeByPath ) { | |
| v( vert.x, vert.y, amount / steps * s ); | |
| } else { | |
| // v( vert.x, vert.y + extrudePts[ s - 1 ].y, extrudePts[ s - 1 ].x ); | |
| normal.copy( splineTube.normals[ s ] ).multiplyScalar( vert.x ); | |
| binormal.copy( splineTube.binormals[ s ] ).multiplyScalar( vert.y ); | |
| position2.copy( extrudePts[ s ] ).add( normal ).add( binormal ); | |
| v( position2.x, position2.y, position2.z ); | |
| } | |
| } | |
| } | |
| // Add bevel segments planes | |
| //for ( b = 1; b <= bevelSegments; b ++ ) { | |
| for ( b = bevelSegments - 1; b >= 0; b -- ) { | |
| t = b / bevelSegments; | |
| z = bevelThickness * Math.cos( t * Math.PI / 2 ); | |
| bs = bevelSize * Math.sin( t * Math.PI / 2 ); | |
| // contract shape | |
| for ( i = 0, il = contour.length; i < il; i ++ ) { | |
| vert = scalePt2( contour[ i ], contourMovements[ i ], bs ); | |
| v( vert.x, vert.y, amount + z ); | |
| } | |
| // expand holes | |
| for ( h = 0, hl = holes.length; h < hl; h ++ ) { | |
| ahole = holes[ h ]; | |
| oneHoleMovements = holesMovements[ h ]; | |
| for ( i = 0, il = ahole.length; i < il; i ++ ) { | |
| vert = scalePt2( ahole[ i ], oneHoleMovements[ i ], bs ); | |
| if ( ! extrudeByPath ) { | |
| v( vert.x, vert.y, amount + z ); | |
| } else { | |
| v( vert.x, vert.y + extrudePts[ steps - 1 ].y, extrudePts[ steps - 1 ].x + z ); | |
| } | |
| } | |
| } | |
| } | |
| /* Faces */ | |
| // Top and bottom faces | |
| buildLidFaces(); | |
| // Sides faces | |
| buildSideFaces(); | |
| ///// Internal functions | |
| function buildLidFaces() { | |
| var start = verticesArray.length / 3; | |
| if ( bevelEnabled ) { | |
| var layer = 0; // steps + 1 | |
| var offset = vlen * layer; | |
| // Bottom faces | |
| for ( i = 0; i < flen; i ++ ) { | |
| face = faces[ i ]; | |
| f3( face[ 2 ] + offset, face[ 1 ] + offset, face[ 0 ] + offset ); | |
| } | |
| layer = steps + bevelSegments * 2; | |
| offset = vlen * layer; | |
| // Top faces | |
| for ( i = 0; i < flen; i ++ ) { | |
| face = faces[ i ]; | |
| f3( face[ 0 ] + offset, face[ 1 ] + offset, face[ 2 ] + offset ); | |
| } | |
| } else { | |
| // Bottom faces | |
| for ( i = 0; i < flen; i ++ ) { | |
| face = faces[ i ]; | |
| f3( face[ 2 ], face[ 1 ], face[ 0 ] ); | |
| } | |
| // Top faces | |
| for ( i = 0; i < flen; i ++ ) { | |
| face = faces[ i ]; | |
| f3( face[ 0 ] + vlen * steps, face[ 1 ] + vlen * steps, face[ 2 ] + vlen * steps ); | |
| } | |
| } | |
| scope.addGroup( start, verticesArray.length / 3 - start, options.material !== undefined ? options.material : 0 ); | |
| } | |
| // Create faces for the z-sides of the shape | |
| function buildSideFaces() { | |
| var start = verticesArray.length / 3; | |
| var layeroffset = 0; | |
| sidewalls( contour, layeroffset ); | |
| layeroffset += contour.length; | |
| for ( h = 0, hl = holes.length; h < hl; h ++ ) { | |
| ahole = holes[ h ]; | |
| sidewalls( ahole, layeroffset ); | |
| //, true | |
| layeroffset += ahole.length; | |
| } | |
| scope.addGroup( start, verticesArray.length / 3 - start, options.extrudeMaterial !== undefined ? options.extrudeMaterial : 1 ); | |
| } | |
| function sidewalls( contour, layeroffset ) { | |
| var j, k; | |
| i = contour.length; | |
| while ( -- i >= 0 ) { | |
| j = i; | |
| k = i - 1; | |
| if ( k < 0 ) k = contour.length - 1; | |
| //console.log('b', i,j, i-1, k,vertices.length); | |
| var s = 0, | |
| sl = steps + bevelSegments * 2; | |
| for ( s = 0; s < sl; s ++ ) { | |
| var slen1 = vlen * s; | |
| var slen2 = vlen * ( s + 1 ); | |
| var a = layeroffset + j + slen1, | |
| b = layeroffset + k + slen1, | |
| c = layeroffset + k + slen2, | |
| d = layeroffset + j + slen2; | |
| f4( a, b, c, d ); | |
| } | |
| } | |
| } | |
| function v( x, y, z ) { | |
| placeholder.push( x ); | |
| placeholder.push( y ); | |
| placeholder.push( z ); | |
| } | |
| function f3( a, b, c ) { | |
| addVertex( a ); | |
| addVertex( b ); | |
| addVertex( c ); | |
| var nextIndex = verticesArray.length / 3; | |
| var uvs = uvgen.generateTopUV( scope, verticesArray, nextIndex - 3, nextIndex - 2, nextIndex - 1 ); | |
| addUV( uvs[ 0 ] ); | |
| addUV( uvs[ 1 ] ); | |
| addUV( uvs[ 2 ] ); | |
| } | |
| function f4( a, b, c, d ) { | |
| addVertex( a ); | |
| addVertex( b ); | |
| addVertex( d ); | |
| addVertex( b ); | |
| addVertex( c ); | |
| addVertex( d ); | |
| var nextIndex = verticesArray.length / 3; | |
| var uvs = uvgen.generateSideWallUV( scope, verticesArray, nextIndex - 6, nextIndex - 3, nextIndex - 2, nextIndex - 1 ); | |
| addUV( uvs[ 0 ] ); | |
| addUV( uvs[ 1 ] ); | |
| addUV( uvs[ 3 ] ); | |
| addUV( uvs[ 1 ] ); | |
| addUV( uvs[ 2 ] ); | |
| addUV( uvs[ 3 ] ); | |
| } | |
| function addVertex( index ) { | |
| indicesArray.push( verticesArray.length / 3 ); | |
| verticesArray.push( placeholder[ index * 3 + 0 ] ); | |
| verticesArray.push( placeholder[ index * 3 + 1 ] ); | |
| verticesArray.push( placeholder[ index * 3 + 2 ] ); | |
| } | |
| function addUV( vector2 ) { | |
| uvArray.push( vector2.x ); | |
| uvArray.push( vector2.y ); | |
| } | |
| if ( ! options.arrays ) { | |
| this.setIndex( indicesArray ); | |
| this.addAttribute( 'position', new Float32BufferAttribute( verticesArray, 3 ) ); | |
| this.addAttribute( 'uv', new Float32BufferAttribute( uvArray, 2 ) ); | |
| } | |
| }; | |
| ExtrudeGeometry.WorldUVGenerator = { | |
| generateTopUV: function ( geometry, vertices, indexA, indexB, indexC ) { | |
| var a_x = vertices[ indexA * 3 ]; | |
| var a_y = vertices[ indexA * 3 + 1 ]; | |
| var b_x = vertices[ indexB * 3 ]; | |
| var b_y = vertices[ indexB * 3 + 1 ]; | |
| var c_x = vertices[ indexC * 3 ]; | |
| var c_y = vertices[ indexC * 3 + 1 ]; | |
| return [ | |
| new Vector2( a_x, a_y ), | |
| new Vector2( b_x, b_y ), | |
| new Vector2( c_x, c_y ) | |
| ]; | |
| }, | |
| generateSideWallUV: function ( geometry, vertices, indexA, indexB, indexC, indexD ) { | |
| var a_x = vertices[ indexA * 3 ]; | |
| var a_y = vertices[ indexA * 3 + 1 ]; | |
| var a_z = vertices[ indexA * 3 + 2 ]; | |
| var b_x = vertices[ indexB * 3 ]; | |
| var b_y = vertices[ indexB * 3 + 1 ]; | |
| var b_z = vertices[ indexB * 3 + 2 ]; | |
| var c_x = vertices[ indexC * 3 ]; | |
| var c_y = vertices[ indexC * 3 + 1 ]; | |
| var c_z = vertices[ indexC * 3 + 2 ]; | |
| var d_x = vertices[ indexD * 3 ]; | |
| var d_y = vertices[ indexD * 3 + 1 ]; | |
| var d_z = vertices[ indexD * 3 + 2 ]; | |
| if ( Math.abs( a_y - b_y ) < 0.01 ) { | |
| return [ | |
| new Vector2( a_x, 1 - a_z ), | |
| new Vector2( b_x, 1 - b_z ), | |
| new Vector2( c_x, 1 - c_z ), | |
| new Vector2( d_x, 1 - d_z ) | |
| ]; | |
| } else { | |
| return [ | |
| new Vector2( a_y, 1 - a_z ), | |
| new Vector2( b_y, 1 - b_z ), | |
| new Vector2( c_y, 1 - c_z ), | |
| new Vector2( d_y, 1 - d_z ) | |
| ]; | |
| } | |
| } | |
| }; | |
| /** | |
| * @author zz85 / http://www.lab4games.net/zz85/blog | |
| * @author alteredq / http://alteredqualia.com/ | |
| * | |
| * Text = 3D Text | |
| * | |
| * parameters = { | |
| * font: <THREE.Font>, // font | |
| * | |
| * size: <float>, // size of the text | |
| * height: <float>, // thickness to extrude text | |
| * curveSegments: <int>, // number of points on the curves | |
| * | |
| * bevelEnabled: <bool>, // turn on bevel | |
| * bevelThickness: <float>, // how deep into text bevel goes | |
| * bevelSize: <float> // how far from text outline is bevel | |
| * } | |
| */ | |
| // TextGeometry | |
| function TextGeometry( text, parameters ) { | |
| Geometry.call( this ); | |
| this.type = 'TextGeometry'; | |
| this.parameters = { | |
| text: text, | |
| parameters: parameters | |
| }; | |
| this.fromBufferGeometry( new TextBufferGeometry( text, parameters ) ); | |
| this.mergeVertices(); | |
| } | |
| TextGeometry.prototype = Object.create( Geometry.prototype ); | |
| TextGeometry.prototype.constructor = TextGeometry; | |
| // TextBufferGeometry | |
| function TextBufferGeometry( text, parameters ) { | |
| parameters = parameters || {}; | |
| var font = parameters.font; | |
| if ( ! ( font && font.isFont ) ) { | |
| console.error( 'THREE.TextGeometry: font parameter is not an instance of THREE.Font.' ); | |
| return new Geometry(); | |
| } | |
| var shapes = font.generateShapes( text, parameters.size, parameters.curveSegments ); | |
| // translate parameters to ExtrudeGeometry API | |
| parameters.amount = parameters.height !== undefined ? parameters.height : 50; | |
| // defaults | |
| if ( parameters.bevelThickness === undefined ) parameters.bevelThickness = 10; | |
| if ( parameters.bevelSize === undefined ) parameters.bevelSize = 8; | |
| if ( parameters.bevelEnabled === undefined ) parameters.bevelEnabled = false; | |
| ExtrudeBufferGeometry.call( this, shapes, parameters ); | |
| this.type = 'TextBufferGeometry'; | |
| } | |
| TextBufferGeometry.prototype = Object.create( ExtrudeBufferGeometry.prototype ); | |
| TextBufferGeometry.prototype.constructor = TextBufferGeometry; | |
| /** | |
| * @author mrdoob / http://mrdoob.com/ | |
| * @author benaadams / https://twitter.com/ben_a_adams | |
| * @author Mugen87 / https://github.com/Mugen87 | |
| */ | |
| // SphereGeometry | |
| function SphereGeometry( radius, widthSegments, heightSegments, phiStart, phiLength, thetaStart, thetaLength ) { | |
| Geometry.call( this ); | |
| this.type = 'SphereGeometry'; | |
| this.parameters = { | |
| radius: radius, | |
| widthSegments: widthSegments, | |
| heightSegments: heightSegments, | |
| phiStart: phiStart, | |
| phiLength: phiLength, | |
| thetaStart: thetaStart, | |
| thetaLength: thetaLength | |
| }; | |
| this.fromBufferGeometry( new SphereBufferGeometry( radius, widthSegments, heightSegments, phiStart, phiLength, thetaStart, thetaLength ) ); | |
| this.mergeVertices(); | |
| } | |
| SphereGeometry.prototype = Object.create( Geometry.prototype ); | |
| SphereGeometry.prototype.constructor = SphereGeometry; | |
| // SphereBufferGeometry | |
| function SphereBufferGeometry( radius, widthSegments, heightSegments, phiStart, phiLength, thetaStart, thetaLength ) { | |
| BufferGeometry.call( this ); | |
| this.type = 'SphereBufferGeometry'; | |
| this.parameters = { | |
| radius: radius, | |
| widthSegments: widthSegments, | |
| heightSegments: heightSegments, | |
| phiStart: phiStart, | |
| phiLength: phiLength, | |
| thetaStart: thetaStart, | |
| thetaLength: thetaLength | |
| }; | |
| radius = radius || 1; | |
| widthSegments = Math.max( 3, Math.floor( widthSegments ) || 8 ); | |
| heightSegments = Math.max( 2, Math.floor( heightSegments ) || 6 ); | |
| phiStart = phiStart !== undefined ? phiStart : 0; | |
| phiLength = phiLength !== undefined ? phiLength : Math.PI * 2; | |
| thetaStart = thetaStart !== undefined ? thetaStart : 0; | |
| thetaLength = thetaLength !== undefined ? thetaLength : Math.PI; | |
| var thetaEnd = thetaStart + thetaLength; | |
| var ix, iy; | |
| var index = 0; | |
| var grid = []; | |
| var vertex = new Vector3(); | |
| var normal = new Vector3(); | |
| // buffers | |
| var indices = []; | |
| var vertices = []; | |
| var normals = []; | |
| var uvs = []; | |
| // generate vertices, normals and uvs | |
| for ( iy = 0; iy <= heightSegments; iy ++ ) { | |
| var verticesRow = []; | |
| var v = iy / heightSegments; | |
| for ( ix = 0; ix <= widthSegments; ix ++ ) { | |
| var u = ix / widthSegments; | |
| // vertex | |
| vertex.x = - radius * Math.cos( phiStart + u * phiLength ) * Math.sin( thetaStart + v * thetaLength ); | |
| vertex.y = radius * Math.cos( thetaStart + v * thetaLength ); | |
| vertex.z = radius * Math.sin( phiStart + u * phiLength ) * Math.sin( thetaStart + v * thetaLength ); | |
| vertices.push( vertex.x, vertex.y, vertex.z ); | |
| // normal | |
| normal.set( vertex.x, vertex.y, vertex.z ).normalize(); | |
| normals.push( normal.x, normal.y, normal.z ); | |
| // uv | |
| uvs.push( u, 1 - v ); | |
| verticesRow.push( index ++ ); | |
| } | |
| grid.push( verticesRow ); | |
| } | |
| // indices | |
| for ( iy = 0; iy < heightSegments; iy ++ ) { | |
| for ( ix = 0; ix < widthSegments; ix ++ ) { | |
| var a = grid[ iy ][ ix + 1 ]; | |
| var b = grid[ iy ][ ix ]; | |
| var c = grid[ iy + 1 ][ ix ]; | |
| var d = grid[ iy + 1 ][ ix + 1 ]; | |
| if ( iy !== 0 || thetaStart > 0 ) indices.push( a, b, d ); | |
| if ( iy !== heightSegments - 1 || thetaEnd < Math.PI ) indices.push( b, c, d ); | |
| } | |
| } | |
| // build geometry | |
| this.setIndex( indices ); | |
| this.addAttribute( 'position', new Float32BufferAttribute( vertices, 3 ) ); | |
| this.addAttribute( 'normal', new Float32BufferAttribute( normals, 3 ) ); | |
| this.addAttribute( 'uv', new Float32BufferAttribute( uvs, 2 ) ); | |
| } | |
| SphereBufferGeometry.prototype = Object.create( BufferGeometry.prototype ); | |
| SphereBufferGeometry.prototype.constructor = SphereBufferGeometry; | |
| /** | |
| * @author Kaleb Murphy | |
| * @author Mugen87 / https://github.com/Mugen87 | |
| */ | |
| // RingGeometry | |
| function RingGeometry( innerRadius, outerRadius, thetaSegments, phiSegments, thetaStart, thetaLength ) { | |
| Geometry.call( this ); | |
| this.type = 'RingGeometry'; | |
| this.parameters = { | |
| innerRadius: innerRadius, | |
| outerRadius: outerRadius, | |
| thetaSegments: thetaSegments, | |
| phiSegments: phiSegments, | |
| thetaStart: thetaStart, | |
| thetaLength: thetaLength | |
| }; | |
| this.fromBufferGeometry( new RingBufferGeometry( innerRadius, outerRadius, thetaSegments, phiSegments, thetaStart, thetaLength ) ); | |
| this.mergeVertices(); | |
| } | |
| RingGeometry.prototype = Object.create( Geometry.prototype ); | |
| RingGeometry.prototype.constructor = RingGeometry; | |
| // RingBufferGeometry | |
| function RingBufferGeometry( innerRadius, outerRadius, thetaSegments, phiSegments, thetaStart, thetaLength ) { | |
| BufferGeometry.call( this ); | |
| this.type = 'RingBufferGeometry'; | |
| this.parameters = { | |
| innerRadius: innerRadius, | |
| outerRadius: outerRadius, | |
| thetaSegments: thetaSegments, | |
| phiSegments: phiSegments, | |
| thetaStart: thetaStart, | |
| thetaLength: thetaLength | |
| }; | |
| innerRadius = innerRadius || 0.5; | |
| outerRadius = outerRadius || 1; | |
| thetaStart = thetaStart !== undefined ? thetaStart : 0; | |
| thetaLength = thetaLength !== undefined ? thetaLength : Math.PI * 2; | |
| thetaSegments = thetaSegments !== undefined ? Math.max( 3, thetaSegments ) : 8; | |
| phiSegments = phiSegments !== undefined ? Math.max( 1, phiSegments ) : 1; | |
| // buffers | |
| var indices = []; | |
| var vertices = []; | |
| var normals = []; | |
| var uvs = []; | |
| // some helper variables | |
| var segment; | |
| var radius = innerRadius; | |
| var radiusStep = ( ( outerRadius - innerRadius ) / phiSegments ); | |
| var vertex = new Vector3(); | |
| var uv = new Vector2(); | |
| var j, i; | |
| // generate vertices, normals and uvs | |
| for ( j = 0; j <= phiSegments; j ++ ) { | |
| for ( i = 0; i <= thetaSegments; i ++ ) { | |
| // values are generate from the inside of the ring to the outside | |
| segment = thetaStart + i / thetaSegments * thetaLength; | |
| // vertex | |
| vertex.x = radius * Math.cos( segment ); | |
| vertex.y = radius * Math.sin( segment ); | |
| vertices.push( vertex.x, vertex.y, vertex.z ); | |
| // normal | |
| normals.push( 0, 0, 1 ); | |
| // uv | |
| uv.x = ( vertex.x / outerRadius + 1 ) / 2; | |
| uv.y = ( vertex.y / outerRadius + 1 ) / 2; | |
| uvs.push( uv.x, uv.y ); | |
| } | |
| // increase the radius for next row of vertices | |
| radius += radiusStep; | |
| } | |
| // indices | |
| for ( j = 0; j < phiSegments; j ++ ) { | |
| var thetaSegmentLevel = j * ( thetaSegments + 1 ); | |
| for ( i = 0; i < thetaSegments; i ++ ) { | |
| segment = i + thetaSegmentLevel; | |
| var a = segment; | |
| var b = segment + thetaSegments + 1; | |
| var c = segment + thetaSegments + 2; | |
| var d = segment + 1; | |
| // faces | |
| indices.push( a, b, d ); | |
| indices.push( b, c, d ); | |
| } | |
| } | |
| // build geometry | |
| this.setIndex( indices ); | |
| this.addAttribute( 'position', new Float32BufferAttribute( vertices, 3 ) ); | |
| this.addAttribute( 'normal', new Float32BufferAttribute( normals, 3 ) ); | |
| this.addAttribute( 'uv', new Float32BufferAttribute( uvs, 2 ) ); | |
| } | |
| RingBufferGeometry.prototype = Object.create( BufferGeometry.prototype ); | |
| RingBufferGeometry.prototype.constructor = RingBufferGeometry; | |
| /** | |
| * @author astrodud / http://astrodud.isgreat.org/ | |
| * @author zz85 / https://github.com/zz85 | |
| * @author bhouston / http://clara.io | |
| * @author Mugen87 / https://github.com/Mugen87 | |
| */ | |
| // LatheGeometry | |
| function LatheGeometry( points, segments, phiStart, phiLength ) { | |
| Geometry.call( this ); | |
| this.type = 'LatheGeometry'; | |
| this.parameters = { | |
| points: points, | |
| segments: segments, | |
| phiStart: phiStart, | |
| phiLength: phiLength | |
| }; | |
| this.fromBufferGeometry( new LatheBufferGeometry( points, segments, phiStart, phiLength ) ); | |
| this.mergeVertices(); | |
| } | |
| LatheGeometry.prototype = Object.create( Geometry.prototype ); | |
| LatheGeometry.prototype.constructor = LatheGeometry; | |
| // LatheBufferGeometry | |
| function LatheBufferGeometry( points, segments, phiStart, phiLength ) { | |
| BufferGeometry.call( this ); | |
| this.type = 'LatheBufferGeometry'; | |
| this.parameters = { | |
| points: points, | |
| segments: segments, | |
| phiStart: phiStart, | |
| phiLength: phiLength | |
| }; | |
| segments = Math.floor( segments ) || 12; | |
| phiStart = phiStart || 0; | |
| phiLength = phiLength || Math.PI * 2; | |
| // clamp phiLength so it's in range of [ 0, 2PI ] | |
| phiLength = _Math.clamp( phiLength, 0, Math.PI * 2 ); | |
| // buffers | |
| var indices = []; | |
| var vertices = []; | |
| var uvs = []; | |
| // helper variables | |
| var base; | |
| var inverseSegments = 1.0 / segments; | |
| var vertex = new Vector3(); | |
| var uv = new Vector2(); | |
| var i, j; | |
| // generate vertices and uvs | |
| for ( i = 0; i <= segments; i ++ ) { | |
| var phi = phiStart + i * inverseSegments * phiLength; | |
| var sin = Math.sin( phi ); | |
| var cos = Math.cos( phi ); | |
| for ( j = 0; j <= ( points.length - 1 ); j ++ ) { | |
| // vertex | |
| vertex.x = points[ j ].x * sin; | |
| vertex.y = points[ j ].y; | |
| vertex.z = points[ j ].x * cos; | |
| vertices.push( vertex.x, vertex.y, vertex.z ); | |
| // uv | |
| uv.x = i / segments; | |
| uv.y = j / ( points.length - 1 ); | |
| uvs.push( uv.x, uv.y ); | |
| } | |
| } | |
| // indices | |
| for ( i = 0; i < segments; i ++ ) { | |
| for ( j = 0; j < ( points.length - 1 ); j ++ ) { | |
| base = j + i * points.length; | |
| var a = base; | |
| var b = base + points.length; | |
| var c = base + points.length + 1; | |
| var d = base + 1; | |
| // faces | |
| indices.push( a, b, d ); | |
| indices.push( b, c, d ); | |
| } | |
| } | |
| // build geometry | |
| this.setIndex( indices ); | |
| this.addAttribute( 'position', new Float32BufferAttribute( vertices, 3 ) ); | |
| this.addAttribute( 'uv', new Float32BufferAttribute( uvs, 2 ) ); | |
| // generate normals | |
| this.computeVertexNormals(); | |
| // if the geometry is closed, we need to average the normals along the seam. | |
| // because the corresponding vertices are identical (but still have different UVs). | |
| if ( phiLength === Math.PI * 2 ) { | |
| var normals = this.attributes.normal.array; | |
| var n1 = new Vector3(); | |
| var n2 = new Vector3(); | |
| var n = new Vector3(); | |
| // this is the buffer offset for the last line of vertices | |
| base = segments * points.length * 3; | |
| for ( i = 0, j = 0; i < points.length; i ++, j += 3 ) { | |
| // select the normal of the vertex in the first line | |
| n1.x = normals[ j + 0 ]; | |
| n1.y = normals[ j + 1 ]; | |
| n1.z = normals[ j + 2 ]; | |
| // select the normal of the vertex in the last line | |
| n2.x = normals[ base + j + 0 ]; | |
| n2.y = normals[ base + j + 1 ]; | |
| n2.z = normals[ base + j + 2 ]; | |
| // average normals | |
| n.addVectors( n1, n2 ).normalize(); | |
| // assign the new values to both normals | |
| normals[ j + 0 ] = normals[ base + j + 0 ] = n.x; | |
| normals[ j + 1 ] = normals[ base + j + 1 ] = n.y; | |
| normals[ j + 2 ] = normals[ base + j + 2 ] = n.z; | |
| } | |
| } | |
| } | |
| LatheBufferGeometry.prototype = Object.create( BufferGeometry.prototype ); | |
| LatheBufferGeometry.prototype.constructor = LatheBufferGeometry; | |
| /** | |
| * @author jonobr1 / http://jonobr1.com | |
| * @author Mugen87 / https://github.com/Mugen87 | |
| */ | |
| // ShapeGeometry | |
| function ShapeGeometry( shapes, curveSegments ) { | |
| Geometry.call( this ); | |
| this.type = 'ShapeGeometry'; | |
| if ( typeof curveSegments === 'object' ) { | |
| console.warn( 'THREE.ShapeGeometry: Options parameter has been removed.' ); | |
| curveSegments = curveSegments.curveSegments; | |
| } | |
| this.parameters = { | |
| shapes: shapes, | |
| curveSegments: curveSegments | |
| }; | |
| this.fromBufferGeometry( new ShapeBufferGeometry( shapes, curveSegments ) ); | |
| this.mergeVertices(); | |
| } | |
| ShapeGeometry.prototype = Object.create( Geometry.prototype ); | |
| ShapeGeometry.prototype.constructor = ShapeGeometry; | |
| ShapeGeometry.prototype.toJSON = function () { | |
| var data = Geometry.prototype.toJSON.call( this ); | |
| var shapes = this.parameters.shapes; | |
| return toJSON( shapes, data ); | |
| }; | |
| // ShapeBufferGeometry | |
| function ShapeBufferGeometry( shapes, curveSegments ) { | |
| BufferGeometry.call( this ); | |
| this.type = 'ShapeBufferGeometry'; | |
| this.parameters = { | |
| shapes: shapes, | |
| curveSegments: curveSegments | |
| }; | |
| curveSegments = curveSegments || 12; | |
| // buffers | |
| var indices = []; | |
| var vertices = []; | |
| var normals = []; | |
| var uvs = []; | |
| // helper variables | |
| var groupStart = 0; | |
| var groupCount = 0; | |
| // allow single and array values for "shapes" parameter | |
| if ( Array.isArray( shapes ) === false ) { | |
| addShape( shapes ); | |
| } else { | |
| for ( var i = 0; i < shapes.length; i ++ ) { | |
| addShape( shapes[ i ] ); | |
| this.addGroup( groupStart, groupCount, i ); // enables MultiMaterial support | |
| groupStart += groupCount; | |
| groupCount = 0; | |
| } | |
| } | |
| // build geometry | |
| this.setIndex( indices ); | |
| this.addAttribute( 'position', new Float32BufferAttribute( vertices, 3 ) ); | |
| this.addAttribute( 'normal', new Float32BufferAttribute( normals, 3 ) ); | |
| this.addAttribute( 'uv', new Float32BufferAttribute( uvs, 2 ) ); | |
| // helper functions | |
| function addShape( shape ) { | |
| var i, l, shapeHole; | |
| var indexOffset = vertices.length / 3; | |
| var points = shape.extractPoints( curveSegments ); | |
| var shapeVertices = points.shape; | |
| var shapeHoles = points.holes; | |
| // check direction of vertices | |
| if ( ShapeUtils.isClockWise( shapeVertices ) === false ) { | |
| shapeVertices = shapeVertices.reverse(); | |
| // also check if holes are in the opposite direction | |
| for ( i = 0, l = shapeHoles.length; i < l; i ++ ) { | |
| shapeHole = shapeHoles[ i ]; | |
| if ( ShapeUtils.isClockWise( shapeHole ) === true ) { | |
| shapeHoles[ i ] = shapeHole.reverse(); | |
| } | |
| } | |
| } | |
| var faces = ShapeUtils.triangulateShape( shapeVertices, shapeHoles ); | |
| // join vertices of inner and outer paths to a single array | |
| for ( i = 0, l = shapeHoles.length; i < l; i ++ ) { | |
| shapeHole = shapeHoles[ i ]; | |
| shapeVertices = shapeVertices.concat( shapeHole ); | |
| } | |
| // vertices, normals, uvs | |
| for ( i = 0, l = shapeVertices.length; i < l; i ++ ) { | |
| var vertex = shapeVertices[ i ]; | |
| vertices.push( vertex.x, vertex.y, 0 ); | |
| normals.push( 0, 0, 1 ); | |
| uvs.push( vertex.x, vertex.y ); // world uvs | |
| } | |
| // incides | |
| for ( i = 0, l = faces.length; i < l; i ++ ) { | |
| var face = faces[ i ]; | |
| var a = face[ 0 ] + indexOffset; | |
| var b = face[ 1 ] + indexOffset; | |
| var c = face[ 2 ] + indexOffset; | |
| indices.push( a, b, c ); | |
| groupCount += 3; | |
| } | |
| } | |
| } | |
| ShapeBufferGeometry.prototype = Object.create( BufferGeometry.prototype ); | |
| ShapeBufferGeometry.prototype.constructor = ShapeBufferGeometry; | |
| ShapeBufferGeometry.prototype.toJSON = function () { | |
| var data = BufferGeometry.prototype.toJSON.call( this ); | |
| var shapes = this.parameters.shapes; | |
| return toJSON( shapes, data ); | |
| }; | |
| // | |
| function toJSON( shapes, data ) { | |
| data.shapes = []; | |
| if ( Array.isArray( shapes ) ) { | |
| for ( var i = 0, l = shapes.length; i < l; i ++ ) { | |
| var shape = shapes[ i ]; | |
| data.shapes.push( shape.uuid ); | |
| } | |
| } else { | |
| data.shapes.push( shapes.uuid ); | |
| } | |
| return data; | |
| } | |
| /** | |
| * @author WestLangley / http://github.com/WestLangley | |
| * @author Mugen87 / https://github.com/Mugen87 | |
| */ | |
| function EdgesGeometry( geometry, thresholdAngle ) { | |
| BufferGeometry.call( this ); | |
| this.type = 'EdgesGeometry'; | |
| this.parameters = { | |
| thresholdAngle: thresholdAngle | |
| }; | |
| thresholdAngle = ( thresholdAngle !== undefined ) ? thresholdAngle : 1; | |
| // buffer | |
| var vertices = []; | |
| // helper variables | |
| var thresholdDot = Math.cos( _Math.DEG2RAD * thresholdAngle ); | |
| var edge = [ 0, 0 ], edges = {}, edge1, edge2; | |
| var key, keys = [ 'a', 'b', 'c' ]; | |
| // prepare source geometry | |
| var geometry2; | |
| if ( geometry.isBufferGeometry ) { | |
| geometry2 = new Geometry(); | |
| geometry2.fromBufferGeometry( geometry ); | |
| } else { | |
| geometry2 = geometry.clone(); | |
| } | |
| geometry2.mergeVertices(); | |
| geometry2.computeFaceNormals(); | |
| var sourceVertices = geometry2.vertices; | |
| var faces = geometry2.faces; | |
| // now create a data structure where each entry represents an edge with its adjoining faces | |
| for ( var i = 0, l = faces.length; i < l; i ++ ) { | |
| var face = faces[ i ]; | |
| for ( var j = 0; j < 3; j ++ ) { | |
| edge1 = face[ keys[ j ] ]; | |
| edge2 = face[ keys[ ( j + 1 ) % 3 ] ]; | |
| edge[ 0 ] = Math.min( edge1, edge2 ); | |
| edge[ 1 ] = Math.max( edge1, edge2 ); | |
| key = edge[ 0 ] + ',' + edge[ 1 ]; | |
| if ( edges[ key ] === undefined ) { | |
| edges[ key ] = { index1: edge[ 0 ], index2: edge[ 1 ], face1: i, face2: undefined }; | |
| } else { | |
| edges[ key ].face2 = i; | |
| } | |
| } | |
| } | |
| // generate vertices | |
| for ( key in edges ) { | |
| var e = edges[ key ]; | |
| // an edge is only rendered if the angle (in degrees) between the face normals of the adjoining faces exceeds this value. default = 1 degree. | |
| if ( e.face2 === undefined || faces[ e.face1 ].normal.dot( faces[ e.face2 ].normal ) <= thresholdDot ) { | |
| var vertex = sourceVertices[ e.index1 ]; | |
| vertices.push( vertex.x, vertex.y, vertex.z ); | |
| vertex = sourceVertices[ e.index2 ]; | |
| vertices.push( vertex.x, vertex.y, vertex.z ); | |
| } | |
| } | |
| // build geometry | |
| this.addAttribute( 'position', new Float32BufferAttribute( vertices, 3 ) ); | |
| } | |
| EdgesGeometry.prototype = Object.create( BufferGeometry.prototype ); | |
| EdgesGeometry.prototype.constructor = EdgesGeometry; | |
| /** | |
| * @author mrdoob / http://mrdoob.com/ | |
| * @author Mugen87 / https://github.com/Mugen87 | |
| */ | |
| // CylinderGeometry | |
| function CylinderGeometry( radiusTop, radiusBottom, height, radialSegments, heightSegments, openEnded, thetaStart, thetaLength ) { | |
| Geometry.call( this ); | |
| this.type = 'CylinderGeometry'; | |
| this.parameters = { | |
| radiusTop: radiusTop, | |
| radiusBottom: radiusBottom, | |
| height: height, | |
| radialSegments: radialSegments, | |
| heightSegments: heightSegments, | |
| openEnded: openEnded, | |
| thetaStart: thetaStart, | |
| thetaLength: thetaLength | |
| }; | |
| this.fromBufferGeometry( new CylinderBufferGeometry( radiusTop, radiusBottom, height, radialSegments, heightSegments, openEnded, thetaStart, thetaLength ) ); | |
| this.mergeVertices(); | |
| } | |
| CylinderGeometry.prototype = Object.create( Geometry.prototype ); | |
| CylinderGeometry.prototype.constructor = CylinderGeometry; | |
| // CylinderBufferGeometry | |
| function CylinderBufferGeometry( radiusTop, radiusBottom, height, radialSegments, heightSegments, openEnded, thetaStart, thetaLength ) { | |
| BufferGeometry.call( this ); | |
| this.type = 'CylinderBufferGeometry'; | |
| this.parameters = { | |
| radiusTop: radiusTop, | |
| radiusBottom: radiusBottom, | |
| height: height, | |
| radialSegments: radialSegments, | |
| heightSegments: heightSegments, | |
| openEnded: openEnded, | |
| thetaStart: thetaStart, | |
| thetaLength: thetaLength | |
| }; | |
| var scope = this; | |
| radiusTop = radiusTop !== undefined ? radiusTop : 1; | |
| radiusBottom = radiusBottom !== undefined ? radiusBottom : 1; | |
| height = height || 1; | |
| radialSegments = Math.floor( radialSegments ) || 8; | |
| heightSegments = Math.floor( heightSegments ) || 1; | |
| openEnded = openEnded !== undefined ? openEnded : false; | |
| thetaStart = thetaStart !== undefined ? thetaStart : 0.0; | |
| thetaLength = thetaLength !== undefined ? thetaLength : Math.PI * 2; | |
| // buffers | |
| var indices = []; | |
| var vertices = []; | |
| var normals = []; | |
| var uvs = []; | |
| // helper variables | |
| var index = 0; | |
| var indexArray = []; | |
| var halfHeight = height / 2; | |
| var groupStart = 0; | |
| // generate geometry | |
| generateTorso(); | |
| if ( openEnded === false ) { | |
| if ( radiusTop > 0 ) generateCap( true ); | |
| if ( radiusBottom > 0 ) generateCap( false ); | |
| } | |
| // build geometry | |
| this.setIndex( indices ); | |
| this.addAttribute( 'position', new Float32BufferAttribute( vertices, 3 ) ); | |
| this.addAttribute( 'normal', new Float32BufferAttribute( normals, 3 ) ); | |
| this.addAttribute( 'uv', new Float32BufferAttribute( uvs, 2 ) ); | |
| function generateTorso() { | |
| var x, y; | |
| var normal = new Vector3(); | |
| var vertex = new Vector3(); | |
| var groupCount = 0; | |
| // this will be used to calculate the normal | |
| var slope = ( radiusBottom - radiusTop ) / height; | |
| // generate vertices, normals and uvs | |
| for ( y = 0; y <= heightSegments; y ++ ) { | |
| var indexRow = []; | |
| var v = y / heightSegments; | |
| // calculate the radius of the current row | |
| var radius = v * ( radiusBottom - radiusTop ) + radiusTop; | |
| for ( x = 0; x <= radialSegments; x ++ ) { | |
| var u = x / radialSegments; | |
| var theta = u * thetaLength + thetaStart; | |
| var sinTheta = Math.sin( theta ); | |
| var cosTheta = Math.cos( theta ); | |
| // vertex | |
| vertex.x = radius * sinTheta; | |
| vertex.y = - v * height + halfHeight; | |
| vertex.z = radius * cosTheta; | |
| vertices.push( vertex.x, vertex.y, vertex.z ); | |
| // normal | |
| normal.set( sinTheta, slope, cosTheta ).normalize(); | |
| normals.push( normal.x, normal.y, normal.z ); | |
| // uv | |
| uvs.push( u, 1 - v ); | |
| // save index of vertex in respective row | |
| indexRow.push( index ++ ); | |
| } | |
| // now save vertices of the row in our index array | |
| indexArray.push( indexRow ); | |
| } | |
| // generate indices | |
| for ( x = 0; x < radialSegments; x ++ ) { | |
| for ( y = 0; y < heightSegments; y ++ ) { | |
| // we use the index array to access the correct indices | |
| var a = indexArray[ y ][ x ]; | |
| var b = indexArray[ y + 1 ][ x ]; | |
| var c = indexArray[ y + 1 ][ x + 1 ]; | |
| var d = indexArray[ y ][ x + 1 ]; | |
| // faces | |
| indices.push( a, b, d ); | |
| indices.push( b, c, d ); | |
| // update group counter | |
| groupCount += 6; | |
| } | |
| } | |
| // add a group to the geometry. this will ensure multi material support | |
| scope.addGroup( groupStart, groupCount, 0 ); | |
| // calculate new start value for groups | |
| groupStart += groupCount; | |
| } | |
| function generateCap( top ) { | |
| var x, centerIndexStart, centerIndexEnd; | |
| var uv = new Vector2(); | |
| var vertex = new Vector3(); | |
| var groupCount = 0; | |
| var radius = ( top === true ) ? radiusTop : radiusBottom; | |
| var sign = ( top === true ) ? 1 : - 1; | |
| // save the index of the first center vertex | |
| centerIndexStart = index; | |
| // first we generate the center vertex data of the cap. | |
| // because the geometry needs one set of uvs per face, | |
| // we must generate a center vertex per face/segment | |
| for ( x = 1; x <= radialSegments; x ++ ) { | |
| // vertex | |
| vertices.push( 0, halfHeight * sign, 0 ); | |
| // normal | |
| normals.push( 0, sign, 0 ); | |
| // uv | |
| uvs.push( 0.5, 0.5 ); | |
| // increase index | |
| index ++; | |
| } | |
| // save the index of the last center vertex | |
| centerIndexEnd = index; | |
| // now we generate the surrounding vertices, normals and uvs | |
| for ( x = 0; x <= radialSegments; x ++ ) { | |
| var u = x / radialSegments; | |
| var theta = u * thetaLength + thetaStart; | |
| var cosTheta = Math.cos( theta ); | |
| var sinTheta = Math.sin( theta ); | |
| // vertex | |
| vertex.x = radius * sinTheta; | |
| vertex.y = halfHeight * sign; | |
| vertex.z = radius * cosTheta; | |
| vertices.push( vertex.x, vertex.y, vertex.z ); | |
| // normal | |
| normals.push( 0, sign, 0 ); | |
| // uv | |
| uv.x = ( cosTheta * 0.5 ) + 0.5; | |
| uv.y = ( sinTheta * 0.5 * sign ) + 0.5; | |
| uvs.push( uv.x, uv.y ); | |
| // increase index | |
| index ++; | |
| } | |
| // generate indices | |
| for ( x = 0; x < radialSegments; x ++ ) { | |
| var c = centerIndexStart + x; | |
| var i = centerIndexEnd + x; | |
| if ( top === true ) { | |
| // face top | |
| indices.push( i, i + 1, c ); | |
| } else { | |
| // face bottom | |
| indices.push( i + 1, i, c ); | |
| } | |
| groupCount += 3; | |
| } | |
| // add a group to the geometry. this will ensure multi material support | |
| scope.addGroup( groupStart, groupCount, top === true ? 1 : 2 ); | |
| // calculate new start value for groups | |
| groupStart += groupCount; | |
| } | |
| } | |
| CylinderBufferGeometry.prototype = Object.create( BufferGeometry.prototype ); | |
| CylinderBufferGeometry.prototype.constructor = CylinderBufferGeometry; | |
| /** | |
| * @author abelnation / http://github.com/abelnation | |
| */ | |
| // ConeGeometry | |
| function ConeGeometry( radius, height, radialSegments, heightSegments, openEnded, thetaStart, thetaLength ) { | |
| CylinderGeometry.call( this, 0, radius, height, radialSegments, heightSegments, openEnded, thetaStart, thetaLength ); | |
| this.type = 'ConeGeometry'; | |
| this.parameters = { | |
| radius: radius, | |
| height: height, | |
| radialSegments: radialSegments, | |
| heightSegments: heightSegments, | |
| openEnded: openEnded, | |
| thetaStart: thetaStart, | |
| thetaLength: thetaLength | |
| }; | |
| } | |
| ConeGeometry.prototype = Object.create( CylinderGeometry.prototype ); | |
| ConeGeometry.prototype.constructor = ConeGeometry; | |
| // ConeBufferGeometry | |
| function ConeBufferGeometry( radius, height, radialSegments, heightSegments, openEnded, thetaStart, thetaLength ) { | |
| CylinderBufferGeometry.call( this, 0, radius, height, radialSegments, heightSegments, openEnded, thetaStart, thetaLength ); | |
| this.type = 'ConeBufferGeometry'; | |
| this.parameters = { | |
| radius: radius, | |
| height: height, | |
| radialSegments: radialSegments, | |
| heightSegments: heightSegments, | |
| openEnded: openEnded, | |
| thetaStart: thetaStart, | |
| thetaLength: thetaLength | |
| }; | |
| } | |
| ConeBufferGeometry.prototype = Object.create( CylinderBufferGeometry.prototype ); | |
| ConeBufferGeometry.prototype.constructor = ConeBufferGeometry; | |
| /** | |
| * @author benaadams / https://twitter.com/ben_a_adams | |
| * @author Mugen87 / https://github.com/Mugen87 | |
| * @author hughes | |
| */ | |
| // CircleGeometry | |
| function CircleGeometry( radius, segments, thetaStart, thetaLength ) { | |
| Geometry.call( this ); | |
| this.type = 'CircleGeometry'; | |
| this.parameters = { | |
| radius: radius, | |
| segments: segments, | |
| thetaStart: thetaStart, | |
| thetaLength: thetaLength | |
| }; | |
| this.fromBufferGeometry( new CircleBufferGeometry( radius, segments, thetaStart, thetaLength ) ); | |
| this.mergeVertices(); | |
| } | |
| CircleGeometry.prototype = Object.create( Geometry.prototype ); | |
| CircleGeometry.prototype.constructor = CircleGeometry; | |
| // CircleBufferGeometry | |
| function CircleBufferGeometry( radius, segments, thetaStart, thetaLength ) { | |
| BufferGeometry.call( this ); | |
| this.type = 'CircleBufferGeometry'; | |
| this.parameters = { | |
| radius: radius, | |
| segments: segments, | |
| thetaStart: thetaStart, | |
| thetaLength: thetaLength | |
| }; | |
| radius = radius || 1; | |
| segments = segments !== undefined ? Math.max( 3, segments ) : 8; | |
| thetaStart = thetaStart !== undefined ? thetaStart : 0; | |
| thetaLength = thetaLength !== undefined ? thetaLength : Math.PI * 2; | |
| // buffers | |
| var indices = []; | |
| var vertices = []; | |
| var normals = []; | |
| var uvs = []; | |
| // helper variables | |
| var i, s; | |
| var vertex = new Vector3(); | |
| var uv = new Vector2(); | |
| // center point | |
| vertices.push( 0, 0, 0 ); | |
| normals.push( 0, 0, 1 ); | |
| uvs.push( 0.5, 0.5 ); | |
| for ( s = 0, i = 3; s <= segments; s ++, i += 3 ) { | |
| var segment = thetaStart + s / segments * thetaLength; | |
| // vertex | |
| vertex.x = radius * Math.cos( segment ); | |
| vertex.y = radius * Math.sin( segment ); | |
| vertices.push( vertex.x, vertex.y, vertex.z ); | |
| // normal | |
| normals.push( 0, 0, 1 ); | |
| // uvs | |
| uv.x = ( vertices[ i ] / radius + 1 ) / 2; | |
| uv.y = ( vertices[ i + 1 ] / radius + 1 ) / 2; | |
| uvs.push( uv.x, uv.y ); | |
| } | |
| // indices | |
| for ( i = 1; i <= segments; i ++ ) { | |
| indices.push( i, i + 1, 0 ); | |
| } | |
| // build geometry | |
| this.setIndex( indices ); | |
| this.addAttribute( 'position', new Float32BufferAttribute( vertices, 3 ) ); | |
| this.addAttribute( 'normal', new Float32BufferAttribute( normals, 3 ) ); | |
| this.addAttribute( 'uv', new Float32BufferAttribute( uvs, 2 ) ); | |
| } | |
| CircleBufferGeometry.prototype = Object.create( BufferGeometry.prototype ); | |
| CircleBufferGeometry.prototype.constructor = CircleBufferGeometry; | |
| var Geometries = Object.freeze({ | |
| WireframeGeometry: WireframeGeometry, | |
| ParametricGeometry: ParametricGeometry, | |
| ParametricBufferGeometry: ParametricBufferGeometry, | |
| TetrahedronGeometry: TetrahedronGeometry, | |
| TetrahedronBufferGeometry: TetrahedronBufferGeometry, | |
| OctahedronGeometry: OctahedronGeometry, | |
| OctahedronBufferGeometry: OctahedronBufferGeometry, | |
| IcosahedronGeometry: IcosahedronGeometry, | |
| IcosahedronBufferGeometry: IcosahedronBufferGeometry, | |
| DodecahedronGeometry: DodecahedronGeometry, | |
| DodecahedronBufferGeometry: DodecahedronBufferGeometry, | |
| PolyhedronGeometry: PolyhedronGeometry, | |
| PolyhedronBufferGeometry: PolyhedronBufferGeometry, | |
| TubeGeometry: TubeGeometry, | |
| TubeBufferGeometry: TubeBufferGeometry, | |
| TorusKnotGeometry: TorusKnotGeometry, | |
| TorusKnotBufferGeometry: TorusKnotBufferGeometry, | |
| TorusGeometry: TorusGeometry, | |
| TorusBufferGeometry: TorusBufferGeometry, | |
| TextGeometry: TextGeometry, | |
| TextBufferGeometry: TextBufferGeometry, | |
| SphereGeometry: SphereGeometry, | |
| SphereBufferGeometry: SphereBufferGeometry, | |
| RingGeometry: RingGeometry, | |
| RingBufferGeometry: RingBufferGeometry, | |
| PlaneGeometry: PlaneGeometry, | |
| PlaneBufferGeometry: PlaneBufferGeometry, | |
| LatheGeometry: LatheGeometry, | |
| LatheBufferGeometry: LatheBufferGeometry, | |
| ShapeGeometry: ShapeGeometry, | |
| ShapeBufferGeometry: ShapeBufferGeometry, | |
| ExtrudeGeometry: ExtrudeGeometry, | |
| ExtrudeBufferGeometry: ExtrudeBufferGeometry, | |
| EdgesGeometry: EdgesGeometry, | |
| ConeGeometry: ConeGeometry, | |
| ConeBufferGeometry: ConeBufferGeometry, | |
| CylinderGeometry: CylinderGeometry, | |
| CylinderBufferGeometry: CylinderBufferGeometry, | |
| CircleGeometry: CircleGeometry, | |
| CircleBufferGeometry: CircleBufferGeometry, | |
| BoxGeometry: BoxGeometry, | |
| BoxBufferGeometry: BoxBufferGeometry | |
| }); | |
| /** | |
| * @author mrdoob / http://mrdoob.com/ | |
| * | |
| * parameters = { | |
| * color: <THREE.Color> | |
| * } | |
| */ | |
| function ShadowMaterial( parameters ) { | |
| Material.call( this ); | |
| this.type = 'ShadowMaterial'; | |
| this.color = new Color( 0x000000 ); | |
| this.transparent = true; | |
| this.setValues( parameters ); | |
| } | |
| ShadowMaterial.prototype = Object.create( Material.prototype ); | |
| ShadowMaterial.prototype.constructor = ShadowMaterial; | |
| ShadowMaterial.prototype.isShadowMaterial = true; | |
| ShadowMaterial.prototype.copy = function ( source ) { | |
| Material.prototype.copy.call( this, source ); | |
| this.color.copy( source.color ); | |
| return this; | |
| }; | |
| /** | |
| * @author mrdoob / http://mrdoob.com/ | |
| */ | |
| function RawShaderMaterial( parameters ) { | |
| ShaderMaterial.call( this, parameters ); | |
| this.type = 'RawShaderMaterial'; | |
| } | |
| RawShaderMaterial.prototype = Object.create( ShaderMaterial.prototype ); | |
| RawShaderMaterial.prototype.constructor = RawShaderMaterial; | |
| RawShaderMaterial.prototype.isRawShaderMaterial = true; | |
| /** | |
| * @author WestLangley / http://github.com/WestLangley | |
| * | |
| * parameters = { | |
| * color: <hex>, | |
| * roughness: <float>, | |
| * metalness: <float>, | |
| * opacity: <float>, | |
| * | |
| * map: new THREE.Texture( <Image> ), | |
| * | |
| * lightMap: new THREE.Texture( <Image> ), | |
| * lightMapIntensity: <float> | |
| * | |
| * aoMap: new THREE.Texture( <Image> ), | |
| * aoMapIntensity: <float> | |
| * | |
| * emissive: <hex>, | |
| * emissiveIntensity: <float> | |
| * emissiveMap: new THREE.Texture( <Image> ), | |
| * | |
| * bumpMap: new THREE.Texture( <Image> ), | |
| * bumpScale: <float>, | |
| * | |
| * normalMap: new THREE.Texture( <Image> ), | |
| * normalScale: <Vector2>, | |
| * | |
| * displacementMap: new THREE.Texture( <Image> ), | |
| * displacementScale: <float>, | |
| * displacementBias: <float>, | |
| * | |
| * roughnessMap: new THREE.Texture( <Image> ), | |
| * | |
| * metalnessMap: new THREE.Texture( <Image> ), | |
| * | |
| * alphaMap: new THREE.Texture( <Image> ), | |
| * | |
| * envMap: new THREE.CubeTexture( [posx, negx, posy, negy, posz, negz] ), | |
| * envMapIntensity: <float> | |
| * | |
| * refractionRatio: <float>, | |
| * | |
| * wireframe: <boolean>, | |
| * wireframeLinewidth: <float>, | |
| * | |
| * skinning: <bool>, | |
| * morphTargets: <bool>, | |
| * morphNormals: <bool> | |
| * } | |
| */ | |
| function MeshStandardMaterial( parameters ) { | |
| Material.call( this ); | |
| this.defines = { 'STANDARD': '' }; | |
| this.type = 'MeshStandardMaterial'; | |
| this.color = new Color( 0xffffff ); // diffuse | |
| this.roughness = 0.5; | |
| this.metalness = 0.5; | |
| this.map = null; | |
| this.lightMap = null; | |
| this.lightMapIntensity = 1.0; | |
| this.aoMap = null; | |
| this.aoMapIntensity = 1.0; | |
| this.emissive = new Color( 0x000000 ); | |
| this.emissiveIntensity = 1.0; | |
| this.emissiveMap = null; | |
| this.bumpMap = null; | |
| this.bumpScale = 1; | |
| this.normalMap = null; | |
| this.normalScale = new Vector2( 1, 1 ); | |
| this.displacementMap = null; | |
| this.displacementScale = 1; | |
| this.displacementBias = 0; | |
| this.roughnessMap = null; | |
| this.metalnessMap = null; | |
| this.alphaMap = null; | |
| this.envMap = null; | |
| this.envMapIntensity = 1.0; | |
| this.refractionRatio = 0.98; | |
| this.wireframe = false; | |
| this.wireframeLinewidth = 1; | |
| this.wireframeLinecap = 'round'; | |
| this.wireframeLinejoin = 'round'; | |
| this.skinning = false; | |
| this.morphTargets = false; | |
| this.morphNormals = false; | |
| this.setValues( parameters ); | |
| } | |
| MeshStandardMaterial.prototype = Object.create( Material.prototype ); | |
| MeshStandardMaterial.prototype.constructor = MeshStandardMaterial; | |
| MeshStandardMaterial.prototype.isMeshStandardMaterial = true; | |
| MeshStandardMaterial.prototype.copy = function ( source ) { | |
| Material.prototype.copy.call( this, source ); | |
| this.defines = { 'STANDARD': '' }; | |
| this.color.copy( source.color ); | |
| this.roughness = source.roughness; | |
| this.metalness = source.metalness; | |
| this.map = source.map; | |
| this.lightMap = source.lightMap; | |
| this.lightMapIntensity = source.lightMapIntensity; | |
| this.aoMap = source.aoMap; | |
| this.aoMapIntensity = source.aoMapIntensity; | |
| this.emissive.copy( source.emissive ); | |
| this.emissiveMap = source.emissiveMap; | |
| this.emissiveIntensity = source.emissiveIntensity; | |
| this.bumpMap = source.bumpMap; | |
| this.bumpScale = source.bumpScale; | |
| this.normalMap = source.normalMap; | |
| this.normalScale.copy( source.normalScale ); | |
| this.displacementMap = source.displacementMap; | |
| this.displacementScale = source.displacementScale; | |
| this.displacementBias = source.displacementBias; | |
| this.roughnessMap = source.roughnessMap; | |
| this.metalnessMap = source.metalnessMap; | |
| this.alphaMap = source.alphaMap; | |
| this.envMap = source.envMap; | |
| this.envMapIntensity = source.envMapIntensity; | |
| this.refractionRatio = source.refractionRatio; | |
| this.wireframe = source.wireframe; | |
| this.wireframeLinewidth = source.wireframeLinewidth; | |
| this.wireframeLinecap = source.wireframeLinecap; | |
| this.wireframeLinejoin = source.wireframeLinejoin; | |
| this.skinning = source.skinning; | |
| this.morphTargets = source.morphTargets; | |
| this.morphNormals = source.morphNormals; | |
| return this; | |
| }; | |
| /** | |
| * @author WestLangley / http://github.com/WestLangley | |
| * | |
| * parameters = { | |
| * reflectivity: <float> | |
| * } | |
| */ | |
| function MeshPhysicalMaterial( parameters ) { | |
| MeshStandardMaterial.call( this ); | |
| this.defines = { 'PHYSICAL': '' }; | |
| this.type = 'MeshPhysicalMaterial'; | |
| this.reflectivity = 0.5; // maps to F0 = 0.04 | |
| this.clearCoat = 0.0; | |
| this.clearCoatRoughness = 0.0; | |
| this.setValues( parameters ); | |
| } | |
| MeshPhysicalMaterial.prototype = Object.create( MeshStandardMaterial.prototype ); | |
| MeshPhysicalMaterial.prototype.constructor = MeshPhysicalMaterial; | |
| MeshPhysicalMaterial.prototype.isMeshPhysicalMaterial = true; | |
| MeshPhysicalMaterial.prototype.copy = function ( source ) { | |
| MeshStandardMaterial.prototype.copy.call( this, source ); | |
| this.defines = { 'PHYSICAL': '' }; | |
| this.reflectivity = source.reflectivity; | |
| this.clearCoat = source.clearCoat; | |
| this.clearCoatRoughness = source.clearCoatRoughness; | |
| return this; | |
| }; | |
| /** | |
| * @author mrdoob / http://mrdoob.com/ | |
| * @author alteredq / http://alteredqualia.com/ | |
| * | |
| * parameters = { | |
| * color: <hex>, | |
| * specular: <hex>, | |
| * shininess: <float>, | |
| * opacity: <float>, | |
| * | |
| * map: new THREE.Texture( <Image> ), | |
| * | |
| * lightMap: new THREE.Texture( <Image> ), | |
| * lightMapIntensity: <float> | |
| * | |
| * aoMap: new THREE.Texture( <Image> ), | |
| * aoMapIntensity: <float> | |
| * | |
| * emissive: <hex>, | |
| * emissiveIntensity: <float> | |
| * emissiveMap: new THREE.Texture( <Image> ), | |
| * | |
| * bumpMap: new THREE.Texture( <Image> ), | |
| * bumpScale: <float>, | |
| * | |
| * normalMap: new THREE.Texture( <Image> ), | |
| * normalScale: <Vector2>, | |
| * | |
| * displacementMap: new THREE.Texture( <Image> ), | |
| * displacementScale: <float>, | |
| * displacementBias: <float>, | |
| * | |
| * specularMap: new THREE.Texture( <Image> ), | |
| * | |
| * alphaMap: new THREE.Texture( <Image> ), | |
| * | |
| * envMap: new THREE.CubeTexture( [posx, negx, posy, negy, posz, negz] ), | |
| * combine: THREE.Multiply, | |
| * reflectivity: <float>, | |
| * refractionRatio: <float>, | |
| * | |
| * wireframe: <boolean>, | |
| * wireframeLinewidth: <float>, | |
| * | |
| * skinning: <bool>, | |
| * morphTargets: <bool>, | |
| * morphNormals: <bool> | |
| * } | |
| */ | |
| function MeshPhongMaterial( parameters ) { | |
| Material.call( this ); | |
| this.type = 'MeshPhongMaterial'; | |
| this.color = new Color( 0xffffff ); // diffuse | |
| this.specular = new Color( 0x111111 ); | |
| this.shininess = 30; | |
| this.map = null; | |
| this.lightMap = null; | |
| this.lightMapIntensity = 1.0; | |
| this.aoMap = null; | |
| this.aoMapIntensity = 1.0; | |
| this.emissive = new Color( 0x000000 ); | |
| this.emissiveIntensity = 1.0; | |
| this.emissiveMap = null; | |
| this.bumpMap = null; | |
| this.bumpScale = 1; | |
| this.normalMap = null; | |
| this.normalScale = new Vector2( 1, 1 ); | |
| this.displacementMap = null; | |
| this.displacementScale = 1; | |
| this.displacementBias = 0; | |
| this.specularMap = null; | |
| this.alphaMap = null; | |
| this.envMap = null; | |
| this.combine = MultiplyOperation; | |
| this.reflectivity = 1; | |
| this.refractionRatio = 0.98; | |
| this.wireframe = false; | |
| this.wireframeLinewidth = 1; | |
| this.wireframeLinecap = 'round'; | |
| this.wireframeLinejoin = 'round'; | |
| this.skinning = false; | |
| this.morphTargets = false; | |
| this.morphNormals = false; | |
| this.setValues( parameters ); | |
| } | |
| MeshPhongMaterial.prototype = Object.create( Material.prototype ); | |
| MeshPhongMaterial.prototype.constructor = MeshPhongMaterial; | |
| MeshPhongMaterial.prototype.isMeshPhongMaterial = true; | |
| MeshPhongMaterial.prototype.copy = function ( source ) { | |
| Material.prototype.copy.call( this, source ); | |
| this.color.copy( source.color ); | |
| this.specular.copy( source.specular ); | |
| this.shininess = source.shininess; | |
| this.map = source.map; | |
| this.lightMap = source.lightMap; | |
| this.lightMapIntensity = source.lightMapIntensity; | |
| this.aoMap = source.aoMap; | |
| this.aoMapIntensity = source.aoMapIntensity; | |
| this.emissive.copy( source.emissive ); | |
| this.emissiveMap = source.emissiveMap; | |
| this.emissiveIntensity = source.emissiveIntensity; | |
| this.bumpMap = source.bumpMap; | |
| this.bumpScale = source.bumpScale; | |
| this.normalMap = source.normalMap; | |
| this.normalScale.copy( source.normalScale ); | |
| this.displacementMap = source.displacementMap; | |
| this.displacementScale = source.displacementScale; | |
| this.displacementBias = source.displacementBias; | |
| this.specularMap = source.specularMap; | |
| this.alphaMap = source.alphaMap; | |
| this.envMap = source.envMap; | |
| this.combine = source.combine; | |
| this.reflectivity = source.reflectivity; | |
| this.refractionRatio = source.refractionRatio; | |
| this.wireframe = source.wireframe; | |
| this.wireframeLinewidth = source.wireframeLinewidth; | |
| this.wireframeLinecap = source.wireframeLinecap; | |
| this.wireframeLinejoin = source.wireframeLinejoin; | |
| this.skinning = source.skinning; | |
| this.morphTargets = source.morphTargets; | |
| this.morphNormals = source.morphNormals; | |
| return this; | |
| }; | |
| /** | |
| * @author takahirox / http://github.com/takahirox | |
| * | |
| * parameters = { | |
| * gradientMap: new THREE.Texture( <Image> ) | |
| * } | |
| */ | |
| function MeshToonMaterial( parameters ) { | |
| MeshPhongMaterial.call( this ); | |
| this.defines = { 'TOON': '' }; | |
| this.type = 'MeshToonMaterial'; | |
| this.gradientMap = null; | |
| this.setValues( parameters ); | |
| } | |
| MeshToonMaterial.prototype = Object.create( MeshPhongMaterial.prototype ); | |
| MeshToonMaterial.prototype.constructor = MeshToonMaterial; | |
| MeshToonMaterial.prototype.isMeshToonMaterial = true; | |
| MeshToonMaterial.prototype.copy = function ( source ) { | |
| MeshPhongMaterial.prototype.copy.call( this, source ); | |
| this.gradientMap = source.gradientMap; | |
| return this; | |
| }; | |
| /** | |
| * @author mrdoob / http://mrdoob.com/ | |
| * @author WestLangley / http://github.com/WestLangley | |
| * | |
| * parameters = { | |
| * opacity: <float>, | |
| * | |
| * bumpMap: new THREE.Texture( <Image> ), | |
| * bumpScale: <float>, | |
| * | |
| * normalMap: new THREE.Texture( <Image> ), | |
| * normalScale: <Vector2>, | |
| * | |
| * displacementMap: new THREE.Texture( <Image> ), | |
| * displacementScale: <float>, | |
| * displacementBias: <float>, | |
| * | |
| * wireframe: <boolean>, | |
| * wireframeLinewidth: <float> | |
| * | |
| * skinning: <bool>, | |
| * morphTargets: <bool>, | |
| * morphNormals: <bool> | |
| * } | |
| */ | |
| function MeshNormalMaterial( parameters ) { | |
| Material.call( this ); | |
| this.type = 'MeshNormalMaterial'; | |
| this.bumpMap = null; | |
| this.bumpScale = 1; | |
| this.normalMap = null; | |
| this.normalScale = new Vector2( 1, 1 ); | |
| this.displacementMap = null; | |
| this.displacementScale = 1; | |
| this.displacementBias = 0; | |
| this.wireframe = false; | |
| this.wireframeLinewidth = 1; | |
| this.fog = false; | |
| this.lights = false; | |
| this.skinning = false; | |
| this.morphTargets = false; | |
| this.morphNormals = false; | |
| this.setValues( parameters ); | |
| } | |
| MeshNormalMaterial.prototype = Object.create( Material.prototype ); | |
| MeshNormalMaterial.prototype.constructor = MeshNormalMaterial; | |
| MeshNormalMaterial.prototype.isMeshNormalMaterial = true; | |
| MeshNormalMaterial.prototype.copy = function ( source ) { | |
| Material.prototype.copy.call( this, source ); | |
| this.bumpMap = source.bumpMap; | |
| this.bumpScale = source.bumpScale; | |
| this.normalMap = source.normalMap; | |
| this.normalScale.copy( source.normalScale ); | |
| this.displacementMap = source.displacementMap; | |
| this.displacementScale = source.displacementScale; | |
| this.displacementBias = source.displacementBias; | |
| this.wireframe = source.wireframe; | |
| this.wireframeLinewidth = source.wireframeLinewidth; | |
| this.skinning = source.skinning; | |
| this.morphTargets = source.morphTargets; | |
| this.morphNormals = source.morphNormals; | |
| return this; | |
| }; | |
| /** | |
| * @author mrdoob / http://mrdoob.com/ | |
| * @author alteredq / http://alteredqualia.com/ | |
| * | |
| * parameters = { | |
| * color: <hex>, | |
| * opacity: <float>, | |
| * | |
| * map: new THREE.Texture( <Image> ), | |
| * | |
| * lightMap: new THREE.Texture( <Image> ), | |
| * lightMapIntensity: <float> | |
| * | |
| * aoMap: new THREE.Texture( <Image> ), | |
| * aoMapIntensity: <float> | |
| * | |
| * emissive: <hex>, | |
| * emissiveIntensity: <float> | |
| * emissiveMap: new THREE.Texture( <Image> ), | |
| * | |
| * specularMap: new THREE.Texture( <Image> ), | |
| * | |
| * alphaMap: new THREE.Texture( <Image> ), | |
| * | |
| * envMap: new THREE.CubeTexture( [posx, negx, posy, negy, posz, negz] ), | |
| * combine: THREE.Multiply, | |
| * reflectivity: <float>, | |
| * refractionRatio: <float>, | |
| * | |
| * wireframe: <boolean>, | |
| * wireframeLinewidth: <float>, | |
| * | |
| * skinning: <bool>, | |
| * morphTargets: <bool>, | |
| * morphNormals: <bool> | |
| * } | |
| */ | |
| function MeshLambertMaterial( parameters ) { | |
| Material.call( this ); | |
| this.type = 'MeshLambertMaterial'; | |
| this.color = new Color( 0xffffff ); // diffuse | |
| this.map = null; | |
| this.lightMap = null; | |
| this.lightMapIntensity = 1.0; | |
| this.aoMap = null; | |
| this.aoMapIntensity = 1.0; | |
| this.emissive = new Color( 0x000000 ); | |
| this.emissiveIntensity = 1.0; | |
| this.emissiveMap = null; | |
| this.specularMap = null; | |
| this.alphaMap = null; | |
| this.envMap = null; | |
| this.combine = MultiplyOperation; | |
| this.reflectivity = 1; | |
| this.refractionRatio = 0.98; | |
| this.wireframe = false; | |
| this.wireframeLinewidth = 1; | |
| this.wireframeLinecap = 'round'; | |
| this.wireframeLinejoin = 'round'; | |
| this.skinning = false; | |
| this.morphTargets = false; | |
| this.morphNormals = false; | |
| this.setValues( parameters ); | |
| } | |
| MeshLambertMaterial.prototype = Object.create( Material.prototype ); | |
| MeshLambertMaterial.prototype.constructor = MeshLambertMaterial; | |
| MeshLambertMaterial.prototype.isMeshLambertMaterial = true; | |
| MeshLambertMaterial.prototype.copy = function ( source ) { | |
| Material.prototype.copy.call( this, source ); | |
| this.color.copy( source.color ); | |
| this.map = source.map; | |
| this.lightMap = source.lightMap; | |
| this.lightMapIntensity = source.lightMapIntensity; | |
| this.aoMap = source.aoMap; | |
| this.aoMapIntensity = source.aoMapIntensity; | |
| this.emissive.copy( source.emissive ); | |
| this.emissiveMap = source.emissiveMap; | |
| this.emissiveIntensity = source.emissiveIntensity; | |
| this.specularMap = source.specularMap; | |
| this.alphaMap = source.alphaMap; | |
| this.envMap = source.envMap; | |
| this.combine = source.combine; | |
| this.reflectivity = source.reflectivity; | |
| this.refractionRatio = source.refractionRatio; | |
| this.wireframe = source.wireframe; | |
| this.wireframeLinewidth = source.wireframeLinewidth; | |
| this.wireframeLinecap = source.wireframeLinecap; | |
| this.wireframeLinejoin = source.wireframeLinejoin; | |
| this.skinning = source.skinning; | |
| this.morphTargets = source.morphTargets; | |
| this.morphNormals = source.morphNormals; | |
| return this; | |
| }; | |
| /** | |
| * @author alteredq / http://alteredqualia.com/ | |
| * | |
| * parameters = { | |
| * color: <hex>, | |
| * opacity: <float>, | |
| * | |
| * linewidth: <float>, | |
| * | |
| * scale: <float>, | |
| * dashSize: <float>, | |
| * gapSize: <float> | |
| * } | |
| */ | |
| function LineDashedMaterial( parameters ) { | |
| LineBasicMaterial.call( this ); | |
| this.type = 'LineDashedMaterial'; | |
| this.scale = 1; | |
| this.dashSize = 3; | |
| this.gapSize = 1; | |
| this.setValues( parameters ); | |
| } | |
| LineDashedMaterial.prototype = Object.create( LineBasicMaterial.prototype ); | |
| LineDashedMaterial.prototype.constructor = LineDashedMaterial; | |
| LineDashedMaterial.prototype.isLineDashedMaterial = true; | |
| LineDashedMaterial.prototype.copy = function ( source ) { | |
| LineBasicMaterial.prototype.copy.call( this, source ); | |
| this.scale = source.scale; | |
| this.dashSize = source.dashSize; | |
| this.gapSize = source.gapSize; | |
| return this; | |
| }; | |
| var Materials = Object.freeze({ | |
| ShadowMaterial: ShadowMaterial, | |
| SpriteMaterial: SpriteMaterial, | |
| RawShaderMaterial: RawShaderMaterial, | |
| ShaderMaterial: ShaderMaterial, | |
| PointsMaterial: PointsMaterial, | |
| MeshPhysicalMaterial: MeshPhysicalMaterial, | |
| MeshStandardMaterial: MeshStandardMaterial, | |
| MeshPhongMaterial: MeshPhongMaterial, | |
| MeshToonMaterial: MeshToonMaterial, | |
| MeshNormalMaterial: MeshNormalMaterial, | |
| MeshLambertMaterial: MeshLambertMaterial, | |
| MeshDepthMaterial: MeshDepthMaterial, | |
| MeshDistanceMaterial: MeshDistanceMaterial, | |
| MeshBasicMaterial: MeshBasicMaterial, | |
| LineDashedMaterial: LineDashedMaterial, | |
| LineBasicMaterial: LineBasicMaterial, | |
| Material: Material | |
| }); | |
| /** | |
| * @author mrdoob / http://mrdoob.com/ | |
| */ | |
| var Cache = { | |
| enabled: false, | |
| files: {}, | |
| add: function ( key, file ) { | |
| if ( this.enabled === false ) return; | |
| // console.log( 'THREE.Cache', 'Adding key:', key ); | |
| this.files[ key ] = file; | |
| }, | |
| get: function ( key ) { | |
| if ( this.enabled === false ) return; | |
| // console.log( 'THREE.Cache', 'Checking key:', key ); | |
| return this.files[ key ]; | |
| }, | |
| remove: function ( key ) { | |
| delete this.files[ key ]; | |
| }, | |
| clear: function () { | |
| this.files = {}; | |
| } | |
| }; | |
| /** | |
| * @author mrdoob / http://mrdoob.com/ | |
| */ | |
| function LoadingManager( onLoad, onProgress, onError ) { | |
| var scope = this; | |
| var isLoading = false; | |
| var itemsLoaded = 0; | |
| var itemsTotal = 0; | |
| var urlModifier = undefined; | |
| this.onStart = undefined; | |
| this.onLoad = onLoad; | |
| this.onProgress = onProgress; | |
| this.onError = onError; | |
| this.itemStart = function ( url ) { | |
| itemsTotal ++; | |
| if ( isLoading === false ) { | |
| if ( scope.onStart !== undefined ) { | |
| scope.onStart( url, itemsLoaded, itemsTotal ); | |
| } | |
| } | |
| isLoading = true; | |
| }; | |
| this.itemEnd = function ( url ) { | |
| itemsLoaded ++; | |
| if ( scope.onProgress !== undefined ) { | |
| scope.onProgress( url, itemsLoaded, itemsTotal ); | |
| } | |
| if ( itemsLoaded === itemsTotal ) { | |
| isLoading = false; | |
| if ( scope.onLoad !== undefined ) { | |
| scope.onLoad(); | |
| } | |
| } | |
| }; | |
| this.itemError = function ( url ) { | |
| if ( scope.onError !== undefined ) { | |
| scope.onError( url ); | |
| } | |
| }; | |
| this.resolveURL = function ( url ) { | |
| if ( urlModifier ) { | |
| return urlModifier( url ); | |
| } | |
| return url; | |
| }; | |
| this.setURLModifier = function ( transform ) { | |
| urlModifier = transform; | |
| return this; | |
| }; | |
| } | |
| var DefaultLoadingManager = new LoadingManager(); | |
| /** | |
| * @author mrdoob / http://mrdoob.com/ | |
| */ | |
| var loading = {}; | |
| function FileLoader( manager ) { | |
| this.manager = ( manager !== undefined ) ? manager : DefaultLoadingManager; | |
| } | |
| Object.assign( FileLoader.prototype, { | |
| load: function ( url, onLoad, onProgress, onError ) { | |
| if ( url === undefined ) url = ''; | |
| if ( this.path !== undefined ) url = this.path + url; | |
| url = this.manager.resolveURL( url ); | |
| var scope = this; | |
| var cached = Cache.get( url ); | |
| if ( cached !== undefined ) { | |
| scope.manager.itemStart( url ); | |
| setTimeout( function () { | |
| if ( onLoad ) onLoad( cached ); | |
| scope.manager.itemEnd( url ); | |
| }, 0 ); | |
| return cached; | |
| } | |
| // Check if request is duplicate | |
| if ( loading[ url ] !== undefined ) { | |
| loading[ url ].push( { | |
| onLoad: onLoad, | |
| onProgress: onProgress, | |
| onError: onError | |
| } ); | |
| return; | |
| } | |
| // Check for data: URI | |
| var dataUriRegex = /^data:(.*?)(;base64)?,(.*)$/; | |
| var dataUriRegexResult = url.match( dataUriRegex ); | |
| // Safari can not handle Data URIs through XMLHttpRequest so process manually | |
| if ( dataUriRegexResult ) { | |
| var mimeType = dataUriRegexResult[ 1 ]; | |
| var isBase64 = !! dataUriRegexResult[ 2 ]; | |
| var data = dataUriRegexResult[ 3 ]; | |
| data = window.decodeURIComponent( data ); | |
| if ( isBase64 ) data = window.atob( data ); | |
| try { | |
| var response; | |
| var responseType = ( this.responseType || '' ).toLowerCase(); | |
| switch ( responseType ) { | |
| case 'arraybuffer': | |
| case 'blob': | |
| var view = new Uint8Array( data.length ); | |
| for ( var i = 0; i < data.length; i ++ ) { | |
| view[ i ] = data.charCodeAt( i ); | |
| } | |
| if ( responseType === 'blob' ) { | |
| response = new Blob( [ view.buffer ], { type: mimeType } ); | |
| } else { | |
| response = view.buffer; | |
| } | |
| break; | |
| case 'document': | |
| var parser = new DOMParser(); | |
| response = parser.parseFromString( data, mimeType ); | |
| break; | |
| case 'json': | |
| response = JSON.parse( data ); | |
| break; | |
| default: // 'text' or other | |
| response = data; | |
| break; | |
| } | |
| // Wait for next browser tick like standard XMLHttpRequest event dispatching does | |
| window.setTimeout( function () { | |
| if ( onLoad ) onLoad( response ); | |
| scope.manager.itemEnd( url ); | |
| }, 0 ); | |
| } catch ( error ) { | |
| // Wait for next browser tick like standard XMLHttpRequest event dispatching does | |
| window.setTimeout( function () { | |
| if ( onError ) onError( error ); | |
| scope.manager.itemEnd( url ); | |
| scope.manager.itemError( url ); | |
| }, 0 ); | |
| } | |
| } else { | |
| // Initialise array for duplicate requests | |
| loading[ url ] = []; | |
| loading[ url ].push( { | |
| onLoad: onLoad, | |
| onProgress: onProgress, | |
| onError: onError | |
| } ); | |
| var request = new XMLHttpRequest(); | |
| request.open( 'GET', url, true ); | |
| request.addEventListener( 'load', function ( event ) { | |
| var response = this.response; | |
| Cache.add( url, response ); | |
| var callbacks = loading[ url ]; | |
| delete loading[ url ]; | |
| if ( this.status === 200 ) { | |
| for ( var i = 0, il = callbacks.length; i < il; i ++ ) { | |
| var callback = callbacks[ i ]; | |
| if ( callback.onLoad ) callback.onLoad( response ); | |
| } | |
| scope.manager.itemEnd( url ); | |
| } else if ( this.status === 0 ) { | |
| // Some browsers return HTTP Status 0 when using non-http protocol | |
| // e.g. 'file://' or 'data://'. Handle as success. | |
| console.warn( 'THREE.FileLoader: HTTP Status 0 received.' ); | |
| for ( var i = 0, il = callbacks.length; i < il; i ++ ) { | |
| var callback = callbacks[ i ]; | |
| if ( callback.onLoad ) callback.onLoad( response ); | |
| } | |
| scope.manager.itemEnd( url ); | |
| } else { | |
| for ( var i = 0, il = callbacks.length; i < il; i ++ ) { | |
| var callback = callbacks[ i ]; | |
| if ( callback.onError ) callback.onError( event ); | |
| } | |
| scope.manager.itemEnd( url ); | |
| scope.manager.itemError( url ); | |
| } | |
| }, false ); | |
| request.addEventListener( 'progress', function ( event ) { | |
| var callbacks = loading[ url ]; | |
| for ( var i = 0, il = callbacks.length; i < il; i ++ ) { | |
| var callback = callbacks[ i ]; | |
| if ( callback.onProgress ) callback.onProgress( event ); | |
| } | |
| }, false ); | |
| request.addEventListener( 'error', function ( event ) { | |
| var callbacks = loading[ url ]; | |
| delete loading[ url ]; | |
| for ( var i = 0, il = callbacks.length; i < il; i ++ ) { | |
| var callback = callbacks[ i ]; | |
| if ( callback.onError ) callback.onError( event ); | |
| } | |
| scope.manager.itemEnd( url ); | |
| scope.manager.itemError( url ); | |
| }, false ); | |
| if ( this.responseType !== undefined ) request.responseType = this.responseType; | |
| if ( this.withCredentials !== undefined ) request.withCredentials = this.withCredentials; | |
| if ( request.overrideMimeType ) request.overrideMimeType( this.mimeType !== undefined ? this.mimeType : 'text/plain' ); | |
| for ( var header in this.requestHeader ) { | |
| request.setRequestHeader( header, this.requestHeader[ header ] ); | |
| } | |
| request.send( null ); | |
| } | |
| scope.manager.itemStart( url ); | |
| return request; | |
| }, | |
| setPath: function ( value ) { | |
| this.path = value; | |
| return this; | |
| }, | |
| setResponseType: function ( value ) { | |
| this.responseType = value; | |
| return this; | |
| }, | |
| setWithCredentials: function ( value ) { | |
| this.withCredentials = value; | |
| return this; | |
| }, | |
| setMimeType: function ( value ) { | |
| this.mimeType = value; | |
| return this; | |
| }, | |
| setRequestHeader: function ( value ) { | |
| this.requestHeader = value; | |
| return this; | |
| } | |
| } ); | |
| /** | |
| * @author mrdoob / http://mrdoob.com/ | |
| * | |
| * Abstract Base class to block based textures loader (dds, pvr, ...) | |
| */ | |
| function CompressedTextureLoader( manager ) { | |
| this.manager = ( manager !== undefined ) ? manager : DefaultLoadingManager; | |
| // override in sub classes | |
| this._parser = null; | |
| } | |
| Object.assign( CompressedTextureLoader.prototype, { | |
| load: function ( url, onLoad, onProgress, onError ) { | |
| var scope = this; | |
| var images = []; | |
| var texture = new CompressedTexture(); | |
| texture.image = images; | |
| var loader = new FileLoader( this.manager ); | |
| loader.setPath( this.path ); | |
| loader.setResponseType( 'arraybuffer' ); | |
| function loadTexture( i ) { | |
| loader.load( url[ i ], function ( buffer ) { | |
| var texDatas = scope._parser( buffer, true ); | |
| images[ i ] = { | |
| width: texDatas.width, | |
| height: texDatas.height, | |
| format: texDatas.format, | |
| mipmaps: texDatas.mipmaps | |
| }; | |
| loaded += 1; | |
| if ( loaded === 6 ) { | |
| if ( texDatas.mipmapCount === 1 ) | |
| texture.minFilter = LinearFilter; | |
| texture.format = texDatas.format; | |
| texture.needsUpdate = true; | |
| if ( onLoad ) onLoad( texture ); | |
| } | |
| }, onProgress, onError ); | |
| } | |
| if ( Array.isArray( url ) ) { | |
| var loaded = 0; | |
| for ( var i = 0, il = url.length; i < il; ++ i ) { | |
| loadTexture( i ); | |
| } | |
| } else { | |
| // compressed cubemap texture stored in a single DDS file | |
| loader.load( url, function ( buffer ) { | |
| var texDatas = scope._parser( buffer, true ); | |
| if ( texDatas.isCubemap ) { | |
| var faces = texDatas.mipmaps.length / texDatas.mipmapCount; | |
| for ( var f = 0; f < faces; f ++ ) { | |
| images[ f ] = { mipmaps: [] }; | |
| for ( var i = 0; i < texDatas.mipmapCount; i ++ ) { | |
| images[ f ].mipmaps.push( texDatas.mipmaps[ f * texDatas.mipmapCount + i ] ); | |
| images[ f ].format = texDatas.format; | |
| images[ f ].width = texDatas.width; | |
| images[ f ].height = texDatas.height; | |
| } | |
| } | |
| } else { | |
| texture.image.width = texDatas.width; | |
| texture.image.height = texDatas.height; | |
| texture.mipmaps = texDatas.mipmaps; | |
| } | |
| if ( texDatas.mipmapCount === 1 ) { | |
| texture.minFilter = LinearFilter; | |
| } | |
| texture.format = texDatas.format; | |
| texture.needsUpdate = true; | |
| if ( onLoad ) onLoad( texture ); | |
| }, onProgress, onError ); | |
| } | |
| return texture; | |
| }, | |
| setPath: function ( value ) { | |
| this.path = value; | |
| return this; | |
| } | |
| } ); | |
| /** | |
| * @author Nikos M. / https://github.com/foo123/ | |
| * | |
| * Abstract Base class to load generic binary textures formats (rgbe, hdr, ...) | |
| */ | |
| function DataTextureLoader( manager ) { | |
| this.manager = ( manager !== undefined ) ? manager : DefaultLoadingManager; | |
| // override in sub classes | |
| this._parser = null; | |
| } | |
| Object.assign( DataTextureLoader.prototype, { | |
| load: function ( url, onLoad, onProgress, onError ) { | |
| var scope = this; | |
| var texture = new DataTexture(); | |
| var loader = new FileLoader( this.manager ); | |
| loader.setResponseType( 'arraybuffer' ); | |
| loader.load( url, function ( buffer ) { | |
| var texData = scope._parser( buffer ); | |
| if ( ! texData ) return; | |
| if ( undefined !== texData.image ) { | |
| texture.image = texData.image; | |
| } else if ( undefined !== texData.data ) { | |
| texture.image.width = texData.width; | |
| texture.image.height = texData.height; | |
| texture.image.data = texData.data; | |
| } | |
| texture.wrapS = undefined !== texData.wrapS ? texData.wrapS : ClampToEdgeWrapping; | |
| texture.wrapT = undefined !== texData.wrapT ? texData.wrapT : ClampToEdgeWrapping; | |
| texture.magFilter = undefined !== texData.magFilter ? texData.magFilter : LinearFilter; | |
| texture.minFilter = undefined !== texData.minFilter ? texData.minFilter : LinearMipMapLinearFilter; | |
| texture.anisotropy = undefined !== texData.anisotropy ? texData.anisotropy : 1; | |
| if ( undefined !== texData.format ) { | |
| texture.format = texData.format; | |
| } | |
| if ( undefined !== texData.type ) { | |
| texture.type = texData.type; | |
| } | |
| if ( undefined !== texData.mipmaps ) { | |
| texture.mipmaps = texData.mipmaps; | |
| } | |
| if ( 1 === texData.mipmapCount ) { | |
| texture.minFilter = LinearFilter; | |
| } | |
| texture.needsUpdate = true; | |
| if ( onLoad ) onLoad( texture, texData ); | |
| }, onProgress, onError ); | |
| return texture; | |
| } | |
| } ); | |
| /** | |
| * @author mrdoob / http://mrdoob.com/ | |
| */ | |
| function ImageLoader( manager ) { | |
| this.manager = ( manager !== undefined ) ? manager : DefaultLoadingManager; | |
| } | |
| Object.assign( ImageLoader.prototype, { | |
| crossOrigin: 'Anonymous', | |
| load: function ( url, onLoad, onProgress, onError ) { | |
| if ( url === undefined ) url = ''; | |
| if ( this.path !== undefined ) url = this.path + url; | |
| url = this.manager.resolveURL( url ); | |
| var scope = this; | |
| var cached = Cache.get( url ); | |
| if ( cached !== undefined ) { | |
| scope.manager.itemStart( url ); | |
| setTimeout( function () { | |
| if ( onLoad ) onLoad( cached ); | |
| scope.manager.itemEnd( url ); | |
| }, 0 ); | |
| return cached; | |
| } | |
| var image = document.createElementNS( 'http://www.w3.org/1999/xhtml', 'img' ); | |
| image.addEventListener( 'load', function () { | |
| Cache.add( url, this ); | |
| if ( onLoad ) onLoad( this ); | |
| scope.manager.itemEnd( url ); | |
| }, false ); | |
| /* | |
| image.addEventListener( 'progress', function ( event ) { | |
| if ( onProgress ) onProgress( event ); | |
| }, false ); | |
| */ | |
| image.addEventListener( 'error', function ( event ) { | |
| if ( onError ) onError( event ); | |
| scope.manager.itemEnd( url ); | |
| scope.manager.itemError( url ); | |
| }, false ); | |
| if ( url.substr( 0, 5 ) !== 'data:' ) { | |
| if ( this.crossOrigin !== undefined ) image.crossOrigin = this.crossOrigin; | |
| } | |
| scope.manager.itemStart( url ); | |
| image.src = url; | |
| return image; | |
| }, | |
| setCrossOrigin: function ( value ) { | |
| this.crossOrigin = value; | |
| return this; | |
| }, | |
| setPath: function ( value ) { | |
| this.path = value; | |
| return this; | |
| } | |
| } ); | |
| /** | |
| * @author mrdoob / http://mrdoob.com/ | |
| */ | |
| function CubeTextureLoader( manager ) { | |
| this.manager = ( manager !== undefined ) ? manager : DefaultLoadingManager; | |
| } | |
| Object.assign( CubeTextureLoader.prototype, { | |
| crossOrigin: 'Anonymous', | |
| load: function ( urls, onLoad, onProgress, onError ) { | |
| var texture = new CubeTexture(); | |
| var loader = new ImageLoader( this.manager ); | |
| loader.setCrossOrigin( this.crossOrigin ); | |
| loader.setPath( this.path ); | |
| var loaded = 0; | |
| function loadTexture( i ) { | |
| loader.load( urls[ i ], function ( image ) { | |
| texture.images[ i ] = image; | |
| loaded ++; | |
| if ( loaded === 6 ) { | |
| texture.needsUpdate = true; | |
| if ( onLoad ) onLoad( texture ); | |
| } | |
| }, undefined, onError ); | |
| } | |
| for ( var i = 0; i < urls.length; ++ i ) { | |
| loadTexture( i ); | |
| } | |
| return texture; | |
| }, | |
| setCrossOrigin: function ( value ) { | |
| this.crossOrigin = value; | |
| return this; | |
| }, | |
| setPath: function ( value ) { | |
| this.path = value; | |
| return this; | |
| } | |
| } ); | |
| /** | |
| * @author mrdoob / http://mrdoob.com/ | |
| */ | |
| function TextureLoader( manager ) { | |
| this.manager = ( manager !== undefined ) ? manager : DefaultLoadingManager; | |
| } | |
| Object.assign( TextureLoader.prototype, { | |
| crossOrigin: 'Anonymous', | |
| load: function ( url, onLoad, onProgress, onError ) { | |
| var texture = new Texture(); | |
| var loader = new ImageLoader( this.manager ); | |
| loader.setCrossOrigin( this.crossOrigin ); | |
| loader.setPath( this.path ); | |
| loader.load( url, function ( image ) { | |
| texture.image = image; | |
| // JPEGs can't have an alpha channel, so memory can be saved by storing them as RGB. | |
| var isJPEG = url.search( /\.(jpg|jpeg)$/ ) > 0 || url.search( /^data\:image\/jpeg/ ) === 0; | |
| texture.format = isJPEG ? RGBFormat : RGBAFormat; | |
| texture.needsUpdate = true; | |
| if ( onLoad !== undefined ) { | |
| onLoad( texture ); | |
| } | |
| }, onProgress, onError ); | |
| return texture; | |
| }, | |
| setCrossOrigin: function ( value ) { | |
| this.crossOrigin = value; | |
| return this; | |
| }, | |
| setPath: function ( value ) { | |
| this.path = value; | |
| return this; | |
| } | |
| } ); | |
| /** | |
| * @author zz85 / http://www.lab4games.net/zz85/blog | |
| * Extensible curve object | |
| * | |
| * Some common of curve methods: | |
| * .getPoint( t, optionalTarget ), .getTangent( t ) | |
| * .getPointAt( u, optionalTarget ), .getTangentAt( u ) | |
| * .getPoints(), .getSpacedPoints() | |
| * .getLength() | |
| * .updateArcLengths() | |
| * | |
| * This following curves inherit from THREE.Curve: | |
| * | |
| * -- 2D curves -- | |
| * THREE.ArcCurve | |
| * THREE.CubicBezierCurve | |
| * THREE.EllipseCurve | |
| * THREE.LineCurve | |
| * THREE.QuadraticBezierCurve | |
| * THREE.SplineCurve | |
| * | |
| * -- 3D curves -- | |
| * THREE.CatmullRomCurve3 | |
| * THREE.CubicBezierCurve3 | |
| * THREE.LineCurve3 | |
| * THREE.QuadraticBezierCurve3 | |
| * | |
| * A series of curves can be represented as a THREE.CurvePath. | |
| * | |
| **/ | |
| /************************************************************** | |
| * Abstract Curve base class | |
| **************************************************************/ | |
| function Curve() { | |
| this.type = 'Curve'; | |
| this.arcLengthDivisions = 200; | |
| } | |
| Object.assign( Curve.prototype, { | |
| // Virtual base class method to overwrite and implement in subclasses | |
| // - t [0 .. 1] | |
| getPoint: function ( /* t, optionalTarget */ ) { | |
| console.warn( 'THREE.Curve: .getPoint() not implemented.' ); | |
| return null; | |
| }, | |
| // Get point at relative position in curve according to arc length | |
| // - u [0 .. 1] | |
| getPointAt: function ( u, optionalTarget ) { | |
| var t = this.getUtoTmapping( u ); | |
| return this.getPoint( t, optionalTarget ); | |
| }, | |
| // Get sequence of points using getPoint( t ) | |
| getPoints: function ( divisions ) { | |
| if ( divisions === undefined ) divisions = 5; | |
| var points = []; | |
| for ( var d = 0; d <= divisions; d ++ ) { | |
| points.push( this.getPoint( d / divisions ) ); | |
| } | |
| return points; | |
| }, | |
| // Get sequence of points using getPointAt( u ) | |
| getSpacedPoints: function ( divisions ) { | |
| if ( divisions === undefined ) divisions = 5; | |
| var points = []; | |
| for ( var d = 0; d <= divisions; d ++ ) { | |
| points.push( this.getPointAt( d / divisions ) ); | |
| } | |
| return points; | |
| }, | |
| // Get total curve arc length | |
| getLength: function () { | |
| var lengths = this.getLengths(); | |
| return lengths[ lengths.length - 1 ]; | |
| }, | |
| // Get list of cumulative segment lengths | |
| getLengths: function ( divisions ) { | |
| if ( divisions === undefined ) divisions = this.arcLengthDivisions; | |
| if ( this.cacheArcLengths && | |
| ( this.cacheArcLengths.length === divisions + 1 ) && | |
| ! this.needsUpdate ) { | |
| return this.cacheArcLengths; | |
| } | |
| this.needsUpdate = false; | |
| var cache = []; | |
| var current, last = this.getPoint( 0 ); | |
| var p, sum = 0; | |
| cache.push( 0 ); | |
| for ( p = 1; p <= divisions; p ++ ) { | |
| current = this.getPoint( p / divisions ); | |
| sum += current.distanceTo( last ); | |
| cache.push( sum ); | |
| last = current; | |
| } | |
| this.cacheArcLengths = cache; | |
| return cache; // { sums: cache, sum: sum }; Sum is in the last element. | |
| }, | |
| updateArcLengths: function () { | |
| this.needsUpdate = true; | |
| this.getLengths(); | |
| }, | |
| // Given u ( 0 .. 1 ), get a t to find p. This gives you points which are equidistant | |
| getUtoTmapping: function ( u, distance ) { | |
| var arcLengths = this.getLengths(); | |
| var i = 0, il = arcLengths.length; | |
| var targetArcLength; // The targeted u distance value to get | |
| if ( distance ) { | |
| targetArcLength = distance; | |
| } else { | |
| targetArcLength = u * arcLengths[ il - 1 ]; | |
| } | |
| // binary search for the index with largest value smaller than target u distance | |
| var low = 0, high = il - 1, comparison; | |
| while ( low <= high ) { | |
| i = Math.floor( low + ( high - low ) / 2 ); // less likely to overflow, though probably not issue here, JS doesn't really have integers, all numbers are floats | |
| comparison = arcLengths[ i ] - targetArcLength; | |
| if ( comparison < 0 ) { | |
| low = i + 1; | |
| } else if ( comparison > 0 ) { | |
| high = i - 1; | |
| } else { | |
| high = i; | |
| break; | |
| // DONE | |
| } | |
| } | |
| i = high; | |
| if ( arcLengths[ i ] === targetArcLength ) { | |
| return i / ( il - 1 ); | |
| } | |
| // we could get finer grain at lengths, or use simple interpolation between two points | |
| var lengthBefore = arcLengths[ i ]; | |
| var lengthAfter = arcLengths[ i + 1 ]; | |
| var segmentLength = lengthAfter - lengthBefore; | |
| // determine where we are between the 'before' and 'after' points | |
| var segmentFraction = ( targetArcLength - lengthBefore ) / segmentLength; | |
| // add that fractional amount to t | |
| var t = ( i + segmentFraction ) / ( il - 1 ); | |
| return t; | |
| }, | |
| // Returns a unit vector tangent at t | |
| // In case any sub curve does not implement its tangent derivation, | |
| // 2 points a small delta apart will be used to find its gradient | |
| // which seems to give a reasonable approximation | |
| getTangent: function ( t ) { | |
| var delta = 0.0001; | |
| var t1 = t - delta; | |
| var t2 = t + delta; | |
| // Capping in case of danger | |
| if ( t1 < 0 ) t1 = 0; | |
| if ( t2 > 1 ) t2 = 1; | |
| var pt1 = this.getPoint( t1 ); | |
| var pt2 = this.getPoint( t2 ); | |
| var vec = pt2.clone().sub( pt1 ); | |
| return vec.normalize(); | |
| }, | |
| getTangentAt: function ( u ) { | |
| var t = this.getUtoTmapping( u ); | |
| return this.getTangent( t ); | |
| }, | |
| computeFrenetFrames: function ( segments, closed ) { | |
| // see http://www.cs.indiana.edu/pub/techreports/TR425.pdf | |
| var normal = new Vector3(); | |
| var tangents = []; | |
| var normals = []; | |
| var binormals = []; | |
| var vec = new Vector3(); | |
| var mat = new Matrix4(); | |
| var i, u, theta; | |
| // compute the tangent vectors for each segment on the curve | |
| for ( i = 0; i <= segments; i ++ ) { | |
| u = i / segments; | |
| tangents[ i ] = this.getTangentAt( u ); | |
| tangents[ i ].normalize(); | |
| } | |
| // select an initial normal vector perpendicular to the first tangent vector, | |
| // and in the direction of the minimum tangent xyz component | |
| normals[ 0 ] = new Vector3(); | |
| binormals[ 0 ] = new Vector3(); | |
| var min = Number.MAX_VALUE; | |
| var tx = Math.abs( tangents[ 0 ].x ); | |
| var ty = Math.abs( tangents[ 0 ].y ); | |
| var tz = Math.abs( tangents[ 0 ].z ); | |
| if ( tx <= min ) { | |
| min = tx; | |
| normal.set( 1, 0, 0 ); | |
| } | |
| if ( ty <= min ) { | |
| min = ty; | |
| normal.set( 0, 1, 0 ); | |
| } | |
| if ( tz <= min ) { | |
| normal.set( 0, 0, 1 ); | |
| } | |
| vec.crossVectors( tangents[ 0 ], normal ).normalize(); | |
| normals[ 0 ].crossVectors( tangents[ 0 ], vec ); | |
| binormals[ 0 ].crossVectors( tangents[ 0 ], normals[ 0 ] ); | |
| // compute the slowly-varying normal and binormal vectors for each segment on the curve | |
| for ( i = 1; i <= segments; i ++ ) { | |
| normals[ i ] = normals[ i - 1 ].clone(); | |
| binormals[ i ] = binormals[ i - 1 ].clone(); | |
| vec.crossVectors( tangents[ i - 1 ], tangents[ i ] ); | |
| if ( vec.length() > Number.EPSILON ) { | |
| vec.normalize(); | |
| theta = Math.acos( _Math.clamp( tangents[ i - 1 ].dot( tangents[ i ] ), - 1, 1 ) ); // clamp for floating pt errors | |
| normals[ i ].applyMatrix4( mat.makeRotationAxis( vec, theta ) ); | |
| } | |
| binormals[ i ].crossVectors( tangents[ i ], normals[ i ] ); | |
| } | |
| // if the curve is closed, postprocess the vectors so the first and last normal vectors are the same | |
| if ( closed === true ) { | |
| theta = Math.acos( _Math.clamp( normals[ 0 ].dot( normals[ segments ] ), - 1, 1 ) ); | |
| theta /= segments; | |
| if ( tangents[ 0 ].dot( vec.crossVectors( normals[ 0 ], normals[ segments ] ) ) > 0 ) { | |
| theta = - theta; | |
| } | |
| for ( i = 1; i <= segments; i ++ ) { | |
| // twist a little... | |
| normals[ i ].applyMatrix4( mat.makeRotationAxis( tangents[ i ], theta * i ) ); | |
| binormals[ i ].crossVectors( tangents[ i ], normals[ i ] ); | |
| } | |
| } | |
| return { | |
| tangents: tangents, | |
| normals: normals, | |
| binormals: binormals | |
| }; | |
| }, | |
| clone: function () { | |
| return new this.constructor().copy( this ); | |
| }, | |
| copy: function ( source ) { | |
| this.arcLengthDivisions = source.arcLengthDivisions; | |
| return this; | |
| }, | |
| toJSON: function () { | |
| var data = { | |
| metadata: { | |
| version: 4.5, | |
| type: 'Curve', | |
| generator: 'Curve.toJSON' | |
| } | |
| }; | |
| data.arcLengthDivisions = this.arcLengthDivisions; | |
| data.type = this.type; | |
| return data; | |
| }, | |
| fromJSON: function ( json ) { | |
| this.arcLengthDivisions = json.arcLengthDivisions; | |
| return this; | |
| } | |
| } ); | |
| function EllipseCurve( aX, aY, xRadius, yRadius, aStartAngle, aEndAngle, aClockwise, aRotation ) { | |
| Curve.call( this ); | |
| this.type = 'EllipseCurve'; | |
| this.aX = aX || 0; | |
| this.aY = aY || 0; | |
| this.xRadius = xRadius || 1; | |
| this.yRadius = yRadius || 1; | |
| this.aStartAngle = aStartAngle || 0; | |
| this.aEndAngle = aEndAngle || 2 * Math.PI; | |
| this.aClockwise = aClockwise || false; | |
| this.aRotation = aRotation || 0; | |
| } | |
| EllipseCurve.prototype = Object.create( Curve.prototype ); | |
| EllipseCurve.prototype.constructor = EllipseCurve; | |
| EllipseCurve.prototype.isEllipseCurve = true; | |
| EllipseCurve.prototype.getPoint = function ( t, optionalTarget ) { | |
| var point = optionalTarget || new Vector2(); | |
| var twoPi = Math.PI * 2; | |
| var deltaAngle = this.aEndAngle - this.aStartAngle; | |
| var samePoints = Math.abs( deltaAngle ) < Number.EPSILON; | |
| // ensures that deltaAngle is 0 .. 2 PI | |
| while ( deltaAngle < 0 ) deltaAngle += twoPi; | |
| while ( deltaAngle > twoPi ) deltaAngle -= twoPi; | |
| if ( deltaAngle < Number.EPSILON ) { | |
| if ( samePoints ) { | |
| deltaAngle = 0; | |
| } else { | |
| deltaAngle = twoPi; | |
| } | |
| } | |
| if ( this.aClockwise === true && ! samePoints ) { | |
| if ( deltaAngle === twoPi ) { | |
| deltaAngle = - twoPi; | |
| } else { | |
| deltaAngle = deltaAngle - twoPi; | |
| } | |
| } | |
| var angle = this.aStartAngle + t * deltaAngle; | |
| var x = this.aX + this.xRadius * Math.cos( angle ); | |
| var y = this.aY + this.yRadius * Math.sin( angle ); | |
| if ( this.aRotation !== 0 ) { | |
| var cos = Math.cos( this.aRotation ); | |
| var sin = Math.sin( this.aRotation ); | |
| var tx = x - this.aX; | |
| var ty = y - this.aY; | |
| // Rotate the point about the center of the ellipse. | |
| x = tx * cos - ty * sin + this.aX; | |
| y = tx * sin + ty * cos + this.aY; | |
| } | |
| return point.set( x, y ); | |
| }; | |
| EllipseCurve.prototype.copy = function ( source ) { | |
| Curve.prototype.copy.call( this, source ); | |
| this.aX = source.aX; | |
| this.aY = source.aY; | |
| this.xRadius = source.xRadius; | |
| this.yRadius = source.yRadius; | |
| this.aStartAngle = source.aStartAngle; | |
| this.aEndAngle = source.aEndAngle; | |
| this.aClockwise = source.aClockwise; | |
| this.aRotation = source.aRotation; | |
| return this; | |
| }; | |
| EllipseCurve.prototype.toJSON = function () { | |
| var data = Curve.prototype.toJSON.call( this ); | |
| data.aX = this.aX; | |
| data.aY = this.aY; | |
| data.xRadius = this.xRadius; | |
| data.yRadius = this.yRadius; | |
| data.aStartAngle = this.aStartAngle; | |
| data.aEndAngle = this.aEndAngle; | |
| data.aClockwise = this.aClockwise; | |
| data.aRotation = this.aRotation; | |
| return data; | |
| }; | |
| EllipseCurve.prototype.fromJSON = function ( json ) { | |
| Curve.prototype.fromJSON.call( this, json ); | |
| this.aX = json.aX; | |
| this.aY = json.aY; | |
| this.xRadius = json.xRadius; | |
| this.yRadius = json.yRadius; | |
| this.aStartAngle = json.aStartAngle; | |
| this.aEndAngle = json.aEndAngle; | |
| this.aClockwise = json.aClockwise; | |
| this.aRotation = json.aRotation; | |
| return this; | |
| }; | |
| function ArcCurve( aX, aY, aRadius, aStartAngle, aEndAngle, aClockwise ) { | |
| EllipseCurve.call( this, aX, aY, aRadius, aRadius, aStartAngle, aEndAngle, aClockwise ); | |
| this.type = 'ArcCurve'; | |
| } | |
| ArcCurve.prototype = Object.create( EllipseCurve.prototype ); | |
| ArcCurve.prototype.constructor = ArcCurve; | |
| ArcCurve.prototype.isArcCurve = true; | |
| /** | |
| * @author zz85 https://github.com/zz85 | |
| * | |
| * Centripetal CatmullRom Curve - which is useful for avoiding | |
| * cusps and self-intersections in non-uniform catmull rom curves. | |
| * http://www.cemyuksel.com/research/catmullrom_param/catmullrom.pdf | |
| * | |
| * curve.type accepts centripetal(default), chordal and catmullrom | |
| * curve.tension is used for catmullrom which defaults to 0.5 | |
| */ | |
| /* | |
| Based on an optimized c++ solution in | |
| - http://stackoverflow.com/questions/9489736/catmull-rom-curve-with-no-cusps-and-no-self-intersections/ | |
| - http://ideone.com/NoEbVM | |
| This CubicPoly class could be used for reusing some variables and calculations, | |
| but for three.js curve use, it could be possible inlined and flatten into a single function call | |
| which can be placed in CurveUtils. | |
| */ | |
| function CubicPoly() { | |
| var c0 = 0, c1 = 0, c2 = 0, c3 = 0; | |
| /* | |
| * Compute coefficients for a cubic polynomial | |
| * p(s) = c0 + c1*s + c2*s^2 + c3*s^3 | |
| * such that | |
| * p(0) = x0, p(1) = x1 | |
| * and | |
| * p'(0) = t0, p'(1) = t1. | |
| */ | |
| function init( x0, x1, t0, t1 ) { | |
| c0 = x0; | |
| c1 = t0; | |
| c2 = - 3 * x0 + 3 * x1 - 2 * t0 - t1; | |
| c3 = 2 * x0 - 2 * x1 + t0 + t1; | |
| } | |
| return { | |
| initCatmullRom: function ( x0, x1, x2, x3, tension ) { | |
| init( x1, x2, tension * ( x2 - x0 ), tension * ( x3 - x1 ) ); | |
| }, | |
| initNonuniformCatmullRom: function ( x0, x1, x2, x3, dt0, dt1, dt2 ) { | |
| // compute tangents when parameterized in [t1,t2] | |
| var t1 = ( x1 - x0 ) / dt0 - ( x2 - x0 ) / ( dt0 + dt1 ) + ( x2 - x1 ) / dt1; | |
| var t2 = ( x2 - x1 ) / dt1 - ( x3 - x1 ) / ( dt1 + dt2 ) + ( x3 - x2 ) / dt2; | |
| // rescale tangents for parametrization in [0,1] | |
| t1 *= dt1; | |
| t2 *= dt1; | |
| init( x1, x2, t1, t2 ); | |
| }, | |
| calc: function ( t ) { | |
| var t2 = t * t; | |
| var t3 = t2 * t; | |
| return c0 + c1 * t + c2 * t2 + c3 * t3; | |
| } | |
| }; | |
| } | |
| // | |
| var tmp = new Vector3(); | |
| var px = new CubicPoly(); | |
| var py = new CubicPoly(); | |
| var pz = new CubicPoly(); | |
| function CatmullRomCurve3( points, closed, curveType, tension ) { | |
| Curve.call( this ); | |
| this.type = 'CatmullRomCurve3'; | |
| this.points = points || []; | |
| this.closed = closed || false; | |
| this.curveType = curveType || 'centripetal'; | |
| this.tension = tension || 0.5; | |
| } | |
| CatmullRomCurve3.prototype = Object.create( Curve.prototype ); | |
| CatmullRomCurve3.prototype.constructor = CatmullRomCurve3; | |
| CatmullRomCurve3.prototype.isCatmullRomCurve3 = true; | |
| CatmullRomCurve3.prototype.getPoint = function ( t, optionalTarget ) { | |
| var point = optionalTarget || new Vector3(); | |
| var points = this.points; | |
| var l = points.length; | |
| var p = ( l - ( this.closed ? 0 : 1 ) ) * t; | |
| var intPoint = Math.floor( p ); | |
| var weight = p - intPoint; | |
| if ( this.closed ) { | |
| intPoint += intPoint > 0 ? 0 : ( Math.floor( Math.abs( intPoint ) / points.length ) + 1 ) * points.length; | |
| } else if ( weight === 0 && intPoint === l - 1 ) { | |
| intPoint = l - 2; | |
| weight = 1; | |
| } | |
| var p0, p1, p2, p3; // 4 points | |
| if ( this.closed || intPoint > 0 ) { | |
| p0 = points[ ( intPoint - 1 ) % l ]; | |
| } else { | |
| // extrapolate first point | |
| tmp.subVectors( points[ 0 ], points[ 1 ] ).add( points[ 0 ] ); | |
| p0 = tmp; | |
| } | |
| p1 = points[ intPoint % l ]; | |
| p2 = points[ ( intPoint + 1 ) % l ]; | |
| if ( this.closed || intPoint + 2 < l ) { | |
| p3 = points[ ( intPoint + 2 ) % l ]; | |
| } else { | |
| // extrapolate last point | |
| tmp.subVectors( points[ l - 1 ], points[ l - 2 ] ).add( points[ l - 1 ] ); | |
| p3 = tmp; | |
| } | |
| if ( this.curveType === 'centripetal' || this.curveType === 'chordal' ) { | |
| // init Centripetal / Chordal Catmull-Rom | |
| var pow = this.curveType === 'chordal' ? 0.5 : 0.25; | |
| var dt0 = Math.pow( p0.distanceToSquared( p1 ), pow ); | |
| var dt1 = Math.pow( p1.distanceToSquared( p2 ), pow ); | |
| var dt2 = Math.pow( p2.distanceToSquared( p3 ), pow ); | |
| // safety check for repeated points | |
| if ( dt1 < 1e-4 ) dt1 = 1.0; | |
| if ( dt0 < 1e-4 ) dt0 = dt1; | |
| if ( dt2 < 1e-4 ) dt2 = dt1; | |
| px.initNonuniformCatmullRom( p0.x, p1.x, p2.x, p3.x, dt0, dt1, dt2 ); | |
| py.initNonuniformCatmullRom( p0.y, p1.y, p2.y, p3.y, dt0, dt1, dt2 ); | |
| pz.initNonuniformCatmullRom( p0.z, p1.z, p2.z, p3.z, dt0, dt1, dt2 ); | |
| } else if ( this.curveType === 'catmullrom' ) { | |
| px.initCatmullRom( p0.x, p1.x, p2.x, p3.x, this.tension ); | |
| py.initCatmullRom( p0.y, p1.y, p2.y, p3.y, this.tension ); | |
| pz.initCatmullRom( p0.z, p1.z, p2.z, p3.z, this.tension ); | |
| } | |
| point.set( | |
| px.calc( weight ), | |
| py.calc( weight ), | |
| pz.calc( weight ) | |
| ); | |
| return point; | |
| }; | |
| CatmullRomCurve3.prototype.copy = function ( source ) { | |
| Curve.prototype.copy.call( this, source ); | |
| this.points = []; | |
| for ( var i = 0, l = source.points.length; i < l; i ++ ) { | |
| var point = source.points[ i ]; | |
| this.points.push( point.clone() ); | |
| } | |
| this.closed = source.closed; | |
| this.curveType = source.curveType; | |
| this.tension = source.tension; | |
| return this; | |
| }; | |
| CatmullRomCurve3.prototype.toJSON = function () { | |
| var data = Curve.prototype.toJSON.call( this ); | |
| data.points = []; | |
| for ( var i = 0, l = this.points.length; i < l; i ++ ) { | |
| var point = this.points[ i ]; | |
| data.points.push( point.toArray() ); | |
| } | |
| data.closed = this.closed; | |
| data.curveType = this.curveType; | |
| data.tension = this.tension; | |
| return data; | |
| }; | |
| CatmullRomCurve3.prototype.fromJSON = function ( json ) { | |
| Curve.prototype.fromJSON.call( this, json ); | |
| this.points = []; | |
| for ( var i = 0, l = json.points.length; i < l; i ++ ) { | |
| var point = json.points[ i ]; | |
| this.points.push( new Vector3().fromArray( point ) ); | |
| } | |
| this.closed = json.closed; | |
| this.curveType = json.curveType; | |
| this.tension = json.tension; | |
| return this; | |
| }; | |
| /** | |
| * @author zz85 / http://www.lab4games.net/zz85/blog | |
| * | |
| * Bezier Curves formulas obtained from | |
| * http://en.wikipedia.org/wiki/Bézier_curve | |
| */ | |
| function CatmullRom( t, p0, p1, p2, p3 ) { | |
| var v0 = ( p2 - p0 ) * 0.5; | |
| var v1 = ( p3 - p1 ) * 0.5; | |
| var t2 = t * t; | |
| var t3 = t * t2; | |
| return ( 2 * p1 - 2 * p2 + v0 + v1 ) * t3 + ( - 3 * p1 + 3 * p2 - 2 * v0 - v1 ) * t2 + v0 * t + p1; | |
| } | |
| // | |
| function QuadraticBezierP0( t, p ) { | |
| var k = 1 - t; | |
| return k * k * p; | |
| } | |
| function QuadraticBezierP1( t, p ) { | |
| return 2 * ( 1 - t ) * t * p; | |
| } | |
| function QuadraticBezierP2( t, p ) { | |
| return t * t * p; | |
| } | |
| function QuadraticBezier( t, p0, p1, p2 ) { | |
| return QuadraticBezierP0( t, p0 ) + QuadraticBezierP1( t, p1 ) + | |
| QuadraticBezierP2( t, p2 ); | |
| } | |
| // | |
| function CubicBezierP0( t, p ) { | |
| var k = 1 - t; | |
| return k * k * k * p; | |
| } | |
| function CubicBezierP1( t, p ) { | |
| var k = 1 - t; | |
| return 3 * k * k * t * p; | |
| } | |
| function CubicBezierP2( t, p ) { | |
| return 3 * ( 1 - t ) * t * t * p; | |
| } | |
| function CubicBezierP3( t, p ) { | |
| return t * t * t * p; | |
| } | |
| function CubicBezier( t, p0, p1, p2, p3 ) { | |
| return CubicBezierP0( t, p0 ) + CubicBezierP1( t, p1 ) + CubicBezierP2( t, p2 ) + | |
| CubicBezierP3( t, p3 ); | |
| } | |
| function CubicBezierCurve( v0, v1, v2, v3 ) { | |
| Curve.call( this ); | |
| this.type = 'CubicBezierCurve'; | |
| this.v0 = v0 || new Vector2(); | |
| this.v1 = v1 || new Vector2(); | |
| this.v2 = v2 || new Vector2(); | |
| this.v3 = v3 || new Vector2(); | |
| } | |
| CubicBezierCurve.prototype = Object.create( Curve.prototype ); | |
| CubicBezierCurve.prototype.constructor = CubicBezierCurve; | |
| CubicBezierCurve.prototype.isCubicBezierCurve = true; | |
| CubicBezierCurve.prototype.getPoint = function ( t, optionalTarget ) { | |
| var point = optionalTarget || new Vector2(); | |
| var v0 = this.v0, v1 = this.v1, v2 = this.v2, v3 = this.v3; | |
| point.set( | |
| CubicBezier( t, v0.x, v1.x, v2.x, v3.x ), | |
| CubicBezier( t, v0.y, v1.y, v2.y, v3.y ) | |
| ); | |
| return point; | |
| }; | |
| CubicBezierCurve.prototype.copy = function ( source ) { | |
| Curve.prototype.copy.call( this, source ); | |
| this.v0.copy( source.v0 ); | |
| this.v1.copy( source.v1 ); | |
| this.v2.copy( source.v2 ); | |
| this.v3.copy( source.v3 ); | |
| return this; | |
| }; | |
| CubicBezierCurve.prototype.toJSON = function () { | |
| var data = Curve.prototype.toJSON.call( this ); | |
| data.v0 = this.v0.toArray(); | |
| data.v1 = this.v1.toArray(); | |
| data.v2 = this.v2.toArray(); | |
| data.v3 = this.v3.toArray(); | |
| return data; | |
| }; | |
| CubicBezierCurve.prototype.fromJSON = function ( json ) { | |
| Curve.prototype.fromJSON.call( this, json ); | |
| this.v0.fromArray( json.v0 ); | |
| this.v1.fromArray( json.v1 ); | |
| this.v2.fromArray( json.v2 ); | |
| this.v3.fromArray( json.v3 ); | |
| return this; | |
| }; | |
| function CubicBezierCurve3( v0, v1, v2, v3 ) { | |
| Curve.call( this ); | |
| this.type = 'CubicBezierCurve3'; | |
| this.v0 = v0 || new Vector3(); | |
| this.v1 = v1 || new Vector3(); | |
| this.v2 = v2 || new Vector3(); | |
| this.v3 = v3 || new Vector3(); | |
| } | |
| CubicBezierCurve3.prototype = Object.create( Curve.prototype ); | |
| CubicBezierCurve3.prototype.constructor = CubicBezierCurve3; | |
| CubicBezierCurve3.prototype.isCubicBezierCurve3 = true; | |
| CubicBezierCurve3.prototype.getPoint = function ( t, optionalTarget ) { | |
| var point = optionalTarget || new Vector3(); | |
| var v0 = this.v0, v1 = this.v1, v2 = this.v2, v3 = this.v3; | |
| point.set( | |
| CubicBezier( t, v0.x, v1.x, v2.x, v3.x ), | |
| CubicBezier( t, v0.y, v1.y, v2.y, v3.y ), | |
| CubicBezier( t, v0.z, v1.z, v2.z, v3.z ) | |
| ); | |
| return point; | |
| }; | |
| CubicBezierCurve3.prototype.copy = function ( source ) { | |
| Curve.prototype.copy.call( this, source ); | |
| this.v0.copy( source.v0 ); | |
| this.v1.copy( source.v1 ); | |
| this.v2.copy( source.v2 ); | |
| this.v3.copy( source.v3 ); | |
| return this; | |
| }; | |
| CubicBezierCurve3.prototype.toJSON = function () { | |
| var data = Curve.prototype.toJSON.call( this ); | |
| data.v0 = this.v0.toArray(); | |
| data.v1 = this.v1.toArray(); | |
| data.v2 = this.v2.toArray(); | |
| data.v3 = this.v3.toArray(); | |
| return data; | |
| }; | |
| CubicBezierCurve3.prototype.fromJSON = function ( json ) { | |
| Curve.prototype.fromJSON.call( this, json ); | |
| this.v0.fromArray( json.v0 ); | |
| this.v1.fromArray( json.v1 ); | |
| this.v2.fromArray( json.v2 ); | |
| this.v3.fromArray( json.v3 ); | |
| return this; | |
| }; | |
| function LineCurve( v1, v2 ) { | |
| Curve.call( this ); | |
| this.type = 'LineCurve'; | |
| this.v1 = v1 || new Vector2(); | |
| this.v2 = v2 || new Vector2(); | |
| } | |
| LineCurve.prototype = Object.create( Curve.prototype ); | |
| LineCurve.prototype.constructor = LineCurve; | |
| LineCurve.prototype.isLineCurve = true; | |
| LineCurve.prototype.getPoint = function ( t, optionalTarget ) { | |
| var point = optionalTarget || new Vector2(); | |
| if ( t === 1 ) { | |
| point.copy( this.v2 ); | |
| } else { | |
| point.copy( this.v2 ).sub( this.v1 ); | |
| point.multiplyScalar( t ).add( this.v1 ); | |
| } | |
| return point; | |
| }; | |
| // Line curve is linear, so we can overwrite default getPointAt | |
| LineCurve.prototype.getPointAt = function ( u, optionalTarget ) { | |
| return this.getPoint( u, optionalTarget ); | |
| }; | |
| LineCurve.prototype.getTangent = function ( /* t */ ) { | |
| var tangent = this.v2.clone().sub( this.v1 ); | |
| return tangent.normalize(); | |
| }; | |
| LineCurve.prototype.copy = function ( source ) { | |
| Curve.prototype.copy.call( this, source ); | |
| this.v1.copy( source.v1 ); | |
| this.v2.copy( source.v2 ); | |
| return this; | |
| }; | |
| LineCurve.prototype.toJSON = function () { | |
| var data = Curve.prototype.toJSON.call( this ); | |
| data.v1 = this.v1.toArray(); | |
| data.v2 = this.v2.toArray(); | |
| return data; | |
| }; | |
| LineCurve.prototype.fromJSON = function ( json ) { | |
| Curve.prototype.fromJSON.call( this, json ); | |
| this.v1.fromArray( json.v1 ); | |
| this.v2.fromArray( json.v2 ); | |
| return this; | |
| }; | |
| function LineCurve3( v1, v2 ) { | |
| Curve.call( this ); | |
| this.type = 'LineCurve3'; | |
| this.v1 = v1 || new Vector3(); | |
| this.v2 = v2 || new Vector3(); | |
| } | |
| LineCurve3.prototype = Object.create( Curve.prototype ); | |
| LineCurve3.prototype.constructor = LineCurve3; | |
| LineCurve3.prototype.isLineCurve3 = true; | |
| LineCurve3.prototype.getPoint = function ( t, optionalTarget ) { | |
| var point = optionalTarget || new Vector3(); | |
| if ( t === 1 ) { | |
| point.copy( this.v2 ); | |
| } else { | |
| point.copy( this.v2 ).sub( this.v1 ); | |
| point.multiplyScalar( t ).add( this.v1 ); | |
| } | |
| return point; | |
| }; | |
| // Line curve is linear, so we can overwrite default getPointAt | |
| LineCurve3.prototype.getPointAt = function ( u, optionalTarget ) { | |
| return this.getPoint( u, optionalTarget ); | |
| }; | |
| LineCurve3.prototype.copy = function ( source ) { | |
| Curve.prototype.copy.call( this, source ); | |
| this.v1.copy( source.v1 ); | |
| this.v2.copy( source.v2 ); | |
| return this; | |
| }; | |
| LineCurve3.prototype.toJSON = function () { | |
| var data = Curve.prototype.toJSON.call( this ); | |
| data.v1 = this.v1.toArray(); | |
| data.v2 = this.v2.toArray(); | |
| return data; | |
| }; | |
| LineCurve3.prototype.fromJSON = function ( json ) { | |
| Curve.prototype.fromJSON.call( this, json ); | |
| this.v1.fromArray( json.v1 ); | |
| this.v2.fromArray( json.v2 ); | |
| return this; | |
| }; | |
| function QuadraticBezierCurve( v0, v1, v2 ) { | |
| Curve.call( this ); | |
| this.type = 'QuadraticBezierCurve'; | |
| this.v0 = v0 || new Vector2(); | |
| this.v1 = v1 || new Vector2(); | |
| this.v2 = v2 || new Vector2(); | |
| } | |
| QuadraticBezierCurve.prototype = Object.create( Curve.prototype ); | |
| QuadraticBezierCurve.prototype.constructor = QuadraticBezierCurve; | |
| QuadraticBezierCurve.prototype.isQuadraticBezierCurve = true; | |
| QuadraticBezierCurve.prototype.getPoint = function ( t, optionalTarget ) { | |
| var point = optionalTarget || new Vector2(); | |
| var v0 = this.v0, v1 = this.v1, v2 = this.v2; | |
| point.set( | |
| QuadraticBezier( t, v0.x, v1.x, v2.x ), | |
| QuadraticBezier( t, v0.y, v1.y, v2.y ) | |
| ); | |
| return point; | |
| }; | |
| QuadraticBezierCurve.prototype.copy = function ( source ) { | |
| Curve.prototype.copy.call( this, source ); | |
| this.v0.copy( source.v0 ); | |
| this.v1.copy( source.v1 ); | |
| this.v2.copy( source.v2 ); | |
| return this; | |
| }; | |
| QuadraticBezierCurve.prototype.toJSON = function () { | |
| var data = Curve.prototype.toJSON.call( this ); | |
| data.v0 = this.v0.toArray(); | |
| data.v1 = this.v1.toArray(); | |
| data.v2 = this.v2.toArray(); | |
| return data; | |
| }; | |
| QuadraticBezierCurve.prototype.fromJSON = function ( json ) { | |
| Curve.prototype.fromJSON.call( this, json ); | |
| this.v0.fromArray( json.v0 ); | |
| this.v1.fromArray( json.v1 ); | |
| this.v2.fromArray( json.v2 ); | |
| return this; | |
| }; | |
| function QuadraticBezierCurve3( v0, v1, v2 ) { | |
| Curve.call( this ); | |
| this.type = 'QuadraticBezierCurve3'; | |
| this.v0 = v0 || new Vector3(); | |
| this.v1 = v1 || new Vector3(); | |
| this.v2 = v2 || new Vector3(); | |
| } | |
| QuadraticBezierCurve3.prototype = Object.create( Curve.prototype ); | |
| QuadraticBezierCurve3.prototype.constructor = QuadraticBezierCurve3; | |
| QuadraticBezierCurve3.prototype.isQuadraticBezierCurve3 = true; | |
| QuadraticBezierCurve3.prototype.getPoint = function ( t, optionalTarget ) { | |
| var point = optionalTarget || new Vector3(); | |
| var v0 = this.v0, v1 = this.v1, v2 = this.v2; | |
| point.set( | |
| QuadraticBezier( t, v0.x, v1.x, v2.x ), | |
| QuadraticBezier( t, v0.y, v1.y, v2.y ), | |
| QuadraticBezier( t, v0.z, v1.z, v2.z ) | |
| ); | |
| return point; | |
| }; | |
| QuadraticBezierCurve3.prototype.copy = function ( source ) { | |
| Curve.prototype.copy.call( this, source ); | |
| this.v0.copy( source.v0 ); | |
| this.v1.copy( source.v1 ); | |
| this.v2.copy( source.v2 ); | |
| return this; | |
| }; | |
| QuadraticBezierCurve3.prototype.toJSON = function () { | |
| var data = Curve.prototype.toJSON.call( this ); | |
| data.v0 = this.v0.toArray(); | |
| data.v1 = this.v1.toArray(); | |
| data.v2 = this.v2.toArray(); | |
| return data; | |
| }; | |
| QuadraticBezierCurve3.prototype.fromJSON = function ( json ) { | |
| Curve.prototype.fromJSON.call( this, json ); | |
| this.v0.fromArray( json.v0 ); | |
| this.v1.fromArray( json.v1 ); | |
| this.v2.fromArray( json.v2 ); | |
| return this; | |
| }; | |
| function SplineCurve( points /* array of Vector2 */ ) { | |
| Curve.call( this ); | |
| this.type = 'SplineCurve'; | |
| this.points = points || []; | |
| } | |
| SplineCurve.prototype = Object.create( Curve.prototype ); | |
| SplineCurve.prototype.constructor = SplineCurve; | |
| SplineCurve.prototype.isSplineCurve = true; | |
| SplineCurve.prototype.getPoint = function ( t, optionalTarget ) { | |
| var point = optionalTarget || new Vector2(); | |
| var points = this.points; | |
| var p = ( points.length - 1 ) * t; | |
| var intPoint = Math.floor( p ); | |
| var weight = p - intPoint; | |
| var p0 = points[ intPoint === 0 ? intPoint : intPoint - 1 ]; | |
| var p1 = points[ intPoint ]; | |
| var p2 = points[ intPoint > points.length - 2 ? points.length - 1 : intPoint + 1 ]; | |
| var p3 = points[ intPoint > points.length - 3 ? points.length - 1 : intPoint + 2 ]; | |
| point.set( | |
| CatmullRom( weight, p0.x, p1.x, p2.x, p3.x ), | |
| CatmullRom( weight, p0.y, p1.y, p2.y, p3.y ) | |
| ); | |
| return point; | |
| }; | |
| SplineCurve.prototype.copy = function ( source ) { | |
| Curve.prototype.copy.call( this, source ); | |
| this.points = []; | |
| for ( var i = 0, l = source.points.length; i < l; i ++ ) { | |
| var point = source.points[ i ]; | |
| this.points.push( point.clone() ); | |
| } | |
| return this; | |
| }; | |
| SplineCurve.prototype.toJSON = function () { | |
| var data = Curve.prototype.toJSON.call( this ); | |
| data.points = []; | |
| for ( var i = 0, l = this.points.length; i < l; i ++ ) { | |
| var point = this.points[ i ]; | |
| data.points.push( point.toArray() ); | |
| } | |
| return data; | |
| }; | |
| SplineCurve.prototype.fromJSON = function ( json ) { | |
| Curve.prototype.fromJSON.call( this, json ); | |
| this.points = []; | |
| for ( var i = 0, l = json.points.length; i < l; i ++ ) { | |
| var point = json.points[ i ]; | |
| this.points.push( new Vector2().fromArray( point ) ); | |
| } | |
| return this; | |
| }; | |
| var Curves = Object.freeze({ | |
| ArcCurve: ArcCurve, | |
| CatmullRomCurve3: CatmullRomCurve3, | |
| CubicBezierCurve: CubicBezierCurve, | |
| CubicBezierCurve3: CubicBezierCurve3, | |
| EllipseCurve: EllipseCurve, | |
| LineCurve: LineCurve, | |
| LineCurve3: LineCurve3, | |
| QuadraticBezierCurve: QuadraticBezierCurve, | |
| QuadraticBezierCurve3: QuadraticBezierCurve3, | |
| SplineCurve: SplineCurve | |
| }); | |
| /** | |
| * @author zz85 / http://www.lab4games.net/zz85/blog | |
| * | |
| **/ | |
| /************************************************************** | |
| * Curved Path - a curve path is simply a array of connected | |
| * curves, but retains the api of a curve | |
| **************************************************************/ | |
| function CurvePath() { | |
| Curve.call( this ); | |
| this.type = 'CurvePath'; | |
| this.curves = []; | |
| this.autoClose = false; // Automatically closes the path | |
| } | |
| CurvePath.prototype = Object.assign( Object.create( Curve.prototype ), { | |
| constructor: CurvePath, | |
| add: function ( curve ) { | |
| this.curves.push( curve ); | |
| }, | |
| closePath: function () { | |
| // Add a line curve if start and end of lines are not connected | |
| var startPoint = this.curves[ 0 ].getPoint( 0 ); | |
| var endPoint = this.curves[ this.curves.length - 1 ].getPoint( 1 ); | |
| if ( ! startPoint.equals( endPoint ) ) { | |
| this.curves.push( new LineCurve( endPoint, startPoint ) ); | |
| } | |
| }, | |
| // To get accurate point with reference to | |
| // entire path distance at time t, | |
| // following has to be done: | |
| // 1. Length of each sub path have to be known | |
| // 2. Locate and identify type of curve | |
| // 3. Get t for the curve | |
| // 4. Return curve.getPointAt(t') | |
| getPoint: function ( t ) { | |
| var d = t * this.getLength(); | |
| var curveLengths = this.getCurveLengths(); | |
| var i = 0; | |
| // To think about boundaries points. | |
| while ( i < curveLengths.length ) { | |
| if ( curveLengths[ i ] >= d ) { | |
| var diff = curveLengths[ i ] - d; | |
| var curve = this.curves[ i ]; | |
| var segmentLength = curve.getLength(); | |
| var u = segmentLength === 0 ? 0 : 1 - diff / segmentLength; | |
| return curve.getPointAt( u ); | |
| } | |
| i ++; | |
| } | |
| return null; | |
| // loop where sum != 0, sum > d , sum+1 <d | |
| }, | |
| // We cannot use the default THREE.Curve getPoint() with getLength() because in | |
| // THREE.Curve, getLength() depends on getPoint() but in THREE.CurvePath | |
| // getPoint() depends on getLength | |
| getLength: function () { | |
| var lens = this.getCurveLengths(); | |
| return lens[ lens.length - 1 ]; | |
| }, | |
| // cacheLengths must be recalculated. | |
| updateArcLengths: function () { | |
| this.needsUpdate = true; | |
| this.cacheLengths = null; | |
| this.getCurveLengths(); | |
| }, | |
| // Compute lengths and cache them | |
| // We cannot overwrite getLengths() because UtoT mapping uses it. | |
| getCurveLengths: function () { | |
| // We use cache values if curves and cache array are same length | |
| if ( this.cacheLengths && this.cacheLengths.length === this.curves.length ) { | |
| return this.cacheLengths; | |
| } | |
| // Get length of sub-curve | |
| // Push sums into cached array | |
| var lengths = [], sums = 0; | |
| for ( var i = 0, l = this.curves.length; i < l; i ++ ) { | |
| sums += this.curves[ i ].getLength(); | |
| lengths.push( sums ); | |
| } | |
| this.cacheLengths = lengths; | |
| return lengths; | |
| }, | |
| getSpacedPoints: function ( divisions ) { | |
| if ( divisions === undefined ) divisions = 40; | |
| var points = []; | |
| for ( var i = 0; i <= divisions; i ++ ) { | |
| points.push( this.getPoint( i / divisions ) ); | |
| } | |
| if ( this.autoClose ) { | |
| points.push( points[ 0 ] ); | |
| } | |
| return points; | |
| }, | |
| getPoints: function ( divisions ) { | |
| divisions = divisions || 12; | |
| var points = [], last; | |
| for ( var i = 0, curves = this.curves; i < curves.length; i ++ ) { | |
| var curve = curves[ i ]; | |
| var resolution = ( curve && curve.isEllipseCurve ) ? divisions * 2 | |
| : ( curve && curve.isLineCurve ) ? 1 | |
| : ( curve && curve.isSplineCurve ) ? divisions * curve.points.length | |
| : divisions; | |
| var pts = curve.getPoints( resolution ); | |
| for ( var j = 0; j < pts.length; j ++ ) { | |
| var point = pts[ j ]; | |
| if ( last && last.equals( point ) ) continue; // ensures no consecutive points are duplicates | |
| points.push( point ); | |
| last = point; | |
| } | |
| } | |
| if ( this.autoClose && points.length > 1 && ! points[ points.length - 1 ].equals( points[ 0 ] ) ) { | |
| points.push( points[ 0 ] ); | |
| } | |
| return points; | |
| }, | |
| copy: function ( source ) { | |
| Curve.prototype.copy.call( this, source ); | |
| this.curves = []; | |
| for ( var i = 0, l = source.curves.length; i < l; i ++ ) { | |
| var curve = source.curves[ i ]; | |
| this.curves.push( curve.clone() ); | |
| } | |
| this.autoClose = source.autoClose; | |
| return this; | |
| }, | |
| toJSON: function () { | |
| var data = Curve.prototype.toJSON.call( this ); | |
| data.autoClose = this.autoClose; | |
| data.curves = []; | |
| for ( var i = 0, l = this.curves.length; i < l; i ++ ) { | |
| var curve = this.curves[ i ]; | |
| data.curves.push( curve.toJSON() ); | |
| } | |
| return data; | |
| }, | |
| fromJSON: function ( json ) { | |
| Curve.prototype.fromJSON.call( this, json ); | |
| this.autoClose = json.autoClose; | |
| this.curves = []; | |
| for ( var i = 0, l = json.curves.length; i < l; i ++ ) { | |
| var curve = json.curves[ i ]; | |
| this.curves.push( new Curves[ curve.type ]().fromJSON( curve ) ); | |
| } | |
| return this; | |
| } | |
| } ); | |
| /** | |
| * @author zz85 / http://www.lab4games.net/zz85/blog | |
| * Creates free form 2d path using series of points, lines or curves. | |
| **/ | |
| function Path( points ) { | |
| CurvePath.call( this ); | |
| this.type = 'Path'; | |
| this.currentPoint = new Vector2(); | |
| if ( points ) { | |
| this.setFromPoints( points ); | |
| } | |
| } | |
| Path.prototype = Object.assign( Object.create( CurvePath.prototype ), { | |
| constructor: Path, | |
| setFromPoints: function ( points ) { | |
| this.moveTo( points[ 0 ].x, points[ 0 ].y ); | |
| for ( var i = 1, l = points.length; i < l; i ++ ) { | |
| this.lineTo( points[ i ].x, points[ i ].y ); | |
| } | |
| }, | |
| moveTo: function ( x, y ) { | |
| this.currentPoint.set( x, y ); // TODO consider referencing vectors instead of copying? | |
| }, | |
| lineTo: function ( x, y ) { | |
| var curve = new LineCurve( this.currentPoint.clone(), new Vector2( x, y ) ); | |
| this.curves.push( curve ); | |
| this.currentPoint.set( x, y ); | |
| }, | |
| quadraticCurveTo: function ( aCPx, aCPy, aX, aY ) { | |
| var curve = new QuadraticBezierCurve( | |
| this.currentPoint.clone(), | |
| new Vector2( aCPx, aCPy ), | |
| new Vector2( aX, aY ) | |
| ); | |
| this.curves.push( curve ); | |
| this.currentPoint.set( aX, aY ); | |
| }, | |
| bezierCurveTo: function ( aCP1x, aCP1y, aCP2x, aCP2y, aX, aY ) { | |
| var curve = new CubicBezierCurve( | |
| this.currentPoint.clone(), | |
| new Vector2( aCP1x, aCP1y ), | |
| new Vector2( aCP2x, aCP2y ), | |
| new Vector2( aX, aY ) | |
| ); | |
| this.curves.push( curve ); | |
| this.currentPoint.set( aX, aY ); | |
| }, | |
| splineThru: function ( pts /*Array of Vector*/ ) { | |
| var npts = [ this.currentPoint.clone() ].concat( pts ); | |
| var curve = new SplineCurve( npts ); | |
| this.curves.push( curve ); | |
| this.currentPoint.copy( pts[ pts.length - 1 ] ); | |
| }, | |
| arc: function ( aX, aY, aRadius, aStartAngle, aEndAngle, aClockwise ) { | |
| var x0 = this.currentPoint.x; | |
| var y0 = this.currentPoint.y; | |
| this.absarc( aX + x0, aY + y0, aRadius, | |
| aStartAngle, aEndAngle, aClockwise ); | |
| }, | |
| absarc: function ( aX, aY, aRadius, aStartAngle, aEndAngle, aClockwise ) { | |
| this.absellipse( aX, aY, aRadius, aRadius, aStartAngle, aEndAngle, aClockwise ); | |
| }, | |
| ellipse: function ( aX, aY, xRadius, yRadius, aStartAngle, aEndAngle, aClockwise, aRotation ) { | |
| var x0 = this.currentPoint.x; | |
| var y0 = this.currentPoint.y; | |
| this.absellipse( aX + x0, aY + y0, xRadius, yRadius, aStartAngle, aEndAngle, aClockwise, aRotation ); | |
| }, | |
| absellipse: function ( aX, aY, xRadius, yRadius, aStartAngle, aEndAngle, aClockwise, aRotation ) { | |
| var curve = new EllipseCurve( aX, aY, xRadius, yRadius, aStartAngle, aEndAngle, aClockwise, aRotation ); | |
| if ( this.curves.length > 0 ) { | |
| // if a previous curve is present, attempt to join | |
| var firstPoint = curve.getPoint( 0 ); | |
| if ( ! firstPoint.equals( this.currentPoint ) ) { | |
| this.lineTo( firstPoint.x, firstPoint.y ); | |
| } | |
| } | |
| this.curves.push( curve ); | |
| var lastPoint = curve.getPoint( 1 ); | |
| this.currentPoint.copy( lastPoint ); | |
| }, | |
| copy: function ( source ) { | |
| CurvePath.prototype.copy.call( this, source ); | |
| this.currentPoint.copy( source.currentPoint ); | |
| return this; | |
| }, | |
| toJSON: function () { | |
| var data = CurvePath.prototype.toJSON.call( this ); | |
| data.currentPoint = this.currentPoint.toArray(); | |
| return data; | |
| }, | |
| fromJSON: function ( json ) { | |
| CurvePath.prototype.fromJSON.call( this, json ); | |
| this.currentPoint.fromArray( json.currentPoint ); | |
| return this; | |
| } | |
| } ); | |
| /** | |
| * @author zz85 / http://www.lab4games.net/zz85/blog | |
| * Defines a 2d shape plane using paths. | |
| **/ | |
| // STEP 1 Create a path. | |
| // STEP 2 Turn path into shape. | |
| // STEP 3 ExtrudeGeometry takes in Shape/Shapes | |
| // STEP 3a - Extract points from each shape, turn to vertices | |
| // STEP 3b - Triangulate each shape, add faces. | |
| function Shape( points ) { | |
| Path.call( this, points ); | |
| this.uuid = _Math.generateUUID(); | |
| this.type = 'Shape'; | |
| this.holes = []; | |
| } | |
| Shape.prototype = Object.assign( Object.create( Path.prototype ), { | |
| constructor: Shape, | |
| getPointsHoles: function ( divisions ) { | |
| var holesPts = []; | |
| for ( var i = 0, l = this.holes.length; i < l; i ++ ) { | |
| holesPts[ i ] = this.holes[ i ].getPoints( divisions ); | |
| } | |
| return holesPts; | |
| }, | |
| // get points of shape and holes (keypoints based on segments parameter) | |
| extractPoints: function ( divisions ) { | |
| return { | |
| shape: this.getPoints( divisions ), | |
| holes: this.getPointsHoles( divisions ) | |
| }; | |
| }, | |
| copy: function ( source ) { | |
| Path.prototype.copy.call( this, source ); | |
| this.holes = []; | |
| for ( var i = 0, l = source.holes.length; i < l; i ++ ) { | |
| var hole = source.holes[ i ]; | |
| this.holes.push( hole.clone() ); | |
| } | |
| return this; | |
| }, | |
| toJSON: function () { | |
| var data = Path.prototype.toJSON.call( this ); | |
| data.uuid = this.uuid; | |
| data.holes = []; | |
| for ( var i = 0, l = this.holes.length; i < l; i ++ ) { | |
| var hole = this.holes[ i ]; | |
| data.holes.push( hole.toJSON() ); | |
| } | |
| return data; | |
| }, | |
| fromJSON: function ( json ) { | |
| Path.prototype.fromJSON.call( this, json ); | |
| this.uuid = json.uuid; | |
| this.holes = []; | |
| for ( var i = 0, l = json.holes.length; i < l; i ++ ) { | |
| var hole = json.holes[ i ]; | |
| this.holes.push( new Path().fromJSON( hole ) ); | |
| } | |
| return this; | |
| } | |
| } ); | |
| /** | |
| * @author mrdoob / http://mrdoob.com/ | |
| * @author alteredq / http://alteredqualia.com/ | |
| */ | |
| function Light( color, intensity ) { | |
| Object3D.call( this ); | |
| this.type = 'Light'; | |
| this.color = new Color( color ); | |
| this.intensity = intensity !== undefined ? intensity : 1; | |
| this.receiveShadow = undefined; | |
| } | |
| Light.prototype = Object.assign( Object.create( Object3D.prototype ), { | |
| constructor: Light, | |
| isLight: true, | |
| copy: function ( source ) { | |
| Object3D.prototype.copy.call( this, source ); | |
| this.color.copy( source.color ); | |
| this.intensity = source.intensity; | |
| return this; | |
| }, | |
| toJSON: function ( meta ) { | |
| var data = Object3D.prototype.toJSON.call( this, meta ); | |
| data.object.color = this.color.getHex(); | |
| data.object.intensity = this.intensity; | |
| if ( this.groundColor !== undefined ) data.object.groundColor = this.groundColor.getHex(); | |
| if ( this.distance !== undefined ) data.object.distance = this.distance; | |
| if ( this.angle !== undefined ) data.object.angle = this.angle; | |
| if ( this.decay !== undefined ) data.object.decay = this.decay; | |
| if ( this.penumbra !== undefined ) data.object.penumbra = this.penumbra; | |
| if ( this.shadow !== undefined ) data.object.shadow = this.shadow.toJSON(); | |
| return data; | |
| } | |
| } ); | |
| /** | |
| * @author alteredq / http://alteredqualia.com/ | |
| */ | |
| function HemisphereLight( skyColor, groundColor, intensity ) { | |
| Light.call( this, skyColor, intensity ); | |
| this.type = 'HemisphereLight'; | |
| this.castShadow = undefined; | |
| this.position.copy( Object3D.DefaultUp ); | |
| this.updateMatrix(); | |
| this.groundColor = new Color( groundColor ); | |
| } | |
| HemisphereLight.prototype = Object.assign( Object.create( Light.prototype ), { | |
| constructor: HemisphereLight, | |
| isHemisphereLight: true, | |
| copy: function ( source ) { | |
| Light.prototype.copy.call( this, source ); | |
| this.groundColor.copy( source.groundColor ); | |
| return this; | |
| } | |
| } ); | |
| /** | |
| * @author mrdoob / http://mrdoob.com/ | |
| */ | |
| function LightShadow( camera ) { | |
| this.camera = camera; | |
| this.bias = 0; | |
| this.radius = 1; | |
| this.mapSize = new Vector2( 512, 512 ); | |
| this.map = null; | |
| this.matrix = new Matrix4(); | |
| } | |
| Object.assign( LightShadow.prototype, { | |
| copy: function ( source ) { | |
| this.camera = source.camera.clone(); | |
| this.bias = source.bias; | |
| this.radius = source.radius; | |
| this.mapSize.copy( source.mapSize ); | |
| return this; | |
| }, | |
| clone: function () { | |
| return new this.constructor().copy( this ); | |
| }, | |
| toJSON: function () { | |
| var object = {}; | |
| if ( this.bias !== 0 ) object.bias = this.bias; | |
| if ( this.radius !== 1 ) object.radius = this.radius; | |
| if ( this.mapSize.x !== 512 || this.mapSize.y !== 512 ) object.mapSize = this.mapSize.toArray(); | |
| object.camera = this.camera.toJSON( false ).object; | |
| delete object.camera.matrix; | |
| return object; | |
| } | |
| } ); | |
| /** | |
| * @author mrdoob / http://mrdoob.com/ | |
| */ | |
| function SpotLightShadow() { | |
| LightShadow.call( this, new PerspectiveCamera( 50, 1, 0.5, 500 ) ); | |
| } | |
| SpotLightShadow.prototype = Object.assign( Object.create( LightShadow.prototype ), { | |
| constructor: SpotLightShadow, | |
| isSpotLightShadow: true, | |
| update: function ( light ) { | |
| var camera = this.camera; | |
| var fov = _Math.RAD2DEG * 2 * light.angle; | |
| var aspect = this.mapSize.width / this.mapSize.height; | |
| var far = light.distance || camera.far; | |
| if ( fov !== camera.fov || aspect !== camera.aspect || far !== camera.far ) { | |
| camera.fov = fov; | |
| camera.aspect = aspect; | |
| camera.far = far; | |
| camera.updateProjectionMatrix(); | |
| } | |
| } | |
| } ); | |
| /** | |
| * @author alteredq / http://alteredqualia.com/ | |
| */ | |
| function SpotLight( color, intensity, distance, angle, penumbra, decay ) { | |
| Light.call( this, color, intensity ); | |
| this.type = 'SpotLight'; | |
| this.position.copy( Object3D.DefaultUp ); | |
| this.updateMatrix(); | |
| this.target = new Object3D(); | |
| Object.defineProperty( this, 'power', { | |
| get: function () { | |
| // intensity = power per solid angle. | |
| // ref: equation (17) from https://seblagarde.files.wordpress.com/2015/07/course_notes_moving_frostbite_to_pbr_v32.pdf | |
| return this.intensity * Math.PI; | |
| }, | |
| set: function ( power ) { | |
| // intensity = power per solid angle. | |
| // ref: equation (17) from https://seblagarde.files.wordpress.com/2015/07/course_notes_moving_frostbite_to_pbr_v32.pdf | |
| this.intensity = power / Math.PI; | |
| } | |
| } ); | |
| this.distance = ( distance !== undefined ) ? distance : 0; | |
| this.angle = ( angle !== undefined ) ? angle : Math.PI / 3; | |
| this.penumbra = ( penumbra !== undefined ) ? penumbra : 0; | |
| this.decay = ( decay !== undefined ) ? decay : 1; // for physically correct lights, should be 2. | |
| this.shadow = new SpotLightShadow(); | |
| } | |
| SpotLight.prototype = Object.assign( Object.create( Light.prototype ), { | |
| constructor: SpotLight, | |
| isSpotLight: true, | |
| copy: function ( source ) { | |
| Light.prototype.copy.call( this, source ); | |
| this.distance = source.distance; | |
| this.angle = source.angle; | |
| this.penumbra = source.penumbra; | |
| this.decay = source.decay; | |
| this.target = source.target.clone(); | |
| this.shadow = source.shadow.clone(); | |
| return this; | |
| } | |
| } ); | |
| /** | |
| * @author mrdoob / http://mrdoob.com/ | |
| */ | |
| function PointLight( color, intensity, distance, decay ) { | |
| Light.call( this, color, intensity ); | |
| this.type = 'PointLight'; | |
| Object.defineProperty( this, 'power', { | |
| get: function () { | |
| // intensity = power per solid angle. | |
| // ref: equation (15) from https://seblagarde.files.wordpress.com/2015/07/course_notes_moving_frostbite_to_pbr_v32.pdf | |
| return this.intensity * 4 * Math.PI; | |
| }, | |
| set: function ( power ) { | |
| // intensity = power per solid angle. | |
| // ref: equation (15) from https://seblagarde.files.wordpress.com/2015/07/course_notes_moving_frostbite_to_pbr_v32.pdf | |
| this.intensity = power / ( 4 * Math.PI ); | |
| } | |
| } ); | |
| this.distance = ( distance !== undefined ) ? distance : 0; | |
| this.decay = ( decay !== undefined ) ? decay : 1; // for physically correct lights, should be 2. | |
| this.shadow = new LightShadow( new PerspectiveCamera( 90, 1, 0.5, 500 ) ); | |
| } | |
| PointLight.prototype = Object.assign( Object.create( Light.prototype ), { | |
| constructor: PointLight, | |
| isPointLight: true, | |
| copy: function ( source ) { | |
| Light.prototype.copy.call( this, source ); | |
| this.distance = source.distance; | |
| this.decay = source.decay; | |
| this.shadow = source.shadow.clone(); | |
| return this; | |
| } | |
| } ); | |
| /** | |
| * @author mrdoob / http://mrdoob.com/ | |
| */ | |
| function DirectionalLightShadow( ) { | |
| LightShadow.call( this, new OrthographicCamera( - 5, 5, 5, - 5, 0.5, 500 ) ); | |
| } | |
| DirectionalLightShadow.prototype = Object.assign( Object.create( LightShadow.prototype ), { | |
| constructor: DirectionalLightShadow | |
| } ); | |
| /** | |
| * @author mrdoob / http://mrdoob.com/ | |
| * @author alteredq / http://alteredqualia.com/ | |
| */ | |
| function DirectionalLight( color, intensity ) { | |
| Light.call( this, color, intensity ); | |
| this.type = 'DirectionalLight'; | |
| this.position.copy( Object3D.DefaultUp ); | |
| this.updateMatrix(); | |
| this.target = new Object3D(); | |
| this.shadow = new DirectionalLightShadow(); | |
| } | |
| DirectionalLight.prototype = Object.assign( Object.create( Light.prototype ), { | |
| constructor: DirectionalLight, | |
| isDirectionalLight: true, | |
| copy: function ( source ) { | |
| Light.prototype.copy.call( this, source ); | |
| this.target = source.target.clone(); | |
| this.shadow = source.shadow.clone(); | |
| return this; | |
| } | |
| } ); | |
| /** | |
| * @author mrdoob / http://mrdoob.com/ | |
| */ | |
| function AmbientLight( color, intensity ) { | |
| Light.call( this, color, intensity ); | |
| this.type = 'AmbientLight'; | |
| this.castShadow = undefined; | |
| } | |
| AmbientLight.prototype = Object.assign( Object.create( Light.prototype ), { | |
| constructor: AmbientLight, | |
| isAmbientLight: true | |
| } ); | |
| /** | |
| * @author abelnation / http://github.com/abelnation | |
| */ | |
| function RectAreaLight( color, intensity, width, height ) { | |
| Light.call( this, color, intensity ); | |
| this.type = 'RectAreaLight'; | |
| this.width = ( width !== undefined ) ? width : 10; | |
| this.height = ( height !== undefined ) ? height : 10; | |
| } | |
| RectAreaLight.prototype = Object.assign( Object.create( Light.prototype ), { | |
| constructor: RectAreaLight, | |
| isRectAreaLight: true, | |
| copy: function ( source ) { | |
| Light.prototype.copy.call( this, source ); | |
| this.width = source.width; | |
| this.height = source.height; | |
| return this; | |
| }, | |
| toJSON: function ( meta ) { | |
| var data = Light.prototype.toJSON.call( this, meta ); | |
| data.object.width = this.width; | |
| data.object.height = this.height; | |
| return data; | |
| } | |
| } ); | |
| /** | |
| * | |
| * A Track that interpolates Strings | |
| * | |
| * | |
| * @author Ben Houston / http://clara.io/ | |
| * @author David Sarno / http://lighthaus.us/ | |
| * @author tschw | |
| */ | |
| function StringKeyframeTrack( name, times, values, interpolation ) { | |
| KeyframeTrack.call( this, name, times, values, interpolation ); | |
| } | |
| StringKeyframeTrack.prototype = Object.assign( Object.create( KeyframeTrack.prototype ), { | |
| constructor: StringKeyframeTrack, | |
| ValueTypeName: 'string', | |
| ValueBufferType: Array, | |
| DefaultInterpolation: InterpolateDiscrete, | |
| InterpolantFactoryMethodLinear: undefined, | |
| InterpolantFactoryMethodSmooth: undefined | |
| } ); | |
| /** | |
| * | |
| * A Track of Boolean keyframe values. | |
| * | |
| * | |
| * @author Ben Houston / http://clara.io/ | |
| * @author David Sarno / http://lighthaus.us/ | |
| * @author tschw | |
| */ | |
| function BooleanKeyframeTrack( name, times, values ) { | |
| KeyframeTrack.call( this, name, times, values ); | |
| } | |
| BooleanKeyframeTrack.prototype = Object.assign( Object.create( KeyframeTrack.prototype ), { | |
| constructor: BooleanKeyframeTrack, | |
| ValueTypeName: 'bool', | |
| ValueBufferType: Array, | |
| DefaultInterpolation: InterpolateDiscrete, | |
| InterpolantFactoryMethodLinear: undefined, | |
| InterpolantFactoryMethodSmooth: undefined | |
| // Note: Actually this track could have a optimized / compressed | |
| // representation of a single value and a custom interpolant that | |
| // computes "firstValue ^ isOdd( index )". | |
| } ); | |
| /** | |
| * Abstract base class of interpolants over parametric samples. | |
| * | |
| * The parameter domain is one dimensional, typically the time or a path | |
| * along a curve defined by the data. | |
| * | |
| * The sample values can have any dimensionality and derived classes may | |
| * apply special interpretations to the data. | |
| * | |
| * This class provides the interval seek in a Template Method, deferring | |
| * the actual interpolation to derived classes. | |
| * | |
| * Time complexity is O(1) for linear access crossing at most two points | |
| * and O(log N) for random access, where N is the number of positions. | |
| * | |
| * References: | |
| * | |
| * http://www.oodesign.com/template-method-pattern.html | |
| * | |
| * @author tschw | |
| */ | |
| function Interpolant( parameterPositions, sampleValues, sampleSize, resultBuffer ) { | |
| this.parameterPositions = parameterPositions; | |
| this._cachedIndex = 0; | |
| this.resultBuffer = resultBuffer !== undefined ? | |
| resultBuffer : new sampleValues.constructor( sampleSize ); | |
| this.sampleValues = sampleValues; | |
| this.valueSize = sampleSize; | |
| } | |
| Object.assign( Interpolant.prototype, { | |
| evaluate: function ( t ) { | |
| var pp = this.parameterPositions, | |
| i1 = this._cachedIndex, | |
| t1 = pp[ i1 ], | |
| t0 = pp[ i1 - 1 ]; | |
| validate_interval: { | |
| seek: { | |
| var right; | |
| linear_scan: { | |
| //- See http://jsperf.com/comparison-to-undefined/3 | |
| //- slower code: | |
| //- | |
| //- if ( t >= t1 || t1 === undefined ) { | |
| forward_scan: if ( ! ( t < t1 ) ) { | |
| for ( var giveUpAt = i1 + 2; ; ) { | |
| if ( t1 === undefined ) { | |
| if ( t < t0 ) break forward_scan; | |
| // after end | |
| i1 = pp.length; | |
| this._cachedIndex = i1; | |
| return this.afterEnd_( i1 - 1, t, t0 ); | |
| } | |
| if ( i1 === giveUpAt ) break; // this loop | |
| t0 = t1; | |
| t1 = pp[ ++ i1 ]; | |
| if ( t < t1 ) { | |
| // we have arrived at the sought interval | |
| break seek; | |
| } | |
| } | |
| // prepare binary search on the right side of the index | |
| right = pp.length; | |
| break linear_scan; | |
| } | |
| //- slower code: | |
| //- if ( t < t0 || t0 === undefined ) { | |
| if ( ! ( t >= t0 ) ) { | |
| // looping? | |
| var t1global = pp[ 1 ]; | |
| if ( t < t1global ) { | |
| i1 = 2; // + 1, using the scan for the details | |
| t0 = t1global; | |
| } | |
| // linear reverse scan | |
| for ( var giveUpAt = i1 - 2; ; ) { | |
| if ( t0 === undefined ) { | |
| // before start | |
| this._cachedIndex = 0; | |
| return this.beforeStart_( 0, t, t1 ); | |
| } | |
| if ( i1 === giveUpAt ) break; // this loop | |
| t1 = t0; | |
| t0 = pp[ -- i1 - 1 ]; | |
| if ( t >= t0 ) { | |
| // we have arrived at the sought interval | |
| break seek; | |
| } | |
| } | |
| // prepare binary search on the left side of the index | |
| right = i1; | |
| i1 = 0; | |
| break linear_scan; | |
| } | |
| // the interval is valid | |
| break validate_interval; | |
| } // linear scan | |
| // binary search | |
| while ( i1 < right ) { | |
| var mid = ( i1 + right ) >>> 1; | |
| if ( t < pp[ mid ] ) { | |
| right = mid; | |
| } else { | |
| i1 = mid + 1; | |
| } | |
| } | |
| t1 = pp[ i1 ]; | |
| t0 = pp[ i1 - 1 ]; | |
| // check boundary cases, again | |
| if ( t0 === undefined ) { | |
| this._cachedIndex = 0; | |
| return this.beforeStart_( 0, t, t1 ); | |
| } | |
| if ( t1 === undefined ) { | |
| i1 = pp.length; | |
| this._cachedIndex = i1; | |
| return this.afterEnd_( i1 - 1, t0, t ); | |
| } | |
| } // seek | |
| this._cachedIndex = i1; | |
| this.intervalChanged_( i1, t0, t1 ); | |
| } // validate_interval | |
| return this.interpolate_( i1, t0, t, t1 ); | |
| }, | |
| settings: null, // optional, subclass-specific settings structure | |
| // Note: The indirection allows central control of many interpolants. | |
| // --- Protected interface | |
| DefaultSettings_: {}, | |
| getSettings_: function () { | |
| return this.settings || this.DefaultSettings_; | |
| }, | |
| copySampleValue_: function ( index ) { | |
| // copies a sample value to the result buffer | |
| var result = this.resultBuffer, | |
| values = this.sampleValues, | |
| stride = this.valueSize, | |
| offset = index * stride; | |
| for ( var i = 0; i !== stride; ++ i ) { | |
| result[ i ] = values[ offset + i ]; | |
| } | |
| return result; | |
| }, | |
| // Template methods for derived classes: | |
| interpolate_: function ( /* i1, t0, t, t1 */ ) { | |
| throw new Error( 'call to abstract method' ); | |
| // implementations shall return this.resultBuffer | |
| }, | |
| intervalChanged_: function ( /* i1, t0, t1 */ ) { | |
| // empty | |
| } | |
| } ); | |
| //!\ DECLARE ALIAS AFTER assign prototype ! | |
| Object.assign( Interpolant.prototype, { | |
| //( 0, t, t0 ), returns this.resultBuffer | |
| beforeStart_: Interpolant.prototype.copySampleValue_, | |
| //( N-1, tN-1, t ), returns this.resultBuffer | |
| afterEnd_: Interpolant.prototype.copySampleValue_, | |
| } ); | |
| /** | |
| * Spherical linear unit quaternion interpolant. | |
| * | |
| * @author tschw | |
| */ | |
| function QuaternionLinearInterpolant( parameterPositions, sampleValues, sampleSize, resultBuffer ) { | |
| Interpolant.call( this, parameterPositions, sampleValues, sampleSize, resultBuffer ); | |
| } | |
| QuaternionLinearInterpolant.prototype = Object.assign( Object.create( Interpolant.prototype ), { | |
| constructor: QuaternionLinearInterpolant, | |
| interpolate_: function ( i1, t0, t, t1 ) { | |
| var result = this.resultBuffer, | |
| values = this.sampleValues, | |
| stride = this.valueSize, | |
| offset = i1 * stride, | |
| alpha = ( t - t0 ) / ( t1 - t0 ); | |
| for ( var end = offset + stride; offset !== end; offset += 4 ) { | |
| Quaternion.slerpFlat( result, 0, values, offset - stride, values, offset, alpha ); | |
| } | |
| return result; | |
| } | |
| } ); | |
| /** | |
| * | |
| * A Track of quaternion keyframe values. | |
| * | |
| * @author Ben Houston / http://clara.io/ | |
| * @author David Sarno / http://lighthaus.us/ | |
| * @author tschw | |
| */ | |
| function QuaternionKeyframeTrack( name, times, values, interpolation ) { | |
| KeyframeTrack.call( this, name, times, values, interpolation ); | |
| } | |
| QuaternionKeyframeTrack.prototype = Object.assign( Object.create( KeyframeTrack.prototype ), { | |
| constructor: QuaternionKeyframeTrack, | |
| ValueTypeName: 'quaternion', | |
| // ValueBufferType is inherited | |
| DefaultInterpolation: InterpolateLinear, | |
| InterpolantFactoryMethodLinear: function ( result ) { | |
| return new QuaternionLinearInterpolant( this.times, this.values, this.getValueSize(), result ); | |
| }, | |
| InterpolantFactoryMethodSmooth: undefined // not yet implemented | |
| } ); | |
| /** | |
| * | |
| * A Track of keyframe values that represent color. | |
| * | |
| * | |
| * @author Ben Houston / http://clara.io/ | |
| * @author David Sarno / http://lighthaus.us/ | |
| * @author tschw | |
| */ | |
| function ColorKeyframeTrack( name, times, values, interpolation ) { | |
| KeyframeTrack.call( this, name, times, values, interpolation ); | |
| } | |
| ColorKeyframeTrack.prototype = Object.assign( Object.create( KeyframeTrack.prototype ), { | |
| constructor: ColorKeyframeTrack, | |
| ValueTypeName: 'color' | |
| // ValueBufferType is inherited | |
| // DefaultInterpolation is inherited | |
| // Note: Very basic implementation and nothing special yet. | |
| // However, this is the place for color space parameterization. | |
| } ); | |
| /** | |
| * | |
| * A Track of numeric keyframe values. | |
| * | |
| * @author Ben Houston / http://clara.io/ | |
| * @author David Sarno / http://lighthaus.us/ | |
| * @author tschw | |
| */ | |
| function NumberKeyframeTrack( name, times, values, interpolation ) { | |
| KeyframeTrack.call( this, name, times, values, interpolation ); | |
| } | |
| NumberKeyframeTrack.prototype = Object.assign( Object.create( KeyframeTrack.prototype ), { | |
| constructor: NumberKeyframeTrack, | |
| ValueTypeName: 'number' | |
| // ValueBufferType is inherited | |
| // DefaultInterpolation is inherited | |
| } ); | |
| /** | |
| * Fast and simple cubic spline interpolant. | |
| * | |
| * It was derived from a Hermitian construction setting the first derivative | |
| * at each sample position to the linear slope between neighboring positions | |
| * over their parameter interval. | |
| * | |
| * @author tschw | |
| */ | |
| function CubicInterpolant( parameterPositions, sampleValues, sampleSize, resultBuffer ) { | |
| Interpolant.call( this, parameterPositions, sampleValues, sampleSize, resultBuffer ); | |
| this._weightPrev = - 0; | |
| this._offsetPrev = - 0; | |
| this._weightNext = - 0; | |
| this._offsetNext = - 0; | |
| } | |
| CubicInterpolant.prototype = Object.assign( Object.create( Interpolant.prototype ), { | |
| constructor: CubicInterpolant, | |
| DefaultSettings_: { | |
| endingStart: ZeroCurvatureEnding, | |
| endingEnd: ZeroCurvatureEnding | |
| }, | |
| intervalChanged_: function ( i1, t0, t1 ) { | |
| var pp = this.parameterPositions, | |
| iPrev = i1 - 2, | |
| iNext = i1 + 1, | |
| tPrev = pp[ iPrev ], | |
| tNext = pp[ iNext ]; | |
| if ( tPrev === undefined ) { | |
| switch ( this.getSettings_().endingStart ) { | |
| case ZeroSlopeEnding: | |
| // f'(t0) = 0 | |
| iPrev = i1; | |
| tPrev = 2 * t0 - t1; | |
| break; | |
| case WrapAroundEnding: | |
| // use the other end of the curve | |
| iPrev = pp.length - 2; | |
| tPrev = t0 + pp[ iPrev ] - pp[ iPrev + 1 ]; | |
| break; | |
| default: // ZeroCurvatureEnding | |
| // f''(t0) = 0 a.k.a. Natural Spline | |
| iPrev = i1; | |
| tPrev = t1; | |
| } | |
| } | |
| if ( tNext === undefined ) { | |
| switch ( this.getSettings_().endingEnd ) { | |
| case ZeroSlopeEnding: | |
| // f'(tN) = 0 | |
| iNext = i1; | |
| tNext = 2 * t1 - t0; | |
| break; | |
| case WrapAroundEnding: | |
| // use the other end of the curve | |
| iNext = 1; | |
| tNext = t1 + pp[ 1 ] - pp[ 0 ]; | |
| break; | |
| default: // ZeroCurvatureEnding | |
| // f''(tN) = 0, a.k.a. Natural Spline | |
| iNext = i1 - 1; | |
| tNext = t0; | |
| } | |
| } | |
| var halfDt = ( t1 - t0 ) * 0.5, | |
| stride = this.valueSize; | |
| this._weightPrev = halfDt / ( t0 - tPrev ); | |
| this._weightNext = halfDt / ( tNext - t1 ); | |
| this._offsetPrev = iPrev * stride; | |
| this._offsetNext = iNext * stride; | |
| }, | |
| interpolate_: function ( i1, t0, t, t1 ) { | |
| var result = this.resultBuffer, | |
| values = this.sampleValues, | |
| stride = this.valueSize, | |
| o1 = i1 * stride, o0 = o1 - stride, | |
| oP = this._offsetPrev, oN = this._offsetNext, | |
| wP = this._weightPrev, wN = this._weightNext, | |
| p = ( t - t0 ) / ( t1 - t0 ), | |
| pp = p * p, | |
| ppp = pp * p; | |
| // evaluate polynomials | |
| var sP = - wP * ppp + 2 * wP * pp - wP * p; | |
| var s0 = ( 1 + wP ) * ppp + ( - 1.5 - 2 * wP ) * pp + ( - 0.5 + wP ) * p + 1; | |
| var s1 = ( - 1 - wN ) * ppp + ( 1.5 + wN ) * pp + 0.5 * p; | |
| var sN = wN * ppp - wN * pp; | |
| // combine data linearly | |
| for ( var i = 0; i !== stride; ++ i ) { | |
| result[ i ] = | |
| sP * values[ oP + i ] + | |
| s0 * values[ o0 + i ] + | |
| s1 * values[ o1 + i ] + | |
| sN * values[ oN + i ]; | |
| } | |
| return result; | |
| } | |
| } ); | |
| /** | |
| * @author tschw | |
| */ | |
| function LinearInterpolant( parameterPositions, sampleValues, sampleSize, resultBuffer ) { | |
| Interpolant.call( this, parameterPositions, sampleValues, sampleSize, resultBuffer ); | |
| } | |
| LinearInterpolant.prototype = Object.assign( Object.create( Interpolant.prototype ), { | |
| constructor: LinearInterpolant, | |
| interpolate_: function ( i1, t0, t, t1 ) { | |
| var result = this.resultBuffer, | |
| values = this.sampleValues, | |
| stride = this.valueSize, | |
| offset1 = i1 * stride, | |
| offset0 = offset1 - stride, | |
| weight1 = ( t - t0 ) / ( t1 - t0 ), | |
| weight0 = 1 - weight1; | |
| for ( var i = 0; i !== stride; ++ i ) { | |
| result[ i ] = | |
| values[ offset0 + i ] * weight0 + | |
| values[ offset1 + i ] * weight1; | |
| } | |
| return result; | |
| } | |
| } ); | |
| /** | |
| * | |
| * Interpolant that evaluates to the sample value at the position preceeding | |
| * the parameter. | |
| * | |
| * @author tschw | |
| */ | |
| function DiscreteInterpolant( parameterPositions, sampleValues, sampleSize, resultBuffer ) { | |
| Interpolant.call( this, parameterPositions, sampleValues, sampleSize, resultBuffer ); | |
| } | |
| DiscreteInterpolant.prototype = Object.assign( Object.create( Interpolant.prototype ), { | |
| constructor: DiscreteInterpolant, | |
| interpolate_: function ( i1 /*, t0, t, t1 */ ) { | |
| return this.copySampleValue_( i1 - 1 ); | |
| } | |
| } ); | |
| /** | |
| * @author tschw | |
| * @author Ben Houston / http://clara.io/ | |
| * @author David Sarno / http://lighthaus.us/ | |
| */ | |
| var AnimationUtils = { | |
| // same as Array.prototype.slice, but also works on typed arrays | |
| arraySlice: function ( array, from, to ) { | |
| if ( AnimationUtils.isTypedArray( array ) ) { | |
| // in ios9 array.subarray(from, undefined) will return empty array | |
| // but array.subarray(from) or array.subarray(from, len) is correct | |
| return new array.constructor( array.subarray( from, to !== undefined ? to : array.length ) ); | |
| } | |
| return array.slice( from, to ); | |
| }, | |
| // converts an array to a specific type | |
| convertArray: function ( array, type, forceClone ) { | |
| if ( ! array || // let 'undefined' and 'null' pass | |
| ! forceClone && array.constructor === type ) return array; | |
| if ( typeof type.BYTES_PER_ELEMENT === 'number' ) { | |
| return new type( array ); // create typed array | |
| } | |
| return Array.prototype.slice.call( array ); // create Array | |
| }, | |
| isTypedArray: function ( object ) { | |
| return ArrayBuffer.isView( object ) && | |
| ! ( object instanceof DataView ); | |
| }, | |
| // returns an array by which times and values can be sorted | |
| getKeyframeOrder: function ( times ) { | |
| function compareTime( i, j ) { | |
| return times[ i ] - times[ j ]; | |
| } | |
| var n = times.length; | |
| var result = new Array( n ); | |
| for ( var i = 0; i !== n; ++ i ) result[ i ] = i; | |
| result.sort( compareTime ); | |
| return result; | |
| }, | |
| // uses the array previously returned by 'getKeyframeOrder' to sort data | |
| sortedArray: function ( values, stride, order ) { | |
| var nValues = values.length; | |
| var result = new values.constructor( nValues ); | |
| for ( var i = 0, dstOffset = 0; dstOffset !== nValues; ++ i ) { | |
| var srcOffset = order[ i ] * stride; | |
| for ( var j = 0; j !== stride; ++ j ) { | |
| result[ dstOffset ++ ] = values[ srcOffset + j ]; | |
| } | |
| } | |
| return result; | |
| }, | |
| // function for parsing AOS keyframe formats | |
| flattenJSON: function ( jsonKeys, times, values, valuePropertyName ) { | |
| var i = 1, key = jsonKeys[ 0 ]; | |
| while ( key !== undefined && key[ valuePropertyName ] === undefined ) { | |
| key = jsonKeys[ i ++ ]; | |
| } | |
| if ( key === undefined ) return; // no data | |
| var value = key[ valuePropertyName ]; | |
| if ( value === undefined ) return; // no data | |
| if ( Array.isArray( value ) ) { | |
| do { | |
| value = key[ valuePropertyName ]; | |
| if ( value !== undefined ) { | |
| times.push( key.time ); | |
| values.push.apply( values, value ); // push all elements | |
| } | |
| key = jsonKeys[ i ++ ]; | |
| } while ( key !== undefined ); | |
| } else if ( value.toArray !== undefined ) { | |
| // ...assume THREE.Math-ish | |
| do { | |
| value = key[ valuePropertyName ]; | |
| if ( value !== undefined ) { | |
| times.push( key.time ); | |
| value.toArray( values, values.length ); | |
| } | |
| key = jsonKeys[ i ++ ]; | |
| } while ( key !== undefined ); | |
| } else { | |
| // otherwise push as-is | |
| do { | |
| value = key[ valuePropertyName ]; | |
| if ( value !== undefined ) { | |
| times.push( key.time ); | |
| values.push( value ); | |
| } | |
| key = jsonKeys[ i ++ ]; | |
| } while ( key !== undefined ); | |
| } | |
| } | |
| }; | |
| /** | |
| * | |
| * A timed sequence of keyframes for a specific property. | |
| * | |
| * | |
| * @author Ben Houston / http://clara.io/ | |
| * @author David Sarno / http://lighthaus.us/ | |
| * @author tschw | |
| */ | |
| function KeyframeTrack( name, times, values, interpolation ) { | |
| if ( name === undefined ) throw new Error( 'THREE.KeyframeTrack: track name is undefined' ); | |
| if ( times === undefined || times.length === 0 ) throw new Error( 'THREE.KeyframeTrack: no keyframes in track named ' + name ); | |
| this.name = name; | |
| this.times = AnimationUtils.convertArray( times, this.TimeBufferType ); | |
| this.values = AnimationUtils.convertArray( values, this.ValueBufferType ); | |
| this.setInterpolation( interpolation || this.DefaultInterpolation ); | |
| this.validate(); | |
| this.optimize(); | |
| } | |
| // Static methods: | |
| Object.assign( KeyframeTrack, { | |
| // Serialization (in static context, because of constructor invocation | |
| // and automatic invocation of .toJSON): | |
| parse: function ( json ) { | |
| if ( json.type === undefined ) { | |
| throw new Error( 'THREE.KeyframeTrack: track type undefined, can not parse' ); | |
| } | |
| var trackType = KeyframeTrack._getTrackTypeForValueTypeName( json.type ); | |
| if ( json.times === undefined ) { | |
| var times = [], values = []; | |
| AnimationUtils.flattenJSON( json.keys, times, values, 'value' ); | |
| json.times = times; | |
| json.values = values; | |
| } | |
| // derived classes can define a static parse method | |
| if ( trackType.parse !== undefined ) { | |
| return trackType.parse( json ); | |
| } else { | |
| // by default, we assume a constructor compatible with the base | |
| return new trackType( json.name, json.times, json.values, json.interpolation ); | |
| } | |
| }, | |
| toJSON: function ( track ) { | |
| var trackType = track.constructor; | |
| var json; | |
| // derived classes can define a static toJSON method | |
| if ( trackType.toJSON !== undefined ) { | |
| json = trackType.toJSON( track ); | |
| } else { | |
| // by default, we assume the data can be serialized as-is | |
| json = { | |
| 'name': track.name, | |
| 'times': AnimationUtils.convertArray( track.times, Array ), | |
| 'values': AnimationUtils.convertArray( track.values, Array ) | |
| }; | |
| var interpolation = track.getInterpolation(); | |
| if ( interpolation !== track.DefaultInterpolation ) { | |
| json.interpolation = interpolation; | |
| } | |
| } | |
| json.type = track.ValueTypeName; // mandatory | |
| return json; | |
| }, | |
| _getTrackTypeForValueTypeName: function ( typeName ) { | |
| switch ( typeName.toLowerCase() ) { | |
| case 'scalar': | |
| case 'double': | |
| case 'float': | |
| case 'number': | |
| case 'integer': | |
| return NumberKeyframeTrack; | |
| case 'vector': | |
| case 'vector2': | |
| case 'vector3': | |
| case 'vector4': | |
| return VectorKeyframeTrack; | |
| case 'color': | |
| return ColorKeyframeTrack; | |
| case 'quaternion': | |
| return QuaternionKeyframeTrack; | |
| case 'bool': | |
| case 'boolean': | |
| return BooleanKeyframeTrack; | |
| case 'string': | |
| return StringKeyframeTrack; | |
| } | |
| throw new Error( 'THREE.KeyframeTrack: Unsupported typeName: ' + typeName ); | |
| } | |
| } ); | |
| Object.assign( KeyframeTrack.prototype, { | |
| constructor: KeyframeTrack, | |
| TimeBufferType: Float32Array, | |
| ValueBufferType: Float32Array, | |
| DefaultInterpolation: InterpolateLinear, | |
| InterpolantFactoryMethodDiscrete: function ( result ) { | |
| return new DiscreteInterpolant( this.times, this.values, this.getValueSize(), result ); | |
| }, | |
| InterpolantFactoryMethodLinear: function ( result ) { | |
| return new LinearInterpolant( this.times, this.values, this.getValueSize(), result ); | |
| }, | |
| InterpolantFactoryMethodSmooth: function ( result ) { | |
| return new CubicInterpolant( this.times, this.values, this.getValueSize(), result ); | |
| }, | |
| setInterpolation: function ( interpolation ) { | |
| var factoryMethod; | |
| switch ( interpolation ) { | |
| case InterpolateDiscrete: | |
| factoryMethod = this.InterpolantFactoryMethodDiscrete; | |
| break; | |
| case InterpolateLinear: | |
| factoryMethod = this.InterpolantFactoryMethodLinear; | |
| break; | |
| case InterpolateSmooth: | |
| factoryMethod = this.InterpolantFactoryMethodSmooth; | |
| break; | |
| } | |
| if ( factoryMethod === undefined ) { | |
| var message = "unsupported interpolation for " + | |
| this.ValueTypeName + " keyframe track named " + this.name; | |
| if ( this.createInterpolant === undefined ) { | |
| // fall back to default, unless the default itself is messed up | |
| if ( interpolation !== this.DefaultInterpolation ) { | |
| this.setInterpolation( this.DefaultInterpolation ); | |
| } else { | |
| throw new Error( message ); // fatal, in this case | |
| } | |
| } | |
| console.warn( 'THREE.KeyframeTrack:', message ); | |
| return; | |
| } | |
| this.createInterpolant = factoryMethod; | |
| }, | |
| getInterpolation: function () { | |
| switch ( this.createInterpolant ) { | |
| case this.InterpolantFactoryMethodDiscrete: | |
| return InterpolateDiscrete; | |
| case this.InterpolantFactoryMethodLinear: | |
| return InterpolateLinear; | |
| case this.InterpolantFactoryMethodSmooth: | |
| return InterpolateSmooth; | |
| } | |
| }, | |
| getValueSize: function () { | |
| return this.values.length / this.times.length; | |
| }, | |
| // move all keyframes either forwards or backwards in time | |
| shift: function ( timeOffset ) { | |
| if ( timeOffset !== 0.0 ) { | |
| var times = this.times; | |
| for ( var i = 0, n = times.length; i !== n; ++ i ) { | |
| times[ i ] += timeOffset; | |
| } | |
| } | |
| return this; | |
| }, | |
| // scale all keyframe times by a factor (useful for frame <-> seconds conversions) | |
| scale: function ( timeScale ) { | |
| if ( timeScale !== 1.0 ) { | |
| var times = this.times; | |
| for ( var i = 0, n = times.length; i !== n; ++ i ) { | |
| times[ i ] *= timeScale; | |
| } | |
| } | |
| return this; | |
| }, | |
| // removes keyframes before and after animation without changing any values within the range [startTime, endTime]. | |
| // IMPORTANT: We do not shift around keys to the start of the track time, because for interpolated keys this will change their values | |
| trim: function ( startTime, endTime ) { | |
| var times = this.times, | |
| nKeys = times.length, | |
| from = 0, | |
| to = nKeys - 1; | |
| while ( from !== nKeys && times[ from ] < startTime ) { | |
| ++ from; | |
| } | |
| while ( to !== - 1 && times[ to ] > endTime ) { | |
| -- to; | |
| } | |
| ++ to; // inclusive -> exclusive bound | |
| if ( from !== 0 || to !== nKeys ) { | |
| // empty tracks are forbidden, so keep at least one keyframe | |
| if ( from >= to ) to = Math.max( to, 1 ), from = to - 1; | |
| var stride = this.getValueSize(); | |
| this.times = AnimationUtils.arraySlice( times, from, to ); | |
| this.values = AnimationUtils.arraySlice( this.values, from * stride, to * stride ); | |
| } | |
| return this; | |
| }, | |
| // ensure we do not get a GarbageInGarbageOut situation, make sure tracks are at least minimally viable | |
| validate: function () { | |
| var valid = true; | |
| var valueSize = this.getValueSize(); | |
| if ( valueSize - Math.floor( valueSize ) !== 0 ) { | |
| console.error( 'THREE.KeyframeTrack: Invalid value size in track.', this ); | |
| valid = false; | |
| } | |
| var times = this.times, | |
| values = this.values, | |
| nKeys = times.length; | |
| if ( nKeys === 0 ) { | |
| console.error( 'THREE.KeyframeTrack: Track is empty.', this ); | |
| valid = false; | |
| } | |
| var prevTime = null; | |
| for ( var i = 0; i !== nKeys; i ++ ) { | |
| var currTime = times[ i ]; | |
| if ( typeof currTime === 'number' && isNaN( currTime ) ) { | |
| console.error( 'THREE.KeyframeTrack: Time is not a valid number.', this, i, currTime ); | |
| valid = false; | |
| break; | |
| } | |
| if ( prevTime !== null && prevTime > currTime ) { | |
| console.error( 'THREE.KeyframeTrack: Out of order keys.', this, i, currTime, prevTime ); | |
| valid = false; | |
| break; | |
| } | |
| prevTime = currTime; | |
| } | |
| if ( values !== undefined ) { | |
| if ( AnimationUtils.isTypedArray( values ) ) { | |
| for ( var i = 0, n = values.length; i !== n; ++ i ) { | |
| var value = values[ i ]; | |
| if ( isNaN( value ) ) { | |
| console.error( 'THREE.KeyframeTrack: Value is not a valid number.', this, i, value ); | |
| valid = false; | |
| break; | |
| } | |
| } | |
| } | |
| } | |
| return valid; | |
| }, | |
| // removes equivalent sequential keys as common in morph target sequences | |
| // (0,0,0,0,1,1,1,0,0,0,0,0,0,0) --> (0,0,1,1,0,0) | |
| optimize: function () { | |
| var times = this.times, | |
| values = this.values, | |
| stride = this.getValueSize(), | |
| smoothInterpolation = this.getInterpolation() === InterpolateSmooth, | |
| writeIndex = 1, | |
| lastIndex = times.length - 1; | |
| for ( var i = 1; i < lastIndex; ++ i ) { | |
| var keep = false; | |
| var time = times[ i ]; | |
| var timeNext = times[ i + 1 ]; | |
| // remove adjacent keyframes scheduled at the same time | |
| if ( time !== timeNext && ( i !== 1 || time !== time[ 0 ] ) ) { | |
| if ( ! smoothInterpolation ) { | |
| // remove unnecessary keyframes same as their neighbors | |
| var offset = i * stride, | |
| offsetP = offset - stride, | |
| offsetN = offset + stride; | |
| for ( var j = 0; j !== stride; ++ j ) { | |
| var value = values[ offset + j ]; | |
| if ( value !== values[ offsetP + j ] || | |
| value !== values[ offsetN + j ] ) { | |
| keep = true; | |
| break; | |
| } | |
| } | |
| } else { | |
| keep = true; | |
| } | |
| } | |
| // in-place compaction | |
| if ( keep ) { | |
| if ( i !== writeIndex ) { | |
| times[ writeIndex ] = times[ i ]; | |
| var readOffset = i * stride, | |
| writeOffset = writeIndex * stride; | |
| for ( var j = 0; j !== stride; ++ j ) { | |
| values[ writeOffset + j ] = values[ readOffset + j ]; | |
| } | |
| } | |
| ++ writeIndex; | |
| } | |
| } | |
| // flush last keyframe (compaction looks ahead) | |
| if ( lastIndex > 0 ) { | |
| times[ writeIndex ] = times[ lastIndex ]; | |
| for ( var readOffset = lastIndex * stride, writeOffset = writeIndex * stride, j = 0; j !== stride; ++ j ) { | |
| values[ writeOffset + j ] = values[ readOffset + j ]; | |
| } | |
| ++ writeIndex; | |
| } | |
| if ( writeIndex !== times.length ) { | |
| this.times = AnimationUtils.arraySlice( times, 0, writeIndex ); | |
| this.values = AnimationUtils.arraySlice( values, 0, writeIndex * stride ); | |
| } | |
| return this; | |
| } | |
| } ); | |
| /** | |
| * | |
| * A Track of vectored keyframe values. | |
| * | |
| * | |
| * @author Ben Houston / http://clara.io/ | |
| * @author David Sarno / http://lighthaus.us/ | |
| * @author tschw | |
| */ | |
| function VectorKeyframeTrack( name, times, values, interpolation ) { | |
| KeyframeTrack.call( this, name, times, values, interpolation ); | |
| } | |
| VectorKeyframeTrack.prototype = Object.assign( Object.create( KeyframeTrack.prototype ), { | |
| constructor: VectorKeyframeTrack, | |
| ValueTypeName: 'vector' | |
| // ValueBufferType is inherited | |
| // DefaultInterpolation is inherited | |
| } ); | |
| /** | |
| * | |
| * Reusable set of Tracks that represent an animation. | |
| * | |
| * @author Ben Houston / http://clara.io/ | |
| * @author David Sarno / http://lighthaus.us/ | |
| */ | |
| function AnimationClip( name, duration, tracks ) { | |
| this.name = name; | |
| this.tracks = tracks; | |
| this.duration = ( duration !== undefined ) ? duration : - 1; | |
| this.uuid = _Math.generateUUID(); | |
| // this means it should figure out its duration by scanning the tracks | |
| if ( this.duration < 0 ) { | |
| this.resetDuration(); | |
| } | |
| this.optimize(); | |
| } | |
| Object.assign( AnimationClip, { | |
| parse: function ( json ) { | |
| var tracks = [], | |
| jsonTracks = json.tracks, | |
| frameTime = 1.0 / ( json.fps || 1.0 ); | |
| for ( var i = 0, n = jsonTracks.length; i !== n; ++ i ) { | |
| tracks.push( KeyframeTrack.parse( jsonTracks[ i ] ).scale( frameTime ) ); | |
| } | |
| return new AnimationClip( json.name, json.duration, tracks ); | |
| }, | |
| toJSON: function ( clip ) { | |
| var tracks = [], | |
| clipTracks = clip.tracks; | |
| var json = { | |
| 'name': clip.name, | |
| 'duration': clip.duration, | |
| 'tracks': tracks | |
| }; | |
| for ( var i = 0, n = clipTracks.length; i !== n; ++ i ) { | |
| tracks.push( KeyframeTrack.toJSON( clipTracks[ i ] ) ); | |
| } | |
| return json; | |
| }, | |
| CreateFromMorphTargetSequence: function ( name, morphTargetSequence, fps, noLoop ) { | |
| var numMorphTargets = morphTargetSequence.length; | |
| var tracks = []; | |
| for ( var i = 0; i < numMorphTargets; i ++ ) { | |
| var times = []; | |
| var values = []; | |
| times.push( | |
| ( i + numMorphTargets - 1 ) % numMorphTargets, | |
| i, | |
| ( i + 1 ) % numMorphTargets ); | |
| values.push( 0, 1, 0 ); | |
| var order = AnimationUtils.getKeyframeOrder( times ); | |
| times = AnimationUtils.sortedArray( times, 1, order ); | |
| values = AnimationUtils.sortedArray( values, 1, order ); | |
| // if there is a key at the first frame, duplicate it as the | |
| // last frame as well for perfect loop. | |
| if ( ! noLoop && times[ 0 ] === 0 ) { | |
| times.push( numMorphTargets ); | |
| values.push( values[ 0 ] ); | |
| } | |
| tracks.push( | |
| new NumberKeyframeTrack( | |
| '.morphTargetInfluences[' + morphTargetSequence[ i ].name + ']', | |
| times, values | |
| ).scale( 1.0 / fps ) ); | |
| } | |
| return new AnimationClip( name, - 1, tracks ); | |
| }, | |
| findByName: function ( objectOrClipArray, name ) { | |
| var clipArray = objectOrClipArray; | |
| if ( ! Array.isArray( objectOrClipArray ) ) { | |
| var o = objectOrClipArray; | |
| clipArray = o.geometry && o.geometry.animations || o.animations; | |
| } | |
| for ( var i = 0; i < clipArray.length; i ++ ) { | |
| if ( clipArray[ i ].name === name ) { | |
| return clipArray[ i ]; | |
| } | |
| } | |
| return null; | |
| }, | |
| CreateClipsFromMorphTargetSequences: function ( morphTargets, fps, noLoop ) { | |
| var animationToMorphTargets = {}; | |
| // tested with https://regex101.com/ on trick sequences | |
| // such flamingo_flyA_003, flamingo_run1_003, crdeath0059 | |
| var pattern = /^([\w-]*?)([\d]+)$/; | |
| // sort morph target names into animation groups based | |
| // patterns like Walk_001, Walk_002, Run_001, Run_002 | |
| for ( var i = 0, il = morphTargets.length; i < il; i ++ ) { | |
| var morphTarget = morphTargets[ i ]; | |
| var parts = morphTarget.name.match( pattern ); | |
| if ( parts && parts.length > 1 ) { | |
| var name = parts[ 1 ]; | |
| var animationMorphTargets = animationToMorphTargets[ name ]; | |
| if ( ! animationMorphTargets ) { | |
| animationToMorphTargets[ name ] = animationMorphTargets = []; | |
| } | |
| animationMorphTargets.push( morphTarget ); | |
| } | |
| } | |
| var clips = []; | |
| for ( var name in animationToMorphTargets ) { | |
| clips.push( AnimationClip.CreateFromMorphTargetSequence( name, animationToMorphTargets[ name ], fps, noLoop ) ); | |
| } | |
| return clips; | |
| }, | |
| // parse the animation.hierarchy format | |
| parseAnimation: function ( animation, bones ) { | |
| if ( ! animation ) { | |
| console.error( 'THREE.AnimationClip: No animation in JSONLoader data.' ); | |
| return null; | |
| } | |
| var addNonemptyTrack = function ( trackType, trackName, animationKeys, propertyName, destTracks ) { | |
| // only return track if there are actually keys. | |
| if ( animationKeys.length !== 0 ) { | |
| var times = []; | |
| var values = []; | |
| AnimationUtils.flattenJSON( animationKeys, times, values, propertyName ); | |
| // empty keys are filtered out, so check again | |
| if ( times.length !== 0 ) { | |
| destTracks.push( new trackType( trackName, times, values ) ); | |
| } | |
| } | |
| }; | |
| var tracks = []; | |
| var clipName = animation.name || 'default'; | |
| // automatic length determination in AnimationClip. | |
| var duration = animation.length || - 1; | |
| var fps = animation.fps || 30; | |
| var hierarchyTracks = animation.hierarchy || []; | |
| for ( var h = 0; h < hierarchyTracks.length; h ++ ) { | |
| var animationKeys = hierarchyTracks[ h ].keys; | |
| // skip empty tracks | |
| if ( ! animationKeys || animationKeys.length === 0 ) continue; | |
| // process morph targets | |
| if ( animationKeys[ 0 ].morphTargets ) { | |
| // figure out all morph targets used in this track | |
| var morphTargetNames = {}; | |
| for ( var k = 0; k < animationKeys.length; k ++ ) { | |
| if ( animationKeys[ k ].morphTargets ) { | |
| for ( var m = 0; m < animationKeys[ k ].morphTargets.length; m ++ ) { | |
| morphTargetNames[ animationKeys[ k ].morphTargets[ m ] ] = - 1; | |
| } | |
| } | |
| } | |
| // create a track for each morph target with all zero | |
| // morphTargetInfluences except for the keys in which | |
| // the morphTarget is named. | |
| for ( var morphTargetName in morphTargetNames ) { | |
| var times = []; | |
| var values = []; | |
| for ( var m = 0; m !== animationKeys[ k ].morphTargets.length; ++ m ) { | |
| var animationKey = animationKeys[ k ]; | |
| times.push( animationKey.time ); | |
| values.push( ( animationKey.morphTarget === morphTargetName ) ? 1 : 0 ); | |
| } | |
| tracks.push( new NumberKeyframeTrack( '.morphTargetInfluence[' + morphTargetName + ']', times, values ) ); | |
| } | |
| duration = morphTargetNames.length * ( fps || 1.0 ); | |
| } else { | |
| // ...assume skeletal animation | |
| var boneName = '.bones[' + bones[ h ].name + ']'; | |
| addNonemptyTrack( | |
| VectorKeyframeTrack, boneName + '.position', | |
| animationKeys, 'pos', tracks ); | |
| addNonemptyTrack( | |
| QuaternionKeyframeTrack, boneName + '.quaternion', | |
| animationKeys, 'rot', tracks ); | |
| addNonemptyTrack( | |
| VectorKeyframeTrack, boneName + '.scale', | |
| animationKeys, 'scl', tracks ); | |
| } | |
| } | |
| if ( tracks.length === 0 ) { | |
| return null; | |
| } | |
| var clip = new AnimationClip( clipName, duration, tracks ); | |
| return clip; | |
| } | |
| } ); | |
| Object.assign( AnimationClip.prototype, { | |
| resetDuration: function () { | |
| var tracks = this.tracks, duration = 0; | |
| for ( var i = 0, n = tracks.length; i !== n; ++ i ) { | |
| var track = this.tracks[ i ]; | |
| duration = Math.max( duration, track.times[ track.times.length - 1 ] ); | |
| } | |
| this.duration = duration; | |
| }, | |
| trim: function () { | |
| for ( var i = 0; i < this.tracks.length; i ++ ) { | |
| this.tracks[ i ].trim( 0, this.duration ); | |
| } | |
| return this; | |
| }, | |
| optimize: function () { | |
| for ( var i = 0; i < this.tracks.length; i ++ ) { | |
| this.tracks[ i ].optimize(); | |
| } | |
| return this; | |
| } | |
| } ); | |
| /** | |
| * @author mrdoob / http://mrdoob.com/ | |
| */ | |
| function MaterialLoader( manager ) { | |
| this.manager = ( manager !== undefined ) ? manager : DefaultLoadingManager; | |
| this.textures = {}; | |
| } | |
| Object.assign( MaterialLoader.prototype, { | |
| load: function ( url, onLoad, onProgress, onError ) { | |
| var scope = this; | |
| var loader = new FileLoader( scope.manager ); | |
| loader.load( url, function ( text ) { | |
| onLoad( scope.parse( JSON.parse( text ) ) ); | |
| }, onProgress, onError ); | |
| }, | |
| setTextures: function ( value ) { | |
| this.textures = value; | |
| }, | |
| parse: function ( json ) { | |
| var textures = this.textures; | |
| function getTexture( name ) { | |
| if ( textures[ name ] === undefined ) { | |
| console.warn( 'THREE.MaterialLoader: Undefined texture', name ); | |
| } | |
| return textures[ name ]; | |
| } | |
| var material = new Materials[ json.type ](); | |
| if ( json.uuid !== undefined ) material.uuid = json.uuid; | |
| if ( json.name !== undefined ) material.name = json.name; | |
| if ( json.color !== undefined ) material.color.setHex( json.color ); | |
| if ( json.roughness !== undefined ) material.roughness = json.roughness; | |
| if ( json.metalness !== undefined ) material.metalness = json.metalness; | |
| if ( json.emissive !== undefined ) material.emissive.setHex( json.emissive ); | |
| if ( json.specular !== undefined ) material.specular.setHex( json.specular ); | |
| if ( json.shininess !== undefined ) material.shininess = json.shininess; | |
| if ( json.clearCoat !== undefined ) material.clearCoat = json.clearCoat; | |
| if ( json.clearCoatRoughness !== undefined ) material.clearCoatRoughness = json.clearCoatRoughness; | |
| if ( json.uniforms !== undefined ) material.uniforms = json.uniforms; | |
| if ( json.vertexShader !== undefined ) material.vertexShader = json.vertexShader; | |
| if ( json.fragmentShader !== undefined ) material.fragmentShader = json.fragmentShader; | |
| if ( json.vertexColors !== undefined ) material.vertexColors = json.vertexColors; | |
| if ( json.fog !== undefined ) material.fog = json.fog; | |
| if ( json.flatShading !== undefined ) material.flatShading = json.flatShading; | |
| if ( json.blending !== undefined ) material.blending = json.blending; | |
| if ( json.side !== undefined ) material.side = json.side; | |
| if ( json.opacity !== undefined ) material.opacity = json.opacity; | |
| if ( json.transparent !== undefined ) material.transparent = json.transparent; | |
| if ( json.alphaTest !== undefined ) material.alphaTest = json.alphaTest; | |
| if ( json.depthTest !== undefined ) material.depthTest = json.depthTest; | |
| if ( json.depthWrite !== undefined ) material.depthWrite = json.depthWrite; | |
| if ( json.colorWrite !== undefined ) material.colorWrite = json.colorWrite; | |
| if ( json.wireframe !== undefined ) material.wireframe = json.wireframe; | |
| if ( json.wireframeLinewidth !== undefined ) material.wireframeLinewidth = json.wireframeLinewidth; | |
| if ( json.wireframeLinecap !== undefined ) material.wireframeLinecap = json.wireframeLinecap; | |
| if ( json.wireframeLinejoin !== undefined ) material.wireframeLinejoin = json.wireframeLinejoin; | |
| if ( json.rotation !== undefined ) material.rotation = json.rotation; | |
| if ( json.linewidth !== 1 ) material.linewidth = json.linewidth; | |
| if ( json.dashSize !== undefined ) material.dashSize = json.dashSize; | |
| if ( json.gapSize !== undefined ) material.gapSize = json.gapSize; | |
| if ( json.scale !== undefined ) material.scale = json.scale; | |
| if ( json.polygonOffset !== undefined ) material.polygonOffset = json.polygonOffset; | |
| if ( json.polygonOffsetFactor !== undefined ) material.polygonOffsetFactor = json.polygonOffsetFactor; | |
| if ( json.polygonOffsetUnits !== undefined ) material.polygonOffsetUnits = json.polygonOffsetUnits; | |
| if ( json.skinning !== undefined ) material.skinning = json.skinning; | |
| if ( json.morphTargets !== undefined ) material.morphTargets = json.morphTargets; | |
| if ( json.dithering !== undefined ) material.dithering = json.dithering; | |
| if ( json.visible !== undefined ) material.visible = json.visible; | |
| if ( json.userData !== undefined ) material.userData = json.userData; | |
| // Deprecated | |
| if ( json.shading !== undefined ) material.flatShading = json.shading === 1; // THREE.FlatShading | |
| // for PointsMaterial | |
| if ( json.size !== undefined ) material.size = json.size; | |
| if ( json.sizeAttenuation !== undefined ) material.sizeAttenuation = json.sizeAttenuation; | |
| // maps | |
| if ( json.map !== undefined ) material.map = getTexture( json.map ); | |
| if ( json.alphaMap !== undefined ) { | |
| material.alphaMap = getTexture( json.alphaMap ); | |
| material.transparent = true; | |
| } | |
| if ( json.bumpMap !== undefined ) material.bumpMap = getTexture( json.bumpMap ); | |
| if ( json.bumpScale !== undefined ) material.bumpScale = json.bumpScale; | |
| if ( json.normalMap !== undefined ) material.normalMap = getTexture( json.normalMap ); | |
| if ( json.normalScale !== undefined ) { | |
| var normalScale = json.normalScale; | |
| if ( Array.isArray( normalScale ) === false ) { | |
| // Blender exporter used to export a scalar. See #7459 | |
| normalScale = [ normalScale, normalScale ]; | |
| } | |
| material.normalScale = new Vector2().fromArray( normalScale ); | |
| } | |
| if ( json.displacementMap !== undefined ) material.displacementMap = getTexture( json.displacementMap ); | |
| if ( json.displacementScale !== undefined ) material.displacementScale = json.displacementScale; | |
| if ( json.displacementBias !== undefined ) material.displacementBias = json.displacementBias; | |
| if ( json.roughnessMap !== undefined ) material.roughnessMap = getTexture( json.roughnessMap ); | |
| if ( json.metalnessMap !== undefined ) material.metalnessMap = getTexture( json.metalnessMap ); | |
| if ( json.emissiveMap !== undefined ) material.emissiveMap = getTexture( json.emissiveMap ); | |
| if ( json.emissiveIntensity !== undefined ) material.emissiveIntensity = json.emissiveIntensity; | |
| if ( json.specularMap !== undefined ) material.specularMap = getTexture( json.specularMap ); | |
| if ( json.envMap !== undefined ) material.envMap = getTexture( json.envMap ); | |
| if ( json.reflectivity !== undefined ) material.reflectivity = json.reflectivity; | |
| if ( json.lightMap !== undefined ) material.lightMap = getTexture( json.lightMap ); | |
| if ( json.lightMapIntensity !== undefined ) material.lightMapIntensity = json.lightMapIntensity; | |
| if ( json.aoMap !== undefined ) material.aoMap = getTexture( json.aoMap ); | |
| if ( json.aoMapIntensity !== undefined ) material.aoMapIntensity = json.aoMapIntensity; | |
| if ( json.gradientMap !== undefined ) material.gradientMap = getTexture( json.gradientMap ); | |
| return material; | |
| } | |
| } ); | |
| /** | |
| * @author mrdoob / http://mrdoob.com/ | |
| */ | |
| function BufferGeometryLoader( manager ) { | |
| this.manager = ( manager !== undefined ) ? manager : DefaultLoadingManager; | |
| } | |
| Object.assign( BufferGeometryLoader.prototype, { | |
| load: function ( url, onLoad, onProgress, onError ) { | |
| var scope = this; | |
| var loader = new FileLoader( scope.manager ); | |
| loader.load( url, function ( text ) { | |
| onLoad( scope.parse( JSON.parse( text ) ) ); | |
| }, onProgress, onError ); | |
| }, | |
| parse: function ( json ) { | |
| var geometry = new BufferGeometry(); | |
| var index = json.data.index; | |
| if ( index !== undefined ) { | |
| var typedArray = new TYPED_ARRAYS[ index.type ]( index.array ); | |
| geometry.setIndex( new BufferAttribute( typedArray, 1 ) ); | |
| } | |
| var attributes = json.data.attributes; | |
| for ( var key in attributes ) { | |
| var attribute = attributes[ key ]; | |
| var typedArray = new TYPED_ARRAYS[ attribute.type ]( attribute.array ); | |
| geometry.addAttribute( key, new BufferAttribute( typedArray, attribute.itemSize, attribute.normalized ) ); | |
| } | |
| var groups = json.data.groups || json.data.drawcalls || json.data.offsets; | |
| if ( groups !== undefined ) { | |
| for ( var i = 0, n = groups.length; i !== n; ++ i ) { | |
| var group = groups[ i ]; | |
| geometry.addGroup( group.start, group.count, group.materialIndex ); | |
| } | |
| } | |
| var boundingSphere = json.data.boundingSphere; | |
| if ( boundingSphere !== undefined ) { | |
| var center = new Vector3(); | |
| if ( boundingSphere.center !== undefined ) { | |
| center.fromArray( boundingSphere.center ); | |
| } | |
| geometry.boundingSphere = new Sphere( center, boundingSphere.radius ); | |
| } | |
| return geometry; | |
| } | |
| } ); | |
| var TYPED_ARRAYS = { | |
| Int8Array: Int8Array, | |
| Uint8Array: Uint8Array, | |
| // Workaround for IE11 pre KB2929437. See #11440 | |
| Uint8ClampedArray: typeof Uint8ClampedArray !== 'undefined' ? Uint8ClampedArray : Uint8Array, | |
| Int16Array: Int16Array, | |
| Uint16Array: Uint16Array, | |
| Int32Array: Int32Array, | |
| Uint32Array: Uint32Array, | |
| Float32Array: Float32Array, | |
| Float64Array: Float64Array | |
| }; | |
| /** | |
| * @author alteredq / http://alteredqualia.com/ | |
| */ | |
| function Loader() { | |
| this.onLoadStart = function () {}; | |
| this.onLoadProgress = function () {}; | |
| this.onLoadComplete = function () {}; | |
| } | |
| Loader.Handlers = { | |
| handlers: [], | |
| add: function ( regex, loader ) { | |
| this.handlers.push( regex, loader ); | |
| }, | |
| get: function ( file ) { | |
| var handlers = this.handlers; | |
| for ( var i = 0, l = handlers.length; i < l; i += 2 ) { | |
| var regex = handlers[ i ]; | |
| var loader = handlers[ i + 1 ]; | |
| if ( regex.test( file ) ) { | |
| return loader; | |
| } | |
| } | |
| return null; | |
| } | |
| }; | |
| Object.assign( Loader.prototype, { | |
| crossOrigin: undefined, | |
| initMaterials: function ( materials, texturePath, crossOrigin ) { | |
| var array = []; | |
| for ( var i = 0; i < materials.length; ++ i ) { | |
| array[ i ] = this.createMaterial( materials[ i ], texturePath, crossOrigin ); | |
| } | |
| return array; | |
| }, | |
| createMaterial: ( function () { | |
| var BlendingMode = { | |
| NoBlending: NoBlending, | |
| NormalBlending: NormalBlending, | |
| AdditiveBlending: AdditiveBlending, | |
| SubtractiveBlending: SubtractiveBlending, | |
| MultiplyBlending: MultiplyBlending, | |
| CustomBlending: CustomBlending | |
| }; | |
| var color = new Color(); | |
| var textureLoader = new TextureLoader(); | |
| var materialLoader = new MaterialLoader(); | |
| return function createMaterial( m, texturePath, crossOrigin ) { | |
| // convert from old material format | |
| var textures = {}; | |
| function loadTexture( path, repeat, offset, wrap, anisotropy ) { | |
| var fullPath = texturePath + path; | |
| var loader = Loader.Handlers.get( fullPath ); | |
| var texture; | |
| if ( loader !== null ) { | |
| texture = loader.load( fullPath ); | |
| } else { | |
| textureLoader.setCrossOrigin( crossOrigin ); | |
| texture = textureLoader.load( fullPath ); | |
| } | |
| if ( repeat !== undefined ) { | |
| texture.repeat.fromArray( repeat ); | |
| if ( repeat[ 0 ] !== 1 ) texture.wrapS = RepeatWrapping; | |
| if ( repeat[ 1 ] !== 1 ) texture.wrapT = RepeatWrapping; | |
| } | |
| if ( offset !== undefined ) { | |
| texture.offset.fromArray( offset ); | |
| } | |
| if ( wrap !== undefined ) { | |
| if ( wrap[ 0 ] === 'repeat' ) texture.wrapS = RepeatWrapping; | |
| if ( wrap[ 0 ] === 'mirror' ) texture.wrapS = MirroredRepeatWrapping; | |
| if ( wrap[ 1 ] === 'repeat' ) texture.wrapT = RepeatWrapping; | |
| if ( wrap[ 1 ] === 'mirror' ) texture.wrapT = MirroredRepeatWrapping; | |
| } | |
| if ( anisotropy !== undefined ) { | |
| texture.anisotropy = anisotropy; | |
| } | |
| var uuid = _Math.generateUUID(); | |
| textures[ uuid ] = texture; | |
| return uuid; | |
| } | |
| // | |
| var json = { | |
| uuid: _Math.generateUUID(), | |
| type: 'MeshLambertMaterial' | |
| }; | |
| for ( var name in m ) { | |
| var value = m[ name ]; | |
| switch ( name ) { | |
| case 'DbgColor': | |
| case 'DbgIndex': | |
| case 'opticalDensity': | |
| case 'illumination': | |
| break; | |
| case 'DbgName': | |
| json.name = value; | |
| break; | |
| case 'blending': | |
| json.blending = BlendingMode[ value ]; | |
| break; | |
| case 'colorAmbient': | |
| case 'mapAmbient': | |
| console.warn( 'THREE.Loader.createMaterial:', name, 'is no longer supported.' ); | |
| break; | |
| case 'colorDiffuse': | |
| json.color = color.fromArray( value ).getHex(); | |
| break; | |
| case 'colorSpecular': | |
| json.specular = color.fromArray( value ).getHex(); | |
| break; | |
| case 'colorEmissive': | |
| json.emissive = color.fromArray( value ).getHex(); | |
| break; | |
| case 'specularCoef': | |
| json.shininess = value; | |
| break; | |
| case 'shading': | |
| if ( value.toLowerCase() === 'basic' ) json.type = 'MeshBasicMaterial'; | |
| if ( value.toLowerCase() === 'phong' ) json.type = 'MeshPhongMaterial'; | |
| if ( value.toLowerCase() === 'standard' ) json.type = 'MeshStandardMaterial'; | |
| break; | |
| case 'mapDiffuse': | |
| json.map = loadTexture( value, m.mapDiffuseRepeat, m.mapDiffuseOffset, m.mapDiffuseWrap, m.mapDiffuseAnisotropy ); | |
| break; | |
| case 'mapDiffuseRepeat': | |
| case 'mapDiffuseOffset': | |
| case 'mapDiffuseWrap': | |
| case 'mapDiffuseAnisotropy': | |
| break; | |
| case 'mapEmissive': | |
| json.emissiveMap = loadTexture( value, m.mapEmissiveRepeat, m.mapEmissiveOffset, m.mapEmissiveWrap, m.mapEmissiveAnisotropy ); | |
| break; | |
| case 'mapEmissiveRepeat': | |
| case 'mapEmissiveOffset': | |
| case 'mapEmissiveWrap': | |
| case 'mapEmissiveAnisotropy': | |
| break; | |
| case 'mapLight': | |
| json.lightMap = loadTexture( value, m.mapLightRepeat, m.mapLightOffset, m.mapLightWrap, m.mapLightAnisotropy ); | |
| break; | |
| case 'mapLightRepeat': | |
| case 'mapLightOffset': | |
| case 'mapLightWrap': | |
| case 'mapLightAnisotropy': | |
| break; | |
| case 'mapAO': | |
| json.aoMap = loadTexture( value, m.mapAORepeat, m.mapAOOffset, m.mapAOWrap, m.mapAOAnisotropy ); | |
| break; | |
| case 'mapAORepeat': | |
| case 'mapAOOffset': | |
| case 'mapAOWrap': | |
| case 'mapAOAnisotropy': | |
| break; | |
| case 'mapBump': | |
| json.bumpMap = loadTexture( value, m.mapBumpRepeat, m.mapBumpOffset, m.mapBumpWrap, m.mapBumpAnisotropy ); | |
| break; | |
| case 'mapBumpScale': | |
| json.bumpScale = value; | |
| break; | |
| case 'mapBumpRepeat': | |
| case 'mapBumpOffset': | |
| case 'mapBumpWrap': | |
| case 'mapBumpAnisotropy': | |
| break; | |
| case 'mapNormal': | |
| json.normalMap = loadTexture( value, m.mapNormalRepeat, m.mapNormalOffset, m.mapNormalWrap, m.mapNormalAnisotropy ); | |
| break; | |
| case 'mapNormalFactor': | |
| json.normalScale = [ value, value ]; | |
| break; | |
| case 'mapNormalRepeat': | |
| case 'mapNormalOffset': | |
| case 'mapNormalWrap': | |
| case 'mapNormalAnisotropy': | |
| break; | |
| case 'mapSpecular': | |
| json.specularMap = loadTexture( value, m.mapSpecularRepeat, m.mapSpecularOffset, m.mapSpecularWrap, m.mapSpecularAnisotropy ); | |
| break; | |
| case 'mapSpecularRepeat': | |
| case 'mapSpecularOffset': | |
| case 'mapSpecularWrap': | |
| case 'mapSpecularAnisotropy': | |
| break; | |
| case 'mapMetalness': | |
| json.metalnessMap = loadTexture( value, m.mapMetalnessRepeat, m.mapMetalnessOffset, m.mapMetalnessWrap, m.mapMetalnessAnisotropy ); | |
| break; | |
| case 'mapMetalnessRepeat': | |
| case 'mapMetalnessOffset': | |
| case 'mapMetalnessWrap': | |
| case 'mapMetalnessAnisotropy': | |
| break; | |
| case 'mapRoughness': | |
| json.roughnessMap = loadTexture( value, m.mapRoughnessRepeat, m.mapRoughnessOffset, m.mapRoughnessWrap, m.mapRoughnessAnisotropy ); | |
| break; | |
| case 'mapRoughnessRepeat': | |
| case 'mapRoughnessOffset': | |
| case 'mapRoughnessWrap': | |
| case 'mapRoughnessAnisotropy': | |
| break; | |
| case 'mapAlpha': | |
| json.alphaMap = loadTexture( value, m.mapAlphaRepeat, m.mapAlphaOffset, m.mapAlphaWrap, m.mapAlphaAnisotropy ); | |
| break; | |
| case 'mapAlphaRepeat': | |
| case 'mapAlphaOffset': | |
| case 'mapAlphaWrap': | |
| case 'mapAlphaAnisotropy': | |
| break; | |
| case 'flipSided': | |
| json.side = BackSide; | |
| break; | |
| case 'doubleSided': | |
| json.side = DoubleSide; | |
| break; | |
| case 'transparency': | |
| console.warn( 'THREE.Loader.createMaterial: transparency has been renamed to opacity' ); | |
| json.opacity = value; | |
| break; | |
| case 'depthTest': | |
| case 'depthWrite': | |
| case 'colorWrite': | |
| case 'opacity': | |
| case 'reflectivity': | |
| case 'transparent': | |
| case 'visible': | |
| case 'wireframe': | |
| json[ name ] = value; | |
| break; | |
| case 'vertexColors': | |
| if ( value === true ) json.vertexColors = VertexColors; | |
| if ( value === 'face' ) json.vertexColors = FaceColors; | |
| break; | |
| default: | |
| console.error( 'THREE.Loader.createMaterial: Unsupported', name, value ); | |
| break; | |
| } | |
| } | |
| if ( json.type === 'MeshBasicMaterial' ) delete json.emissive; | |
| if ( json.type !== 'MeshPhongMaterial' ) delete json.specular; | |
| if ( json.opacity < 1 ) json.transparent = true; | |
| materialLoader.setTextures( textures ); | |
| return materialLoader.parse( json ); | |
| }; | |
| } )() | |
| } ); | |
| /** | |
| * @author Don McCurdy / https://www.donmccurdy.com | |
| */ | |
| var LoaderUtils = { | |
| decodeText: function ( array ) { | |
| if ( typeof TextDecoder !== 'undefined' ) { | |
| return new TextDecoder().decode( array ); | |
| } | |
| // Avoid the String.fromCharCode.apply(null, array) shortcut, which | |
| // throws a "maximum call stack size exceeded" error for large arrays. | |
| var s = ''; | |
| for ( var i = 0, il = array.length; i < il; i ++ ) { | |
| // Implicitly assumes little-endian. | |
| s += String.fromCharCode( array[ i ] ); | |
| } | |
| // Merges multi-byte utf-8 characters. | |
| return decodeURIComponent( escape( s ) ); | |
| }, | |
| extractUrlBase: function ( url ) { | |
| var parts = url.split( '/' ); | |
| if ( parts.length === 1 ) return './'; | |
| parts.pop(); | |
| return parts.join( '/' ) + '/'; | |
| } | |
| }; | |
| /** | |
| * @author mrdoob / http://mrdoob.com/ | |
| * @author alteredq / http://alteredqualia.com/ | |
| */ | |
| function JSONLoader( manager ) { | |
| if ( typeof manager === 'boolean' ) { | |
| console.warn( 'THREE.JSONLoader: showStatus parameter has been removed from constructor.' ); | |
| manager = undefined; | |
| } | |
| this.manager = ( manager !== undefined ) ? manager : DefaultLoadingManager; | |
| this.withCredentials = false; | |
| } | |
| Object.assign( JSONLoader.prototype, { | |
| load: function ( url, onLoad, onProgress, onError ) { | |
| var scope = this; | |
| var texturePath = this.texturePath && ( typeof this.texturePath === 'string' ) ? this.texturePath : LoaderUtils.extractUrlBase( url ); | |
| var loader = new FileLoader( this.manager ); | |
| loader.setWithCredentials( this.withCredentials ); | |
| loader.load( url, function ( text ) { | |
| var json = JSON.parse( text ); | |
| var metadata = json.metadata; | |
| if ( metadata !== undefined ) { | |
| var type = metadata.type; | |
| if ( type !== undefined ) { | |
| if ( type.toLowerCase() === 'object' ) { | |
| console.error( 'THREE.JSONLoader: ' + url + ' should be loaded with THREE.ObjectLoader instead.' ); | |
| return; | |
| } | |
| } | |
| } | |
| var object = scope.parse( json, texturePath ); | |
| onLoad( object.geometry, object.materials ); | |
| }, onProgress, onError ); | |
| }, | |
| setTexturePath: function ( value ) { | |
| this.texturePath = value; | |
| }, | |
| parse: ( function () { | |
| function parseModel( json, geometry ) { | |
| function isBitSet( value, position ) { | |
| return value & ( 1 << position ); | |
| } | |
| var i, j, fi, | |
| offset, zLength, | |
| colorIndex, normalIndex, uvIndex, materialIndex, | |
| type, | |
| isQuad, | |
| hasMaterial, | |
| hasFaceVertexUv, | |
| hasFaceNormal, hasFaceVertexNormal, | |
| hasFaceColor, hasFaceVertexColor, | |
| vertex, face, faceA, faceB, hex, normal, | |
| uvLayer, uv, u, v, | |
| faces = json.faces, | |
| vertices = json.vertices, | |
| normals = json.normals, | |
| colors = json.colors, | |
| scale = json.scale, | |
| nUvLayers = 0; | |
| if ( json.uvs !== undefined ) { | |
| // disregard empty arrays | |
| for ( i = 0; i < json.uvs.length; i ++ ) { | |
| if ( json.uvs[ i ].length ) nUvLayers ++; | |
| } | |
| for ( i = 0; i < nUvLayers; i ++ ) { | |
| geometry.faceVertexUvs[ i ] = []; | |
| } | |
| } | |
| offset = 0; | |
| zLength = vertices.length; | |
| while ( offset < zLength ) { | |
| vertex = new Vector3(); | |
| vertex.x = vertices[ offset ++ ] * scale; | |
| vertex.y = vertices[ offset ++ ] * scale; | |
| vertex.z = vertices[ offset ++ ] * scale; | |
| geometry.vertices.push( vertex ); | |
| } | |
| offset = 0; | |
| zLength = faces.length; | |
| while ( offset < zLength ) { | |
| type = faces[ offset ++ ]; | |
| isQuad = isBitSet( type, 0 ); | |
| hasMaterial = isBitSet( type, 1 ); | |
| hasFaceVertexUv = isBitSet( type, 3 ); | |
| hasFaceNormal = isBitSet( type, 4 ); | |
| hasFaceVertexNormal = isBitSet( type, 5 ); | |
| hasFaceColor = isBitSet( type, 6 ); | |
| hasFaceVertexColor = isBitSet( type, 7 ); | |
| // console.log("type", type, "bits", isQuad, hasMaterial, hasFaceVertexUv, hasFaceNormal, hasFaceVertexNormal, hasFaceColor, hasFaceVertexColor); | |
| if ( isQuad ) { | |
| faceA = new Face3(); | |
| faceA.a = faces[ offset ]; | |
| faceA.b = faces[ offset + 1 ]; | |
| faceA.c = faces[ offset + 3 ]; | |
| faceB = new Face3(); | |
| faceB.a = faces[ offset + 1 ]; | |
| faceB.b = faces[ offset + 2 ]; | |
| faceB.c = faces[ offset + 3 ]; | |
| offset += 4; | |
| if ( hasMaterial ) { | |
| materialIndex = faces[ offset ++ ]; | |
| faceA.materialIndex = materialIndex; | |
| faceB.materialIndex = materialIndex; | |
| } | |
| // to get face <=> uv index correspondence | |
| fi = geometry.faces.length; | |
| if ( hasFaceVertexUv ) { | |
| for ( i = 0; i < nUvLayers; i ++ ) { | |
| uvLayer = json.uvs[ i ]; | |
| geometry.faceVertexUvs[ i ][ fi ] = []; | |
| geometry.faceVertexUvs[ i ][ fi + 1 ] = []; | |
| for ( j = 0; j < 4; j ++ ) { | |
| uvIndex = faces[ offset ++ ]; | |
| u = uvLayer[ uvIndex * 2 ]; | |
| v = uvLayer[ uvIndex * 2 + 1 ]; | |
| uv = new Vector2( u, v ); | |
| if ( j !== 2 ) geometry.faceVertexUvs[ i ][ fi ].push( uv ); | |
| if ( j !== 0 ) geometry.faceVertexUvs[ i ][ fi + 1 ].push( uv ); | |
| } | |
| } | |
| } | |
| if ( hasFaceNormal ) { | |
| normalIndex = faces[ offset ++ ] * 3; | |
| faceA.normal.set( | |
| normals[ normalIndex ++ ], | |
| normals[ normalIndex ++ ], | |
| normals[ normalIndex ] | |
| ); | |
| faceB.normal.copy( faceA.normal ); | |
| } | |
| if ( hasFaceVertexNormal ) { | |
| for ( i = 0; i < 4; i ++ ) { | |
| normalIndex = faces[ offset ++ ] * 3; | |
| normal = new Vector3( | |
| normals[ normalIndex ++ ], | |
| normals[ normalIndex ++ ], | |
| normals[ normalIndex ] | |
| ); | |
| if ( i !== 2 ) faceA.vertexNormals.push( normal ); | |
| if ( i !== 0 ) faceB.vertexNormals.push( normal ); | |
| } | |
| } | |
| if ( hasFaceColor ) { | |
| colorIndex = faces[ offset ++ ]; | |
| hex = colors[ colorIndex ]; | |
| faceA.color.setHex( hex ); | |
| faceB.color.setHex( hex ); | |
| } | |
| if ( hasFaceVertexColor ) { | |
| for ( i = 0; i < 4; i ++ ) { | |
| colorIndex = faces[ offset ++ ]; | |
| hex = colors[ colorIndex ]; | |
| if ( i !== 2 ) faceA.vertexColors.push( new Color( hex ) ); | |
| if ( i !== 0 ) faceB.vertexColors.push( new Color( hex ) ); | |
| } | |
| } | |
| geometry.faces.push( faceA ); | |
| geometry.faces.push( faceB ); | |
| } else { | |
| face = new Face3(); | |
| face.a = faces[ offset ++ ]; | |
| face.b = faces[ offset ++ ]; | |
| face.c = faces[ offset ++ ]; | |
| if ( hasMaterial ) { | |
| materialIndex = faces[ offset ++ ]; | |
| face.materialIndex = materialIndex; | |
| } | |
| // to get face <=> uv index correspondence | |
| fi = geometry.faces.length; | |
| if ( hasFaceVertexUv ) { | |
| for ( i = 0; i < nUvLayers; i ++ ) { | |
| uvLayer = json.uvs[ i ]; | |
| geometry.faceVertexUvs[ i ][ fi ] = []; | |
| for ( j = 0; j < 3; j ++ ) { | |
| uvIndex = faces[ offset ++ ]; | |
| u = uvLayer[ uvIndex * 2 ]; | |
| v = uvLayer[ uvIndex * 2 + 1 ]; | |
| uv = new Vector2( u, v ); | |
| geometry.faceVertexUvs[ i ][ fi ].push( uv ); | |
| } | |
| } | |
| } | |
| if ( hasFaceNormal ) { | |
| normalIndex = faces[ offset ++ ] * 3; | |
| face.normal.set( | |
| normals[ normalIndex ++ ], | |
| normals[ normalIndex ++ ], | |
| normals[ normalIndex ] | |
| ); | |
| } | |
| if ( hasFaceVertexNormal ) { | |
| for ( i = 0; i < 3; i ++ ) { | |
| normalIndex = faces[ offset ++ ] * 3; | |
| normal = new Vector3( | |
| normals[ normalIndex ++ ], | |
| normals[ normalIndex ++ ], | |
| normals[ normalIndex ] | |
| ); | |
| face.vertexNormals.push( normal ); | |
| } | |
| } | |
| if ( hasFaceColor ) { | |
| colorIndex = faces[ offset ++ ]; | |
| face.color.setHex( colors[ colorIndex ] ); | |
| } | |
| if ( hasFaceVertexColor ) { | |
| for ( i = 0; i < 3; i ++ ) { | |
| colorIndex = faces[ offset ++ ]; | |
| face.vertexColors.push( new Color( colors[ colorIndex ] ) ); | |
| } | |
| } | |
| geometry.faces.push( face ); | |
| } | |
| } | |
| } | |
| function parseSkin( json, geometry ) { | |
| var influencesPerVertex = ( json.influencesPerVertex !== undefined ) ? json.influencesPerVertex : 2; | |
| if ( json.skinWeights ) { | |
| for ( var i = 0, l = json.skinWeights.length; i < l; i += influencesPerVertex ) { | |
| var x = json.skinWeights[ i ]; | |
| var y = ( influencesPerVertex > 1 ) ? json.skinWeights[ i + 1 ] : 0; | |
| var z = ( influencesPerVertex > 2 ) ? json.skinWeights[ i + 2 ] : 0; | |
| var w = ( influencesPerVertex > 3 ) ? json.skinWeights[ i + 3 ] : 0; | |
| geometry.skinWeights.push( new Vector4( x, y, z, w ) ); | |
| } | |
| } | |
| if ( json.skinIndices ) { | |
| for ( var i = 0, l = json.skinIndices.length; i < l; i += influencesPerVertex ) { | |
| var a = json.skinIndices[ i ]; | |
| var b = ( influencesPerVertex > 1 ) ? json.skinIndices[ i + 1 ] : 0; | |
| var c = ( influencesPerVertex > 2 ) ? json.skinIndices[ i + 2 ] : 0; | |
| var d = ( influencesPerVertex > 3 ) ? json.skinIndices[ i + 3 ] : 0; | |
| geometry.skinIndices.push( new Vector4( a, b, c, d ) ); | |
| } | |
| } | |
| geometry.bones = json.bones; | |
| if ( geometry.bones && geometry.bones.length > 0 && ( geometry.skinWeights.length !== geometry.skinIndices.length || geometry.skinIndices.length !== geometry.vertices.length ) ) { | |
| console.warn( 'When skinning, number of vertices (' + geometry.vertices.length + '), skinIndices (' + | |
| geometry.skinIndices.length + '), and skinWeights (' + geometry.skinWeights.length + ') should match.' ); | |
| } | |
| } | |
| function parseMorphing( json, geometry ) { | |
| var scale = json.scale; | |
| if ( json.morphTargets !== undefined ) { | |
| for ( var i = 0, l = json.morphTargets.length; i < l; i ++ ) { | |
| geometry.morphTargets[ i ] = {}; | |
| geometry.morphTargets[ i ].name = json.morphTargets[ i ].name; | |
| geometry.morphTargets[ i ].vertices = []; | |
| var dstVertices = geometry.morphTargets[ i ].vertices; | |
| var srcVertices = json.morphTargets[ i ].vertices; | |
| for ( var v = 0, vl = srcVertices.length; v < vl; v += 3 ) { | |
| var vertex = new Vector3(); | |
| vertex.x = srcVertices[ v ] * scale; | |
| vertex.y = srcVertices[ v + 1 ] * scale; | |
| vertex.z = srcVertices[ v + 2 ] * scale; | |
| dstVertices.push( vertex ); | |
| } | |
| } | |
| } | |
| if ( json.morphColors !== undefined && json.morphColors.length > 0 ) { | |
| console.warn( 'THREE.JSONLoader: "morphColors" no longer supported. Using them as face colors.' ); | |
| var faces = geometry.faces; | |
| var morphColors = json.morphColors[ 0 ].colors; | |
| for ( var i = 0, l = faces.length; i < l; i ++ ) { | |
| faces[ i ].color.fromArray( morphColors, i * 3 ); | |
| } | |
| } | |
| } | |
| function parseAnimations( json, geometry ) { | |
| var outputAnimations = []; | |
| // parse old style Bone/Hierarchy animations | |
| var animations = []; | |
| if ( json.animation !== undefined ) { | |
| animations.push( json.animation ); | |
| } | |
| if ( json.animations !== undefined ) { | |
| if ( json.animations.length ) { | |
| animations = animations.concat( json.animations ); | |
| } else { | |
| animations.push( json.animations ); | |
| } | |
| } | |
| for ( var i = 0; i < animations.length; i ++ ) { | |
| var clip = AnimationClip.parseAnimation( animations[ i ], geometry.bones ); | |
| if ( clip ) outputAnimations.push( clip ); | |
| } | |
| // parse implicit morph animations | |
| if ( geometry.morphTargets ) { | |
| // TODO: Figure out what an appropraite FPS is for morph target animations -- defaulting to 10, but really it is completely arbitrary. | |
| var morphAnimationClips = AnimationClip.CreateClipsFromMorphTargetSequences( geometry.morphTargets, 10 ); | |
| outputAnimations = outputAnimations.concat( morphAnimationClips ); | |
| } | |
| if ( outputAnimations.length > 0 ) geometry.animations = outputAnimations; | |
| } | |
| return function parse( json, texturePath ) { | |
| if ( json.data !== undefined ) { | |
| // Geometry 4.0 spec | |
| json = json.data; | |
| } | |
| if ( json.scale !== undefined ) { | |
| json.scale = 1.0 / json.scale; | |
| } else { | |
| json.scale = 1.0; | |
| } | |
| var geometry = new Geometry(); | |
| parseModel( json, geometry ); | |
| parseSkin( json, geometry ); | |
| parseMorphing( json, geometry ); | |
| parseAnimations( json, geometry ); | |
| geometry.computeFaceNormals(); | |
| geometry.computeBoundingSphere(); | |
| if ( json.materials === undefined || json.materials.length === 0 ) { | |
| return { geometry: geometry }; | |
| } else { | |
| var materials = Loader.prototype.initMaterials( json.materials, texturePath, this.crossOrigin ); | |
| return { geometry: geometry, materials: materials }; | |
| } | |
| }; | |
| } )() | |
| } ); | |
| /** | |
| * @author mrdoob / http://mrdoob.com/ | |
| */ | |
| function ObjectLoader( manager ) { | |
| this.manager = ( manager !== undefined ) ? manager : DefaultLoadingManager; | |
| this.texturePath = ''; | |
| } | |
| Object.assign( ObjectLoader.prototype, { | |
| load: function ( url, onLoad, onProgress, onError ) { | |
| if ( this.texturePath === '' ) { | |
| this.texturePath = url.substring( 0, url.lastIndexOf( '/' ) + 1 ); | |
| } | |
| var scope = this; | |
| var loader = new FileLoader( scope.manager ); | |
| loader.load( url, function ( text ) { | |
| var json = null; | |
| try { | |
| json = JSON.parse( text ); | |
| } catch ( error ) { | |
| if ( onError !== undefined ) onError( error ); | |
| console.error( 'THREE:ObjectLoader: Can\'t parse ' + url + '.', error.message ); | |
| return; | |
| } | |
| var metadata = json.metadata; | |
| if ( metadata === undefined || metadata.type === undefined || metadata.type.toLowerCase() === 'geometry' ) { | |
| console.error( 'THREE.ObjectLoader: Can\'t load ' + url + '. Use THREE.JSONLoader instead.' ); | |
| return; | |
| } | |
| scope.parse( json, onLoad ); | |
| }, onProgress, onError ); | |
| }, | |
| setTexturePath: function ( value ) { | |
| this.texturePath = value; | |
| }, | |
| setCrossOrigin: function ( value ) { | |
| this.crossOrigin = value; | |
| }, | |
| parse: function ( json, onLoad ) { | |
| var shapes = this.parseShape( json.shapes ); | |
| var geometries = this.parseGeometries( json.geometries, shapes ); | |
| var images = this.parseImages( json.images, function () { | |
| if ( onLoad !== undefined ) onLoad( object ); | |
| } ); | |
| var textures = this.parseTextures( json.textures, images ); | |
| var materials = this.parseMaterials( json.materials, textures ); | |
| var object = this.parseObject( json.object, geometries, materials ); | |
| if ( json.animations ) { | |
| object.animations = this.parseAnimations( json.animations ); | |
| } | |
| if ( json.images === undefined || json.images.length === 0 ) { | |
| if ( onLoad !== undefined ) onLoad( object ); | |
| } | |
| return object; | |
| }, | |
| parseShape: function ( json ) { | |
| var shapes = {}; | |
| if ( json !== undefined ) { | |
| for ( var i = 0, l = json.length; i < l; i ++ ) { | |
| var shape = new Shape().fromJSON( json[ i ] ); | |
| shapes[ shape.uuid ] = shape; | |
| } | |
| } | |
| return shapes; | |
| }, | |
| parseGeometries: function ( json, shapes ) { | |
| var geometries = {}; | |
| if ( json !== undefined ) { | |
| var geometryLoader = new JSONLoader(); | |
| var bufferGeometryLoader = new BufferGeometryLoader(); | |
| for ( var i = 0, l = json.length; i < l; i ++ ) { | |
| var geometry; | |
| var data = json[ i ]; | |
| switch ( data.type ) { | |
| case 'PlaneGeometry': | |
| case 'PlaneBufferGeometry': | |
| geometry = new Geometries[ data.type ]( | |
| data.width, | |
| data.height, | |
| data.widthSegments, | |
| data.heightSegments | |
| ); | |
| break; | |
| case 'BoxGeometry': | |
| case 'BoxBufferGeometry': | |
| case 'CubeGeometry': // backwards compatible | |
| geometry = new Geometries[ data.type ]( | |
| data.width, | |
| data.height, | |
| data.depth, | |
| data.widthSegments, | |
| data.heightSegments, | |
| data.depthSegments | |
| ); | |
| break; | |
| case 'CircleGeometry': | |
| case 'CircleBufferGeometry': | |
| geometry = new Geometries[ data.type ]( | |
| data.radius, | |
| data.segments, | |
| data.thetaStart, | |
| data.thetaLength | |
| ); | |
| break; | |
| case 'CylinderGeometry': | |
| case 'CylinderBufferGeometry': | |
| geometry = new Geometries[ data.type ]( | |
| data.radiusTop, | |
| data.radiusBottom, | |
| data.height, | |
| data.radialSegments, | |
| data.heightSegments, | |
| data.openEnded, | |
| data.thetaStart, | |
| data.thetaLength | |
| ); | |
| break; | |
| case 'ConeGeometry': | |
| case 'ConeBufferGeometry': | |
| geometry = new Geometries[ data.type ]( | |
| data.radius, | |
| data.height, | |
| data.radialSegments, | |
| data.heightSegments, | |
| data.openEnded, | |
| data.thetaStart, | |
| data.thetaLength | |
| ); | |
| break; | |
| case 'SphereGeometry': | |
| case 'SphereBufferGeometry': | |
| geometry = new Geometries[ data.type ]( | |
| data.radius, | |
| data.widthSegments, | |
| data.heightSegments, | |
| data.phiStart, | |
| data.phiLength, | |
| data.thetaStart, | |
| data.thetaLength | |
| ); | |
| break; | |
| case 'DodecahedronGeometry': | |
| case 'DodecahedronBufferGeometry': | |
| case 'IcosahedronGeometry': | |
| case 'IcosahedronBufferGeometry': | |
| case 'OctahedronGeometry': | |
| case 'OctahedronBufferGeometry': | |
| case 'TetrahedronGeometry': | |
| case 'TetrahedronBufferGeometry': | |
| geometry = new Geometries[ data.type ]( | |
| data.radius, | |
| data.detail | |
| ); | |
| break; | |
| case 'RingGeometry': | |
| case 'RingBufferGeometry': | |
| geometry = new Geometries[ data.type ]( | |
| data.innerRadius, | |
| data.outerRadius, | |
| data.thetaSegments, | |
| data.phiSegments, | |
| data.thetaStart, | |
| data.thetaLength | |
| ); | |
| break; | |
| case 'TorusGeometry': | |
| case 'TorusBufferGeometry': | |
| geometry = new Geometries[ data.type ]( | |
| data.radius, | |
| data.tube, | |
| data.radialSegments, | |
| data.tubularSegments, | |
| data.arc | |
| ); | |
| break; | |
| case 'TorusKnotGeometry': | |
| case 'TorusKnotBufferGeometry': | |
| geometry = new Geometries[ data.type ]( | |
| data.radius, | |
| data.tube, | |
| data.tubularSegments, | |
| data.radialSegments, | |
| data.p, | |
| data.q | |
| ); | |
| break; | |
| case 'LatheGeometry': | |
| case 'LatheBufferGeometry': | |
| geometry = new Geometries[ data.type ]( | |
| data.points, | |
| data.segments, | |
| data.phiStart, | |
| data.phiLength | |
| ); | |
| break; | |
| case 'PolyhedronGeometry': | |
| case 'PolyhedronBufferGeometry': | |
| geometry = new Geometries[ data.type ]( | |
| data.vertices, | |
| data.indices, | |
| data.radius, | |
| data.details | |
| ); | |
| break; | |
| case 'ShapeGeometry': | |
| case 'ShapeBufferGeometry': | |
| var geometryShapes = []; | |
| for ( var i = 0, l = data.shapes.length; i < l; i ++ ) { | |
| var shape = shapes[ data.shapes[ i ] ]; | |
| geometryShapes.push( shape ); | |
| } | |
| geometry = new Geometries[ data.type ]( | |
| geometryShapes, | |
| data.curveSegments | |
| ); | |
| break; | |
| case 'BufferGeometry': | |
| geometry = bufferGeometryLoader.parse( data ); | |
| break; | |
| case 'Geometry': | |
| geometry = geometryLoader.parse( data, this.texturePath ).geometry; | |
| break; | |
| default: | |
| console.warn( 'THREE.ObjectLoader: Unsupported geometry type "' + data.type + '"' ); | |
| continue; | |
| } | |
| geometry.uuid = data.uuid; | |
| if ( data.name !== undefined ) geometry.name = data.name; | |
| geometries[ data.uuid ] = geometry; | |
| } | |
| } | |
| return geometries; | |
| }, | |
| parseMaterials: function ( json, textures ) { | |
| var materials = {}; | |
| if ( json !== undefined ) { | |
| var loader = new MaterialLoader(); | |
| loader.setTextures( textures ); | |
| for ( var i = 0, l = json.length; i < l; i ++ ) { | |
| var data = json[ i ]; | |
| if ( data.type === 'MultiMaterial' ) { | |
| // Deprecated | |
| var array = []; | |
| for ( var j = 0; j < data.materials.length; j ++ ) { | |
| array.push( loader.parse( data.materials[ j ] ) ); | |
| } | |
| materials[ data.uuid ] = array; | |
| } else { | |
| materials[ data.uuid ] = loader.parse( data ); | |
| } | |
| } | |
| } | |
| return materials; | |
| }, | |
| parseAnimations: function ( json ) { | |
| var animations = []; | |
| for ( var i = 0; i < json.length; i ++ ) { | |
| var clip = AnimationClip.parse( json[ i ] ); | |
| animations.push( clip ); | |
| } | |
| return animations; | |
| }, | |
| parseImages: function ( json, onLoad ) { | |
| var scope = this; | |
| var images = {}; | |
| function loadImage( url ) { | |
| scope.manager.itemStart( url ); | |
| return loader.load( url, function () { | |
| scope.manager.itemEnd( url ); | |
| }, undefined, function () { | |
| scope.manager.itemEnd( url ); | |
| scope.manager.itemError( url ); | |
| } ); | |
| } | |
| if ( json !== undefined && json.length > 0 ) { | |
| var manager = new LoadingManager( onLoad ); | |
| var loader = new ImageLoader( manager ); | |
| loader.setCrossOrigin( this.crossOrigin ); | |
| for ( var i = 0, l = json.length; i < l; i ++ ) { | |
| var image = json[ i ]; | |
| var path = /^(\/\/)|([a-z]+:(\/\/)?)/i.test( image.url ) ? image.url : scope.texturePath + image.url; | |
| images[ image.uuid ] = loadImage( path ); | |
| } | |
| } | |
| return images; | |
| }, | |
| parseTextures: function ( json, images ) { | |
| function parseConstant( value, type ) { | |
| if ( typeof value === 'number' ) return value; | |
| console.warn( 'THREE.ObjectLoader.parseTexture: Constant should be in numeric form.', value ); | |
| return type[ value ]; | |
| } | |
| var textures = {}; | |
| if ( json !== undefined ) { | |
| for ( var i = 0, l = json.length; i < l; i ++ ) { | |
| var data = json[ i ]; | |
| if ( data.image === undefined ) { | |
| console.warn( 'THREE.ObjectLoader: No "image" specified for', data.uuid ); | |
| } | |
| if ( images[ data.image ] === undefined ) { | |
| console.warn( 'THREE.ObjectLoader: Undefined image', data.image ); | |
| } | |
| var texture = new Texture( images[ data.image ] ); | |
| texture.needsUpdate = true; | |
| texture.uuid = data.uuid; | |
| if ( data.name !== undefined ) texture.name = data.name; | |
| if ( data.mapping !== undefined ) texture.mapping = parseConstant( data.mapping, TEXTURE_MAPPING ); | |
| if ( data.offset !== undefined ) texture.offset.fromArray( data.offset ); | |
| if ( data.repeat !== undefined ) texture.repeat.fromArray( data.repeat ); | |
| if ( data.center !== undefined ) texture.center.fromArray( data.center ); | |
| if ( data.rotation !== undefined ) texture.rotation = data.rotation; | |
| if ( data.wrap !== undefined ) { | |
| texture.wrapS = parseConstant( data.wrap[ 0 ], TEXTURE_WRAPPING ); | |
| texture.wrapT = parseConstant( data.wrap[ 1 ], TEXTURE_WRAPPING ); | |
| } | |
| if ( data.minFilter !== undefined ) texture.minFilter = parseConstant( data.minFilter, TEXTURE_FILTER ); | |
| if ( data.magFilter !== undefined ) texture.magFilter = parseConstant( data.magFilter, TEXTURE_FILTER ); | |
| if ( data.anisotropy !== undefined ) texture.anisotropy = data.anisotropy; | |
| if ( data.flipY !== undefined ) texture.flipY = data.flipY; | |
| textures[ data.uuid ] = texture; | |
| } | |
| } | |
| return textures; | |
| }, | |
| parseObject: function ( data, geometries, materials ) { | |
| var object; | |
| function getGeometry( name ) { | |
| if ( geometries[ name ] === undefined ) { | |
| console.warn( 'THREE.ObjectLoader: Undefined geometry', name ); | |
| } | |
| return geometries[ name ]; | |
| } | |
| function getMaterial( name ) { | |
| if ( name === undefined ) return undefined; | |
| if ( Array.isArray( name ) ) { | |
| var array = []; | |
| for ( var i = 0, l = name.length; i < l; i ++ ) { | |
| var uuid = name[ i ]; | |
| if ( materials[ uuid ] === undefined ) { | |
| console.warn( 'THREE.ObjectLoader: Undefined material', uuid ); | |
| } | |
| array.push( materials[ uuid ] ); | |
| } | |
| return array; | |
| } | |
| if ( materials[ name ] === undefined ) { | |
| console.warn( 'THREE.ObjectLoader: Undefined material', name ); | |
| } | |
| return materials[ name ]; | |
| } | |
| switch ( data.type ) { | |
| case 'Scene': | |
| object = new Scene(); | |
| if ( data.background !== undefined ) { | |
| if ( Number.isInteger( data.background ) ) { | |
| object.background = new Color( data.background ); | |
| } | |
| } | |
| if ( data.fog !== undefined ) { | |
| if ( data.fog.type === 'Fog' ) { | |
| object.fog = new Fog( data.fog.color, data.fog.near, data.fog.far ); | |
| } else if ( data.fog.type === 'FogExp2' ) { | |
| object.fog = new FogExp2( data.fog.color, data.fog.density ); | |
| } | |
| } | |
| break; | |
| case 'PerspectiveCamera': | |
| object = new PerspectiveCamera( data.fov, data.aspect, data.near, data.far ); | |
| if ( data.focus !== undefined ) object.focus = data.focus; | |
| if ( data.zoom !== undefined ) object.zoom = data.zoom; | |
| if ( data.filmGauge !== undefined ) object.filmGauge = data.filmGauge; | |
| if ( data.filmOffset !== undefined ) object.filmOffset = data.filmOffset; | |
| if ( data.view !== undefined ) object.view = Object.assign( {}, data.view ); | |
| break; | |
| case 'OrthographicCamera': | |
| object = new OrthographicCamera( data.left, data.right, data.top, data.bottom, data.near, data.far ); | |
| break; | |
| case 'AmbientLight': | |
| object = new AmbientLight( data.color, data.intensity ); | |
| break; | |
| case 'DirectionalLight': | |
| object = new DirectionalLight( data.color, data.intensity ); | |
| break; | |
| case 'PointLight': | |
| object = new PointLight( data.color, data.intensity, data.distance, data.decay ); | |
| break; | |
| case 'RectAreaLight': | |
| object = new RectAreaLight( data.color, data.intensity, data.width, data.height ); | |
| break; | |
| case 'SpotLight': | |
| object = new SpotLight( data.color, data.intensity, data.distance, data.angle, data.penumbra, data.decay ); | |
| break; | |
| case 'HemisphereLight': | |
| object = new HemisphereLight( data.color, data.groundColor, data.intensity ); | |
| break; | |
| case 'SkinnedMesh': | |
| console.warn( 'THREE.ObjectLoader.parseObject() does not support SkinnedMesh yet.' ); | |
| case 'Mesh': | |
| var geometry = getGeometry( data.geometry ); | |
| var material = getMaterial( data.material ); | |
| if ( geometry.bones && geometry.bones.length > 0 ) { | |
| object = new SkinnedMesh( geometry, material ); | |
| } else { | |
| object = new Mesh( geometry, material ); | |
| } | |
| break; | |
| case 'LOD': | |
| object = new LOD(); | |
| break; | |
| case 'Line': | |
| object = new Line( getGeometry( data.geometry ), getMaterial( data.material ), data.mode ); | |
| break; | |
| case 'LineLoop': | |
| object = new LineLoop( getGeometry( data.geometry ), getMaterial( data.material ) ); | |
| break; | |
| case 'LineSegments': | |
| object = new LineSegments( getGeometry( data.geometry ), getMaterial( data.material ) ); | |
| break; | |
| case 'PointCloud': | |
| case 'Points': | |
| object = new Points( getGeometry( data.geometry ), getMaterial( data.material ) ); | |
| break; | |
| case 'Sprite': | |
| object = new Sprite( getMaterial( data.material ) ); | |
| break; | |
| case 'Group': | |
| object = new Group(); | |
| break; | |
| default: | |
| object = new Object3D(); | |
| } | |
| object.uuid = data.uuid; | |
| if ( data.name !== undefined ) object.name = data.name; | |
| if ( data.matrix !== undefined ) { | |
| object.matrix.fromArray( data.matrix ); | |
| object.matrix.decompose( object.position, object.quaternion, object.scale ); | |
| } else { | |
| if ( data.position !== undefined ) object.position.fromArray( data.position ); | |
| if ( data.rotation !== undefined ) object.rotation.fromArray( data.rotation ); | |
| if ( data.quaternion !== undefined ) object.quaternion.fromArray( data.quaternion ); | |
| if ( data.scale !== undefined ) object.scale.fromArray( data.scale ); | |
| } | |
| if ( data.castShadow !== undefined ) object.castShadow = data.castShadow; | |
| if ( data.receiveShadow !== undefined ) object.receiveShadow = data.receiveShadow; | |
| if ( data.shadow ) { | |
| if ( data.shadow.bias !== undefined ) object.shadow.bias = data.shadow.bias; | |
| if ( data.shadow.radius !== undefined ) object.shadow.radius = data.shadow.radius; | |
| if ( data.shadow.mapSize !== undefined ) object.shadow.mapSize.fromArray( data.shadow.mapSize ); | |
| if ( data.shadow.camera !== undefined ) object.shadow.camera = this.parseObject( data.shadow.camera ); | |
| } | |
| if ( data.visible !== undefined ) object.visible = data.visible; | |
| if ( data.userData !== undefined ) object.userData = data.userData; | |
| if ( data.children !== undefined ) { | |
| var children = data.children; | |
| for ( var i = 0; i < children.length; i ++ ) { | |
| object.add( this.parseObject( children[ i ], geometries, materials ) ); | |
| } | |
| } | |
| if ( data.type === 'LOD' ) { | |
| var levels = data.levels; | |
| for ( var l = 0; l < levels.length; l ++ ) { | |
| var level = levels[ l ]; | |
| var child = object.getObjectByProperty( 'uuid', level.object ); | |
| if ( child !== undefined ) { | |
| object.addLevel( child, level.distance ); | |
| } | |
| } | |
| } | |
| return object; | |
| } | |
| } ); | |
| var TEXTURE_MAPPING = { | |
| UVMapping: UVMapping, | |
| CubeReflectionMapping: CubeReflectionMapping, | |
| CubeRefractionMapping: CubeRefractionMapping, | |
| EquirectangularReflectionMapping: EquirectangularReflectionMapping, | |
| EquirectangularRefractionMapping: EquirectangularRefractionMapping, | |
| SphericalReflectionMapping: SphericalReflectionMapping, | |
| CubeUVReflectionMapping: CubeUVReflectionMapping, | |
| CubeUVRefractionMapping: CubeUVRefractionMapping | |
| }; | |
| var TEXTURE_WRAPPING = { | |
| RepeatWrapping: RepeatWrapping, | |
| ClampToEdgeWrapping: ClampToEdgeWrapping, | |
| MirroredRepeatWrapping: MirroredRepeatWrapping | |
| }; | |
| var TEXTURE_FILTER = { | |
| NearestFilter: NearestFilter, | |
| NearestMipMapNearestFilter: NearestMipMapNearestFilter, | |
| NearestMipMapLinearFilter: NearestMipMapLinearFilter, | |
| LinearFilter: LinearFilter, | |
| LinearMipMapNearestFilter: LinearMipMapNearestFilter, | |
| LinearMipMapLinearFilter: LinearMipMapLinearFilter | |
| }; | |
| /** | |
| * @author thespite / http://clicktorelease.com/ | |
| */ | |
| function ImageBitmapLoader( manager ) { | |
| if ( typeof createImageBitmap === 'undefined' ) { | |
| console.warn( 'THREE.ImageBitmapLoader: createImageBitmap() not supported.' ); | |
| } | |
| if ( typeof fetch === 'undefined' ) { | |
| console.warn( 'THREE.ImageBitmapLoader: fetch() not supported.' ); | |
| } | |
| this.manager = manager !== undefined ? manager : DefaultLoadingManager; | |
| this.options = undefined; | |
| } | |
| ImageBitmapLoader.prototype = { | |
| constructor: ImageBitmapLoader, | |
| setOptions: function setOptions( options ) { | |
| this.options = options; | |
| return this; | |
| }, | |
| load: function load( url, onLoad, onProgress, onError ) { | |
| if ( url === undefined ) url = ''; | |
| if ( this.path !== undefined ) url = this.path + url; | |
| var scope = this; | |
| var cached = Cache.get( url ); | |
| if ( cached !== undefined ) { | |
| scope.manager.itemStart( url ); | |
| setTimeout( function () { | |
| if ( onLoad ) onLoad( cached ); | |
| scope.manager.itemEnd( url ); | |
| }, 0 ); | |
| return cached; | |
| } | |
| fetch( url ).then( function ( res ) { | |
| return res.blob(); | |
| } ).then( function ( blob ) { | |
| return createImageBitmap( blob, scope.options ); | |
| } ).then( function ( imageBitmap ) { | |
| Cache.add( url, imageBitmap ); | |
| if ( onLoad ) onLoad( imageBitmap ); | |
| scope.manager.itemEnd( url ); | |
| } ).catch( function ( e ) { | |
| if ( onError ) onError( e ); | |
| scope.manager.itemEnd( url ); | |
| scope.manager.itemError( url ); | |
| } ); | |
| }, | |
| setCrossOrigin: function ( /* value */ ) { | |
| return this; | |
| }, | |
| setPath: function ( value ) { | |
| this.path = value; | |
| return this; | |
| } | |
| }; | |
| /** | |
| * @author zz85 / http://www.lab4games.net/zz85/blog | |
| * minimal class for proxing functions to Path. Replaces old "extractSubpaths()" | |
| **/ | |
| function ShapePath() { | |
| this.type = 'ShapePath'; | |
| this.subPaths = []; | |
| this.currentPath = null; | |
| } | |
| Object.assign( ShapePath.prototype, { | |
| moveTo: function ( x, y ) { | |
| this.currentPath = new Path(); | |
| this.subPaths.push( this.currentPath ); | |
| this.currentPath.moveTo( x, y ); | |
| }, | |
| lineTo: function ( x, y ) { | |
| this.currentPath.lineTo( x, y ); | |
| }, | |
| quadraticCurveTo: function ( aCPx, aCPy, aX, aY ) { | |
| this.currentPath.quadraticCurveTo( aCPx, aCPy, aX, aY ); | |
| }, | |
| bezierCurveTo: function ( aCP1x, aCP1y, aCP2x, aCP2y, aX, aY ) { | |
| this.currentPath.bezierCurveTo( aCP1x, aCP1y, aCP2x, aCP2y, aX, aY ); | |
| }, | |
| splineThru: function ( pts ) { | |
| this.currentPath.splineThru( pts ); | |
| }, | |
| toShapes: function ( isCCW, noHoles ) { | |
| function toShapesNoHoles( inSubpaths ) { | |
| var shapes = []; | |
| for ( var i = 0, l = inSubpaths.length; i < l; i ++ ) { | |
| var tmpPath = inSubpaths[ i ]; | |
| var tmpShape = new Shape(); | |
| tmpShape.curves = tmpPath.curves; | |
| shapes.push( tmpShape ); | |
| } | |
| return shapes; | |
| } | |
| function isPointInsidePolygon( inPt, inPolygon ) { | |
| var polyLen = inPolygon.length; | |
| // inPt on polygon contour => immediate success or | |
| // toggling of inside/outside at every single! intersection point of an edge | |
| // with the horizontal line through inPt, left of inPt | |
| // not counting lowerY endpoints of edges and whole edges on that line | |
| var inside = false; | |
| for ( var p = polyLen - 1, q = 0; q < polyLen; p = q ++ ) { | |
| var edgeLowPt = inPolygon[ p ]; | |
| var edgeHighPt = inPolygon[ q ]; | |
| var edgeDx = edgeHighPt.x - edgeLowPt.x; | |
| var edgeDy = edgeHighPt.y - edgeLowPt.y; | |
| if ( Math.abs( edgeDy ) > Number.EPSILON ) { | |
| // not parallel | |
| if ( edgeDy < 0 ) { | |
| edgeLowPt = inPolygon[ q ]; edgeDx = - edgeDx; | |
| edgeHighPt = inPolygon[ p ]; edgeDy = - edgeDy; | |
| } | |
| if ( ( inPt.y < edgeLowPt.y ) || ( inPt.y > edgeHighPt.y ) ) continue; | |
| if ( inPt.y === edgeLowPt.y ) { | |
| if ( inPt.x === edgeLowPt.x ) return true; // inPt is on contour ? | |
| // continue; // no intersection or edgeLowPt => doesn't count !!! | |
| } else { | |
| var perpEdge = edgeDy * ( inPt.x - edgeLowPt.x ) - edgeDx * ( inPt.y - edgeLowPt.y ); | |
| if ( perpEdge === 0 ) return true; // inPt is on contour ? | |
| if ( perpEdge < 0 ) continue; | |
| inside = ! inside; // true intersection left of inPt | |
| } | |
| } else { | |
| // parallel or collinear | |
| if ( inPt.y !== edgeLowPt.y ) continue; // parallel | |
| // edge lies on the same horizontal line as inPt | |
| if ( ( ( edgeHighPt.x <= inPt.x ) && ( inPt.x <= edgeLowPt.x ) ) || | |
| ( ( edgeLowPt.x <= inPt.x ) && ( inPt.x <= edgeHighPt.x ) ) ) return true; // inPt: Point on contour ! | |
| // continue; | |
| } | |
| } | |
| return inside; | |
| } | |
| var isClockWise = ShapeUtils.isClockWise; | |
| var subPaths = this.subPaths; | |
| if ( subPaths.length === 0 ) return []; | |
| if ( noHoles === true ) return toShapesNoHoles( subPaths ); | |
| var solid, tmpPath, tmpShape, shapes = []; | |
| if ( subPaths.length === 1 ) { | |
| tmpPath = subPaths[ 0 ]; | |
| tmpShape = new Shape(); | |
| tmpShape.curves = tmpPath.curves; | |
| shapes.push( tmpShape ); | |
| return shapes; | |
| } | |
| var holesFirst = ! isClockWise( subPaths[ 0 ].getPoints() ); | |
| holesFirst = isCCW ? ! holesFirst : holesFirst; | |
| // console.log("Holes first", holesFirst); | |
| var betterShapeHoles = []; | |
| var newShapes = []; | |
| var newShapeHoles = []; | |
| var mainIdx = 0; | |
| var tmpPoints; | |
| newShapes[ mainIdx ] = undefined; | |
| newShapeHoles[ mainIdx ] = []; | |
| for ( var i = 0, l = subPaths.length; i < l; i ++ ) { | |
| tmpPath = subPaths[ i ]; | |
| tmpPoints = tmpPath.getPoints(); | |
| solid = isClockWise( tmpPoints ); | |
| solid = isCCW ? ! solid : solid; | |
| if ( solid ) { | |
| if ( ( ! holesFirst ) && ( newShapes[ mainIdx ] ) ) mainIdx ++; | |
| newShapes[ mainIdx ] = { s: new Shape(), p: tmpPoints }; | |
| newShapes[ mainIdx ].s.curves = tmpPath.curves; | |
| if ( holesFirst ) mainIdx ++; | |
| newShapeHoles[ mainIdx ] = []; | |
| //console.log('cw', i); | |
| } else { | |
| newShapeHoles[ mainIdx ].push( { h: tmpPath, p: tmpPoints[ 0 ] } ); | |
| //console.log('ccw', i); | |
| } | |
| } | |
| // only Holes? -> probably all Shapes with wrong orientation | |
| if ( ! newShapes[ 0 ] ) return toShapesNoHoles( subPaths ); | |
| if ( newShapes.length > 1 ) { | |
| var ambiguous = false; | |
| var toChange = []; | |
| for ( var sIdx = 0, sLen = newShapes.length; sIdx < sLen; sIdx ++ ) { | |
| betterShapeHoles[ sIdx ] = []; | |
| } | |
| for ( var sIdx = 0, sLen = newShapes.length; sIdx < sLen; sIdx ++ ) { | |
| var sho = newShapeHoles[ sIdx ]; | |
| for ( var hIdx = 0; hIdx < sho.length; hIdx ++ ) { | |
| var ho = sho[ hIdx ]; | |
| var hole_unassigned = true; | |
| for ( var s2Idx = 0; s2Idx < newShapes.length; s2Idx ++ ) { | |
| if ( isPointInsidePolygon( ho.p, newShapes[ s2Idx ].p ) ) { | |
| if ( sIdx !== s2Idx ) toChange.push( { froms: sIdx, tos: s2Idx, hole: hIdx } ); | |
| if ( hole_unassigned ) { | |
| hole_unassigned = false; | |
| betterShapeHoles[ s2Idx ].push( ho ); | |
| } else { | |
| ambiguous = true; | |
| } | |
| } | |
| } | |
| if ( hole_unassigned ) { | |
| betterShapeHoles[ sIdx ].push( ho ); | |
| } | |
| } | |
| } | |
| // console.log("ambiguous: ", ambiguous); | |
| if ( toChange.length > 0 ) { | |
| // console.log("to change: ", toChange); | |
| if ( ! ambiguous ) newShapeHoles = betterShapeHoles; | |
| } | |
| } | |
| var tmpHoles; | |
| for ( var i = 0, il = newShapes.length; i < il; i ++ ) { | |
| tmpShape = newShapes[ i ].s; | |
| shapes.push( tmpShape ); | |
| tmpHoles = newShapeHoles[ i ]; | |
| for ( var j = 0, jl = tmpHoles.length; j < jl; j ++ ) { | |
| tmpShape.holes.push( tmpHoles[ j ].h ); | |
| } | |
| } | |
| //console.log("shape", shapes); | |
| return shapes; | |
| } | |
| } ); | |
| /** | |
| * @author zz85 / http://www.lab4games.net/zz85/blog | |
| * @author mrdoob / http://mrdoob.com/ | |
| */ | |
| function Font( data ) { | |
| this.type = 'Font'; | |
| this.data = data; | |
| } | |
| Object.assign( Font.prototype, { | |
| isFont: true, | |
| generateShapes: function ( text, size, divisions ) { | |
| if ( size === undefined ) size = 100; | |
| if ( divisions === undefined ) divisions = 4; | |
| var shapes = []; | |
| var paths = createPaths( text, size, divisions, this.data ); | |
| for ( var p = 0, pl = paths.length; p < pl; p ++ ) { | |
| Array.prototype.push.apply( shapes, paths[ p ].toShapes() ); | |
| } | |
| return shapes; | |
| } | |
| } ); | |
| function createPaths( text, size, divisions, data ) { | |
| var chars = String( text ).split( '' ); | |
| var scale = size / data.resolution; | |
| var line_height = ( data.boundingBox.yMax - data.boundingBox.yMin + data.underlineThickness ) * scale; | |
| var paths = []; | |
| var offsetX = 0, offsetY = 0; | |
| for ( var i = 0; i < chars.length; i ++ ) { | |
| var char = chars[ i ]; | |
| if ( char === '\n' ) { | |
| offsetX = 0; | |
| offsetY -= line_height; | |
| } else { | |
| var ret = createPath( char, divisions, scale, offsetX, offsetY, data ); | |
| offsetX += ret.offsetX; | |
| paths.push( ret.path ); | |
| } | |
| } | |
| return paths; | |
| } | |
| function createPath( char, divisions, scale, offsetX, offsetY, data ) { | |
| var glyph = data.glyphs[ char ] || data.glyphs[ '?' ]; | |
| if ( ! glyph ) return; | |
| var path = new ShapePath(); | |
| var x, y, cpx, cpy, cpx1, cpy1, cpx2, cpy2; | |
| if ( glyph.o ) { | |
| var outline = glyph._cachedOutline || ( glyph._cachedOutline = glyph.o.split( ' ' ) ); | |
| for ( var i = 0, l = outline.length; i < l; ) { | |
| var action = outline[ i ++ ]; | |
| switch ( action ) { | |
| case 'm': // moveTo | |
| x = outline[ i ++ ] * scale + offsetX; | |
| y = outline[ i ++ ] * scale + offsetY; | |
| path.moveTo( x, y ); | |
| break; | |
| case 'l': // lineTo | |
| x = outline[ i ++ ] * scale + offsetX; | |
| y = outline[ i ++ ] * scale + offsetY; | |
| path.lineTo( x, y ); | |
| break; | |
| case 'q': // quadraticCurveTo | |
| cpx = outline[ i ++ ] * scale + offsetX; | |
| cpy = outline[ i ++ ] * scale + offsetY; | |
| cpx1 = outline[ i ++ ] * scale + offsetX; | |
| cpy1 = outline[ i ++ ] * scale + offsetY; | |
| path.quadraticCurveTo( cpx1, cpy1, cpx, cpy ); | |
| break; | |
| case 'b': // bezierCurveTo | |
| cpx = outline[ i ++ ] * scale + offsetX; | |
| cpy = outline[ i ++ ] * scale + offsetY; | |
| cpx1 = outline[ i ++ ] * scale + offsetX; | |
| cpy1 = outline[ i ++ ] * scale + offsetY; | |
| cpx2 = outline[ i ++ ] * scale + offsetX; | |
| cpy2 = outline[ i ++ ] * scale + offsetY; | |
| path.bezierCurveTo( cpx1, cpy1, cpx2, cpy2, cpx, cpy ); | |
| break; | |
| } | |
| } | |
| } | |
| return { offsetX: glyph.ha * scale, path: path }; | |
| } | |
| /** | |
| * @author mrdoob / http://mrdoob.com/ | |
| */ | |
| function FontLoader( manager ) { | |
| this.manager = ( manager !== undefined ) ? manager : DefaultLoadingManager; | |
| } | |
| Object.assign( FontLoader.prototype, { | |
| load: function ( url, onLoad, onProgress, onError ) { | |
| var scope = this; | |
| var loader = new FileLoader( this.manager ); | |
| loader.setPath( this.path ); | |
| loader.load( url, function ( text ) { | |
| var json; | |
| try { | |
| json = JSON.parse( text ); | |
| } catch ( e ) { | |
| console.warn( 'THREE.FontLoader: typeface.js support is being deprecated. Use typeface.json instead.' ); | |
| json = JSON.parse( text.substring( 65, text.length - 2 ) ); | |
| } | |
| var font = scope.parse( json ); | |
| if ( onLoad ) onLoad( font ); | |
| }, onProgress, onError ); | |
| }, | |
| parse: function ( json ) { | |
| return new Font( json ); | |
| }, | |
| setPath: function ( value ) { | |
| this.path = value; | |
| return this; | |
| } | |
| } ); | |
| /** | |
| * @author mrdoob / http://mrdoob.com/ | |
| */ | |
| var context; | |
| var AudioContext = { | |
| getContext: function () { | |
| if ( context === undefined ) { | |
| context = new ( window.AudioContext || window.webkitAudioContext )(); | |
| } | |
| return context; | |
| }, | |
| setContext: function ( value ) { | |
| context = value; | |
| } | |
| }; | |
| /** | |
| * @author Reece Aaron Lecrivain / http://reecenotes.com/ | |
| */ | |
| function AudioLoader( manager ) { | |
| this.manager = ( manager !== undefined ) ? manager : DefaultLoadingManager; | |
| } | |
| Object.assign( AudioLoader.prototype, { | |
| load: function ( url, onLoad, onProgress, onError ) { | |
| var loader = new FileLoader( this.manager ); | |
| loader.setResponseType( 'arraybuffer' ); | |
| loader.load( url, function ( buffer ) { | |
| var context = AudioContext.getContext(); | |
| context.decodeAudioData( buffer, function ( audioBuffer ) { | |
| onLoad( audioBuffer ); | |
| } ); | |
| }, onProgress, onError ); | |
| } | |
| } ); | |
| /** | |
| * @author mrdoob / http://mrdoob.com/ | |
| */ | |
| function StereoCamera() { | |
| this.type = 'StereoCamera'; | |
| this.aspect = 1; | |
| this.eyeSep = 0.064; | |
| this.cameraL = new PerspectiveCamera(); | |
| this.cameraL.layers.enable( 1 ); | |
| this.cameraL.matrixAutoUpdate = false; | |
| this.cameraR = new PerspectiveCamera(); | |
| this.cameraR.layers.enable( 2 ); | |
| this.cameraR.matrixAutoUpdate = false; | |
| } | |
| Object.assign( StereoCamera.prototype, { | |
| update: ( function () { | |
| var instance, focus, fov, aspect, near, far, zoom, eyeSep; | |
| var eyeRight = new Matrix4(); | |
| var eyeLeft = new Matrix4(); | |
| return function update( camera ) { | |
| var needsUpdate = instance !== this || focus !== camera.focus || fov !== camera.fov || | |
| aspect !== camera.aspect * this.aspect || near !== camera.near || | |
| far !== camera.far || zoom !== camera.zoom || eyeSep !== this.eyeSep; | |
| if ( needsUpdate ) { | |
| instance = this; | |
| focus = camera.focus; | |
| fov = camera.fov; | |
| aspect = camera.aspect * this.aspect; | |
| near = camera.near; | |
| far = camera.far; | |
| zoom = camera.zoom; | |
| // Off-axis stereoscopic effect based on | |
| // http://paulbourke.net/stereographics/stereorender/ | |
| var projectionMatrix = camera.projectionMatrix.clone(); | |
| eyeSep = this.eyeSep / 2; | |
| var eyeSepOnProjection = eyeSep * near / focus; | |
| var ymax = ( near * Math.tan( _Math.DEG2RAD * fov * 0.5 ) ) / zoom; | |
| var xmin, xmax; | |
| // translate xOffset | |
| eyeLeft.elements[ 12 ] = - eyeSep; | |
| eyeRight.elements[ 12 ] = eyeSep; | |
| // for left eye | |
| xmin = - ymax * aspect + eyeSepOnProjection; | |
| xmax = ymax * aspect + eyeSepOnProjection; | |
| projectionMatrix.elements[ 0 ] = 2 * near / ( xmax - xmin ); | |
| projectionMatrix.elements[ 8 ] = ( xmax + xmin ) / ( xmax - xmin ); | |
| this.cameraL.projectionMatrix.copy( projectionMatrix ); | |
| // for right eye | |
| xmin = - ymax * aspect - eyeSepOnProjection; | |
| xmax = ymax * aspect - eyeSepOnProjection; | |
| projectionMatrix.elements[ 0 ] = 2 * near / ( xmax - xmin ); | |
| projectionMatrix.elements[ 8 ] = ( xmax + xmin ) / ( xmax - xmin ); | |
| this.cameraR.projectionMatrix.copy( projectionMatrix ); | |
| } | |
| this.cameraL.matrixWorld.copy( camera.matrixWorld ).multiply( eyeLeft ); | |
| this.cameraR.matrixWorld.copy( camera.matrixWorld ).multiply( eyeRight ); | |
| }; | |
| } )() | |
| } ); | |
| /** | |
| * Camera for rendering cube maps | |
| * - renders scene into axis-aligned cube | |
| * | |
| * @author alteredq / http://alteredqualia.com/ | |
| */ | |
| function CubeCamera( near, far, cubeResolution ) { | |
| Object3D.call( this ); | |
| this.type = 'CubeCamera'; | |
| var fov = 90, aspect = 1; | |
| var cameraPX = new PerspectiveCamera( fov, aspect, near, far ); | |
| cameraPX.up.set( 0, - 1, 0 ); | |
| cameraPX.lookAt( new Vector3( 1, 0, 0 ) ); | |
| this.add( cameraPX ); | |
| var cameraNX = new PerspectiveCamera( fov, aspect, near, far ); | |
| cameraNX.up.set( 0, - 1, 0 ); | |
| cameraNX.lookAt( new Vector3( - 1, 0, 0 ) ); | |
| this.add( cameraNX ); | |
| var cameraPY = new PerspectiveCamera( fov, aspect, near, far ); | |
| cameraPY.up.set( 0, 0, 1 ); | |
| cameraPY.lookAt( new Vector3( 0, 1, 0 ) ); | |
| this.add( cameraPY ); | |
| var cameraNY = new PerspectiveCamera( fov, aspect, near, far ); | |
| cameraNY.up.set( 0, 0, - 1 ); | |
| cameraNY.lookAt( new Vector3( 0, - 1, 0 ) ); | |
| this.add( cameraNY ); | |
| var cameraPZ = new PerspectiveCamera( fov, aspect, near, far ); | |
| cameraPZ.up.set( 0, - 1, 0 ); | |
| cameraPZ.lookAt( new Vector3( 0, 0, 1 ) ); | |
| this.add( cameraPZ ); | |
| var cameraNZ = new PerspectiveCamera( fov, aspect, near, far ); | |
| cameraNZ.up.set( 0, - 1, 0 ); | |
| cameraNZ.lookAt( new Vector3( 0, 0, - 1 ) ); | |
| this.add( cameraNZ ); | |
| var options = { format: RGBFormat, magFilter: LinearFilter, minFilter: LinearFilter }; | |
| this.renderTarget = new WebGLRenderTargetCube( cubeResolution, cubeResolution, options ); | |
| this.renderTarget.texture.name = "CubeCamera"; | |
| this.update = function ( renderer, scene ) { | |
| if ( this.parent === null ) this.updateMatrixWorld(); | |
| var renderTarget = this.renderTarget; | |
| var generateMipmaps = renderTarget.texture.generateMipmaps; | |
| renderTarget.texture.generateMipmaps = false; | |
| renderTarget.activeCubeFace = 0; | |
| renderer.render( scene, cameraPX, renderTarget ); | |
| renderTarget.activeCubeFace = 1; | |
| renderer.render( scene, cameraNX, renderTarget ); | |
| renderTarget.activeCubeFace = 2; | |
| renderer.render( scene, cameraPY, renderTarget ); | |
| renderTarget.activeCubeFace = 3; | |
| renderer.render( scene, cameraNY, renderTarget ); | |
| renderTarget.activeCubeFace = 4; | |
| renderer.render( scene, cameraPZ, renderTarget ); | |
| renderTarget.texture.generateMipmaps = generateMipmaps; | |
| renderTarget.activeCubeFace = 5; | |
| renderer.render( scene, cameraNZ, renderTarget ); | |
| renderer.setRenderTarget( null ); | |
| }; | |
| this.clear = function ( renderer, color, depth, stencil ) { | |
| var renderTarget = this.renderTarget; | |
| for ( var i = 0; i < 6; i ++ ) { | |
| renderTarget.activeCubeFace = i; | |
| renderer.setRenderTarget( renderTarget ); | |
| renderer.clear( color, depth, stencil ); | |
| } | |
| renderer.setRenderTarget( null ); | |
| }; | |
| } | |
| CubeCamera.prototype = Object.create( Object3D.prototype ); | |
| CubeCamera.prototype.constructor = CubeCamera; | |
| /** | |
| * @author mrdoob / http://mrdoob.com/ | |
| */ | |
| function AudioListener() { | |
| Object3D.call( this ); | |
| this.type = 'AudioListener'; | |
| this.context = AudioContext.getContext(); | |
| this.gain = this.context.createGain(); | |
| this.gain.connect( this.context.destination ); | |
| this.filter = null; | |
| } | |
| AudioListener.prototype = Object.assign( Object.create( Object3D.prototype ), { | |
| constructor: AudioListener, | |
| getInput: function () { | |
| return this.gain; | |
| }, | |
| removeFilter: function ( ) { | |
| if ( this.filter !== null ) { | |
| this.gain.disconnect( this.filter ); | |
| this.filter.disconnect( this.context.destination ); | |
| this.gain.connect( this.context.destination ); | |
| this.filter = null; | |
| } | |
| }, | |
| getFilter: function () { | |
| return this.filter; | |
| }, | |
| setFilter: function ( value ) { | |
| if ( this.filter !== null ) { | |
| this.gain.disconnect( this.filter ); | |
| this.filter.disconnect( this.context.destination ); | |
| } else { | |
| this.gain.disconnect( this.context.destination ); | |
| } | |
| this.filter = value; | |
| this.gain.connect( this.filter ); | |
| this.filter.connect( this.context.destination ); | |
| }, | |
| getMasterVolume: function () { | |
| return this.gain.gain.value; | |
| }, | |
| setMasterVolume: function ( value ) { | |
| this.gain.gain.value = value; | |
| }, | |
| updateMatrixWorld: ( function () { | |
| var position = new Vector3(); | |
| var quaternion = new Quaternion(); | |
| var scale = new Vector3(); | |
| var orientation = new Vector3(); | |
| return function updateMatrixWorld( force ) { | |
| Object3D.prototype.updateMatrixWorld.call( this, force ); | |
| var listener = this.context.listener; | |
| var up = this.up; | |
| this.matrixWorld.decompose( position, quaternion, scale ); | |
| orientation.set( 0, 0, - 1 ).applyQuaternion( quaternion ); | |
| if ( listener.positionX ) { | |
| listener.positionX.setValueAtTime( position.x, this.context.currentTime ); | |
| listener.positionY.setValueAtTime( position.y, this.context.currentTime ); | |
| listener.positionZ.setValueAtTime( position.z, this.context.currentTime ); | |
| listener.forwardX.setValueAtTime( orientation.x, this.context.currentTime ); | |
| listener.forwardY.setValueAtTime( orientation.y, this.context.currentTime ); | |
| listener.forwardZ.setValueAtTime( orientation.z, this.context.currentTime ); | |
| listener.upX.setValueAtTime( up.x, this.context.currentTime ); | |
| listener.upY.setValueAtTime( up.y, this.context.currentTime ); | |
| listener.upZ.setValueAtTime( up.z, this.context.currentTime ); | |
| } else { | |
| listener.setPosition( position.x, position.y, position.z ); | |
| listener.setOrientation( orientation.x, orientation.y, orientation.z, up.x, up.y, up.z ); | |
| } | |
| }; | |
| } )() | |
| } ); | |
| /** | |
| * @author mrdoob / http://mrdoob.com/ | |
| * @author Reece Aaron Lecrivain / http://reecenotes.com/ | |
| */ | |
| function Audio( listener ) { | |
| Object3D.call( this ); | |
| this.type = 'Audio'; | |
| this.context = listener.context; | |
| this.gain = this.context.createGain(); | |
| this.gain.connect( listener.getInput() ); | |
| this.autoplay = false; | |
| this.buffer = null; | |
| this.loop = false; | |
| this.startTime = 0; | |
| this.offset = 0; | |
| this.playbackRate = 1; | |
| this.isPlaying = false; | |
| this.hasPlaybackControl = true; | |
| this.sourceType = 'empty'; | |
| this.filters = []; | |
| } | |
| Audio.prototype = Object.assign( Object.create( Object3D.prototype ), { | |
| constructor: Audio, | |
| getOutput: function () { | |
| return this.gain; | |
| }, | |
| setNodeSource: function ( audioNode ) { | |
| this.hasPlaybackControl = false; | |
| this.sourceType = 'audioNode'; | |
| this.source = audioNode; | |
| this.connect(); | |
| return this; | |
| }, | |
| setBuffer: function ( audioBuffer ) { | |
| this.buffer = audioBuffer; | |
| this.sourceType = 'buffer'; | |
| if ( this.autoplay ) this.play(); | |
| return this; | |
| }, | |
| play: function () { | |
| if ( this.isPlaying === true ) { | |
| console.warn( 'THREE.Audio: Audio is already playing.' ); | |
| return; | |
| } | |
| if ( this.hasPlaybackControl === false ) { | |
| console.warn( 'THREE.Audio: this Audio has no playback control.' ); | |
| return; | |
| } | |
| var source = this.context.createBufferSource(); | |
| source.buffer = this.buffer; | |
| source.loop = this.loop; | |
| source.onended = this.onEnded.bind( this ); | |
| source.playbackRate.setValueAtTime( this.playbackRate, this.startTime ); | |
| this.startTime = this.context.currentTime; | |
| source.start( this.startTime, this.offset ); | |
| this.isPlaying = true; | |
| this.source = source; | |
| return this.connect(); | |
| }, | |
| pause: function () { | |
| if ( this.hasPlaybackControl === false ) { | |
| console.warn( 'THREE.Audio: this Audio has no playback control.' ); | |
| return; | |
| } | |
| if ( this.isPlaying === true ) { | |
| this.source.stop(); | |
| this.offset += ( this.context.currentTime - this.startTime ) * this.playbackRate; | |
| this.isPlaying = false; | |
| } | |
| return this; | |
| }, | |
| stop: function () { | |
| if ( this.hasPlaybackControl === false ) { | |
| console.warn( 'THREE.Audio: this Audio has no playback control.' ); | |
| return; | |
| } | |
| this.source.stop(); | |
| this.offset = 0; | |
| this.isPlaying = false; | |
| return this; | |
| }, | |
| connect: function () { | |
| if ( this.filters.length > 0 ) { | |
| this.source.connect( this.filters[ 0 ] ); | |
| for ( var i = 1, l = this.filters.length; i < l; i ++ ) { | |
| this.filters[ i - 1 ].connect( this.filters[ i ] ); | |
| } | |
| this.filters[ this.filters.length - 1 ].connect( this.getOutput() ); | |
| } else { | |
| this.source.connect( this.getOutput() ); | |
| } | |
| return this; | |
| }, | |
| disconnect: function () { | |
| if ( this.filters.length > 0 ) { | |
| this.source.disconnect( this.filters[ 0 ] ); | |
| for ( var i = 1, l = this.filters.length; i < l; i ++ ) { | |
| this.filters[ i - 1 ].disconnect( this.filters[ i ] ); | |
| } | |
| this.filters[ this.filters.length - 1 ].disconnect( this.getOutput() ); | |
| } else { | |
| this.source.disconnect( this.getOutput() ); | |
| } | |
| return this; | |
| }, | |
| getFilters: function () { | |
| return this.filters; | |
| }, | |
| setFilters: function ( value ) { | |
| if ( ! value ) value = []; | |
| if ( this.isPlaying === true ) { | |
| this.disconnect(); | |
| this.filters = value; | |
| this.connect(); | |
| } else { | |
| this.filters = value; | |
| } | |
| return this; | |
| }, | |
| getFilter: function () { | |
| return this.getFilters()[ 0 ]; | |
| }, | |
| setFilter: function ( filter ) { | |
| return this.setFilters( filter ? [ filter ] : [] ); | |
| }, | |
| setPlaybackRate: function ( value ) { | |
| if ( this.hasPlaybackControl === false ) { | |
| console.warn( 'THREE.Audio: this Audio has no playback control.' ); | |
| return; | |
| } | |
| this.playbackRate = value; | |
| if ( this.isPlaying === true ) { | |
| this.source.playbackRate.setValueAtTime( this.playbackRate, this.context.currentTime ); | |
| } | |
| return this; | |
| }, | |
| getPlaybackRate: function () { | |
| return this.playbackRate; | |
| }, | |
| onEnded: function () { | |
| this.isPlaying = false; | |
| }, | |
| getLoop: function () { | |
| if ( this.hasPlaybackControl === false ) { | |
| console.warn( 'THREE.Audio: this Audio has no playback control.' ); | |
| return false; | |
| } | |
| return this.loop; | |
| }, | |
| setLoop: function ( value ) { | |
| if ( this.hasPlaybackControl === false ) { | |
| console.warn( 'THREE.Audio: this Audio has no playback control.' ); | |
| return; | |
| } | |
| this.loop = value; | |
| if ( this.isPlaying === true ) { | |
| this.source.loop = this.loop; | |
| } | |
| return this; | |
| }, | |
| getVolume: function () { | |
| return this.gain.gain.value; | |
| }, | |
| setVolume: function ( value ) { | |
| this.gain.gain.value = value; | |
| return this; | |
| } | |
| } ); | |
| /** | |
| * @author mrdoob / http://mrdoob.com/ | |
| */ | |
| function PositionalAudio( listener ) { | |
| Audio.call( this, listener ); | |
| this.panner = this.context.createPanner(); | |
| this.panner.connect( this.gain ); | |
| } | |
| PositionalAudio.prototype = Object.assign( Object.create( Audio.prototype ), { | |
| constructor: PositionalAudio, | |
| getOutput: function () { | |
| return this.panner; | |
| }, | |
| getRefDistance: function () { | |
| return this.panner.refDistance; | |
| }, | |
| setRefDistance: function ( value ) { | |
| this.panner.refDistance = value; | |
| }, | |
| getRolloffFactor: function () { | |
| return this.panner.rolloffFactor; | |
| }, | |
| setRolloffFactor: function ( value ) { | |
| this.panner.rolloffFactor = value; | |
| }, | |
| getDistanceModel: function () { | |
| return this.panner.distanceModel; | |
| }, | |
| setDistanceModel: function ( value ) { | |
| this.panner.distanceModel = value; | |
| }, | |
| getMaxDistance: function () { | |
| return this.panner.maxDistance; | |
| }, | |
| setMaxDistance: function ( value ) { | |
| this.panner.maxDistance = value; | |
| }, | |
| updateMatrixWorld: ( function () { | |
| var position = new Vector3(); | |
| return function updateMatrixWorld( force ) { | |
| Object3D.prototype.updateMatrixWorld.call( this, force ); |
Webstorm is telling me this. (Got this code from the small tutorial in the comments). Maybe
if (error)would be better