UNPKG

3d-tiles-renderer

Version:

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

217 lines (146 loc) 6.18 kB
import { PNTSLoaderBase } from '../../../core/renderer/loaders/PNTSLoaderBase.js'; import { Points, PointsMaterial, BufferGeometry, BufferAttribute, DefaultLoadingManager, Vector3, Color, } from 'three'; import { rgb565torgb } from '../../../core/renderer/utilities/rgb565torgb.js'; import { decodeOctNormal } from '../../../core/renderer/utilities/decodeOctNormal.js'; const DRACO_ATTRIBUTE_MAP = { RGB: 'color', POSITION: 'position', }; export class PNTSLoader extends PNTSLoaderBase { constructor( manager = DefaultLoadingManager ) { super(); this.manager = manager; } parse( buffer ) { return super.parse( buffer ).then( async ( result ) => { const { featureTable, batchTable } = result; const material = new PointsMaterial(); const extensions = featureTable.header.extensions; const translationOffset = new Vector3(); let geometry; // handle loading the draco data if ( extensions && extensions[ '3DTILES_draco_point_compression' ] ) { const { byteOffset, byteLength, properties } = extensions[ '3DTILES_draco_point_compression' ]; const dracoLoader = this.manager.getHandler( 'draco.drc' ); if ( dracoLoader == null ) { throw new Error( 'PNTSLoader: dracoLoader not available.' ); } // map PNTS keys to draco types const attributeIDs = {}; for ( const key in properties ) { if ( key in DRACO_ATTRIBUTE_MAP && key in properties ) { const mappedKey = DRACO_ATTRIBUTE_MAP[ key ]; attributeIDs[ mappedKey ] = properties[ key ]; } } // decode the geometry const taskConfig = { attributeIDs, attributeTypes: { position: 'Float32Array', color: 'Uint8Array', }, useUniqueIDs: true, }; const buffer = featureTable.getBuffer( byteOffset, byteLength ); geometry = await dracoLoader.decodeGeometry( buffer, taskConfig ); if ( geometry.attributes.color ) { material.vertexColors = true; } } else { // handle non compressed case const POINTS_LENGTH = featureTable.getData( 'POINTS_LENGTH' ); const POSITION = featureTable.getData( 'POSITION', POINTS_LENGTH, 'FLOAT', 'VEC3' ); const NORMAL = featureTable.getData( 'NORMAL', POINTS_LENGTH, 'FLOAT', 'VEC3' ); const NORMAL_OCT16P = featureTable.getData( 'NORMAL', POINTS_LENGTH, 'UNSIGNED_BYTE', 'VEC2' ); const RGB = featureTable.getData( 'RGB', POINTS_LENGTH, 'UNSIGNED_BYTE', 'VEC3' ); const RGBA = featureTable.getData( 'RGBA', POINTS_LENGTH, 'UNSIGNED_BYTE', 'VEC4' ); const RGB565 = featureTable.getData( 'RGB565', POINTS_LENGTH, 'UNSIGNED_SHORT', 'SCALAR' ); const CONSTANT_RGBA = featureTable.getData( 'CONSTANT_RGBA', POINTS_LENGTH, 'UNSIGNED_BYTE', 'VEC4' ); const POSITION_QUANTIZED = featureTable.getData( 'POSITION_QUANTIZED', POINTS_LENGTH, 'UNSIGNED_SHORT', 'VEC3' ); const QUANTIZED_VOLUME_SCALE = featureTable.getData( 'QUANTIZED_VOLUME_SCALE', POINTS_LENGTH, 'FLOAT', 'VEC3' ); const QUANTIZED_VOLUME_OFFSET = featureTable.getData( 'QUANTIZED_VOLUME_OFFSET', POINTS_LENGTH, 'FLOAT', 'VEC3' ); geometry = new BufferGeometry(); if ( POSITION_QUANTIZED ) { const decodedPositions = new Float32Array( POINTS_LENGTH * 3 ); for ( let i = 0; i < POINTS_LENGTH; i ++ ) { for ( let j = 0; j < 3; j ++ ) { const index = 3 * i + j; decodedPositions[ index ] = ( POSITION_QUANTIZED[ index ] / 65535.0 ) * QUANTIZED_VOLUME_SCALE[ j ]; } } translationOffset.x = QUANTIZED_VOLUME_OFFSET[ 0 ]; translationOffset.y = QUANTIZED_VOLUME_OFFSET[ 1 ]; translationOffset.z = QUANTIZED_VOLUME_OFFSET[ 2 ]; geometry.setAttribute( 'position', new BufferAttribute( decodedPositions, 3, false ) ); } else { geometry.setAttribute( 'position', new BufferAttribute( POSITION, 3, false ) ); } if ( NORMAL !== null ) { geometry.setAttribute( 'normal', new BufferAttribute( NORMAL, 3, false ) ); } else if ( NORMAL_OCT16P !== null ) { const decodedNormals = new Float32Array( POINTS_LENGTH * 3 ); const n = new Vector3(); for ( let i = 0; i < POINTS_LENGTH; i ++ ) { const x = NORMAL_OCT16P[ i * 2 ]; const y = NORMAL_OCT16P[ i * 2 + 1 ]; const normal = decodeOctNormal( x, y, n ); decodedNormals[ i * 3 ] = normal.x; decodedNormals[ i * 3 + 1 ] = normal.y; decodedNormals[ i * 3 + 2 ] = normal.z; } geometry.setAttribute( 'normal', new BufferAttribute( decodedNormals, 3, false ) ); } if ( RGBA !== null ) { geometry.setAttribute( 'color', new BufferAttribute( RGBA, 4, true ) ); material.vertexColors = true; material.transparent = true; material.depthWrite = false; } else if ( RGB !== null ) { geometry.setAttribute( 'color', new BufferAttribute( RGB, 3, true ) ); material.vertexColors = true; } else if ( RGB565 !== null ) { const color = new Uint8Array( POINTS_LENGTH * 3 ); for ( let i = 0; i < POINTS_LENGTH; i ++ ) { const rgbColor = rgb565torgb( RGB565[ i ] ); for ( let j = 0; j < 3; j ++ ) { const index = 3 * i + j; color[ index ] = rgbColor[ j ]; } } geometry.setAttribute( 'color', new BufferAttribute( color, 3, true ) ); material.vertexColors = true; } else if ( CONSTANT_RGBA !== null ) { const color = new Color( CONSTANT_RGBA[ 0 ], CONSTANT_RGBA[ 1 ], CONSTANT_RGBA[ 2 ] ); material.color = color; const opacity = CONSTANT_RGBA[ 3 ] / 255; if ( opacity < 1 ) { material.opacity = opacity; material.transparent = true; material.depthWrite = false; } } } const object = new Points( geometry, material ); object.position.copy( translationOffset ); result.scene = object; result.scene.featureTable = featureTable; result.scene.batchTable = batchTable; const rtcCenter = featureTable.getData( 'RTC_CENTER', 1, 'FLOAT', 'VEC3' ); if ( rtcCenter ) { result.scene.position.x += rtcCenter[ 0 ]; result.scene.position.y += rtcCenter[ 1 ]; result.scene.position.z += rtcCenter[ 2 ]; } return result; } ); } }