Spaces:
Running
Running
| function WebGLBindingStates(gl, extensions, attributes, capabilities) { | |
| const maxVertexAttributes = gl.getParameter(gl.MAX_VERTEX_ATTRIBS); | |
| const extension = capabilities.isWebGL2 ? null : extensions.get('OES_vertex_array_object'); | |
| const vaoAvailable = capabilities.isWebGL2 || extension !== null; | |
| const bindingStates = {}; | |
| const defaultState = createBindingState(null); | |
| let currentState = defaultState; | |
| function setup(object, material, program, geometry, index) { | |
| let updateBuffers = false; | |
| if (vaoAvailable) { | |
| const state = getBindingState(geometry, program, material); | |
| if (currentState !== state) { | |
| currentState = state; | |
| bindVertexArrayObject(currentState.object); | |
| } | |
| updateBuffers = needsUpdate(geometry, index); | |
| if (updateBuffers) saveCache(geometry, index); | |
| } else { | |
| const wireframe = material.wireframe === true; | |
| if (currentState.geometry !== geometry.id || currentState.program !== program.id || currentState.wireframe !== wireframe) { | |
| currentState.geometry = geometry.id; | |
| currentState.program = program.id; | |
| currentState.wireframe = wireframe; | |
| updateBuffers = true; | |
| } | |
| } | |
| if (object.isInstancedMesh === true) { | |
| updateBuffers = true; | |
| } | |
| if (index !== null) { | |
| attributes.update(index, gl.ELEMENT_ARRAY_BUFFER); | |
| } | |
| if (updateBuffers) { | |
| setupVertexAttributes(object, material, program, geometry); | |
| if (index !== null) { | |
| gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, attributes.get(index).buffer); | |
| } | |
| } | |
| } | |
| function createVertexArrayObject() { | |
| if (capabilities.isWebGL2) return gl.createVertexArray(); | |
| return extension.createVertexArrayOES(); | |
| } | |
| function bindVertexArrayObject(vao) { | |
| if (capabilities.isWebGL2) return gl.bindVertexArray(vao); | |
| return extension.bindVertexArrayOES(vao); | |
| } | |
| function deleteVertexArrayObject(vao) { | |
| if (capabilities.isWebGL2) return gl.deleteVertexArray(vao); | |
| return extension.deleteVertexArrayOES(vao); | |
| } | |
| function getBindingState(geometry, program, material) { | |
| const wireframe = material.wireframe === true; | |
| let programMap = bindingStates[geometry.id]; | |
| if (programMap === undefined) { | |
| programMap = {}; | |
| bindingStates[geometry.id] = programMap; | |
| } | |
| let stateMap = programMap[program.id]; | |
| if (stateMap === undefined) { | |
| stateMap = {}; | |
| programMap[program.id] = stateMap; | |
| } | |
| let state = stateMap[wireframe]; | |
| if (state === undefined) { | |
| state = createBindingState(createVertexArrayObject()); | |
| stateMap[wireframe] = state; | |
| } | |
| return state; | |
| } | |
| function createBindingState(vao) { | |
| const newAttributes = []; | |
| const enabledAttributes = []; | |
| const attributeDivisors = []; | |
| for (let i = 0; i < maxVertexAttributes; i++) { | |
| newAttributes[i] = 0; | |
| enabledAttributes[i] = 0; | |
| attributeDivisors[i] = 0; | |
| } | |
| return { | |
| // for backward compatibility on non-VAO support browser | |
| geometry: null, | |
| program: null, | |
| wireframe: false, | |
| newAttributes: newAttributes, | |
| enabledAttributes: enabledAttributes, | |
| attributeDivisors: attributeDivisors, | |
| object: vao, | |
| attributes: {}, | |
| index: null, | |
| }; | |
| } | |
| function needsUpdate(geometry, index) { | |
| const cachedAttributes = currentState.attributes; | |
| const geometryAttributes = geometry.attributes; | |
| let attributesNum = 0; | |
| for (const key in geometryAttributes) { | |
| const cachedAttribute = cachedAttributes[key]; | |
| const geometryAttribute = geometryAttributes[key]; | |
| if (cachedAttribute === undefined) return true; | |
| if (cachedAttribute.attribute !== geometryAttribute) return true; | |
| if (cachedAttribute.data !== geometryAttribute.data) return true; | |
| attributesNum++; | |
| } | |
| if (currentState.attributesNum !== attributesNum) return true; | |
| if (currentState.index !== index) return true; | |
| return false; | |
| } | |
| function saveCache(geometry, index) { | |
| const cache = {}; | |
| const attributes = geometry.attributes; | |
| let attributesNum = 0; | |
| for (const key in attributes) { | |
| const attribute = attributes[key]; | |
| const data = {}; | |
| data.attribute = attribute; | |
| if (attribute.data) { | |
| data.data = attribute.data; | |
| } | |
| cache[key] = data; | |
| attributesNum++; | |
| } | |
| currentState.attributes = cache; | |
| currentState.attributesNum = attributesNum; | |
| currentState.index = index; | |
| } | |
| function initAttributes() { | |
| const newAttributes = currentState.newAttributes; | |
| for (let i = 0, il = newAttributes.length; i < il; i++) { | |
| newAttributes[i] = 0; | |
| } | |
| } | |
| function enableAttribute(attribute) { | |
| enableAttributeAndDivisor(attribute, 0); | |
| } | |
| function enableAttributeAndDivisor(attribute, meshPerAttribute) { | |
| const newAttributes = currentState.newAttributes; | |
| const enabledAttributes = currentState.enabledAttributes; | |
| const attributeDivisors = currentState.attributeDivisors; | |
| newAttributes[attribute] = 1; | |
| if (enabledAttributes[attribute] === 0) { | |
| gl.enableVertexAttribArray(attribute); | |
| enabledAttributes[attribute] = 1; | |
| } | |
| if (attributeDivisors[attribute] !== meshPerAttribute) { | |
| const extension = capabilities.isWebGL2 ? gl : extensions.get('ANGLE_instanced_arrays'); | |
| extension[capabilities.isWebGL2 ? 'vertexAttribDivisor' : 'vertexAttribDivisorANGLE'](attribute, meshPerAttribute); | |
| attributeDivisors[attribute] = meshPerAttribute; | |
| } | |
| } | |
| function disableUnusedAttributes() { | |
| const newAttributes = currentState.newAttributes; | |
| const enabledAttributes = currentState.enabledAttributes; | |
| for (let i = 0, il = enabledAttributes.length; i < il; i++) { | |
| if (enabledAttributes[i] !== newAttributes[i]) { | |
| gl.disableVertexAttribArray(i); | |
| enabledAttributes[i] = 0; | |
| } | |
| } | |
| } | |
| function vertexAttribPointer(index, size, type, normalized, stride, offset) { | |
| if (capabilities.isWebGL2 === true && (type === gl.INT || type === gl.UNSIGNED_INT)) { | |
| gl.vertexAttribIPointer(index, size, type, stride, offset); | |
| } else { | |
| gl.vertexAttribPointer(index, size, type, normalized, stride, offset); | |
| } | |
| } | |
| function setupVertexAttributes(object, material, program, geometry) { | |
| if (capabilities.isWebGL2 === false && (object.isInstancedMesh || geometry.isInstancedBufferGeometry)) { | |
| if (extensions.get('ANGLE_instanced_arrays') === null) return; | |
| } | |
| initAttributes(); | |
| const geometryAttributes = geometry.attributes; | |
| const programAttributes = program.getAttributes(); | |
| const materialDefaultAttributeValues = material.defaultAttributeValues; | |
| for (const name in programAttributes) { | |
| const programAttribute = programAttributes[name]; | |
| if (programAttribute.location >= 0) { | |
| let geometryAttribute = geometryAttributes[name]; | |
| if (geometryAttribute === undefined) { | |
| if (name === 'instanceMatrix' && object.instanceMatrix) geometryAttribute = object.instanceMatrix; | |
| if (name === 'instanceColor' && object.instanceColor) geometryAttribute = object.instanceColor; | |
| } | |
| if (geometryAttribute !== undefined) { | |
| const normalized = geometryAttribute.normalized; | |
| const size = geometryAttribute.itemSize; | |
| const attribute = attributes.get(geometryAttribute); | |
| // TODO Attribute may not be available on context restore | |
| if (attribute === undefined) continue; | |
| const buffer = attribute.buffer; | |
| const type = attribute.type; | |
| const bytesPerElement = attribute.bytesPerElement; | |
| if (geometryAttribute.isInterleavedBufferAttribute) { | |
| const data = geometryAttribute.data; | |
| const stride = data.stride; | |
| const offset = geometryAttribute.offset; | |
| if (data && data.isInstancedInterleavedBuffer) { | |
| for (let i = 0; i < programAttribute.locationSize; i++) { | |
| enableAttributeAndDivisor(programAttribute.location + i, data.meshPerAttribute); | |
| } | |
| if (object.isInstancedMesh !== true && geometry._maxInstanceCount === undefined) { | |
| geometry._maxInstanceCount = data.meshPerAttribute * data.count; | |
| } | |
| } else { | |
| for (let i = 0; i < programAttribute.locationSize; i++) { | |
| enableAttribute(programAttribute.location + i); | |
| } | |
| } | |
| gl.bindBuffer(gl.ARRAY_BUFFER, buffer); | |
| for (let i = 0; i < programAttribute.locationSize; i++) { | |
| vertexAttribPointer( | |
| programAttribute.location + i, | |
| size / programAttribute.locationSize, | |
| type, | |
| normalized, | |
| stride * bytesPerElement, | |
| (offset + (size / programAttribute.locationSize) * i) * bytesPerElement | |
| ); | |
| } | |
| } else { | |
| if (geometryAttribute.isInstancedBufferAttribute) { | |
| for (let i = 0; i < programAttribute.locationSize; i++) { | |
| enableAttributeAndDivisor(programAttribute.location + i, geometryAttribute.meshPerAttribute); | |
| } | |
| if (object.isInstancedMesh !== true && geometry._maxInstanceCount === undefined) { | |
| geometry._maxInstanceCount = geometryAttribute.meshPerAttribute * geometryAttribute.count; | |
| } | |
| } else { | |
| for (let i = 0; i < programAttribute.locationSize; i++) { | |
| enableAttribute(programAttribute.location + i); | |
| } | |
| } | |
| gl.bindBuffer(gl.ARRAY_BUFFER, buffer); | |
| for (let i = 0; i < programAttribute.locationSize; i++) { | |
| vertexAttribPointer( | |
| programAttribute.location + i, | |
| size / programAttribute.locationSize, | |
| type, | |
| normalized, | |
| size * bytesPerElement, | |
| (size / programAttribute.locationSize) * i * bytesPerElement | |
| ); | |
| } | |
| } | |
| } else if (materialDefaultAttributeValues !== undefined) { | |
| const value = materialDefaultAttributeValues[name]; | |
| if (value !== undefined) { | |
| switch (value.length) { | |
| case 2: | |
| gl.vertexAttrib2fv(programAttribute.location, value); | |
| break; | |
| case 3: | |
| gl.vertexAttrib3fv(programAttribute.location, value); | |
| break; | |
| case 4: | |
| gl.vertexAttrib4fv(programAttribute.location, value); | |
| break; | |
| default: | |
| gl.vertexAttrib1fv(programAttribute.location, value); | |
| } | |
| } | |
| } | |
| } | |
| } | |
| disableUnusedAttributes(); | |
| } | |
| function dispose() { | |
| reset(); | |
| for (const geometryId in bindingStates) { | |
| const programMap = bindingStates[geometryId]; | |
| for (const programId in programMap) { | |
| const stateMap = programMap[programId]; | |
| for (const wireframe in stateMap) { | |
| deleteVertexArrayObject(stateMap[wireframe].object); | |
| delete stateMap[wireframe]; | |
| } | |
| delete programMap[programId]; | |
| } | |
| delete bindingStates[geometryId]; | |
| } | |
| } | |
| function releaseStatesOfGeometry(geometry) { | |
| if (bindingStates[geometry.id] === undefined) return; | |
| const programMap = bindingStates[geometry.id]; | |
| for (const programId in programMap) { | |
| const stateMap = programMap[programId]; | |
| for (const wireframe in stateMap) { | |
| deleteVertexArrayObject(stateMap[wireframe].object); | |
| delete stateMap[wireframe]; | |
| } | |
| delete programMap[programId]; | |
| } | |
| delete bindingStates[geometry.id]; | |
| } | |
| function releaseStatesOfProgram(program) { | |
| for (const geometryId in bindingStates) { | |
| const programMap = bindingStates[geometryId]; | |
| if (programMap[program.id] === undefined) continue; | |
| const stateMap = programMap[program.id]; | |
| for (const wireframe in stateMap) { | |
| deleteVertexArrayObject(stateMap[wireframe].object); | |
| delete stateMap[wireframe]; | |
| } | |
| delete programMap[program.id]; | |
| } | |
| } | |
| function reset() { | |
| resetDefaultState(); | |
| if (currentState === defaultState) return; | |
| currentState = defaultState; | |
| bindVertexArrayObject(currentState.object); | |
| } | |
| // for backward-compatilibity | |
| function resetDefaultState() { | |
| defaultState.geometry = null; | |
| defaultState.program = null; | |
| defaultState.wireframe = false; | |
| } | |
| return { | |
| setup: setup, | |
| reset: reset, | |
| resetDefaultState: resetDefaultState, | |
| dispose: dispose, | |
| releaseStatesOfGeometry: releaseStatesOfGeometry, | |
| releaseStatesOfProgram: releaseStatesOfProgram, | |
| initAttributes: initAttributes, | |
| enableAttribute: enableAttribute, | |
| disableUnusedAttributes: disableUnusedAttributes, | |
| }; | |
| } | |
| export { WebGLBindingStates }; | |