Spaces:
Running
Running
| import { Color, ColorManagement, SRGBColorSpace } from 'three'; | |
| /** | |
| * Export draco compressed files from threejs geometry objects. | |
| * | |
| * Draco files are compressed and usually are smaller than conventional 3D file formats. | |
| * | |
| * The exporter receives a options object containing | |
| * - decodeSpeed, indicates how to tune the encoder regarding decode speed (0 gives better speed but worst quality) | |
| * - encodeSpeed, indicates how to tune the encoder parameters (0 gives better speed but worst quality) | |
| * - encoderMethod | |
| * - quantization, indicates the presision of each type of data stored in the draco file in the order (POSITION, NORMAL, COLOR, TEX_COORD, GENERIC) | |
| * - exportUvs | |
| * - exportNormals | |
| * - exportColor | |
| */ | |
| /* global DracoEncoderModule */ | |
| class DRACOExporter { | |
| parse( object, options = {} ) { | |
| options = Object.assign( { | |
| decodeSpeed: 5, | |
| encodeSpeed: 5, | |
| encoderMethod: DRACOExporter.MESH_EDGEBREAKER_ENCODING, | |
| quantization: [ 16, 8, 8, 8, 8 ], | |
| exportUvs: true, | |
| exportNormals: true, | |
| exportColor: false, | |
| }, options ); | |
| if ( DracoEncoderModule === undefined ) { | |
| throw new Error( 'THREE.DRACOExporter: required the draco_encoder to work.' ); | |
| } | |
| const geometry = object.geometry; | |
| const dracoEncoder = DracoEncoderModule(); | |
| const encoder = new dracoEncoder.Encoder(); | |
| let builder; | |
| let dracoObject; | |
| if ( object.isMesh === true ) { | |
| builder = new dracoEncoder.MeshBuilder(); | |
| dracoObject = new dracoEncoder.Mesh(); | |
| const vertices = geometry.getAttribute( 'position' ); | |
| builder.AddFloatAttributeToMesh( dracoObject, dracoEncoder.POSITION, vertices.count, vertices.itemSize, vertices.array ); | |
| const faces = geometry.getIndex(); | |
| if ( faces !== null ) { | |
| builder.AddFacesToMesh( dracoObject, faces.count / 3, faces.array ); | |
| } else { | |
| const faces = new ( vertices.count > 65535 ? Uint32Array : Uint16Array )( vertices.count ); | |
| for ( let i = 0; i < faces.length; i ++ ) { | |
| faces[ i ] = i; | |
| } | |
| builder.AddFacesToMesh( dracoObject, vertices.count, faces ); | |
| } | |
| if ( options.exportNormals === true ) { | |
| const normals = geometry.getAttribute( 'normal' ); | |
| if ( normals !== undefined ) { | |
| builder.AddFloatAttributeToMesh( dracoObject, dracoEncoder.NORMAL, normals.count, normals.itemSize, normals.array ); | |
| } | |
| } | |
| if ( options.exportUvs === true ) { | |
| const uvs = geometry.getAttribute( 'uv' ); | |
| if ( uvs !== undefined ) { | |
| builder.AddFloatAttributeToMesh( dracoObject, dracoEncoder.TEX_COORD, uvs.count, uvs.itemSize, uvs.array ); | |
| } | |
| } | |
| if ( options.exportColor === true ) { | |
| const colors = geometry.getAttribute( 'color' ); | |
| if ( colors !== undefined ) { | |
| const array = createVertexColorSRGBArray( colors ); | |
| builder.AddFloatAttributeToMesh( dracoObject, dracoEncoder.COLOR, colors.count, colors.itemSize, array ); | |
| } | |
| } | |
| } else if ( object.isPoints === true ) { | |
| builder = new dracoEncoder.PointCloudBuilder(); | |
| dracoObject = new dracoEncoder.PointCloud(); | |
| const vertices = geometry.getAttribute( 'position' ); | |
| builder.AddFloatAttribute( dracoObject, dracoEncoder.POSITION, vertices.count, vertices.itemSize, vertices.array ); | |
| if ( options.exportColor === true ) { | |
| const colors = geometry.getAttribute( 'color' ); | |
| if ( colors !== undefined ) { | |
| const array = createVertexColorSRGBArray( colors ); | |
| builder.AddFloatAttribute( dracoObject, dracoEncoder.COLOR, colors.count, colors.itemSize, array ); | |
| } | |
| } | |
| } else { | |
| throw new Error( 'DRACOExporter: Unsupported object type.' ); | |
| } | |
| //Compress using draco encoder | |
| const encodedData = new dracoEncoder.DracoInt8Array(); | |
| //Sets the desired encoding and decoding speed for the given options from 0 (slowest speed, but the best compression) to 10 (fastest, but the worst compression). | |
| const encodeSpeed = ( options.encodeSpeed !== undefined ) ? options.encodeSpeed : 5; | |
| const decodeSpeed = ( options.decodeSpeed !== undefined ) ? options.decodeSpeed : 5; | |
| encoder.SetSpeedOptions( encodeSpeed, decodeSpeed ); | |
| // Sets the desired encoding method for a given geometry. | |
| if ( options.encoderMethod !== undefined ) { | |
| encoder.SetEncodingMethod( options.encoderMethod ); | |
| } | |
| // Sets the quantization (number of bits used to represent) compression options for a named attribute. | |
| // The attribute values will be quantized in a box defined by the maximum extent of the attribute values. | |
| if ( options.quantization !== undefined ) { | |
| for ( let i = 0; i < 5; i ++ ) { | |
| if ( options.quantization[ i ] !== undefined ) { | |
| encoder.SetAttributeQuantization( i, options.quantization[ i ] ); | |
| } | |
| } | |
| } | |
| let length; | |
| if ( object.isMesh === true ) { | |
| length = encoder.EncodeMeshToDracoBuffer( dracoObject, encodedData ); | |
| } else { | |
| length = encoder.EncodePointCloudToDracoBuffer( dracoObject, true, encodedData ); | |
| } | |
| dracoEncoder.destroy( dracoObject ); | |
| if ( length === 0 ) { | |
| throw new Error( 'THREE.DRACOExporter: Draco encoding failed.' ); | |
| } | |
| //Copy encoded data to buffer. | |
| const outputData = new Int8Array( new ArrayBuffer( length ) ); | |
| for ( let i = 0; i < length; i ++ ) { | |
| outputData[ i ] = encodedData.GetValue( i ); | |
| } | |
| dracoEncoder.destroy( encodedData ); | |
| dracoEncoder.destroy( encoder ); | |
| dracoEncoder.destroy( builder ); | |
| return outputData; | |
| } | |
| } | |
| function createVertexColorSRGBArray( attribute ) { | |
| // While .drc files do not specify colorspace, the only 'official' tooling | |
| // is PLY and OBJ converters, which use sRGB. We'll assume sRGB is expected | |
| // for .drc files, but note that Draco buffers embedded in glTF files will | |
| // be Linear-sRGB instead. | |
| const _color = new Color(); | |
| const count = attribute.count; | |
| const itemSize = attribute.itemSize; | |
| const array = new Float32Array( count * itemSize ); | |
| for ( let i = 0, il = count; i < il; i ++ ) { | |
| _color.fromBufferAttribute( attribute, i ); | |
| ColorManagement.fromWorkingColorSpace( _color, SRGBColorSpace ); | |
| array[ i * itemSize ] = _color.r; | |
| array[ i * itemSize + 1 ] = _color.g; | |
| array[ i * itemSize + 2 ] = _color.b; | |
| if ( itemSize === 4 ) { | |
| array[ i * itemSize + 3 ] = attribute.getW( i ); | |
| } | |
| } | |
| return array; | |
| } | |
| // Encoder methods | |
| DRACOExporter.MESH_EDGEBREAKER_ENCODING = 1; | |
| DRACOExporter.MESH_SEQUENTIAL_ENCODING = 0; | |
| // Geometry type | |
| DRACOExporter.POINT_CLOUD = 0; | |
| DRACOExporter.TRIANGULAR_MESH = 1; | |
| // Attribute type | |
| DRACOExporter.INVALID = - 1; | |
| DRACOExporter.POSITION = 0; | |
| DRACOExporter.NORMAL = 1; | |
| DRACOExporter.COLOR = 2; | |
| DRACOExporter.TEX_COORD = 3; | |
| DRACOExporter.GENERIC = 4; | |
| export { DRACOExporter }; | |