UNPKG

3d-tiles-renderer

Version:

https://github.com/AnalyticalGraphicsInc/3d-tiles/tree/master/specification

210 lines (135 loc) 5.74 kB
import { ClassProperty } from './ClassProperty.js'; import { PropertySetAccessor } from './PropertySetAccessor.js'; import { initializeFromClass, getArrayConstructorFromComponentType, readDataFromBufferToType, getField, isNumericType, typeToComponentCount, } from '../utilities/ClassPropertyHelpers.js'; class PropertyTableClassProperty extends ClassProperty { constructor( enums, classProperty, tableProperty = null ) { super( enums, classProperty, tableProperty ); this.values = tableProperty?.values ?? null; this.valueLength = typeToComponentCount( this.type ); this.arrayOffsets = getField( tableProperty, 'arrayOffsets', null ); this.stringOffsets = getField( tableProperty, 'stringOffsets', null ); this.arrayOffsetType = getField( tableProperty, 'arrayOffsetType', 'UINT32' ); this.stringOffsetType = getField( tableProperty, 'stringOffsetType', 'UINT32' ); } // returns the necessary array length based on the array offsets if present getArrayLengthFromId( buffers, id ) { let count = this.count; if ( this.arrayOffsets !== null ) { const { arrayOffsets, arrayOffsetType } = this; const bufferCons = getArrayConstructorFromComponentType( arrayOffsetType ); const arr = new bufferCons( buffers[ arrayOffsets ] ); count = arr[ id + 1 ] - arr[ id ]; } return count; } // returns the index offset into the data buffer for the given id based on the // the array offsets if present getIndexOffsetFromId( buffers, id ) { let indexOffset = id; if ( this.arrayOffsets ) { const { arrayOffsets, arrayOffsetType } = this; const bufferCons = getArrayConstructorFromComponentType( arrayOffsetType ); const arr = new bufferCons( buffers[ arrayOffsets ] ); indexOffset = arr[ indexOffset ]; } else if ( this.array ) { // TODO: why do this? Revisit indexOffset *= this.count; } return indexOffset; } } export class PropertyTableAccessor extends PropertySetAccessor { constructor( ...args ) { super( ...args ); this.isPropertyTableAccessor = true; this.count = this.definition.count; this._initProperties( PropertyTableClassProperty ); } getData( id, target = {} ) { const properties = this.properties; initializeFromClass( properties, target ); for ( const name in properties ) { target[ name ] = this.getPropertyValue( name, id, target[ name ] ); } return target; } // reads an individual element _readValueAtIndex( name, id, index, target = null ) { const property = this.properties[ name ]; const { componentType, type } = property; const buffers = this.data; const bufferView = buffers[ property.values ]; const bufferCons = getArrayConstructorFromComponentType( componentType, type ); const dataArray = new bufferCons( bufferView ); // array offsets contain element offsets, not byte offsets const indexOffset = property.getIndexOffsetFromId( buffers, id ); if ( isNumericType( type ) || type === 'ENUM' ) { // "readDataFromBufferToType" takes the start offset to read from so we multiply the // index by the final value length return readDataFromBufferToType( dataArray, ( indexOffset + index ) * property.valueLength, type, target ); } else if ( type === 'STRING' ) { // https://github.com/CesiumGS/3d-tiles/tree/main/specification/Metadata/#variable-length-arrays let stringIndex = indexOffset + index; let stringLength = 0; if ( property.stringOffsets !== null ) { // get the string lengths and beginning offsets if variable const { stringOffsets, stringOffsetType } = property; const bufferCons = getArrayConstructorFromComponentType( stringOffsetType ); const stringOffsetBuffer = new bufferCons( buffers[ stringOffsets ] ); stringLength = stringOffsetBuffer[ stringIndex + 1 ] - stringOffsetBuffer[ stringIndex ]; stringIndex = stringOffsetBuffer[ stringIndex ]; } const byteArray = new Uint8Array( dataArray.buffer, stringIndex, stringLength ); target = new TextDecoder().decode( byteArray ); } else if ( type === 'BOOLEAN' ) { const offset = indexOffset + index; const byteIndex = Math.floor( offset / 8 ); const bitIndex = offset % 8; const bitValue = ( dataArray[ byteIndex ] >> bitIndex ) & 1; target = bitValue === 1; } return target; } // Reads the data for the given table index getPropertyValue( name, id, target = null ) { // check if the requested id is outside of the size of the table if ( id >= this.count ) { throw new Error( 'PropertyTableAccessor: Requested index is outside the range of the table.' ); } // check to see if we skip this field since its not in the table const property = this.properties[ name ]; if ( ! property ) { throw new Error( 'PropertyTableAccessor: Requested property does not exist.' ); } else if ( ! this.definition.properties[ name ] ) { return property.resolveDefault( target ); } // get the dynamic array count from the property buffer const array = property.array; const buffers = this.data; const count = property.getArrayLengthFromId( buffers, id ); // initialize the array target = property.shapeToProperty( target, count ); // read all data if ( array ) { for ( let i = 0, l = target.length; i < l; i ++ ) { target[ i ] = this._readValueAtIndex( name, id, i, target[ i ] ); } } else { target = this._readValueAtIndex( name, id, 0, target ); } // scale the numeric values target = property.adjustValueScaleOffset( target ); // convert to enum strings - no data enum values are stored as strings target = property.resolveEnumsToStrings( target ); // resolve to default values target = property.resolveNoData( target ); return target; } }