Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
108 changes: 106 additions & 2 deletions examples/jsm/exporters/PLYExporter.js
Original file line number Diff line number Diff line change
Expand Up @@ -63,12 +63,15 @@ class PLYExporter {
const defaultOptions = {
binary: false,
excludeAttributes: [], // normal, uv, color, index
littleEndian: false
littleEndian: false,
customPropertyMapping: {}
};

options = Object.assign( defaultOptions, options );

const excludeAttributes = options.excludeAttributes;
const customPropertyMapping = options.customPropertyMapping;
const customAttributeNames = Object.keys( customPropertyMapping );
let includeIndices = true;
let includeNormals = false;
let includeColors = false;
Expand All @@ -81,6 +84,9 @@ class PLYExporter {
let uvType = 'float';
let colorType = 'uchar';

const customTypes = {};
for ( const name of customAttributeNames ) customTypes[ name ] = 'float';

// count the vertices, check which properties are used,
// and cache the BufferGeometry
let vertexCount = 0;
Expand Down Expand Up @@ -131,6 +137,13 @@ class PLYExporter {

}

for ( const name of customAttributeNames ) {

const attr = geometry.getAttribute( name );
if ( attr !== undefined ) customTypes[ name ] = getPlyType( attr.array );

}

} else if ( child.isPoints ) {

const mesh = child;
Expand Down Expand Up @@ -158,6 +171,13 @@ class PLYExporter {

}

for ( const name of customAttributeNames ) {

const attr = geometry.getAttribute( name );
if ( attr !== undefined ) customTypes[ name ] = getPlyType( attr.array );

}

includeIndices = false;

}
Expand Down Expand Up @@ -228,6 +248,19 @@ class PLYExporter {

}

// custom attributes

for ( const name of customAttributeNames ) {

const type = customTypes[ name ];
for ( const propName of customPropertyMapping[ name ] ) {

header += `property ${type} ${propName}\n`;

}

}

if ( includeIndices === true ) {

// faces
Expand Down Expand Up @@ -257,11 +290,26 @@ class PLYExporter {
const colorIsFloat = isFloatType( colorType );
const colorScale = getColorScale( colorType );

const customWriters = {};
const customIsFloat = {};
let customStride = 0;

for ( const name of customAttributeNames ) {

const type = customTypes[ name ];
const writer = getBinaryWriter( type );
customWriters[ name ] = writer;
customIsFloat[ name ] = isFloatType( type );
customStride += customPropertyMapping[ name ].length * writer.size;

}

const vertexListLength = vertexCount * (
3 * posWriter.size +
( includeNormals ? 3 * normalWriter.size : 0 ) +
( includeUVs ? 2 * uvWriter.size : 0 ) +
( includeColors ? 3 * colorWriter.size : 0 )
( includeColors ? 3 * colorWriter.size : 0 ) +
customStride
);

// 1 byte shape descriptor
Expand Down Expand Up @@ -396,6 +444,25 @@ class PLYExporter {

}

// Custom attributes

for ( const name of customAttributeNames ) {

const writer = customWriters[ name ];
const propCount = customPropertyMapping[ name ].length;
const attr = geometry.getAttribute( name );
const isFloat = customIsFloat[ name ];

for ( let c = 0; c < propCount; c ++ ) {

const raw = attr != null ? getAttributeComponent( attr, i, c ) : 0;
writer.write( output, vOffset, isFloat ? raw : Math.round( raw ), options.littleEndian );
vOffset += writer.size;

}

}

}

if ( includeIndices === true ) {
Expand Down Expand Up @@ -465,6 +532,9 @@ class PLYExporter {
const colorIsFloat = isFloatType( colorType );
const colorScale = getColorScale( colorType );

const customIsFloat = {};
for ( const name of customAttributeNames ) customIsFloat[ name ] = isFloatType( customTypes[ name ] );

const encode = ( v, isFloat ) => isFloat ? v : Math.round( v );

traverseMeshes( function ( mesh, geometry ) {
Expand Down Expand Up @@ -554,6 +624,23 @@ class PLYExporter {

}

// Custom attributes

for ( const name of customAttributeNames ) {

const propCount = customPropertyMapping[ name ].length;
const attr = geometry.getAttribute( name );
const isFloat = customIsFloat[ name ];

for ( let c = 0; c < propCount; c ++ ) {

const raw = attr != null ? getAttributeComponent( attr, i, c ) : 0;
line += ' ' + encode( raw, isFloat );

}

}

vertexList += line + '\n';

}
Expand Down Expand Up @@ -638,6 +725,19 @@ function isFloatType( type ) {

}

function getAttributeComponent( attr, i, c ) {

switch ( c ) {

case 0: return attr.getX( i );
case 1: return attr.getY( i );
case 2: return attr.getZ( i );
case 3: return attr.getW( i );

}

}

function getColorScale( type ) {

switch ( type ) {
Expand All @@ -659,6 +759,10 @@ function getColorScale( type ) {
* the exported PLY file. Valid values are `'color'`, `'normal'`, `'uv'`, and `'index'`. If triangle
* indices are excluded, then a point cloud is exported.
* @property {boolean} [littleEndian=false] - Whether the binary export uses little or big endian.
* @property {Object<string, Array<string>>} [customPropertyMapping] - A mapping that allows
* exporting custom buffer attributes as PLY vertex properties. Each entry maps a buffer attribute
* name to an array of PLY property names. The number of property names must match the item size
* of the buffer attribute. This is the inverse of `PLYLoader.setCustomPropertyNameMapping()`.
**/

/**
Expand Down