123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575 |
- 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 );
- if ( updateBuffers ) saveCache( geometry );
- } 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: {}
- };
- }
- function needsUpdate( geometry ) {
- const cachedAttributes = currentState.attributes;
- const geometryAttributes = geometry.attributes;
- if ( Object.keys( cachedAttributes ).length !== Object.keys( geometryAttributes ).length ) return true;
- 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;
- }
- return false;
- }
- function saveCache( geometry ) {
- const cache = {};
- const attributes = geometry.attributes;
- for ( const key in attributes ) {
- const attribute = attributes[ key ];
- const data = {};
- data.attribute = attribute;
- if ( attribute.data ) {
- data.data = attribute.data;
- }
- cache[ key ] = data;
- }
- currentState.attributes = cache;
- }
- 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 >= 0 ) {
- const geometryAttribute = geometryAttributes[ name ];
- 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 ) {
- enableAttributeAndDivisor( programAttribute, data.meshPerAttribute );
- if ( geometry._maxInstanceCount === undefined ) {
- geometry._maxInstanceCount = data.meshPerAttribute * data.count;
- }
- } else {
- enableAttribute( programAttribute );
- }
- gl.bindBuffer( gl.ARRAY_BUFFER, buffer );
- vertexAttribPointer( programAttribute, size, type, normalized, stride * bytesPerElement, offset * bytesPerElement );
- } else {
- if ( geometryAttribute.isInstancedBufferAttribute ) {
- enableAttributeAndDivisor( programAttribute, geometryAttribute.meshPerAttribute );
- if ( geometry._maxInstanceCount === undefined ) {
- geometry._maxInstanceCount = geometryAttribute.meshPerAttribute * geometryAttribute.count;
- }
- } else {
- enableAttribute( programAttribute );
- }
- gl.bindBuffer( gl.ARRAY_BUFFER, buffer );
- vertexAttribPointer( programAttribute, size, type, normalized, 0, 0 );
- }
- } else if ( name === 'instanceMatrix' ) {
- const attribute = attributes.get( object.instanceMatrix );
- // TODO Attribute may not be available on context restore
- if ( attribute === undefined ) continue;
- const buffer = attribute.buffer;
- const type = attribute.type;
- enableAttributeAndDivisor( programAttribute + 0, 1 );
- enableAttributeAndDivisor( programAttribute + 1, 1 );
- enableAttributeAndDivisor( programAttribute + 2, 1 );
- enableAttributeAndDivisor( programAttribute + 3, 1 );
- gl.bindBuffer( gl.ARRAY_BUFFER, buffer );
- gl.vertexAttribPointer( programAttribute + 0, 4, type, false, 64, 0 );
- gl.vertexAttribPointer( programAttribute + 1, 4, type, false, 64, 16 );
- gl.vertexAttribPointer( programAttribute + 2, 4, type, false, 64, 32 );
- gl.vertexAttribPointer( programAttribute + 3, 4, type, false, 64, 48 );
- } else if ( name === 'instanceColor' ) {
- const attribute = attributes.get( object.instanceColor );
- // TODO Attribute may not be available on context restore
- if ( attribute === undefined ) continue;
- const buffer = attribute.buffer;
- const type = attribute.type;
- enableAttributeAndDivisor( programAttribute, 1 );
- gl.bindBuffer( gl.ARRAY_BUFFER, buffer );
- gl.vertexAttribPointer( programAttribute, 3, type, false, 12, 0 );
- } else if ( materialDefaultAttributeValues !== undefined ) {
- const 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 );
- }
- }
- }
- }
- }
- 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 };
|