officialAccount/node_modules/three/examples/jsm/exporters/DRACOExporter.js

312 lines
8.3 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import { Color, ColorManagement, SRGBColorSpace } from 'three';
/* global DracoEncoderModule */
/**
* An exporter to compress geometry with the Draco library.
*
* [Draco]{@link https://google.github.io/draco/} is an open source library for compressing and
* decompressing 3D meshes and point clouds. Compressed geometry can be significantly smaller,
* at the cost of additional decoding time on the client device.
*
* Standalone Draco files have a `.drc` extension, and contain vertex positions,
* normals, colors, and other attributes. Draco files *do not* contain materials,
* textures, animation, or node hierarchies to use these features, embed Draco geometry
* inside of a glTF file. A normal glTF file can be converted to a Draco-compressed glTF file
* using [glTF-Pipeline]{@link https://github.com/AnalyticalGraphicsInc/gltf-pipeline}.
*
* ```js
* const exporter = new DRACOExporter();
* const data = exporter.parse( mesh, options );
* ```
*
* @three_import import { DRACOExporter } from 'three/addons/exporters/DRACOExporter.js';
*/
class DRACOExporter {
/**
* Parses the given mesh or point cloud and generates the Draco output.
*
* @param {(Mesh|Points)} object - The mesh or point cloud to export.
* @param {DRACOExporter~Options} options - The export options.
* @return {Int8Array} The exported Draco.
*/
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
/**
* Edgebreaker encoding.
*
* @static
* @constant
* @type {number}
* @default 1
*/
DRACOExporter.MESH_EDGEBREAKER_ENCODING = 1;
/**
* Sequential encoding.
*
* @static
* @constant
* @type {number}
* @default 0
*/
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 options of `DRACOExporter`.
*
* @typedef {Object} DRACOExporter~Options
* @property {number} [decodeSpeed=5] - Indicates how to tune the encoder regarding decode speed (0 gives better speed but worst quality).
* @property {number} [encodeSpeed=5] - Indicates how to tune the encoder parameters (0 gives better speed but worst quality).
* @property {number} [encoderMethod=1] - Either sequential (very little compression) or Edgebreaker. Edgebreaker traverses the triangles of the mesh in a deterministic, spiral-like way which provides most of the benefits of this data format.
* @property {Array<number>} [quantization=[ 16, 8, 8, 8, 8 ]] - Indicates the precision of each type of data stored in the draco file in the order (POSITION, NORMAL, COLOR, TEX_COORD, GENERIC).
* @property {boolean} [exportUvs=true] - Whether to export UVs or not.
* @property {boolean} [exportNormals=true] - Whether to export normals or not.
* @property {boolean} [exportColor=false] - Whether to export colors or not.
**/
export { DRACOExporter };