/* eslint-disable */ // @ts-nocheck // Copyright 2010-2023 Three.js Authors // SPDX-License-Identifier: MIT /* eslint-disable no-useless-escape */ import { Quaternion, Vector3, Vector4, Matrix4, Color, Ray, generateUUID } from './math'; // constants.js let NoBlending = 0; let NormalBlending = 1; // core/EventDispatcher.js class EventDispatcher { addEventListener(type, listener) { if (this._listeners === undefined) this._listeners = {}; const listeners = this._listeners; if (listeners[type] === undefined) { listeners[type] = []; } if (listeners[type].indexOf(listener) === -1) { listeners[type].push(listener); } } removeEventListener(type, listener) { if (this._listeners === undefined) return; const listeners = this._listeners; const listenerArray = listeners[type]; if (listenerArray !== undefined) { const index = listenerArray.indexOf(listener); if (index !== -1) { listenerArray.splice(index, 1); } } } dispatchEvent(event) { if (this._listeners === undefined) return; const listeners = this._listeners; const listenerArray = listeners[event.type]; if (listenerArray !== undefined) { event.target = this; // Make a copy, in case listeners are removed while iterating. const array = listenerArray.slice(0); for (let i = 0, l = array.length; i < l; i++) { array[i].call(this, event); } event.target = null; } } } // textures/Source.js let _sourceId = 0; class Source { constructor(data = null) { Object.defineProperty(this, 'id', { value: _sourceId++ }); this.uuid = generateUUID(); this.data = data; this.dataReady = true; this.version = 0; } set needsUpdate( value ) { if (value === true) this.version++; } } // textures/Texture.js let _textureId = 0; class Texture extends EventDispatcher { constructor(image) { super(); Object.defineProperty(this, 'id', { value: _textureId++ }); this.uuid = generateUUID(); this.name = ''; this.source = new Source(image); this.version = 0; } get image() { return this.source.data; } set image(value) { this.source.data = value; } dispose() { this.dispatchEvent({ type: 'dispose' }); } set needsUpdate(value) { if (value === true) { this.version++; this.source.needsUpdate = true; } } } // renderers/webgl/WebGLUniforms.js /** * 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 )'. * * * 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, [textures] ) * * uploads a uniform value(s) * the 'textures' parameter is needed for sampler uniforms * * * Static methods of the top-level container (textures factorizations): * * .upload( gl, seq, values, textures ) * * 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 (textures factorizations): * * .setValue( gl, name, value, textures ) * * sets uniform with name 'name' to 'value' * * .setOptional( gl, obj, prop ) * * like .set for an optional property of the object * */ const emptyTexture = /*@__PURE__*/ new Texture(); // --- Utilities --- // Float32Array caches used for uploading Matrix uniforms const mat4array = new Float32Array(16); const mat3array = new Float32Array(9); const mat2array = new Float32Array(4); function arraysEqual(a, b) { if (a.length !== b.length) return false; for (let i = 0, l = a.length; i < l; i++) { if (a[i] !== b[i]) return false; } return true; } function copyArray(a, b) { for (let i = 0, l = b.length; i < l; i++) { a[i] = b[i]; } } // --- Setters --- // Note: Defining these methods externally, because they come in a bunch // and this way their names minify. // Single scalar function setValueV1f(gl, v) { const cache = this.cache; if (cache[0] === v) return; gl.uniform1f(this.addr, v); cache[0] = v; } // Single float vector (from flat array or THREE.VectorN) function setValueV2f(gl, v) { const cache = this.cache; if (v.x !== undefined) { if (cache[0] !== v.x || cache[1] !== v.y) { gl.uniform2f(this.addr, v.x, v.y); cache[0] = v.x; cache[1] = v.y; } } else { if (arraysEqual(cache, v)) return; gl.uniform2fv(this.addr, v); copyArray(cache, v); } } function setValueV3f(gl, v) { const cache = this.cache; if (v.x !== undefined) { if (cache[0] !== v.x || cache[1] !== v.y || cache[2] !== v.z) { gl.uniform3f(this.addr, v.x, v.y, v.z); cache[0] = v.x; cache[1] = v.y; cache[2] = v.z; } } else if (v.r !== undefined) { if (cache[0] !== v.r || cache[1] !== v.g || cache[2] !== v.b) { gl.uniform3f(this.addr, v.r, v.g, v.b); cache[0] = v.r; cache[1] = v.g; cache[2] = v.b; } } else { if (arraysEqual(cache, v)) return; gl.uniform3fv(this.addr, v); copyArray(cache, v); } } function setValueV4f(gl, v) { const cache = this.cache; if (v.x !== undefined) { if (cache[0] !== v.x || cache[1] !== v.y || cache[2] !== v.z || cache[3] !== v.w) { gl.uniform4f(this.addr, v.x, v.y, v.z, v.w); cache[0] = v.x; cache[1] = v.y; cache[2] = v.z; cache[3] = v.w; } } else { if (arraysEqual(cache, v)) return; gl.uniform4fv(this.addr, v); copyArray(cache, v); } } // Single matrix (from flat array or THREE.MatrixN) function setValueM2(gl, v) { const cache = this.cache; const elements = v.elements; if (elements === undefined) { if (arraysEqual(cache, v)) return; gl.uniformMatrix2fv(this.addr, false, v); copyArray(cache, v); } else { if (arraysEqual(cache, elements)) return; mat2array.set(elements); gl.uniformMatrix2fv(this.addr, false, mat2array); copyArray(cache, elements); } } function setValueM3(gl, v) { const cache = this.cache; const elements = v.elements; if (elements === undefined) { if (arraysEqual(cache, v)) return; gl.uniformMatrix3fv(this.addr, false, v); copyArray(cache, v); } else { if (arraysEqual(cache, elements)) return; mat3array.set(elements); gl.uniformMatrix3fv(this.addr, false, mat3array); copyArray(cache, elements); } } function setValueM4(gl, v) { const cache = this.cache; const elements = v.elements; if (elements === undefined) { if (arraysEqual(cache, v)) return; gl.uniformMatrix4fv(this.addr, false, v); copyArray(cache, v); } else { if (arraysEqual(cache, elements)) return; mat4array.set(elements); gl.uniformMatrix4fv(this.addr, false, mat4array); copyArray(cache, elements); } } // Single integer / boolean function setValueV1i(gl, v) { const cache = this.cache; if (cache[0] === v) return; gl.uniform1i(this.addr, v); cache[0] = v; } // Single integer / boolean vector (from flat array or THREE.VectorN) function setValueV2i(gl, v) { const cache = this.cache; if (v.x !== undefined) { if (cache[0] !== v.x || cache[1] !== v.y) { gl.uniform2i(this.addr, v.x, v.y); cache[0] = v.x; cache[1] = v.y; } } else { if (arraysEqual(cache, v)) return; gl.uniform2iv(this.addr, v); copyArray(cache, v); } } function setValueV3i(gl, v) { const cache = this.cache; if (v.x !== undefined) { if (cache[0] !== v.x || cache[1] !== v.y || cache[2] !== v.z) { gl.uniform3i(this.addr, v.x, v.y, v.z); cache[0] = v.x; cache[1] = v.y; cache[2] = v.z; } } else { if (arraysEqual(cache, v)) return; gl.uniform3iv(this.addr, v); copyArray(cache, v); } } function setValueV4i(gl, v) { const cache = this.cache; if (v.x !== undefined) { if (cache[0] !== v.x || cache[1] !== v.y || cache[2] !== v.z || cache[3] !== v.w) { gl.uniform4i(this.addr, v.x, v.y, v.z, v.w); cache[0] = v.x; cache[1] = v.y; cache[2] = v.z; cache[3] = v.w; } } else { if (arraysEqual(cache, v)) return; gl.uniform4iv(this.addr, v); copyArray(cache, v); } } // Single unsigned integer function setValueV1ui(gl, v) { const cache = this.cache; if (cache[0] === v) return; gl.uniform1ui(this.addr, v); cache[0] = v; } // Single unsigned integer vector (from flat array or THREE.VectorN) function setValueV2ui(gl, v) { const cache = this.cache; if (v.x !== undefined) { if (cache[0] !== v.x || cache[1] !== v.y) { gl.uniform2ui(this.addr, v.x, v.y); cache[0] = v.x; cache[1] = v.y; } } else { if (arraysEqual(cache, v)) return; gl.uniform2uiv(this.addr, v); copyArray(cache, v); } } function setValueV3ui(gl, v) { const cache = this.cache; if (v.x !== undefined) { if (cache[0] !== v.x || cache[1] !== v.y || cache[2] !== v.z) { gl.uniform3ui(this.addr, v.x, v.y, v.z); cache[0] = v.x; cache[1] = v.y; cache[2] = v.z; } } else { if (arraysEqual(cache, v)) return; gl.uniform3uiv(this.addr, v); copyArray(cache, v); } } function setValueV4ui(gl, v) { const cache = this.cache; if (v.x !== undefined) { if (cache[0] !== v.x || cache[1] !== v.y || cache[2] !== v.z || cache[3] !== v.w) { gl.uniform4ui(this.addr, v.x, v.y, v.z, v.w); cache[0] = v.x; cache[1] = v.y; cache[2] = v.z; cache[3] = v.w; } } else { if (arraysEqual(cache, v)) return; gl.uniform4uiv(this.addr, v); copyArray(cache, v); } } // Single texture (2D / Cube) function setValueT1(gl, v, textures) { const cache = this.cache; const unit = textures.allocateTextureUnit(); if (cache[0] !== unit) { gl.uniform1i(this.addr, unit); cache[0] = unit; } const emptyTexture2D = emptyTexture; textures.setTexture2D(v || emptyTexture2D, unit); } // Helper to pick the right setter for the singular case function getSingularSetter(type) { switch (type) { case 0x1406: return setValueV1f; // FLOAT case 0x8b50: return setValueV2f; // _VEC2 case 0x8b51: return setValueV3f; // _VEC3 case 0x8b52: return setValueV4f; // _VEC4 case 0x8b5a: return setValueM2; // _MAT2 case 0x8b5b: return setValueM3; // _MAT3 case 0x8b5c: return setValueM4; // _MAT4 case 0x1404: case 0x8b56: return setValueV1i; // INT, BOOL case 0x8b53: case 0x8b57: return setValueV2i; // _VEC2 case 0x8b54: case 0x8b58: return setValueV3i; // _VEC3 case 0x8b55: case 0x8b59: return setValueV4i; // _VEC4 case 0x1405: return setValueV1ui; // UINT case 0x8dc6: return setValueV2ui; // _VEC2 case 0x8dc7: return setValueV3ui; // _VEC3 case 0x8dc8: return setValueV4ui; // _VEC4 case 0x8b5e: // SAMPLER_2D case 0x8d66: // SAMPLER_EXTERNAL_OES case 0x8dca: // INT_SAMPLER_2D case 0x8dd2: // UNSIGNED_INT_SAMPLER_2D return setValueT1; } } // --- Uniform Classes --- class SingleUniform { constructor(id, activeInfo, addr) { this.id = id; this.addr = addr; this.cache = []; this.type = activeInfo.type; this.setValue = getSingularSetter(activeInfo.type); // this.path = activeInfo.name; // DEBUG } } class StructuredUniform { constructor(id) { this.id = id; this.seq = []; this.map = {}; } setValue(gl, value, textures) { const seq = this.seq; for (let i = 0, n = seq.length; i !== n; ++i) { const u = seq[i]; u.setValue(gl, value[u.id], textures); } } } // --- Top-level --- // Parser - builds up the property tree from the path strings const RePathPart = /(\w+)(\])?(\[|\.)?/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) { const path = activeInfo.name, pathLength = path.length; // reset RegExp object, because of the early exit of a previous run RePathPart.lastIndex = 0; while (true) { const match = RePathPart.exec(path), matchEnd = RePathPart.lastIndex; let id = match[1]; const 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 if (subscript !== undefined) throw new TypeError('PureArrayUniform?'); addUniform(container, new SingleUniform(id, activeInfo, addr)); break; } else { // step into inner node / create it in case it doesn't exist const map = container.map; let next = map[id]; if (next === undefined) { next = new StructuredUniform(id); addUniform(container, next); } container = next; } } } // Root Container class WebGLUniforms { constructor(gl, program) { this.seq = []; this.map = {}; const n = gl.getProgramParameter(program, gl.ACTIVE_UNIFORMS); for (let i = 0; i < n; ++i) { const info = gl.getActiveUniform(program, i), addr = gl.getUniformLocation(program, info.name); parseUniform(info, addr, this); } } setValue(gl, name, value, textures) { const u = this.map[name]; if (u !== undefined) u.setValue(gl, value, textures); } setOptional(gl, object, name) { const v = object[name]; if (v !== undefined) this.setValue(gl, name, v); } static upload(gl, seq, values, textures) { for (let i = 0, n = seq.length; i !== n; ++i) { const u = seq[i], v = values[u.id]; if (v.needsUpdate !== false) { // note: always updating when .needsUpdate is undefined u.setValue(gl, v.value, textures); } } } static seqWithValue(seq, values) { const r = []; for (let i = 0, n = seq.length; i !== n; ++i) { const u = seq[i]; if (u.id in values) r.push(u); } return r; } } // materials/Material.js let _materialId = 0; class Material extends EventDispatcher { constructor() { super(); Object.defineProperty(this, 'id', { value: _materialId++ }); this.uuid = generateUUID(); this.name = ''; this.type = 'Material'; this.opacity = 1; this.transparent = false; this.depthTest = true; this.depthWrite = true; this.precision = null; // override the renderer's default precision for this material this.premultipliedAlpha = false; this.visible = true; //TODO //this.version = 0; this._needsUpdate = true; } setValues(values) { if (values === undefined) return; for (const key in values) { const newValue = values[key]; if (newValue === undefined) { console.warn(`THREE.Material: parameter '${key}' has value of undefined.`); continue; } const currentValue = this[key]; if (currentValue === undefined) { console.warn(`THREE.Material: '${key}' is not a property of THREE.${this.type}.`); continue; } if (currentValue && currentValue.isColor) { currentValue.set(newValue); } else if (currentValue && currentValue.isVector3 && newValue && newValue.isVector3) { currentValue.copy(newValue); } else { this[key] = newValue; } } } dispose() { this.dispatchEvent({ type: 'dispose' }); } //TODO //set needsUpdate(value) { // if (value === true) this.version++; //} //old: get needsUpdate() { return this._needsUpdate; } set needsUpdate(value) { if ( value === true ) this.update(); this._needsUpdate = value; } update() { this.dispatchEvent( { type: 'update' } ); } } Material.prototype.isMaterial = true; // materials/ShaderMaterial.js class ShaderMaterial extends Material { constructor(parameters) { super(); this.type = 'ShaderMaterial'; this.uniforms = {}; this.vertexShader = ''; this.fragmentShader = ''; this.linewidth = 1; this.fog = false; // set to use scene fog this.extensions = { fragDepth: false, // set to use fragment depth values }; this.setValues(parameters); } } ShaderMaterial.prototype.isShaderMaterial = true; // core/Object3D.js let _object3DId = 0; const _addedEvent = { type: 'added' }; const _removedEvent = { type: 'removed' }; class Object3D extends EventDispatcher { constructor() { super(); Object.defineProperty(this, 'id', { value: _object3DId++ }); this.uuid = generateUUID(); this.name = ''; this.type = 'Object3D'; this.parent = null; this.children = []; this.up = Object3D.DEFAULT_UP.clone(); const position = new Vector3(); //const rotation = new Euler(); const quaternion = new Quaternion(); const 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: { configurable: true, enumerable: true, value: position, }, //rotation: { // configurable: true, // enumerable: true, // value: rotation, //}, quaternion: { configurable: true, enumerable: true, value: quaternion, }, scale: { configurable: true, enumerable: true, value: scale, }, modelViewMatrix: { value: new Matrix4(), }, //normalMatrix: { // value: new Matrix3(), //}, }); this.matrix = new Matrix4(); this.matrixWorld = new Matrix4(); this.matrixAutoUpdate = Object3D.DEFAULT_MATRIX_AUTO_UPDATE; this.matrixWorldAutoUpdate = Object3D.DEFAULT_MATRIX_WORLD_AUTO_UPDATE; // checked by the renderer this.matrixWorldNeedsUpdate = false; //this.layers = new Layers(); this.visible = true; //this.castShadow = false; //this.receiveShadow = false; this.frustumCulled = true; this.renderOrder = 0; //this.animations = []; this.userData = {}; } add(object) { if (arguments.length > 1) { for (let i = 0; i < arguments.length; i++) { this.add(arguments[i]); } return this; } if (object && object.isObject3D) { if (object.parent !== null) { object.parent.remove(object); } object.parent = this; this.children.push(object); object.dispatchEvent(_addedEvent); } return this; } remove(object) { if (arguments.length > 1) { for (let i = 0; i < arguments.length; i++) { this.remove(arguments[i]); } return this; } const index = this.children.indexOf(object); if (index !== -1) { object.parent = null; this.children.splice(index, 1); object.dispatchEvent(_removedEvent); } return this; } updateMatrix() { this.matrix.compose(this.position, this.quaternion, this.scale); this.matrixWorldNeedsUpdate = true; } updateMatrixWorld(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 const children = this.children; for (let i = 0, l = children.length; i < l; i++) { const child = children[i]; //if (child.matrixWorldAutoUpdate === true || force === true) { child.updateMatrixWorld(force); //} } } } Object3D.prototype.isObject3D = true; Object3D.DEFAULT_UP = /*@__PURE__*/ new Vector3(0, 1, 0); Object3D.DEFAULT_MATRIX_AUTO_UPDATE = true; Object3D.DEFAULT_MATRIX_WORLD_AUTO_UPDATE = true; // core/BufferAttribute.js class BufferAttribute { constructor(array, itemSize, normalized = false) { if (Array.isArray(array)) { throw new TypeError('BufferAttribute: array should be a Typed Array.'); } this.array = array; this.itemSize = itemSize; this.count = array !== undefined ? array.length / itemSize : 0; this.normalized = normalized; // FIXME: new variables //this.usage = StaticDrawUsage; //this._updateRange = { offset: 0, count: -1 }; //this.updateRanges = []; //this.gpuType = FloatType; // FIXME: old variables this.dynamic = false; this.updateRange = { offset: 0, count: - 1 }; this.uuid = generateUUID(); this.version = 0 } onUploadCallback() {} } BufferAttribute.prototype.isBufferAttribute = true; // core/BufferGeometry.js let _id = 0; class BufferGeometry extends EventDispatcher { constructor() { super(); Object.defineProperty(this, 'id', { value: _id++ }); this.uuid = generateUUID(); this.name = ''; this.type = 'BufferGeometry'; this.index = null; this.attributes = {}; this.groups = []; this.boundingBox = null; this.boundingSphere = null; this.drawRange = { start: 0, count: Infinity }; } getIndex() { return this.index; } setIndex(index) { this.index = index; } setAttribute(name, attribute) { this.attributes[name] = attribute; return this; } dispose() { this.dispatchEvent({ type: 'dispose' }); } } BufferGeometry.prototype.isBufferGeometry = true; // objects/Mesh.js class Mesh extends Object3D { constructor(geometry, material) { super(); this.type = 'Mesh'; if (!geometry) throw new TypeError('Mesh: geometry not set'); this.geometry = geometry; this.material = material; } } Mesh.prototype.isMesh = true; // cameras/Camera.js class Camera extends Object3D { constructor() { super(); this.type = 'Camera'; this.matrixWorldInverse = new Matrix4(); this.projectionMatrix = new Matrix4(); this.projectionMatrixInverse = new Matrix4(); //this.coordinateSystem = WebGLCoordinateSystem; } // FIXME //updateMatrixWorld(force) { // super.updateMatrixWorld(force); // this.matrixWorldInverse.copy(this.matrixWorld).invert(); //} //updateWorldMatrix(updateParents, updateChildren) { // super.updateWorldMatrix(updateParents, updateChildren); // this.matrixWorldInverse.copy(this.matrixWorld).invert(); //} } Camera.prototype.isCamera = true; // cameras/OrthographicCamera.js class OrthographicCamera extends Camera { constructor(left = -1, right = 1, top = 1, bottom = -1, near = 0.1, far = 2000) { super(); this.type = 'OrthographicCamera'; this.zoom = 1; //this.view = null; this.left = left; this.right = right; this.top = top; this.bottom = bottom; this.near = near; this.far = far; this.updateProjectionMatrix(); } updateProjectionMatrix() { const dx = (this.right - this.left) / (2 * this.zoom); const dy = (this.top - this.bottom) / (2 * this.zoom); const cx = (this.right + this.left) / 2; const cy = (this.top + this.bottom) / 2; let left = cx - dx; let right = cx + dx; let top = cy + dy; let bottom = cy - dy; this.projectionMatrix.makeOrthographic(left, right, top, bottom, this.near, this.far); this.projectionMatrixInverse.copy(this.projectionMatrix).invert(); } } // renderers/webgl/WebGLIndexedBufferRenderer.js (not updated) function WebGLIndexedBufferRenderer( gl, extensions, infoRender ) { let mode; function setMode( value ) { mode = value; } let type, size; function setIndex( index ) { if ( index.array instanceof Uint32Array && extensions.get( 'OES_element_index_uint' ) ) { type = gl.UNSIGNED_INT; size = 4; } else if ( index.array instanceof Uint16Array ) { type = gl.UNSIGNED_SHORT; size = 2; } else { type = gl.UNSIGNED_BYTE; size = 1; } } function render( start, count ) { gl.drawElements( mode, count, type, start * size ); infoRender.calls ++; infoRender.vertices += count; if ( mode === gl.TRIANGLES ) infoRender.faces += count / 3; } return { setMode: setMode, setIndex: setIndex, render: render, }; } // renderers/webgl/WebGLBufferRenderer.js (not updated) function WebGLBufferRenderer( gl, extensions, infoRender ) { let mode; function setMode( value ) { mode = value; } function render( start, count ) { gl.drawArrays( mode, start, count ); infoRender.calls ++; infoRender.vertices += count; if ( mode === gl.TRIANGLES ) infoRender.faces += count / 3; } return { setMode: setMode, render: render, }; } // renderers/webgl/WebGLShader.js (not updated) function WebGLShader( gl, type, string ) { let shader = gl.createShader( type ); gl.shaderSource( shader, string ); gl.compileShader( shader ); if ( gl.getShaderParameter( shader, gl.COMPILE_STATUS ) === false ) { console.error( 'WebGLShader: Shader couldn\'t compile.' ); } if ( gl.getShaderInfoLog( shader ) !== '' ) { let info = gl.getShaderInfoLog( shader ); // workaround for https://github.com/mrdoob/three.js/issues/9716 if (info.indexOf('GL_ARB_gpu_shader5') === -1) { console.warn( 'WebGLShader: gl.getShaderInfoLog()', type === gl.VERTEX_SHADER ? 'vertex' : 'fragment', info, string ); } } return shader; } // renderers/webgl/WebGLProgram.js (not updated) let programIdCount = 0; function generateExtensions( extensions, parameters, rendererExtensions ) { extensions = extensions || {}; let chunks = [ ( extensions.fragDepth ) && rendererExtensions.get( 'EXT_frag_depth' ) ? '#extension GL_EXT_frag_depth : enable' : '', ]; return chunks.join( '\n' ); } function fetchAttributeLocations( gl, program ) { let attributes = {}; let n = gl.getProgramParameter( program, gl.ACTIVE_ATTRIBUTES ); for ( let i = 0; i < n; i ++ ) { let info = gl.getActiveAttrib( program, i ); let name = info.name; // console.log("WebGLProgram: ACTIVE VERTEX ATTRIBUTE:", name, i ); attributes[name] = gl.getAttribLocation( program, name ); } return attributes; } function WebGLProgram( renderer, code, material, parameters ) { let gl = renderer.context; let extensions = material.extensions; let vertexShader = material.__webglShader.vertexShader; let fragmentShader = material.__webglShader.fragmentShader; // console.log( 'building new program ' ); // let customExtensions = generateExtensions( extensions, parameters, renderer.extensions ); // let program = gl.createProgram(); let prefixVertex, prefixFragment; prefixVertex = [ 'precision ' + parameters.precision + ' float;', 'precision ' + parameters.precision + ' int;', '#define SHADER_NAME ' + material.__webglShader.name, 'uniform mat4 modelMatrix;', 'uniform mat4 modelViewMatrix;', 'uniform mat4 projectionMatrix;', 'uniform mat4 viewMatrix;', 'attribute vec3 position;', '', ].join( '\n' ); prefixFragment = [ customExtensions, 'precision ' + parameters.precision + ' float;', 'precision ' + parameters.precision + ' int;', '#define SHADER_NAME ' + material.__webglShader.name, ( parameters.useFog && parameters.fog ) ? '#define USE_FOG' : '', '', ].join( '\n' ); let vertexGlsl = prefixVertex + vertexShader; let fragmentGlsl = prefixFragment + fragmentShader; // console.log( '*VERTEX*', vertexGlsl ); // console.log( '*FRAGMENT*', fragmentGlsl ); let glVertexShader = WebGLShader( gl, gl.VERTEX_SHADER, vertexGlsl ); let glFragmentShader = WebGLShader( gl, gl.FRAGMENT_SHADER, fragmentGlsl ); gl.attachShader( program, glVertexShader ); gl.attachShader( program, glFragmentShader ); gl.linkProgram( program ); let programLog = gl.getProgramInfoLog( program ); let vertexLog = gl.getShaderInfoLog( glVertexShader ); let fragmentLog = gl.getShaderInfoLog( glFragmentShader ); // 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 ) { console.error( 'WebGLProgram: shader error: ', gl.getError(), 'gl.VALIDATE_STATUS', gl.getProgramParameter( program, gl.VALIDATE_STATUS ), 'gl.getProgramInfoLog', programLog, vertexLog, fragmentLog ); } else if ( programLog !== '' ) { console.warn( 'WebGLProgram: gl.getProgramInfoLog()', programLog ); } // clean up gl.deleteShader( glVertexShader ); gl.deleteShader( glFragmentShader ); // set up caching for uniform locations let cachedUniforms; this.getUniforms = function () { if ( cachedUniforms === undefined ) { cachedUniforms = new WebGLUniforms( gl, program ); } return cachedUniforms; }; // set up caching for attribute locations let cachedAttributes; this.getAttributes = function () { if ( cachedAttributes === undefined ) { cachedAttributes = fetchAttributeLocations( gl, program ); } return cachedAttributes; }; // free resource this.destroy = function () { gl.deleteProgram( program ); this.program = undefined; }; // this.id = programIdCount ++; this.code = code; this.usedTimes = 1; this.program = program; this.vertexShader = glVertexShader; this.fragmentShader = glFragmentShader; return this; } function WebGLPrograms( renderer, capabilities ) { let programs = []; let parameterNames = [ 'precision', 'fog', 'useFog', 'premultipliedAlpha', ]; this.getParameters = function ( material, fog ) { let precision = renderer.getPrecision(); if ( material.precision !== null ) { precision = capabilities.getMaxPrecision( material.precision ); if ( precision !== material.precision ) { console.warn( 'WebGLProgram.getParameters:', material.precision, 'not supported, using', precision, 'instead.' ); } } let parameters = { precision: precision, fog: !! fog, useFog: material.fog, premultipliedAlpha: material.premultipliedAlpha, }; return parameters; }; this.getProgramCode = function ( material, parameters ) { let array = []; array.push( material.fragmentShader ); array.push( material.vertexShader ); for ( let i = 0; i < parameterNames.length; i ++ ) { array.push( parameters[parameterNames[i]] ); } return array.join(); }; this.acquireProgram = function ( material, parameters, code ) { let program; // Check if code has been already compiled for ( let p = 0, pl = programs.length; p < pl; p ++ ) { let programInfo = programs[p]; if ( programInfo.code === code ) { program = programInfo; ++ program.usedTimes; break; } } if ( program === undefined ) { program = new WebGLProgram( renderer, code, material, parameters ); programs.push( program ); } return program; }; this.releaseProgram = function ( program ) { if ( -- program.usedTimes === 0 ) { // Remove from unordered set let 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; } // renderers/webgl/WebGLGeometries.js (not updated) function WebGLGeometries( gl, properties ) { let geometries = {}; function onGeometryDispose( event ) { let geometry = event.target; let buffergeometry = geometries[geometry.id]; if ( buffergeometry.index !== null ) { deleteAttribute( buffergeometry.index ); } deleteAttributes( buffergeometry.attributes ); geometry.removeEventListener( 'dispose', onGeometryDispose ); delete geometries[geometry.id]; properties.delete( geometry ); properties.delete( buffergeometry ); } function getAttributeBuffer( attribute ) { return properties.get( attribute ).__webglBuffer; } function deleteAttribute( attribute ) { let buffer = getAttributeBuffer( attribute ); if ( buffer !== undefined ) { gl.deleteBuffer( buffer ); removeAttributeBuffer( attribute ); } } function deleteAttributes( attributes ) { for ( let name in attributes ) { deleteAttribute( attributes[name] ); } } function removeAttributeBuffer( attribute ) { properties.delete( attribute ); } return { get: function ( object ) { let geometry = object.geometry; if ( geometries[geometry.id] !== undefined ) { return geometries[geometry.id]; } geometry.addEventListener( 'dispose', onGeometryDispose ); let buffergeometry; if ( geometry.isBufferGeometry ) { buffergeometry = geometry; } geometries[geometry.id] = buffergeometry; return buffergeometry; }, }; } // renderers/webgl/WebGLObjects.js (not updated) function WebGLObjects( gl, properties, info ) { let geometries = new WebGLGeometries( gl, properties, info ); // function update( object ) { let geometry = geometries.get( object ); let index = geometry.index; let attributes = geometry.attributes; if ( index !== null ) { updateAttribute( index, gl.ELEMENT_ARRAY_BUFFER ); } for ( let name in attributes ) { updateAttribute( attributes[name], gl.ARRAY_BUFFER ); } return geometry; } function updateAttribute( attribute, bufferType ) { let data = attribute; let attributeProperties = properties.get( data ); if ( attributeProperties.__webglBuffer === undefined ) { createBuffer( attributeProperties, data, bufferType ); } else if ( attributeProperties.version !== data.version ) { updateBuffer( attributeProperties, data, bufferType ); } } function createBuffer( attributeProperties, data, bufferType ) { attributeProperties.__webglBuffer = gl.createBuffer(); gl.bindBuffer( bufferType, attributeProperties.__webglBuffer ); let usage = data.dynamic ? gl.DYNAMIC_DRAW : gl.STATIC_DRAW; gl.bufferData( bufferType, data.array, usage ); let type = gl.FLOAT; let array = data.array; if ( array instanceof Float32Array ) { type = gl.FLOAT; } else if ( array instanceof Float64Array ) { console.warn( '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; } attributeProperties.bytesPerElement = array.BYTES_PER_ELEMENT; attributeProperties.type = type; attributeProperties.version = data.version; data.onUploadCallback(); } function updateBuffer( attributeProperties, data, bufferType ) { gl.bindBuffer( bufferType, attributeProperties.__webglBuffer ); if ( data.dynamic === false ) { gl.bufferData( bufferType, data.array, gl.STATIC_DRAW ); } else if ( data.updateRange.count === - 1 ) { // Not using update ranges gl.bufferSubData( bufferType, 0, data.array ); } else if ( data.updateRange.count === 0 ) { console.error( 'WebGLObjects.updateBuffer: updateRange.count is 0.' ); } else { gl.bufferSubData( bufferType, data.updateRange.offset * data.array.BYTES_PER_ELEMENT, data.array.subarray( data.updateRange.offset, data.updateRange.offset + data.updateRange.count ) ); data.updateRange.count = 0; // reset range } attributeProperties.version = data.version; } function getAttributeBuffer( attribute ) { return properties.get( attribute ).__webglBuffer; } function getAttributeProperties( attribute ) { return properties.get( attribute ); } return { getAttributeBuffer: getAttributeBuffer, getAttributeProperties: getAttributeProperties, update: update, }; } // renderers/webgl/WebGLTextures.js (not updated) function WebGLTextures( _gl, extensions, state, properties, capabilities ) { function onTextureDispose( event ) { let texture = event.target; texture.removeEventListener( 'dispose', onTextureDispose ); deallocateTexture( texture ); } // function deallocateTexture( texture ) { let textureProperties = properties.get( texture ); // 2D texture if ( textureProperties.__webglInit === undefined ) return; _gl.deleteTexture( textureProperties.__webglTexture ); // remove all webgl properties properties.delete( texture ); } let textureUnits = 0; function resetTextureUnits() { textureUnits = 0; } function allocateTextureUnit() { const textureUnit = textureUnits; if (textureUnit >= capabilities.maxTextures) { console.warn('WebGLTextures: Trying to use ' + textureUnit + ' texture units while this GPU supports only ' + capabilities.maxTextures); } textureUnits += 1; return textureUnit; } function setTexture2D( texture, slot ) { let textureProperties = properties.get( texture ); if ( texture.version > 0 && textureProperties.__version !== texture.version ) { let image = texture.image; if ( image === undefined ) { console.warn( 'WebGLRenderer: Texture marked for update but image is undefined', texture ); } else if ( image.complete === false ) { console.warn( '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 setTextureParameters( textureType ) { _gl.texParameteri( textureType, _gl.TEXTURE_WRAP_S, _gl.CLAMP_TO_EDGE ); _gl.texParameteri( textureType, _gl.TEXTURE_WRAP_T, _gl.CLAMP_TO_EDGE ); _gl.texParameteri( textureType, _gl.TEXTURE_MAG_FILTER, _gl.LINEAR ); _gl.texParameteri( textureType, _gl.TEXTURE_MIN_FILTER, _gl.LINEAR_MIPMAP_LINEAR ); } function uploadTexture( textureProperties, texture, slot ) { if ( textureProperties.__webglInit === undefined ) { textureProperties.__webglInit = true; texture.addEventListener( 'dispose', onTextureDispose ); textureProperties.__webglTexture = _gl.createTexture(); } state.activeTexture( _gl.TEXTURE0 + slot ); state.bindTexture( _gl.TEXTURE_2D, textureProperties.__webglTexture ); _gl.pixelStorei( _gl.UNPACK_FLIP_Y_WEBGL, true ); _gl.pixelStorei( _gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, false ); _gl.pixelStorei( _gl.UNPACK_ALIGNMENT, 4 ); let image = texture.image; let glFormat = _gl.RGBA; let glType = _gl.UNSIGNED_BYTE; setTextureParameters( _gl.TEXTURE_2D ); state.texImage2D( _gl.TEXTURE_2D, 0, glFormat, glFormat, glType, image ); _gl.generateMipmap( _gl.TEXTURE_2D ); textureProperties.__version = texture.version; } this.setTexture2D = setTexture2D; this.resetTextureUnits = resetTextureUnits; this.allocateTextureUnit = allocateTextureUnit; } // renderers/webgl/WebGLProperties.js (not updated) function WebGLProperties() { let properties = {}; return { get: function ( object ) { let uuid = object.uuid; let map = properties[uuid]; if ( map === undefined ) { map = {}; properties[uuid] = map; } return map; }, delete: function ( object ) { delete properties[object.uuid]; }, clear: function () { properties = {}; } }; } // renderers/webgl/WebGLState.js (not updated) function WebGLState( gl ) { function ColorBuffer() { let color = new Vector4(); let currentColorClear = new Vector4(); return { 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 () { currentColorClear.set( 0, 0, 0, 1 ); } }; } function DepthBuffer() { let currentDepthMask = null; let currentDepthClear = null; return { setTest: function ( depthTest ) { if ( depthTest ) { enable( gl.DEPTH_TEST ); } else { disable( gl.DEPTH_TEST ); } }, setMask: function ( depthMask ) { if ( currentDepthMask !== depthMask ) { gl.depthMask( depthMask ); currentDepthMask = depthMask; } }, setClear: function ( depth ) { if ( currentDepthClear !== depth ) { gl.clearDepth( depth ); currentDepthClear = depth; } }, reset: function () { currentDepthMask = null; currentDepthClear = null; }, }; } // let colorBuffer = new ColorBuffer(); let depthBuffer = new DepthBuffer(); let maxVertexAttributes = gl.getParameter( gl.MAX_VERTEX_ATTRIBS ); let newAttributes = new Uint8Array( maxVertexAttributes ); let enabledAttributes = new Uint8Array( maxVertexAttributes ); let capabilities = {}; let currentBlending = null; let currentPremultipledAlpha = false; let currentLineWidth = null; const glVersion = gl.getParameter(gl.VERSION); let lineWidthAvailable = false; if ( glVersion.indexOf( 'WebGL' ) !== - 1 ) { let version = parseFloat( /^WebGL\ (\d)/.exec( glVersion )[1] ); lineWidthAvailable = ( version >= 1.0 ); } let currentTextureSlot = null; let currentBoundTextures = {}; let currentViewport = new Vector4(); function createTexture( type, target, count ) { let data = new Uint8Array( 4 ); // 4 is required to match default unpack alignment of 4. let 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 ( let i = 0; i < count; i ++ ) { gl.texImage2D( target + i, 0, gl.RGBA, 1, 1, 0, gl.RGBA, gl.UNSIGNED_BYTE, data ); } return texture; } let emptyTextures = {}; emptyTextures[gl.TEXTURE_2D] = createTexture( gl.TEXTURE_2D, gl.TEXTURE_2D, 1 ); // function init() { colorBuffer.setClear( 0, 0, 0, 1 ); depthBuffer.setClear( 1 ); enable( gl.DEPTH_TEST ); gl.depthFunc( gl.LEQUAL ); enable( gl.BLEND ); setBlending( NormalBlending ); } function initAttributes() { for ( let 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; } } function disableUnusedAttributes() { for ( let 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 setBlending( blending, premultipliedAlpha ) { if ( blending !== NoBlending ) { enable( gl.BLEND ); } else { disable( gl.BLEND ); } if ( blending !== currentBlending || premultipliedAlpha !== currentPremultipledAlpha ) { 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 ); } currentBlending = blending; currentPremultipledAlpha = premultipliedAlpha; } } function setDepthTest( depthTest ) { depthBuffer.setTest( depthTest ); } function setDepthWrite( depthWrite ) { depthBuffer.setMask( depthWrite ); } // function setLineWidth( width ) { if ( width !== currentLineWidth ) { if ( lineWidthAvailable ) gl.lineWidth( width ); currentLineWidth = width; } } // texture function activeTexture( webglSlot ) { if ( currentTextureSlot !== webglSlot ) { gl.activeTexture( webglSlot ); currentTextureSlot = webglSlot; } } function bindTexture( webglType, webglTexture ) { let 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 texImage2D() { try { gl.texImage2D.apply( gl, arguments ); } catch ( error ) { console.error( error ); } } // function viewport( viewport ) { if ( currentViewport.equals( viewport ) === false ) { gl.viewport( viewport.x, viewport.y, viewport.z, viewport.w ); currentViewport.copy( viewport ); } } function reset() { for ( let i = 0; i < enabledAttributes.length; i ++ ) { if ( enabledAttributes[ i ] === 1 ) { gl.disableVertexAttribArray( i ); enabledAttributes[ i ] = 0; } } capabilities = {}; currentTextureSlot = null; currentBoundTextures = {}; currentBlending = null; colorBuffer.reset(); depthBuffer.reset(); } // return { buffers: { color: colorBuffer, depth: depthBuffer, }, init: init, initAttributes: initAttributes, enableAttribute: enableAttribute, disableUnusedAttributes: disableUnusedAttributes, enable: enable, disable: disable, setBlending: setBlending, setDepthTest: setDepthTest, setDepthWrite: setDepthWrite, setLineWidth: setLineWidth, activeTexture: activeTexture, bindTexture: bindTexture, texImage2D: texImage2D, viewport: viewport, reset: reset }; } // renderers/webgl/WebGLCapabilities.js (not updated) function WebGLCapabilities( gl, extensions, parameters ) { 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'; } let precision = parameters.precision !== undefined ? parameters.precision : 'highp'; let maxPrecision = getMaxPrecision( precision ); if ( maxPrecision !== precision ) { console.warn( 'WebGLRenderer:', precision, 'not supported, using', maxPrecision, 'instead.' ); precision = maxPrecision; } let maxTextures = gl.getParameter( gl.MAX_TEXTURE_IMAGE_UNITS ); return { getMaxPrecision: getMaxPrecision, precision: precision, maxTextures: maxTextures, }; } // renderers/webgl/WebGLExtensions.js (not updated) function WebGLExtensions( gl ) { let extensions = {}; return { get: function ( name ) { if ( extensions[name] !== undefined ) { return extensions[name]; } let 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; default: extension = gl.getExtension( name ); } if ( extension === null ) { console.warn( 'WebGLRenderer: ' + name + ' extension not supported.' ); } extensions[name] = extension; return extension; }, }; } // renderers/WebGLRenderer.js (not updated) function WebGLRenderer( parameters ) { parameters = parameters || {}; let _canvas = parameters.canvas !== undefined ? parameters.canvas : document.createElementNS( 'http://www.w3.org/1999/xhtml', 'canvas' ), _context = parameters.context !== undefined ? parameters.context : null, _antialias = parameters.antialias !== undefined ? parameters.antialias : false, _premultipliedAlpha = parameters.premultipliedAlpha !== undefined ? parameters.premultipliedAlpha : true; let opaqueObjects = []; let opaqueObjectsLastIndex = - 1; let transparentObjects = []; let transparentObjectsLastIndex = - 1; // public properties this.domElement = _canvas; this.context = null; // clearing this.autoClear = true; this.autoClearColor = true; this.autoClearDepth = true; // scene graph this.sortObjects = true; // internal properties let _this = this, // internal state cache _currentProgram = null, _currentRenderTarget = null, _currentMaterialId = - 1, _currentGeometryProgram = '', _currentCamera = null, _currentViewport = new Vector4(), // _clearColor = new Color( 0x000000 ), _clearAlpha = 0, _width = _canvas.width, _height = _canvas.height, _pixelRatio = 1, _viewport = new Vector4( 0, 0, _width, _height ), // camera matrices cache _projScreenMatrix = new Matrix4(), _vector3 = new Vector3(), // info _infoRender = { calls: 0, vertices: 0, faces: 0, points: 0, }; this.info = { render: _infoRender, programs: null, }; // initialize let _gl; try { let attributes = { alpha: false, depth: true, antialias: _antialias, premultipliedAlpha: _premultipliedAlpha, preserveDrawingBuffer: false, }; _gl = _context || _canvas.getContext( 'webgl', attributes ) || _canvas.getContext( 'experimental-webgl', attributes ); 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 }; }; } _canvas.addEventListener( 'webglcontextlost', onContextLost, false ); } catch ( error ) { console.error( 'WebGLRenderer: ' + error ); } let extensions = new WebGLExtensions( _gl ); extensions.get( 'WEBGL_depth_texture' ); extensions.get( 'OES_element_index_uint' ); let capabilities = new WebGLCapabilities( _gl, extensions, parameters ); let state = new WebGLState( _gl ); let properties = new WebGLProperties(); let textures = new WebGLTextures( _gl, extensions, state, properties, capabilities ); let objects = new WebGLObjects( _gl, properties, this.info ); let programCache = new WebGLPrograms( this, capabilities ); this.info.programs = programCache.programs; let bufferRenderer = new WebGLBufferRenderer( _gl, extensions, _infoRender ); let indexedBufferRenderer = new WebGLIndexedBufferRenderer( _gl, extensions, _infoRender ); // function getTargetPixelRatio() { return _currentRenderTarget === null ? _pixelRatio : 1; } function setDefaultGLState() { state.init(); state.viewport( _currentViewport.copy( _viewport ).multiplyScalar( _pixelRatio ) ); state.buffers.color.setClear( _clearColor.r, _clearColor.g, _clearColor.b, _clearAlpha, _premultipliedAlpha ); } function resetGLState() { _currentProgram = null; _currentCamera = null; _currentGeometryProgram = ''; _currentMaterialId = - 1; state.reset(); } setDefaultGLState(); this.context = _gl; this.capabilities = capabilities; this.extensions = extensions; this.properties = properties; this.state = state; // API this.getContext = function () { return _gl; }; this.getPrecision = function () { return capabilities.precision; }; this.getPixelRatio = function () { return _pixelRatio; }; this.setPixelRatio = function ( value ) { if ( value === undefined ) return; _pixelRatio = value; this.setSize( _viewport.z, _viewport.w, false ); }; this.setSize = function ( width, height, updateStyle ) { _width = width; _height = height; _canvas.width = Math.floor(width * _pixelRatio); _canvas.height = Math.floor(height * _pixelRatio); if ( updateStyle !== false ) { _canvas.style.width = width + 'px'; _canvas.style.height = height + 'px'; } this.setViewport( 0, 0, width, height ); }; this.setViewport = function ( x, y, width, height ) { state.viewport( _viewport.set( x, y, width, height ) ); }; // Clearing this.getClearColor = function () { return _clearColor; }; this.setClearColor = function ( color, alpha ) { _clearColor.set( color ); _clearAlpha = alpha !== undefined ? alpha : 1; state.buffers.color.setClear( _clearColor.r, _clearColor.g, _clearColor.b, _clearAlpha, _premultipliedAlpha ); }; this.clear = function ( color, depth ) { let bits = 0; if ( color === undefined || color ) bits |= _gl.COLOR_BUFFER_BIT; if ( depth === undefined || depth ) bits |= _gl.DEPTH_BUFFER_BIT; if ( bits !== 0 ) { _gl.clear( bits ); } }; this.clearColor = function () { this.clear( true, false ); }; this.clearDepth = function () { this.clear( false, true ); }; // Reset this.resetGLState = resetGLState; this.dispose = function () { transparentObjects = []; transparentObjectsLastIndex = -1; opaqueObjects = []; opaqueObjectsLastIndex = -1; _canvas.removeEventListener( 'webglcontextlost', onContextLost, false ); }; // Events function onContextLost( event ) { event.preventDefault(); resetGLState(); setDefaultGLState(); properties.clear(); } function onMaterialDispose( event ) { let material = event.target; material.removeEventListener( 'dispose', onMaterialDispose ); deallocateMaterial( material ); } // Buffer deallocation function deallocateMaterial( material ) { releaseMaterialProgramReference( material ); properties.delete( material ); } function releaseMaterialProgramReference( material ) { let programInfo = properties.get( material ).program; material.program = undefined; if ( programInfo !== undefined ) { programCache.releaseProgram( programInfo ); } } // Buffer rendering this.renderBufferDirect = function ( camera, fog, geometry, material, object, group ) { setMaterial( material ); let program = setProgram( camera, fog, material, object ); let updateBuffers = false; let geometryProgram = geometry.id + '_' + program.id; if ( geometryProgram !== _currentGeometryProgram ) { _currentGeometryProgram = geometryProgram; updateBuffers = true; } // let index = geometry.index; let position = geometry.attributes.position; let rangeFactor = 1; let renderer; if ( index !== null ) { renderer = indexedBufferRenderer; renderer.setIndex( index ); } else { renderer = bufferRenderer; } if ( updateBuffers ) { setupVertexAttributes( material, program, geometry ); if ( index !== null ) { _gl.bindBuffer( _gl.ELEMENT_ARRAY_BUFFER, objects.getAttributeBuffer( index ) ); } } // let dataCount = 0; if ( index !== null ) { dataCount = index.count; } else if ( position !== undefined ) { dataCount = position.count; } let rangeStart = geometry.drawRange.start * rangeFactor; let rangeCount = geometry.drawRange.count * rangeFactor; let groupStart = group !== null ? group.start * rangeFactor : 0; let groupCount = group !== null ? group.count * rangeFactor : Infinity; let drawStart = Math.max( rangeStart, groupStart ); let drawEnd = Math.min( dataCount, rangeStart + rangeCount, groupStart + groupCount ) - 1; let drawCount = Math.max( 0, drawEnd - drawStart + 1 ); if ( drawCount === 0 ) return; // if ( object.isMesh ) { renderer.setMode( _gl.TRIANGLES ); } else if ( object.isLine ) { let lineWidth = material.linewidth; if ( lineWidth === undefined ) lineWidth = 1; // Not using Line*Material state.setLineWidth( lineWidth * getTargetPixelRatio() ); if ( object.isLineSegments ) { renderer.setMode( _gl.LINES ); } else { renderer.setMode( _gl.LINE_STRIP ); } } else if ( object.isPoints ) { renderer.setMode( _gl.POINTS ); } renderer.render( drawStart, drawCount ); }; function setupVertexAttributes( material, program, geometry, startIndex ) { if ( startIndex === undefined ) startIndex = 0; state.initAttributes(); let geometryAttributes = geometry.attributes; let programAttributes = program.getAttributes(); for ( let name in programAttributes ) { let programAttribute = programAttributes[name]; if ( programAttribute >= 0 ) { let geometryAttribute = geometryAttributes[name]; if ( geometryAttribute !== undefined ) { let normalized = geometryAttribute.normalized; let size = geometryAttribute.itemSize; let attributeProperties = objects.getAttributeProperties( geometryAttribute ); let buffer = attributeProperties.__webglBuffer; let type = attributeProperties.type; let bytesPerElement = attributeProperties.bytesPerElement; state.enableAttribute( programAttribute ); _gl.bindBuffer( _gl.ARRAY_BUFFER, buffer ); _gl.vertexAttribPointer( programAttribute, size, type, normalized, 0, startIndex * size * bytesPerElement ); } else { console.error( 'undefined geometryAttribute' ); } } } state.disableUnusedAttributes(); } // Sorting function painterSortStable( a, b ) { if ( a.object.renderOrder !== b.object.renderOrder ) { return a.object.renderOrder - b.object.renderOrder; } else if ( a.material.program && b.material.program && a.material.program !== b.material.program ) { return a.material.program.id - b.material.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.object.renderOrder !== b.object.renderOrder ) { return a.object.renderOrder - b.object.renderOrder; } if ( a.z !== b.z ) { return b.z - a.z; } else { return a.id - b.id; } } // Rendering this.render = function ( scene, camera, renderTarget, forceClear ) { if ( camera !== undefined && camera.isCamera !== true ) { console.error( 'camera is not an instance of Camera.' ); return; } // reset caching for this frame _currentGeometryProgram = ''; _currentMaterialId = - 1; _currentCamera = null; // update scene graph if ( scene.matrixWorldAutoUpdate === true ) scene.updateMatrixWorld(); // update camera matrices and frustum if ( camera.parent === null ) camera.updateMatrixWorld(); camera.matrixWorldInverse.copy( camera.matrixWorld ).invert(); _projScreenMatrix.multiplyMatrices( camera.projectionMatrix, camera.matrixWorldInverse ); opaqueObjectsLastIndex = - 1; transparentObjectsLastIndex = - 1; projectObject( scene ); opaqueObjects.length = opaqueObjectsLastIndex + 1; transparentObjects.length = transparentObjectsLastIndex + 1; if ( _this.sortObjects === true ) { opaqueObjects.sort( painterSortStable ); transparentObjects.sort( reversePainterSortStable ); } // _infoRender.calls = 0; _infoRender.vertices = 0; _infoRender.faces = 0; _infoRender.points = 0; this.setRenderTarget( ); state.buffers.color.setClear( _clearColor.r, _clearColor.g, _clearColor.b, _clearAlpha, _premultipliedAlpha ); if ( this.autoClear || forceClear ) { this.clear( this.autoClearColor, this.autoClearDepth ); } // opaque pass (front-to-back order) state.setBlending( NoBlending ); renderObjects( opaqueObjects, scene, camera ); // transparent pass (back-to-front order) renderObjects( transparentObjects, scene, camera ); // Ensure depth buffer writing is enabled so it can be cleared on next render state.setDepthTest( true ); state.setDepthWrite( true ); // _gl.finish(); }; function pushRenderItem( object, geometry, material, z, group ) { let array, index; // allocate the next position in the appropriate array if ( material.transparent ) { array = transparentObjects; index = ++ transparentObjectsLastIndex; } else { array = opaqueObjects; index = ++ opaqueObjectsLastIndex; } // recycle existing render item or grow the array let renderItem = array[index]; if ( renderItem !== undefined ) { renderItem.id = object.id; renderItem.object = object; renderItem.geometry = geometry; renderItem.material = material; renderItem.z = _vector3.z; renderItem.group = group; } else { renderItem = { id: object.id, object: object, geometry: geometry, material: material, z: _vector3.z, group: group, }; // assert( index === array.length ); array.push( renderItem ); } } function projectObject( object ) { if ( object.visible === false ) return; if ( object.isMesh || object.isLine || object.isPoints ) { let material = object.material; if ( material.visible === true ) { if ( _this.sortObjects === true ) { _vector3.setFromMatrixPosition( object.matrixWorld ); _vector3.applyMatrix4( _projScreenMatrix ); } let geometry = objects.update( object ); pushRenderItem( object, geometry, material, _vector3.z, null ); } } let children = object.children; for ( let i = 0, l = children.length; i < l; i ++ ) { projectObject( children[i] ); } } function renderObjects( renderList, scene, camera, overrideMaterial ) { for ( let i = 0, l = renderList.length; i < l; i ++ ) { let renderItem = renderList[i]; let object = renderItem.object; let geometry = renderItem.geometry; let material = overrideMaterial === undefined ? renderItem.material : overrideMaterial; let group = renderItem.group; object.modelViewMatrix.multiplyMatrices( camera.matrixWorldInverse, object.matrixWorld ); _this.renderBufferDirect( camera, scene.fog, geometry, material, object, group ); } } function initMaterial( material, fog, object ) { let materialProperties = properties.get( material ); let parameters = programCache.getParameters( material, fog, object ); let code = programCache.getProgramCode( material, parameters ); let program = materialProperties.program; let programChange = true; if ( program === undefined ) { // new material material.addEventListener( 'dispose', onMaterialDispose ); } else if ( program.code !== code ) { // changed glsl or parameters releaseMaterialProgramReference( material ); } else { // only rebuild uniform list programChange = false; } if ( programChange ) { materialProperties.__webglShader = { name: material.type, uniforms: material.uniforms, vertexShader: material.vertexShader, fragmentShader: material.fragmentShader, }; material.__webglShader = materialProperties.__webglShader; program = programCache.acquireProgram( material, parameters, code ); materialProperties.program = program; material.program = program; } let uniforms = materialProperties.__webglShader.uniforms; materialProperties.fog = fog; let progUniforms = materialProperties.program.getUniforms(), uniformsList = WebGLUniforms.seqWithValue( progUniforms.seq, uniforms ); materialProperties.uniformsList = uniformsList; } function setMaterial( material ) { material.transparent === true ? state.setBlending( NormalBlending, material.premultipliedAlpha ) : state.setBlending( NoBlending ); state.setDepthTest( material.depthTest ); state.setDepthWrite( material.depthWrite ); } function setProgram( camera, fog, material, object ) { textures.resetTextureUnits() let materialProperties = properties.get( material ); if ( material.needsUpdate === false ) { if ( materialProperties.program === undefined ) { material.needsUpdate = true; } else if ( material.fog && materialProperties.fog !== fog ) { material.needsUpdate = true; } } if ( material.needsUpdate ) { initMaterial( material, fog, object ); material.needsUpdate = false; } let refreshProgram = false; let refreshMaterial = false; let program = materialProperties.program, p_uniforms = program.getUniforms(), m_uniforms = materialProperties.__webglShader.uniforms; if ( program.id !== _currentProgram ) { _gl.useProgram( program.program ); _currentProgram = program.id; refreshProgram = true; refreshMaterial = true; } if ( material.id !== _currentMaterialId ) { _currentMaterialId = material.id; refreshMaterial = true; } if ( refreshProgram || camera !== _currentCamera ) { p_uniforms.setValue(_gl, 'projectionMatrix', camera.projectionMatrix); if ( camera !== _currentCamera ) { _currentCamera = 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 } // load material specific uniforms // (shader material also gets them for the sake of genericity) if ( material.isShaderMaterial ) { p_uniforms.setValue( _gl, 'viewMatrix', camera.matrixWorldInverse ); } } if ( refreshMaterial ) { // refresh uniforms common to several materials if ( fog && material.fog ) { refreshUniformsFog( m_uniforms, fog ); } // refresh single material specific uniforms WebGLUniforms.upload( _gl, materialProperties.uniformsList, m_uniforms, textures ); } // common matrices p_uniforms.setValue(_gl, 'modelViewMatrix', object.modelViewMatrix); p_uniforms.setValue( _gl, 'modelMatrix', object.matrixWorld ); return program; } // Uniforms (refresh uniforms objects) function refreshUniformsFog( uniforms, fog ) { uniforms.fogColor.value = fog.color; if ( fog.isFog ) { uniforms.fogNear.value = fog.near; uniforms.fogFar.value = fog.far; } } this.setRenderTarget = function ( ) { _currentRenderTarget = null; _currentViewport.copy( _viewport ).multiplyScalar( _pixelRatio ); state.viewport( _currentViewport ); }; } // scenes/Fog.js class Fog { constructor(color, near = 1, far = 1000) { this.isFog = true; this.name = ''; this.color = new Color(color); this.near = near; this.far = far; } } // scenes/Scene.js class Scene extends Object3D { constructor() { super(); this.type = 'Scene'; this.fog = null; } } // objects/Line.js class Line extends Object3D { constructor(geometry, material) { super(); this.type = 'Line'; this.geometry = geometry; this.material = material; } } Line.prototype.isLine = true; // objects/LineSegments.js class LineSegments extends Line { constructor(geometry, material) { super(geometry, material); this.isLineSegments = true; this.type = 'LineSegments'; } } // objects/Points.js class Points extends Object3D { constructor(geometry, material) { super(); this.type = 'Points'; this.geometry = geometry; this.material = material; } } Points.prototype.isPoints = true; export { WebGLRenderer, Fog, Scene, Mesh, LineSegments, Line, Points, ShaderMaterial, OrthographicCamera, BufferGeometry, BufferAttribute, Object3D, Ray, Matrix4, Vector3, Quaternion, Color, Texture, };