3d-tiles-renderer
Version:
https://github.com/AnalyticalGraphicsInc/3d-tiles/tree/master/specification
245 lines (160 loc) • 6.62 kB
JavaScript
import { Vector2 } from 'three';
import { PropertySetAccessor } from './PropertySetAccessor.js';
import { ClassProperty } from './ClassProperty.js';
import { TextureReadUtility } from '../utilities/TextureReadUtility.js';
import { getTexCoord, getTexelIndices, getTriangleVertexIndices } from '../utilities/TexCoordUtilities.js';
import {
initializeFromClass,
initializeFromProperty,
getArrayConstructorFromComponentType,
readDataFromBufferToType,
getField
} from '../utilities/ClassPropertyHelpers.js';
const _uv = /* @__PURE__ */ new Vector2();
const _srcPixel = /* @__PURE__ */ new Vector2();
const _dstPixel = /* @__PURE__ */ new Vector2();
class PropertyTextureClassProperty extends ClassProperty {
constructor( enums, classProperty, textureProperty = null ) {
super( enums, classProperty, textureProperty );
this.channels = getField( textureProperty, 'channels', [ 0 ] );
this.index = getField( textureProperty, 'index', null );
this.texCoord = getField( textureProperty, 'texCoord', null );
this.valueLength = parseInt( this.type.replace( /[^0-9]/g, '' ) ) || 1;
}
// takes the buffer to read from and the value index to read
readDataFromBuffer( buffer, index, target = null ) {
const type = this.type;
if ( type === 'BOOLEAN' || type === 'STRING' ) {
throw new Error( 'PropertyTextureAccessor: BOOLEAN and STRING types not supported.' );
}
// "readDataFromBufferToType" takes the start offset to read from so we multiply the index by the
// final value length
return readDataFromBufferToType( buffer, index * this.valueLength, type, target );
}
}
// Reads and accesses data encoded to textures
export class PropertyTextureAccessor extends PropertySetAccessor {
constructor( ...args ) {
super( ...args );
this.isPropertyTextureAccessor = true;
this._asyncRead = false;
this._initProperties( PropertyTextureClassProperty );
}
// Reads the full set of property data
getData( faceIndex, barycoord, geometry, target = {} ) {
const properties = this.properties;
initializeFromClass( properties, target );
const names = Object.keys( properties );
const results = names.map( n => target[ n ] );
this.getPropertyValuesAtTexel( names, faceIndex, barycoord, geometry, results );
names.forEach( ( n, i ) => target[ n ] = results[ i ] );
return target;
}
// Reads the full set of property data asynchronously
async getDataAsync( faceIndex, barycoord, geometry, target = {} ) {
const properties = this.properties;
initializeFromClass( properties, target );
const names = Object.keys( properties );
const results = names.map( n => target[ n ] );
await this.getPropertyValuesAtTexelAsync( names, faceIndex, barycoord, geometry, results );
names.forEach( ( n, i ) => target[ n ] = results[ i ] );
return target;
}
// Reads values asynchronously
getPropertyValuesAtTexelAsync( ...args ) {
this._asyncRead = true;
const result = this.getPropertyValuesAtTexel( ...args );
this._asyncRead = false;
return result;
}
// Reads values from the textures synchronously
getPropertyValuesAtTexel( names, faceIndex, barycoord, geometry, target = [] ) {
// resize our targets appropriately
while ( target.length < names.length ) target.push( null );
target.length = names.length;
TextureReadUtility.increaseSizeTo( target.length );
// get the attribute indices
const textures = this.data;
const accessorProperties = this.definition.properties;
const properties = this.properties;
const indices = getTriangleVertexIndices( geometry, faceIndex );
for ( let i = 0, l = names.length; i < l; i ++ ) {
// skip any requested class schema properties that are not provided via the accessor
const name = names[ i ];
if ( ! accessorProperties[ name ] ) {
continue;
}
// get the attribute of the target tex coord
const property = properties[ name ];
const texture = textures[ property.index ];
getTexCoord( geometry, property.texCoord, barycoord, indices, _uv );
getTexelIndices( _uv, texture.image.width, texture.image.height, _srcPixel );
_dstPixel.set( i, 0 );
TextureReadUtility.renderPixelToTarget( texture, _srcPixel, _dstPixel );
}
// read the data
const buffer = new Uint8Array( names.length * 4 );
if ( this._asyncRead ) {
return TextureReadUtility
.readDataAsync( buffer )
.then( () => {
readTextureSampleResults.call( this );
return target;
} );
} else {
TextureReadUtility.readData( buffer );
readTextureSampleResults.call( this );
return target;
}
function readTextureSampleResults() {
for ( let i = 0, l = names.length; i < l; i ++ ) {
const name = names[ i ];
const property = properties[ name ];
const type = property.type;
// initialize the output value
target[ i ] = initializeFromProperty( property, target[ i ] );
// use a default of the texture accessor definition does not include the value
if ( ! property ) {
throw new Error( 'PropertyTextureAccessor: Requested property does not exist.' );
} else if ( ! accessorProperties[ name ] ) {
target[ i ] = property.resolveDefault( target );
continue;
}
// get the final array length to read all data based on used buffer data
const length = property.valueLength * ( property.count || 1 );
// set the data read back from the texture to the target type
const data = property.channels.map( c => buffer[ 4 * i + c ] );
const componentType = property.componentType;
const BufferCons = getArrayConstructorFromComponentType( componentType, type );
const readBuffer = new BufferCons( length );
new Uint8Array( readBuffer.buffer ).set( data );
// read all the data
if ( property.array ) {
const arr = target[ i ];
for ( let j = 0, lj = arr.length; j < lj; j ++ ) {
arr[ j ] = property.readDataFromBuffer( readBuffer, j, arr[ j ] );
}
} else {
target[ i ] = property.readDataFromBuffer( readBuffer, 0, target[ i ] );
}
// scale the numeric values
target[ i ] = property.adjustValueScaleOffset( target[ i ] );
// convert to enum strings - no data enum values are stored as strings
target[ i ] = property.resolveEnumsToStrings( target[ i ] );
// resolve to default values
target[ i ] = property.resolveNoData( target[ i ] );
}
}
}
// dispose all of the texture data used
dispose() {
this.data.forEach( texture => {
if ( texture ) {
texture.dispose();
if ( texture.image instanceof ImageBitmap ) {
texture.image.close();
}
}
} );
}
}