From 42ed4ae9cdcb8110a4634090b781eccfe2ddc735 Mon Sep 17 00:00:00 2001 From: Deep Date: Mon, 28 Jan 2019 15:17:54 +0530 Subject: [PATCH 1/3] Loaders: Create ES modules --- examples/jsm/Volume.js | 456 ++ examples/jsm/VolumeSlice.js | 228 + examples/jsm/curves/NURBSCurve.js | 78 + examples/jsm/curves/NURBSSurface.js | 60 + examples/jsm/curves/NURBSUtils.js | 476 ++ examples/jsm/loaders/3MFLoader.js | 628 +++ examples/jsm/loaders/AMFLoader.js | 509 ++ examples/jsm/loaders/AWDLoader.js | 1251 +++++ examples/jsm/loaders/AssimpJSONLoader.js | 305 ++ examples/jsm/loaders/AssimpLoader.js | 2402 ++++++++++ examples/jsm/loaders/BVHLoader.js | 428 ++ examples/jsm/loaders/BabylonLoader.js | 272 ++ examples/jsm/loaders/ColladaLoader.js | 3959 ++++++++++++++++ examples/jsm/loaders/DDSLoader.js | 282 ++ examples/jsm/loaders/DRACOLoader.js | 659 +++ examples/jsm/loaders/EXRLoader.js | 1203 +++++ .../loaders/EquirectangularToCubeGenerator.js | 242 + examples/jsm/loaders/FBXLoader.js | 4163 +++++++++++++++++ examples/jsm/loaders/GCodeLoader.js | 236 + examples/jsm/loaders/GLTFLoader.js | 12 +- examples/jsm/loaders/HDRCubeTextureLoader.js | 214 + examples/jsm/loaders/KMZLoader.js | 122 + examples/jsm/loaders/KTXLoader.js | 171 + examples/jsm/loaders/LDrawLoader.js | 1356 ++++++ examples/jsm/loaders/LoaderSupport.js | 1909 ++++++++ examples/jsm/loaders/MD2Loader.js | 398 ++ examples/jsm/loaders/MMDLoader.js | 2053 ++++++++ examples/jsm/loaders/MTLLoader.js | 597 +++ examples/jsm/loaders/NRRDLoader.js | 607 +++ examples/jsm/loaders/NodeMaterialLoader.js | 272 ++ examples/jsm/loaders/OBJLoader2.js | 1610 +++++++ examples/jsm/loaders/PCDLoader.js | 321 ++ examples/jsm/loaders/PDBLoader.js | 226 + examples/jsm/loaders/PLYLoader.js | 515 ++ examples/jsm/loaders/PRWMLoader.js | 302 ++ examples/jsm/loaders/PVRLoader.js | 256 + examples/jsm/loaders/PlayCanvasLoader.js | 218 + examples/jsm/loaders/RGBELoader.js | 405 ++ examples/jsm/loaders/STLLoader.js | 353 ++ examples/jsm/loaders/SVGLoader.js | 1095 +++++ examples/jsm/loaders/TDSLoader.js | 1143 +++++ examples/jsm/loaders/TGALoader.js | 556 +++ examples/jsm/loaders/TTFLoader.js | 207 + examples/jsm/loaders/VRMLLoader.js | 1326 ++++++ examples/jsm/loaders/VRMLoader.js | 86 + examples/jsm/loaders/VTKLoader.js | 1174 +++++ examples/jsm/loaders/XLoader.js | 1698 +++++++ .../loaders/deprecated/LegacyGLTFLoader.js | 2325 +++++++++ .../loaders/deprecated/LegacyJSONLoader.js | 587 +++ package-lock.json | 5 + package.json | 3 + 51 files changed, 39951 insertions(+), 8 deletions(-) create mode 100644 examples/jsm/Volume.js create mode 100644 examples/jsm/VolumeSlice.js create mode 100644 examples/jsm/curves/NURBSCurve.js create mode 100644 examples/jsm/curves/NURBSSurface.js create mode 100644 examples/jsm/curves/NURBSUtils.js create mode 100644 examples/jsm/loaders/3MFLoader.js create mode 100644 examples/jsm/loaders/AMFLoader.js create mode 100644 examples/jsm/loaders/AWDLoader.js create mode 100644 examples/jsm/loaders/AssimpJSONLoader.js create mode 100644 examples/jsm/loaders/AssimpLoader.js create mode 100644 examples/jsm/loaders/BVHLoader.js create mode 100644 examples/jsm/loaders/BabylonLoader.js create mode 100644 examples/jsm/loaders/ColladaLoader.js create mode 100644 examples/jsm/loaders/DDSLoader.js create mode 100644 examples/jsm/loaders/DRACOLoader.js create mode 100644 examples/jsm/loaders/EXRLoader.js create mode 100644 examples/jsm/loaders/EquirectangularToCubeGenerator.js create mode 100644 examples/jsm/loaders/FBXLoader.js create mode 100644 examples/jsm/loaders/GCodeLoader.js create mode 100644 examples/jsm/loaders/HDRCubeTextureLoader.js create mode 100644 examples/jsm/loaders/KMZLoader.js create mode 100644 examples/jsm/loaders/KTXLoader.js create mode 100644 examples/jsm/loaders/LDrawLoader.js create mode 100644 examples/jsm/loaders/LoaderSupport.js create mode 100644 examples/jsm/loaders/MD2Loader.js create mode 100644 examples/jsm/loaders/MMDLoader.js create mode 100644 examples/jsm/loaders/MTLLoader.js create mode 100644 examples/jsm/loaders/NRRDLoader.js create mode 100644 examples/jsm/loaders/NodeMaterialLoader.js create mode 100644 examples/jsm/loaders/OBJLoader2.js create mode 100644 examples/jsm/loaders/PCDLoader.js create mode 100644 examples/jsm/loaders/PDBLoader.js create mode 100644 examples/jsm/loaders/PLYLoader.js create mode 100644 examples/jsm/loaders/PRWMLoader.js create mode 100644 examples/jsm/loaders/PVRLoader.js create mode 100644 examples/jsm/loaders/PlayCanvasLoader.js create mode 100644 examples/jsm/loaders/RGBELoader.js create mode 100644 examples/jsm/loaders/STLLoader.js create mode 100644 examples/jsm/loaders/SVGLoader.js create mode 100644 examples/jsm/loaders/TDSLoader.js create mode 100644 examples/jsm/loaders/TGALoader.js create mode 100644 examples/jsm/loaders/TTFLoader.js create mode 100644 examples/jsm/loaders/VRMLLoader.js create mode 100644 examples/jsm/loaders/VRMLoader.js create mode 100644 examples/jsm/loaders/VTKLoader.js create mode 100644 examples/jsm/loaders/XLoader.js create mode 100644 examples/jsm/loaders/deprecated/LegacyGLTFLoader.js create mode 100644 examples/jsm/loaders/deprecated/LegacyJSONLoader.js diff --git a/examples/jsm/Volume.js b/examples/jsm/Volume.js new file mode 100644 index 00000000000000..ae0b04c935c5ea --- /dev/null +++ b/examples/jsm/Volume.js @@ -0,0 +1,456 @@ +/** + * This class had been written to handle the output of the NRRD loader. + * It contains a volume of data and informations about it. + * For now it only handles 3 dimensional data. + * See the webgl_loader_nrrd.html example and the loaderNRRD.js file to see how to use this class. + * @class + * @author Valentin Demeusy / https://github.com/stity + * @param {number} xLength Width of the volume + * @param {number} yLength Length of the volume + * @param {number} zLength Depth of the volume + * @param {string} type The type of data (uint8, uint16, ...) + * @param {ArrayBuffer} arrayBuffer The buffer with volume data + */ +import { + Matrix3, + Matrix4, + Vector3, +} from "../../build/three.module.js"; +import { VolumeSlice } from './VolumeSlice'; + +var Volume = function ( xLength, yLength, zLength, type, arrayBuffer ) { + + if ( arguments.length > 0 ) { + + /** + * @member {number} xLength Width of the volume in the IJK coordinate system + */ + this.xLength = Number( xLength ) || 1; + /** + * @member {number} yLength Height of the volume in the IJK coordinate system + */ + this.yLength = Number( yLength ) || 1; + /** + * @member {number} zLength Depth of the volume in the IJK coordinate system + */ + this.zLength = Number( zLength ) || 1; + + /** + * @member {TypedArray} data Data of the volume + */ + + switch ( type ) { + + case 'Uint8' : + case 'uint8' : + case 'uchar' : + case 'unsigned char' : + case 'uint8_t' : + this.data = new Uint8Array( arrayBuffer ); + break; + case 'Int8' : + case 'int8' : + case 'signed char' : + case 'int8_t' : + this.data = new Int8Array( arrayBuffer ); + break; + case 'Int16' : + case 'int16' : + case 'short' : + case 'short int' : + case 'signed short' : + case 'signed short int' : + case 'int16_t' : + this.data = new Int16Array( arrayBuffer ); + break; + case 'Uint16' : + case 'uint16' : + case 'ushort' : + case 'unsigned short' : + case 'unsigned short int' : + case 'uint16_t' : + this.data = new Uint16Array( arrayBuffer ); + break; + case 'Int32' : + case 'int32' : + case 'int' : + case 'signed int' : + case 'int32_t' : + this.data = new Int32Array( arrayBuffer ); + break; + case 'Uint32' : + case 'uint32' : + case 'uint' : + case 'unsigned int' : + case 'uint32_t' : + this.data = new Uint32Array( arrayBuffer ); + break; + case 'longlong' : + case 'long long' : + case 'long long int' : + case 'signed long long' : + case 'signed long long int' : + case 'int64' : + case 'int64_t' : + case 'ulonglong' : + case 'unsigned long long' : + case 'unsigned long long int' : + case 'uint64' : + case 'uint64_t' : + throw 'Error in THREE.Volume constructor : this type is not supported in JavaScript'; + break; + case 'Float32' : + case 'float32' : + case 'float' : + this.data = new Float32Array( arrayBuffer ); + break; + case 'Float64' : + case 'float64' : + case 'double' : + this.data = new Float64Array( arrayBuffer ); + break; + default : + this.data = new Uint8Array( arrayBuffer ); + + } + + if ( this.data.length !== this.xLength * this.yLength * this.zLength ) { + + throw 'Error in THREE.Volume constructor, lengths are not matching arrayBuffer size'; + + } + + } + + /** + * @member {Array} spacing Spacing to apply to the volume from IJK to RAS coordinate system + */ + this.spacing = [ 1, 1, 1 ]; + /** + * @member {Array} offset Offset of the volume in the RAS coordinate system + */ + this.offset = [ 0, 0, 0 ]; + /** + * @member {Matrix3} matrix The IJK to RAS matrix + */ + this.matrix = new Matrix3(); + this.matrix.identity(); + /** + * @member {Matrix3} inverseMatrix The RAS to IJK matrix + */ + /** + * @member {number} lowerThreshold The voxels with values under this threshold won't appear in the slices. + * If changed, geometryNeedsUpdate is automatically set to true on all the slices associated to this volume + */ + var lowerThreshold = - Infinity; + Object.defineProperty( this, 'lowerThreshold', { + get: function () { + + return lowerThreshold; + + }, + set: function ( value ) { + + lowerThreshold = value; + this.sliceList.forEach( function ( slice ) { + + slice.geometryNeedsUpdate = true; + + } ); + + } + } ); + /** + * @member {number} upperThreshold The voxels with values over this threshold won't appear in the slices. + * If changed, geometryNeedsUpdate is automatically set to true on all the slices associated to this volume + */ + var upperThreshold = Infinity; + Object.defineProperty( this, 'upperThreshold', { + get: function () { + + return upperThreshold; + + }, + set: function ( value ) { + + upperThreshold = value; + this.sliceList.forEach( function ( slice ) { + + slice.geometryNeedsUpdate = true; + + } ); + + } + } ); + + + /** + * @member {Array} sliceList The list of all the slices associated to this volume + */ + this.sliceList = []; + + + /** + * @member {Array} RASDimensions This array holds the dimensions of the volume in the RAS space + */ + +}; + +Volume.prototype = { + + constructor: Volume, + + /** + * @member {Function} getData Shortcut for data[access(i,j,k)] + * @memberof Volume + * @param {number} i First coordinate + * @param {number} j Second coordinate + * @param {number} k Third coordinate + * @returns {number} value in the data array + */ + getData: function ( i, j, k ) { + + return this.data[ k * this.xLength * this.yLength + j * this.xLength + i ]; + + }, + + /** + * @member {Function} access compute the index in the data array corresponding to the given coordinates in IJK system + * @memberof Volume + * @param {number} i First coordinate + * @param {number} j Second coordinate + * @param {number} k Third coordinate + * @returns {number} index + */ + access: function ( i, j, k ) { + + return k * this.xLength * this.yLength + j * this.xLength + i; + + }, + + /** + * @member {Function} reverseAccess Retrieve the IJK coordinates of the voxel corresponding of the given index in the data + * @memberof Volume + * @param {number} index index of the voxel + * @returns {Array} [x,y,z] + */ + reverseAccess: function ( index ) { + + var z = Math.floor( index / ( this.yLength * this.xLength ) ); + var y = Math.floor( ( index - z * this.yLength * this.xLength ) / this.xLength ); + var x = index - z * this.yLength * this.xLength - y * this.xLength; + return [ x, y, z ]; + + }, + + /** + * @member {Function} map Apply a function to all the voxels, be careful, the value will be replaced + * @memberof Volume + * @param {Function} functionToMap A function to apply to every voxel, will be called with the following parameters : + * value of the voxel + * index of the voxel + * the data (TypedArray) + * @param {Object} context You can specify a context in which call the function, default if this Volume + * @returns {Volume} this + */ + map: function ( functionToMap, context ) { + + var length = this.data.length; + context = context || this; + + for ( var i = 0; i < length; i ++ ) { + + this.data[ i ] = functionToMap.call( context, this.data[ i ], i, this.data ); + + } + + return this; + + }, + + /** + * @member {Function} extractPerpendicularPlane Compute the orientation of the slice and returns all the information relative to the geometry such as sliceAccess, the plane matrix (orientation and position in RAS coordinate) and the dimensions of the plane in both coordinate system. + * @memberof Volume + * @param {string} axis the normal axis to the slice 'x' 'y' or 'z' + * @param {number} index the index of the slice + * @returns {Object} an object containing all the usefull information on the geometry of the slice + */ + extractPerpendicularPlane: function ( axis, RASIndex ) { + + var iLength, + jLength, + sliceAccess, + planeMatrix = ( new Matrix4() ).identity(), + volume = this, + planeWidth, + planeHeight, + firstSpacing, + secondSpacing, + positionOffset, + IJKIndex; + + var axisInIJK = new Vector3(), + firstDirection = new Vector3(), + secondDirection = new Vector3(); + + var dimensions = new Vector3( this.xLength, this.yLength, this.zLength ); + + + switch ( axis ) { + + case 'x' : + axisInIJK.set( 1, 0, 0 ); + firstDirection.set( 0, 0, - 1 ); + secondDirection.set( 0, - 1, 0 ); + firstSpacing = this.spacing[ 2 ]; + secondSpacing = this.spacing[ 1 ]; + IJKIndex = new Vector3( RASIndex, 0, 0 ); + + planeMatrix.multiply( ( new Matrix4() ).makeRotationY( Math.PI / 2 ) ); + positionOffset = ( volume.RASDimensions[ 0 ] - 1 ) / 2; + planeMatrix.setPosition( new Vector3( RASIndex - positionOffset, 0, 0 ) ); + break; + case 'y' : + axisInIJK.set( 0, 1, 0 ); + firstDirection.set( 1, 0, 0 ); + secondDirection.set( 0, 0, 1 ); + firstSpacing = this.spacing[ 0 ]; + secondSpacing = this.spacing[ 2 ]; + IJKIndex = new Vector3( 0, RASIndex, 0 ); + + planeMatrix.multiply( ( new Matrix4() ).makeRotationX( - Math.PI / 2 ) ); + positionOffset = ( volume.RASDimensions[ 1 ] - 1 ) / 2; + planeMatrix.setPosition( new Vector3( 0, RASIndex - positionOffset, 0 ) ); + break; + case 'z' : + default : + axisInIJK.set( 0, 0, 1 ); + firstDirection.set( 1, 0, 0 ); + secondDirection.set( 0, - 1, 0 ); + firstSpacing = this.spacing[ 0 ]; + secondSpacing = this.spacing[ 1 ]; + IJKIndex = new Vector3( 0, 0, RASIndex ); + + positionOffset = ( volume.RASDimensions[ 2 ] - 1 ) / 2; + planeMatrix.setPosition( new Vector3( 0, 0, RASIndex - positionOffset ) ); + break; + + } + + firstDirection.applyMatrix4( volume.inverseMatrix ).normalize(); + firstDirection.argVar = 'i'; + secondDirection.applyMatrix4( volume.inverseMatrix ).normalize(); + secondDirection.argVar = 'j'; + axisInIJK.applyMatrix4( volume.inverseMatrix ).normalize(); + iLength = Math.floor( Math.abs( firstDirection.dot( dimensions ) ) ); + jLength = Math.floor( Math.abs( secondDirection.dot( dimensions ) ) ); + planeWidth = Math.abs( iLength * firstSpacing ); + planeHeight = Math.abs( jLength * secondSpacing ); + + IJKIndex = Math.abs( Math.round( IJKIndex.applyMatrix4( volume.inverseMatrix ).dot( axisInIJK ) ) ); + var base = [ new Vector3( 1, 0, 0 ), new Vector3( 0, 1, 0 ), new Vector3( 0, 0, 1 ) ]; + var iDirection = [ firstDirection, secondDirection, axisInIJK ].find( function ( x ) { + + return Math.abs( x.dot( base[ 0 ] ) ) > 0.9; + + } ); + var jDirection = [ firstDirection, secondDirection, axisInIJK ].find( function ( x ) { + + return Math.abs( x.dot( base[ 1 ] ) ) > 0.9; + + } ); + var kDirection = [ firstDirection, secondDirection, axisInIJK ].find( function ( x ) { + + return Math.abs( x.dot( base[ 2 ] ) ) > 0.9; + + } ); + var argumentsWithInversion = [ 'volume.xLength-1-', 'volume.yLength-1-', 'volume.zLength-1-' ]; + var argArray = [ iDirection, jDirection, kDirection ].map( function ( direction, n ) { + + return ( direction.dot( base[ n ] ) > 0 ? '' : argumentsWithInversion[ n ] ) + ( direction === axisInIJK ? 'IJKIndex' : direction.argVar ); + + } ); + var argString = argArray.join( ',' ); + sliceAccess = eval( '(function sliceAccess (i,j) {return volume.access( ' + argString + ');})' ); + + + return { + iLength: iLength, + jLength: jLength, + sliceAccess: sliceAccess, + matrix: planeMatrix, + planeWidth: planeWidth, + planeHeight: planeHeight + }; + + }, + + /** + * @member {Function} extractSlice Returns a slice corresponding to the given axis and index + * The coordinate are given in the Right Anterior Superior coordinate format + * @memberof Volume + * @param {string} axis the normal axis to the slice 'x' 'y' or 'z' + * @param {number} index the index of the slice + * @returns {VolumeSlice} the extracted slice + */ + extractSlice: function ( axis, index ) { + + var slice = new VolumeSlice( this, index, axis ); + this.sliceList.push( slice ); + return slice; + + }, + + /** + * @member {Function} repaintAllSlices Call repaint on all the slices extracted from this volume + * @see VolumeSlice.repaint + * @memberof Volume + * @returns {Volume} this + */ + repaintAllSlices: function () { + + this.sliceList.forEach( function ( slice ) { + + slice.repaint(); + + } ); + + return this; + + }, + + /** + * @member {Function} computeMinMax Compute the minimum and the maximum of the data in the volume + * @memberof Volume + * @returns {Array} [min,max] + */ + computeMinMax: function () { + + var min = Infinity; + var max = - Infinity; + + // buffer the length + var datasize = this.data.length; + + var i = 0; + for ( i = 0; i < datasize; i ++ ) { + + if ( ! isNaN( this.data[ i ] ) ) { + + var value = this.data[ i ]; + min = Math.min( min, value ); + max = Math.max( max, value ); + + } + + } + this.min = min; + this.max = max; + + return [ min, max ]; + + } + +}; + +export { Volume }; diff --git a/examples/jsm/VolumeSlice.js b/examples/jsm/VolumeSlice.js new file mode 100644 index 00000000000000..afe429003b9087 --- /dev/null +++ b/examples/jsm/VolumeSlice.js @@ -0,0 +1,228 @@ +/** + * This class has been made to hold a slice of a volume data + * @class + * @author Valentin Demeusy / https://github.com/stity + * @param {Volume} volume The associated volume + * @param {number} [index=0] The index of the slice + * @param {string} [axis='z'] For now only 'x', 'y' or 'z' but later it will change to a normal vector + * @see Volume + */ +import { + ClampToEdgeWrapping, + DoubleSide, + LinearFilter, + Mesh, + MeshBasicMaterial, + PlaneBufferGeometry, + Texture, +} from "../../build/three.module.js"; + +var VolumeSlice = function ( volume, index, axis ) { + + var slice = this; + /** + * @member {Volume} volume The associated volume + */ + this.volume = volume; + /** + * @member {Number} index The index of the slice, if changed, will automatically call updateGeometry at the next repaint + */ + index = index || 0; + Object.defineProperty( this, 'index', { + get: function () { + + return index; + + }, + set: function ( value ) { + + index = value; + slice.geometryNeedsUpdate = true; + return index; + + } + } ); + /** + * @member {String} axis The normal axis + */ + this.axis = axis || 'z'; + + /** + * @member {HTMLCanvasElement} canvas The final canvas used for the texture + */ + /** + * @member {CanvasRenderingContext2D} ctx Context of the canvas + */ + this.canvas = document.createElement( 'canvas' ); + /** + * @member {HTMLCanvasElement} canvasBuffer The intermediary canvas used to paint the data + */ + /** + * @member {CanvasRenderingContext2D} ctxBuffer Context of the canvas buffer + */ + this.canvasBuffer = document.createElement( 'canvas' ); + this.updateGeometry(); + + + var canvasMap = new Texture( this.canvas ); + canvasMap.minFilter = LinearFilter; + canvasMap.wrapS = canvasMap.wrapT = ClampToEdgeWrapping; + var material = new MeshBasicMaterial( { map: canvasMap, side: DoubleSide, transparent: true } ); + /** + * @member {Mesh} mesh The mesh ready to get used in the scene + */ + this.mesh = new Mesh( this.geometry, material ); + /** + * @member {Boolean} geometryNeedsUpdate If set to true, updateGeometry will be triggered at the next repaint + */ + this.geometryNeedsUpdate = true; + this.repaint(); + + /** + * @member {Number} iLength Width of slice in the original coordinate system, corresponds to the width of the buffer canvas + */ + + /** + * @member {Number} jLength Height of slice in the original coordinate system, corresponds to the height of the buffer canvas + */ + + /** + * @member {Function} sliceAccess Function that allow the slice to access right data + * @see Volume.extractPerpendicularPlane + * @param {Number} i The first coordinate + * @param {Number} j The second coordinate + * @returns {Number} the index corresponding to the voxel in volume.data of the given position in the slice + */ + + +}; + +VolumeSlice.prototype = { + + constructor: VolumeSlice, + + /** + * @member {Function} repaint Refresh the texture and the geometry if geometryNeedsUpdate is set to true + * @memberof VolumeSlice + */ + repaint: function () { + + if ( this.geometryNeedsUpdate ) { + + this.updateGeometry(); + + } + + var iLength = this.iLength, + jLength = this.jLength, + sliceAccess = this.sliceAccess, + volume = this.volume, + canvas = this.canvasBuffer, + ctx = this.ctxBuffer; + + + // get the imageData and pixel array from the canvas + var imgData = ctx.getImageData( 0, 0, iLength, jLength ); + var data = imgData.data; + var volumeData = volume.data; + var upperThreshold = volume.upperThreshold; + var lowerThreshold = volume.lowerThreshold; + var windowLow = volume.windowLow; + var windowHigh = volume.windowHigh; + + // manipulate some pixel elements + var pixelCount = 0; + + if ( volume.dataType === 'label' ) { + + //this part is currently useless but will be used when colortables will be handled + for ( var j = 0; j < jLength; j ++ ) { + + for ( var i = 0; i < iLength; i ++ ) { + + var label = volumeData[ sliceAccess( i, j ) ]; + label = label >= this.colorMap.length ? ( label % this.colorMap.length ) + 1 : label; + var color = this.colorMap[ label ]; + data[ 4 * pixelCount ] = ( color >> 24 ) & 0xff; + data[ 4 * pixelCount + 1 ] = ( color >> 16 ) & 0xff; + data[ 4 * pixelCount + 2 ] = ( color >> 8 ) & 0xff; + data[ 4 * pixelCount + 3 ] = color & 0xff; + pixelCount ++; + + } + + } + + } else { + + for ( var j = 0; j < jLength; j ++ ) { + + for ( var i = 0; i < iLength; i ++ ) { + + var value = volumeData[ sliceAccess( i, j ) ]; + var alpha = 0xff; + //apply threshold + alpha = upperThreshold >= value ? ( lowerThreshold <= value ? alpha : 0 ) : 0; + //apply window level + value = Math.floor( 255 * ( value - windowLow ) / ( windowHigh - windowLow ) ); + value = value > 255 ? 255 : ( value < 0 ? 0 : value | 0 ); + + data[ 4 * pixelCount ] = value; + data[ 4 * pixelCount + 1 ] = value; + data[ 4 * pixelCount + 2 ] = value; + data[ 4 * pixelCount + 3 ] = alpha; + pixelCount ++; + + } + + } + + } + ctx.putImageData( imgData, 0, 0 ); + this.ctx.drawImage( canvas, 0, 0, iLength, jLength, 0, 0, this.canvas.width, this.canvas.height ); + + + this.mesh.material.map.needsUpdate = true; + + }, + + /** + * @member {Function} Refresh the geometry according to axis and index + * @see Volume.extractPerpendicularPlane + * @memberof VolumeSlice + */ + updateGeometry: function () { + + var extracted = this.volume.extractPerpendicularPlane( this.axis, this.index ); + this.sliceAccess = extracted.sliceAccess; + this.jLength = extracted.jLength; + this.iLength = extracted.iLength; + this.matrix = extracted.matrix; + + this.canvas.width = extracted.planeWidth; + this.canvas.height = extracted.planeHeight; + this.canvasBuffer.width = this.iLength; + this.canvasBuffer.height = this.jLength; + this.ctx = this.canvas.getContext( '2d' ); + this.ctxBuffer = this.canvasBuffer.getContext( '2d' ); + + if ( this.geometry ) this.geometry.dispose(); // dispose existing geometry + + this.geometry = new PlaneBufferGeometry( extracted.planeWidth, extracted.planeHeight ); + + if ( this.mesh ) { + + this.mesh.geometry = this.geometry; + //reset mesh matrix + this.mesh.matrix.identity(); + this.mesh.applyMatrix( this.matrix ); + + } + + this.geometryNeedsUpdate = false; + + } + +}; + +export { VolumeSlice }; diff --git a/examples/jsm/curves/NURBSCurve.js b/examples/jsm/curves/NURBSCurve.js new file mode 100644 index 00000000000000..ad11efff7aad4b --- /dev/null +++ b/examples/jsm/curves/NURBSCurve.js @@ -0,0 +1,78 @@ +/** + * @author renej + * NURBS curve object + * + * Derives from Curve, overriding getPoint and getTangent. + * + * Implementation is based on (x, y [, z=0 [, w=1]]) control points with w=weight. + * + **/ + + +/************************************************************** + * NURBS curve + **************************************************************/ + +import { + Curve, + Vector3, + Vector4, +} from '../../../build/three.module.js'; +import { NURBSUtils } from './NURBSUtils'; + +var NURBSCurve = function ( degree, knots /* array of reals */, controlPoints /* array of Vector(2|3|4) */, startKnot /* index in knots */, endKnot /* index in knots */ ) { + + Curve.call( this ); + + this.degree = degree; + this.knots = knots; + this.controlPoints = []; + // Used by periodic NURBS to remove hidden spans + this.startKnot = startKnot || 0; + this.endKnot = endKnot || ( this.knots.length - 1 ); + for ( var i = 0; i < controlPoints.length; ++ i ) { + + // ensure Vector4 for control points + var point = controlPoints[ i ]; + this.controlPoints[ i ] = new Vector4( point.x, point.y, point.z, point.w ); + + } + +}; + + +NURBSCurve.prototype = Object.create( Curve.prototype ); +NURBSCurve.prototype.constructor = NURBSCurve; + + +NURBSCurve.prototype.getPoint = function ( t ) { + + var u = this.knots[ this.startKnot ] + t * ( this.knots[ this.endKnot ] - this.knots[ this.startKnot ] ); // linear mapping t->u + + // following results in (wx, wy, wz, w) homogeneous point + var hpoint = NURBSUtils.calcBSplinePoint( this.degree, this.knots, this.controlPoints, u ); + + if ( hpoint.w != 1.0 ) { + + // project to 3D space: (wx, wy, wz, w) -> (x, y, z, 1) + hpoint.divideScalar( hpoint.w ); + + } + + return new Vector3( hpoint.x, hpoint.y, hpoint.z ); + +}; + + +NURBSCurve.prototype.getTangent = function ( t ) { + + var u = this.knots[ 0 ] + t * ( this.knots[ this.knots.length - 1 ] - this.knots[ 0 ] ); + var ders = NURBSUtils.calcNURBSDerivatives( this.degree, this.knots, this.controlPoints, u, 1 ); + var tangent = ders[ 1 ].clone(); + tangent.normalize(); + + return tangent; + +}; + +export { NURBSCurve }; diff --git a/examples/jsm/curves/NURBSSurface.js b/examples/jsm/curves/NURBSSurface.js new file mode 100644 index 00000000000000..125f31f0240b57 --- /dev/null +++ b/examples/jsm/curves/NURBSSurface.js @@ -0,0 +1,60 @@ +/** + * @author renej + * NURBS surface object + * + * Implementation is based on (x, y [, z=0 [, w=1]]) control points with w=weight. + * + **/ + + +import { NURBSUtils } from "./NURBSUtils"; +import { + Vector4, +} from "../../../build/three.module.js"; + +/************************************************************** + * NURBS surface + **************************************************************/ + +var NURBSSurface = function ( degree1, degree2, knots1, knots2 /* arrays of reals */, controlPoints /* array^2 of Vector(2|3|4) */ ) { + + this.degree1 = degree1; + this.degree2 = degree2; + this.knots1 = knots1; + this.knots2 = knots2; + this.controlPoints = []; + + var len1 = knots1.length - degree1 - 1; + var len2 = knots2.length - degree2 - 1; + + // ensure Vector4 for control points + for ( var i = 0; i < len1; ++ i ) { + + this.controlPoints[ i ] = []; + for ( var j = 0; j < len2; ++ j ) { + + var point = controlPoints[ i ][ j ]; + this.controlPoints[ i ][ j ] = new Vector4( point.x, point.y, point.z, point.w ); + + } + + } + +}; + + +NURBSSurface.prototype = { + + constructor: NURBSSurface, + + getPoint: function ( t1, t2, target ) { + + var u = this.knots1[ 0 ] + t1 * ( this.knots1[ this.knots1.length - 1 ] - this.knots1[ 0 ] ); // linear mapping t1->u + var v = this.knots2[ 0 ] + t2 * ( this.knots2[ this.knots2.length - 1 ] - this.knots2[ 0 ] ); // linear mapping t2->u + + NURBSUtils.calcSurfacePoint( this.degree1, this.degree2, this.knots1, this.knots2, this.controlPoints, u, v, target ); + + } +}; + +export { NURBSSurface }; diff --git a/examples/jsm/curves/NURBSUtils.js b/examples/jsm/curves/NURBSUtils.js new file mode 100644 index 00000000000000..8e8dd35760a23a --- /dev/null +++ b/examples/jsm/curves/NURBSUtils.js @@ -0,0 +1,476 @@ +/** + * @author renej + * NURBS utils + * + * See NURBSCurve and NURBSSurface. + * + **/ + + +/************************************************************** + * NURBS Utils + **************************************************************/ + +import { + Vector3, + Vector4, +} from "../../../build/three.module.js"; + +var NURBSUtils = { + + /* + Finds knot vector span. + + p : degree + u : parametric value + U : knot vector + + returns the span + */ + findSpan: function ( p, u, U ) { + + var n = U.length - p - 1; + + if ( u >= U[ n ] ) { + + return n - 1; + + } + + if ( u <= U[ p ] ) { + + return p; + + } + + var low = p; + var high = n; + var mid = Math.floor( ( low + high ) / 2 ); + + while ( u < U[ mid ] || u >= U[ mid + 1 ] ) { + + if ( u < U[ mid ] ) { + + high = mid; + + } else { + + low = mid; + + } + + mid = Math.floor( ( low + high ) / 2 ); + + } + + return mid; + + }, + + + /* + Calculate basis functions. See The NURBS Book, page 70, algorithm A2.2 + + span : span in which u lies + u : parametric point + p : degree + U : knot vector + + returns array[p+1] with basis functions values. + */ + calcBasisFunctions: function ( span, u, p, U ) { + + var N = []; + var left = []; + var right = []; + N[ 0 ] = 1.0; + + for ( var j = 1; j <= p; ++ j ) { + + left[ j ] = u - U[ span + 1 - j ]; + right[ j ] = U[ span + j ] - u; + + var saved = 0.0; + + for ( var r = 0; r < j; ++ r ) { + + var rv = right[ r + 1 ]; + var lv = left[ j - r ]; + var temp = N[ r ] / ( rv + lv ); + N[ r ] = saved + rv * temp; + saved = lv * temp; + + } + + N[ j ] = saved; + + } + + return N; + + }, + + + /* + Calculate B-Spline curve points. See The NURBS Book, page 82, algorithm A3.1. + + p : degree of B-Spline + U : knot vector + P : control points (x, y, z, w) + u : parametric point + + returns point for given u + */ + calcBSplinePoint: function ( p, U, P, u ) { + + var span = this.findSpan( p, u, U ); + var N = this.calcBasisFunctions( span, u, p, U ); + var C = new Vector4( 0, 0, 0, 0 ); + + for ( var j = 0; j <= p; ++ j ) { + + var point = P[ span - p + j ]; + var Nj = N[ j ]; + var wNj = point.w * Nj; + C.x += point.x * wNj; + C.y += point.y * wNj; + C.z += point.z * wNj; + C.w += point.w * Nj; + + } + + return C; + + }, + + + /* + Calculate basis functions derivatives. See The NURBS Book, page 72, algorithm A2.3. + + span : span in which u lies + u : parametric point + p : degree + n : number of derivatives to calculate + U : knot vector + + returns array[n+1][p+1] with basis functions derivatives + */ + calcBasisFunctionDerivatives: function ( span, u, p, n, U ) { + + var zeroArr = []; + for ( var i = 0; i <= p; ++ i ) + zeroArr[ i ] = 0.0; + + var ders = []; + for ( var i = 0; i <= n; ++ i ) + ders[ i ] = zeroArr.slice( 0 ); + + var ndu = []; + for ( var i = 0; i <= p; ++ i ) + ndu[ i ] = zeroArr.slice( 0 ); + + ndu[ 0 ][ 0 ] = 1.0; + + var left = zeroArr.slice( 0 ); + var right = zeroArr.slice( 0 ); + + for ( var j = 1; j <= p; ++ j ) { + + left[ j ] = u - U[ span + 1 - j ]; + right[ j ] = U[ span + j ] - u; + + var saved = 0.0; + + for ( var r = 0; r < j; ++ r ) { + + var rv = right[ r + 1 ]; + var lv = left[ j - r ]; + ndu[ j ][ r ] = rv + lv; + + var temp = ndu[ r ][ j - 1 ] / ndu[ j ][ r ]; + ndu[ r ][ j ] = saved + rv * temp; + saved = lv * temp; + + } + + ndu[ j ][ j ] = saved; + + } + + for ( var j = 0; j <= p; ++ j ) { + + ders[ 0 ][ j ] = ndu[ j ][ p ]; + + } + + for ( var r = 0; r <= p; ++ r ) { + + var s1 = 0; + var s2 = 1; + + var a = []; + for ( var i = 0; i <= p; ++ i ) { + + a[ i ] = zeroArr.slice( 0 ); + + } + a[ 0 ][ 0 ] = 1.0; + + for ( var k = 1; k <= n; ++ k ) { + + var d = 0.0; + var rk = r - k; + var pk = p - k; + + if ( r >= k ) { + + a[ s2 ][ 0 ] = a[ s1 ][ 0 ] / ndu[ pk + 1 ][ rk ]; + d = a[ s2 ][ 0 ] * ndu[ rk ][ pk ]; + + } + + var j1 = ( rk >= - 1 ) ? 1 : - rk; + var j2 = ( r - 1 <= pk ) ? k - 1 : p - r; + + for ( var j = j1; j <= j2; ++ j ) { + + a[ s2 ][ j ] = ( a[ s1 ][ j ] - a[ s1 ][ j - 1 ] ) / ndu[ pk + 1 ][ rk + j ]; + d += a[ s2 ][ j ] * ndu[ rk + j ][ pk ]; + + } + + if ( r <= pk ) { + + a[ s2 ][ k ] = - a[ s1 ][ k - 1 ] / ndu[ pk + 1 ][ r ]; + d += a[ s2 ][ k ] * ndu[ r ][ pk ]; + + } + + ders[ k ][ r ] = d; + + var j = s1; + s1 = s2; + s2 = j; + + } + + } + + var r = p; + + for ( var k = 1; k <= n; ++ k ) { + + for ( var j = 0; j <= p; ++ j ) { + + ders[ k ][ j ] *= r; + + } + r *= p - k; + + } + + return ders; + + }, + + + /* + Calculate derivatives of a B-Spline. See The NURBS Book, page 93, algorithm A3.2. + + p : degree + U : knot vector + P : control points + u : Parametric points + nd : number of derivatives + + returns array[d+1] with derivatives + */ + calcBSplineDerivatives: function ( p, U, P, u, nd ) { + + var du = nd < p ? nd : p; + var CK = []; + var span = this.findSpan( p, u, U ); + var nders = this.calcBasisFunctionDerivatives( span, u, p, du, U ); + var Pw = []; + + for ( var i = 0; i < P.length; ++ i ) { + + var point = P[ i ].clone(); + var w = point.w; + + point.x *= w; + point.y *= w; + point.z *= w; + + Pw[ i ] = point; + + } + for ( var k = 0; k <= du; ++ k ) { + + var point = Pw[ span - p ].clone().multiplyScalar( nders[ k ][ 0 ] ); + + for ( var j = 1; j <= p; ++ j ) { + + point.add( Pw[ span - p + j ].clone().multiplyScalar( nders[ k ][ j ] ) ); + + } + + CK[ k ] = point; + + } + + for ( var k = du + 1; k <= nd + 1; ++ k ) { + + CK[ k ] = new Vector4( 0, 0, 0 ); + + } + + return CK; + + }, + + + /* + Calculate "K over I" + + returns k!/(i!(k-i)!) + */ + calcKoverI: function ( k, i ) { + + var nom = 1; + + for ( var j = 2; j <= k; ++ j ) { + + nom *= j; + + } + + var denom = 1; + + for ( var j = 2; j <= i; ++ j ) { + + denom *= j; + + } + + for ( var j = 2; j <= k - i; ++ j ) { + + denom *= j; + + } + + return nom / denom; + + }, + + + /* + Calculate derivatives (0-nd) of rational curve. See The NURBS Book, page 127, algorithm A4.2. + + Pders : result of function calcBSplineDerivatives + + returns array with derivatives for rational curve. + */ + calcRationalCurveDerivatives: function ( Pders ) { + + var nd = Pders.length; + var Aders = []; + var wders = []; + + for ( var i = 0; i < nd; ++ i ) { + + var point = Pders[ i ]; + Aders[ i ] = new Vector3( point.x, point.y, point.z ); + wders[ i ] = point.w; + + } + + var CK = []; + + for ( var k = 0; k < nd; ++ k ) { + + var v = Aders[ k ].clone(); + + for ( var i = 1; i <= k; ++ i ) { + + v.sub( CK[ k - i ].clone().multiplyScalar( this.calcKoverI( k, i ) * wders[ i ] ) ); + + } + + CK[ k ] = v.divideScalar( wders[ 0 ] ); + + } + + return CK; + + }, + + + /* + Calculate NURBS curve derivatives. See The NURBS Book, page 127, algorithm A4.2. + + p : degree + U : knot vector + P : control points in homogeneous space + u : parametric points + nd : number of derivatives + + returns array with derivatives. + */ + calcNURBSDerivatives: function ( p, U, P, u, nd ) { + + var Pders = this.calcBSplineDerivatives( p, U, P, u, nd ); + return this.calcRationalCurveDerivatives( Pders ); + + }, + + + /* + Calculate rational B-Spline surface point. See The NURBS Book, page 134, algorithm A4.3. + + p1, p2 : degrees of B-Spline surface + U1, U2 : knot vectors + P : control points (x, y, z, w) + u, v : parametric values + + returns point for given (u, v) + */ + calcSurfacePoint: function ( p, q, U, V, P, u, v, target ) { + + var uspan = this.findSpan( p, u, U ); + var vspan = this.findSpan( q, v, V ); + var Nu = this.calcBasisFunctions( uspan, u, p, U ); + var Nv = this.calcBasisFunctions( vspan, v, q, V ); + var temp = []; + + for ( var l = 0; l <= q; ++ l ) { + + temp[ l ] = new Vector4( 0, 0, 0, 0 ); + for ( var k = 0; k <= p; ++ k ) { + + var point = P[ uspan - p + k ][ vspan - q + l ].clone(); + var w = point.w; + point.x *= w; + point.y *= w; + point.z *= w; + temp[ l ].add( point.multiplyScalar( Nu[ k ] ) ); + + } + + } + + var Sw = new Vector4( 0, 0, 0, 0 ); + for ( var l = 0; l <= q; ++ l ) { + + Sw.add( temp[ l ].multiplyScalar( Nv[ l ] ) ); + + } + + Sw.divideScalar( Sw.w ); + target.set( Sw.x, Sw.y, Sw.z ); + + } + +}; + +export { NURBSUtils }; diff --git a/examples/jsm/loaders/3MFLoader.js b/examples/jsm/loaders/3MFLoader.js new file mode 100644 index 00000000000000..8489c33b036e48 --- /dev/null +++ b/examples/jsm/loaders/3MFLoader.js @@ -0,0 +1,628 @@ +/** + * @author technohippy / https://github.com/technohippy + */ + +import { + BufferAttribute, + BufferGeometry, + DefaultLoadingManager, + FileLoader, + Group, + LoaderUtils, + Matrix4, + MeshPhongMaterial, + VertexColors, +} from "../../../build/three.module.js"; + +var ThreeMFLoader = function ( manager ) { + + this.manager = ( manager !== undefined ) ? manager : DefaultLoadingManager; + this.availableExtensions = []; + +}; + +ThreeMFLoader.prototype = { + + constructor: ThreeMFLoader, + + load: function ( url, onLoad, onProgress, onError ) { + + var scope = this; + var loader = new FileLoader( scope.manager ); + loader.setPath( scope.path ); + loader.setResponseType( 'arraybuffer' ); + loader.load( url, function ( buffer ) { + + onLoad( scope.parse( buffer ) ); + + }, onProgress, onError ); + + }, + + setPath: function ( value ) { + + this.path = value; + return this; + + }, + + parse: function ( data ) { + + var scope = this; + + function loadDocument( data ) { + + var zip = null; + var file = null; + + var relsName; + var modelPartNames = []; + var printTicketPartNames = []; + var texturesPartNames = []; + var otherPartNames = []; + + var rels; + var modelParts = {}; + var printTicketParts = {}; + var texturesParts = {}; + var otherParts = {}; + + try { + + zip = new JSZip( data ); // eslint-disable-line no-undef + + } catch ( e ) { + + if ( e instanceof ReferenceError ) { + + console.error( 'THREE.ThreeMFLoader: jszip missing and file is compressed.' ); + return null; + + } + + } + + for ( file in zip.files ) { + + if ( file.match( /\.rels$/ ) ) { + + relsName = file; + + } else if ( file.match( /^3D\/.*\.model$/ ) ) { + + modelPartNames.push( file ); + + } else if ( file.match( /^3D\/Metadata\/.*\.xml$/ ) ) { + + printTicketPartNames.push( file ); + + } else if ( file.match( /^3D\/Textures\/.*/ ) ) { + + texturesPartNames.push( file ); + + } else if ( file.match( /^3D\/Other\/.*/ ) ) { + + otherPartNames.push( file ); + + } + + } + + var relsView = new Uint8Array( zip.file( relsName ).asArrayBuffer() ); + var relsFileText = LoaderUtils.decodeText( relsView ); + rels = parseRelsXml( relsFileText ); + + for ( var i = 0; i < modelPartNames.length; i ++ ) { + + var modelPart = modelPartNames[ i ]; + var view = new Uint8Array( zip.file( modelPart ).asArrayBuffer() ); + + var fileText = LoaderUtils.decodeText( view ); + var xmlData = new DOMParser().parseFromString( fileText, 'application/xml' ); + + if ( xmlData.documentElement.nodeName.toLowerCase() !== 'model' ) { + + console.error( 'THREE.ThreeMFLoader: Error loading 3MF - no 3MF document found: ', modelPart ); + + } + + var modelNode = xmlData.querySelector( 'model' ); + var extensions = {}; + + for ( var i = 0; i < modelNode.attributes.length; i ++ ) { + + var attr = modelNode.attributes[ i ]; + if ( attr.name.match( /^xmlns:(.+)$/ ) ) { + + extensions[ attr.value ] = RegExp.$1; + + } + + } + + var modelData = parseModelNode( modelNode ); + modelData[ 'xml' ] = modelNode; + + if ( 0 < Object.keys( extensions ).length ) { + + modelData[ 'extensions' ] = extensions; + + } + + modelParts[ modelPart ] = modelData; + + } + + for ( var i = 0; i < texturesPartNames.length; i ++ ) { + + var texturesPartName = texturesPartNames[ i ]; + texturesParts[ texturesPartName ] = zip.file( texturesPartName ).asBinary(); + + } + + return { + rels: rels, + model: modelParts, + printTicket: printTicketParts, + texture: texturesParts, + other: otherParts + }; + + } + + function parseRelsXml( relsFileText ) { + + var relsXmlData = new DOMParser().parseFromString( relsFileText, 'application/xml' ); + var relsNode = relsXmlData.querySelector( 'Relationship' ); + var target = relsNode.getAttribute( 'Target' ); + var id = relsNode.getAttribute( 'Id' ); + var type = relsNode.getAttribute( 'Type' ); + + return { + target: target, + id: id, + type: type + }; + + } + + function parseMetadataNodes( metadataNodes ) { + + var metadataData = {}; + + for ( var i = 0; i < metadataNodes.length; i ++ ) { + + var metadataNode = metadataNodes[ i ]; + var name = metadataNode.getAttribute( 'name' ); + var validNames = [ + 'Title', + 'Designer', + 'Description', + 'Copyright', + 'LicenseTerms', + 'Rating', + 'CreationDate', + 'ModificationDate' + ]; + + if ( 0 <= validNames.indexOf( name ) ) { + + metadataData[ name ] = metadataNode.textContent; + + } + + } + + return metadataData; + + } + + function parseBasematerialsNode( basematerialsNode ) { + } + + function parseMeshNode( meshNode, extensions ) { + + var meshData = {}; + + var vertices = []; + var vertexNodes = meshNode.querySelectorAll( 'vertices vertex' ); + + for ( var i = 0; i < vertexNodes.length; i ++ ) { + + var vertexNode = vertexNodes[ i ]; + var x = vertexNode.getAttribute( 'x' ); + var y = vertexNode.getAttribute( 'y' ); + var z = vertexNode.getAttribute( 'z' ); + + vertices.push( parseFloat( x ), parseFloat( y ), parseFloat( z ) ); + + } + + meshData[ 'vertices' ] = new Float32Array( vertices.length ); + + for ( var i = 0; i < vertices.length; i ++ ) { + + meshData[ 'vertices' ][ i ] = vertices[ i ]; + + } + + var triangleProperties = []; + var triangles = []; + var triangleNodes = meshNode.querySelectorAll( 'triangles triangle' ); + + for ( var i = 0; i < triangleNodes.length; i ++ ) { + + var triangleNode = triangleNodes[ i ]; + var v1 = triangleNode.getAttribute( 'v1' ); + var v2 = triangleNode.getAttribute( 'v2' ); + var v3 = triangleNode.getAttribute( 'v3' ); + var p1 = triangleNode.getAttribute( 'p1' ); + var p2 = triangleNode.getAttribute( 'p2' ); + var p3 = triangleNode.getAttribute( 'p3' ); + var pid = triangleNode.getAttribute( 'pid' ); + + triangles.push( parseInt( v1, 10 ), parseInt( v2, 10 ), parseInt( v3, 10 ) ); + + var triangleProperty = {}; + + if ( p1 ) { + + triangleProperty[ 'p1' ] = parseInt( p1, 10 ); + + } + + if ( p2 ) { + + triangleProperty[ 'p2' ] = parseInt( p2, 10 ); + + } + + if ( p3 ) { + + triangleProperty[ 'p3' ] = parseInt( p3, 10 ); + + } + + if ( pid ) { + + triangleProperty[ 'pid' ] = pid; + + } + + if ( 0 < Object.keys( triangleProperty ).length ) { + + triangleProperties.push( triangleProperty ); + + } + + } + + meshData[ 'triangleProperties' ] = triangleProperties; + meshData[ 'triangles' ] = new Uint32Array( triangles.length ); + + for ( var i = 0; i < triangles.length; i ++ ) { + + meshData[ 'triangles' ][ i ] = triangles[ i ]; + + } + + return meshData; + + } + + function parseComponentsNode( componentsNode ) { + + } + + function parseObjectNode( objectNode ) { + + var objectData = { + type: objectNode.getAttribute( 'type' ) + }; + + var id = objectNode.getAttribute( 'id' ); + + if ( id ) { + + objectData[ 'id' ] = id; + + } + + var pid = objectNode.getAttribute( 'pid' ); + + if ( pid ) { + + objectData[ 'pid' ] = pid; + + } + + var pindex = objectNode.getAttribute( 'pindex' ); + + if ( pindex ) { + + objectData[ 'pindex' ] = pindex; + + } + + var thumbnail = objectNode.getAttribute( 'thumbnail' ); + + if ( thumbnail ) { + + objectData[ 'thumbnail' ] = thumbnail; + + } + + var partnumber = objectNode.getAttribute( 'partnumber' ); + + if ( partnumber ) { + + objectData[ 'partnumber' ] = partnumber; + + } + + var name = objectNode.getAttribute( 'name' ); + + if ( name ) { + + objectData[ 'name' ] = name; + + } + + var meshNode = objectNode.querySelector( 'mesh' ); + + if ( meshNode ) { + + objectData[ 'mesh' ] = parseMeshNode( meshNode ); + + } + + var componentsNode = objectNode.querySelector( 'components' ); + + if ( componentsNode ) { + + objectData[ 'components' ] = parseComponentsNode( componentsNode ); + + } + + return objectData; + + } + + function parseResourcesNode( resourcesNode ) { + + var resourcesData = {}; + var basematerialsNode = resourcesNode.querySelector( 'basematerials' ); + + if ( basematerialsNode ) { + + resourcesData[ 'basematerial' ] = parseBasematerialsNode( basematerialsNode ); + + } + + resourcesData[ 'object' ] = {}; + var objectNodes = resourcesNode.querySelectorAll( 'object' ); + + for ( var i = 0; i < objectNodes.length; i ++ ) { + + var objectNode = objectNodes[ i ]; + var objectData = parseObjectNode( objectNode ); + resourcesData[ 'object' ][ objectData[ 'id' ] ] = objectData; + + } + + return resourcesData; + + } + + function parseBuildNode( buildNode ) { + + var buildData = []; + var itemNodes = buildNode.querySelectorAll( 'item' ); + + for ( var i = 0; i < itemNodes.length; i ++ ) { + + var itemNode = itemNodes[ i ]; + var buildItem = { + objectid: itemNode.getAttribute( 'objectid' ) + }; + var transform = itemNode.getAttribute( 'transform' ); + + if ( transform ) { + + var t = []; + transform.split( ' ' ).forEach( function ( s ) { + + t.push( parseFloat( s ) ); + + } ); + var mat4 = new Matrix4(); + buildItem[ 'transform' ] = mat4.set( + t[ 0 ], t[ 3 ], t[ 6 ], t[ 9 ], + t[ 1 ], t[ 4 ], t[ 7 ], t[ 10 ], + t[ 2 ], t[ 5 ], t[ 8 ], t[ 11 ], + 0.0, 0.0, 0.0, 1.0 + ); + + } + + buildData.push( buildItem ); + + } + + return buildData; + + } + + function parseModelNode( modelNode ) { + + var modelData = { unit: modelNode.getAttribute( 'unit' ) || 'millimeter' }; + var metadataNodes = modelNode.querySelectorAll( 'metadata' ); + + if ( metadataNodes ) { + + modelData[ 'metadata' ] = parseMetadataNodes( metadataNodes ); + + } + + var resourcesNode = modelNode.querySelector( 'resources' ); + + if ( resourcesNode ) { + + modelData[ 'resources' ] = parseResourcesNode( resourcesNode ); + + } + + var buildNode = modelNode.querySelector( 'build' ); + + if ( buildNode ) { + + modelData[ 'build' ] = parseBuildNode( buildNode ); + + } + + return modelData; + + } + + function buildMesh( meshData, data3mf ) { + + var geometry = new BufferGeometry(); + geometry.setIndex( new BufferAttribute( meshData[ 'triangles' ], 1 ) ); + geometry.addAttribute( 'position', new BufferAttribute( meshData[ 'vertices' ], 3 ) ); + + if ( meshData[ 'colors' ] ) { + + geometry.addAttribute( 'color', new BufferAttribute( meshData[ 'colors' ], 3 ) ); + + } + + geometry.computeBoundingSphere(); + + var materialOpts = { + flatShading: true + }; + + if ( meshData[ 'colors' ] && 0 < meshData[ 'colors' ].length ) { + + materialOpts[ 'vertexColors' ] = VertexColors; + + } else { + + materialOpts[ 'color' ] = 0xaaaaff; + + } + + var material = new MeshPhongMaterial( materialOpts ); + return new Mesh( geometry, material ); + + } + + function applyExtensions( extensions, meshData, modelXml, data3mf ) { + + if ( ! extensions ) { + + return; + + } + + var availableExtensions = []; + var keys = Object.keys( extensions ); + + for ( var i = 0; i < keys.length; i ++ ) { + + var ns = keys[ i ]; + + for ( var j = 0; j < scope.availableExtensions.length; j ++ ) { + + var extension = scope.availableExtensions[ j ]; + + if ( extension.ns === ns ) { + + availableExtensions.push( extension ); + + } + + } + + } + + for ( var i = 0; i < availableExtensions.length; i ++ ) { + + var extension = availableExtensions[ i ]; + extension.apply( modelXml, extensions[ extension[ 'ns' ] ], meshData ); + + } + + } + + function buildMeshes( data3mf ) { + + var modelsData = data3mf.model; + var meshes = {}; + var modelsKeys = Object.keys( modelsData ); + + for ( var i = 0; i < modelsKeys.length; i ++ ) { + + var modelsKey = modelsKeys[ i ]; + var modelData = modelsData[ modelsKey ]; + var modelXml = modelData[ 'xml' ]; + var extensions = modelData[ 'extensions' ]; + + var objectIds = Object.keys( modelData[ 'resources' ][ 'object' ] ); + + for ( var j = 0; j < objectIds.length; j ++ ) { + + var objectId = objectIds[ j ]; + var objectData = modelData[ 'resources' ][ 'object' ][ objectId ]; + var meshData = objectData[ 'mesh' ]; + applyExtensions( extensions, meshData, modelXml, data3mf ); + meshes[ objectId ] = buildMesh( meshData, data3mf ); + + } + + } + + return meshes; + + } + + function build( meshes, refs, data3mf ) { + + var group = new Group(); + var buildData = data3mf.model[ refs[ 'target' ].substring( 1 ) ][ 'build' ]; + + for ( var i = 0; i < buildData.length; i ++ ) { + + var buildItem = buildData[ i ]; + var mesh = meshes[ buildItem[ 'objectid' ] ]; + + if ( buildItem[ 'transform' ] ) { + + mesh.geometry.applyMatrix( buildItem[ 'transform' ] ); + + } + + group.add( mesh ); + + } + + return group; + + } + + var data3mf = loadDocument( data ); + var meshes = buildMeshes( data3mf ); + + return build( meshes, data3mf[ 'rels' ], data3mf ); + + }, + + addExtension: function ( extension ) { + + this.availableExtensions.push( extension ); + + } + +}; + +export { ThreeMFLoader }; diff --git a/examples/jsm/loaders/AMFLoader.js b/examples/jsm/loaders/AMFLoader.js new file mode 100644 index 00000000000000..00a2a3954a0b83 --- /dev/null +++ b/examples/jsm/loaders/AMFLoader.js @@ -0,0 +1,509 @@ +/* + * @author tamarintech / https://tamarintech.com + * + * Description: Early release of an AMF Loader following the pattern of the + * example loaders in the three.js project. + * + * More information about the AMF format: http://amf.wikispaces.com + * + * Usage: + * var loader = new AMFLoader(); + * loader.load('/path/to/project.amf', function(objecttree) { + * scene.add(objecttree); + * }); + * + * Materials now supported, material colors supported + * Zip support, requires jszip + * No constellation support (yet)! + * + */ + +import { + BufferGeometry, + Color, + DefaultLoadingManager, + FileLoader, + Float32BufferAttribute, + Group, + LoaderUtils, + Mesh, + MeshPhongMaterial, +} from "../../../build/three.module.js"; + +var AMFLoader = function ( manager ) { + + this.manager = ( manager !== undefined ) ? manager : DefaultLoadingManager; + +}; + +AMFLoader.prototype = { + + constructor: AMFLoader, + + load: function ( url, onLoad, onProgress, onError ) { + + var scope = this; + + var loader = new FileLoader( scope.manager ); + loader.setPath( scope.path ); + loader.setResponseType( 'arraybuffer' ); + loader.load( url, function ( text ) { + + onLoad( scope.parse( text ) ); + + }, onProgress, onError ); + + }, + + setPath: function ( value ) { + + this.path = value; + return this; + + }, + + parse: function ( data ) { + + function loadDocument( data ) { + + var view = new DataView( data ); + var magic = String.fromCharCode( view.getUint8( 0 ), view.getUint8( 1 ) ); + + if ( magic === 'PK' ) { + + var zip = null; + var file = null; + + console.log( 'THREE.AMFLoader: Loading Zip' ); + + try { + + zip = new JSZip( data ); // eslint-disable-line no-undef + + } catch ( e ) { + + if ( e instanceof ReferenceError ) { + + console.log( 'THREE.AMFLoader: jszip missing and file is compressed.' ); + return null; + + } + + } + + for ( file in zip.files ) { + + if ( file.toLowerCase().substr( - 4 ) === '.amf' ) { + + break; + + } + + } + + console.log( 'THREE.AMFLoader: Trying to load file asset: ' + file ); + view = new DataView( zip.file( file ).asArrayBuffer() ); + + } + + var fileText = LoaderUtils.decodeText( view ); + var xmlData = new DOMParser().parseFromString( fileText, 'application/xml' ); + + if ( xmlData.documentElement.nodeName.toLowerCase() !== 'amf' ) { + + console.log( 'THREE.AMFLoader: Error loading AMF - no AMF document found.' ); + return null; + + } + + return xmlData; + + } + + function loadDocumentScale( node ) { + + var scale = 1.0; + var unit = 'millimeter'; + + if ( node.documentElement.attributes.unit !== undefined ) { + + unit = node.documentElement.attributes.unit.value.toLowerCase(); + + } + + var scaleUnits = { + millimeter: 1.0, + inch: 25.4, + feet: 304.8, + meter: 1000.0, + micron: 0.001 + }; + + if ( scaleUnits[ unit ] !== undefined ) { + + scale = scaleUnits[ unit ]; + + } + + console.log( 'THREE.AMFLoader: Unit scale: ' + scale ); + return scale; + + } + + function loadMaterials( node ) { + + var matName = 'AMF Material'; + var matId = node.attributes.id.textContent; + var color = { r: 1.0, g: 1.0, b: 1.0, a: 1.0 }; + + var loadedMaterial = null; + + for ( var i = 0; i < node.childNodes.length; i ++ ) { + + var matChildEl = node.childNodes[ i ]; + + if ( matChildEl.nodeName === 'metadata' && matChildEl.attributes.type !== undefined ) { + + if ( matChildEl.attributes.type.value === 'name' ) { + + matName = matChildEl.textContent; + + } + + } else if ( matChildEl.nodeName === 'color' ) { + + color = loadColor( matChildEl ); + + } + + } + + loadedMaterial = new MeshPhongMaterial( { + flatShading: true, + color: new Color( color.r, color.g, color.b ), + name: matName + } ); + + if ( color.a !== 1.0 ) { + + loadedMaterial.transparent = true; + loadedMaterial.opacity = color.a; + + } + + return { id: matId, material: loadedMaterial }; + + } + + function loadColor( node ) { + + var color = { r: 1.0, g: 1.0, b: 1.0, a: 1.0 }; + + for ( var i = 0; i < node.childNodes.length; i ++ ) { + + var matColor = node.childNodes[ i ]; + + if ( matColor.nodeName === 'r' ) { + + color.r = matColor.textContent; + + } else if ( matColor.nodeName === 'g' ) { + + color.g = matColor.textContent; + + } else if ( matColor.nodeName === 'b' ) { + + color.b = matColor.textContent; + + } else if ( matColor.nodeName === 'a' ) { + + color.a = matColor.textContent; + + } + + } + + return color; + + } + + function loadMeshVolume( node ) { + + var volume = { name: '', triangles: [], materialid: null }; + + var currVolumeNode = node.firstElementChild; + + if ( node.attributes.materialid !== undefined ) { + + volume.materialId = node.attributes.materialid.nodeValue; + + } + + while ( currVolumeNode ) { + + if ( currVolumeNode.nodeName === 'metadata' ) { + + if ( currVolumeNode.attributes.type !== undefined ) { + + if ( currVolumeNode.attributes.type.value === 'name' ) { + + volume.name = currVolumeNode.textContent; + + } + + } + + } else if ( currVolumeNode.nodeName === 'triangle' ) { + + var v1 = currVolumeNode.getElementsByTagName( 'v1' )[ 0 ].textContent; + var v2 = currVolumeNode.getElementsByTagName( 'v2' )[ 0 ].textContent; + var v3 = currVolumeNode.getElementsByTagName( 'v3' )[ 0 ].textContent; + + volume.triangles.push( v1, v2, v3 ); + + } + + currVolumeNode = currVolumeNode.nextElementSibling; + + } + + return volume; + + } + + function loadMeshVertices( node ) { + + var vertArray = []; + var normalArray = []; + var currVerticesNode = node.firstElementChild; + + while ( currVerticesNode ) { + + if ( currVerticesNode.nodeName === 'vertex' ) { + + var vNode = currVerticesNode.firstElementChild; + + while ( vNode ) { + + if ( vNode.nodeName === 'coordinates' ) { + + var x = vNode.getElementsByTagName( 'x' )[ 0 ].textContent; + var y = vNode.getElementsByTagName( 'y' )[ 0 ].textContent; + var z = vNode.getElementsByTagName( 'z' )[ 0 ].textContent; + + vertArray.push( x, y, z ); + + } else if ( vNode.nodeName === 'normal' ) { + + var nx = vNode.getElementsByTagName( 'nx' )[ 0 ].textContent; + var ny = vNode.getElementsByTagName( 'ny' )[ 0 ].textContent; + var nz = vNode.getElementsByTagName( 'nz' )[ 0 ].textContent; + + normalArray.push( nx, ny, nz ); + + } + + vNode = vNode.nextElementSibling; + + } + + } + currVerticesNode = currVerticesNode.nextElementSibling; + + } + + return { 'vertices': vertArray, 'normals': normalArray }; + + } + + function loadObject( node ) { + + var objId = node.attributes.id.textContent; + var loadedObject = { name: 'amfobject', meshes: [] }; + var currColor = null; + var currObjNode = node.firstElementChild; + + while ( currObjNode ) { + + if ( currObjNode.nodeName === 'metadata' ) { + + if ( currObjNode.attributes.type !== undefined ) { + + if ( currObjNode.attributes.type.value === 'name' ) { + + loadedObject.name = currObjNode.textContent; + + } + + } + + } else if ( currObjNode.nodeName === 'color' ) { + + currColor = loadColor( currObjNode ); + + } else if ( currObjNode.nodeName === 'mesh' ) { + + var currMeshNode = currObjNode.firstElementChild; + var mesh = { vertices: [], normals: [], volumes: [], color: currColor }; + + while ( currMeshNode ) { + + if ( currMeshNode.nodeName === 'vertices' ) { + + var loadedVertices = loadMeshVertices( currMeshNode ); + + mesh.normals = mesh.normals.concat( loadedVertices.normals ); + mesh.vertices = mesh.vertices.concat( loadedVertices.vertices ); + + } else if ( currMeshNode.nodeName === 'volume' ) { + + mesh.volumes.push( loadMeshVolume( currMeshNode ) ); + + } + + currMeshNode = currMeshNode.nextElementSibling; + + } + + loadedObject.meshes.push( mesh ); + + } + + currObjNode = currObjNode.nextElementSibling; + + } + + return { 'id': objId, 'obj': loadedObject }; + + } + + var xmlData = loadDocument( data ); + var amfName = ''; + var amfAuthor = ''; + var amfScale = loadDocumentScale( xmlData ); + var amfMaterials = {}; + var amfObjects = {}; + var childNodes = xmlData.documentElement.childNodes; + + var i, j; + + for ( i = 0; i < childNodes.length; i ++ ) { + + var child = childNodes[ i ]; + + if ( child.nodeName === 'metadata' ) { + + if ( child.attributes.type !== undefined ) { + + if ( child.attributes.type.value === 'name' ) { + + amfName = child.textContent; + + } else if ( child.attributes.type.value === 'author' ) { + + amfAuthor = child.textContent; + + } + + } + + } else if ( child.nodeName === 'material' ) { + + var loadedMaterial = loadMaterials( child ); + + amfMaterials[ loadedMaterial.id ] = loadedMaterial.material; + + } else if ( child.nodeName === 'object' ) { + + var loadedObject = loadObject( child ); + + amfObjects[ loadedObject.id ] = loadedObject.obj; + + } + + } + + var sceneObject = new Group(); + var defaultMaterial = new MeshPhongMaterial( { color: 0xaaaaff, flatShading: true } ); + + sceneObject.name = amfName; + sceneObject.userData.author = amfAuthor; + sceneObject.userData.loader = 'AMF'; + + for ( var id in amfObjects ) { + + var part = amfObjects[ id ]; + var meshes = part.meshes; + var newObject = new Group(); + newObject.name = part.name || ''; + + for ( i = 0; i < meshes.length; i ++ ) { + + var objDefaultMaterial = defaultMaterial; + var mesh = meshes[ i ]; + var vertices = new Float32BufferAttribute( mesh.vertices, 3 ); + var normals = null; + + if ( mesh.normals.length ) { + + normals = new Float32BufferAttribute( mesh.normals, 3 ); + + } + + if ( mesh.color ) { + + var color = mesh.color; + + objDefaultMaterial = defaultMaterial.clone(); + objDefaultMaterial.color = new THREE.Color( color.r, color.g, color.b ); + + if ( color.a !== 1.0 ) { + + objDefaultMaterial.transparent = true; + objDefaultMaterial.opacity = color.a; + + } + + } + + var volumes = mesh.volumes; + + for ( j = 0; j < volumes.length; j ++ ) { + + var volume = volumes[ j ]; + var newGeometry = new BufferGeometry(); + var material = objDefaultMaterial; + + newGeometry.setIndex( volume.triangles ); + newGeometry.addAttribute( 'position', vertices.clone() ); + + if ( normals ) { + + newGeometry.addAttribute( 'normal', normals.clone() ); + + } + + if ( amfMaterials[ volume.materialId ] !== undefined ) { + + material = amfMaterials[ volume.materialId ]; + + } + + newGeometry.scale( amfScale, amfScale, amfScale ); + newObject.add( new Mesh( newGeometry, material.clone() ) ); + + } + + } + + sceneObject.add( newObject ); + + } + + return sceneObject; + + } + +}; + +export { AMFLoader }; diff --git a/examples/jsm/loaders/AWDLoader.js b/examples/jsm/loaders/AWDLoader.js new file mode 100644 index 00000000000000..d8a1655f012046 --- /dev/null +++ b/examples/jsm/loaders/AWDLoader.js @@ -0,0 +1,1251 @@ +/** + * Author: Pierre Lepers + * Date: 09/12/2013 17:21 + */ + +import { + Bone, + BufferAttribute, + BufferGeometry, + DefaultLoadingManager, + FileLoader, + ImageLoader, + Matrix4, + Mesh, + MeshPhongMaterial, + Texture, + Object3D, +} from "../../../build/three.module.js"; + +var UNCOMPRESSED = 0, + DEFLATE = 1, + LZMA = 2, + + AWD_FIELD_INT8 = 1, + AWD_FIELD_INT16 = 2, + AWD_FIELD_INT32 = 3, + AWD_FIELD_UINT8 = 4, + AWD_FIELD_UINT16 = 5, + AWD_FIELD_UINT32 = 6, + AWD_FIELD_FLOAT32 = 7, + AWD_FIELD_FLOAT64 = 8, + AWD_FIELD_BOOL = 21, + AWD_FIELD_COLOR = 22, + AWD_FIELD_BADDR = 23, + AWD_FIELD_STRING = 31, + AWD_FIELD_BYTEARRAY = 32, + AWD_FIELD_VECTOR2x1 = 41, + AWD_FIELD_VECTOR3x1 = 42, + AWD_FIELD_VECTOR4x1 = 43, + AWD_FIELD_MTX3x2 = 44, + AWD_FIELD_MTX3x3 = 45, + AWD_FIELD_MTX4x3 = 46, + AWD_FIELD_MTX4x4 = 47, + + BOOL = 21, + COLOR = 22, + BADDR = 23, + + INT8 = 1, + INT16 = 2, + INT32 = 3, + UINT8 = 4, + UINT16 = 5, + UINT32 = 6, + FLOAT32 = 7, + FLOAT64 = 8; + +var littleEndian = true; + +function Block() { + + this.id = 0; + this.data = null; + +} + +function AWDProperties() {} + +AWDProperties.prototype = { + set: function ( key, value ) { + + this[ key ] = value; + + }, + + get: function ( key, fallback ) { + + if ( this.hasOwnProperty( key ) ) { + + return this[ key ]; + + } else { + + return fallback; + + } + + } +}; + +var AWDLoader = function ( manager ) { + + this.manager = ( manager !== undefined ) ? manager : DefaultLoadingManager; + + this.trunk = new Object3D(); + + this.materialFactory = undefined; + + this._url = ''; + this._baseDir = ''; + + this._data = undefined; + this._ptr = 0; + + this._version = []; + this._streaming = false; + this._optimized_for_accuracy = false; + this._compression = 0; + this._bodylen = 0xFFFFFFFF; + + this._blocks = [ new Block() ]; + + this._accuracyMatrix = false; + this._accuracyGeo = false; + this._accuracyProps = false; + +}; + +AWDLoader.prototype = { + + constructor: AWDLoader, + + load: function ( url, onLoad, onProgress, onError ) { + + var scope = this; + + this._url = url; + this._baseDir = url.substr( 0, url.lastIndexOf( '/' ) + 1 ); + + var loader = new FileLoader( this.manager ); + loader.setPath( this.path ); + loader.setResponseType( 'arraybuffer' ); + loader.load( url, function ( text ) { + + onLoad( scope.parse( text ) ); + + }, onProgress, onError ); + + }, + + setPath: function ( value ) { + + this.path = value; + return this; + + }, + + parse: function ( data ) { + + var blen = data.byteLength; + + this._ptr = 0; + this._data = new DataView( data ); + + this._parseHeader( ); + + if ( this._compression != 0 ) { + + console.error( 'compressed AWD not supported' ); + + } + + if ( ! this._streaming && this._bodylen != data.byteLength - this._ptr ) { + + console.error( 'AWDLoader: body len does not match file length', this._bodylen, blen - this._ptr ); + + } + + while ( this._ptr < blen ) { + + this.parseNextBlock(); + + } + + return this.trunk; + + }, + + parseNextBlock: function () { + + var assetData, + ns, type, len, block, + blockId = this.readU32(), + ns = this.readU8(), + type = this.readU8(), + flags = this.readU8(), + len = this.readU32(); + + + switch ( type ) { + + case 1: + assetData = this.parseMeshData( len ); + break; + + case 22: + assetData = this.parseContainer( len ); + break; + + case 23: + assetData = this.parseMeshInstance( len ); + break; + + case 81: + assetData = this.parseMaterial( len ); + break; + + case 82: + assetData = this.parseTexture( len ); + break; + + case 101: + assetData = this.parseSkeleton( len ); + break; + + // case 111: + // assetData = this.parseMeshPoseAnimation(len, true); + // break; + + case 112: + assetData = this.parseMeshPoseAnimation( len, false ); + break; + + case 113: + assetData = this.parseVertexAnimationSet( len ); + break; + + case 102: + assetData = this.parseSkeletonPose( len ); + break; + + case 103: + assetData = this.parseSkeletonAnimation( len ); + break; + + case 122: + assetData = this.parseAnimatorSet( len ); + break; + + // case 121: + // assetData = parseUVAnimation(len); + // break; + + default: + //debug('Ignoring block!',type, len); + this._ptr += len; + break; + + } + + + // Store block reference for later use + this._blocks[ blockId ] = block = new Block(); + block.data = assetData; + block.id = blockId; + + + }, + + _parseHeader: function () { + + var version = this._version, + awdmagic = ( this.readU8() << 16 ) | ( this.readU8() << 8 ) | this.readU8(); + + if ( awdmagic != 4282180 ) + throw new Error( "AWDLoader - bad magic" ); + + version[ 0 ] = this.readU8(); + version[ 1 ] = this.readU8(); + + var flags = this.readU16(); + + this._streaming = ( flags & 0x1 ) == 0x1; + + if ( ( version[ 0 ] === 2 ) && ( version[ 1 ] === 1 ) ) { + + this._accuracyMatrix = ( flags & 0x2 ) === 0x2; + this._accuracyGeo = ( flags & 0x4 ) === 0x4; + this._accuracyProps = ( flags & 0x8 ) === 0x8; + + } + + this._geoNrType = this._accuracyGeo ? FLOAT64 : FLOAT32; + this._matrixNrType = this._accuracyMatrix ? FLOAT64 : FLOAT32; + this._propsNrType = this._accuracyProps ? FLOAT64 : FLOAT32; + + this._optimized_for_accuracy = ( flags & 0x2 ) === 0x2; + + this._compression = this.readU8(); + this._bodylen = this.readU32(); + + }, + + parseContainer: function ( len ) { + + var parent, + ctr = new Object3D(), + par_id = this.readU32(), + mtx = this.parseMatrix4(); + + ctr.name = this.readUTF(); + ctr.applyMatrix( mtx ); + + parent = this._blocks[ par_id ].data || this.trunk; + parent.add( ctr ); + + this.parseProperties( { + 1: this._matrixNrType, + 2: this._matrixNrType, + 3: this._matrixNrType, + 4: UINT8 + } ); + + ctr.extra = this.parseUserAttributes(); + + return ctr; + + }, + + parseMeshInstance: function ( len ) { + + var name, + mesh, geometries, meshLen, meshes, + par_id, data_id, + mtx, + materials, mat, mat_id, + num_materials, + parent, + i; + + par_id = this.readU32(); + mtx = this.parseMatrix4(); + name = this.readUTF(); + data_id = this.readU32(); + num_materials = this.readU16(); + + geometries = this.getBlock( data_id ); + + materials = []; + + for ( i = 0; i < num_materials; i ++ ) { + + mat_id = this.readU32(); + mat = this.getBlock( mat_id ); + materials.push( mat ); + + } + + meshLen = geometries.length; + meshes = []; + + // TODO : BufferGeometry don't support "geometryGroups" for now. + // so we create sub meshes for each groups + if ( meshLen > 1 ) { + + mesh = new Object3D(); + for ( i = 0; i < meshLen; i ++ ) { + + var sm = new Mesh( geometries[ i ] ); + meshes.push( sm ); + mesh.add( sm ); + + } + + } else { + + mesh = new Mesh( geometries[ 0 ] ); + meshes.push( mesh ); + + } + + mesh.applyMatrix( mtx ); + mesh.name = name; + + + parent = this.getBlock( par_id ) || this.trunk; + parent.add( mesh ); + + + var matLen = materials.length; + var maxLen = Math.max( meshLen, matLen ); + for ( i = 0; i < maxLen; i ++ ) + meshes[ i % meshLen ].material = materials[ i % matLen ]; + + + // Ignore for now + this.parseProperties( null ); + mesh.extra = this.parseUserAttributes(); + + return mesh; + + }, + + parseMaterial: function ( len ) { + + var name, + type, + props, + mat, + attributes, + finalize, + num_methods, + methods_parsed; + + name = this.readUTF(); + type = this.readU8(); + num_methods = this.readU8(); + + //log( "AWDLoader parseMaterial ",name ) + + // Read material numerical properties + // (1=color, 2=bitmap url, 11=alpha_blending, 12=alpha_threshold, 13=repeat) + props = this.parseProperties( { + 1: AWD_FIELD_INT32, + 2: AWD_FIELD_BADDR, + 11: AWD_FIELD_BOOL, + 12: AWD_FIELD_FLOAT32, + 13: AWD_FIELD_BOOL + } ); + + methods_parsed = 0; + + while ( methods_parsed < num_methods ) { + + var method_type = this.readU16(); + this.parseProperties( null ); + this.parseUserAttributes(); + + } + + attributes = this.parseUserAttributes(); + + if ( this.materialFactory !== undefined ) { + + mat = this.materialFactory( name ); + if ( mat ) return mat; + + } + + mat = new MeshPhongMaterial(); + + if ( type === 1 ) { + + // Color material + mat.color.setHex( props.get( 1, 0xcccccc ) ); + + } else if ( type === 2 ) { + + // Bitmap material + var tex_addr = props.get( 2, 0 ); + mat.map = this.getBlock( tex_addr ); + + } + + mat.extra = attributes; + mat.alphaThreshold = props.get( 12, 0.0 ); + mat.repeat = props.get( 13, false ); + + + return mat; + + }, + + parseTexture: function ( len ) { + + var name = this.readUTF(), + type = this.readU8(), + asset, + data_len; + + // External + if ( type === 0 ) { + + data_len = this.readU32(); + var url = this.readUTFBytes( data_len ); + console.log( url ); + + asset = this.loadTexture( url ); + + } else { + // embed texture not supported + } + // Ignore for now + this.parseProperties( null ); + + this.parseUserAttributes(); + return asset; + + }, + + loadTexture: function ( url ) { + + var tex = new Texture(); + + var loader = new ImageLoader( this.manager ); + + loader.load( this._baseDir + url, function ( image ) { + + tex.image = image; + tex.needsUpdate = true; + + } ); + + return tex; + + }, + + parseSkeleton: function ( len ) { + + // Array + var name = this.readUTF(), + num_joints = this.readU16(), + skeleton = [], + joints_parsed = 0; + + this.parseProperties( null ); + + while ( joints_parsed < num_joints ) { + + var joint, ibp; + + // Ignore joint id + this.readU16(); + + joint = new Bone(); + joint.parent = this.readU16() - 1; // 0=null in AWD + joint.name = this.readUTF(); + + ibp = this.parseMatrix4(); + joint.skinMatrix = ibp; + + // Ignore joint props/attributes for now + this.parseProperties( null ); + this.parseUserAttributes(); + + skeleton.push( joint ); + joints_parsed ++; + + } + + // Discard attributes for now + this.parseUserAttributes(); + + + return skeleton; + + }, + + parseSkeletonPose: function ( blockID ) { + + var name = this.readUTF(); + + var num_joints = this.readU16(); + this.parseProperties( null ); + + // debug( 'parse Skeleton Pose. joints : ' + num_joints); + + var pose = []; + + var joints_parsed = 0; + + while ( joints_parsed < num_joints ) { + + var joint_pose; + + var has_transform; //:uint; + var mtx_data; + + has_transform = this.readU8(); + + if ( has_transform === 1 ) { + + mtx_data = this.parseMatrix4(); + + } else { + + mtx_data = new Matrix4(); + + } + pose[ joints_parsed ] = mtx_data; + joints_parsed ++; + + } + + // Skip attributes for now + this.parseUserAttributes(); + + return pose; + + }, + + parseSkeletonAnimation: function ( blockID ) { + + var frame_dur; + var pose_addr; + var pose; + + var name = this.readUTF(); + + var clip = []; + + var num_frames = this.readU16(); + this.parseProperties( null ); + + var frames_parsed = 0; + var returnedArray; + + // debug( 'parse Skeleton Animation. frames : ' + num_frames); + + while ( frames_parsed < num_frames ) { + + pose_addr = this.readU32(); + frame_dur = this.readU16(); + + pose = this._blocks[ pose_addr ].data; + // debug( 'pose address ',pose[2].elements[12],pose[2].elements[13],pose[2].elements[14] ); + clip.push( { + pose: pose, + duration: frame_dur + } ); + + frames_parsed ++; + + } + + if ( clip.length === 0 ) { + + // debug("Could not this SkeletonClipNode, because no Frames where set."); + return; + + } + // Ignore attributes for now + this.parseUserAttributes(); + return clip; + + }, + + parseVertexAnimationSet: function ( len ) { + + var poseBlockAdress, + name = this.readUTF(), + num_frames = this.readU16(), + props = this.parseProperties( { 1: UINT16 } ), + frames_parsed = 0, + skeletonFrames = []; + + while ( frames_parsed < num_frames ) { + + poseBlockAdress = this.readU32(); + skeletonFrames.push( this._blocks[ poseBlockAdress ].data ); + frames_parsed ++; + + } + + this.parseUserAttributes(); + + + return skeletonFrames; + + }, + + parseAnimatorSet: function ( len ) { + + var targetMesh; + + var animSetBlockAdress; //:int + + var targetAnimationSet; //:AnimationSetBase; + var outputString = ""; //:String = ""; + var name = this.readUTF(); + var type = this.readU16(); + + var props = this.parseProperties( { 1: BADDR } ); + + animSetBlockAdress = this.readU32(); + var targetMeshLength = this.readU16(); + + var meshAdresses = []; //:Vector. = new Vector.; + + for ( var i = 0; i < targetMeshLength; i ++ ) + meshAdresses.push( this.readU32() ); + + var activeState = this.readU16(); + var autoplay = Boolean( this.readU8() ); + this.parseUserAttributes(); + this.parseUserAttributes(); + + var returnedArray; + var targetMeshes = []; //:Vector. = new Vector.; + + for ( i = 0; i < meshAdresses.length; i ++ ) { + + // returnedArray = getAssetByID(meshAdresses[i], [AssetType.MESH]); + // if (returnedArray[0]) + targetMeshes.push( this._blocks[ meshAdresses[ i ] ].data ); + + } + + targetAnimationSet = this._blocks[ animSetBlockAdress ].data; + var thisAnimator; + + if ( type == 1 ) { + + + thisAnimator = { + animationSet: targetAnimationSet, + skeleton: this._blocks[ props.get( 1, 0 ) ].data + }; + + } else if ( type == 2 ) { + // debug( "vertex Anim???"); + } + + + for ( i = 0; i < targetMeshes.length; i ++ ) { + + targetMeshes[ i ].animator = thisAnimator; + + } + // debug("Parsed a Animator: Name = " + name); + + return thisAnimator; + + }, + + parseMeshData: function ( len ) { + + var name = this.readUTF(), + num_subs = this.readU16(), + geom, + subs_parsed = 0, + buffer, + skinW, skinI, + geometries = []; + + // Ignore for now + this.parseProperties( { 1: this._geoNrType, 2: this._geoNrType } ); + + // Loop through sub meshes + while ( subs_parsed < num_subs ) { + + var sm_len, sm_end, attrib; + + geom = new BufferGeometry(); + geom.name = name; + geometries.push( geom ); + + + sm_len = this.readU32(); + sm_end = this._ptr + sm_len; + + + // Ignore for now + this.parseProperties( { 1: this._geoNrType, 2: this._geoNrType } ); + + // Loop through data streams + while ( this._ptr < sm_end ) { + + var idx = 0, + str_type = this.readU8(), + str_ftype = this.readU8(), + str_len = this.readU32(), + str_end = str_len + this._ptr; + + if ( str_type === 1 ) { + + // VERTICES + + buffer = new Float32Array( ( str_len / 12 ) * 3 ); + attrib = new BufferAttribute( buffer, 3 ); + + geom.addAttribute( 'position', attrib ); + idx = 0; + + while ( this._ptr < str_end ) { + + buffer[ idx ] = - this.readF32(); + buffer[ idx + 1 ] = this.readF32(); + buffer[ idx + 2 ] = this.readF32(); + idx += 3; + + } + + } else if ( str_type === 2 ) { + + // INDICES + + buffer = new Uint16Array( str_len / 2 ); + attrib = new BufferAttribute( buffer, 1 ); + geom.setIndex( attrib ); + + idx = 0; + + while ( this._ptr < str_end ) { + + buffer[ idx + 1 ] = this.readU16(); + buffer[ idx ] = this.readU16(); + buffer[ idx + 2 ] = this.readU16(); + idx += 3; + + } + + } else if ( str_type === 3 ) { + + // UVS + + buffer = new Float32Array( ( str_len / 8 ) * 2 ); + attrib = new BufferAttribute( buffer, 2 ); + + geom.addAttribute( 'uv', attrib ); + idx = 0; + + while ( this._ptr < str_end ) { + + buffer[ idx ] = this.readF32(); + buffer[ idx + 1 ] = 1.0 - this.readF32(); + idx += 2; + + } + + } else if ( str_type === 4 ) { + + // NORMALS + + buffer = new Float32Array( ( str_len / 12 ) * 3 ); + attrib = new BufferAttribute( buffer, 3 ); + geom.addAttribute( 'normal', attrib ); + idx = 0; + + while ( this._ptr < str_end ) { + + buffer[ idx ] = - this.readF32(); + buffer[ idx + 1 ] = this.readF32(); + buffer[ idx + 2 ] = this.readF32(); + idx += 3; + + } + + } else { + + this._ptr = str_end; + + } + + } + + this.parseUserAttributes(); + + geom.computeBoundingSphere(); + subs_parsed ++; + + } + + //geom.computeFaceNormals(); + + this.parseUserAttributes(); + //finalizeAsset(geom, name); + + return geometries; + + }, + + parseMeshPoseAnimation: function ( len, poseOnly ) { + + var num_frames = 1, + num_submeshes, + frames_parsed, + subMeshParsed, + frame_dur, + x, y, z, + + str_len, + str_end, + geom, + subGeom, + idx = 0, + clip = {}, + indices, + verts, + num_Streams, + streamsParsed, + streamtypes = [], + + props, + thisGeo, + name = this.readUTF(), + geoAdress = this.readU32(); + + var mesh = this.getBlock( geoAdress ); + + if ( mesh === null ) { + + console.log( "parseMeshPoseAnimation target mesh not found at:", geoAdress ); + return; + + } + + geom = mesh.geometry; + geom.morphTargets = []; + + if ( ! poseOnly ) + num_frames = this.readU16(); + + num_submeshes = this.readU16(); + num_Streams = this.readU16(); + + // debug("VA num_frames : ", num_frames ); + // debug("VA num_submeshes : ", num_submeshes ); + // debug("VA numstreams : ", num_Streams ); + + streamsParsed = 0; + while ( streamsParsed < num_Streams ) { + + streamtypes.push( this.readU16() ); + streamsParsed ++; + + } + props = this.parseProperties( { 1: BOOL, 2: BOOL } ); + + clip.looping = props.get( 1, true ); + clip.stitchFinalFrame = props.get( 2, false ); + + frames_parsed = 0; + + while ( frames_parsed < num_frames ) { + + frame_dur = this.readU16(); + subMeshParsed = 0; + + while ( subMeshParsed < num_submeshes ) { + + streamsParsed = 0; + str_len = this.readU32(); + str_end = this._ptr + str_len; + + while ( streamsParsed < num_Streams ) { + + if ( streamtypes[ streamsParsed ] === 1 ) { + + //geom.addAttribute( 'morphTarget'+frames_parsed, Float32Array, str_len/12, 3 ); + var buffer = new Float32Array( str_len / 4 ); + geom.morphTargets.push( { + array: buffer + } ); + + //buffer = geom.attributes['morphTarget'+frames_parsed].array + idx = 0; + + while ( this._ptr < str_end ) { + + buffer[ idx ] = this.readF32(); + buffer[ idx + 1 ] = this.readF32(); + buffer[ idx + 2 ] = this.readF32(); + idx += 3; + + } + + + subMeshParsed ++; + + } else + this._ptr = str_end; + streamsParsed ++; + + } + + } + + + frames_parsed ++; + + } + + this.parseUserAttributes(); + + return null; + + }, + + getBlock: function ( id ) { + + return this._blocks[ id ].data; + + }, + + parseMatrix4: function () { + + var mtx = new Matrix4(); + var e = mtx.elements; + + e[ 0 ] = this.readF32(); + e[ 1 ] = this.readF32(); + e[ 2 ] = this.readF32(); + e[ 3 ] = 0.0; + //e[3] = 0.0; + + e[ 4 ] = this.readF32(); + e[ 5 ] = this.readF32(); + e[ 6 ] = this.readF32(); + //e[7] = this.readF32(); + e[ 7 ] = 0.0; + + e[ 8 ] = this.readF32(); + e[ 9 ] = this.readF32(); + e[ 10 ] = this.readF32(); + //e[11] = this.readF32(); + e[ 11 ] = 0.0; + + e[ 12 ] = - this.readF32(); + e[ 13 ] = this.readF32(); + e[ 14 ] = this.readF32(); + //e[15] = this.readF32(); + e[ 15 ] = 1.0; + return mtx; + + }, + + parseProperties: function ( expected ) { + + var list_len = this.readU32(); + var list_end = this._ptr + list_len; + + var props = new AWDProperties(); + + if ( expected ) { + + while ( this._ptr < list_end ) { + + var key = this.readU16(); + var len = this.readU32(); + var type; + + if ( expected.hasOwnProperty( key ) ) { + + type = expected[ key ]; + props.set( key, this.parseAttrValue( type, len ) ); + + } else { + + this._ptr += len; + + } + + } + + } + + return props; + + }, + + parseUserAttributes: function () { + + // skip for now + this._ptr = this.readU32() + this._ptr; + return null; + + }, + + parseAttrValue: function ( type, len ) { + + var elem_len; + var read_func; + + switch ( type ) { + + case AWD_FIELD_INT8: + elem_len = 1; + read_func = this.readI8; + break; + + case AWD_FIELD_INT16: + elem_len = 2; + read_func = this.readI16; + break; + + case AWD_FIELD_INT32: + elem_len = 4; + read_func = this.readI32; + break; + + case AWD_FIELD_BOOL: + case AWD_FIELD_UINT8: + elem_len = 1; + read_func = this.readU8; + break; + + case AWD_FIELD_UINT16: + elem_len = 2; + read_func = this.readU16; + break; + + case AWD_FIELD_UINT32: + case AWD_FIELD_BADDR: + elem_len = 4; + read_func = this.readU32; + break; + + case AWD_FIELD_FLOAT32: + elem_len = 4; + read_func = this.readF32; + break; + + case AWD_FIELD_FLOAT64: + elem_len = 8; + read_func = this.readF64; + break; + + case AWD_FIELD_VECTOR2x1: + case AWD_FIELD_VECTOR3x1: + case AWD_FIELD_VECTOR4x1: + case AWD_FIELD_MTX3x2: + case AWD_FIELD_MTX3x3: + case AWD_FIELD_MTX4x3: + case AWD_FIELD_MTX4x4: + elem_len = 8; + read_func = this.readF64; + break; + + } + + if ( elem_len < len ) { + + var list; + var num_read; + var num_elems; + + list = []; + num_read = 0; + num_elems = len / elem_len; + + while ( num_read < num_elems ) { + + list.push( read_func.call( this ) ); + num_read ++; + + } + + return list; + + } else { + + return read_func.call( this ); + + } + + }, + + readU8: function () { + + return this._data.getUint8( this._ptr ++ ); + + }, + readI8: function () { + + return this._data.getInt8( this._ptr ++ ); + + }, + readU16: function () { + + var a = this._data.getUint16( this._ptr, littleEndian ); + this._ptr += 2; + return a; + + }, + readI16: function () { + + var a = this._data.getInt16( this._ptr, littleEndian ); + this._ptr += 2; + return a; + + }, + readU32: function () { + + var a = this._data.getUint32( this._ptr, littleEndian ); + this._ptr += 4; + return a; + + }, + readI32: function () { + + var a = this._data.getInt32( this._ptr, littleEndian ); + this._ptr += 4; + return a; + + }, + readF32: function () { + + var a = this._data.getFloat32( this._ptr, littleEndian ); + this._ptr += 4; + return a; + + }, + readF64: function () { + + var a = this._data.getFloat64( this._ptr, littleEndian ); + this._ptr += 8; + return a; + + }, + + /** + * Converts a UTF-8 byte array to JavaScript's 16-bit Unicode. + * @param {Array.} bytes UTF-8 byte array. + * @return {string} 16-bit Unicode string. + */ + readUTF: function () { + + var len = this.readU16(); + return this.readUTFBytes( len ); + + }, + + /** + * Converts a UTF-8 byte array to JavaScript's 16-bit Unicode. + * @param {Array.} bytes UTF-8 byte array. + * @return {string} 16-bit Unicode string. + */ + readUTFBytes: function ( len ) { + + // TODO(user): Use native implementations if/when available + var out = [], c = 0; + + while ( out.length < len ) { + + var c1 = this._data.getUint8( this._ptr ++, littleEndian ); + if ( c1 < 128 ) { + + out[ c ++ ] = String.fromCharCode( c1 ); + + } else if ( c1 > 191 && c1 < 224 ) { + + var c2 = this._data.getUint8( this._ptr ++, littleEndian ); + out[ c ++ ] = String.fromCharCode( ( c1 & 31 ) << 6 | c2 & 63 ); + + } else { + + var c2 = this._data.getUint8( this._ptr ++, littleEndian ); + var c3 = this._data.getUint8( this._ptr ++, littleEndian ); + out[ c ++ ] = String.fromCharCode( ( c1 & 15 ) << 12 | ( c2 & 63 ) << 6 | c3 & 63 ); + + } + + } + return out.join( '' ); + + } + +}; + +export { AWDLoader }; diff --git a/examples/jsm/loaders/AssimpJSONLoader.js b/examples/jsm/loaders/AssimpJSONLoader.js new file mode 100644 index 00000000000000..10a097f16e8d66 --- /dev/null +++ b/examples/jsm/loaders/AssimpJSONLoader.js @@ -0,0 +1,305 @@ +/** + * @author Alexander Gessler / http://www.greentoken.de/ + * https://github.com/acgessler + * + * Loader for models imported with Open Asset Import Library (http://assimp.sf.net) + * through assimp2json (https://github.com/acgessler/assimp2json). + * + * Supports any input format that assimp supports, including 3ds, obj, dae, blend, + * fbx, x, ms3d, lwo (and many more). + * + * See webgl_loader_assimp2json example. + */ + +import { + BufferGeometry, + DefaultLoadingManager, + FileLoader, + Float32BufferAttribute, + LoaderUtils, + Matrix4, + Mesh, + MeshPhongMaterial, + Object3D, + RepeatWrapping, + TextureLoader, +} from "../../../build/three.module.js"; + +var AssimpJSONLoader = function ( manager ) { + + this.manager = ( manager !== undefined ) ? manager : DefaultLoadingManager; + +}; + +AssimpJSONLoader.prototype = { + + constructor: AssimpJSONLoader, + + crossOrigin: 'anonymous', + + load: function ( url, onLoad, onProgress, onError ) { + + var scope = this; + + var path = ( scope.path === undefined ) ? LoaderUtils.extractUrlBase( url ) : scope.path; + + var loader = new FileLoader( this.manager ); + loader.setPath( scope.path ); + loader.load( url, function ( text ) { + + var json = JSON.parse( text ); + var metadata = json.__metadata__; + + // check if __metadata__ meta header is present + // this header is used to disambiguate between different JSON-based file formats + + if ( typeof metadata !== 'undefined' ) { + + // check if assimp2json at all + + if ( metadata.format !== 'assimp2json' ) { + + onError( 'THREE.AssimpJSONLoader: Not an assimp2json scene.' ); + return; + + // check major format version + + } else if ( metadata.version < 100 && metadata.version >= 200 ) { + + onError( 'THREE.AssimpJSONLoader: Unsupported assimp2json file format version.' ); + return; + + } + + } + + onLoad( scope.parse( json, path ) ); + + }, onProgress, onError ); + + }, + + setPath: function ( value ) { + + this.path = value; + return this; + + }, + + setResourcePath: function ( value ) { + + this.resourcePath = value; + return this; + + }, + + setCrossOrigin: function ( value ) { + + this.crossOrigin = value; + return this; + + }, + + parse: function ( json, path ) { + + function parseList( json, handler ) { + + var meshes = new Array( json.length ); + + for ( var i = 0; i < json.length; ++ i ) { + + meshes[ i ] = handler.call( this, json[ i ] ); + + } + + return meshes; + + } + + function parseMesh( json ) { + + var geometry = new BufferGeometry(); + + var i, l, face; + + var indices = []; + + var vertices = json.vertices || []; + var normals = json.normals || []; + var uvs = json.texturecoords || []; + var colors = json.colors || []; + + uvs = uvs[ 0 ] || []; // only support for a single set of uvs + + for ( i = 0, l = json.faces.length; i < l; i ++ ) { + + face = json.faces[ i ]; + indices.push( face[ 0 ], face[ 1 ], face[ 2 ] ); + + } + + geometry.setIndex( indices ); + geometry.addAttribute( 'position', new Float32BufferAttribute( vertices, 3 ) ); + + if ( normals.length > 0 ) { + + geometry.addAttribute( 'normal', new Float32BufferAttribute( normals, 3 ) ); + + } + + if ( uvs.length > 0 ) { + + geometry.addAttribute( 'uv', new Float32BufferAttribute( uvs, 2 ) ); + + } + + if ( colors.length > 0 ) { + + geometry.addAttribute( 'color', new Float32BufferAttribute( colors, 3 ) ); + + } + + geometry.computeBoundingSphere(); + + return geometry; + + } + + function parseMaterial( json ) { + + var material = new MeshPhongMaterial(); + + for ( var i in json.properties ) { + + var property = json.properties[ i ]; + var key = property.key; + var value = property.value; + + switch ( key ) { + + case '$tex.file': { + + var semantic = property.semantic; + + // prop.semantic gives the type of the texture + // 1: diffuse + // 2: specular mao + // 5: height map (bumps) + // 6: normal map + // more values (i.e. emissive, environment) are known by assimp and may be relevant + + if ( semantic === 1 || semantic === 2 || semantic === 5 || semantic === 6 ) { + + var keyname; + + switch ( semantic ) { + + case 1: + keyname = 'map'; + break; + case 2: + keyname = 'specularMap'; + break; + case 5: + keyname = 'bumpMap'; + break; + case 6: + keyname = 'normalMap'; + break; + + } + + var texture = textureLoader.load( value ); + + // TODO: read texture settings from assimp. + // Wrapping is the default, though. + + texture.wrapS = texture.wrapT = RepeatWrapping; + + material[ keyname ] = texture; + + } + + break; + + } + + case '?mat.name': + material.name = value; + break; + + case '$clr.diffuse': + material.color.fromArray( value ); + break; + + case '$clr.specular': + material.specular.fromArray( value ); + break; + + case '$clr.emissive': + material.emissive.fromArray( value ); + break; + + case '$mat.shininess': + material.shininess = value; + break; + + case '$mat.shadingm': + // aiShadingMode_Flat + material.flatShading = ( value === 1 ) ? true : false; + break; + + case '$mat.opacity': + if ( value < 1 ) { + + material.opacity = value; + material.transparent = true; + + } + break; + + } + + } + + return material; + + } + + function parseObject( json, node, meshes, materials ) { + + var obj = new Object3D(), i, idx; + + obj.name = node.name || ''; + obj.matrix = new Matrix4().fromArray( node.transformation ).transpose(); + obj.matrix.decompose( obj.position, obj.quaternion, obj.scale ); + + for ( i = 0; node.meshes && i < node.meshes.length; i ++ ) { + + idx = node.meshes[ i ]; + obj.add( new Mesh( meshes[ idx ], materials[ json.meshes[ idx ].materialindex ] ) ); + + } + + for ( i = 0; node.children && i < node.children.length; i ++ ) { + + obj.add( parseObject( json, node.children[ i ], meshes, materials ) ); + + } + + return obj; + + } + + var textureLoader = new TextureLoader( this.manager ); + textureLoader.setPath( this.resourcePath || path ).setCrossOrigin( this.crossOrigin ); + + var meshes = parseList( json.meshes, parseMesh ); + var materials = parseList( json.materials, parseMaterial ); + return parseObject( json, json.rootnode, meshes, materials ); + + } + +}; + +export { AssimpJSONLoader }; diff --git a/examples/jsm/loaders/AssimpLoader.js b/examples/jsm/loaders/AssimpLoader.js new file mode 100644 index 00000000000000..aa7d600992fb68 --- /dev/null +++ b/examples/jsm/loaders/AssimpLoader.js @@ -0,0 +1,2402 @@ +/** + * @author Virtulous / https://virtulo.us/ + */ + +import { + Bone, + BufferAttribute, + BufferGeometry, + Color, + DefaultLoadingManager, + FileLoader, + LoaderUtils, + Matrix4, + Mesh, + MeshLambertMaterial, + MeshPhongMaterial, + Object3D, + Quaternion, + Skeleton, + SkinnedMesh, + TextureLoader, + Vector2, + Vector3, + Vector4, +} from "../../../build/three.module.js"; + +var AssimpLoader = function ( manager ) { + + this.manager = ( manager !== undefined ) ? manager : DefaultLoadingManager; + +}; + +AssimpLoader.prototype = { + + constructor: AssimpLoader, + + crossOrigin: 'anonymous', + + load: function ( url, onLoad, onProgress, onError ) { + + var scope = this; + + var path = ( scope.path === undefined ) ? LoaderUtils.extractUrlBase( url ) : scope.path; + + var loader = new FileLoader( this.manager ); + loader.setPath( scope.path ); + loader.setResponseType( 'arraybuffer' ); + + loader.load( url, function ( buffer ) { + + onLoad( scope.parse( buffer, path ) ); + + }, onProgress, onError ); + + }, + + setPath: function ( value ) { + + this.path = value; + return this; + + }, + + setResourcePath: function ( value ) { + + this.resourcePath = value; + return this; + + }, + + setCrossOrigin: function ( value ) { + + this.crossOrigin = value; + return this; + + }, + + parse: function ( buffer, path ) { + + var textureLoader = new TextureLoader( this.manager ); + textureLoader.setPath( this.resourcePath || path ).setCrossOrigin( this.crossOrigin ); + + var Virtulous = {}; + + Virtulous.KeyFrame = function ( time, matrix ) { + + this.time = time; + this.matrix = matrix.clone(); + this.position = new Vector3(); + this.quaternion = new Quaternion(); + this.scale = new Vector3( 1, 1, 1 ); + this.matrix.decompose( this.position, this.quaternion, this.scale ); + this.clone = function () { + + var n = new Virtulous.KeyFrame( this.time, this.matrix ); + return n; + + }; + this.lerp = function ( nextKey, time ) { + + time -= this.time; + var dist = ( nextKey.time - this.time ); + var l = time / dist; + var l2 = 1 - l; + var keypos = this.position; + var keyrot = this.quaternion; + // var keyscl = key.parentspaceScl || key.scl; + var key2pos = nextKey.position; + var key2rot = nextKey.quaternion; + // var key2scl = key2.parentspaceScl || key2.scl; + Virtulous.KeyFrame.tempAniPos.x = keypos.x * l2 + key2pos.x * l; + Virtulous.KeyFrame.tempAniPos.y = keypos.y * l2 + key2pos.y * l; + Virtulous.KeyFrame.tempAniPos.z = keypos.z * l2 + key2pos.z * l; + // tempAniScale.x = keyscl[0] * l2 + key2scl[0] * l; + // tempAniScale.y = keyscl[1] * l2 + key2scl[1] * l; + // tempAniScale.z = keyscl[2] * l2 + key2scl[2] * l; + Virtulous.KeyFrame.tempAniQuat.set( keyrot.x, keyrot.y, keyrot.z, keyrot.w ); + Virtulous.KeyFrame.tempAniQuat.slerp( key2rot, l ); + return Virtulous.KeyFrame.tempAniMatrix.compose( Virtulous.KeyFrame.tempAniPos, Virtulous.KeyFrame.tempAniQuat, Virtulous.KeyFrame.tempAniScale ); + + }; + + }; + + Virtulous.KeyFrame.tempAniPos = new Vector3(); + Virtulous.KeyFrame.tempAniQuat = new Quaternion(); + Virtulous.KeyFrame.tempAniScale = new Vector3( 1, 1, 1 ); + Virtulous.KeyFrame.tempAniMatrix = new Matrix4(); + Virtulous.KeyFrameTrack = function () { + + this.keys = []; + this.target = null; + this.time = 0; + this.length = 0; + this._accelTable = {}; + this.fps = 20; + this.addKey = function ( key ) { + + this.keys.push( key ); + + }; + this.init = function () { + + this.sortKeys(); + + if ( this.keys.length > 0 ) + this.length = this.keys[ this.keys.length - 1 ].time; + else + this.length = 0; + + if ( ! this.fps ) return; + + for ( var j = 0; j < this.length * this.fps; j ++ ) { + + for ( var i = 0; i < this.keys.length; i ++ ) { + + if ( this.keys[ i ].time == j ) { + + this._accelTable[ j ] = i; + break; + + } else if ( this.keys[ i ].time < j / this.fps && this.keys[ i + 1 ] && this.keys[ i + 1 ].time >= j / this.fps ) { + + this._accelTable[ j ] = i; + break; + + } + + } + + } + + }; + + this.parseFromThree = function ( data ) { + + var fps = data.fps; + this.target = data.node; + var track = data.hierarchy[ 0 ].keys; + for ( var i = 0; i < track.length; i ++ ) { + + this.addKey( new Virtulous.KeyFrame( i / fps || track[ i ].time, track[ i ].targets[ 0 ].data ) ); + + } + this.init(); + + }; + + this.parseFromCollada = function ( data ) { + + var track = data.keys; + var fps = this.fps; + + for ( var i = 0; i < track.length; i ++ ) { + + this.addKey( new Virtulous.KeyFrame( i / fps || track[ i ].time, track[ i ].matrix ) ); + + } + + this.init(); + + }; + + this.sortKeys = function () { + + this.keys.sort( this.keySortFunc ); + + }; + + this.keySortFunc = function ( a, b ) { + + return a.time - b.time; + + }; + + this.clone = function () { + + var t = new Virtulous.KeyFrameTrack(); + t.target = this.target; + t.time = this.time; + t.length = this.length; + + for ( var i = 0; i < this.keys.length; i ++ ) { + + t.addKey( this.keys[ i ].clone() ); + + } + + t.init(); + return t; + + }; + + this.reTarget = function ( root, compareitor ) { + + if ( ! compareitor ) compareitor = Virtulous.TrackTargetNodeNameCompare; + this.target = compareitor( root, this.target ); + + }; + + this.keySearchAccel = function ( time ) { + + time *= this.fps; + time = Math.floor( time ); + return this._accelTable[ time ] || 0; + + }; + + this.setTime = function ( time ) { + + time = Math.abs( time ); + if ( this.length ) + time = time % this.length + .05; + var key0 = null; + var key1 = null; + + for ( var i = this.keySearchAccel( time ); i < this.keys.length; i ++ ) { + + if ( this.keys[ i ].time == time ) { + + key0 = this.keys[ i ]; + key1 = this.keys[ i ]; + break; + + } else if ( this.keys[ i ].time < time && this.keys[ i + 1 ] && this.keys[ i + 1 ].time > time ) { + + key0 = this.keys[ i ]; + key1 = this.keys[ i + 1 ]; + break; + + } else if ( this.keys[ i ].time < time && i == this.keys.length - 1 ) { + + key0 = this.keys[ i ]; + key1 = this.keys[ 0 ].clone(); + key1.time += this.length + .05; + break; + + } + + } + + if ( key0 && key1 && key0 !== key1 ) { + + this.target.matrixAutoUpdate = false; + this.target.matrix.copy( key0.lerp( key1, time ) ); + this.target.matrixWorldNeedsUpdate = true; + return; + + } + + if ( key0 && key1 && key0 == key1 ) { + + this.target.matrixAutoUpdate = false; + this.target.matrix.copy( key0.matrix ); + this.target.matrixWorldNeedsUpdate = true; + return; + + } + + }; + + }; + + Virtulous.TrackTargetNodeNameCompare = function ( root, target ) { + + function find( node, name ) { + + if ( node.name == name ) + return node; + + for ( var i = 0; i < node.children.length; i ++ ) { + + var r = find( node.children[ i ], name ); + if ( r ) return r; + + } + + return null; + + } + + return find( root, target.name ); + + }; + + Virtulous.Animation = function () { + + this.tracks = []; + this.length = 0; + + this.addTrack = function ( track ) { + + this.tracks.push( track ); + this.length = Math.max( track.length, this.length ); + + }; + + this.setTime = function ( time ) { + + this.time = time; + + for ( var i = 0; i < this.tracks.length; i ++ ) + this.tracks[ i ].setTime( time ); + + }; + + this.clone = function ( target, compareitor ) { + + if ( ! compareitor ) compareitor = Virtulous.TrackTargetNodeNameCompare; + var n = new Virtulous.Animation(); + n.target = target; + for ( var i = 0; i < this.tracks.length; i ++ ) { + + var track = this.tracks[ i ].clone(); + track.reTarget( target, compareitor ); + n.addTrack( track ); + + } + + return n; + + }; + + }; + + var ASSBIN_CHUNK_AICAMERA = 0x1234; + var ASSBIN_CHUNK_AILIGHT = 0x1235; + var ASSBIN_CHUNK_AITEXTURE = 0x1236; + var ASSBIN_CHUNK_AIMESH = 0x1237; + var ASSBIN_CHUNK_AINODEANIM = 0x1238; + var ASSBIN_CHUNK_AISCENE = 0x1239; + var ASSBIN_CHUNK_AIBONE = 0x123a; + var ASSBIN_CHUNK_AIANIMATION = 0x123b; + var ASSBIN_CHUNK_AINODE = 0x123c; + var ASSBIN_CHUNK_AIMATERIAL = 0x123d; + var ASSBIN_CHUNK_AIMATERIALPROPERTY = 0x123e; + var ASSBIN_MESH_HAS_POSITIONS = 0x1; + var ASSBIN_MESH_HAS_NORMALS = 0x2; + var ASSBIN_MESH_HAS_TANGENTS_AND_BITANGENTS = 0x4; + var ASSBIN_MESH_HAS_TEXCOORD_BASE = 0x100; + var ASSBIN_MESH_HAS_COLOR_BASE = 0x10000; + var AI_MAX_NUMBER_OF_COLOR_SETS = 1; + var AI_MAX_NUMBER_OF_TEXTURECOORDS = 4; + var aiLightSource_UNDEFINED = 0x0; + //! A directional light source has a well-defined direction + //! but is infinitely far away. That's quite a good + //! approximation for sun light. + var aiLightSource_DIRECTIONAL = 0x1; + //! A point light source has a well-defined position + //! in space but no direction - it emits light in all + //! directions. A normal bulb is a point light. + var aiLightSource_POINT = 0x2; + //! A spot light source emits light in a specific + //! angle. It has a position and a direction it is pointing to. + //! A good example for a spot light is a light spot in + //! sport arenas. + var aiLightSource_SPOT = 0x3; + //! The generic light level of the world, including the bounces + //! of all other lightsources. + //! Typically, there's at most one ambient light in a scene. + //! This light type doesn't have a valid position, direction, or + //! other properties, just a color. + var aiLightSource_AMBIENT = 0x4; + /** Flat shading. Shading is done on per-face base, + * diffuse only. Also known as 'faceted shading'. + */ + var aiShadingMode_Flat = 0x1; + /** Simple Gouraud shading. + */ + var aiShadingMode_Gouraud = 0x2; + /** Phong-Shading - + */ + var aiShadingMode_Phong = 0x3; + /** Phong-Blinn-Shading + */ + var aiShadingMode_Blinn = 0x4; + /** Toon-Shading per pixel + * + * Also known as 'comic' shader. + */ + var aiShadingMode_Toon = 0x5; + /** OrenNayar-Shading per pixel + * + * Extension to standard Lambertian shading, taking the + * roughness of the material into account + */ + var aiShadingMode_OrenNayar = 0x6; + /** Minnaert-Shading per pixel + * + * Extension to standard Lambertian shading, taking the + * "darkness" of the material into account + */ + var aiShadingMode_Minnaert = 0x7; + /** CookTorrance-Shading per pixel + * + * Special shader for metallic surfaces. + */ + var aiShadingMode_CookTorrance = 0x8; + /** No shading at all. Constant light influence of 1.0. + */ + var aiShadingMode_NoShading = 0x9; + /** Fresnel shading + */ + var aiShadingMode_Fresnel = 0xa; + var aiTextureType_NONE = 0x0; + /** The texture is combined with the result of the diffuse + * lighting equation. + */ + var aiTextureType_DIFFUSE = 0x1; + /** The texture is combined with the result of the specular + * lighting equation. + */ + var aiTextureType_SPECULAR = 0x2; + /** The texture is combined with the result of the ambient + * lighting equation. + */ + var aiTextureType_AMBIENT = 0x3; + /** The texture is added to the result of the lighting + * calculation. It isn't influenced by incoming light. + */ + var aiTextureType_EMISSIVE = 0x4; + /** The texture is a height map. + * + * By convention, higher gray-scale values stand for + * higher elevations from the base height. + */ + var aiTextureType_HEIGHT = 0x5; + /** The texture is a (tangent space) normal-map. + * + * Again, there are several conventions for tangent-space + * normal maps. Assimp does (intentionally) not + * distinguish here. + */ + var aiTextureType_NORMALS = 0x6; + /** The texture defines the glossiness of the material. + * + * The glossiness is in fact the exponent of the specular + * (phong) lighting equation. Usually there is a conversion + * function defined to map the linear color values in the + * texture to a suitable exponent. Have fun. + */ + var aiTextureType_SHININESS = 0x7; + /** The texture defines per-pixel opacity. + * + * Usually 'white' means opaque and 'black' means + * 'transparency'. Or quite the opposite. Have fun. + */ + var aiTextureType_OPACITY = 0x8; + /** Displacement texture + * + * The exact purpose and format is application-dependent. + * Higher color values stand for higher vertex displacements. + */ + var aiTextureType_DISPLACEMENT = 0x9; + /** Lightmap texture (aka Ambient Occlusion) + * + * Both 'Lightmaps' and dedicated 'ambient occlusion maps' are + * covered by this material property. The texture contains a + * scaling value for the final color value of a pixel. Its + * intensity is not affected by incoming light. + */ + var aiTextureType_LIGHTMAP = 0xA; + /** Reflection texture + * + * Contains the color of a perfect mirror reflection. + * Rarely used, almost never for real-time applications. + */ + var aiTextureType_REFLECTION = 0xB; + /** Unknown texture + * + * A texture reference that does not match any of the definitions + * above is considered to be 'unknown'. It is still imported, + * but is excluded from any further postprocessing. + */ + var aiTextureType_UNKNOWN = 0xC; + var BONESPERVERT = 4; + + function ASSBIN_MESH_HAS_TEXCOORD( n ) { + + return ASSBIN_MESH_HAS_TEXCOORD_BASE << n; + + } + + function ASSBIN_MESH_HAS_COLOR( n ) { + + return ASSBIN_MESH_HAS_COLOR_BASE << n; + + } + + function markBones( scene ) { + + for ( var i in scene.mMeshes ) { + + var mesh = scene.mMeshes[ i ]; + for ( var k in mesh.mBones ) { + + var boneNode = scene.findNode( mesh.mBones[ k ].mName ); + if ( boneNode ) + boneNode.isBone = true; + + } + + } + + } + function cloneTreeToBones( root, scene ) { + + var rootBone = new Bone(); + rootBone.matrix.copy( root.matrix ); + rootBone.matrixWorld.copy( root.matrixWorld ); + rootBone.position.copy( root.position ); + rootBone.quaternion.copy( root.quaternion ); + rootBone.scale.copy( root.scale ); + scene.nodeCount ++; + rootBone.name = "bone_" + root.name + scene.nodeCount.toString(); + + if ( ! scene.nodeToBoneMap[ root.name ] ) + scene.nodeToBoneMap[ root.name ] = []; + scene.nodeToBoneMap[ root.name ].push( rootBone ); + for ( var i in root.children ) { + + var child = cloneTreeToBones( root.children[ i ], scene ); + if ( child ) + rootBone.add( child ); + + } + + return rootBone; + + } + + function sortWeights( indexes, weights ) { + + var pairs = []; + + for ( var i = 0; i < indexes.length; i ++ ) { + + pairs.push( { + i: indexes[ i ], + w: weights[ i ] + } ); + + } + + pairs.sort( function ( a, b ) { + + return b.w - a.w; + + } ); + + while ( pairs.length < 4 ) { + + pairs.push( { + i: 0, + w: 0 + } ); + + } + + if ( pairs.length > 4 ) + pairs.length = 4; + var sum = 0; + + for ( var i = 0; i < 4; i ++ ) { + + sum += pairs[ i ].w * pairs[ i ].w; + + } + + sum = Math.sqrt( sum ); + + for ( var i = 0; i < 4; i ++ ) { + + pairs[ i ].w = pairs[ i ].w / sum; + indexes[ i ] = pairs[ i ].i; + weights[ i ] = pairs[ i ].w; + + } + + } + + function findMatchingBone( root, name ) { + + if ( root.name.indexOf( "bone_" + name ) == 0 ) + return root; + + for ( var i in root.children ) { + + var ret = findMatchingBone( root.children[ i ], name ); + + if ( ret ) + return ret; + + } + + return undefined; + + } + + function aiMesh() { + + this.mPrimitiveTypes = 0; + this.mNumVertices = 0; + this.mNumFaces = 0; + this.mNumBones = 0; + this.mMaterialIndex = 0; + this.mVertices = []; + this.mNormals = []; + this.mTangents = []; + this.mBitangents = []; + this.mColors = [ + [] + ]; + this.mTextureCoords = [ + [] + ]; + this.mFaces = []; + this.mBones = []; + this.hookupSkeletons = function ( scene, threeScene ) { + + if ( this.mBones.length == 0 ) return; + + var allBones = []; + var offsetMatrix = []; + var skeletonRoot = scene.findNode( this.mBones[ 0 ].mName ); + + while ( skeletonRoot.mParent && skeletonRoot.mParent.isBone ) { + + skeletonRoot = skeletonRoot.mParent; + + } + + var threeSkeletonRoot = skeletonRoot.toTHREE( scene ); + var threeSkeletonRootBone = cloneTreeToBones( threeSkeletonRoot, scene ); + this.threeNode.add( threeSkeletonRootBone ); + + for ( var i = 0; i < this.mBones.length; i ++ ) { + + var bone = findMatchingBone( threeSkeletonRootBone, this.mBones[ i ].mName ); + + if ( bone ) { + + var tbone = bone; + allBones.push( tbone ); + //tbone.matrixAutoUpdate = false; + offsetMatrix.push( this.mBones[ i ].mOffsetMatrix.toTHREE() ); + + } else { + + var skeletonRoot = scene.findNode( this.mBones[ i ].mName ); + if ( ! skeletonRoot ) return; + var threeSkeletonRoot = skeletonRoot.toTHREE( scene ); + var threeSkeletonRootParent = threeSkeletonRoot.parent; + var threeSkeletonRootBone = cloneTreeToBones( threeSkeletonRoot, scene ); + this.threeNode.add( threeSkeletonRootBone ); + var bone = findMatchingBone( threeSkeletonRootBone, this.mBones[ i ].mName ); + var tbone = bone; + allBones.push( tbone ); + //tbone.matrixAutoUpdate = false; + offsetMatrix.push( this.mBones[ i ].mOffsetMatrix.toTHREE() ); + + } + + } + var skeleton = new Skeleton( allBones, offsetMatrix ); + + this.threeNode.bind( skeleton, new Matrix4() ); + this.threeNode.material.skinning = true; + + }; + + this.toTHREE = function ( scene ) { + + if ( this.threeNode ) return this.threeNode; + var geometry = new BufferGeometry(); + var mat; + if ( scene.mMaterials[ this.mMaterialIndex ] ) + mat = scene.mMaterials[ this.mMaterialIndex ].toTHREE( scene ); + else + mat = new MeshLambertMaterial(); + geometry.setIndex( new BufferAttribute( new Uint32Array( this.mIndexArray ), 1 ) ); + geometry.addAttribute( 'position', new BufferAttribute( this.mVertexBuffer, 3 ) ); + if ( this.mNormalBuffer && this.mNormalBuffer.length > 0 ) + geometry.addAttribute( 'normal', new BufferAttribute( this.mNormalBuffer, 3 ) ); + if ( this.mColorBuffer && this.mColorBuffer.length > 0 ) + geometry.addAttribute( 'color', new BufferAttribute( this.mColorBuffer, 4 ) ); + if ( this.mTexCoordsBuffers[ 0 ] && this.mTexCoordsBuffers[ 0 ].length > 0 ) + geometry.addAttribute( 'uv', new BufferAttribute( new Float32Array( this.mTexCoordsBuffers[ 0 ] ), 2 ) ); + if ( this.mTexCoordsBuffers[ 1 ] && this.mTexCoordsBuffers[ 1 ].length > 0 ) + geometry.addAttribute( 'uv1', new BufferAttribute( new Float32Array( this.mTexCoordsBuffers[ 1 ] ), 2 ) ); + if ( this.mTangentBuffer && this.mTangentBuffer.length > 0 ) + geometry.addAttribute( 'tangents', new BufferAttribute( this.mTangentBuffer, 3 ) ); + if ( this.mBitangentBuffer && this.mBitangentBuffer.length > 0 ) + geometry.addAttribute( 'bitangents', new BufferAttribute( this.mBitangentBuffer, 3 ) ); + if ( this.mBones.length > 0 ) { + + var weights = []; + var bones = []; + + for ( var i = 0; i < this.mBones.length; i ++ ) { + + for ( var j = 0; j < this.mBones[ i ].mWeights.length; j ++ ) { + + var weight = this.mBones[ i ].mWeights[ j ]; + if ( weight ) { + + if ( ! weights[ weight.mVertexId ] ) weights[ weight.mVertexId ] = []; + if ( ! bones[ weight.mVertexId ] ) bones[ weight.mVertexId ] = []; + weights[ weight.mVertexId ].push( weight.mWeight ); + bones[ weight.mVertexId ].push( parseInt( i ) ); + + } + + } + + } + + for ( var i in bones ) { + + sortWeights( bones[ i ], weights[ i ] ); + + } + + var _weights = []; + var _bones = []; + + for ( var i = 0; i < weights.length; i ++ ) { + + for ( var j = 0; j < 4; j ++ ) { + + if ( weights[ i ] && bones[ i ] ) { + + _weights.push( weights[ i ][ j ] ); + _bones.push( bones[ i ][ j ] ); + + } else { + + _weights.push( 0 ); + _bones.push( 0 ); + + } + + } + + } + + geometry.addAttribute( 'skinWeight', new BufferAttribute( new Float32Array( _weights ), BONESPERVERT ) ); + geometry.addAttribute( 'skinIndex', new BufferAttribute( new Float32Array( _bones ), BONESPERVERT ) ); + + } + + var mesh; + + if ( this.mBones.length == 0 ) + mesh = new Mesh( geometry, mat ); + + if ( this.mBones.length > 0 ) { + + mesh = new SkinnedMesh( geometry, mat ); + mesh.normalizeSkinWeights(); + + } + + this.threeNode = mesh; + //mesh.matrixAutoUpdate = false; + return mesh; + + }; + + } + + function aiFace() { + + this.mNumIndices = 0; + this.mIndices = []; + + } + + function aiVector3D() { + + this.x = 0; + this.y = 0; + this.z = 0; + + this.toTHREE = function () { + + return new Vector3( this.x, this.y, this.z ); + + }; + + } + + function aiVector2D() { + + this.x = 0; + this.y = 0; + this.toTHREE = function () { + + return new Vector2( this.x, this.y ); + + }; + + } + + function aiVector4D() { + + this.w = 0; + this.x = 0; + this.y = 0; + this.z = 0; + this.toTHREE = function () { + + return new Vector4( this.w, this.x, this.y, this.z ); + + }; + + } + + function aiColor4D() { + + this.r = 0; + this.g = 0; + this.b = 0; + this.a = 0; + this.toTHREE = function () { + + return new Color( this.r, this.g, this.b, this.a ); + + }; + + } + + function aiColor3D() { + + this.r = 0; + this.g = 0; + this.b = 0; + this.a = 0; + this.toTHREE = function () { + + return new Color( this.r, this.g, this.b, 1 ); + + }; + + } + + function aiQuaternion() { + + this.x = 0; + this.y = 0; + this.z = 0; + this.w = 0; + this.toTHREE = function () { + + return new Quaternion( this.x, this.y, this.z, this.w ); + + }; + + } + + function aiVertexWeight() { + + this.mVertexId = 0; + this.mWeight = 0; + + } + + function aiString() { + + this.data = []; + this.toString = function () { + + var str = ''; + this.data.forEach( function ( i ) { + + str += ( String.fromCharCode( i ) ); + + } ); + return str.replace( /[^\x20-\x7E]+/g, '' ); + + }; + + } + + function aiVectorKey() { + + this.mTime = 0; + this.mValue = null; + + } + + function aiQuatKey() { + + this.mTime = 0; + this.mValue = null; + + } + + function aiNode() { + + this.mName = ''; + this.mTransformation = []; + this.mNumChildren = 0; + this.mNumMeshes = 0; + this.mMeshes = []; + this.mChildren = []; + this.toTHREE = function ( scene ) { + + if ( this.threeNode ) return this.threeNode; + var o = new Object3D(); + o.name = this.mName; + o.matrix = this.mTransformation.toTHREE(); + + for ( var i = 0; i < this.mChildren.length; i ++ ) { + + o.add( this.mChildren[ i ].toTHREE( scene ) ); + + } + + for ( var i = 0; i < this.mMeshes.length; i ++ ) { + + o.add( scene.mMeshes[ this.mMeshes[ i ] ].toTHREE( scene ) ); + + } + + this.threeNode = o; + //o.matrixAutoUpdate = false; + o.matrix.decompose( o.position, o.quaternion, o.scale ); + return o; + + }; + + } + + function aiBone() { + + this.mName = ''; + this.mNumWeights = 0; + this.mOffsetMatrix = 0; + + } + + function aiMaterialProperty() { + + this.mKey = ""; + this.mSemantic = 0; + this.mIndex = 0; + this.mData = []; + this.mDataLength = 0; + this.mType = 0; + this.dataAsColor = function () { + + var array = ( new Uint8Array( this.mData ) ).buffer; + var reader = new DataView( array ); + var r = reader.getFloat32( 0, true ); + var g = reader.getFloat32( 4, true ); + var b = reader.getFloat32( 8, true ); + //var a = reader.getFloat32(12, true); + return new Color( r, g, b ); + + }; + + this.dataAsFloat = function () { + + var array = ( new Uint8Array( this.mData ) ).buffer; + var reader = new DataView( array ); + var r = reader.getFloat32( 0, true ); + return r; + + }; + + this.dataAsBool = function () { + + var array = ( new Uint8Array( this.mData ) ).buffer; + var reader = new DataView( array ); + var r = reader.getFloat32( 0, true ); + return !! r; + + }; + + this.dataAsString = function () { + + var s = new aiString(); + s.data = this.mData; + return s.toString(); + + }; + + this.dataAsMap = function () { + + var s = new aiString(); + s.data = this.mData; + var path = s.toString(); + path = path.replace( /\\/g, '/' ); + + if ( path.indexOf( '/' ) != - 1 ) { + + path = path.substr( path.lastIndexOf( '/' ) + 1 ); + + } + + return textureLoader.load( path ); + + }; + + } + var namePropMapping = { + + "?mat.name": "name", + "$mat.shadingm": "shading", + "$mat.twosided": "twoSided", + "$mat.wireframe": "wireframe", + "$clr.ambient": "ambient", + "$clr.diffuse": "color", + "$clr.specular": "specular", + "$clr.emissive": "emissive", + "$clr.transparent": "transparent", + "$clr.reflective": "reflect", + "$mat.shininess": "shininess", + "$mat.reflectivity": "reflectivity", + "$mat.refracti": "refraction", + "$tex.file": "map" + + }; + + var nameTypeMapping = { + + "?mat.name": "string", + "$mat.shadingm": "bool", + "$mat.twosided": "bool", + "$mat.wireframe": "bool", + "$clr.ambient": "color", + "$clr.diffuse": "color", + "$clr.specular": "color", + "$clr.emissive": "color", + "$clr.transparent": "color", + "$clr.reflective": "color", + "$mat.shininess": "float", + "$mat.reflectivity": "float", + "$mat.refracti": "float", + "$tex.file": "map" + + }; + + function aiMaterial() { + + this.mNumAllocated = 0; + this.mNumProperties = 0; + this.mProperties = []; + this.toTHREE = function ( scene ) { + + var name = this.mProperties[ 0 ].dataAsString(); + var mat = new MeshPhongMaterial(); + + for ( var i = 0; i < this.mProperties.length; i ++ ) { + + if ( nameTypeMapping[ this.mProperties[ i ].mKey ] == 'float' ) + mat[ namePropMapping[ this.mProperties[ i ].mKey ] ] = this.mProperties[ i ].dataAsFloat(); + if ( nameTypeMapping[ this.mProperties[ i ].mKey ] == 'color' ) + mat[ namePropMapping[ this.mProperties[ i ].mKey ] ] = this.mProperties[ i ].dataAsColor(); + if ( nameTypeMapping[ this.mProperties[ i ].mKey ] == 'bool' ) + mat[ namePropMapping[ this.mProperties[ i ].mKey ] ] = this.mProperties[ i ].dataAsBool(); + if ( nameTypeMapping[ this.mProperties[ i ].mKey ] == 'string' ) + mat[ namePropMapping[ this.mProperties[ i ].mKey ] ] = this.mProperties[ i ].dataAsString(); + if ( nameTypeMapping[ this.mProperties[ i ].mKey ] == 'map' ) { + + var prop = this.mProperties[ i ]; + if ( prop.mSemantic == aiTextureType_DIFFUSE ) + mat.map = this.mProperties[ i ].dataAsMap(); + if ( prop.mSemantic == aiTextureType_NORMALS ) + mat.normalMap = this.mProperties[ i ].dataAsMap(); + if ( prop.mSemantic == aiTextureType_LIGHTMAP ) + mat.lightMap = this.mProperties[ i ].dataAsMap(); + if ( prop.mSemantic == aiTextureType_OPACITY ) + mat.alphaMap = this.mProperties[ i ].dataAsMap(); + + } + + } + + mat.ambient.r = .53; + mat.ambient.g = .53; + mat.ambient.b = .53; + mat.color.r = 1; + mat.color.g = 1; + mat.color.b = 1; + return mat; + + }; + + } + + + function veclerp( v1, v2, l ) { + + var v = new Vector3(); + var lm1 = 1 - l; + v.x = v1.x * l + v2.x * lm1; + v.y = v1.y * l + v2.y * lm1; + v.z = v1.z * l + v2.z * lm1; + return v; + + } + + function quatlerp( q1, q2, l ) { + + return q1.clone().slerp( q2, 1 - l ); + + } + + function sampleTrack( keys, time, lne, lerp ) { + + if ( keys.length == 1 ) return keys[ 0 ].mValue.toTHREE(); + + var dist = Infinity; + var key = null; + var nextKey = null; + + for ( var i = 0; i < keys.length; i ++ ) { + + var timeDist = Math.abs( keys[ i ].mTime - time ); + + if ( timeDist < dist && keys[ i ].mTime <= time ) { + + dist = timeDist; + key = keys[ i ]; + nextKey = keys[ i + 1 ]; + + } + + } + + if ( ! key ) { + + return null; + + } else if ( nextKey ) { + + var dT = nextKey.mTime - key.mTime; + var T = key.mTime - time; + var l = T / dT; + + return lerp( key.mValue.toTHREE(), nextKey.mValue.toTHREE(), l ); + + } else { + + nextKey = keys[ 0 ].clone(); + nextKey.mTime += lne; + + var dT = nextKey.mTime - key.mTime; + var T = key.mTime - time; + var l = T / dT; + + return lerp( key.mValue.toTHREE(), nextKey.mValue.toTHREE(), l ); + + } + + } + + function aiNodeAnim() { + + this.mNodeName = ""; + this.mNumPositionKeys = 0; + this.mNumRotationKeys = 0; + this.mNumScalingKeys = 0; + this.mPositionKeys = []; + this.mRotationKeys = []; + this.mScalingKeys = []; + this.mPreState = ""; + this.mPostState = ""; + this.init = function ( tps ) { + + if ( ! tps ) tps = 1; + + function t( t ) { + + t.mTime /= tps; + + } + + this.mPositionKeys.forEach( t ); + this.mRotationKeys.forEach( t ); + this.mScalingKeys.forEach( t ); + + }; + + this.sortKeys = function () { + + function comp( a, b ) { + + return a.mTime - b.mTime; + + } + + this.mPositionKeys.sort( comp ); + this.mRotationKeys.sort( comp ); + this.mScalingKeys.sort( comp ); + + }; + + this.getLength = function () { + + return Math.max( + Math.max.apply( null, this.mPositionKeys.map( function ( a ) { + + return a.mTime; + + } ) ), + Math.max.apply( null, this.mRotationKeys.map( function ( a ) { + + return a.mTime; + + } ) ), + Math.max.apply( null, this.mScalingKeys.map( function ( a ) { + + return a.mTime; + + } ) ) + ); + + }; + + this.toTHREE = function ( o, tps ) { + + this.sortKeys(); + var length = this.getLength(); + var track = new Virtulous.KeyFrameTrack(); + + for ( var i = 0; i < length; i += .05 ) { + + var matrix = new Matrix4(); + var time = i; + var pos = sampleTrack( this.mPositionKeys, time, length, veclerp ); + var scale = sampleTrack( this.mScalingKeys, time, length, veclerp ); + var rotation = sampleTrack( this.mRotationKeys, time, length, quatlerp ); + matrix.compose( pos, rotation, scale ); + + var key = new Virtulous.KeyFrame( time, matrix ); + track.addKey( key ); + + } + + track.target = o.findNode( this.mNodeName ).toTHREE(); + + var tracks = [ track ]; + + if ( o.nodeToBoneMap[ this.mNodeName ] ) { + + for ( var i = 0; i < o.nodeToBoneMap[ this.mNodeName ].length; i ++ ) { + + var t2 = track.clone(); + t2.target = o.nodeToBoneMap[ this.mNodeName ][ i ]; + tracks.push( t2 ); + + } + + } + + return tracks; + + }; + + } + + function aiAnimation() { + + this.mName = ""; + this.mDuration = 0; + this.mTicksPerSecond = 0; + this.mNumChannels = 0; + this.mChannels = []; + this.toTHREE = function ( root ) { + + var animationHandle = new Virtulous.Animation(); + + for ( var i in this.mChannels ) { + + this.mChannels[ i ].init( this.mTicksPerSecond ); + + var tracks = this.mChannels[ i ].toTHREE( root ); + + for ( var j in tracks ) { + + tracks[ j ].init(); + animationHandle.addTrack( tracks[ j ] ); + + } + + } + + animationHandle.length = Math.max.apply( null, animationHandle.tracks.map( function ( e ) { + + return e.length; + + } ) ); + return animationHandle; + + }; + + } + + function aiTexture() { + + this.mWidth = 0; + this.mHeight = 0; + this.texAchFormatHint = []; + this.pcData = []; + + } + + function aiLight() { + + this.mName = ''; + this.mType = 0; + this.mAttenuationConstant = 0; + this.mAttenuationLinear = 0; + this.mAttenuationQuadratic = 0; + this.mAngleInnerCone = 0; + this.mAngleOuterCone = 0; + this.mColorDiffuse = null; + this.mColorSpecular = null; + this.mColorAmbient = null; + + } + + function aiCamera() { + + this.mName = ''; + this.mPosition = null; + this.mLookAt = null; + this.mUp = null; + this.mHorizontalFOV = 0; + this.mClipPlaneNear = 0; + this.mClipPlaneFar = 0; + this.mAspect = 0; + + } + + function aiScene() { + + this.mFlags = 0; + this.mNumMeshes = 0; + this.mNumMaterials = 0; + this.mNumAnimations = 0; + this.mNumTextures = 0; + this.mNumLights = 0; + this.mNumCameras = 0; + this.mRootNode = null; + this.mMeshes = []; + this.mMaterials = []; + this.mAnimations = []; + this.mLights = []; + this.mCameras = []; + this.nodeToBoneMap = {}; + this.findNode = function ( name, root ) { + + if ( ! root ) { + + root = this.mRootNode; + + } + + if ( root.mName == name ) { + + return root; + + } + + for ( var i = 0; i < root.mChildren.length; i ++ ) { + + var ret = this.findNode( name, root.mChildren[ i ] ); + if ( ret ) return ret; + + } + + return null; + + }; + + this.toTHREE = function () { + + this.nodeCount = 0; + + markBones( this ); + + var o = this.mRootNode.toTHREE( this ); + + for ( var i in this.mMeshes ) + this.mMeshes[ i ].hookupSkeletons( this, o ); + + if ( this.mAnimations.length > 0 ) { + + var a = this.mAnimations[ 0 ].toTHREE( this ); + + } + + return { object: o, animation: a }; + + }; + + } + + function aiMatrix4() { + + this.elements = [ + [], + [], + [], + [] + ]; + this.toTHREE = function () { + + var m = new Matrix4(); + + for ( var i = 0; i < 4; ++ i ) { + + for ( var i2 = 0; i2 < 4; ++ i2 ) { + + m.elements[ i * 4 + i2 ] = this.elements[ i2 ][ i ]; + + } + + } + + return m; + + }; + + } + + var littleEndian = true; + + function readFloat( dataview ) { + + var val = dataview.getFloat32( dataview.readOffset, littleEndian ); + dataview.readOffset += 4; + return val; + + } + + function Read_double( dataview ) { + + var val = dataview.getFloat64( dataview.readOffset, littleEndian ); + dataview.readOffset += 8; + return val; + + } + + function Read_uint8_t( dataview ) { + + var val = dataview.getUint8( dataview.readOffset ); + dataview.readOffset += 1; + return val; + + } + + function Read_uint16_t( dataview ) { + + var val = dataview.getUint16( dataview.readOffset, littleEndian ); + dataview.readOffset += 2; + return val; + + } + + function Read_unsigned_int( dataview ) { + + var val = dataview.getUint32( dataview.readOffset, littleEndian ); + dataview.readOffset += 4; + return val; + + } + + function Read_uint32_t( dataview ) { + + var val = dataview.getUint32( dataview.readOffset, littleEndian ); + dataview.readOffset += 4; + return val; + + } + + function Read_aiVector3D( stream ) { + + var v = new aiVector3D(); + v.x = readFloat( stream ); + v.y = readFloat( stream ); + v.z = readFloat( stream ); + return v; + + } + + function Read_aiVector2D( stream ) { + + var v = new aiVector2D(); + v.x = readFloat( stream ); + v.y = readFloat( stream ); + return v; + + } + + function Read_aiVector4D( stream ) { + + var v = new aiVector4D(); + v.w = readFloat( stream ); + v.x = readFloat( stream ); + v.y = readFloat( stream ); + v.z = readFloat( stream ); + return v; + + } + + function Read_aiColor3D( stream ) { + + var c = new aiColor3D(); + c.r = readFloat( stream ); + c.g = readFloat( stream ); + c.b = readFloat( stream ); + return c; + + } + + function Read_aiColor4D( stream ) { + + var c = new aiColor4D(); + c.r = readFloat( stream ); + c.g = readFloat( stream ); + c.b = readFloat( stream ); + c.a = readFloat( stream ); + return c; + + } + + function Read_aiQuaternion( stream ) { + + var v = new aiQuaternion(); + v.w = readFloat( stream ); + v.x = readFloat( stream ); + v.y = readFloat( stream ); + v.z = readFloat( stream ); + return v; + + } + + function Read_aiString( stream ) { + + var s = new aiString(); + var stringlengthbytes = Read_unsigned_int( stream ); + stream.ReadBytes( s.data, 1, stringlengthbytes ); + return s.toString(); + + } + + function Read_aiVertexWeight( stream ) { + + var w = new aiVertexWeight(); + w.mVertexId = Read_unsigned_int( stream ); + w.mWeight = readFloat( stream ); + return w; + + } + + function Read_aiMatrix4x4( stream ) { + + var m = new aiMatrix4(); + + for ( var i = 0; i < 4; ++ i ) { + + for ( var i2 = 0; i2 < 4; ++ i2 ) { + + m.elements[ i ][ i2 ] = readFloat( stream ); + + } + + } + + return m; + + } + + function Read_aiVectorKey( stream ) { + + var v = new aiVectorKey(); + v.mTime = Read_double( stream ); + v.mValue = Read_aiVector3D( stream ); + return v; + + } + + function Read_aiQuatKey( stream ) { + + var v = new aiQuatKey(); + v.mTime = Read_double( stream ); + v.mValue = Read_aiQuaternion( stream ); + return v; + + } + + function ReadArray( stream, data, size ) { + + for ( var i = 0; i < size; i ++ ) data[ i ] = Read( stream ); + + } + + function ReadArray_aiVector2D( stream, data, size ) { + + for ( var i = 0; i < size; i ++ ) data[ i ] = Read_aiVector2D( stream ); + + } + + function ReadArray_aiVector3D( stream, data, size ) { + + for ( var i = 0; i < size; i ++ ) data[ i ] = Read_aiVector3D( stream ); + + } + + function ReadArray_aiVector4D( stream, data, size ) { + + for ( var i = 0; i < size; i ++ ) data[ i ] = Read_aiVector4D( stream ); + + } + + function ReadArray_aiVertexWeight( stream, data, size ) { + + for ( var i = 0; i < size; i ++ ) data[ i ] = Read_aiVertexWeight( stream ); + + } + + function ReadArray_aiColor4D( stream, data, size ) { + + for ( var i = 0; i < size; i ++ ) data[ i ] = Read_aiColor4D( stream ); + + } + + function ReadArray_aiVectorKey( stream, data, size ) { + + for ( var i = 0; i < size; i ++ ) data[ i ] = Read_aiVectorKey( stream ); + + } + + function ReadArray_aiQuatKey( stream, data, size ) { + + for ( var i = 0; i < size; i ++ ) data[ i ] = Read_aiQuatKey( stream ); + + } + + function ReadBounds( stream, T /*p*/, n ) { + + // not sure what to do here, the data isn't really useful. + return stream.Seek( sizeof( T ) * n, aiOrigin_CUR ); + + } + + function ai_assert( bool ) { + + if ( ! bool ) + throw ( "asset failed" ); + + } + + function ReadBinaryNode( stream, parent, depth ) { + + var chunkID = Read_uint32_t( stream ); + ai_assert( chunkID == ASSBIN_CHUNK_AINODE ); + /*uint32_t size =*/ + Read_uint32_t( stream ); + var node = new aiNode(); + node.mParent = parent; + node.mDepth = depth; + node.mName = Read_aiString( stream ); + node.mTransformation = Read_aiMatrix4x4( stream ); + node.mNumChildren = Read_unsigned_int( stream ); + node.mNumMeshes = Read_unsigned_int( stream ); + + if ( node.mNumMeshes ) { + + node.mMeshes = []; + + for ( var i = 0; i < node.mNumMeshes; ++ i ) { + + node.mMeshes[ i ] = Read_unsigned_int( stream ); + + } + + } + + if ( node.mNumChildren ) { + + node.mChildren = []; + + for ( var i = 0; i < node.mNumChildren; ++ i ) { + + var node2 = ReadBinaryNode( stream, node, depth ++ ); + node.mChildren[ i ] = node2; + + } + + } + + return node; + + } + + // ----------------------------------------------------------------------------------- + + function ReadBinaryBone( stream, b ) { + + var chunkID = Read_uint32_t( stream ); + ai_assert( chunkID == ASSBIN_CHUNK_AIBONE ); + /*uint32_t size =*/ + Read_uint32_t( stream ); + b.mName = Read_aiString( stream ); + b.mNumWeights = Read_unsigned_int( stream ); + b.mOffsetMatrix = Read_aiMatrix4x4( stream ); + // for the moment we write dumb min/max values for the bones, too. + // maybe I'll add a better, hash-like solution later + if ( shortened ) { + + ReadBounds( stream, b.mWeights, b.mNumWeights ); + + } else { + + // else write as usual + + b.mWeights = []; + ReadArray_aiVertexWeight( stream, b.mWeights, b.mNumWeights ); + + } + + return b; + + } + + function ReadBinaryMesh( stream, mesh ) { + + var chunkID = Read_uint32_t( stream ); + ai_assert( chunkID == ASSBIN_CHUNK_AIMESH ); + /*uint32_t size =*/ + Read_uint32_t( stream ); + mesh.mPrimitiveTypes = Read_unsigned_int( stream ); + mesh.mNumVertices = Read_unsigned_int( stream ); + mesh.mNumFaces = Read_unsigned_int( stream ); + mesh.mNumBones = Read_unsigned_int( stream ); + mesh.mMaterialIndex = Read_unsigned_int( stream ); + mesh.mNumUVComponents = []; + // first of all, write bits for all existent vertex components + var c = Read_unsigned_int( stream ); + + if ( c & ASSBIN_MESH_HAS_POSITIONS ) { + + if ( shortened ) { + + ReadBounds( stream, mesh.mVertices, mesh.mNumVertices ); + + } else { + + // else write as usual + + mesh.mVertices = []; + mesh.mVertexBuffer = stream.subArray32( stream.readOffset, stream.readOffset + mesh.mNumVertices * 3 * 4 ); + stream.Seek( mesh.mNumVertices * 3 * 4, aiOrigin_CUR ); + + } + + } + + if ( c & ASSBIN_MESH_HAS_NORMALS ) { + + if ( shortened ) { + + ReadBounds( stream, mesh.mNormals, mesh.mNumVertices ); + + } else { + + // else write as usual + + mesh.mNormals = []; + mesh.mNormalBuffer = stream.subArray32( stream.readOffset, stream.readOffset + mesh.mNumVertices * 3 * 4 ); + stream.Seek( mesh.mNumVertices * 3 * 4, aiOrigin_CUR ); + + } + + } + + if ( c & ASSBIN_MESH_HAS_TANGENTS_AND_BITANGENTS ) { + + if ( shortened ) { + + ReadBounds( stream, mesh.mTangents, mesh.mNumVertices ); + ReadBounds( stream, mesh.mBitangents, mesh.mNumVertices ); + + } else { + + // else write as usual + + mesh.mTangents = []; + mesh.mTangentBuffer = stream.subArray32( stream.readOffset, stream.readOffset + mesh.mNumVertices * 3 * 4 ); + stream.Seek( mesh.mNumVertices * 3 * 4, aiOrigin_CUR ); + mesh.mBitangents = []; + mesh.mBitangentBuffer = stream.subArray32( stream.readOffset, stream.readOffset + mesh.mNumVertices * 3 * 4 ); + stream.Seek( mesh.mNumVertices * 3 * 4, aiOrigin_CUR ); + + } + + } + + for ( var n = 0; n < AI_MAX_NUMBER_OF_COLOR_SETS; ++ n ) { + + if ( ! ( c & ASSBIN_MESH_HAS_COLOR( n ) ) ) break; + + if ( shortened ) { + + ReadBounds( stream, mesh.mColors[ n ], mesh.mNumVertices ); + + } else { + + // else write as usual + + mesh.mColors[ n ] = []; + mesh.mColorBuffer = stream.subArray32( stream.readOffset, stream.readOffset + mesh.mNumVertices * 4 * 4 ); + stream.Seek( mesh.mNumVertices * 4 * 4, aiOrigin_CUR ); + + } + + } + + mesh.mTexCoordsBuffers = []; + + for ( var n = 0; n < AI_MAX_NUMBER_OF_TEXTURECOORDS; ++ n ) { + + if ( ! ( c & ASSBIN_MESH_HAS_TEXCOORD( n ) ) ) break; + + // write number of UV components + mesh.mNumUVComponents[ n ] = Read_unsigned_int( stream ); + + if ( shortened ) { + + ReadBounds( stream, mesh.mTextureCoords[ n ], mesh.mNumVertices ); + + } else { + + // else write as usual + + mesh.mTextureCoords[ n ] = []; + //note that assbin always writes 3d texcoords + mesh.mTexCoordsBuffers[ n ] = []; + + for ( var uv = 0; uv < mesh.mNumVertices; uv ++ ) { + + mesh.mTexCoordsBuffers[ n ].push( readFloat( stream ) ); + mesh.mTexCoordsBuffers[ n ].push( readFloat( stream ) ); + readFloat( stream ); + + } + + } + + } + // write faces. There are no floating-point calculations involved + // in these, so we can write a simple hash over the face data + // to the dump file. We generate a single 32 Bit hash for 512 faces + // using Assimp's standard hashing function. + if ( shortened ) { + + Read_unsigned_int( stream ); + + } else { + + // else write as usual + + // if there are less than 2^16 vertices, we can simply use 16 bit integers ... + mesh.mFaces = []; + + var indexCounter = 0; + mesh.mIndexArray = []; + + for ( var i = 0; i < mesh.mNumFaces; ++ i ) { + + var f = mesh.mFaces[ i ] = new aiFace(); + // BOOST_STATIC_ASSERT(AI_MAX_FACE_INDICES <= 0xffff); + f.mNumIndices = Read_uint16_t( stream ); + f.mIndices = []; + + for ( var a = 0; a < f.mNumIndices; ++ a ) { + + if ( mesh.mNumVertices < ( 1 << 16 ) ) { + + f.mIndices[ a ] = Read_uint16_t( stream ); + + } else { + + f.mIndices[ a ] = Read_unsigned_int( stream ); + + } + + + + } + + if ( f.mNumIndices === 3 ) { + + mesh.mIndexArray.push( f.mIndices[ 0 ] ); + mesh.mIndexArray.push( f.mIndices[ 1 ] ); + mesh.mIndexArray.push( f.mIndices[ 2 ] ); + + } else if ( f.mNumIndices === 4 ) { + + mesh.mIndexArray.push( f.mIndices[ 0 ] ); + mesh.mIndexArray.push( f.mIndices[ 1 ] ); + mesh.mIndexArray.push( f.mIndices[ 2 ] ); + mesh.mIndexArray.push( f.mIndices[ 2 ] ); + mesh.mIndexArray.push( f.mIndices[ 3 ] ); + mesh.mIndexArray.push( f.mIndices[ 0 ] ); + + } else { + + throw ( new Error( "Sorry, can't currently triangulate polys. Use the triangulate preprocessor in Assimp." ) ); + + } + + + + } + + } + // write bones + if ( mesh.mNumBones ) { + + mesh.mBones = []; + + for ( var a = 0; a < mesh.mNumBones; ++ a ) { + + mesh.mBones[ a ] = new aiBone(); + ReadBinaryBone( stream, mesh.mBones[ a ] ); + + } + + } + + } + + function ReadBinaryMaterialProperty( stream, prop ) { + + var chunkID = Read_uint32_t( stream ); + ai_assert( chunkID == ASSBIN_CHUNK_AIMATERIALPROPERTY ); + /*uint32_t size =*/ + Read_uint32_t( stream ); + prop.mKey = Read_aiString( stream ); + prop.mSemantic = Read_unsigned_int( stream ); + prop.mIndex = Read_unsigned_int( stream ); + prop.mDataLength = Read_unsigned_int( stream ); + prop.mType = Read_unsigned_int( stream ); + prop.mData = []; + stream.ReadBytes( prop.mData, 1, prop.mDataLength ); + + } + + // ----------------------------------------------------------------------------------- + + function ReadBinaryMaterial( stream, mat ) { + + var chunkID = Read_uint32_t( stream ); + ai_assert( chunkID == ASSBIN_CHUNK_AIMATERIAL ); + /*uint32_t size =*/ + Read_uint32_t( stream ); + mat.mNumAllocated = mat.mNumProperties = Read_unsigned_int( stream ); + + if ( mat.mNumProperties ) { + + if ( mat.mProperties ) { + + delete mat.mProperties; + + } + + mat.mProperties = []; + + for ( var i = 0; i < mat.mNumProperties; ++ i ) { + + mat.mProperties[ i ] = new aiMaterialProperty(); + ReadBinaryMaterialProperty( stream, mat.mProperties[ i ] ); + + } + + } + + } + // ----------------------------------------------------------------------------------- + function ReadBinaryNodeAnim( stream, nd ) { + + var chunkID = Read_uint32_t( stream ); + ai_assert( chunkID == ASSBIN_CHUNK_AINODEANIM ); + /*uint32_t size =*/ + Read_uint32_t( stream ); + nd.mNodeName = Read_aiString( stream ); + nd.mNumPositionKeys = Read_unsigned_int( stream ); + nd.mNumRotationKeys = Read_unsigned_int( stream ); + nd.mNumScalingKeys = Read_unsigned_int( stream ); + nd.mPreState = Read_unsigned_int( stream ); + nd.mPostState = Read_unsigned_int( stream ); + + if ( nd.mNumPositionKeys ) { + + if ( shortened ) { + + ReadBounds( stream, nd.mPositionKeys, nd.mNumPositionKeys ); + + } else { + + // else write as usual + + nd.mPositionKeys = []; + ReadArray_aiVectorKey( stream, nd.mPositionKeys, nd.mNumPositionKeys ); + + } + + } + + if ( nd.mNumRotationKeys ) { + + if ( shortened ) { + + ReadBounds( stream, nd.mRotationKeys, nd.mNumRotationKeys ); + + } else { + + // else write as usual + + nd.mRotationKeys = []; + ReadArray_aiQuatKey( stream, nd.mRotationKeys, nd.mNumRotationKeys ); + + } + + } + + if ( nd.mNumScalingKeys ) { + + if ( shortened ) { + + ReadBounds( stream, nd.mScalingKeys, nd.mNumScalingKeys ); + + } else { + + // else write as usual + + nd.mScalingKeys = []; + ReadArray_aiVectorKey( stream, nd.mScalingKeys, nd.mNumScalingKeys ); + + } + + } + + } + // ----------------------------------------------------------------------------------- + function ReadBinaryAnim( stream, anim ) { + + var chunkID = Read_uint32_t( stream ); + ai_assert( chunkID == ASSBIN_CHUNK_AIANIMATION ); + /*uint32_t size =*/ + Read_uint32_t( stream ); + anim.mName = Read_aiString( stream ); + anim.mDuration = Read_double( stream ); + anim.mTicksPerSecond = Read_double( stream ); + anim.mNumChannels = Read_unsigned_int( stream ); + + if ( anim.mNumChannels ) { + + anim.mChannels = []; + + for ( var a = 0; a < anim.mNumChannels; ++ a ) { + + anim.mChannels[ a ] = new aiNodeAnim(); + ReadBinaryNodeAnim( stream, anim.mChannels[ a ] ); + + } + + } + + } + + function ReadBinaryTexture( stream, tex ) { + + var chunkID = Read_uint32_t( stream ); + ai_assert( chunkID == ASSBIN_CHUNK_AITEXTURE ); + /*uint32_t size =*/ + Read_uint32_t( stream ); + tex.mWidth = Read_unsigned_int( stream ); + tex.mHeight = Read_unsigned_int( stream ); + stream.ReadBytes( tex.achFormatHint, 1, 4 ); + + if ( ! shortened ) { + + if ( ! tex.mHeight ) { + + tex.pcData = []; + stream.ReadBytes( tex.pcData, 1, tex.mWidth ); + + } else { + + tex.pcData = []; + stream.ReadBytes( tex.pcData, 1, tex.mWidth * tex.mHeight * 4 ); + + } + + } + + } + // ----------------------------------------------------------------------------------- + function ReadBinaryLight( stream, l ) { + + var chunkID = Read_uint32_t( stream ); + ai_assert( chunkID == ASSBIN_CHUNK_AILIGHT ); + /*uint32_t size =*/ + Read_uint32_t( stream ); + l.mName = Read_aiString( stream ); + l.mType = Read_unsigned_int( stream ); + + if ( l.mType != aiLightSource_DIRECTIONAL ) { + + l.mAttenuationConstant = readFloat( stream ); + l.mAttenuationLinear = readFloat( stream ); + l.mAttenuationQuadratic = readFloat( stream ); + + } + + l.mColorDiffuse = Read_aiColor3D( stream ); + l.mColorSpecular = Read_aiColor3D( stream ); + l.mColorAmbient = Read_aiColor3D( stream ); + + if ( l.mType == aiLightSource_SPOT ) { + + l.mAngleInnerCone = readFloat( stream ); + l.mAngleOuterCone = readFloat( stream ); + + } + + } + // ----------------------------------------------------------------------------------- + function ReadBinaryCamera( stream, cam ) { + + var chunkID = Read_uint32_t( stream ); + ai_assert( chunkID == ASSBIN_CHUNK_AICAMERA ); + /*uint32_t size =*/ + Read_uint32_t( stream ); + cam.mName = Read_aiString( stream ); + cam.mPosition = Read_aiVector3D( stream ); + cam.mLookAt = Read_aiVector3D( stream ); + cam.mUp = Read_aiVector3D( stream ); + cam.mHorizontalFOV = readFloat( stream ); + cam.mClipPlaneNear = readFloat( stream ); + cam.mClipPlaneFar = readFloat( stream ); + cam.mAspect = readFloat( stream ); + + } + + function ReadBinaryScene( stream, scene ) { + + var chunkID = Read_uint32_t( stream ); + ai_assert( chunkID == ASSBIN_CHUNK_AISCENE ); + /*uint32_t size =*/ + Read_uint32_t( stream ); + scene.mFlags = Read_unsigned_int( stream ); + scene.mNumMeshes = Read_unsigned_int( stream ); + scene.mNumMaterials = Read_unsigned_int( stream ); + scene.mNumAnimations = Read_unsigned_int( stream ); + scene.mNumTextures = Read_unsigned_int( stream ); + scene.mNumLights = Read_unsigned_int( stream ); + scene.mNumCameras = Read_unsigned_int( stream ); + // Read node graph + scene.mRootNode = new aiNode(); + scene.mRootNode = ReadBinaryNode( stream, null, 0 ); + // Read all meshes + if ( scene.mNumMeshes ) { + + scene.mMeshes = []; + + for ( var i = 0; i < scene.mNumMeshes; ++ i ) { + + scene.mMeshes[ i ] = new aiMesh(); + ReadBinaryMesh( stream, scene.mMeshes[ i ] ); + + } + + } + // Read materials + if ( scene.mNumMaterials ) { + + scene.mMaterials = []; + + for ( var i = 0; i < scene.mNumMaterials; ++ i ) { + + scene.mMaterials[ i ] = new aiMaterial(); + ReadBinaryMaterial( stream, scene.mMaterials[ i ] ); + + } + + } + // Read all animations + if ( scene.mNumAnimations ) { + + scene.mAnimations = []; + + for ( var i = 0; i < scene.mNumAnimations; ++ i ) { + + scene.mAnimations[ i ] = new aiAnimation(); + ReadBinaryAnim( stream, scene.mAnimations[ i ] ); + + } + + } + // Read all textures + if ( scene.mNumTextures ) { + + scene.mTextures = []; + + for ( var i = 0; i < scene.mNumTextures; ++ i ) { + + scene.mTextures[ i ] = new aiTexture(); + ReadBinaryTexture( stream, scene.mTextures[ i ] ); + + } + + } + // Read lights + if ( scene.mNumLights ) { + + scene.mLights = []; + + for ( var i = 0; i < scene.mNumLights; ++ i ) { + + scene.mLights[ i ] = new aiLight(); + ReadBinaryLight( stream, scene.mLights[ i ] ); + + } + + } + // Read cameras + if ( scene.mNumCameras ) { + + scene.mCameras = []; + + for ( var i = 0; i < scene.mNumCameras; ++ i ) { + + scene.mCameras[ i ] = new aiCamera(); + ReadBinaryCamera( stream, scene.mCameras[ i ] ); + + } + + } + + } + var aiOrigin_CUR = 0; + var aiOrigin_BEG = 1; + + function extendStream( stream ) { + + stream.readOffset = 0; + stream.Seek = function ( off, ori ) { + + if ( ori == aiOrigin_CUR ) { + + stream.readOffset += off; + + } + if ( ori == aiOrigin_BEG ) { + + stream.readOffset = off; + + } + + }; + + stream.ReadBytes = function ( buff, size, n ) { + + var bytes = size * n; + for ( var i = 0; i < bytes; i ++ ) + buff[ i ] = Read_uint8_t( this ); + + }; + + stream.subArray32 = function ( start, end ) { + + var buff = this.buffer; + var newbuff = buff.slice( start, end ); + return new Float32Array( newbuff ); + + }; + + stream.subArrayUint16 = function ( start, end ) { + + var buff = this.buffer; + var newbuff = buff.slice( start, end ); + return new Uint16Array( newbuff ); + + }; + + stream.subArrayUint8 = function ( start, end ) { + + var buff = this.buffer; + var newbuff = buff.slice( start, end ); + return new Uint8Array( newbuff ); + + }; + + stream.subArrayUint32 = function ( start, end ) { + + var buff = this.buffer; + var newbuff = buff.slice( start, end ); + return new Uint32Array( newbuff ); + + }; + + } + + var shortened, compressed; + + function InternReadFile( pFiledata ) { + + var pScene = new aiScene(); + var stream = new DataView( pFiledata ); + extendStream( stream ); + stream.Seek( 44, aiOrigin_CUR ); // signature + /*unsigned int versionMajor =*/ + var versionMajor = Read_unsigned_int( stream ); + /*unsigned int versionMinor =*/ + var versionMinor = Read_unsigned_int( stream ); + /*unsigned int versionRevision =*/ + var versionRevision = Read_unsigned_int( stream ); + /*unsigned int compileFlags =*/ + var compileFlags = Read_unsigned_int( stream ); + shortened = Read_uint16_t( stream ) > 0; + compressed = Read_uint16_t( stream ) > 0; + if ( shortened ) + throw "Shortened binaries are not supported!"; + stream.Seek( 256, aiOrigin_CUR ); // original filename + stream.Seek( 128, aiOrigin_CUR ); // options + stream.Seek( 64, aiOrigin_CUR ); // padding + if ( compressed ) { + + var uncompressedSize = Read_uint32_t( stream ); + var compressedSize = stream.FileSize() - stream.Tell(); + var compressedData = []; + stream.Read( compressedData, 1, compressedSize ); + var uncompressedData = []; + uncompress( uncompressedData, uncompressedSize, compressedData, compressedSize ); + var buff = new ArrayBuffer( uncompressedData ); + ReadBinaryScene( buff, pScene ); + + } else { + + ReadBinaryScene( stream, pScene ); + return pScene.toTHREE(); + + } + + } + + return InternReadFile( buffer ); + + } + +}; + +export { AssimpLoader }; diff --git a/examples/jsm/loaders/BVHLoader.js b/examples/jsm/loaders/BVHLoader.js new file mode 100644 index 00000000000000..e56c5183f96292 --- /dev/null +++ b/examples/jsm/loaders/BVHLoader.js @@ -0,0 +1,428 @@ +/** + * @author herzig / http://github.com/herzig + * @author Mugen87 / https://github.com/Mugen87 + * + * Description: reads BVH files and outputs a single THREE.Skeleton and an THREE.AnimationClip + * + * Currently only supports bvh files containing a single root. + * + */ + +import { + AnimationClip, + Bone, + DefaultLoadingManager, + FileLoader, + Quaternion, + QuaternionKeyframeTrack, + Skeleton, + Vector3, + VectorKeyframeTrack +} from "../../../build/three.module.js"; + +var BVHLoader = function ( manager ) { + + this.manager = ( manager !== undefined ) ? manager : DefaultLoadingManager; + + this.animateBonePositions = true; + this.animateBoneRotations = true; + +}; + +BVHLoader.prototype = { + + constructor: BVHLoader, + + load: function ( url, onLoad, onProgress, onError ) { + + var scope = this; + + var loader = new FileLoader( scope.manager ); + loader.setPath( scope.path ); + loader.load( url, function ( text ) { + + onLoad( scope.parse( text ) ); + + }, onProgress, onError ); + + }, + + setPath: function ( value ) { + + this.path = value; + return this; + + }, + + parse: function ( text ) { + + /* + reads a string array (lines) from a BVH file + and outputs a skeleton structure including motion data + + returns thee root node: + { name: '', channels: [], children: [] } + */ + function readBvh( lines ) { + + // read model structure + + if ( nextLine( lines ) !== 'HIERARCHY' ) { + + console.error( 'THREE.BVHLoader: HIERARCHY expected.' ); + + } + + var list = []; // collects flat array of all bones + var root = readNode( lines, nextLine( lines ), list ); + + // read motion data + + if ( nextLine( lines ) !== 'MOTION' ) { + + console.error( 'THREE.BVHLoader: MOTION expected.' ); + + } + + // number of frames + + var tokens = nextLine( lines ).split( /[\s]+/ ); + var numFrames = parseInt( tokens[ 1 ] ); + + if ( isNaN( numFrames ) ) { + + console.error( 'THREE.BVHLoader: Failed to read number of frames.' ); + + } + + // frame time + + tokens = nextLine( lines ).split( /[\s]+/ ); + var frameTime = parseFloat( tokens[ 2 ] ); + + if ( isNaN( frameTime ) ) { + + console.error( 'THREE.BVHLoader: Failed to read frame time.' ); + + } + + // read frame data line by line + + for ( var i = 0; i < numFrames; i ++ ) { + + tokens = nextLine( lines ).split( /[\s]+/ ); + readFrameData( tokens, i * frameTime, root ); + + } + + return list; + + } + + /* + Recursively reads data from a single frame into the bone hierarchy. + The passed bone hierarchy has to be structured in the same order as the BVH file. + keyframe data is stored in bone.frames. + + - data: splitted string array (frame values), values are shift()ed so + this should be empty after parsing the whole hierarchy. + - frameTime: playback time for this keyframe. + - bone: the bone to read frame data from. + */ + function readFrameData( data, frameTime, bone ) { + + // end sites have no motion data + + if ( bone.type === 'ENDSITE' ) return; + + // add keyframe + + var keyframe = { + time: frameTime, + position: new Vector3(), + rotation: new Quaternion() + }; + + bone.frames.push( keyframe ); + + var quat = new Quaternion(); + + var vx = new Vector3( 1, 0, 0 ); + var vy = new Vector3( 0, 1, 0 ); + var vz = new Vector3( 0, 0, 1 ); + + // parse values for each channel in node + + for ( var i = 0; i < bone.channels.length; i ++ ) { + + switch ( bone.channels[ i ] ) { + + case 'Xposition': + keyframe.position.x = parseFloat( data.shift().trim() ); + break; + case 'Yposition': + keyframe.position.y = parseFloat( data.shift().trim() ); + break; + case 'Zposition': + keyframe.position.z = parseFloat( data.shift().trim() ); + break; + case 'Xrotation': + quat.setFromAxisAngle( vx, parseFloat( data.shift().trim() ) * Math.PI / 180 ); + keyframe.rotation.multiply( quat ); + break; + case 'Yrotation': + quat.setFromAxisAngle( vy, parseFloat( data.shift().trim() ) * Math.PI / 180 ); + keyframe.rotation.multiply( quat ); + break; + case 'Zrotation': + quat.setFromAxisAngle( vz, parseFloat( data.shift().trim() ) * Math.PI / 180 ); + keyframe.rotation.multiply( quat ); + break; + default: + console.warn( 'THREE.BVHLoader: Invalid channel type.' ); + + } + + } + + // parse child nodes + + for ( var i = 0; i < bone.children.length; i ++ ) { + + readFrameData( data, frameTime, bone.children[ i ] ); + + } + + } + + /* + Recursively parses the HIERACHY section of the BVH file + + - lines: all lines of the file. lines are consumed as we go along. + - firstline: line containing the node type and name e.g. 'JOINT hip' + - list: collects a flat list of nodes + + returns: a BVH node including children + */ + function readNode( lines, firstline, list ) { + + var node = { name: '', type: '', frames: [] }; + list.push( node ); + + // parse node type and name + + var tokens = firstline.split( /[\s]+/ ); + + if ( tokens[ 0 ].toUpperCase() === 'END' && tokens[ 1 ].toUpperCase() === 'SITE' ) { + + node.type = 'ENDSITE'; + node.name = 'ENDSITE'; // bvh end sites have no name + + } else { + + node.name = tokens[ 1 ]; + node.type = tokens[ 0 ].toUpperCase(); + + } + + if ( nextLine( lines ) !== '{' ) { + + console.error( 'THREE.BVHLoader: Expected opening { after type & name' ); + + } + + // parse OFFSET + + tokens = nextLine( lines ).split( /[\s]+/ ); + + if ( tokens[ 0 ] !== 'OFFSET' ) { + + console.error( 'THREE.BVHLoader: Expected OFFSET but got: ' + tokens[ 0 ] ); + + } + + if ( tokens.length !== 4 ) { + + console.error( 'THREE.BVHLoader: Invalid number of values for OFFSET.' ); + + } + + var offset = new Vector3( + parseFloat( tokens[ 1 ] ), + parseFloat( tokens[ 2 ] ), + parseFloat( tokens[ 3 ] ) + ); + + if ( isNaN( offset.x ) || isNaN( offset.y ) || isNaN( offset.z ) ) { + + console.error( 'THREE.BVHLoader: Invalid values of OFFSET.' ); + + } + + node.offset = offset; + + // parse CHANNELS definitions + + if ( node.type !== 'ENDSITE' ) { + + tokens = nextLine( lines ).split( /[\s]+/ ); + + if ( tokens[ 0 ] !== 'CHANNELS' ) { + + console.error( 'THREE.BVHLoader: Expected CHANNELS definition.' ); + + } + + var numChannels = parseInt( tokens[ 1 ] ); + node.channels = tokens.splice( 2, numChannels ); + node.children = []; + + } + + // read children + + while ( true ) { + + var line = nextLine( lines ); + + if ( line === '}' ) { + + return node; + + } else { + + node.children.push( readNode( lines, line, list ) ); + + } + + } + + } + + /* + recursively converts the internal bvh node structure to a THREE.Bone hierarchy + + source: the bvh root node + list: pass an empty array, collects a flat list of all converted THREE.Bones + + returns the root THREE.Bone + */ + function toTHREEBone( source, list ) { + + var bone = new Bone(); + list.push( bone ); + + bone.position.add( source.offset ); + bone.name = source.name; + + if ( source.type !== 'ENDSITE' ) { + + for ( var i = 0; i < source.children.length; i ++ ) { + + bone.add( toTHREEBone( source.children[ i ], list ) ); + + } + + } + + return bone; + + } + + /* + builds a THREE.AnimationClip from the keyframe data saved in each bone. + + bone: bvh root node + + returns: a THREE.AnimationClip containing position and quaternion tracks + */ + function toTHREEAnimation( bones ) { + + var tracks = []; + + // create a position and quaternion animation track for each node + + for ( var i = 0; i < bones.length; i ++ ) { + + var bone = bones[ i ]; + + if ( bone.type === 'ENDSITE' ) + continue; + + // track data + + var times = []; + var positions = []; + var rotations = []; + + for ( var j = 0; j < bone.frames.length; j ++ ) { + + var frame = bone.frames[ j ]; + + times.push( frame.time ); + + // the animation system animates the position property, + // so we have to add the joint offset to all values + + positions.push( frame.position.x + bone.offset.x ); + positions.push( frame.position.y + bone.offset.y ); + positions.push( frame.position.z + bone.offset.z ); + + rotations.push( frame.rotation.x ); + rotations.push( frame.rotation.y ); + rotations.push( frame.rotation.z ); + rotations.push( frame.rotation.w ); + + } + + if ( scope.animateBonePositions ) { + + tracks.push( new VectorKeyframeTrack( '.bones[' + bone.name + '].position', times, positions ) ); + + } + + if ( scope.animateBoneRotations ) { + + tracks.push( new QuaternionKeyframeTrack( '.bones[' + bone.name + '].quaternion', times, rotations ) ); + + } + + } + + return new AnimationClip( 'animation', - 1, tracks ); + + } + + /* + returns the next non-empty line in lines + */ + function nextLine( lines ) { + + var line; + // skip empty lines + while ( ( line = lines.shift().trim() ).length === 0 ) { } + return line; + + } + + var scope = this; + + var lines = text.split( /[\r\n]+/g ); + + var bones = readBvh( lines ); + + var threeBones = []; + toTHREEBone( bones[ 0 ], threeBones ); + + var threeClip = toTHREEAnimation( bones ); + + return { + skeleton: new Skeleton( threeBones ), + clip: threeClip + }; + + } + +}; + +export { BVHLoader }; diff --git a/examples/jsm/loaders/BabylonLoader.js b/examples/jsm/loaders/BabylonLoader.js new file mode 100644 index 00000000000000..07ab0acdda4735 --- /dev/null +++ b/examples/jsm/loaders/BabylonLoader.js @@ -0,0 +1,272 @@ +/** + * @author mrdoob / http://mrdoob.com/ + * @author Mugen87 / https://github.com/Mugen87 + */ + +import { + BufferGeometry, + DefaultLoadingManager, + DirectionalLight, + FileLoader, + Float32BufferAttribute, + Group, + HemisphereLight, + MeshPhongMaterial, + PerspectiveCamera, + PointLight, + Scene, + SpotLight, +} from "../../../build/three.module.js"; + +var BabylonLoader = function ( manager ) { + + this.manager = ( manager !== undefined ) ? manager : DefaultLoadingManager; + +}; + +BabylonLoader.prototype = { + + constructor: BabylonLoader, + + load: function ( url, onLoad, onProgress, onError ) { + + var scope = this; + + var loader = new FileLoader( scope.manager ); + loader.setPath( scope.path ); + loader.load( url, function ( text ) { + + onLoad( scope.parse( JSON.parse( text ) ) ); + + }, onProgress, onError ); + + }, + + setPath: function ( value ) { + + this.path = value; + return this; + + }, + + parse: function ( json ) { + + function parseMaterials( json ) { + + var materials = {}; + + for ( var i = 0, l = json.materials.length; i < l; i ++ ) { + + var data = json.materials[ i ]; + + var material = new MeshPhongMaterial(); + material.name = data.name; + material.color.fromArray( data.diffuse ); + material.emissive.fromArray( data.emissive ); + material.specular.fromArray( data.specular ); + material.shininess = data.specularPower; + material.opacity = data.alpha; + + materials[ data.id ] = material; + + } + + if ( json.multiMaterials ) { + + for ( var i = 0, l = json.multiMaterials.length; i < l; i ++ ) { + + var data = json.multiMaterials[ i ]; + + console.warn( 'THREE.BabylonLoader: Multi materials not yet supported.' ); + + materials[ data.id ] = new MeshPhongMaterial(); + + } + + } + + return materials; + + } + + function parseGeometry( json ) { + + var geometry = new BufferGeometry(); + + var indices = json.indices; + var positions = json.positions; + var normals = json.normals; + var uvs = json.uvs; + + // indices + + geometry.setIndex( indices ); + + // positions + + for ( var j = 2, jl = positions.length; j < jl; j += 3 ) { + + positions[ j ] = - positions[ j ]; + + } + + geometry.addAttribute( 'position', new Float32BufferAttribute( positions, 3 ) ); + + // normals + + if ( normals ) { + + for ( var j = 2, jl = normals.length; j < jl; j += 3 ) { + + normals[ j ] = - normals[ j ]; + + } + + geometry.addAttribute( 'normal', new Float32BufferAttribute( normals, 3 ) ); + + } + + // uvs + + if ( uvs ) { + + geometry.addAttribute( 'uv', new Float32BufferAttribute( uvs, 2 ) ); + + } + + // offsets + + var subMeshes = json.subMeshes; + + if ( subMeshes ) { + + for ( var j = 0, jl = subMeshes.length; j < jl; j ++ ) { + + var subMesh = subMeshes[ j ]; + + geometry.addGroup( subMesh.indexStart, subMesh.indexCount ); + + } + + } + + return geometry; + + } + + function parseObjects( json, materials ) { + + var objects = {}; + var scene = new Scene(); + + var cameras = json.cameras; + + for ( var i = 0, l = cameras.length; i < l; i ++ ) { + + var data = cameras[ i ]; + + var camera = new PerspectiveCamera( ( data.fov / Math.PI ) * 180, 1.33, data.minZ, data.maxZ ); + + camera.name = data.name; + camera.position.fromArray( data.position ); + if ( data.rotation ) camera.rotation.fromArray( data.rotation ); + + objects[ data.id ] = camera; + + } + + var lights = json.lights; + + for ( var i = 0, l = lights.length; i < l; i ++ ) { + + var data = lights[ i ]; + + var light; + + switch ( data.type ) { + + case 0: + light = new PointLight(); + break; + + case 1: + light = new DirectionalLight(); + break; + + case 2: + light = new SpotLight(); + break; + + case 3: + light = new HemisphereLight(); + break; + + } + + light.name = data.name; + if ( data.position ) light.position.set( data.position[ 0 ], data.position[ 1 ], - data.position[ 2 ] ); + light.color.fromArray( data.diffuse ); + if ( data.groundColor ) light.groundColor.fromArray( data.groundColor ); + if ( data.intensity ) light.intensity = data.intensity; + + objects[ data.id ] = light; + + scene.add( light ); + + } + + var meshes = json.meshes; + + for ( var i = 0, l = meshes.length; i < l; i ++ ) { + + var data = meshes[ i ]; + + var object; + + if ( data.indices ) { + + var geometry = parseGeometry( data ); + + object = new Mesh( geometry, materials[ data.materialId ] ); + + } else { + + object = new Group(); + + } + + object.name = data.name; + object.position.set( data.position[ 0 ], data.position[ 1 ], - data.position[ 2 ] ); + object.rotation.fromArray( data.rotation ); + if ( data.rotationQuaternion ) object.quaternion.fromArray( data.rotationQuaternion ); + object.scale.fromArray( data.scaling ); + // object.visible = data.isVisible; + + if ( data.parentId ) { + + objects[ data.parentId ].add( object ); + + } else { + + scene.add( object ); + + } + + objects[ data.id ] = object; + + } + + return scene; + + } + + var materials = parseMaterials( json ); + var scene = parseObjects( json, materials ); + + return scene; + + } + +}; + +export { BabylonLoader }; diff --git a/examples/jsm/loaders/ColladaLoader.js b/examples/jsm/loaders/ColladaLoader.js new file mode 100644 index 00000000000000..36f3653c9521a1 --- /dev/null +++ b/examples/jsm/loaders/ColladaLoader.js @@ -0,0 +1,3959 @@ +/** + * @author mrdoob / http://mrdoob.com/ + * @author Mugen87 / https://github.com/Mugen87 + */ + +import { + AmbientLight, + AnimationClip, + Bone, + BufferGeometry, + ClampToEdgeWrapping, + Color, + DefaultLoadingManager, + DirectionalLight, + DoubleSide, + Euler, + FileLoader, + Float32BufferAttribute, + Group, + Line, + LineBasicMaterial, + LineSegments, + LoaderUtils, + Math, + Matrix4, + Mesh, + MeshBasicMaterial, + MeshLambertMaterial, + MeshPhongMaterial, + OrthographicCamera, + PerspectiveCamera, + PointLight, + Quaternion, + QuaternionKeyframeTrack, + RepeatWrapping, + Scene, + Skeleton, + SkinnedMesh, + SpotLight, + TextureLoader, + Vector3, + VectorKeyframeTrack, +} from "../../../build/three.module.js"; + +var ColladaLoader = function ( manager ) { + + this.manager = ( manager !== undefined ) ? manager : DefaultLoadingManager; + +}; + +ColladaLoader.prototype = { + + constructor: ColladaLoader, + + crossOrigin: 'anonymous', + + load: function ( url, onLoad, onProgress, onError ) { + + var scope = this; + + var path = ( scope.path === undefined ) ? LoaderUtils.extractUrlBase( url ) : scope.path; + + var loader = new FileLoader( scope.manager ); + loader.setPath( scope.path ); + loader.load( url, function ( text ) { + + onLoad( scope.parse( text, path ) ); + + }, onProgress, onError ); + + }, + + setPath: function ( value ) { + + this.path = value; + return this; + + }, + + setResourcePath: function ( value ) { + + this.resourcePath = value; + return this; + + }, + + options: { + + set convertUpAxis( value ) { + + console.warn( 'THREE.ColladaLoader: options.convertUpAxis() has been removed. Up axis is converted automatically.' ); + + } + + }, + + setCrossOrigin: function ( value ) { + + this.crossOrigin = value; + return this; + + }, + + parse: function ( text, path ) { + + function getElementsByTagName( xml, name ) { + + // Non recursive xml.getElementsByTagName() ... + + var array = []; + var childNodes = xml.childNodes; + + for ( var i = 0, l = childNodes.length; i < l; i ++ ) { + + var child = childNodes[ i ]; + + if ( child.nodeName === name ) { + + array.push( child ); + + } + + } + + return array; + + } + + function parseStrings( text ) { + + if ( text.length === 0 ) return []; + + var parts = text.trim().split( /\s+/ ); + var array = new Array( parts.length ); + + for ( var i = 0, l = parts.length; i < l; i ++ ) { + + array[ i ] = parts[ i ]; + + } + + return array; + + } + + function parseFloats( text ) { + + if ( text.length === 0 ) return []; + + var parts = text.trim().split( /\s+/ ); + var array = new Array( parts.length ); + + for ( var i = 0, l = parts.length; i < l; i ++ ) { + + array[ i ] = parseFloat( parts[ i ] ); + + } + + return array; + + } + + function parseInts( text ) { + + if ( text.length === 0 ) return []; + + var parts = text.trim().split( /\s+/ ); + var array = new Array( parts.length ); + + for ( var i = 0, l = parts.length; i < l; i ++ ) { + + array[ i ] = parseInt( parts[ i ] ); + + } + + return array; + + } + + function parseId( text ) { + + return text.substring( 1 ); + + } + + function generateId() { + + return 'three_default_' + ( count ++ ); + + } + + function isEmpty( object ) { + + return Object.keys( object ).length === 0; + + } + + // asset + + function parseAsset( xml ) { + + return { + unit: parseAssetUnit( getElementsByTagName( xml, 'unit' )[ 0 ] ), + upAxis: parseAssetUpAxis( getElementsByTagName( xml, 'up_axis' )[ 0 ] ) + }; + + } + + function parseAssetUnit( xml ) { + + if ( ( xml !== undefined ) && ( xml.hasAttribute( 'meter' ) === true ) ) { + + return parseFloat( xml.getAttribute( 'meter' ) ); + + } else { + + return 1; // default 1 meter + + } + + } + + function parseAssetUpAxis( xml ) { + + return xml !== undefined ? xml.textContent : 'Y_UP'; + + } + + // library + + function parseLibrary( xml, libraryName, nodeName, parser ) { + + var library = getElementsByTagName( xml, libraryName )[ 0 ]; + + if ( library !== undefined ) { + + var elements = getElementsByTagName( library, nodeName ); + + for ( var i = 0; i < elements.length; i ++ ) { + + parser( elements[ i ] ); + + } + + } + + } + + function buildLibrary( data, builder ) { + + for ( var name in data ) { + + var object = data[ name ]; + object.build = builder( data[ name ] ); + + } + + } + + // get + + function getBuild( data, builder ) { + + if ( data.build !== undefined ) return data.build; + + data.build = builder( data ); + + return data.build; + + } + + // animation + + function parseAnimation( xml ) { + + var data = { + sources: {}, + samplers: {}, + channels: {} + }; + + for ( var i = 0, l = xml.childNodes.length; i < l; i ++ ) { + + var child = xml.childNodes[ i ]; + + if ( child.nodeType !== 1 ) continue; + + var id; + + switch ( child.nodeName ) { + + case 'source': + id = child.getAttribute( 'id' ); + data.sources[ id ] = parseSource( child ); + break; + + case 'sampler': + id = child.getAttribute( 'id' ); + data.samplers[ id ] = parseAnimationSampler( child ); + break; + + case 'channel': + id = child.getAttribute( 'target' ); + data.channels[ id ] = parseAnimationChannel( child ); + break; + + default: + console.log( child ); + + } + + } + + library.animations[ xml.getAttribute( 'id' ) ] = data; + + } + + function parseAnimationSampler( xml ) { + + var data = { + inputs: {}, + }; + + for ( var i = 0, l = xml.childNodes.length; i < l; i ++ ) { + + var child = xml.childNodes[ i ]; + + if ( child.nodeType !== 1 ) continue; + + switch ( child.nodeName ) { + + case 'input': + var id = parseId( child.getAttribute( 'source' ) ); + var semantic = child.getAttribute( 'semantic' ); + data.inputs[ semantic ] = id; + break; + + } + + } + + return data; + + } + + function parseAnimationChannel( xml ) { + + var data = {}; + + var target = xml.getAttribute( 'target' ); + + // parsing SID Addressing Syntax + + var parts = target.split( '/' ); + + var id = parts.shift(); + var sid = parts.shift(); + + // check selection syntax + + var arraySyntax = ( sid.indexOf( '(' ) !== - 1 ); + var memberSyntax = ( sid.indexOf( '.' ) !== - 1 ); + + if ( memberSyntax ) { + + // member selection access + + parts = sid.split( '.' ); + sid = parts.shift(); + data.member = parts.shift(); + + } else if ( arraySyntax ) { + + // array-access syntax. can be used to express fields in one-dimensional vectors or two-dimensional matrices. + + var indices = sid.split( '(' ); + sid = indices.shift(); + + for ( var i = 0; i < indices.length; i ++ ) { + + indices[ i ] = parseInt( indices[ i ].replace( /\)/, '' ) ); + + } + + data.indices = indices; + + } + + data.id = id; + data.sid = sid; + + data.arraySyntax = arraySyntax; + data.memberSyntax = memberSyntax; + + data.sampler = parseId( xml.getAttribute( 'source' ) ); + + return data; + + } + + function buildAnimation( data ) { + + var tracks = []; + + var channels = data.channels; + var samplers = data.samplers; + var sources = data.sources; + + for ( var target in channels ) { + + if ( channels.hasOwnProperty( target ) ) { + + var channel = channels[ target ]; + var sampler = samplers[ channel.sampler ]; + + var inputId = sampler.inputs.INPUT; + var outputId = sampler.inputs.OUTPUT; + + var inputSource = sources[ inputId ]; + var outputSource = sources[ outputId ]; + + var animation = buildAnimationChannel( channel, inputSource, outputSource ); + + createKeyframeTracks( animation, tracks ); + + } + + } + + return tracks; + + } + + function getAnimation( id ) { + + return getBuild( library.animations[ id ], buildAnimation ); + + } + + function buildAnimationChannel( channel, inputSource, outputSource ) { + + var node = library.nodes[ channel.id ]; + var object3D = getNode( node.id ); + + var transform = node.transforms[ channel.sid ]; + var defaultMatrix = node.matrix.clone().transpose(); + + var time, stride; + var i, il, j, jl; + + var data = {}; + + // the collada spec allows the animation of data in various ways. + // depending on the transform type (matrix, translate, rotate, scale), we execute different logic + + switch ( transform ) { + + case 'matrix': + + for ( i = 0, il = inputSource.array.length; i < il; i ++ ) { + + time = inputSource.array[ i ]; + stride = i * outputSource.stride; + + if ( data[ time ] === undefined ) data[ time ] = {}; + + if ( channel.arraySyntax === true ) { + + var value = outputSource.array[ stride ]; + var index = channel.indices[ 0 ] + 4 * channel.indices[ 1 ]; + + data[ time ][ index ] = value; + + } else { + + for ( j = 0, jl = outputSource.stride; j < jl; j ++ ) { + + data[ time ][ j ] = outputSource.array[ stride + j ]; + + } + + } + + } + + break; + + case 'translate': + console.warn( 'THREE.ColladaLoader: Animation transform type "%s" not yet implemented.', transform ); + break; + + case 'rotate': + console.warn( 'THREE.ColladaLoader: Animation transform type "%s" not yet implemented.', transform ); + break; + + case 'scale': + console.warn( 'THREE.ColladaLoader: Animation transform type "%s" not yet implemented.', transform ); + break; + + } + + var keyframes = prepareAnimationData( data, defaultMatrix ); + + var animation = { + name: object3D.uuid, + keyframes: keyframes + }; + + return animation; + + } + + function prepareAnimationData( data, defaultMatrix ) { + + var keyframes = []; + + // transfer data into a sortable array + + for ( var time in data ) { + + keyframes.push( { time: parseFloat( time ), value: data[ time ] } ); + + } + + // ensure keyframes are sorted by time + + keyframes.sort( ascending ); + + // now we clean up all animation data, so we can use them for keyframe tracks + + for ( var i = 0; i < 16; i ++ ) { + + transformAnimationData( keyframes, i, defaultMatrix.elements[ i ] ); + + } + + return keyframes; + + // array sort function + + function ascending( a, b ) { + + return a.time - b.time; + + } + + } + + var position = new Vector3(); + var scale = new Vector3(); + var quaternion = new Quaternion(); + + function createKeyframeTracks( animation, tracks ) { + + var keyframes = animation.keyframes; + var name = animation.name; + + var times = []; + var positionData = []; + var quaternionData = []; + var scaleData = []; + + for ( var i = 0, l = keyframes.length; i < l; i ++ ) { + + var keyframe = keyframes[ i ]; + + var time = keyframe.time; + var value = keyframe.value; + + matrix.fromArray( value ).transpose(); + matrix.decompose( position, quaternion, scale ); + + times.push( time ); + positionData.push( position.x, position.y, position.z ); + quaternionData.push( quaternion.x, quaternion.y, quaternion.z, quaternion.w ); + scaleData.push( scale.x, scale.y, scale.z ); + + } + + if ( positionData.length > 0 ) tracks.push( new VectorKeyframeTrack( name + '.position', times, positionData ) ); + if ( quaternionData.length > 0 ) tracks.push( new QuaternionKeyframeTrack( name + '.quaternion', times, quaternionData ) ); + if ( scaleData.length > 0 ) tracks.push( new VectorKeyframeTrack( name + '.scale', times, scaleData ) ); + + return tracks; + + } + + function transformAnimationData( keyframes, property, defaultValue ) { + + var keyframe; + + var empty = true; + var i, l; + + // check, if values of a property are missing in our keyframes + + for ( i = 0, l = keyframes.length; i < l; i ++ ) { + + keyframe = keyframes[ i ]; + + if ( keyframe.value[ property ] === undefined ) { + + keyframe.value[ property ] = null; // mark as missing + + } else { + + empty = false; + + } + + } + + if ( empty === true ) { + + // no values at all, so we set a default value + + for ( i = 0, l = keyframes.length; i < l; i ++ ) { + + keyframe = keyframes[ i ]; + + keyframe.value[ property ] = defaultValue; + + } + + } else { + + // filling gaps + + createMissingKeyframes( keyframes, property ); + + } + + } + + function createMissingKeyframes( keyframes, property ) { + + var prev, next; + + for ( var i = 0, l = keyframes.length; i < l; i ++ ) { + + var keyframe = keyframes[ i ]; + + if ( keyframe.value[ property ] === null ) { + + prev = getPrev( keyframes, i, property ); + next = getNext( keyframes, i, property ); + + if ( prev === null ) { + + keyframe.value[ property ] = next.value[ property ]; + continue; + + } + + if ( next === null ) { + + keyframe.value[ property ] = prev.value[ property ]; + continue; + + } + + interpolate( keyframe, prev, next, property ); + + } + + } + + } + + function getPrev( keyframes, i, property ) { + + while ( i >= 0 ) { + + var keyframe = keyframes[ i ]; + + if ( keyframe.value[ property ] !== null ) return keyframe; + + i --; + + } + + return null; + + } + + function getNext( keyframes, i, property ) { + + while ( i < keyframes.length ) { + + var keyframe = keyframes[ i ]; + + if ( keyframe.value[ property ] !== null ) return keyframe; + + i ++; + + } + + return null; + + } + + function interpolate( key, prev, next, property ) { + + if ( ( next.time - prev.time ) === 0 ) { + + key.value[ property ] = prev.value[ property ]; + return; + + } + + key.value[ property ] = ( ( key.time - prev.time ) * ( next.value[ property ] - prev.value[ property ] ) / ( next.time - prev.time ) ) + prev.value[ property ]; + + } + + // animation clips + + function parseAnimationClip( xml ) { + + var data = { + name: xml.getAttribute( 'id' ) || 'default', + start: parseFloat( xml.getAttribute( 'start' ) || 0 ), + end: parseFloat( xml.getAttribute( 'end' ) || 0 ), + animations: [] + }; + + for ( var i = 0, l = xml.childNodes.length; i < l; i ++ ) { + + var child = xml.childNodes[ i ]; + + if ( child.nodeType !== 1 ) continue; + + switch ( child.nodeName ) { + + case 'instance_animation': + data.animations.push( parseId( child.getAttribute( 'url' ) ) ); + break; + + } + + } + + library.clips[ xml.getAttribute( 'id' ) ] = data; + + } + + function buildAnimationClip( data ) { + + var tracks = []; + + var name = data.name; + var duration = ( data.end - data.start ) || - 1; + var animations = data.animations; + + for ( var i = 0, il = animations.length; i < il; i ++ ) { + + var animationTracks = getAnimation( animations[ i ] ); + + for ( var j = 0, jl = animationTracks.length; j < jl; j ++ ) { + + tracks.push( animationTracks[ j ] ); + + } + + } + + return new AnimationClip( name, duration, tracks ); + + } + + function getAnimationClip( id ) { + + return getBuild( library.clips[ id ], buildAnimationClip ); + + } + + // controller + + function parseController( xml ) { + + var data = {}; + + for ( var i = 0, l = xml.childNodes.length; i < l; i ++ ) { + + var child = xml.childNodes[ i ]; + + if ( child.nodeType !== 1 ) continue; + + switch ( child.nodeName ) { + + case 'skin': + // there is exactly one skin per controller + data.id = parseId( child.getAttribute( 'source' ) ); + data.skin = parseSkin( child ); + break; + + case 'morph': + data.id = parseId( child.getAttribute( 'source' ) ); + console.warn( 'THREE.ColladaLoader: Morph target animation not supported yet.' ); + break; + + } + + } + + library.controllers[ xml.getAttribute( 'id' ) ] = data; + + } + + function parseSkin( xml ) { + + var data = { + sources: {} + }; + + for ( var i = 0, l = xml.childNodes.length; i < l; i ++ ) { + + var child = xml.childNodes[ i ]; + + if ( child.nodeType !== 1 ) continue; + + switch ( child.nodeName ) { + + case 'bind_shape_matrix': + data.bindShapeMatrix = parseFloats( child.textContent ); + break; + + case 'source': + var id = child.getAttribute( 'id' ); + data.sources[ id ] = parseSource( child ); + break; + + case 'joints': + data.joints = parseJoints( child ); + break; + + case 'vertex_weights': + data.vertexWeights = parseVertexWeights( child ); + break; + + } + + } + + return data; + + } + + function parseJoints( xml ) { + + var data = { + inputs: {} + }; + + for ( var i = 0, l = xml.childNodes.length; i < l; i ++ ) { + + var child = xml.childNodes[ i ]; + + if ( child.nodeType !== 1 ) continue; + + switch ( child.nodeName ) { + + case 'input': + var semantic = child.getAttribute( 'semantic' ); + var id = parseId( child.getAttribute( 'source' ) ); + data.inputs[ semantic ] = id; + break; + + } + + } + + return data; + + } + + function parseVertexWeights( xml ) { + + var data = { + inputs: {} + }; + + for ( var i = 0, l = xml.childNodes.length; i < l; i ++ ) { + + var child = xml.childNodes[ i ]; + + if ( child.nodeType !== 1 ) continue; + + switch ( child.nodeName ) { + + case 'input': + var semantic = child.getAttribute( 'semantic' ); + var id = parseId( child.getAttribute( 'source' ) ); + var offset = parseInt( child.getAttribute( 'offset' ) ); + data.inputs[ semantic ] = { id: id, offset: offset }; + break; + + case 'vcount': + data.vcount = parseInts( child.textContent ); + break; + + case 'v': + data.v = parseInts( child.textContent ); + break; + + } + + } + + return data; + + } + + function buildController( data ) { + + var build = { + id: data.id + }; + + var geometry = library.geometries[ build.id ]; + + if ( data.skin !== undefined ) { + + build.skin = buildSkin( data.skin ); + + // we enhance the 'sources' property of the corresponding geometry with our skin data + + geometry.sources.skinIndices = build.skin.indices; + geometry.sources.skinWeights = build.skin.weights; + + } + + return build; + + } + + function buildSkin( data ) { + + var BONE_LIMIT = 4; + + var build = { + joints: [], // this must be an array to preserve the joint order + indices: { + array: [], + stride: BONE_LIMIT + }, + weights: { + array: [], + stride: BONE_LIMIT + } + }; + + var sources = data.sources; + var vertexWeights = data.vertexWeights; + + var vcount = vertexWeights.vcount; + var v = vertexWeights.v; + var jointOffset = vertexWeights.inputs.JOINT.offset; + var weightOffset = vertexWeights.inputs.WEIGHT.offset; + + var jointSource = data.sources[ data.joints.inputs.JOINT ]; + var inverseSource = data.sources[ data.joints.inputs.INV_BIND_MATRIX ]; + + var weights = sources[ vertexWeights.inputs.WEIGHT.id ].array; + var stride = 0; + + var i, j, l; + + // procces skin data for each vertex + + for ( i = 0, l = vcount.length; i < l; i ++ ) { + + var jointCount = vcount[ i ]; // this is the amount of joints that affect a single vertex + var vertexSkinData = []; + + for ( j = 0; j < jointCount; j ++ ) { + + var skinIndex = v[ stride + jointOffset ]; + var weightId = v[ stride + weightOffset ]; + var skinWeight = weights[ weightId ]; + + vertexSkinData.push( { index: skinIndex, weight: skinWeight } ); + + stride += 2; + + } + + // we sort the joints in descending order based on the weights. + // this ensures, we only procced the most important joints of the vertex + + vertexSkinData.sort( descending ); + + // now we provide for each vertex a set of four index and weight values. + // the order of the skin data matches the order of vertices + + for ( j = 0; j < BONE_LIMIT; j ++ ) { + + var d = vertexSkinData[ j ]; + + if ( d !== undefined ) { + + build.indices.array.push( d.index ); + build.weights.array.push( d.weight ); + + } else { + + build.indices.array.push( 0 ); + build.weights.array.push( 0 ); + + } + + } + + } + + // setup bind matrix + + if ( data.bindShapeMatrix ) { + + build.bindMatrix = new Matrix4().fromArray( data.bindShapeMatrix ).transpose(); + + } else { + + build.bindMatrix = new Matrix4().identity(); + + } + + // process bones and inverse bind matrix data + + for ( i = 0, l = jointSource.array.length; i < l; i ++ ) { + + var name = jointSource.array[ i ]; + var boneInverse = new Matrix4().fromArray( inverseSource.array, i * inverseSource.stride ).transpose(); + + build.joints.push( { name: name, boneInverse: boneInverse } ); + + } + + return build; + + // array sort function + + function descending( a, b ) { + + return b.weight - a.weight; + + } + + } + + function getController( id ) { + + return getBuild( library.controllers[ id ], buildController ); + + } + + // image + + function parseImage( xml ) { + + var data = { + init_from: getElementsByTagName( xml, 'init_from' )[ 0 ].textContent + }; + + library.images[ xml.getAttribute( 'id' ) ] = data; + + } + + function buildImage( data ) { + + if ( data.build !== undefined ) return data.build; + + return data.init_from; + + } + + function getImage( id ) { + + var data = library.images[ id ]; + + if ( data !== undefined ) { + + return getBuild( data, buildImage ); + + } + + console.warn( 'THREE.ColladaLoader: Couldn\'t find image with ID:', id ); + + return null; + + } + + // effect + + function parseEffect( xml ) { + + var data = {}; + + for ( var i = 0, l = xml.childNodes.length; i < l; i ++ ) { + + var child = xml.childNodes[ i ]; + + if ( child.nodeType !== 1 ) continue; + + switch ( child.nodeName ) { + + case 'profile_COMMON': + data.profile = parseEffectProfileCOMMON( child ); + break; + + } + + } + + library.effects[ xml.getAttribute( 'id' ) ] = data; + + } + + function parseEffectProfileCOMMON( xml ) { + + var data = { + surfaces: {}, + samplers: {} + }; + + for ( var i = 0, l = xml.childNodes.length; i < l; i ++ ) { + + var child = xml.childNodes[ i ]; + + if ( child.nodeType !== 1 ) continue; + + switch ( child.nodeName ) { + + case 'newparam': + parseEffectNewparam( child, data ); + break; + + case 'technique': + data.technique = parseEffectTechnique( child ); + break; + + case 'extra': + data.extra = parseEffectExtra( child ); + break; + + } + + } + + return data; + + } + + function parseEffectNewparam( xml, data ) { + + var sid = xml.getAttribute( 'sid' ); + + for ( var i = 0, l = xml.childNodes.length; i < l; i ++ ) { + + var child = xml.childNodes[ i ]; + + if ( child.nodeType !== 1 ) continue; + + switch ( child.nodeName ) { + + case 'surface': + data.surfaces[ sid ] = parseEffectSurface( child ); + break; + + case 'sampler2D': + data.samplers[ sid ] = parseEffectSampler( child ); + break; + + } + + } + + } + + function parseEffectSurface( xml ) { + + var data = {}; + + for ( var i = 0, l = xml.childNodes.length; i < l; i ++ ) { + + var child = xml.childNodes[ i ]; + + if ( child.nodeType !== 1 ) continue; + + switch ( child.nodeName ) { + + case 'init_from': + data.init_from = child.textContent; + break; + + } + + } + + return data; + + } + + function parseEffectSampler( xml ) { + + var data = {}; + + for ( var i = 0, l = xml.childNodes.length; i < l; i ++ ) { + + var child = xml.childNodes[ i ]; + + if ( child.nodeType !== 1 ) continue; + + switch ( child.nodeName ) { + + case 'source': + data.source = child.textContent; + break; + + } + + } + + return data; + + } + + function parseEffectTechnique( xml ) { + + var data = {}; + + for ( var i = 0, l = xml.childNodes.length; i < l; i ++ ) { + + var child = xml.childNodes[ i ]; + + if ( child.nodeType !== 1 ) continue; + + switch ( child.nodeName ) { + + case 'constant': + case 'lambert': + case 'blinn': + case 'phong': + data.type = child.nodeName; + data.parameters = parseEffectParameters( child ); + break; + + } + + } + + return data; + + } + + function parseEffectParameters( xml ) { + + var data = {}; + + for ( var i = 0, l = xml.childNodes.length; i < l; i ++ ) { + + var child = xml.childNodes[ i ]; + + if ( child.nodeType !== 1 ) continue; + + switch ( child.nodeName ) { + + case 'emission': + case 'diffuse': + case 'specular': + case 'bump': + case 'ambient': + case 'shininess': + case 'transparency': + data[ child.nodeName ] = parseEffectParameter( child ); + break; + case 'transparent': + data[ child.nodeName ] = { + opaque: child.getAttribute( 'opaque' ), + data: parseEffectParameter( child ) + }; + break; + + } + + } + + return data; + + } + + function parseEffectParameter( xml ) { + + var data = {}; + + for ( var i = 0, l = xml.childNodes.length; i < l; i ++ ) { + + var child = xml.childNodes[ i ]; + + if ( child.nodeType !== 1 ) continue; + + switch ( child.nodeName ) { + + case 'color': + data[ child.nodeName ] = parseFloats( child.textContent ); + break; + + case 'float': + data[ child.nodeName ] = parseFloat( child.textContent ); + break; + + case 'texture': + data[ child.nodeName ] = { id: child.getAttribute( 'texture' ), extra: parseEffectParameterTexture( child ) }; + break; + + } + + } + + return data; + + } + + function parseEffectParameterTexture( xml ) { + + var data = { + technique: {} + }; + + for ( var i = 0, l = xml.childNodes.length; i < l; i ++ ) { + + var child = xml.childNodes[ i ]; + + if ( child.nodeType !== 1 ) continue; + + switch ( child.nodeName ) { + + case 'extra': + parseEffectParameterTextureExtra( child, data ); + break; + + } + + } + + return data; + + } + + function parseEffectParameterTextureExtra( xml, data ) { + + for ( var i = 0, l = xml.childNodes.length; i < l; i ++ ) { + + var child = xml.childNodes[ i ]; + + if ( child.nodeType !== 1 ) continue; + + switch ( child.nodeName ) { + + case 'technique': + parseEffectParameterTextureExtraTechnique( child, data ); + break; + + } + + } + + } + + function parseEffectParameterTextureExtraTechnique( xml, data ) { + + for ( var i = 0, l = xml.childNodes.length; i < l; i ++ ) { + + var child = xml.childNodes[ i ]; + + if ( child.nodeType !== 1 ) continue; + + switch ( child.nodeName ) { + + case 'repeatU': + case 'repeatV': + case 'offsetU': + case 'offsetV': + data.technique[ child.nodeName ] = parseFloat( child.textContent ); + break; + + case 'wrapU': + case 'wrapV': + + // some files have values for wrapU/wrapV which become NaN via parseInt + + if ( child.textContent.toUpperCase() === 'TRUE' ) { + + data.technique[ child.nodeName ] = 1; + + } else if ( child.textContent.toUpperCase() === 'FALSE' ) { + + data.technique[ child.nodeName ] = 0; + + } else { + + data.technique[ child.nodeName ] = parseInt( child.textContent ); + + } + + break; + + } + + } + + } + + function parseEffectExtra( xml ) { + + var data = {}; + + for ( var i = 0, l = xml.childNodes.length; i < l; i ++ ) { + + var child = xml.childNodes[ i ]; + + if ( child.nodeType !== 1 ) continue; + + switch ( child.nodeName ) { + + case 'technique': + data.technique = parseEffectExtraTechnique( child ); + break; + + } + + } + + return data; + + } + + function parseEffectExtraTechnique( xml ) { + + var data = {}; + + for ( var i = 0, l = xml.childNodes.length; i < l; i ++ ) { + + var child = xml.childNodes[ i ]; + + if ( child.nodeType !== 1 ) continue; + + switch ( child.nodeName ) { + + case 'double_sided': + data[ child.nodeName ] = parseInt( child.textContent ); + break; + + } + + } + + return data; + + } + + function buildEffect( data ) { + + return data; + + } + + function getEffect( id ) { + + return getBuild( library.effects[ id ], buildEffect ); + + } + + // material + + function parseMaterial( xml ) { + + var data = { + name: xml.getAttribute( 'name' ) + }; + + for ( var i = 0, l = xml.childNodes.length; i < l; i ++ ) { + + var child = xml.childNodes[ i ]; + + if ( child.nodeType !== 1 ) continue; + + switch ( child.nodeName ) { + + case 'instance_effect': + data.url = parseId( child.getAttribute( 'url' ) ); + break; + + } + + } + + library.materials[ xml.getAttribute( 'id' ) ] = data; + + } + + function getTextureLoader( image ) { + + var loader; + + var extension = image.slice( ( image.lastIndexOf( '.' ) - 1 >>> 0 ) + 2 ); // http://www.jstips.co/en/javascript/get-file-extension/ + extension = extension.toLowerCase(); + + switch ( extension ) { + + case 'tga': + loader = tgaLoader; + break; + + default: + loader = textureLoader; + + } + + return loader; + + } + + function buildMaterial( data ) { + + var effect = getEffect( data.url ); + var technique = effect.profile.technique; + var extra = effect.profile.extra; + + var material; + + switch ( technique.type ) { + + case 'phong': + case 'blinn': + material = new MeshPhongMaterial(); + break; + + case 'lambert': + material = new MeshLambertMaterial(); + break; + + default: + material = new MeshBasicMaterial(); + break; + + } + + material.name = data.name; + + function getTexture( textureObject ) { + + var sampler = effect.profile.samplers[ textureObject.id ]; + var image = null; + + // get image + + if ( sampler !== undefined ) { + + var surface = effect.profile.surfaces[ sampler.source ]; + image = getImage( surface.init_from ); + + } else { + + console.warn( 'THREE.ColladaLoader: Undefined sampler. Access image directly (see #12530).' ); + image = getImage( textureObject.id ); + + } + + // create texture if image is avaiable + + if ( image !== null ) { + + var loader = getTextureLoader( image ); + + if ( loader !== undefined ) { + + var texture = loader.load( image ); + + var extra = textureObject.extra; + + if ( extra !== undefined && extra.technique !== undefined && isEmpty( extra.technique ) === false ) { + + var technique = extra.technique; + + texture.wrapS = technique.wrapU ? RepeatWrapping : ClampToEdgeWrapping; + texture.wrapT = technique.wrapV ? RepeatWrapping : ClampToEdgeWrapping; + + texture.offset.set( technique.offsetU || 0, technique.offsetV || 0 ); + texture.repeat.set( technique.repeatU || 1, technique.repeatV || 1 ); + + } else { + + texture.wrapS = RepeatWrapping; + texture.wrapT = RepeatWrapping; + + } + + return texture; + + } else { + + console.warn( 'THREE.ColladaLoader: Loader for texture %s not found.', image ); + + return null; + + } + + } else { + + console.warn( 'THREE.ColladaLoader: Couldn\'t create texture with ID:', textureObject.id ); + + return null; + + } + + } + + var parameters = technique.parameters; + + for ( var key in parameters ) { + + var parameter = parameters[ key ]; + + switch ( key ) { + + case 'diffuse': + if ( parameter.color ) material.color.fromArray( parameter.color ); + if ( parameter.texture ) material.map = getTexture( parameter.texture ); + break; + case 'specular': + if ( parameter.color && material.specular ) material.specular.fromArray( parameter.color ); + if ( parameter.texture ) material.specularMap = getTexture( parameter.texture ); + break; + case 'bump': + if ( parameter.texture ) material.normalMap = getTexture( parameter.texture ); + break; + case 'ambient': + if ( parameter.texture ) material.lightMap = getTexture( parameter.texture ); + break; + case 'shininess': + if ( parameter.float && material.shininess ) material.shininess = parameter.float; + break; + case 'emission': + if ( parameter.color && material.emissive ) material.emissive.fromArray( parameter.color ); + if ( parameter.texture ) material.emissiveMap = getTexture( parameter.texture ); + break; + + } + + } + + // + + var transparent = parameters[ 'transparent' ]; + var transparency = parameters[ 'transparency' ]; + + // does not exist but + + if ( transparency === undefined && transparent ) { + + transparency = { + float: 1 + }; + + } + + // does not exist but + + if ( transparent === undefined && transparency ) { + + transparent = { + opaque: 'A_ONE', + data: { + color: [ 1, 1, 1, 1 ] + } }; + + } + + if ( transparent && transparency ) { + + // handle case if a texture exists but no color + + if ( transparent.data.texture ) { + + // we do not set an alpha map (see #13792) + + material.transparent = true; + + } else { + + var color = transparent.data.color; + + switch ( transparent.opaque ) { + + case 'A_ONE': + material.opacity = color[ 3 ] * transparency.float; + break; + case 'RGB_ZERO': + material.opacity = 1 - ( color[ 0 ] * transparency.float ); + break; + case 'A_ZERO': + material.opacity = 1 - ( color[ 3 ] * transparency.float ); + break; + case 'RGB_ONE': + material.opacity = color[ 0 ] * transparency.float; + break; + default: + console.warn( 'THREE.ColladaLoader: Invalid opaque type "%s" of transparent tag.', transparent.opaque ); + + } + + if ( material.opacity < 1 ) material.transparent = true; + + } + + } + + // + + if ( extra !== undefined && extra.technique !== undefined && extra.technique.double_sided === 1 ) { + + material.side = DoubleSide; + + } + + return material; + + } + + function getMaterial( id ) { + + return getBuild( library.materials[ id ], buildMaterial ); + + } + + // camera + + function parseCamera( xml ) { + + var data = { + name: xml.getAttribute( 'name' ) + }; + + for ( var i = 0, l = xml.childNodes.length; i < l; i ++ ) { + + var child = xml.childNodes[ i ]; + + if ( child.nodeType !== 1 ) continue; + + switch ( child.nodeName ) { + + case 'optics': + data.optics = parseCameraOptics( child ); + break; + + } + + } + + library.cameras[ xml.getAttribute( 'id' ) ] = data; + + } + + function parseCameraOptics( xml ) { + + for ( var i = 0; i < xml.childNodes.length; i ++ ) { + + var child = xml.childNodes[ i ]; + + switch ( child.nodeName ) { + + case 'technique_common': + return parseCameraTechnique( child ); + + } + + } + + return {}; + + } + + function parseCameraTechnique( xml ) { + + var data = {}; + + for ( var i = 0; i < xml.childNodes.length; i ++ ) { + + var child = xml.childNodes[ i ]; + + switch ( child.nodeName ) { + + case 'perspective': + case 'orthographic': + + data.technique = child.nodeName; + data.parameters = parseCameraParameters( child ); + + break; + + } + + } + + return data; + + } + + function parseCameraParameters( xml ) { + + var data = {}; + + for ( var i = 0; i < xml.childNodes.length; i ++ ) { + + var child = xml.childNodes[ i ]; + + switch ( child.nodeName ) { + + case 'xfov': + case 'yfov': + case 'xmag': + case 'ymag': + case 'znear': + case 'zfar': + case 'aspect_ratio': + data[ child.nodeName ] = parseFloat( child.textContent ); + break; + + } + + } + + return data; + + } + + function buildCamera( data ) { + + var camera; + + switch ( data.optics.technique ) { + + case 'perspective': + camera = new PerspectiveCamera( + data.optics.parameters.yfov, + data.optics.parameters.aspect_ratio, + data.optics.parameters.znear, + data.optics.parameters.zfar + ); + break; + + case 'orthographic': + var ymag = data.optics.parameters.ymag; + var xmag = data.optics.parameters.xmag; + var aspectRatio = data.optics.parameters.aspect_ratio; + + xmag = ( xmag === undefined ) ? ( ymag * aspectRatio ) : xmag; + ymag = ( ymag === undefined ) ? ( xmag / aspectRatio ) : ymag; + + xmag *= 0.5; + ymag *= 0.5; + + camera = new OrthographicCamera( + - xmag, xmag, ymag, - ymag, // left, right, top, bottom + data.optics.parameters.znear, + data.optics.parameters.zfar + ); + break; + + default: + camera = new PerspectiveCamera(); + break; + + } + + camera.name = data.name; + + return camera; + + } + + function getCamera( id ) { + + var data = library.cameras[ id ]; + + if ( data !== undefined ) { + + return getBuild( data, buildCamera ); + + } + + console.warn( 'THREE.ColladaLoader: Couldn\'t find camera with ID:', id ); + + return null; + + } + + // light + + function parseLight( xml ) { + + var data = {}; + + for ( var i = 0, l = xml.childNodes.length; i < l; i ++ ) { + + var child = xml.childNodes[ i ]; + + if ( child.nodeType !== 1 ) continue; + + switch ( child.nodeName ) { + + case 'technique_common': + data = parseLightTechnique( child ); + break; + + } + + } + + library.lights[ xml.getAttribute( 'id' ) ] = data; + + } + + function parseLightTechnique( xml ) { + + var data = {}; + + for ( var i = 0, l = xml.childNodes.length; i < l; i ++ ) { + + var child = xml.childNodes[ i ]; + + if ( child.nodeType !== 1 ) continue; + + switch ( child.nodeName ) { + + case 'directional': + case 'point': + case 'spot': + case 'ambient': + + data.technique = child.nodeName; + data.parameters = parseLightParameters( child ); + + } + + } + + return data; + + } + + function parseLightParameters( xml ) { + + var data = {}; + + for ( var i = 0, l = xml.childNodes.length; i < l; i ++ ) { + + var child = xml.childNodes[ i ]; + + if ( child.nodeType !== 1 ) continue; + + switch ( child.nodeName ) { + + case 'color': + var array = parseFloats( child.textContent ); + data.color = new Color().fromArray( array ); + break; + + case 'falloff_angle': + data.falloffAngle = parseFloat( child.textContent ); + break; + + case 'quadratic_attenuation': + var f = parseFloat( child.textContent ); + data.distance = f ? Math.sqrt( 1 / f ) : 0; + break; + + } + + } + + return data; + + } + + function buildLight( data ) { + + var light; + + switch ( data.technique ) { + + case 'directional': + light = new DirectionalLight(); + break; + + case 'point': + light = new PointLight(); + break; + + case 'spot': + light = new SpotLight(); + break; + + case 'ambient': + light = new AmbientLight(); + break; + + } + + if ( data.parameters.color ) light.color.copy( data.parameters.color ); + if ( data.parameters.distance ) light.distance = data.parameters.distance; + + return light; + + } + + function getLight( id ) { + + var data = library.lights[ id ]; + + if ( data !== undefined ) { + + return getBuild( data, buildLight ); + + } + + console.warn( 'THREE.ColladaLoader: Couldn\'t find light with ID:', id ); + + return null; + + } + + // geometry + + function parseGeometry( xml ) { + + var data = { + name: xml.getAttribute( 'name' ), + sources: {}, + vertices: {}, + primitives: [] + }; + + var mesh = getElementsByTagName( xml, 'mesh' )[ 0 ]; + + // the following tags inside geometry are not supported yet (see https://github.com/mrdoob/three.js/pull/12606): convex_mesh, spline, brep + if ( mesh === undefined ) return; + + for ( var i = 0; i < mesh.childNodes.length; i ++ ) { + + var child = mesh.childNodes[ i ]; + + if ( child.nodeType !== 1 ) continue; + + var id = child.getAttribute( 'id' ); + + switch ( child.nodeName ) { + + case 'source': + data.sources[ id ] = parseSource( child ); + break; + + case 'vertices': + // data.sources[ id ] = data.sources[ parseId( getElementsByTagName( child, 'input' )[ 0 ].getAttribute( 'source' ) ) ]; + data.vertices = parseGeometryVertices( child ); + break; + + case 'polygons': + console.warn( 'THREE.ColladaLoader: Unsupported primitive type: ', child.nodeName ); + break; + + case 'lines': + case 'linestrips': + case 'polylist': + case 'triangles': + data.primitives.push( parseGeometryPrimitive( child ) ); + break; + + default: + console.log( child ); + + } + + } + + library.geometries[ xml.getAttribute( 'id' ) ] = data; + + } + + function parseSource( xml ) { + + var data = { + array: [], + stride: 3 + }; + + for ( var i = 0; i < xml.childNodes.length; i ++ ) { + + var child = xml.childNodes[ i ]; + + if ( child.nodeType !== 1 ) continue; + + switch ( child.nodeName ) { + + case 'float_array': + data.array = parseFloats( child.textContent ); + break; + + case 'Name_array': + data.array = parseStrings( child.textContent ); + break; + + case 'technique_common': + var accessor = getElementsByTagName( child, 'accessor' )[ 0 ]; + + if ( accessor !== undefined ) { + + data.stride = parseInt( accessor.getAttribute( 'stride' ) ); + + } + break; + + } + + } + + return data; + + } + + function parseGeometryVertices( xml ) { + + var data = {}; + + for ( var i = 0; i < xml.childNodes.length; i ++ ) { + + var child = xml.childNodes[ i ]; + + if ( child.nodeType !== 1 ) continue; + + data[ child.getAttribute( 'semantic' ) ] = parseId( child.getAttribute( 'source' ) ); + + } + + return data; + + } + + function parseGeometryPrimitive( xml ) { + + var primitive = { + type: xml.nodeName, + material: xml.getAttribute( 'material' ), + count: parseInt( xml.getAttribute( 'count' ) ), + inputs: {}, + stride: 0, + hasUV: false + }; + + for ( var i = 0, l = xml.childNodes.length; i < l; i ++ ) { + + var child = xml.childNodes[ i ]; + + if ( child.nodeType !== 1 ) continue; + + switch ( child.nodeName ) { + + case 'input': + var id = parseId( child.getAttribute( 'source' ) ); + var semantic = child.getAttribute( 'semantic' ); + var offset = parseInt( child.getAttribute( 'offset' ) ); + var set = parseInt( child.getAttribute( 'set' ) ); + var inputname = ( set > 0 ? semantic + set : semantic ); + primitive.inputs[ inputname ] = { id: id, offset: offset }; + primitive.stride = Math.max( primitive.stride, offset + 1 ); + if ( semantic === 'TEXCOORD' ) primitive.hasUV = true; + break; + + case 'vcount': + primitive.vcount = parseInts( child.textContent ); + break; + + case 'p': + primitive.p = parseInts( child.textContent ); + break; + + } + + } + + return primitive; + + } + + function groupPrimitives( primitives ) { + + var build = {}; + + for ( var i = 0; i < primitives.length; i ++ ) { + + var primitive = primitives[ i ]; + + if ( build[ primitive.type ] === undefined ) build[ primitive.type ] = []; + + build[ primitive.type ].push( primitive ); + + } + + return build; + + } + + function checkUVCoordinates( primitives ) { + + var count = 0; + + for ( var i = 0, l = primitives.length; i < l; i ++ ) { + + var primitive = primitives[ i ]; + + if ( primitive.hasUV === true ) { + + count ++; + + } + + } + + if ( count > 0 && count < primitives.length ) { + + primitives.uvsNeedsFix = true; + + } + + } + + function buildGeometry( data ) { + + var build = {}; + + var sources = data.sources; + var vertices = data.vertices; + var primitives = data.primitives; + + if ( primitives.length === 0 ) return {}; + + // our goal is to create one buffer geometry for a single type of primitives + // first, we group all primitives by their type + + var groupedPrimitives = groupPrimitives( primitives ); + + for ( var type in groupedPrimitives ) { + + var primitiveType = groupedPrimitives[ type ]; + + // second, ensure consistent uv coordinates for each type of primitives (polylist,triangles or lines) + + checkUVCoordinates( primitiveType ); + + // third, create a buffer geometry for each type of primitives + + build[ type ] = buildGeometryType( primitiveType, sources, vertices ); + + } + + return build; + + } + + function buildGeometryType( primitives, sources, vertices ) { + + var build = {}; + + var position = { array: [], stride: 0 }; + var normal = { array: [], stride: 0 }; + var uv = { array: [], stride: 0 }; + var uv2 = { array: [], stride: 0 }; + var color = { array: [], stride: 0 }; + + var skinIndex = { array: [], stride: 4 }; + var skinWeight = { array: [], stride: 4 }; + + var geometry = new BufferGeometry(); + + var materialKeys = []; + + var start = 0; + + for ( var p = 0; p < primitives.length; p ++ ) { + + var primitive = primitives[ p ]; + var inputs = primitive.inputs; + + // groups + + var count = 0; + + switch ( primitive.type ) { + + case 'lines': + case 'linestrips': + count = primitive.count * 2; + break; + + case 'triangles': + count = primitive.count * 3; + break; + + case 'polylist': + + for ( var g = 0; g < primitive.count; g ++ ) { + + var vc = primitive.vcount[ g ]; + + switch ( vc ) { + + case 3: + count += 3; // single triangle + break; + + case 4: + count += 6; // quad, subdivided into two triangles + break; + + default: + count += ( vc - 2 ) * 3; // polylist with more than four vertices + break; + + } + + } + + break; + + default: + console.warn( 'THREE.ColladaLoader: Unknow primitive type:', primitive.type ); + + } + + geometry.addGroup( start, count, p ); + start += count; + + // material + + if ( primitive.material ) { + + materialKeys.push( primitive.material ); + + } + + // geometry data + + for ( var name in inputs ) { + + var input = inputs[ name ]; + + switch ( name ) { + + case 'VERTEX': + for ( var key in vertices ) { + + var id = vertices[ key ]; + + switch ( key ) { + + case 'POSITION': + var prevLength = position.array.length; + buildGeometryData( primitive, sources[ id ], input.offset, position.array ); + position.stride = sources[ id ].stride; + + if ( sources.skinWeights && sources.skinIndices ) { + + buildGeometryData( primitive, sources.skinIndices, input.offset, skinIndex.array ); + buildGeometryData( primitive, sources.skinWeights, input.offset, skinWeight.array ); + + } + + // see #3803 + + if ( primitive.hasUV === false && primitives.uvsNeedsFix === true ) { + + var count = ( position.array.length - prevLength ) / position.stride; + + for ( var i = 0; i < count; i ++ ) { + + // fill missing uv coordinates + + uv.array.push( 0, 0 ); + + } + + } + break; + + case 'NORMAL': + buildGeometryData( primitive, sources[ id ], input.offset, normal.array ); + normal.stride = sources[ id ].stride; + break; + + case 'COLOR': + buildGeometryData( primitive, sources[ id ], input.offset, color.array ); + color.stride = sources[ id ].stride; + break; + + case 'TEXCOORD': + buildGeometryData( primitive, sources[ id ], input.offset, uv.array ); + uv.stride = sources[ id ].stride; + break; + + case 'TEXCOORD1': + buildGeometryData( primitive, sources[ id ], input.offset, uv2.array ); + uv.stride = sources[ id ].stride; + break; + + default: + console.warn( 'THREE.ColladaLoader: Semantic "%s" not handled in geometry build process.', key ); + + } + + } + break; + + case 'NORMAL': + buildGeometryData( primitive, sources[ input.id ], input.offset, normal.array ); + normal.stride = sources[ input.id ].stride; + break; + + case 'COLOR': + buildGeometryData( primitive, sources[ input.id ], input.offset, color.array ); + color.stride = sources[ input.id ].stride; + break; + + case 'TEXCOORD': + buildGeometryData( primitive, sources[ input.id ], input.offset, uv.array ); + uv.stride = sources[ input.id ].stride; + break; + + case 'TEXCOORD1': + buildGeometryData( primitive, sources[ input.id ], input.offset, uv2.array ); + uv2.stride = sources[ input.id ].stride; + break; + + } + + } + + } + + // build geometry + + if ( position.array.length > 0 ) geometry.addAttribute( 'position', new Float32BufferAttribute( position.array, position.stride ) ); + if ( normal.array.length > 0 ) geometry.addAttribute( 'normal', new Float32BufferAttribute( normal.array, normal.stride ) ); + if ( color.array.length > 0 ) geometry.addAttribute( 'color', new Float32BufferAttribute( color.array, color.stride ) ); + if ( uv.array.length > 0 ) geometry.addAttribute( 'uv', new Float32BufferAttribute( uv.array, uv.stride ) ); + if ( uv2.array.length > 0 ) geometry.addAttribute( 'uv2', new Float32BufferAttribute( uv2.array, uv2.stride ) ); + + if ( skinIndex.array.length > 0 ) geometry.addAttribute( 'skinIndex', new Float32BufferAttribute( skinIndex.array, skinIndex.stride ) ); + if ( skinWeight.array.length > 0 ) geometry.addAttribute( 'skinWeight', new Float32BufferAttribute( skinWeight.array, skinWeight.stride ) ); + + build.data = geometry; + build.type = primitives[ 0 ].type; + build.materialKeys = materialKeys; + + return build; + + } + + function buildGeometryData( primitive, source, offset, array ) { + + var indices = primitive.p; + var stride = primitive.stride; + var vcount = primitive.vcount; + + function pushVector( i ) { + + var index = indices[ i + offset ] * sourceStride; + var length = index + sourceStride; + + for ( ; index < length; index ++ ) { + + array.push( sourceArray[ index ] ); + + } + + } + + var sourceArray = source.array; + var sourceStride = source.stride; + + if ( primitive.vcount !== undefined ) { + + var index = 0; + + for ( var i = 0, l = vcount.length; i < l; i ++ ) { + + var count = vcount[ i ]; + + if ( count === 4 ) { + + var a = index + stride * 0; + var b = index + stride * 1; + var c = index + stride * 2; + var d = index + stride * 3; + + pushVector( a ); pushVector( b ); pushVector( d ); + pushVector( b ); pushVector( c ); pushVector( d ); + + } else if ( count === 3 ) { + + var a = index + stride * 0; + var b = index + stride * 1; + var c = index + stride * 2; + + pushVector( a ); pushVector( b ); pushVector( c ); + + } else if ( count > 4 ) { + + for ( var k = 1, kl = ( count - 2 ); k <= kl; k ++ ) { + + var a = index + stride * 0; + var b = index + stride * k; + var c = index + stride * ( k + 1 ); + + pushVector( a ); pushVector( b ); pushVector( c ); + + } + + } + + index += stride * count; + + } + + } else { + + for ( var i = 0, l = indices.length; i < l; i += stride ) { + + pushVector( i ); + + } + + } + + } + + function getGeometry( id ) { + + return getBuild( library.geometries[ id ], buildGeometry ); + + } + + // kinematics + + function parseKinematicsModel( xml ) { + + var data = { + name: xml.getAttribute( 'name' ) || '', + joints: {}, + links: [] + }; + + for ( var i = 0; i < xml.childNodes.length; i ++ ) { + + var child = xml.childNodes[ i ]; + + if ( child.nodeType !== 1 ) continue; + + switch ( child.nodeName ) { + + case 'technique_common': + parseKinematicsTechniqueCommon( child, data ); + break; + + } + + } + + library.kinematicsModels[ xml.getAttribute( 'id' ) ] = data; + + } + + function buildKinematicsModel( data ) { + + if ( data.build !== undefined ) return data.build; + + return data; + + } + + function getKinematicsModel( id ) { + + return getBuild( library.kinematicsModels[ id ], buildKinematicsModel ); + + } + + function parseKinematicsTechniqueCommon( xml, data ) { + + for ( var i = 0; i < xml.childNodes.length; i ++ ) { + + var child = xml.childNodes[ i ]; + + if ( child.nodeType !== 1 ) continue; + + switch ( child.nodeName ) { + + case 'joint': + data.joints[ child.getAttribute( 'sid' ) ] = parseKinematicsJoint( child ); + break; + + case 'link': + data.links.push( parseKinematicsLink( child ) ); + break; + + } + + } + + } + + function parseKinematicsJoint( xml ) { + + var data; + + for ( var i = 0; i < xml.childNodes.length; i ++ ) { + + var child = xml.childNodes[ i ]; + + if ( child.nodeType !== 1 ) continue; + + switch ( child.nodeName ) { + + case 'prismatic': + case 'revolute': + data = parseKinematicsJointParameter( child ); + break; + + } + + } + + return data; + + } + + function parseKinematicsJointParameter( xml, data ) { + + var data = { + sid: xml.getAttribute( 'sid' ), + name: xml.getAttribute( 'name' ) || '', + axis: new Vector3(), + limits: { + min: 0, + max: 0 + }, + type: xml.nodeName, + static: false, + zeroPosition: 0, + middlePosition: 0 + }; + + for ( var i = 0; i < xml.childNodes.length; i ++ ) { + + var child = xml.childNodes[ i ]; + + if ( child.nodeType !== 1 ) continue; + + switch ( child.nodeName ) { + + case 'axis': + var array = parseFloats( child.textContent ); + data.axis.fromArray( array ); + break; + case 'limits': + var max = child.getElementsByTagName( 'max' )[ 0 ]; + var min = child.getElementsByTagName( 'min' )[ 0 ]; + + data.limits.max = parseFloat( max.textContent ); + data.limits.min = parseFloat( min.textContent ); + break; + + } + + } + + // if min is equal to or greater than max, consider the joint static + + if ( data.limits.min >= data.limits.max ) { + + data.static = true; + + } + + // calculate middle position + + data.middlePosition = ( data.limits.min + data.limits.max ) / 2.0; + + return data; + + } + + function parseKinematicsLink( xml ) { + + var data = { + sid: xml.getAttribute( 'sid' ), + name: xml.getAttribute( 'name' ) || '', + attachments: [], + transforms: [] + }; + + for ( var i = 0; i < xml.childNodes.length; i ++ ) { + + var child = xml.childNodes[ i ]; + + if ( child.nodeType !== 1 ) continue; + + switch ( child.nodeName ) { + + case 'attachment_full': + data.attachments.push( parseKinematicsAttachment( child ) ); + break; + + case 'matrix': + case 'translate': + case 'rotate': + data.transforms.push( parseKinematicsTransform( child ) ); + break; + + } + + } + + return data; + + } + + function parseKinematicsAttachment( xml ) { + + var data = { + joint: xml.getAttribute( 'joint' ).split( '/' ).pop(), + transforms: [], + links: [] + }; + + for ( var i = 0; i < xml.childNodes.length; i ++ ) { + + var child = xml.childNodes[ i ]; + + if ( child.nodeType !== 1 ) continue; + + switch ( child.nodeName ) { + + case 'link': + data.links.push( parseKinematicsLink( child ) ); + break; + + case 'matrix': + case 'translate': + case 'rotate': + data.transforms.push( parseKinematicsTransform( child ) ); + break; + + } + + } + + return data; + + } + + function parseKinematicsTransform( xml ) { + + var data = { + type: xml.nodeName + }; + + var array = parseFloats( xml.textContent ); + + switch ( data.type ) { + + case 'matrix': + data.obj = new Matrix4(); + data.obj.fromArray( array ).transpose(); + break; + + case 'translate': + data.obj = new Vector3(); + data.obj.fromArray( array ); + break; + + case 'rotate': + data.obj = new Vector3(); + data.obj.fromArray( array ); + data.angle = Math.degToRad( array[ 3 ] ); + break; + + } + + return data; + + } + + // physics + + function parsePhysicsModel( xml ) { + + var data = { + name: xml.getAttribute( 'name' ) || '', + rigidBodies: {} + }; + + for ( var i = 0; i < xml.childNodes.length; i ++ ) { + + var child = xml.childNodes[ i ]; + + if ( child.nodeType !== 1 ) continue; + + switch ( child.nodeName ) { + + case 'rigid_body': + data.rigidBodies[ child.getAttribute( 'name' ) ] = {}; + parsePhysicsRigidBody( child, data.rigidBodies[ child.getAttribute( 'name' ) ] ); + break; + + } + + } + + library.physicsModels[ xml.getAttribute( 'id' ) ] = data; + + } + + function parsePhysicsRigidBody( xml, data ) { + + for ( var i = 0; i < xml.childNodes.length; i ++ ) { + + var child = xml.childNodes[ i ]; + + if ( child.nodeType !== 1 ) continue; + + switch ( child.nodeName ) { + + case 'technique_common': + parsePhysicsTechniqueCommon( child, data ); + break; + + } + + } + + } + + function parsePhysicsTechniqueCommon( xml, data ) { + + for ( var i = 0; i < xml.childNodes.length; i ++ ) { + + var child = xml.childNodes[ i ]; + + if ( child.nodeType !== 1 ) continue; + + switch ( child.nodeName ) { + + case 'inertia': + data.inertia = parseFloats( child.textContent ); + break; + + case 'mass': + data.mass = parseFloats( child.textContent )[ 0 ]; + break; + + } + + } + + } + + // scene + + function parseKinematicsScene( xml ) { + + var data = { + bindJointAxis: [] + }; + + for ( var i = 0; i < xml.childNodes.length; i ++ ) { + + var child = xml.childNodes[ i ]; + + if ( child.nodeType !== 1 ) continue; + + switch ( child.nodeName ) { + + case 'bind_joint_axis': + data.bindJointAxis.push( parseKinematicsBindJointAxis( child ) ); + break; + + } + + } + + library.kinematicsScenes[ parseId( xml.getAttribute( 'url' ) ) ] = data; + + } + + function parseKinematicsBindJointAxis( xml ) { + + var data = { + target: xml.getAttribute( 'target' ).split( '/' ).pop() + }; + + for ( var i = 0; i < xml.childNodes.length; i ++ ) { + + var child = xml.childNodes[ i ]; + + if ( child.nodeType !== 1 ) continue; + + switch ( child.nodeName ) { + + case 'axis': + var param = child.getElementsByTagName( 'param' )[ 0 ]; + data.axis = param.textContent; + var tmpJointIndex = data.axis.split( 'inst_' ).pop().split( 'axis' )[ 0 ]; + data.jointIndex = tmpJointIndex.substr( 0, tmpJointIndex.length - 1 ); + break; + + } + + } + + return data; + + } + + function buildKinematicsScene( data ) { + + if ( data.build !== undefined ) return data.build; + + return data; + + } + + function getKinematicsScene( id ) { + + return getBuild( library.kinematicsScenes[ id ], buildKinematicsScene ); + + } + + function setupKinematics() { + + var kinematicsModelId = Object.keys( library.kinematicsModels )[ 0 ]; + var kinematicsSceneId = Object.keys( library.kinematicsScenes )[ 0 ]; + var visualSceneId = Object.keys( library.visualScenes )[ 0 ]; + + if ( kinematicsModelId === undefined || kinematicsSceneId === undefined ) return; + + var kinematicsModel = getKinematicsModel( kinematicsModelId ); + var kinematicsScene = getKinematicsScene( kinematicsSceneId ); + var visualScene = getVisualScene( visualSceneId ); + + var bindJointAxis = kinematicsScene.bindJointAxis; + var jointMap = {}; + + for ( var i = 0, l = bindJointAxis.length; i < l; i ++ ) { + + var axis = bindJointAxis[ i ]; + + // the result of the following query is an element of type 'translate', 'rotate','scale' or 'matrix' + + var targetElement = collada.querySelector( '[sid="' + axis.target + '"]' ); + + if ( targetElement ) { + + // get the parent of the transfrom element + + var parentVisualElement = targetElement.parentElement; + + // connect the joint of the kinematics model with the element in the visual scene + + connect( axis.jointIndex, parentVisualElement ); + + } + + } + + function connect( jointIndex, visualElement ) { + + var visualElementName = visualElement.getAttribute( 'name' ); + var joint = kinematicsModel.joints[ jointIndex ]; + + visualScene.traverse( function ( object ) { + + if ( object.name === visualElementName ) { + + jointMap[ jointIndex ] = { + object: object, + transforms: buildTransformList( visualElement ), + joint: joint, + position: joint.zeroPosition + }; + + } + + } ); + + } + + var m0 = new Matrix4(); + + kinematics = { + + joints: kinematicsModel && kinematicsModel.joints, + + getJointValue: function ( jointIndex ) { + + var jointData = jointMap[ jointIndex ]; + + if ( jointData ) { + + return jointData.position; + + } else { + + console.warn( 'THREE.ColladaLoader: Joint ' + jointIndex + ' doesn\'t exist.' ); + + } + + }, + + setJointValue: function ( jointIndex, value ) { + + var jointData = jointMap[ jointIndex ]; + + if ( jointData ) { + + var joint = jointData.joint; + + if ( value > joint.limits.max || value < joint.limits.min ) { + + console.warn( 'THREE.ColladaLoader: Joint ' + jointIndex + ' value ' + value + ' outside of limits (min: ' + joint.limits.min + ', max: ' + joint.limits.max + ').' ); + + } else if ( joint.static ) { + + console.warn( 'THREE.ColladaLoader: Joint ' + jointIndex + ' is static.' ); + + } else { + + var object = jointData.object; + var axis = joint.axis; + var transforms = jointData.transforms; + + matrix.identity(); + + // each update, we have to apply all transforms in the correct order + + for ( var i = 0; i < transforms.length; i ++ ) { + + var transform = transforms[ i ]; + + // if there is a connection of the transform node with a joint, apply the joint value + + if ( transform.sid && transform.sid.indexOf( jointIndex ) !== - 1 ) { + + switch ( joint.type ) { + + case 'revolute': + matrix.multiply( m0.makeRotationAxis( axis, Math.degToRad( value ) ) ); + break; + + case 'prismatic': + matrix.multiply( m0.makeTranslation( axis.x * value, axis.y * value, axis.z * value ) ); + break; + + default: + console.warn( 'THREE.ColladaLoader: Unknown joint type: ' + joint.type ); + break; + + } + + } else { + + switch ( transform.type ) { + + case 'matrix': + matrix.multiply( transform.obj ); + break; + + case 'translate': + matrix.multiply( m0.makeTranslation( transform.obj.x, transform.obj.y, transform.obj.z ) ); + break; + + case 'scale': + matrix.scale( transform.obj ); + break; + + case 'rotate': + matrix.multiply( m0.makeRotationAxis( transform.obj, transform.angle ) ); + break; + + } + + } + + } + + object.matrix.copy( matrix ); + object.matrix.decompose( object.position, object.quaternion, object.scale ); + + jointMap[ jointIndex ].position = value; + + } + + } else { + + console.log( 'THREE.ColladaLoader: ' + jointIndex + ' does not exist.' ); + + } + + } + + }; + + } + + function buildTransformList( node ) { + + var transforms = []; + + var xml = collada.querySelector( '[id="' + node.id + '"]' ); + + for ( var i = 0; i < xml.childNodes.length; i ++ ) { + + var child = xml.childNodes[ i ]; + + if ( child.nodeType !== 1 ) continue; + + switch ( child.nodeName ) { + + case 'matrix': + var array = parseFloats( child.textContent ); + var matrix = new Matrix4().fromArray( array ).transpose(); + transforms.push( { + sid: child.getAttribute( 'sid' ), + type: child.nodeName, + obj: matrix + } ); + break; + + case 'translate': + case 'scale': + var array = parseFloats( child.textContent ); + var vector = new Vector3().fromArray( array ); + transforms.push( { + sid: child.getAttribute( 'sid' ), + type: child.nodeName, + obj: vector + } ); + break; + + case 'rotate': + var array = parseFloats( child.textContent ); + var vector = new Vector3().fromArray( array ); + var angle = Math.degToRad( array[ 3 ] ); + transforms.push( { + sid: child.getAttribute( 'sid' ), + type: child.nodeName, + obj: vector, + angle: angle + } ); + break; + + } + + } + + return transforms; + + } + + // nodes + + function prepareNodes( xml ) { + + var elements = xml.getElementsByTagName( 'node' ); + + // ensure all node elements have id attributes + + for ( var i = 0; i < elements.length; i ++ ) { + + var element = elements[ i ]; + + if ( element.hasAttribute( 'id' ) === false ) { + + element.setAttribute( 'id', generateId() ); + + } + + } + + } + + var matrix = new Matrix4(); + var vector = new Vector3(); + + function parseNode( xml ) { + + var data = { + name: xml.getAttribute( 'name' ) || '', + type: xml.getAttribute( 'type' ), + id: xml.getAttribute( 'id' ), + sid: xml.getAttribute( 'sid' ), + matrix: new Matrix4(), + nodes: [], + instanceCameras: [], + instanceControllers: [], + instanceLights: [], + instanceGeometries: [], + instanceNodes: [], + transforms: {} + }; + + for ( var i = 0; i < xml.childNodes.length; i ++ ) { + + var child = xml.childNodes[ i ]; + + if ( child.nodeType !== 1 ) continue; + + switch ( child.nodeName ) { + + case 'node': + data.nodes.push( child.getAttribute( 'id' ) ); + parseNode( child ); + break; + + case 'instance_camera': + data.instanceCameras.push( parseId( child.getAttribute( 'url' ) ) ); + break; + + case 'instance_controller': + data.instanceControllers.push( parseNodeInstance( child ) ); + break; + + case 'instance_light': + data.instanceLights.push( parseId( child.getAttribute( 'url' ) ) ); + break; + + case 'instance_geometry': + data.instanceGeometries.push( parseNodeInstance( child ) ); + break; + + case 'instance_node': + data.instanceNodes.push( parseId( child.getAttribute( 'url' ) ) ); + break; + + case 'matrix': + var array = parseFloats( child.textContent ); + data.matrix.multiply( matrix.fromArray( array ).transpose() ); + data.transforms[ child.getAttribute( 'sid' ) ] = child.nodeName; + break; + + case 'translate': + var array = parseFloats( child.textContent ); + vector.fromArray( array ); + data.matrix.multiply( matrix.makeTranslation( vector.x, vector.y, vector.z ) ); + data.transforms[ child.getAttribute( 'sid' ) ] = child.nodeName; + break; + + case 'rotate': + var array = parseFloats( child.textContent ); + var angle = Math.degToRad( array[ 3 ] ); + data.matrix.multiply( matrix.makeRotationAxis( vector.fromArray( array ), angle ) ); + data.transforms[ child.getAttribute( 'sid' ) ] = child.nodeName; + break; + + case 'scale': + var array = parseFloats( child.textContent ); + data.matrix.scale( vector.fromArray( array ) ); + data.transforms[ child.getAttribute( 'sid' ) ] = child.nodeName; + break; + + case 'extra': + break; + + default: + console.log( child ); + + } + + } + + if ( hasNode( data.id ) ) { + + console.warn( 'THREE.ColladaLoader: There is already a node with ID %s. Exclude current node from further processing.', data.id ); + + } else { + + library.nodes[ data.id ] = data; + + } + + return data; + + } + + function parseNodeInstance( xml ) { + + var data = { + id: parseId( xml.getAttribute( 'url' ) ), + materials: {}, + skeletons: [] + }; + + for ( var i = 0; i < xml.childNodes.length; i ++ ) { + + var child = xml.childNodes[ i ]; + + switch ( child.nodeName ) { + + case 'bind_material': + var instances = child.getElementsByTagName( 'instance_material' ); + + for ( var j = 0; j < instances.length; j ++ ) { + + var instance = instances[ j ]; + var symbol = instance.getAttribute( 'symbol' ); + var target = instance.getAttribute( 'target' ); + + data.materials[ symbol ] = parseId( target ); + + } + + break; + + case 'skeleton': + data.skeletons.push( parseId( child.textContent ) ); + break; + + default: + break; + + } + + } + + return data; + + } + + function buildSkeleton( skeletons, joints ) { + + var boneData = []; + var sortedBoneData = []; + + var i, j, data; + + // a skeleton can have multiple root bones. collada expresses this + // situtation with multiple "skeleton" tags per controller instance + + for ( i = 0; i < skeletons.length; i ++ ) { + + var skeleton = skeletons[ i ]; + + var root; + + if ( hasNode( skeleton ) ) { + + root = getNode( skeleton ); + buildBoneHierarchy( root, joints, boneData ); + + } else if ( hasVisualScene( skeleton ) ) { + + // handle case where the skeleton refers to the visual scene (#13335) + + var visualScene = library.visualScenes[ skeleton ]; + var children = visualScene.children; + + for ( var j = 0; j < children.length; j ++ ) { + + var child = children[ j ]; + + if ( child.type === 'JOINT' ) { + + var root = getNode( child.id ); + buildBoneHierarchy( root, joints, boneData ); + + } + + } + + } else { + + console.error( 'THREE.ColladaLoader: Unable to find root bone of skeleton with ID:', skeleton ); + + } + + } + + // sort bone data (the order is defined in the corresponding controller) + + for ( i = 0; i < joints.length; i ++ ) { + + for ( j = 0; j < boneData.length; j ++ ) { + + data = boneData[ j ]; + + if ( data.bone.name === joints[ i ].name ) { + + sortedBoneData[ i ] = data; + data.processed = true; + break; + + } + + } + + } + + // add unprocessed bone data at the end of the list + + for ( i = 0; i < boneData.length; i ++ ) { + + data = boneData[ i ]; + + if ( data.processed === false ) { + + sortedBoneData.push( data ); + data.processed = true; + + } + + } + + // setup arrays for skeleton creation + + var bones = []; + var boneInverses = []; + + for ( i = 0; i < sortedBoneData.length; i ++ ) { + + data = sortedBoneData[ i ]; + + bones.push( data.bone ); + boneInverses.push( data.boneInverse ); + + } + + return new Skeleton( bones, boneInverses ); + + } + + function buildBoneHierarchy( root, joints, boneData ) { + + // setup bone data from visual scene + + root.traverse( function ( object ) { + + if ( object.isBone === true ) { + + var boneInverse; + + // retrieve the boneInverse from the controller data + + for ( var i = 0; i < joints.length; i ++ ) { + + var joint = joints[ i ]; + + if ( joint.name === object.name ) { + + boneInverse = joint.boneInverse; + break; + + } + + } + + if ( boneInverse === undefined ) { + + // Unfortunately, there can be joints in the visual scene that are not part of the + // corresponding controller. In this case, we have to create a dummy boneInverse matrix + // for the respective bone. This bone won't affect any vertices, because there are no skin indices + // and weights defined for it. But we still have to add the bone to the sorted bone list in order to + // ensure a correct animation of the model. + + boneInverse = new Matrix4(); + + } + + boneData.push( { bone: object, boneInverse: boneInverse, processed: false } ); + + } + + } ); + + } + + function buildNode( data ) { + + var objects = []; + + var matrix = data.matrix; + var nodes = data.nodes; + var type = data.type; + var instanceCameras = data.instanceCameras; + var instanceControllers = data.instanceControllers; + var instanceLights = data.instanceLights; + var instanceGeometries = data.instanceGeometries; + var instanceNodes = data.instanceNodes; + + // nodes + + for ( var i = 0, l = nodes.length; i < l; i ++ ) { + + objects.push( getNode( nodes[ i ] ) ); + + } + + // instance cameras + + for ( var i = 0, l = instanceCameras.length; i < l; i ++ ) { + + var instanceCamera = getCamera( instanceCameras[ i ] ); + + if ( instanceCamera !== null ) { + + objects.push( instanceCamera.clone() ); + + } + + } + + // instance controllers + + for ( var i = 0, l = instanceControllers.length; i < l; i ++ ) { + + var instance = instanceControllers[ i ]; + var controller = getController( instance.id ); + var geometries = getGeometry( controller.id ); + var newObjects = buildObjects( geometries, instance.materials ); + + var skeletons = instance.skeletons; + var joints = controller.skin.joints; + + var skeleton = buildSkeleton( skeletons, joints ); + + for ( var j = 0, jl = newObjects.length; j < jl; j ++ ) { + + var object = newObjects[ j ]; + + if ( object.isSkinnedMesh ) { + + object.bind( skeleton, controller.skin.bindMatrix ); + object.normalizeSkinWeights(); + + } + + objects.push( object ); + + } + + } + + // instance lights + + for ( var i = 0, l = instanceLights.length; i < l; i ++ ) { + + var instanceLight = getLight( instanceLights[ i ] ); + + if ( instanceLight !== null ) { + + objects.push( instanceLight.clone() ); + + } + + } + + // instance geometries + + for ( var i = 0, l = instanceGeometries.length; i < l; i ++ ) { + + var instance = instanceGeometries[ i ]; + + // a single geometry instance in collada can lead to multiple object3Ds. + // this is the case when primitives are combined like triangles and lines + + var geometries = getGeometry( instance.id ); + var newObjects = buildObjects( geometries, instance.materials ); + + for ( var j = 0, jl = newObjects.length; j < jl; j ++ ) { + + objects.push( newObjects[ j ] ); + + } + + } + + // instance nodes + + for ( var i = 0, l = instanceNodes.length; i < l; i ++ ) { + + objects.push( getNode( instanceNodes[ i ] ).clone() ); + + } + + var object; + + if ( nodes.length === 0 && objects.length === 1 ) { + + object = objects[ 0 ]; + + } else { + + object = ( type === 'JOINT' ) ? new Bone() : new Group(); + + for ( var i = 0; i < objects.length; i ++ ) { + + object.add( objects[ i ] ); + + } + + } + + if ( object.name === '' ) { + + object.name = ( type === 'JOINT' ) ? data.sid : data.name; + + } + + object.matrix.copy( matrix ); + object.matrix.decompose( object.position, object.quaternion, object.scale ); + + return object; + + } + + var fallbackMaterial = new MeshBasicMaterial( { color: 0xff00ff } ); + + function resolveMaterialBinding( keys, instanceMaterials ) { + + var materials = []; + + for ( var i = 0, l = keys.length; i < l; i ++ ) { + + var id = instanceMaterials[ keys[ i ] ]; + + if ( id === undefined ) { + + console.warn( 'THREE.ColladaLoader: Material with key %s not found. Apply fallback material.', keys[ i ] ); + materials.push( fallbackMaterial ); + + } else { + + materials.push( getMaterial( id ) ); + + } + + } + + return materials; + + } + + function buildObjects( geometries, instanceMaterials ) { + + var objects = []; + + for ( var type in geometries ) { + + var geometry = geometries[ type ]; + + var materials = resolveMaterialBinding( geometry.materialKeys, instanceMaterials ); + + // handle case if no materials are defined + + if ( materials.length === 0 ) { + + if ( type === 'lines' || type === 'linestrips' ) { + + materials.push( new LineBasicMaterial() ); + + } else { + + materials.push( new MeshPhongMaterial() ); + + } + + } + + // regard skinning + + var skinning = ( geometry.data.attributes.skinIndex !== undefined ); + + if ( skinning ) { + + for ( var i = 0, l = materials.length; i < l; i ++ ) { + + materials[ i ].skinning = true; + + } + + } + + // choose between a single or multi materials (material array) + + var material = ( materials.length === 1 ) ? materials[ 0 ] : materials; + + // now create a specific 3D object + + var object; + + switch ( type ) { + + case 'lines': + object = new LineSegments( geometry.data, material ); + break; + + case 'linestrips': + object = new Line( geometry.data, material ); + break; + + case 'triangles': + case 'polylist': + if ( skinning ) { + + object = new SkinnedMesh( geometry.data, material ); + + } else { + + object = new Mesh( geometry.data, material ); + + } + break; + + } + + objects.push( object ); + + } + + return objects; + + } + + function hasNode( id ) { + + return library.nodes[ id ] !== undefined; + + } + + function getNode( id ) { + + return getBuild( library.nodes[ id ], buildNode ); + + } + + // visual scenes + + function parseVisualScene( xml ) { + + var data = { + name: xml.getAttribute( 'name' ), + children: [] + }; + + prepareNodes( xml ); + + var elements = getElementsByTagName( xml, 'node' ); + + for ( var i = 0; i < elements.length; i ++ ) { + + data.children.push( parseNode( elements[ i ] ) ); + + } + + library.visualScenes[ xml.getAttribute( 'id' ) ] = data; + + } + + function buildVisualScene( data ) { + + var group = new Group(); + group.name = data.name; + + var children = data.children; + + for ( var i = 0; i < children.length; i ++ ) { + + var child = children[ i ]; + + group.add( getNode( child.id ) ); + + } + + return group; + + } + + function hasVisualScene( id ) { + + return library.visualScenes[ id ] !== undefined; + + } + + function getVisualScene( id ) { + + return getBuild( library.visualScenes[ id ], buildVisualScene ); + + } + + // scenes + + function parseScene( xml ) { + + var instance = getElementsByTagName( xml, 'instance_visual_scene' )[ 0 ]; + return getVisualScene( parseId( instance.getAttribute( 'url' ) ) ); + + } + + function setupAnimations() { + + var clips = library.clips; + + if ( isEmpty( clips ) === true ) { + + if ( isEmpty( library.animations ) === false ) { + + // if there are animations but no clips, we create a default clip for playback + + var tracks = []; + + for ( var id in library.animations ) { + + var animationTracks = getAnimation( id ); + + for ( var i = 0, l = animationTracks.length; i < l; i ++ ) { + + tracks.push( animationTracks[ i ] ); + + } + + } + + animations.push( new AnimationClip( 'default', - 1, tracks ) ); + + } + + } else { + + for ( var id in clips ) { + + animations.push( getAnimationClip( id ) ); + + } + + } + + } + + if ( text.length === 0 ) { + + return { scene: new Scene() }; + + } + + var xml = new DOMParser().parseFromString( text, 'application/xml' ); + + var collada = getElementsByTagName( xml, 'COLLADA' )[ 0 ]; + + // metadata + + var version = collada.getAttribute( 'version' ); + console.log( 'THREE.ColladaLoader: File version', version ); + + var asset = parseAsset( getElementsByTagName( collada, 'asset' )[ 0 ] ); + var textureLoader = new TextureLoader( this.manager ); + textureLoader.setPath( this.resourcePath || path ).setCrossOrigin( this.crossOrigin ); + + var tgaLoader; + + if ( THREE.TGALoader ) { + + tgaLoader = new THREE.TGALoader( this.manager ); + tgaLoader.setPath( this.resourcePath || path ); + + } + + // + + var animations = []; + var kinematics = {}; + var count = 0; + + // + + var library = { + animations: {}, + clips: {}, + controllers: {}, + images: {}, + effects: {}, + materials: {}, + cameras: {}, + lights: {}, + geometries: {}, + nodes: {}, + visualScenes: {}, + kinematicsModels: {}, + physicsModels: {}, + kinematicsScenes: {} + }; + + parseLibrary( collada, 'library_animations', 'animation', parseAnimation ); + parseLibrary( collada, 'library_animation_clips', 'animation_clip', parseAnimationClip ); + parseLibrary( collada, 'library_controllers', 'controller', parseController ); + parseLibrary( collada, 'library_images', 'image', parseImage ); + parseLibrary( collada, 'library_effects', 'effect', parseEffect ); + parseLibrary( collada, 'library_materials', 'material', parseMaterial ); + parseLibrary( collada, 'library_cameras', 'camera', parseCamera ); + parseLibrary( collada, 'library_lights', 'light', parseLight ); + parseLibrary( collada, 'library_geometries', 'geometry', parseGeometry ); + parseLibrary( collada, 'library_nodes', 'node', parseNode ); + parseLibrary( collada, 'library_visual_scenes', 'visual_scene', parseVisualScene ); + parseLibrary( collada, 'library_kinematics_models', 'kinematics_model', parseKinematicsModel ); + parseLibrary( collada, 'library_physics_models', 'physics_model', parsePhysicsModel ); + parseLibrary( collada, 'scene', 'instance_kinematics_scene', parseKinematicsScene ); + + buildLibrary( library.animations, buildAnimation ); + buildLibrary( library.clips, buildAnimationClip ); + buildLibrary( library.controllers, buildController ); + buildLibrary( library.images, buildImage ); + buildLibrary( library.effects, buildEffect ); + buildLibrary( library.materials, buildMaterial ); + buildLibrary( library.cameras, buildCamera ); + buildLibrary( library.lights, buildLight ); + buildLibrary( library.geometries, buildGeometry ); + buildLibrary( library.visualScenes, buildVisualScene ); + + setupAnimations(); + setupKinematics(); + + var scene = parseScene( getElementsByTagName( collada, 'scene' )[ 0 ] ); + + if ( asset.upAxis === 'Z_UP' ) { + + scene.quaternion.setFromEuler( new Euler( - Math.PI / 2, 0, 0 ) ); + + } + + scene.scale.multiplyScalar( asset.unit ); + + return { + animations: animations, + kinematics: kinematics, + library: library, + scene: scene + }; + + } + +}; + +export { ColladaLoader }; diff --git a/examples/jsm/loaders/DDSLoader.js b/examples/jsm/loaders/DDSLoader.js new file mode 100644 index 00000000000000..11663c5977797c --- /dev/null +++ b/examples/jsm/loaders/DDSLoader.js @@ -0,0 +1,282 @@ +/* + * @author mrdoob / http://mrdoob.com/ + */ + +import { + CompressedTextureLoader, + RGB_ETC1_Format, + RGB_S3TC_DXT1_Format, + RGBA_S3TC_DXT3_Format, + RGBA_S3TC_DXT5_Format, RGBAFormat, +} from "../../../build/three.module.js"; + +var DDSLoader = function ( manager ) { + + CompressedTextureLoader.call( this, manager ); + + this._parser = DDSLoader.parse; + +}; + +DDSLoader.prototype = Object.create( CompressedTextureLoader.prototype ); +DDSLoader.prototype.constructor = DDSLoader; + +DDSLoader.parse = function ( buffer, loadMipmaps ) { + + var dds = { mipmaps: [], width: 0, height: 0, format: null, mipmapCount: 1 }; + + // Adapted from @toji's DDS utils + // https://github.com/toji/webgl-texture-utils/blob/master/texture-util/dds.js + + // All values and structures referenced from: + // http://msdn.microsoft.com/en-us/library/bb943991.aspx/ + + var DDS_MAGIC = 0x20534444; + + var DDSD_CAPS = 0x1, + DDSD_HEIGHT = 0x2, + DDSD_WIDTH = 0x4, + DDSD_PITCH = 0x8, + DDSD_PIXELFORMAT = 0x1000, + DDSD_MIPMAPCOUNT = 0x20000, + DDSD_LINEARSIZE = 0x80000, + DDSD_DEPTH = 0x800000; + + var DDSCAPS_COMPLEX = 0x8, + DDSCAPS_MIPMAP = 0x400000, + DDSCAPS_TEXTURE = 0x1000; + + var DDSCAPS2_CUBEMAP = 0x200, + DDSCAPS2_CUBEMAP_POSITIVEX = 0x400, + DDSCAPS2_CUBEMAP_NEGATIVEX = 0x800, + DDSCAPS2_CUBEMAP_POSITIVEY = 0x1000, + DDSCAPS2_CUBEMAP_NEGATIVEY = 0x2000, + DDSCAPS2_CUBEMAP_POSITIVEZ = 0x4000, + DDSCAPS2_CUBEMAP_NEGATIVEZ = 0x8000, + DDSCAPS2_VOLUME = 0x200000; + + var DDPF_ALPHAPIXELS = 0x1, + DDPF_ALPHA = 0x2, + DDPF_FOURCC = 0x4, + DDPF_RGB = 0x40, + DDPF_YUV = 0x200, + DDPF_LUMINANCE = 0x20000; + + function fourCCToInt32( value ) { + + return value.charCodeAt( 0 ) + + ( value.charCodeAt( 1 ) << 8 ) + + ( value.charCodeAt( 2 ) << 16 ) + + ( value.charCodeAt( 3 ) << 24 ); + + } + + function int32ToFourCC( value ) { + + return String.fromCharCode( + value & 0xff, + ( value >> 8 ) & 0xff, + ( value >> 16 ) & 0xff, + ( value >> 24 ) & 0xff + ); + + } + + function loadARGBMip( buffer, dataOffset, width, height ) { + + var dataLength = width * height * 4; + var srcBuffer = new Uint8Array( buffer, dataOffset, dataLength ); + var byteArray = new Uint8Array( dataLength ); + var dst = 0; + var src = 0; + for ( var y = 0; y < height; y ++ ) { + + for ( var x = 0; x < width; x ++ ) { + + var b = srcBuffer[ src ]; src ++; + var g = srcBuffer[ src ]; src ++; + var r = srcBuffer[ src ]; src ++; + var a = srcBuffer[ src ]; src ++; + byteArray[ dst ] = r; dst ++; //r + byteArray[ dst ] = g; dst ++; //g + byteArray[ dst ] = b; dst ++; //b + byteArray[ dst ] = a; dst ++; //a + + } + + } + return byteArray; + + } + + var FOURCC_DXT1 = fourCCToInt32( "DXT1" ); + var FOURCC_DXT3 = fourCCToInt32( "DXT3" ); + var FOURCC_DXT5 = fourCCToInt32( "DXT5" ); + var FOURCC_ETC1 = fourCCToInt32( "ETC1" ); + + var headerLengthInt = 31; // The header length in 32 bit ints + + // Offsets into the header array + + var off_magic = 0; + + var off_size = 1; + var off_flags = 2; + var off_height = 3; + var off_width = 4; + + var off_mipmapCount = 7; + + var off_pfFlags = 20; + var off_pfFourCC = 21; + var off_RGBBitCount = 22; + var off_RBitMask = 23; + var off_GBitMask = 24; + var off_BBitMask = 25; + var off_ABitMask = 26; + + var off_caps = 27; + var off_caps2 = 28; + var off_caps3 = 29; + var off_caps4 = 30; + + // Parse header + + var header = new Int32Array( buffer, 0, headerLengthInt ); + + if ( header[ off_magic ] !== DDS_MAGIC ) { + + console.error( 'THREE.DDSLoader.parse: Invalid magic number in DDS header.' ); + return dds; + + } + + if ( ! header[ off_pfFlags ] & DDPF_FOURCC ) { + + console.error( 'THREE.DDSLoader.parse: Unsupported format, must contain a FourCC code.' ); + return dds; + + } + + var blockBytes; + + var fourCC = header[ off_pfFourCC ]; + + var isRGBAUncompressed = false; + + switch ( fourCC ) { + + case FOURCC_DXT1: + + blockBytes = 8; + dds.format = RGB_S3TC_DXT1_Format; + break; + + case FOURCC_DXT3: + + blockBytes = 16; + dds.format = RGBA_S3TC_DXT3_Format; + break; + + case FOURCC_DXT5: + + blockBytes = 16; + dds.format = RGBA_S3TC_DXT5_Format; + break; + + case FOURCC_ETC1: + + blockBytes = 8; + dds.format = RGB_ETC1_Format; + break; + + default: + + if ( header[ off_RGBBitCount ] === 32 + && header[ off_RBitMask ] & 0xff0000 + && header[ off_GBitMask ] & 0xff00 + && header[ off_BBitMask ] & 0xff + && header[ off_ABitMask ] & 0xff000000 ) { + + isRGBAUncompressed = true; + blockBytes = 64; + dds.format = RGBAFormat; + + } else { + + console.error( 'THREE.DDSLoader.parse: Unsupported FourCC code ', int32ToFourCC( fourCC ) ); + return dds; + + } + + } + + dds.mipmapCount = 1; + + if ( header[ off_flags ] & DDSD_MIPMAPCOUNT && loadMipmaps !== false ) { + + dds.mipmapCount = Math.max( 1, header[ off_mipmapCount ] ); + + } + + var caps2 = header[ off_caps2 ]; + dds.isCubemap = caps2 & DDSCAPS2_CUBEMAP ? true : false; + if ( dds.isCubemap && ( + ! ( caps2 & DDSCAPS2_CUBEMAP_POSITIVEX ) || + ! ( caps2 & DDSCAPS2_CUBEMAP_NEGATIVEX ) || + ! ( caps2 & DDSCAPS2_CUBEMAP_POSITIVEY ) || + ! ( caps2 & DDSCAPS2_CUBEMAP_NEGATIVEY ) || + ! ( caps2 & DDSCAPS2_CUBEMAP_POSITIVEZ ) || + ! ( caps2 & DDSCAPS2_CUBEMAP_NEGATIVEZ ) + ) ) { + + console.error( 'THREE.DDSLoader.parse: Incomplete cubemap faces' ); + return dds; + + } + + dds.width = header[ off_width ]; + dds.height = header[ off_height ]; + + var dataOffset = header[ off_size ] + 4; + + // Extract mipmaps buffers + + var faces = dds.isCubemap ? 6 : 1; + + for ( var face = 0; face < faces; face ++ ) { + + var width = dds.width; + var height = dds.height; + + for ( var i = 0; i < dds.mipmapCount; i ++ ) { + + if ( isRGBAUncompressed ) { + + var byteArray = loadARGBMip( buffer, dataOffset, width, height ); + var dataLength = byteArray.length; + + } else { + + var dataLength = Math.max( 4, width ) / 4 * Math.max( 4, height ) / 4 * blockBytes; + var byteArray = new Uint8Array( buffer, dataOffset, dataLength ); + + } + + var mipmap = { "data": byteArray, "width": width, "height": height }; + dds.mipmaps.push( mipmap ); + + dataOffset += dataLength; + + width = Math.max( width >> 1, 1 ); + height = Math.max( height >> 1, 1 ); + + } + + } + + return dds; + +}; + +export { DDSLoader }; diff --git a/examples/jsm/loaders/DRACOLoader.js b/examples/jsm/loaders/DRACOLoader.js new file mode 100644 index 00000000000000..6e42deb6ea975a --- /dev/null +++ b/examples/jsm/loaders/DRACOLoader.js @@ -0,0 +1,659 @@ +// Copyright 2016 The Draco Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +import { + BufferGeometry, + DefaultLoadingManager, + FileLoader, + Float32BufferAttribute, + Int16BufferAttribute, + Int32BufferAttribute, + Int8BufferAttribute, + TrianglesDrawMode, + TriangleStripDrawMode, + Uint16BufferAttribute, + Uint32BufferAttribute, + Uint8BufferAttribute, +} from "../../../build/three.module.js"; + +/** + * @param {LoadingManager} manager + */ +var DRACOLoader = function ( manager ) { + + this.timeLoaded = 0; + this.manager = manager || DefaultLoadingManager; + this.materials = null; + this.verbosity = 0; + this.attributeOptions = {}; + this.drawMode = TrianglesDrawMode; + // Native Draco attribute type to Three.JS attribute type. + this.nativeAttributeMap = { + 'position': 'POSITION', + 'normal': 'NORMAL', + 'color': 'COLOR', + 'uv': 'TEX_COORD' + }; + +}; + +DRACOLoader.prototype = { + + constructor: DRACOLoader, + + load: function ( url, onLoad, onProgress, onError ) { + + var scope = this; + var loader = new FileLoader( scope.manager ); + loader.setPath( this.path ); + loader.setResponseType( 'arraybuffer' ); + loader.load( url, function ( blob ) { + + scope.decodeDracoFile( blob, onLoad ); + + }, onProgress, onError ); + + }, + + setPath: function ( value ) { + + this.path = value; + return this; + + }, + + setVerbosity: function ( level ) { + + this.verbosity = level; + return this; + + }, + + /** + * Sets desired mode for generated geometry indices. + * Can be either: + * TrianglesDrawMode + * TriangleStripDrawMode + */ + setDrawMode: function ( drawMode ) { + + this.drawMode = drawMode; + return this; + + }, + + /** + * Skips dequantization for a specific attribute. + * |attributeName| is the THREE.js name of the given attribute type. + * The only currently supported |attributeName| is 'position', more may be + * added in future. + */ + setSkipDequantization: function ( attributeName, skip ) { + + var skipDequantization = true; + if ( typeof skip !== 'undefined' ) + skipDequantization = skip; + this.getAttributeOptions( attributeName ).skipDequantization = + skipDequantization; + return this; + + }, + + /** + * Decompresses a Draco buffer. Names of attributes (for ID and type maps) + * must be one of the supported three.js types, including: position, color, + * normal, uv, uv2, skinIndex, skinWeight. + * + * @param {ArrayBuffer} rawBuffer + * @param {Function} callback + * @param {Object|undefined} attributeUniqueIdMap Provides a pre-defined ID + * for each attribute in the geometry to be decoded. If given, + * `attributeTypeMap` is required and `nativeAttributeMap` will be + * ignored. + * @param {Object|undefined} attributeTypeMap Provides a predefined data + * type (as a typed array constructor) for each attribute in the + * geometry to be decoded. + */ + decodeDracoFile: function ( rawBuffer, callback, attributeUniqueIdMap, + attributeTypeMap ) { + + var scope = this; + DRACOLoader.getDecoderModule() + .then( function ( module ) { + + scope.decodeDracoFileInternal( rawBuffer, module.decoder, callback, + attributeUniqueIdMap, attributeTypeMap ); + + } ); + + }, + + decodeDracoFileInternal: function ( rawBuffer, dracoDecoder, callback, + attributeUniqueIdMap, attributeTypeMap ) { + + /* + * Here is how to use Draco Javascript decoder and get the geometry. + */ + var buffer = new dracoDecoder.DecoderBuffer(); + buffer.Init( new Int8Array( rawBuffer ), rawBuffer.byteLength ); + var decoder = new dracoDecoder.Decoder(); + + /* + * Determine what type is this file: mesh or point cloud. + */ + var geometryType = decoder.GetEncodedGeometryType( buffer ); + if ( geometryType == dracoDecoder.TRIANGULAR_MESH ) { + + if ( this.verbosity > 0 ) { + + console.log( 'Loaded a mesh.' ); + + } + + } else if ( geometryType == dracoDecoder.POINT_CLOUD ) { + + if ( this.verbosity > 0 ) { + + console.log( 'Loaded a point cloud.' ); + + } + + } else { + + var errorMsg = 'DRACOLoader: Unknown geometry type.'; + console.error( errorMsg ); + throw new Error( errorMsg ); + + } + callback( this.convertDracoGeometryTo3JS( dracoDecoder, decoder, + geometryType, buffer, attributeUniqueIdMap, attributeTypeMap ) ); + + }, + + addAttributeToGeometry: function ( dracoDecoder, decoder, dracoGeometry, + attributeName, attributeType, attribute, + geometry, geometryBuffer ) { + + if ( attribute.ptr === 0 ) { + + var errorMsg = 'DRACOLoader: No attribute ' + attributeName; + console.error( errorMsg ); + throw new Error( errorMsg ); + + } + + var numComponents = attribute.num_components(); + var numPoints = dracoGeometry.num_points(); + var numValues = numPoints * numComponents; + var attributeData; + var TypedBufferAttribute; + + switch ( attributeType ) { + + case Float32Array: + attributeData = new dracoDecoder.DracoFloat32Array(); + decoder.GetAttributeFloatForAllPoints( + dracoGeometry, attribute, attributeData ); + geometryBuffer[ attributeName ] = new Float32Array( numValues ); + TypedBufferAttribute = Float32BufferAttribute; + break; + + case Int8Array: + attributeData = new dracoDecoder.DracoInt8Array(); + decoder.GetAttributeInt8ForAllPoints( + dracoGeometry, attribute, attributeData ); + geometryBuffer[ attributeName ] = new Int8Array( numValues ); + TypedBufferAttribute = Int8BufferAttribute; + break; + + case Int16Array: + attributeData = new dracoDecoder.DracoInt16Array(); + decoder.GetAttributeInt16ForAllPoints( + dracoGeometry, attribute, attributeData ); + geometryBuffer[ attributeName ] = new Int16Array( numValues ); + TypedBufferAttribute = Int16BufferAttribute; + break; + + case Int32Array: + attributeData = new dracoDecoder.DracoInt32Array(); + decoder.GetAttributeInt32ForAllPoints( + dracoGeometry, attribute, attributeData ); + geometryBuffer[ attributeName ] = new Int32Array( numValues ); + TypedBufferAttribute = Int32BufferAttribute; + break; + + case Uint8Array: + attributeData = new dracoDecoder.DracoUInt8Array(); + decoder.GetAttributeUInt8ForAllPoints( + dracoGeometry, attribute, attributeData ); + geometryBuffer[ attributeName ] = new Uint8Array( numValues ); + TypedBufferAttribute = Uint8BufferAttribute; + break; + + case Uint16Array: + attributeData = new dracoDecoder.DracoUInt16Array(); + decoder.GetAttributeUInt16ForAllPoints( + dracoGeometry, attribute, attributeData ); + geometryBuffer[ attributeName ] = new Uint16Array( numValues ); + TypedBufferAttribute = Uint16BufferAttribute; + break; + + case Uint32Array: + attributeData = new dracoDecoder.DracoUInt32Array(); + decoder.GetAttributeUInt32ForAllPoints( + dracoGeometry, attribute, attributeData ); + geometryBuffer[ attributeName ] = new Uint32Array( numValues ); + TypedBufferAttribute = Uint32BufferAttribute; + break; + + default: + var errorMsg = 'DRACOLoader: Unexpected attribute type.'; + console.error( errorMsg ); + throw new Error( errorMsg ); + + } + + // Copy data from decoder. + for ( var i = 0; i < numValues; i ++ ) { + + geometryBuffer[ attributeName ][ i ] = attributeData.GetValue( i ); + + } + // Add attribute to THREEJS geometry for rendering. + geometry.addAttribute( attributeName, + new TypedBufferAttribute( geometryBuffer[ attributeName ], + numComponents ) ); + dracoDecoder.destroy( attributeData ); + + }, + + convertDracoGeometryTo3JS: function ( dracoDecoder, decoder, geometryType, + buffer, attributeUniqueIdMap, + attributeTypeMap ) { + + // TODO: Should not assume native Draco attribute IDs apply. + if ( this.getAttributeOptions( 'position' ).skipDequantization === true ) { + + decoder.SkipAttributeTransform( dracoDecoder.POSITION ); + + } + var dracoGeometry; + var decodingStatus; + var start_time = performance.now(); + if ( geometryType === dracoDecoder.TRIANGULAR_MESH ) { + + dracoGeometry = new dracoDecoder.Mesh(); + decodingStatus = decoder.DecodeBufferToMesh( buffer, dracoGeometry ); + + } else { + + dracoGeometry = new dracoDecoder.PointCloud(); + decodingStatus = + decoder.DecodeBufferToPointCloud( buffer, dracoGeometry ); + + } + if ( ! decodingStatus.ok() || dracoGeometry.ptr == 0 ) { + + var errorMsg = 'DRACOLoader: Decoding failed: '; + errorMsg += decodingStatus.error_msg(); + console.error( errorMsg ); + dracoDecoder.destroy( decoder ); + dracoDecoder.destroy( dracoGeometry ); + throw new Error( errorMsg ); + + } + + var decode_end = performance.now(); + dracoDecoder.destroy( buffer ); + /* + * Example on how to retrieve mesh and attributes. + */ + var numFaces; + if ( geometryType == dracoDecoder.TRIANGULAR_MESH ) { + + numFaces = dracoGeometry.num_faces(); + if ( this.verbosity > 0 ) { + + console.log( 'Number of faces loaded: ' + numFaces.toString() ); + + } + + } else { + + numFaces = 0; + + } + + var numPoints = dracoGeometry.num_points(); + var numAttributes = dracoGeometry.num_attributes(); + if ( this.verbosity > 0 ) { + + console.log( 'Number of points loaded: ' + numPoints.toString() ); + console.log( 'Number of attributes loaded: ' + + numAttributes.toString() ); + + } + + // Verify if there is position attribute. + // TODO: Should not assume native Draco attribute IDs apply. + var posAttId = decoder.GetAttributeId( dracoGeometry, + dracoDecoder.POSITION ); + if ( posAttId == - 1 ) { + + var errorMsg = 'DRACOLoader: No position attribute found.'; + console.error( errorMsg ); + dracoDecoder.destroy( decoder ); + dracoDecoder.destroy( dracoGeometry ); + throw new Error( errorMsg ); + + } + var posAttribute = decoder.GetAttribute( dracoGeometry, posAttId ); + + // Structure for converting to THREEJS geometry later. + var geometryBuffer = {}; + // Import data to Three JS geometry. + var geometry = new BufferGeometry(); + + // Do not use both the native attribute map and a provided (e.g. glTF) map. + if ( attributeUniqueIdMap ) { + + // Add attributes of user specified unique id. E.g. GLTF models. + for ( var attributeName in attributeUniqueIdMap ) { + + var attributeType = attributeTypeMap[ attributeName ]; + var attributeId = attributeUniqueIdMap[ attributeName ]; + var attribute = decoder.GetAttributeByUniqueId( dracoGeometry, + attributeId ); + this.addAttributeToGeometry( dracoDecoder, decoder, dracoGeometry, + attributeName, attributeType, attribute, geometry, geometryBuffer ); + + } + + } else { + + // Add native Draco attribute type to geometry. + for ( var attributeName in this.nativeAttributeMap ) { + + var attId = decoder.GetAttributeId( dracoGeometry, + dracoDecoder[ this.nativeAttributeMap[ attributeName ] ] ); + if ( attId !== - 1 ) { + + if ( this.verbosity > 0 ) { + + console.log( 'Loaded ' + attributeName + ' attribute.' ); + + } + var attribute = decoder.GetAttribute( dracoGeometry, attId ); + this.addAttributeToGeometry( dracoDecoder, decoder, dracoGeometry, + attributeName, Float32Array, attribute, geometry, geometryBuffer ); + + } + + } + + } + + // For mesh, we need to generate the faces. + if ( geometryType == dracoDecoder.TRIANGULAR_MESH ) { + + if ( this.drawMode === TriangleStripDrawMode ) { + + var stripsArray = new dracoDecoder.DracoInt32Array(); + var numStrips = decoder.GetTriangleStripsFromMesh( + dracoGeometry, stripsArray ); + geometryBuffer.indices = new Uint32Array( stripsArray.size() ); + for ( var i = 0; i < stripsArray.size(); ++ i ) { + + geometryBuffer.indices[ i ] = stripsArray.GetValue( i ); + + } + dracoDecoder.destroy( stripsArray ); + + } else { + + var numIndices = numFaces * 3; + geometryBuffer.indices = new Uint32Array( numIndices ); + var ia = new dracoDecoder.DracoInt32Array(); + for ( var i = 0; i < numFaces; ++ i ) { + + decoder.GetFaceFromMesh( dracoGeometry, i, ia ); + var index = i * 3; + geometryBuffer.indices[ index ] = ia.GetValue( 0 ); + geometryBuffer.indices[ index + 1 ] = ia.GetValue( 1 ); + geometryBuffer.indices[ index + 2 ] = ia.GetValue( 2 ); + + } + dracoDecoder.destroy( ia ); + + } + + } + + geometry.drawMode = this.drawMode; + if ( geometryType == dracoDecoder.TRIANGULAR_MESH ) { + + geometry.setIndex( new ( geometryBuffer.indices.length > 65535 ? + Uint32BufferAttribute : Uint16BufferAttribute) + ( geometryBuffer.indices, 1 ) ); + + } + + // TODO: Should not assume native Draco attribute IDs apply. + // TODO: Can other attribute types be quantized? + var posTransform = new dracoDecoder.AttributeQuantizationTransform(); + if ( posTransform.InitFromAttribute( posAttribute ) ) { + + // Quantized attribute. Store the quantization parameters into the + // THREE.js attribute. + geometry.attributes[ 'position' ].isQuantized = true; + geometry.attributes[ 'position' ].maxRange = posTransform.range(); + geometry.attributes[ 'position' ].numQuantizationBits = + posTransform.quantization_bits(); + geometry.attributes[ 'position' ].minValues = new Float32Array( 3 ); + for ( var i = 0; i < 3; ++ i ) { + + geometry.attributes[ 'position' ].minValues[ i ] = + posTransform.min_value( i ); + + } + + } + dracoDecoder.destroy( posTransform ); + dracoDecoder.destroy( decoder ); + dracoDecoder.destroy( dracoGeometry ); + + this.decode_time = decode_end - start_time; + this.import_time = performance.now() - decode_end; + + if ( this.verbosity > 0 ) { + + console.log( 'Decode time: ' + this.decode_time ); + console.log( 'Import time: ' + this.import_time ); + + } + return geometry; + + }, + + isVersionSupported: function ( version, callback ) { + + DRACOLoader.getDecoderModule() + .then( function ( module ) { + + callback( module.decoder.isVersionSupported( version ) ); + + } ); + + }, + + getAttributeOptions: function ( attributeName ) { + + if ( typeof this.attributeOptions[ attributeName ] === 'undefined' ) + this.attributeOptions[ attributeName ] = {}; + return this.attributeOptions[ attributeName ]; + + } +}; + +DRACOLoader.decoderPath = './'; +DRACOLoader.decoderConfig = {}; +DRACOLoader.decoderModulePromise = null; + +/** + * Sets the base path for decoder source files. + * @param {string} path + */ +DRACOLoader.setDecoderPath = function ( path ) { + + DRACOLoader.decoderPath = path; + +}; + +/** + * Sets decoder configuration and releases singleton decoder module. Module + * will be recreated with the next decoding call. + * @param {Object} config + */ +DRACOLoader.setDecoderConfig = function ( config ) { + + var wasmBinary = DRACOLoader.decoderConfig.wasmBinary; + DRACOLoader.decoderConfig = config || {}; + DRACOLoader.releaseDecoderModule(); + + // Reuse WASM binary. + if ( wasmBinary ) DRACOLoader.decoderConfig.wasmBinary = wasmBinary; + +}; + +/** + * Releases the singleton DracoDecoderModule instance. Module will be recreated + * with the next decoding call. + */ +DRACOLoader.releaseDecoderModule = function () { + + DRACOLoader.decoderModulePromise = null; + +}; + +/** + * Gets WebAssembly or asm.js singleton instance of DracoDecoderModule + * after testing for browser support. Returns Promise that resolves when + * module is available. + * @return {Promise<{decoder: DracoDecoderModule}>} + */ +DRACOLoader.getDecoderModule = function () { + + var scope = this; + var path = DRACOLoader.decoderPath; + var config = DRACOLoader.decoderConfig; + var promise = DRACOLoader.decoderModulePromise; + + if ( promise ) return promise; + + // Load source files. + if ( typeof DracoDecoderModule !== 'undefined' ) { + + // Loaded externally. + promise = Promise.resolve(); + + } else if ( typeof WebAssembly !== 'object' || config.type === 'js' ) { + + // Load with asm.js. + promise = DRACOLoader._loadScript( path + 'draco_decoder.js' ); + + } else { + + // Load with WebAssembly. + config.wasmBinaryFile = path + 'draco_decoder.wasm'; + promise = DRACOLoader._loadScript( path + 'draco_wasm_wrapper.js' ) + .then( function () { + + return DRACOLoader._loadArrayBuffer( config.wasmBinaryFile ); + + } ) + .then( function ( wasmBinary ) { + + config.wasmBinary = wasmBinary; + + } ); + + } + + // Wait for source files, then create and return a decoder. + promise = promise.then( function () { + + return new Promise( function ( resolve ) { + + config.onModuleLoaded = function ( decoder ) { + + scope.timeLoaded = performance.now(); + // Module is Promise-like. Wrap before resolving to avoid loop. + resolve( { decoder: decoder } ); + + }; + DracoDecoderModule( config ); + + } ); + + } ); + + DRACOLoader.decoderModulePromise = promise; + return promise; + +}; + +/** + * @param {string} src + * @return {Promise} + */ +DRACOLoader._loadScript = function ( src ) { + + var prevScript = document.getElementById( 'decoder_script' ); + if ( prevScript !== null ) { + + prevScript.parentNode.removeChild( prevScript ); + + } + var head = document.getElementsByTagName( 'head' )[ 0 ]; + var script = document.createElement( 'script' ); + script.id = 'decoder_script'; + script.type = 'text/javascript'; + script.src = src; + return new Promise( function ( resolve ) { + + script.onload = resolve; + head.appendChild( script ); + + } ); + +}; + +/** + * @param {string} src + * @return {Promise} + */ +DRACOLoader._loadArrayBuffer = function ( src ) { + + var loader = new FileLoader(); + loader.setResponseType( 'arraybuffer' ); + return new Promise( function ( resolve, reject ) { + + loader.load( src, resolve, undefined, reject ); + + } ); + +}; + +export { DRACOLoader }; diff --git a/examples/jsm/loaders/EXRLoader.js b/examples/jsm/loaders/EXRLoader.js new file mode 100644 index 00000000000000..11f0072d302e39 --- /dev/null +++ b/examples/jsm/loaders/EXRLoader.js @@ -0,0 +1,1203 @@ +/** + * @author Richard M. / https://github.com/richardmonette + * + * OpenEXR loader which, currently, supports reading 16 bit half data, in either + * uncompressed or PIZ wavelet compressed form. + * + * Referred to the original Industrial Light & Magic OpenEXR implementation and the TinyEXR / Syoyo Fujita + * implementation, so I have preserved their copyright notices. + */ + +// /* +// Copyright (c) 2014 - 2017, Syoyo Fujita +// All rights reserved. + +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of the Syoyo Fujita nor the +// names of its contributors may be used to endorse or promote products +// derived from this software without specific prior written permission. + +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY +// DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// */ + +// // TinyEXR contains some OpenEXR code, which is licensed under ------------ + +// /////////////////////////////////////////////////////////////////////////// +// // +// // Copyright (c) 2002, Industrial Light & Magic, a division of Lucas +// // Digital Ltd. LLC +// // +// // All rights reserved. +// // +// // Redistribution and use in source and binary forms, with or without +// // modification, are permitted provided that the following conditions are +// // met: +// // * Redistributions of source code must retain the above copyright +// // notice, this list of conditions and the following disclaimer. +// // * Redistributions in binary form must reproduce the above +// // copyright notice, this list of conditions and the following disclaimer +// // in the documentation and/or other materials provided with the +// // distribution. +// // * Neither the name of Industrial Light & Magic nor the names of +// // its contributors may be used to endorse or promote products derived +// // from this software without specific prior written permission. +// // +// // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// // +// /////////////////////////////////////////////////////////////////////////// + +// // End of OpenEXR license ------------------------------------------------- + +import { + DataTextureLoader, + DefaultLoadingManager, + FloatType, + RGBAFormat, + RGBFormat +} from "../../../build/three.module.js"; + +var EXRLoader = function ( manager ) { + + this.manager = ( manager !== undefined ) ? manager : DefaultLoadingManager; + +}; + +EXRLoader.prototype = Object.create( DataTextureLoader.prototype ); + +EXRLoader.prototype._parser = function ( buffer ) { + + const USHORT_RANGE = ( 1 << 16 ); + const BITMAP_SIZE = ( USHORT_RANGE >> 3 ); + + const HUF_ENCBITS = 16; // literal (value) bit length + const HUF_DECBITS = 14; // decoding bit size (>= 8) + + const HUF_ENCSIZE = ( 1 << HUF_ENCBITS ) + 1; // encoding table size + const HUF_DECSIZE = 1 << HUF_DECBITS; // decoding table size + const HUF_DECMASK = HUF_DECSIZE - 1; + + const SHORT_ZEROCODE_RUN = 59; + const LONG_ZEROCODE_RUN = 63; + const SHORTEST_LONG_RUN = 2 + LONG_ZEROCODE_RUN - SHORT_ZEROCODE_RUN; + const LONGEST_LONG_RUN = 255 + SHORTEST_LONG_RUN; + + const BYTES_PER_HALF = 2; + + const ULONG_SIZE = 8; + const FLOAT32_SIZE = 4; + const INT32_SIZE = 4; + const INT16_SIZE = 2; + const INT8_SIZE = 1; + + function reverseLutFromBitmap( bitmap, lut ) { + + var k = 0; + + for ( var i = 0; i < USHORT_RANGE; ++ i ) { + + if ( ( i == 0 ) || ( bitmap[ i >> 3 ] & ( 1 << ( i & 7 ) ) ) ) { + + lut[ k ++ ] = i; + + } + + } + + var n = k - 1; + + while ( k < USHORT_RANGE ) lut[ k ++ ] = 0; + + return n; + + } + + function hufClearDecTable( hdec ) { + + for ( var i = 0; i < HUF_DECSIZE; i ++ ) { + + hdec[ i ] = {}; + hdec[ i ].len = 0; + hdec[ i ].lit = 0; + hdec[ i ].p = null; + + } + + } + + const getBitsReturn = { l: 0, c: 0, lc: 0 }; + + function getBits( nBits, c, lc, uInt8Array, inOffset ) { + + while ( lc < nBits ) { + + c = ( c << 8 ) | parseUint8Array( uInt8Array, inOffset ); + lc += 8; + + } + + lc -= nBits; + + getBitsReturn.l = ( c >> lc ) & ( ( 1 << nBits ) - 1 ); + getBitsReturn.c = c; + getBitsReturn.lc = lc; + + } + + const hufTableBuffer = new Array( 59 ); + + function hufCanonicalCodeTable( hcode ) { + + for ( var i = 0; i <= 58; ++ i ) hufTableBuffer[ i ] = 0; + for ( var i = 0; i < HUF_ENCSIZE; ++ i ) hufTableBuffer[ hcode[ i ] ] += 1; + + var c = 0; + + for ( var i = 58; i > 0; -- i ) { + + var nc = ( ( c + hufTableBuffer[ i ] ) >> 1 ); + hufTableBuffer[ i ] = c; + c = nc; + + } + + for ( var i = 0; i < HUF_ENCSIZE; ++ i ) { + + var l = hcode[ i ]; + if ( l > 0 ) hcode[ i ] = l | ( hufTableBuffer[ l ] ++ << 6 ); + + } + + } + + function hufUnpackEncTable( uInt8Array, inDataView, inOffset, ni, im, iM, hcode ) { + + var p = inOffset; + var c = 0; + var lc = 0; + + for ( ; im <= iM; im ++ ) { + + if ( p.value - inOffset.value > ni ) return false; + + getBits( 6, c, lc, uInt8Array, p ); + + var l = getBitsReturn.l; + c = getBitsReturn.c; + lc = getBitsReturn.lc; + + hcode[ im ] = l; + + if ( l == LONG_ZEROCODE_RUN ) { + + if ( p.value - inOffset.value > ni ) { + + throw 'Something wrong with hufUnpackEncTable'; + + } + + getBits( 8, c, lc, uInt8Array, p ); + + var zerun = getBitsReturn.l + SHORTEST_LONG_RUN; + c = getBitsReturn.c; + lc = getBitsReturn.lc; + + if ( im + zerun > iM + 1 ) { + + throw 'Something wrong with hufUnpackEncTable'; + + } + + while ( zerun -- ) hcode[ im ++ ] = 0; + + im --; + + } else if ( l >= SHORT_ZEROCODE_RUN ) { + + var zerun = l - SHORT_ZEROCODE_RUN + 2; + + if ( im + zerun > iM + 1 ) { + + throw 'Something wrong with hufUnpackEncTable'; + + } + + while ( zerun -- ) hcode[ im ++ ] = 0; + + im --; + + } + + } + + hufCanonicalCodeTable( hcode ); + + } + + function hufLength( code ) { + + return code & 63; + + } + + function hufCode( code ) { + + return code >> 6; + + } + + function hufBuildDecTable( hcode, im, iM, hdecod ) { + + for ( ; im <= iM; im ++ ) { + + var c = hufCode( hcode[ im ] ); + var l = hufLength( hcode[ im ] ); + + if ( c >> l ) { + + throw 'Invalid table entry'; + + } + + if ( l > HUF_DECBITS ) { + + var pl = hdecod[ ( c >> ( l - HUF_DECBITS ) ) ]; + + if ( pl.len ) { + + throw 'Invalid table entry'; + + } + + pl.lit ++; + + if ( pl.p ) { + + var p = pl.p; + pl.p = new Array( pl.lit ); + + for ( var i = 0; i < pl.lit - 1; ++ i ) { + + pl.p[ i ] = p[ i ]; + + } + + } else { + + pl.p = new Array( 1 ); + + } + + pl.p[ pl.lit - 1 ] = im; + + } else if ( l ) { + + var plOffset = 0; + + for ( var i = 1 << ( HUF_DECBITS - l ); i > 0; i -- ) { + + var pl = hdecod[ ( c << ( HUF_DECBITS - l ) ) + plOffset ]; + + if ( pl.len || pl.p ) { + + throw 'Invalid table entry'; + + } + + pl.len = l; + pl.lit = im; + + plOffset ++; + + } + + } + + } + + return true; + + } + + const getCharReturn = { c: 0, lc: 0 }; + + function getChar( c, lc, uInt8Array, inOffset ) { + + c = ( c << 8 ) | parseUint8Array( uInt8Array, inOffset ); + lc += 8; + + getCharReturn.c = c; + getCharReturn.lc = lc; + + } + + const getCodeReturn = { c: 0, lc: 0 }; + + function getCode( po, rlc, c, lc, uInt8Array, inDataView, inOffset, outBuffer, outBufferOffset, outBufferEndOffset ) { + + if ( po == rlc ) { + + if ( lc < 8 ) { + + getChar( c, lc, uInt8Array, inOffset ); + c = getCharReturn.c; + lc = getCharReturn.lc; + + } + + lc -= 8; + + var cs = ( c >> lc ); + var cs = new Uint8Array( [ cs ] )[ 0 ]; + + if ( outBufferOffset.value + cs > outBufferEndOffset ) { + + return false; + + } + + var s = outBuffer[ outBufferOffset.value - 1 ]; + + while ( cs -- > 0 ) { + + outBuffer[ outBufferOffset.value ++ ] = s; + + } + + } else if ( outBufferOffset.value < outBufferEndOffset ) { + + outBuffer[ outBufferOffset.value ++ ] = po; + + } else { + + return false; + + } + + getCodeReturn.c = c; + getCodeReturn.lc = lc; + + } + + var NBITS = 16; + var A_OFFSET = 1 << ( NBITS - 1 ); + var M_OFFSET = 1 << ( NBITS - 1 ); + var MOD_MASK = ( 1 << NBITS ) - 1; + + function UInt16( value ) { + + return ( value & 0xFFFF ); + + } + + function Int16( value ) { + + var ref = UInt16( value ); + return ( ref > 0x7FFF ) ? ref - 0x10000 : ref; + + } + + const wdec14Return = { a: 0, b: 0 }; + + function wdec14( l, h ) { + + var ls = Int16( l ); + var hs = Int16( h ); + + var hi = hs; + var ai = ls + ( hi & 1 ) + ( hi >> 1 ); + + var as = ai; + var bs = ai - hi; + + wdec14Return.a = as; + wdec14Return.b = bs; + + } + + function wav2Decode( j, buffer, nx, ox, ny, oy, mx ) { + + var n = ( nx > ny ) ? ny : nx; + var p = 1; + var p2; + + while ( p <= n ) p <<= 1; + + p >>= 1; + p2 = p; + p >>= 1; + + while ( p >= 1 ) { + + var py = 0; + var ey = py + oy * ( ny - p2 ); + var oy1 = oy * p; + var oy2 = oy * p2; + var ox1 = ox * p; + var ox2 = ox * p2; + var i00, i01, i10, i11; + + for ( ; py <= ey; py += oy2 ) { + + var px = py; + var ex = py + ox * ( nx - p2 ); + + for ( ; px <= ex; px += ox2 ) { + + var p01 = px + ox1; + var p10 = px + oy1; + var p11 = p10 + ox1; + + wdec14( buffer[ px + j ], buffer[ p10 + j ] ); + + i00 = wdec14Return.a; + i10 = wdec14Return.b; + + wdec14( buffer[ p01 + j ], buffer[ p11 + j ] ); + + i01 = wdec14Return.a; + i11 = wdec14Return.b; + + wdec14( i00, i01 ); + + buffer[ px + j ] = wdec14Return.a; + buffer[ p01 + j ] = wdec14Return.b; + + wdec14( i10, i11 ); + + buffer[ p10 + j ] = wdec14Return.a; + buffer[ p11 + j ] = wdec14Return.b; + + } + + if ( nx & p ) { + + var p10 = px + oy1; + + wdec14( buffer[ px + j ], buffer[ p10 + j ] ); + + i00 = wdec14Return.a; + buffer[ p10 + j ] = wdec14Return.b; + + buffer[ px + j ] = i00; + + } + + } + + if ( ny & p ) { + + var px = py; + var ex = py + ox * ( nx - p2 ); + + for ( ; px <= ex; px += ox2 ) { + + var p01 = px + ox1; + + wdec14( buffer[ px + j ], buffer[ p01 + j ] ); + + i00 = wdec14Return.a; + buffer[ p01 + j ] = wdec14Return.b; + + buffer[ px + j ] = i00; + + } + + } + + p2 = p; + p >>= 1; + + } + + return py; + + } + + function hufDecode( encodingTable, decodingTable, uInt8Array, inDataView, inOffset, ni, rlc, no, outBuffer, outOffset ) { + + var c = 0; + var lc = 0; + var outBufferEndOffset = no; + var inOffsetEnd = Math.trunc( inOffset.value + ( ni + 7 ) / 8 ); + + while ( inOffset.value < inOffsetEnd ) { + + getChar( c, lc, uInt8Array, inOffset ); + + c = getCharReturn.c; + lc = getCharReturn.lc; + + while ( lc >= HUF_DECBITS ) { + + var index = ( c >> ( lc - HUF_DECBITS ) ) & HUF_DECMASK; + var pl = decodingTable[ index ]; + + if ( pl.len ) { + + lc -= pl.len; + + getCode( pl.lit, rlc, c, lc, uInt8Array, inDataView, inOffset, outBuffer, outOffset, outBufferEndOffset ); + + c = getCodeReturn.c; + lc = getCodeReturn.lc; + + } else { + + if ( ! pl.p ) { + + throw 'hufDecode issues'; + + } + + var j; + + for ( j = 0; j < pl.lit; j ++ ) { + + var l = hufLength( encodingTable[ pl.p[ j ] ] ); + + while ( lc < l && inOffset.value < inOffsetEnd ) { + + getChar( c, lc, uInt8Array, inOffset ); + + c = getCharReturn.c; + lc = getCharReturn.lc; + + } + + if ( lc >= l ) { + + if ( hufCode( encodingTable[ pl.p[ j ] ] ) == ( ( c >> ( lc - l ) ) & ( ( 1 << l ) - 1 ) ) ) { + + lc -= l; + + getCode( pl.p[ j ], rlc, c, lc, uInt8Array, inDataView, inOffset, outBuffer, outOffset, outBufferEndOffset ); + + c = getCodeReturn.c; + lc = getCodeReturn.lc; + + break; + + } + + } + + } + + if ( j == pl.lit ) { + + throw 'hufDecode issues'; + + } + + } + + } + + } + + var i = ( 8 - ni ) & 7; + + c >>= i; + lc -= i; + + while ( lc > 0 ) { + + var pl = decodingTable[ ( c << ( HUF_DECBITS - lc ) ) & HUF_DECMASK ]; + + if ( pl.len ) { + + lc -= pl.len; + + getCode( pl.lit, rlc, c, lc, uInt8Array, inDataView, inOffset, outBuffer, outOffset, outBufferEndOffset ); + + c = getCodeReturn.c; + lc = getCodeReturn.lc; + + } else { + + throw 'hufDecode issues'; + + } + + } + + return true; + + } + + function hufUncompress( uInt8Array, inDataView, inOffset, nCompressed, outBuffer, outOffset, nRaw ) { + + var initialInOffset = inOffset.value; + + var im = parseUint32( inDataView, inOffset ); + var iM = parseUint32( inDataView, inOffset ); + + inOffset.value += 4; + + var nBits = parseUint32( inDataView, inOffset ); + + inOffset.value += 4; + + if ( im < 0 || im >= HUF_ENCSIZE || iM < 0 || iM >= HUF_ENCSIZE ) { + + throw 'Something wrong with HUF_ENCSIZE'; + + } + + var freq = new Array( HUF_ENCSIZE ); + var hdec = new Array( HUF_DECSIZE ); + + hufClearDecTable( hdec ); + + var ni = nCompressed - ( inOffset.value - initialInOffset ); + + hufUnpackEncTable( uInt8Array, inDataView, inOffset, ni, im, iM, freq ); + + if ( nBits > 8 * ( nCompressed - ( inOffset.value - initialInOffset ) ) ) { + + throw 'Something wrong with hufUncompress'; + + } + + hufBuildDecTable( freq, im, iM, hdec ); + + hufDecode( freq, hdec, uInt8Array, inDataView, inOffset, nBits, iM, nRaw, outBuffer, outOffset ); + + } + + function applyLut( lut, data, nData ) { + + for ( var i = 0; i < nData; ++ i ) { + + data[ i ] = lut[ data[ i ] ]; + + } + + } + + function decompressPIZ( outBuffer, outOffset, uInt8Array, inDataView, inOffset, tmpBufSize, num_channels, exrChannelInfos, dataWidth, num_lines ) { + + var bitmap = new Uint8Array( BITMAP_SIZE ); + + var minNonZero = parseUint16( inDataView, inOffset ); + var maxNonZero = parseUint16( inDataView, inOffset ); + + if ( maxNonZero >= BITMAP_SIZE ) { + + throw 'Something is wrong with PIZ_COMPRESSION BITMAP_SIZE'; + + } + + if ( minNonZero <= maxNonZero ) { + + for ( var i = 0; i < maxNonZero - minNonZero + 1; i ++ ) { + + bitmap[ i + minNonZero ] = parseUint8( inDataView, inOffset ); + + } + + } + + var lut = new Uint16Array( USHORT_RANGE ); + var maxValue = reverseLutFromBitmap( bitmap, lut ); + + var length = parseUint32( inDataView, inOffset ); + + hufUncompress( uInt8Array, inDataView, inOffset, length, outBuffer, outOffset, tmpBufSize ); + + var pizChannelData = new Array( num_channels ); + + var outBufferEnd = 0; + + for ( var i = 0; i < num_channels; i ++ ) { + + var exrChannelInfo = exrChannelInfos[ i ]; + + var pixelSize = 2; // assumes HALF_FLOAT + + pizChannelData[ i ] = {}; + pizChannelData[ i ][ 'start' ] = outBufferEnd; + pizChannelData[ i ][ 'end' ] = pizChannelData[ i ][ 'start' ]; + pizChannelData[ i ][ 'nx' ] = dataWidth; + pizChannelData[ i ][ 'ny' ] = num_lines; + pizChannelData[ i ][ 'size' ] = 1; + + outBufferEnd += pizChannelData[ i ].nx * pizChannelData[ i ].ny * pizChannelData[ i ].size; + + } + + var fooOffset = 0; + + for ( var i = 0; i < num_channels; i ++ ) { + + for ( var j = 0; j < pizChannelData[ i ].size; ++ j ) { + + fooOffset += wav2Decode( + j + fooOffset, + outBuffer, + pizChannelData[ i ].nx, + pizChannelData[ i ].size, + pizChannelData[ i ].ny, + pizChannelData[ i ].nx * pizChannelData[ i ].size, + maxValue + ); + + } + + } + + applyLut( lut, outBuffer, outBufferEnd ); + + return true; + + } + + function parseNullTerminatedString( buffer, offset ) { + + var uintBuffer = new Uint8Array( buffer ); + var endOffset = 0; + + while ( uintBuffer[ offset.value + endOffset ] != 0 ) { + + endOffset += 1; + + } + + var stringValue = new TextDecoder().decode( + uintBuffer.slice( offset.value, offset.value + endOffset ) + ); + + offset.value = offset.value + endOffset + 1; + + return stringValue; + + } + + function parseFixedLengthString( buffer, offset, size ) { + + var stringValue = new TextDecoder().decode( + new Uint8Array( buffer ).slice( offset.value, offset.value + size ) + ); + + offset.value = offset.value + size; + + return stringValue; + + } + + function parseUlong( dataView, offset ) { + + var uLong = dataView.getUint32( 0, true ); + + offset.value = offset.value + ULONG_SIZE; + + return uLong; + + } + + function parseUint32( dataView, offset ) { + + var Uint32 = dataView.getUint32( offset.value, true ); + + offset.value = offset.value + INT32_SIZE; + + return Uint32; + + } + + function parseUint8Array( uInt8Array, offset ) { + + var Uint8 = uInt8Array[ offset.value ]; + + offset.value = offset.value + INT8_SIZE; + + return Uint8; + + } + + function parseUint8( dataView, offset ) { + + var Uint8 = dataView.getUint8( offset.value ); + + offset.value = offset.value + INT8_SIZE; + + return Uint8; + + } + + function parseFloat32( dataView, offset ) { + + var float = dataView.getFloat32( offset.value, true ); + + offset.value += FLOAT32_SIZE; + + return float; + + } + + // https://stackoverflow.com/questions/5678432/decompressing-half-precision-floats-in-javascript + function decodeFloat16( binary ) { + + var exponent = ( binary & 0x7C00 ) >> 10, + fraction = binary & 0x03FF; + + return ( binary >> 15 ? - 1 : 1 ) * ( + exponent ? + ( + exponent === 0x1F ? + fraction ? NaN : Infinity : + Math.pow( 2, exponent - 15 ) * ( 1 + fraction / 0x400 ) + ) : + 6.103515625e-5 * ( fraction / 0x400 ) + ); + + } + + function parseUint16( dataView, offset ) { + + var Uint16 = dataView.getUint16( offset.value, true ); + + offset.value += INT16_SIZE; + + return Uint16; + + } + + function parseFloat16( buffer, offset ) { + + return decodeFloat16( parseUint16( buffer, offset ) ); + + } + + function parseChlist( dataView, buffer, offset, size ) { + + var startOffset = offset.value; + var channels = []; + + while ( offset.value < ( startOffset + size - 1 ) ) { + + var name = parseNullTerminatedString( buffer, offset ); + var pixelType = parseUint32( dataView, offset ); // TODO: Cast this to UINT, HALF or FLOAT + var pLinear = parseUint8( dataView, offset ); + offset.value += 3; // reserved, three chars + var xSampling = parseUint32( dataView, offset ); + var ySampling = parseUint32( dataView, offset ); + + channels.push( { + name: name, + pixelType: pixelType, + pLinear: pLinear, + xSampling: xSampling, + ySampling: ySampling + } ); + + } + + offset.value += 1; + + return channels; + + } + + function parseChromaticities( dataView, offset ) { + + var redX = parseFloat32( dataView, offset ); + var redY = parseFloat32( dataView, offset ); + var greenX = parseFloat32( dataView, offset ); + var greenY = parseFloat32( dataView, offset ); + var blueX = parseFloat32( dataView, offset ); + var blueY = parseFloat32( dataView, offset ); + var whiteX = parseFloat32( dataView, offset ); + var whiteY = parseFloat32( dataView, offset ); + + return { redX: redX, redY: redY, greenX: greenX, greenY: greenY, blueX: blueX, blueY: blueY, whiteX: whiteX, whiteY: whiteY }; + + } + + function parseCompression( dataView, offset ) { + + var compressionCodes = [ + 'NO_COMPRESSION', + 'RLE_COMPRESSION', + 'ZIPS_COMPRESSION', + 'ZIP_COMPRESSION', + 'PIZ_COMPRESSION', + 'PXR24_COMPRESSION', + 'B44_COMPRESSION', + 'B44A_COMPRESSION', + 'DWAA_COMPRESSION', + 'DWAB_COMPRESSION' + ]; + + var compression = parseUint8( dataView, offset ); + + return compressionCodes[ compression ]; + + } + + function parseBox2i( dataView, offset ) { + + var xMin = parseUint32( dataView, offset ); + var yMin = parseUint32( dataView, offset ); + var xMax = parseUint32( dataView, offset ); + var yMax = parseUint32( dataView, offset ); + + return { xMin: xMin, yMin: yMin, xMax: xMax, yMax: yMax }; + + } + + function parseLineOrder( dataView, offset ) { + + var lineOrders = [ + 'INCREASING_Y' + ]; + + var lineOrder = parseUint8( dataView, offset ); + + return lineOrders[ lineOrder ]; + + } + + function parseV2f( dataView, offset ) { + + var x = parseFloat32( dataView, offset ); + var y = parseFloat32( dataView, offset ); + + return [ x, y ]; + + } + + function parseValue( dataView, buffer, offset, type, size ) { + + if ( type === 'string' || type === 'iccProfile' ) { + + return parseFixedLengthString( buffer, offset, size ); + + } else if ( type === 'chlist' ) { + + return parseChlist( dataView, buffer, offset, size ); + + } else if ( type === 'chromaticities' ) { + + return parseChromaticities( dataView, offset ); + + } else if ( type === 'compression' ) { + + return parseCompression( dataView, offset ); + + } else if ( type === 'box2i' ) { + + return parseBox2i( dataView, offset ); + + } else if ( type === 'lineOrder' ) { + + return parseLineOrder( dataView, offset ); + + } else if ( type === 'float' ) { + + return parseFloat32( dataView, offset ); + + } else if ( type === 'v2f' ) { + + return parseV2f( dataView, offset ); + + } else if ( type === 'int' ) { + + return parseUint32( dataView, offset ); + + } else { + + throw 'Cannot parse value for unsupported type: ' + type; + + } + + } + + var bufferDataView = new DataView( buffer ); + var uInt8Array = new Uint8Array( buffer ); + + var EXRHeader = {}; + + var magic = bufferDataView.getUint32( 0, true ); + var versionByteZero = bufferDataView.getUint8( 4, true ); + var fullMask = bufferDataView.getUint8( 5, true ); + + // start of header + + var offset = { value: 8 }; // start at 8, after magic stuff + + var keepReading = true; + + while ( keepReading ) { + + var attributeName = parseNullTerminatedString( buffer, offset ); + + if ( attributeName == 0 ) { + + keepReading = false; + + } else { + + var attributeType = parseNullTerminatedString( buffer, offset ); + var attributeSize = parseUint32( bufferDataView, offset ); + var attributeValue = parseValue( bufferDataView, buffer, offset, attributeType, attributeSize ); + + EXRHeader[ attributeName ] = attributeValue; + + } + + } + + // offsets + + var dataWindowHeight = EXRHeader.dataWindow.yMax + 1; + var scanlineBlockSize = 1; // 1 for NO_COMPRESSION + + if ( EXRHeader.compression === 'PIZ_COMPRESSION' ) { + + scanlineBlockSize = 32; + + } + + var numBlocks = dataWindowHeight / scanlineBlockSize; + + for ( var i = 0; i < numBlocks; i ++ ) { + + var scanlineOffset = parseUlong( bufferDataView, offset ); + + } + + // we should be passed the scanline offset table, start reading pixel data + + var width = EXRHeader.dataWindow.xMax - EXRHeader.dataWindow.xMin + 1; + var height = EXRHeader.dataWindow.yMax - EXRHeader.dataWindow.yMin + 1; + var numChannels = EXRHeader.channels.length; + + var byteArray = new Float32Array( width * height * numChannels ); + + var channelOffsets = { + R: 0, + G: 1, + B: 2, + A: 3 + }; + + if ( EXRHeader.compression === 'NO_COMPRESSION' ) { + + for ( var y = 0; y < height; y ++ ) { + + var y_scanline = parseUint32( bufferDataView, offset ); + var dataSize = parseUint32( bufferDataView, offset ); + + for ( var channelID = 0; channelID < EXRHeader.channels.length; channelID ++ ) { + + var cOff = channelOffsets[ EXRHeader.channels[ channelID ].name ]; + + if ( EXRHeader.channels[ channelID ].pixelType === 1 ) { + + // HALF + for ( var x = 0; x < width; x ++ ) { + + var val = parseFloat16( bufferDataView, offset ); + + byteArray[ ( ( ( height - y_scanline ) * ( width * numChannels ) ) + ( x * numChannels ) ) + cOff ] = val; + + } + + } else { + + throw 'EXRLoader._parser: unsupported pixelType ' + EXRHeader.channels[ channelID ].pixelType + '. Only pixelType is 1 (HALF) is supported.'; + + } + + } + + } + + } else if ( EXRHeader.compression === 'PIZ_COMPRESSION' ) { + + for ( var scanlineBlockIdx = 0; scanlineBlockIdx < height / scanlineBlockSize; scanlineBlockIdx ++ ) { + + var line_no = parseUint32( bufferDataView, offset ); + var data_len = parseUint32( bufferDataView, offset ); + + var tmpBufferSize = width * scanlineBlockSize * ( EXRHeader.channels.length * BYTES_PER_HALF ); + var tmpBuffer = new Uint16Array( tmpBufferSize ); + var tmpOffset = { value: 0 }; + + decompressPIZ( tmpBuffer, tmpOffset, uInt8Array, bufferDataView, offset, tmpBufferSize, numChannels, EXRHeader.channels, width, scanlineBlockSize ); + + for ( var line_y = 0; line_y < scanlineBlockSize; line_y ++ ) { + + for ( var channelID = 0; channelID < EXRHeader.channels.length; channelID ++ ) { + + var cOff = channelOffsets[ EXRHeader.channels[ channelID ].name ]; + + if ( EXRHeader.channels[ channelID ].pixelType === 1 ) { + + // HALF + for ( var x = 0; x < width; x ++ ) { + + var val = decodeFloat16( tmpBuffer[ ( channelID * ( scanlineBlockSize * width ) ) + ( line_y * width ) + x ] ); + + var true_y = line_y + ( scanlineBlockIdx * scanlineBlockSize ); + + byteArray[ ( ( ( height - true_y ) * ( width * numChannels ) ) + ( x * numChannels ) ) + cOff ] = val; + + } + + } else { + + throw 'EXRLoader._parser: unsupported pixelType ' + EXRHeader.channels[ channelID ].pixelType + '. Only pixelType is 1 (HALF) is supported.'; + + } + + } + + } + + } + + } else { + + throw 'EXRLoader._parser: ' + EXRHeader.compression + ' is unsupported'; + + } + + return { + header: EXRHeader, + width: width, + height: height, + data: byteArray, + format: EXRHeader.channels.length == 4 ? RGBAFormat : RGBFormat, + type: FloatType + }; + +}; + +export { EXRLoader }; diff --git a/examples/jsm/loaders/EquirectangularToCubeGenerator.js b/examples/jsm/loaders/EquirectangularToCubeGenerator.js new file mode 100644 index 00000000000000..bac353ab23ac10 --- /dev/null +++ b/examples/jsm/loaders/EquirectangularToCubeGenerator.js @@ -0,0 +1,242 @@ +/** + * @author Richard M. / https://github.com/richardmonette + * @author WestLangley / http://github.com/WestLangley + */ + +import { + BackSide, + BoxBufferGeometry, + CubeCamera, + Mesh, + NoBlending, + PerspectiveCamera, + Scene, + ShaderMaterial, + UniformsUtils, +} from "../../../build/three.module.js"; + +var CubemapGenerator = function ( renderer ) { + + this.renderer = renderer; + +}; + +CubemapGenerator.prototype.fromEquirectangular = function ( texture, options ) { + + var scene = new Scene(); + + var shader = { + + uniforms: { + tEquirect: { value: null }, + }, + + vertexShader: + + ` + varying vec3 vWorldDirection; + + //include + vec3 transformDirection( in vec3 dir, in mat4 matrix ) { + + return normalize( ( matrix * vec4( dir, 0.0 ) ).xyz ); + + } + + void main() { + + vWorldDirection = transformDirection( position, modelMatrix ); + + #include + #include + + } + `, + + fragmentShader: + + ` + uniform sampler2D tEquirect; + + varying vec3 vWorldDirection; + + //include + #define RECIPROCAL_PI 0.31830988618 + #define RECIPROCAL_PI2 0.15915494 + + void main() { + + vec3 direction = normalize( vWorldDirection ); + + vec2 sampleUV; + + sampleUV.y = asin( clamp( direction.y, - 1.0, 1.0 ) ) * RECIPROCAL_PI + 0.5; + + sampleUV.x = atan( direction.z, direction.x ) * RECIPROCAL_PI2 + 0.5; + + gl_FragColor = texture2D( tEquirect, sampleUV ); + + } + ` + }; + + var material = new ShaderMaterial( { + + type: 'CubemapFromEquirect', + + uniforms: UniformsUtils.clone( shader.uniforms ), + vertexShader: shader.vertexShader, + fragmentShader: shader.fragmentShader, + side: BackSide, + blending: NoBlending + + } ); + + material.uniforms.tEquirect.value = texture; + + var mesh = new Mesh( new BoxBufferGeometry( 5, 5, 5 ), material ); + + scene.add( mesh ); + + var resolution = options.resolution || 512; + + var params = { + type: texture.type, + format: texture.format, + encoding: texture.encoding, + generateMipmaps: ( options.generateMipmaps !== undefined ) ? options.generateMipmaps : texture.generateMipmaps, + minFilter: ( options.minFilter !== undefined ) ? options.minFilter : texture.minFilter, + magFilter: ( options.magFilter !== undefined ) ? options.magFilter : texture.magFilter + }; + + var camera = new CubeCamera( 1, 10, resolution, params ); + + camera.update( this.renderer, scene ); + + mesh.geometry.dispose(); + mesh.material.dispose(); + + return camera.renderTarget; + +}; + +// + +var EquirectangularToCubeGenerator = ( function () { + + var camera = new PerspectiveCamera( 90, 1, 0.1, 10 ); + var scene = new Scene(); + var boxMesh = new Mesh( new BoxBufferGeometry( 1, 1, 1 ), getShader() ); + boxMesh.material.side = BackSide; + scene.add( boxMesh ); + + var EquirectangularToCubeGenerator = function ( sourceTexture, options ) { + + this.sourceTexture = sourceTexture; + this.resolution = options.resolution || 512; + + this.views = [ + { t: [ 1, 0, 0 ], u: [ 0, - 1, 0 ] }, + { t: [ - 1, 0, 0 ], u: [ 0, - 1, 0 ] }, + { t: [ 0, 1, 0 ], u: [ 0, 0, 1 ] }, + { t: [ 0, - 1, 0 ], u: [ 0, 0, - 1 ] }, + { t: [ 0, 0, 1 ], u: [ 0, - 1, 0 ] }, + { t: [ 0, 0, - 1 ], u: [ 0, - 1, 0 ] }, + ]; + + var params = { + format: options.format || this.sourceTexture.format, + magFilter: this.sourceTexture.magFilter, + minFilter: this.sourceTexture.minFilter, + type: options.type || this.sourceTexture.type, + generateMipmaps: this.sourceTexture.generateMipmaps, + anisotropy: this.sourceTexture.anisotropy, + encoding: this.sourceTexture.encoding + }; + + this.renderTarget = new THREE.WebGLRenderTargetCube( this.resolution, this.resolution, params ); + + }; + + EquirectangularToCubeGenerator.prototype = { + + constructor: EquirectangularToCubeGenerator, + + update: function ( renderer ) { + + boxMesh.material.uniforms.equirectangularMap.value = this.sourceTexture; + + for ( var i = 0; i < 6; i ++ ) { + + this.renderTarget.activeCubeFace = i; + + var v = this.views[ i ]; + + camera.position.set( 0, 0, 0 ); + camera.up.set( v.u[ 0 ], v.u[ 1 ], v.u[ 2 ] ); + camera.lookAt( v.t[ 0 ], v.t[ 1 ], v.t[ 2 ] ); + + renderer.render( scene, camera, this.renderTarget, true ); + + } + + return this.renderTarget.texture; + + }, + + dispose: function () { + + this.renderTarget.dispose(); + + } + + }; + + function getShader() { + + var shaderMaterial = new THREE.ShaderMaterial( { + + uniforms: { + "equirectangularMap": { value: null }, + }, + + vertexShader: + "varying vec3 localPosition;\n\ + \n\ + void main() {\n\ + localPosition = position;\n\ + gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );\n\ + }", + + fragmentShader: + "#include \n\ + varying vec3 localPosition;\n\ + uniform sampler2D equirectangularMap;\n\ + \n\ + vec2 EquirectangularSampleUV(vec3 v) {\n\ + vec2 uv = vec2(atan(v.z, v.x), asin(v.y));\n\ + uv *= vec2(0.1591, 0.3183); // inverse atan\n\ + uv += 0.5;\n\ + return uv;\n\ + }\n\ + \n\ + void main() {\n\ + vec2 uv = EquirectangularSampleUV(normalize(localPosition));\n\ + gl_FragColor = texture2D(equirectangularMap, uv);\n\ + }", + + blending: THREE.NoBlending + + } ); + + shaderMaterial.type = 'EquirectangularToCubeGenerator'; + + return shaderMaterial; + + } + + return EquirectangularToCubeGenerator; + +} )(); + +export { EquirectangularToCubeGenerator }; diff --git a/examples/jsm/loaders/FBXLoader.js b/examples/jsm/loaders/FBXLoader.js new file mode 100644 index 00000000000000..eaefcaa7488630 --- /dev/null +++ b/examples/jsm/loaders/FBXLoader.js @@ -0,0 +1,4163 @@ +/** + * @author Kyle-Larson https://github.com/Kyle-Larson + * @author Takahiro https://github.com/takahirox + * @author Lewy Blue https://github.com/looeee + * + * Loader loads FBX file and generates Group representing FBX scene. + * Requires FBX file to be >= 7.0 and in ASCII or >= 6400 in Binary format + * Versions lower than this may load but will probably have errors + * + * Needs Support: + * Morph normals / blend shape normals + * + * FBX format references: + * https://wiki.blender.org/index.php/User:Mont29/Foundation/FBX_File_Structure + * http://help.autodesk.com/view/FBX/2017/ENU/?guid=__cpp_ref_index_html (C++ SDK reference) + * + * Binary format specification: + * https://code.blender.org/2013/08/fbx-binary-file-format-specification/ + */ + + +import { + AnimationClip, + AmbientLight, + Bone, + BufferAttribute, + BufferGeometry, + Color, + ClampToEdgeWrapping, + DefaultLoadingManager, + DirectionalLight, + Euler, + EquirectangularReflectionMapping, + FileLoader, + Float32BufferAttribute, + Group, + LoaderUtils, + Loader, + Line, + LineBasicMaterial, + Matrix3, + Matrix4, + Mesh, + MeshLambertMaterial, + MeshPhongMaterial, + NumberKeyframeTrack, + Object3D, + OrthographicCamera, + PerspectiveCamera, + PointLight, + PropertyBinding, + Quaternion, + QuaternionKeyframeTrack, + RepeatWrapping, + Skeleton, + SkinnedMesh, + SpotLight, + Texture, + TextureLoader, + Uint16BufferAttribute, + Vector3, + Vector4, + VectorKeyframeTrack, + VertexColors, +} from "../../../build/three.module.js"; +import { TGALoader } from './TGALoader'; +import { NURBSCurve } from "../curves/NURBSCurve"; + +var fbxTree; +var connections; +var sceneGraph; + +function FBXLoader( manager ) { + + this.manager = ( manager !== undefined ) ? manager : DefaultLoadingManager; + +} + +FBXLoader.prototype = { + + constructor: FBXLoader, + + crossOrigin: 'anonymous', + + load: function ( url, onLoad, onProgress, onError ) { + + var self = this; + + var path = ( self.path === undefined ) ? LoaderUtils.extractUrlBase( url ) : self.path; + + var loader = new FileLoader( this.manager ); + loader.setResponseType( 'arraybuffer' ); + + loader.load( url, function ( buffer ) { + + try { + + onLoad( self.parse( buffer, path ) ); + + } catch ( error ) { + + setTimeout( function () { + + if ( onError ) onError( error ); + + self.manager.itemError( url ); + + }, 0 ); + + } + + }, onProgress, onError ); + + }, + + setPath: function ( value ) { + + this.path = value; + return this; + + }, + + setResourcePath: function ( value ) { + + this.resourcePath = value; + return this; + + }, + + setCrossOrigin: function ( value ) { + + this.crossOrigin = value; + return this; + + }, + + parse: function ( FBXBuffer, path ) { + + if ( isFbxFormatBinary( FBXBuffer ) ) { + + fbxTree = new BinaryParser().parse( FBXBuffer ); + + } else { + + var FBXText = convertArrayBufferToString( FBXBuffer ); + + if ( ! isFbxFormatASCII( FBXText ) ) { + + throw new Error( 'THREE.FBXLoader: Unknown format.' ); + + } + + if ( getFbxVersion( FBXText ) < 7000 ) { + + throw new Error( 'THREE.FBXLoader: FBX version not supported, FileVersion: ' + getFbxVersion( FBXText ) ); + + } + + fbxTree = new TextParser().parse( FBXText ); + + } + + // console.log( fbxTree ); + + var textureLoader = new TextureLoader( this.manager ).setPath( this.resourcePath || path ).setCrossOrigin( this.crossOrigin ); + + return new FBXTreeParser( textureLoader ).parse( fbxTree ); + + } + +}; + +// Parse the FBXTree object returned by the BinaryParser or TextParser and return a Group +function FBXTreeParser( textureLoader ) { + + this.textureLoader = textureLoader; + +} + +FBXTreeParser.prototype = { + + constructor: FBXTreeParser, + + parse: function () { + + connections = this.parseConnections(); + + var images = this.parseImages(); + var textures = this.parseTextures( images ); + var materials = this.parseMaterials( textures ); + var deformers = this.parseDeformers(); + var geometryMap = new GeometryParser().parse( deformers ); + + this.parseScene( deformers, geometryMap, materials ); + + return sceneGraph; + + }, + + // Parses FBXTree.Connections which holds parent-child connections between objects (e.g. material -> texture, model->geometry ) + // and details the connection type + parseConnections: function () { + + var connectionMap = new Map(); + + if ( 'Connections' in fbxTree ) { + + var rawConnections = fbxTree.Connections.connections; + + rawConnections.forEach( function ( rawConnection ) { + + var fromID = rawConnection[ 0 ]; + var toID = rawConnection[ 1 ]; + var relationship = rawConnection[ 2 ]; + + if ( ! connectionMap.has( fromID ) ) { + + connectionMap.set( fromID, { + parents: [], + children: [] + } ); + + } + + var parentRelationship = { ID: toID, relationship: relationship }; + connectionMap.get( fromID ).parents.push( parentRelationship ); + + if ( ! connectionMap.has( toID ) ) { + + connectionMap.set( toID, { + parents: [], + children: [] + } ); + + } + + var childRelationship = { ID: fromID, relationship: relationship }; + connectionMap.get( toID ).children.push( childRelationship ); + + } ); + + } + + return connectionMap; + + }, + + // Parse FBXTree.Objects.Video for embedded image data + // These images are connected to textures in FBXTree.Objects.Textures + // via FBXTree.Connections. + parseImages: function () { + + var images = {}; + var blobs = {}; + + if ( 'Video' in fbxTree.Objects ) { + + var videoNodes = fbxTree.Objects.Video; + + for ( var nodeID in videoNodes ) { + + var videoNode = videoNodes[ nodeID ]; + + var id = parseInt( nodeID ); + + images[ id ] = videoNode.RelativeFilename || videoNode.Filename; + + // raw image data is in videoNode.Content + if ( 'Content' in videoNode ) { + + var arrayBufferContent = ( videoNode.Content instanceof ArrayBuffer ) && ( videoNode.Content.byteLength > 0 ); + var base64Content = ( typeof videoNode.Content === 'string' ) && ( videoNode.Content !== '' ); + + if ( arrayBufferContent || base64Content ) { + + var image = this.parseImage( videoNodes[ nodeID ] ); + + blobs[ videoNode.RelativeFilename || videoNode.Filename ] = image; + + } + + } + + } + + } + + for ( var id in images ) { + + var filename = images[ id ]; + + if ( blobs[ filename ] !== undefined ) images[ id ] = blobs[ filename ]; + else images[ id ] = images[ id ].split( '\\' ).pop(); + + } + + return images; + + }, + + // Parse embedded image data in FBXTree.Video.Content + parseImage: function ( videoNode ) { + + var content = videoNode.Content; + var fileName = videoNode.RelativeFilename || videoNode.Filename; + var extension = fileName.slice( fileName.lastIndexOf( '.' ) + 1 ).toLowerCase(); + + var type; + + switch ( extension ) { + + case 'bmp': + + type = 'image/bmp'; + break; + + case 'jpg': + case 'jpeg': + + type = 'image/jpeg'; + break; + + case 'png': + + type = 'image/png'; + break; + + case 'tif': + + type = 'image/tiff'; + break; + + case 'tga': + + if ( typeof TGALoader !== 'function' ) { + + console.warn( 'FBXLoader: THREE.TGALoader is required to load TGA textures' ); + return; + + } else { + + if ( Loader.Handlers.get( '.tga' ) === null ) { + + var tgaLoader = new TGALoader(); + tgaLoader.setPath( this.textureLoader.path ); + + Loader.Handlers.add( /\.tga$/i, tgaLoader ); + + } + + type = 'image/tga'; + break; + + } + + default: + + console.warn( 'FBXLoader: Image type "' + extension + '" is not supported.' ); + return; + + } + + if ( typeof content === 'string' ) { // ASCII format + + return 'data:' + type + ';base64,' + content; + + } else { // Binary Format + + var array = new Uint8Array( content ); + return window.URL.createObjectURL( new Blob( [ array ], { type: type } ) ); + + } + + }, + + // Parse nodes in FBXTree.Objects.Texture + // These contain details such as UV scaling, cropping, rotation etc and are connected + // to images in FBXTree.Objects.Video + parseTextures: function ( images ) { + + var textureMap = new Map(); + + if ( 'Texture' in fbxTree.Objects ) { + + var textureNodes = fbxTree.Objects.Texture; + for ( var nodeID in textureNodes ) { + + var texture = this.parseTexture( textureNodes[ nodeID ], images ); + textureMap.set( parseInt( nodeID ), texture ); + + } + + } + + return textureMap; + + }, + + // Parse individual node in FBXTree.Objects.Texture + parseTexture: function ( textureNode, images ) { + + var texture = this.loadTexture( textureNode, images ); + + texture.ID = textureNode.id; + + texture.name = textureNode.attrName; + + var wrapModeU = textureNode.WrapModeU; + var wrapModeV = textureNode.WrapModeV; + + var valueU = wrapModeU !== undefined ? wrapModeU.value : 0; + var valueV = wrapModeV !== undefined ? wrapModeV.value : 0; + + // http://download.autodesk.com/us/fbx/SDKdocs/FBX_SDK_Help/files/fbxsdkref/class_k_fbx_texture.html#889640e63e2e681259ea81061b85143a + // 0: repeat(default), 1: clamp + + texture.wrapS = valueU === 0 ? RepeatWrapping : ClampToEdgeWrapping; + texture.wrapT = valueV === 0 ? RepeatWrapping : ClampToEdgeWrapping; + + if ( 'Scaling' in textureNode ) { + + var values = textureNode.Scaling.value; + + texture.repeat.x = values[ 0 ]; + texture.repeat.y = values[ 1 ]; + + } + + return texture; + + }, + + // load a texture specified as a blob or data URI, or via an external URL using TextureLoader + loadTexture: function ( textureNode, images ) { + + var fileName; + + var currentPath = this.textureLoader.path; + + var children = connections.get( textureNode.id ).children; + + if ( children !== undefined && children.length > 0 && images[ children[ 0 ].ID ] !== undefined ) { + + fileName = images[ children[ 0 ].ID ]; + + if ( fileName.indexOf( 'blob:' ) === 0 || fileName.indexOf( 'data:' ) === 0 ) { + + this.textureLoader.setPath( undefined ); + + } + + } + + var texture; + + var extension = textureNode.FileName.slice( - 3 ).toLowerCase(); + + if ( extension === 'tga' ) { + + var loader = Loader.Handlers.get( '.tga' ); + + if ( loader === null ) { + + console.warn( 'FBXLoader: TGALoader not found, creating empty placeholder texture for', fileName ); + texture = new Texture(); + + } else { + + texture = loader.load( fileName ); + + } + + } else if ( extension === 'psd' ) { + + console.warn( 'FBXLoader: PSD textures are not supported, creating empty placeholder texture for', fileName ); + texture = new Texture(); + + } else { + + texture = this.textureLoader.load( fileName ); + + } + + this.textureLoader.setPath( currentPath ); + + return texture; + + }, + + // Parse nodes in FBXTree.Objects.Material + parseMaterials: function ( textureMap ) { + + var materialMap = new Map(); + + if ( 'Material' in fbxTree.Objects ) { + + var materialNodes = fbxTree.Objects.Material; + + for ( var nodeID in materialNodes ) { + + var material = this.parseMaterial( materialNodes[ nodeID ], textureMap ); + + if ( material !== null ) materialMap.set( parseInt( nodeID ), material ); + + } + + } + + return materialMap; + + }, + + // Parse single node in FBXTree.Objects.Material + // Materials are connected to texture maps in FBXTree.Objects.Textures + // FBX format currently only supports Lambert and Phong shading models + parseMaterial: function ( materialNode, textureMap ) { + + var ID = materialNode.id; + var name = materialNode.attrName; + var type = materialNode.ShadingModel; + + // Case where FBX wraps shading model in property object. + if ( typeof type === 'object' ) { + + type = type.value; + + } + + // Ignore unused materials which don't have any connections. + if ( ! connections.has( ID ) ) return null; + + var parameters = this.parseParameters( materialNode, textureMap, ID ); + + var material; + + switch ( type.toLowerCase() ) { + + case 'phong': + material = new MeshPhongMaterial(); + break; + case 'lambert': + material = new MeshLambertMaterial(); + break; + default: + console.warn( 'THREE.FBXLoader: unknown material type "%s". Defaulting to MeshPhongMaterial.', type ); + material = new MeshPhongMaterial( { color: 0x3300ff } ); + break; + + } + + material.setValues( parameters ); + material.name = name; + + return material; + + }, + + // Parse FBX material and return parameters suitable for a three.js material + // Also parse the texture map and return any textures associated with the material + parseParameters: function ( materialNode, textureMap, ID ) { + + var parameters = {}; + + if ( materialNode.BumpFactor ) { + + parameters.bumpScale = materialNode.BumpFactor.value; + + } + if ( materialNode.Diffuse ) { + + parameters.color = new Color().fromArray( materialNode.Diffuse.value ); + + } else if ( materialNode.DiffuseColor && materialNode.DiffuseColor.type === 'Color' ) { + + // The blender exporter exports diffuse here instead of in materialNode.Diffuse + parameters.color = new Color().fromArray( materialNode.DiffuseColor.value ); + + } + if ( materialNode.DisplacementFactor ) { + + parameters.displacementScale = materialNode.DisplacementFactor.value; + + } + if ( materialNode.Emissive ) { + + parameters.emissive = new Color().fromArray( materialNode.Emissive.value ); + + } else if ( materialNode.EmissiveColor && materialNode.EmissiveColor.type === 'Color' ) { + + // The blender exporter exports emissive color here instead of in materialNode.Emissive + parameters.emissive = new Color().fromArray( materialNode.EmissiveColor.value ); + + } + if ( materialNode.EmissiveFactor ) { + + parameters.emissiveIntensity = parseFloat( materialNode.EmissiveFactor.value ); + + } + if ( materialNode.Opacity ) { + + parameters.opacity = parseFloat( materialNode.Opacity.value ); + + } + if ( parameters.opacity < 1.0 ) { + + parameters.transparent = true; + + } + if ( materialNode.ReflectionFactor ) { + + parameters.reflectivity = materialNode.ReflectionFactor.value; + + } + if ( materialNode.Shininess ) { + + parameters.shininess = materialNode.Shininess.value; + + } + if ( materialNode.Specular ) { + + parameters.specular = new Color().fromArray( materialNode.Specular.value ); + + } else if ( materialNode.SpecularColor && materialNode.SpecularColor.type === 'Color' ) { + + // The blender exporter exports specular color here instead of in materialNode.Specular + parameters.specular = new Color().fromArray( materialNode.SpecularColor.value ); + + } + + var self = this; + connections.get( ID ).children.forEach( function ( child ) { + + var type = child.relationship; + + switch ( type ) { + + case 'Bump': + parameters.bumpMap = self.getTexture( textureMap, child.ID ); + break; + + case 'DiffuseColor': + parameters.map = self.getTexture( textureMap, child.ID ); + break; + + case 'DisplacementColor': + parameters.displacementMap = self.getTexture( textureMap, child.ID ); + break; + + + case 'EmissiveColor': + parameters.emissiveMap = self.getTexture( textureMap, child.ID ); + break; + + case 'NormalMap': + parameters.normalMap = self.getTexture( textureMap, child.ID ); + break; + + case 'ReflectionColor': + parameters.envMap = self.getTexture( textureMap, child.ID ); + parameters.envMap.mapping = EquirectangularReflectionMapping; + break; + + case 'SpecularColor': + parameters.specularMap = self.getTexture( textureMap, child.ID ); + break; + + case 'TransparentColor': + parameters.alphaMap = self.getTexture( textureMap, child.ID ); + parameters.transparent = true; + break; + + case 'AmbientColor': + case 'ShininessExponent': // AKA glossiness map + case 'SpecularFactor': // AKA specularLevel + case 'VectorDisplacementColor': // NOTE: Seems to be a copy of DisplacementColor + default: + console.warn( 'THREE.FBXLoader: %s map is not supported in three.js, skipping texture.', type ); + break; + + } + + } ); + + return parameters; + + }, + + // get a texture from the textureMap for use by a material. + getTexture: function ( textureMap, id ) { + + // if the texture is a layered texture, just use the first layer and issue a warning + if ( 'LayeredTexture' in fbxTree.Objects && id in fbxTree.Objects.LayeredTexture ) { + + console.warn( 'THREE.FBXLoader: layered textures are not supported in three.js. Discarding all but first layer.' ); + id = connections.get( id ).children[ 0 ].ID; + + } + + return textureMap.get( id ); + + }, + + // Parse nodes in FBXTree.Objects.Deformer + // Deformer node can contain skinning or Vertex Cache animation data, however only skinning is supported here + // Generates map of Skeleton-like objects for use later when generating and binding skeletons. + parseDeformers: function () { + + var skeletons = {}; + var morphTargets = {}; + + if ( 'Deformer' in fbxTree.Objects ) { + + var DeformerNodes = fbxTree.Objects.Deformer; + + for ( var nodeID in DeformerNodes ) { + + var deformerNode = DeformerNodes[ nodeID ]; + + var relationships = connections.get( parseInt( nodeID ) ); + + if ( deformerNode.attrType === 'Skin' ) { + + var skeleton = this.parseSkeleton( relationships, DeformerNodes ); + skeleton.ID = nodeID; + + if ( relationships.parents.length > 1 ) console.warn( 'THREE.FBXLoader: skeleton attached to more than one geometry is not supported.' ); + skeleton.geometryID = relationships.parents[ 0 ].ID; + + skeletons[ nodeID ] = skeleton; + + } else if ( deformerNode.attrType === 'BlendShape' ) { + + var morphTarget = { + id: nodeID, + }; + + morphTarget.rawTargets = this.parseMorphTargets( relationships, DeformerNodes ); + morphTarget.id = nodeID; + + if ( relationships.parents.length > 1 ) console.warn( 'THREE.FBXLoader: morph target attached to more than one geometry is not supported.' ); + + morphTargets[ nodeID ] = morphTarget; + + } + + } + + } + + return { + + skeletons: skeletons, + morphTargets: morphTargets, + + }; + + }, + + // Parse single nodes in FBXTree.Objects.Deformer + // The top level skeleton node has type 'Skin' and sub nodes have type 'Cluster' + // Each skin node represents a skeleton and each cluster node represents a bone + parseSkeleton: function ( relationships, deformerNodes ) { + + var rawBones = []; + + relationships.children.forEach( function ( child ) { + + var boneNode = deformerNodes[ child.ID ]; + + if ( boneNode.attrType !== 'Cluster' ) return; + + var rawBone = { + + ID: child.ID, + indices: [], + weights: [], + transformLink: new Matrix4().fromArray( boneNode.TransformLink.a ), + // transform: new Matrix4().fromArray( boneNode.Transform.a ), + // linkMode: boneNode.Mode, + + }; + + if ( 'Indexes' in boneNode ) { + + rawBone.indices = boneNode.Indexes.a; + rawBone.weights = boneNode.Weights.a; + + } + + rawBones.push( rawBone ); + + } ); + + return { + + rawBones: rawBones, + bones: [] + + }; + + }, + + // The top level morph deformer node has type "BlendShape" and sub nodes have type "BlendShapeChannel" + parseMorphTargets: function ( relationships, deformerNodes ) { + + var rawMorphTargets = []; + + for ( var i = 0; i < relationships.children.length; i ++ ) { + + var child = relationships.children[ i ]; + + var morphTargetNode = deformerNodes[ child.ID ]; + + var rawMorphTarget = { + + name: morphTargetNode.attrName, + initialWeight: morphTargetNode.DeformPercent, + id: morphTargetNode.id, + fullWeights: morphTargetNode.FullWeights.a + + }; + + if ( morphTargetNode.attrType !== 'BlendShapeChannel' ) return; + + rawMorphTarget.geoID = connections.get( parseInt( child.ID ) ).children.filter( function ( child ) { + + return child.relationship === undefined; + + } )[ 0 ].ID; + + rawMorphTargets.push( rawMorphTarget ); + + } + + return rawMorphTargets; + + }, + + // create the main Group() to be returned by the loader + parseScene: function ( deformers, geometryMap, materialMap ) { + + sceneGraph = new Group(); + + var modelMap = this.parseModels( deformers.skeletons, geometryMap, materialMap ); + + var modelNodes = fbxTree.Objects.Model; + + var self = this; + modelMap.forEach( function ( model ) { + + var modelNode = modelNodes[ model.ID ]; + self.setLookAtProperties( model, modelNode ); + + var parentConnections = connections.get( model.ID ).parents; + + parentConnections.forEach( function ( connection ) { + + var parent = modelMap.get( connection.ID ); + if ( parent !== undefined ) parent.add( model ); + + } ); + + if ( model.parent === null ) { + + sceneGraph.add( model ); + + } + + + } ); + + this.bindSkeleton( deformers.skeletons, geometryMap, modelMap ); + + this.createAmbientLight(); + + this.setupMorphMaterials(); + + sceneGraph.traverse( function ( node ) { + + if ( node.userData.transformData ) { + + if ( node.parent ) node.userData.transformData.parentMatrixWorld = node.parent.matrix; + + var transform = generateTransform( node.userData.transformData ); + + node.applyMatrix( transform ); + + } + + } ); + + var animations = new AnimationParser().parse(); + + // if all the models where already combined in a single group, just return that + if ( sceneGraph.children.length === 1 && sceneGraph.children[ 0 ].isGroup ) { + + sceneGraph.children[ 0 ].animations = animations; + sceneGraph = sceneGraph.children[ 0 ]; + + } + + sceneGraph.animations = animations; + + }, + + // parse nodes in FBXTree.Objects.Model + parseModels: function ( skeletons, geometryMap, materialMap ) { + + var modelMap = new Map(); + var modelNodes = fbxTree.Objects.Model; + + for ( var nodeID in modelNodes ) { + + var id = parseInt( nodeID ); + var node = modelNodes[ nodeID ]; + var relationships = connections.get( id ); + + var model = this.buildSkeleton( relationships, skeletons, id, node.attrName ); + + if ( ! model ) { + + switch ( node.attrType ) { + + case 'Camera': + model = this.createCamera( relationships ); + break; + case 'Light': + model = this.createLight( relationships ); + break; + case 'Mesh': + model = this.createMesh( relationships, geometryMap, materialMap ); + break; + case 'NurbsCurve': + model = this.createCurve( relationships, geometryMap ); + break; + case 'LimbNode': + case 'Root': + model = new Bone(); + break; + case 'Null': + default: + model = new Group(); + break; + + } + + model.name = PropertyBinding.sanitizeNodeName( node.attrName ); + model.ID = id; + + } + + this.getTransformData( model, node ); + modelMap.set( id, model ); + + } + + return modelMap; + + }, + + buildSkeleton: function ( relationships, skeletons, id, name ) { + + var bone = null; + + relationships.parents.forEach( function ( parent ) { + + for ( var ID in skeletons ) { + + var skeleton = skeletons[ ID ]; + + skeleton.rawBones.forEach( function ( rawBone, i ) { + + if ( rawBone.ID === parent.ID ) { + + var subBone = bone; + bone = new Bone(); + + bone.matrixWorld.copy( rawBone.transformLink ); + + // set name and id here - otherwise in cases where "subBone" is created it will not have a name / id + bone.name = PropertyBinding.sanitizeNodeName( name ); + bone.ID = id; + + skeleton.bones[ i ] = bone; + + // In cases where a bone is shared between multiple meshes + // duplicate the bone here and and it as a child of the first bone + if ( subBone !== null ) { + + bone.add( subBone ); + + } + + } + + } ); + + } + + } ); + + return bone; + + }, + + // create a PerspectiveCamera or OrthographicCamera + createCamera: function ( relationships ) { + + var model; + var cameraAttribute; + + relationships.children.forEach( function ( child ) { + + var attr = fbxTree.Objects.NodeAttribute[ child.ID ]; + + if ( attr !== undefined ) { + + cameraAttribute = attr; + + } + + } ); + + if ( cameraAttribute === undefined ) { + + model = new Object3D(); + + } else { + + var type = 0; + if ( cameraAttribute.CameraProjectionType !== undefined && cameraAttribute.CameraProjectionType.value === 1 ) { + + type = 1; + + } + + var nearClippingPlane = 1; + if ( cameraAttribute.NearPlane !== undefined ) { + + nearClippingPlane = cameraAttribute.NearPlane.value / 1000; + + } + + var farClippingPlane = 1000; + if ( cameraAttribute.FarPlane !== undefined ) { + + farClippingPlane = cameraAttribute.FarPlane.value / 1000; + + } + + + var width = window.innerWidth; + var height = window.innerHeight; + + if ( cameraAttribute.AspectWidth !== undefined && cameraAttribute.AspectHeight !== undefined ) { + + width = cameraAttribute.AspectWidth.value; + height = cameraAttribute.AspectHeight.value; + + } + + var aspect = width / height; + + var fov = 45; + if ( cameraAttribute.FieldOfView !== undefined ) { + + fov = cameraAttribute.FieldOfView.value; + + } + + var focalLength = cameraAttribute.FocalLength ? cameraAttribute.FocalLength.value : null; + + switch ( type ) { + + case 0: // Perspective + model = new PerspectiveCamera( fov, aspect, nearClippingPlane, farClippingPlane ); + if ( focalLength !== null ) model.setFocalLength( focalLength ); + break; + + case 1: // Orthographic + model = new OrthographicCamera( - width / 2, width / 2, height / 2, - height / 2, nearClippingPlane, farClippingPlane ); + break; + + default: + console.warn( 'THREE.FBXLoader: Unknown camera type ' + type + '.' ); + model = new Object3D(); + break; + + } + + } + + return model; + + }, + + // Create a DirectionalLight, PointLight or SpotLight + createLight: function ( relationships ) { + + var model; + var lightAttribute; + + relationships.children.forEach( function ( child ) { + + var attr = fbxTree.Objects.NodeAttribute[ child.ID ]; + + if ( attr !== undefined ) { + + lightAttribute = attr; + + } + + } ); + + if ( lightAttribute === undefined ) { + + model = new Object3D(); + + } else { + + var type; + + // LightType can be undefined for Point lights + if ( lightAttribute.LightType === undefined ) { + + type = 0; + + } else { + + type = lightAttribute.LightType.value; + + } + + var color = 0xffffff; + + if ( lightAttribute.Color !== undefined ) { + + color = new Color().fromArray( lightAttribute.Color.value ); + + } + + var intensity = ( lightAttribute.Intensity === undefined ) ? 1 : lightAttribute.Intensity.value / 100; + + // light disabled + if ( lightAttribute.CastLightOnObject !== undefined && lightAttribute.CastLightOnObject.value === 0 ) { + + intensity = 0; + + } + + var distance = 0; + if ( lightAttribute.FarAttenuationEnd !== undefined ) { + + if ( lightAttribute.EnableFarAttenuation !== undefined && lightAttribute.EnableFarAttenuation.value === 0 ) { + + distance = 0; + + } else { + + distance = lightAttribute.FarAttenuationEnd.value; + + } + + } + + // TODO: could this be calculated linearly from FarAttenuationStart to FarAttenuationEnd? + var decay = 1; + + switch ( type ) { + + case 0: // Point + model = new PointLight( color, intensity, distance, decay ); + break; + + case 1: // Directional + model = new DirectionalLight( color, intensity ); + break; + + case 2: // Spot + var angle = Math.PI / 3; + + if ( lightAttribute.InnerAngle !== undefined ) { + + angle = Math.degToRad( lightAttribute.InnerAngle.value ); + + } + + var penumbra = 0; + if ( lightAttribute.OuterAngle !== undefined ) { + + // TODO: this is not correct - FBX calculates outer and inner angle in degrees + // with OuterAngle > InnerAngle && OuterAngle <= Math.PI + // while three.js uses a penumbra between (0, 1) to attenuate the inner angle + penumbra = Math.degToRad( lightAttribute.OuterAngle.value ); + penumbra = Math.max( penumbra, 1 ); + + } + + model = new SpotLight( color, intensity, distance, angle, penumbra, decay ); + break; + + default: + console.warn( 'THREE.FBXLoader: Unknown light type ' + lightAttribute.LightType.value + ', defaulting to a PointLight.' ); + model = new PointLight( color, intensity ); + break; + + } + + if ( lightAttribute.CastShadows !== undefined && lightAttribute.CastShadows.value === 1 ) { + + model.castShadow = true; + + } + + } + + return model; + + }, + + createMesh: function ( relationships, geometryMap, materialMap ) { + + var model; + var geometry = null; + var material = null; + var materials = []; + + // get geometry and materials(s) from connections + relationships.children.forEach( function ( child ) { + + if ( geometryMap.has( child.ID ) ) { + + geometry = geometryMap.get( child.ID ); + + } + + if ( materialMap.has( child.ID ) ) { + + materials.push( materialMap.get( child.ID ) ); + + } + + } ); + + if ( materials.length > 1 ) { + + material = materials; + + } else if ( materials.length > 0 ) { + + material = materials[ 0 ]; + + } else { + + material = new MeshPhongMaterial( { color: 0xcccccc } ); + materials.push( material ); + + } + + if ( 'color' in geometry.attributes ) { + + materials.forEach( function ( material ) { + + material.vertexColors = VertexColors; + + } ); + + } + + if ( geometry.FBX_Deformer ) { + + materials.forEach( function ( material ) { + + material.skinning = true; + + } ); + + model = new SkinnedMesh( geometry, material ); + model.normalizeSkinWeights(); + + } else { + + model = new Mesh( geometry, material ); + + } + + return model; + + }, + + createCurve: function ( relationships, geometryMap ) { + + var geometry = relationships.children.reduce( function ( geo, child ) { + + if ( geometryMap.has( child.ID ) ) geo = geometryMap.get( child.ID ); + + return geo; + + }, null ); + + // FBX does not list materials for Nurbs lines, so we'll just put our own in here. + var material = new LineBasicMaterial( { color: 0x3300ff, linewidth: 1 } ); + return new Line( geometry, material ); + + }, + + // parse the model node for transform data + getTransformData: function ( model, modelNode ) { + + var transformData = {}; + + if ( 'InheritType' in modelNode ) transformData.inheritType = parseInt( modelNode.InheritType.value ); + + if ( 'RotationOrder' in modelNode ) transformData.eulerOrder = getEulerOrder( modelNode.RotationOrder.value ); + else transformData.eulerOrder = 'ZYX'; + + if ( 'Lcl_Translation' in modelNode ) transformData.translation = modelNode.Lcl_Translation.value; + + if ( 'PreRotation' in modelNode ) transformData.preRotation = modelNode.PreRotation.value; + if ( 'Lcl_Rotation' in modelNode ) transformData.rotation = modelNode.Lcl_Rotation.value; + if ( 'PostRotation' in modelNode ) transformData.postRotation = modelNode.PostRotation.value; + + if ( 'Lcl_Scaling' in modelNode ) transformData.scale = modelNode.Lcl_Scaling.value; + + if ( 'ScalingOffset' in modelNode ) transformData.scalingOffset = modelNode.ScalingOffset.value; + if ( 'ScalingPivot' in modelNode ) transformData.scalingPivot = modelNode.ScalingPivot.value; + + if ( 'RotationOffset' in modelNode ) transformData.rotationOffset = modelNode.RotationOffset.value; + if ( 'RotationPivot' in modelNode ) transformData.rotationPivot = modelNode.RotationPivot.value; + + model.userData.transformData = transformData; + + }, + + setLookAtProperties: function ( model, modelNode ) { + + if ( 'LookAtProperty' in modelNode ) { + + var children = connections.get( model.ID ).children; + + children.forEach( function ( child ) { + + if ( child.relationship === 'LookAtProperty' ) { + + var lookAtTarget = fbxTree.Objects.Model[ child.ID ]; + + if ( 'Lcl_Translation' in lookAtTarget ) { + + var pos = lookAtTarget.Lcl_Translation.value; + + // DirectionalLight, SpotLight + if ( model.target !== undefined ) { + + model.target.position.fromArray( pos ); + sceneGraph.add( model.target ); + + } else { // Cameras and other Object3Ds + + model.lookAt( new Vector3().fromArray( pos ) ); + + } + + } + + } + + } ); + + } + + }, + + bindSkeleton: function ( skeletons, geometryMap, modelMap ) { + + var bindMatrices = this.parsePoseNodes(); + + for ( var ID in skeletons ) { + + var skeleton = skeletons[ ID ]; + + var parents = connections.get( parseInt( skeleton.ID ) ).parents; + + parents.forEach( function ( parent ) { + + if ( geometryMap.has( parent.ID ) ) { + + var geoID = parent.ID; + var geoRelationships = connections.get( geoID ); + + geoRelationships.parents.forEach( function ( geoConnParent ) { + + if ( modelMap.has( geoConnParent.ID ) ) { + + var model = modelMap.get( geoConnParent.ID ); + + model.bind( new Skeleton( skeleton.bones ), bindMatrices[ geoConnParent.ID ] ); + + } + + } ); + + } + + } ); + + } + + }, + + parsePoseNodes: function () { + + var bindMatrices = {}; + + if ( 'Pose' in fbxTree.Objects ) { + + var BindPoseNode = fbxTree.Objects.Pose; + + for ( var nodeID in BindPoseNode ) { + + if ( BindPoseNode[ nodeID ].attrType === 'BindPose' ) { + + var poseNodes = BindPoseNode[ nodeID ].PoseNode; + + if ( Array.isArray( poseNodes ) ) { + + poseNodes.forEach( function ( poseNode ) { + + bindMatrices[ poseNode.Node ] = new Matrix4().fromArray( poseNode.Matrix.a ); + + } ); + + } else { + + bindMatrices[ poseNodes.Node ] = new Matrix4().fromArray( poseNodes.Matrix.a ); + + } + + } + + } + + } + + return bindMatrices; + + }, + + // Parse ambient color in FBXTree.GlobalSettings - if it's not set to black (default), create an ambient light + createAmbientLight: function () { + + if ( 'GlobalSettings' in fbxTree && 'AmbientColor' in fbxTree.GlobalSettings ) { + + var ambientColor = fbxTree.GlobalSettings.AmbientColor.value; + var r = ambientColor[ 0 ]; + var g = ambientColor[ 1 ]; + var b = ambientColor[ 2 ]; + + if ( r !== 0 || g !== 0 || b !== 0 ) { + + var color = new Color( r, g, b ); + sceneGraph.add( new AmbientLight( color, 1 ) ); + + } + + } + + }, + + setupMorphMaterials: function () { + + var self = this; + sceneGraph.traverse( function ( child ) { + + if ( child.isMesh ) { + + if ( child.geometry.morphAttributes.position && child.geometry.morphAttributes.position.length ) { + + if ( Array.isArray( child.material ) ) { + + child.material.forEach( function ( material, i ) { + + self.setupMorphMaterial( child, material, i ); + + } ); + + } else { + + self.setupMorphMaterial( child, child.material ); + + } + + } + + } + + } ); + + }, + + setupMorphMaterial: function ( child, material, index ) { + + var uuid = child.uuid; + var matUuid = material.uuid; + + // if a geometry has morph targets, it cannot share the material with other geometries + var sharedMat = false; + + sceneGraph.traverse( function ( node ) { + + if ( node.isMesh ) { + + if ( Array.isArray( node.material ) ) { + + node.material.forEach( function ( mat ) { + + if ( mat.uuid === matUuid && node.uuid !== uuid ) sharedMat = true; + + } ); + + } else if ( node.material.uuid === matUuid && node.uuid !== uuid ) sharedMat = true; + + } + + } ); + + if ( sharedMat === true ) { + + var clonedMat = material.clone(); + clonedMat.morphTargets = true; + + if ( index === undefined ) child.material = clonedMat; + else child.material[ index ] = clonedMat; + + } else material.morphTargets = true; + + } + +}; + +// parse Geometry data from FBXTree and return map of BufferGeometries +function GeometryParser() {} + +GeometryParser.prototype = { + + constructor: GeometryParser, + + // Parse nodes in FBXTree.Objects.Geometry + parse: function ( deformers ) { + + var geometryMap = new Map(); + + if ( 'Geometry' in fbxTree.Objects ) { + + var geoNodes = fbxTree.Objects.Geometry; + + for ( var nodeID in geoNodes ) { + + var relationships = connections.get( parseInt( nodeID ) ); + var geo = this.parseGeometry( relationships, geoNodes[ nodeID ], deformers ); + + geometryMap.set( parseInt( nodeID ), geo ); + + } + + } + + return geometryMap; + + }, + + // Parse single node in FBXTree.Objects.Geometry + parseGeometry: function ( relationships, geoNode, deformers ) { + + switch ( geoNode.attrType ) { + + case 'Mesh': + return this.parseMeshGeometry( relationships, geoNode, deformers ); + break; + + case 'NurbsCurve': + return this.parseNurbsGeometry( geoNode ); + break; + + } + + }, + + // Parse single node mesh geometry in FBXTree.Objects.Geometry + parseMeshGeometry: function ( relationships, geoNode, deformers ) { + + var skeletons = deformers.skeletons; + var morphTargets = deformers.morphTargets; + + var modelNodes = relationships.parents.map( function ( parent ) { + + return fbxTree.Objects.Model[ parent.ID ]; + + } ); + + // don't create geometry if it is not associated with any models + if ( modelNodes.length === 0 ) return; + + var skeleton = relationships.children.reduce( function ( skeleton, child ) { + + if ( skeletons[ child.ID ] !== undefined ) skeleton = skeletons[ child.ID ]; + + return skeleton; + + }, null ); + + var morphTarget = relationships.children.reduce( function ( morphTarget, child ) { + + if ( morphTargets[ child.ID ] !== undefined ) morphTarget = morphTargets[ child.ID ]; + + return morphTarget; + + }, null ); + + // Assume one model and get the preRotation from that + // if there is more than one model associated with the geometry this may cause problems + var modelNode = modelNodes[ 0 ]; + + var transformData = {}; + + if ( 'RotationOrder' in modelNode ) transformData.eulerOrder = getEulerOrder( modelNode.RotationOrder.value ); + if ( 'InheritType' in modelNode ) transformData.inheritType = parseInt( modelNode.InheritType.value ); + + if ( 'GeometricTranslation' in modelNode ) transformData.translation = modelNode.GeometricTranslation.value; + if ( 'GeometricRotation' in modelNode ) transformData.rotation = modelNode.GeometricRotation.value; + if ( 'GeometricScaling' in modelNode ) transformData.scale = modelNode.GeometricScaling.value; + + var transform = generateTransform( transformData ); + + return this.genGeometry( geoNode, skeleton, morphTarget, transform ); + + }, + + // Generate a BufferGeometry from a node in FBXTree.Objects.Geometry + genGeometry: function ( geoNode, skeleton, morphTarget, preTransform ) { + + var geo = new BufferGeometry(); + if ( geoNode.attrName ) geo.name = geoNode.attrName; + + var geoInfo = this.parseGeoNode( geoNode, skeleton ); + var buffers = this.genBuffers( geoInfo ); + + var positionAttribute = new Float32BufferAttribute( buffers.vertex, 3 ); + + preTransform.applyToBufferAttribute( positionAttribute ); + + geo.addAttribute( 'position', positionAttribute ); + + if ( buffers.colors.length > 0 ) { + + geo.addAttribute( 'color', new Float32BufferAttribute( buffers.colors, 3 ) ); + + } + + if ( skeleton ) { + + geo.addAttribute( 'skinIndex', new Uint16BufferAttribute( buffers.weightsIndices, 4 ) ); + + geo.addAttribute( 'skinWeight', new Float32BufferAttribute( buffers.vertexWeights, 4 ) ); + + // used later to bind the skeleton to the model + geo.FBX_Deformer = skeleton; + + } + + if ( buffers.normal.length > 0 ) { + + var normalAttribute = new Float32BufferAttribute( buffers.normal, 3 ); + + var normalMatrix = new Matrix3().getNormalMatrix( preTransform ); + normalMatrix.applyToBufferAttribute( normalAttribute ); + + geo.addAttribute( 'normal', normalAttribute ); + + } + + buffers.uvs.forEach( function ( uvBuffer, i ) { + + // subsequent uv buffers are called 'uv1', 'uv2', ... + var name = 'uv' + ( i + 1 ).toString(); + + // the first uv buffer is just called 'uv' + if ( i === 0 ) { + + name = 'uv'; + + } + + geo.addAttribute( name, new Float32BufferAttribute( buffers.uvs[ i ], 2 ) ); + + } ); + + if ( geoInfo.material && geoInfo.material.mappingType !== 'AllSame' ) { + + // Convert the material indices of each vertex into rendering groups on the geometry. + var prevMaterialIndex = buffers.materialIndex[ 0 ]; + var startIndex = 0; + + buffers.materialIndex.forEach( function ( currentIndex, i ) { + + if ( currentIndex !== prevMaterialIndex ) { + + geo.addGroup( startIndex, i - startIndex, prevMaterialIndex ); + + prevMaterialIndex = currentIndex; + startIndex = i; + + } + + } ); + + // the loop above doesn't add the last group, do that here. + if ( geo.groups.length > 0 ) { + + var lastGroup = geo.groups[ geo.groups.length - 1 ]; + var lastIndex = lastGroup.start + lastGroup.count; + + if ( lastIndex !== buffers.materialIndex.length ) { + + geo.addGroup( lastIndex, buffers.materialIndex.length - lastIndex, prevMaterialIndex ); + + } + + } + + // case where there are multiple materials but the whole geometry is only + // using one of them + if ( geo.groups.length === 0 ) { + + geo.addGroup( 0, buffers.materialIndex.length, buffers.materialIndex[ 0 ] ); + + } + + } + + this.addMorphTargets( geo, geoNode, morphTarget, preTransform ); + + return geo; + + }, + + parseGeoNode: function ( geoNode, skeleton ) { + + var geoInfo = {}; + + geoInfo.vertexPositions = ( geoNode.Vertices !== undefined ) ? geoNode.Vertices.a : []; + geoInfo.vertexIndices = ( geoNode.PolygonVertexIndex !== undefined ) ? geoNode.PolygonVertexIndex.a : []; + + if ( geoNode.LayerElementColor ) { + + geoInfo.color = this.parseVertexColors( geoNode.LayerElementColor[ 0 ] ); + + } + + if ( geoNode.LayerElementMaterial ) { + + geoInfo.material = this.parseMaterialIndices( geoNode.LayerElementMaterial[ 0 ] ); + + } + + if ( geoNode.LayerElementNormal ) { + + geoInfo.normal = this.parseNormals( geoNode.LayerElementNormal[ 0 ] ); + + } + + if ( geoNode.LayerElementUV ) { + + geoInfo.uv = []; + + var i = 0; + while ( geoNode.LayerElementUV[ i ] ) { + + geoInfo.uv.push( this.parseUVs( geoNode.LayerElementUV[ i ] ) ); + i ++; + + } + + } + + geoInfo.weightTable = {}; + + if ( skeleton !== null ) { + + geoInfo.skeleton = skeleton; + + skeleton.rawBones.forEach( function ( rawBone, i ) { + + // loop over the bone's vertex indices and weights + rawBone.indices.forEach( function ( index, j ) { + + if ( geoInfo.weightTable[ index ] === undefined ) geoInfo.weightTable[ index ] = []; + + geoInfo.weightTable[ index ].push( { + + id: i, + weight: rawBone.weights[ j ], + + } ); + + } ); + + } ); + + } + + return geoInfo; + + }, + + genBuffers: function ( geoInfo ) { + + var buffers = { + vertex: [], + normal: [], + colors: [], + uvs: [], + materialIndex: [], + vertexWeights: [], + weightsIndices: [], + }; + + var polygonIndex = 0; + var faceLength = 0; + var displayedWeightsWarning = false; + + // these will hold data for a single face + var facePositionIndexes = []; + var faceNormals = []; + var faceColors = []; + var faceUVs = []; + var faceWeights = []; + var faceWeightIndices = []; + + var self = this; + geoInfo.vertexIndices.forEach( function ( vertexIndex, polygonVertexIndex ) { + + var endOfFace = false; + + // Face index and vertex index arrays are combined in a single array + // A cube with quad faces looks like this: + // PolygonVertexIndex: *24 { + // a: 0, 1, 3, -3, 2, 3, 5, -5, 4, 5, 7, -7, 6, 7, 1, -1, 1, 7, 5, -4, 6, 0, 2, -5 + // } + // Negative numbers mark the end of a face - first face here is 0, 1, 3, -3 + // to find index of last vertex bit shift the index: ^ - 1 + if ( vertexIndex < 0 ) { + + vertexIndex = vertexIndex ^ - 1; // equivalent to ( x * -1 ) - 1 + endOfFace = true; + + } + + var weightIndices = []; + var weights = []; + + facePositionIndexes.push( vertexIndex * 3, vertexIndex * 3 + 1, vertexIndex * 3 + 2 ); + + if ( geoInfo.color ) { + + var data = getData( polygonVertexIndex, polygonIndex, vertexIndex, geoInfo.color ); + + faceColors.push( data[ 0 ], data[ 1 ], data[ 2 ] ); + + } + + if ( geoInfo.skeleton ) { + + if ( geoInfo.weightTable[ vertexIndex ] !== undefined ) { + + geoInfo.weightTable[ vertexIndex ].forEach( function ( wt ) { + + weights.push( wt.weight ); + weightIndices.push( wt.id ); + + } ); + + + } + + if ( weights.length > 4 ) { + + if ( ! displayedWeightsWarning ) { + + console.warn( 'THREE.FBXLoader: Vertex has more than 4 skinning weights assigned to vertex. Deleting additional weights.' ); + displayedWeightsWarning = true; + + } + + var wIndex = [ 0, 0, 0, 0 ]; + var Weight = [ 0, 0, 0, 0 ]; + + weights.forEach( function ( weight, weightIndex ) { + + var currentWeight = weight; + var currentIndex = weightIndices[ weightIndex ]; + + Weight.forEach( function ( comparedWeight, comparedWeightIndex, comparedWeightArray ) { + + if ( currentWeight > comparedWeight ) { + + comparedWeightArray[ comparedWeightIndex ] = currentWeight; + currentWeight = comparedWeight; + + var tmp = wIndex[ comparedWeightIndex ]; + wIndex[ comparedWeightIndex ] = currentIndex; + currentIndex = tmp; + + } + + } ); + + } ); + + weightIndices = wIndex; + weights = Weight; + + } + + // if the weight array is shorter than 4 pad with 0s + while ( weights.length < 4 ) { + + weights.push( 0 ); + weightIndices.push( 0 ); + + } + + for ( var i = 0; i < 4; ++ i ) { + + faceWeights.push( weights[ i ] ); + faceWeightIndices.push( weightIndices[ i ] ); + + } + + } + + if ( geoInfo.normal ) { + + var data = getData( polygonVertexIndex, polygonIndex, vertexIndex, geoInfo.normal ); + + faceNormals.push( data[ 0 ], data[ 1 ], data[ 2 ] ); + + } + + if ( geoInfo.material && geoInfo.material.mappingType !== 'AllSame' ) { + + var materialIndex = getData( polygonVertexIndex, polygonIndex, vertexIndex, geoInfo.material )[ 0 ]; + + } + + if ( geoInfo.uv ) { + + geoInfo.uv.forEach( function ( uv, i ) { + + var data = getData( polygonVertexIndex, polygonIndex, vertexIndex, uv ); + + if ( faceUVs[ i ] === undefined ) { + + faceUVs[ i ] = []; + + } + + faceUVs[ i ].push( data[ 0 ] ); + faceUVs[ i ].push( data[ 1 ] ); + + } ); + + } + + faceLength ++; + + if ( endOfFace ) { + + self.genFace( buffers, geoInfo, facePositionIndexes, materialIndex, faceNormals, faceColors, faceUVs, faceWeights, faceWeightIndices, faceLength ); + + polygonIndex ++; + faceLength = 0; + + // reset arrays for the next face + facePositionIndexes = []; + faceNormals = []; + faceColors = []; + faceUVs = []; + faceWeights = []; + faceWeightIndices = []; + + } + + } ); + + return buffers; + + }, + + // Generate data for a single face in a geometry. If the face is a quad then split it into 2 tris + genFace: function ( buffers, geoInfo, facePositionIndexes, materialIndex, faceNormals, faceColors, faceUVs, faceWeights, faceWeightIndices, faceLength ) { + + for ( var i = 2; i < faceLength; i ++ ) { + + buffers.vertex.push( geoInfo.vertexPositions[ facePositionIndexes[ 0 ] ] ); + buffers.vertex.push( geoInfo.vertexPositions[ facePositionIndexes[ 1 ] ] ); + buffers.vertex.push( geoInfo.vertexPositions[ facePositionIndexes[ 2 ] ] ); + + buffers.vertex.push( geoInfo.vertexPositions[ facePositionIndexes[ ( i - 1 ) * 3 ] ] ); + buffers.vertex.push( geoInfo.vertexPositions[ facePositionIndexes[ ( i - 1 ) * 3 + 1 ] ] ); + buffers.vertex.push( geoInfo.vertexPositions[ facePositionIndexes[ ( i - 1 ) * 3 + 2 ] ] ); + + buffers.vertex.push( geoInfo.vertexPositions[ facePositionIndexes[ i * 3 ] ] ); + buffers.vertex.push( geoInfo.vertexPositions[ facePositionIndexes[ i * 3 + 1 ] ] ); + buffers.vertex.push( geoInfo.vertexPositions[ facePositionIndexes[ i * 3 + 2 ] ] ); + + if ( geoInfo.skeleton ) { + + buffers.vertexWeights.push( faceWeights[ 0 ] ); + buffers.vertexWeights.push( faceWeights[ 1 ] ); + buffers.vertexWeights.push( faceWeights[ 2 ] ); + buffers.vertexWeights.push( faceWeights[ 3 ] ); + + buffers.vertexWeights.push( faceWeights[ ( i - 1 ) * 4 ] ); + buffers.vertexWeights.push( faceWeights[ ( i - 1 ) * 4 + 1 ] ); + buffers.vertexWeights.push( faceWeights[ ( i - 1 ) * 4 + 2 ] ); + buffers.vertexWeights.push( faceWeights[ ( i - 1 ) * 4 + 3 ] ); + + buffers.vertexWeights.push( faceWeights[ i * 4 ] ); + buffers.vertexWeights.push( faceWeights[ i * 4 + 1 ] ); + buffers.vertexWeights.push( faceWeights[ i * 4 + 2 ] ); + buffers.vertexWeights.push( faceWeights[ i * 4 + 3 ] ); + + buffers.weightsIndices.push( faceWeightIndices[ 0 ] ); + buffers.weightsIndices.push( faceWeightIndices[ 1 ] ); + buffers.weightsIndices.push( faceWeightIndices[ 2 ] ); + buffers.weightsIndices.push( faceWeightIndices[ 3 ] ); + + buffers.weightsIndices.push( faceWeightIndices[ ( i - 1 ) * 4 ] ); + buffers.weightsIndices.push( faceWeightIndices[ ( i - 1 ) * 4 + 1 ] ); + buffers.weightsIndices.push( faceWeightIndices[ ( i - 1 ) * 4 + 2 ] ); + buffers.weightsIndices.push( faceWeightIndices[ ( i - 1 ) * 4 + 3 ] ); + + buffers.weightsIndices.push( faceWeightIndices[ i * 4 ] ); + buffers.weightsIndices.push( faceWeightIndices[ i * 4 + 1 ] ); + buffers.weightsIndices.push( faceWeightIndices[ i * 4 + 2 ] ); + buffers.weightsIndices.push( faceWeightIndices[ i * 4 + 3 ] ); + + } + + if ( geoInfo.color ) { + + buffers.colors.push( faceColors[ 0 ] ); + buffers.colors.push( faceColors[ 1 ] ); + buffers.colors.push( faceColors[ 2 ] ); + + buffers.colors.push( faceColors[ ( i - 1 ) * 3 ] ); + buffers.colors.push( faceColors[ ( i - 1 ) * 3 + 1 ] ); + buffers.colors.push( faceColors[ ( i - 1 ) * 3 + 2 ] ); + + buffers.colors.push( faceColors[ i * 3 ] ); + buffers.colors.push( faceColors[ i * 3 + 1 ] ); + buffers.colors.push( faceColors[ i * 3 + 2 ] ); + + } + + if ( geoInfo.material && geoInfo.material.mappingType !== 'AllSame' ) { + + buffers.materialIndex.push( materialIndex ); + buffers.materialIndex.push( materialIndex ); + buffers.materialIndex.push( materialIndex ); + + } + + if ( geoInfo.normal ) { + + buffers.normal.push( faceNormals[ 0 ] ); + buffers.normal.push( faceNormals[ 1 ] ); + buffers.normal.push( faceNormals[ 2 ] ); + + buffers.normal.push( faceNormals[ ( i - 1 ) * 3 ] ); + buffers.normal.push( faceNormals[ ( i - 1 ) * 3 + 1 ] ); + buffers.normal.push( faceNormals[ ( i - 1 ) * 3 + 2 ] ); + + buffers.normal.push( faceNormals[ i * 3 ] ); + buffers.normal.push( faceNormals[ i * 3 + 1 ] ); + buffers.normal.push( faceNormals[ i * 3 + 2 ] ); + + } + + if ( geoInfo.uv ) { + + geoInfo.uv.forEach( function ( uv, j ) { + + if ( buffers.uvs[ j ] === undefined ) buffers.uvs[ j ] = []; + + buffers.uvs[ j ].push( faceUVs[ j ][ 0 ] ); + buffers.uvs[ j ].push( faceUVs[ j ][ 1 ] ); + + buffers.uvs[ j ].push( faceUVs[ j ][ ( i - 1 ) * 2 ] ); + buffers.uvs[ j ].push( faceUVs[ j ][ ( i - 1 ) * 2 + 1 ] ); + + buffers.uvs[ j ].push( faceUVs[ j ][ i * 2 ] ); + buffers.uvs[ j ].push( faceUVs[ j ][ i * 2 + 1 ] ); + + } ); + + } + + } + + }, + + addMorphTargets: function ( parentGeo, parentGeoNode, morphTarget, preTransform ) { + + if ( morphTarget === null ) return; + + parentGeo.morphAttributes.position = []; + // parentGeo.morphAttributes.normal = []; // not implemented + + var self = this; + morphTarget.rawTargets.forEach( function ( rawTarget ) { + + var morphGeoNode = fbxTree.Objects.Geometry[ rawTarget.geoID ]; + + if ( morphGeoNode !== undefined ) { + + self.genMorphGeometry( parentGeo, parentGeoNode, morphGeoNode, preTransform, rawTarget.name ); + + } + + } ); + + }, + + // a morph geometry node is similar to a standard node, and the node is also contained + // in FBXTree.Objects.Geometry, however it can only have attributes for position, normal + // and a special attribute Index defining which vertices of the original geometry are affected + // Normal and position attributes only have data for the vertices that are affected by the morph + genMorphGeometry: function ( parentGeo, parentGeoNode, morphGeoNode, preTransform, name ) { + + var morphGeo = new BufferGeometry(); + if ( morphGeoNode.attrName ) morphGeo.name = morphGeoNode.attrName; + + var vertexIndices = ( parentGeoNode.PolygonVertexIndex !== undefined ) ? parentGeoNode.PolygonVertexIndex.a : []; + + // make a copy of the parent's vertex positions + var vertexPositions = ( parentGeoNode.Vertices !== undefined ) ? parentGeoNode.Vertices.a.slice() : []; + + var morphPositions = ( morphGeoNode.Vertices !== undefined ) ? morphGeoNode.Vertices.a : []; + var indices = ( morphGeoNode.Indexes !== undefined ) ? morphGeoNode.Indexes.a : []; + + for ( var i = 0; i < indices.length; i ++ ) { + + var morphIndex = indices[ i ] * 3; + + // FBX format uses blend shapes rather than morph targets. This can be converted + // by additively combining the blend shape positions with the original geometry's positions + vertexPositions[ morphIndex ] += morphPositions[ i * 3 ]; + vertexPositions[ morphIndex + 1 ] += morphPositions[ i * 3 + 1 ]; + vertexPositions[ morphIndex + 2 ] += morphPositions[ i * 3 + 2 ]; + + } + + // TODO: add morph normal support + var morphGeoInfo = { + vertexIndices: vertexIndices, + vertexPositions: vertexPositions, + }; + + var morphBuffers = this.genBuffers( morphGeoInfo ); + + var positionAttribute = new Float32BufferAttribute( morphBuffers.vertex, 3 ); + positionAttribute.name = name || morphGeoNode.attrName; + + preTransform.applyToBufferAttribute( positionAttribute ); + + parentGeo.morphAttributes.position.push( positionAttribute ); + + }, + + // Parse normal from FBXTree.Objects.Geometry.LayerElementNormal if it exists + parseNormals: function ( NormalNode ) { + + var mappingType = NormalNode.MappingInformationType; + var referenceType = NormalNode.ReferenceInformationType; + var buffer = NormalNode.Normals.a; + var indexBuffer = []; + if ( referenceType === 'IndexToDirect' ) { + + if ( 'NormalIndex' in NormalNode ) { + + indexBuffer = NormalNode.NormalIndex.a; + + } else if ( 'NormalsIndex' in NormalNode ) { + + indexBuffer = NormalNode.NormalsIndex.a; + + } + + } + + return { + dataSize: 3, + buffer: buffer, + indices: indexBuffer, + mappingType: mappingType, + referenceType: referenceType + }; + + }, + + // Parse UVs from FBXTree.Objects.Geometry.LayerElementUV if it exists + parseUVs: function ( UVNode ) { + + var mappingType = UVNode.MappingInformationType; + var referenceType = UVNode.ReferenceInformationType; + var buffer = UVNode.UV.a; + var indexBuffer = []; + if ( referenceType === 'IndexToDirect' ) { + + indexBuffer = UVNode.UVIndex.a; + + } + + return { + dataSize: 2, + buffer: buffer, + indices: indexBuffer, + mappingType: mappingType, + referenceType: referenceType + }; + + }, + + // Parse Vertex Colors from FBXTree.Objects.Geometry.LayerElementColor if it exists + parseVertexColors: function ( ColorNode ) { + + var mappingType = ColorNode.MappingInformationType; + var referenceType = ColorNode.ReferenceInformationType; + var buffer = ColorNode.Colors.a; + var indexBuffer = []; + if ( referenceType === 'IndexToDirect' ) { + + indexBuffer = ColorNode.ColorIndex.a; + + } + + return { + dataSize: 4, + buffer: buffer, + indices: indexBuffer, + mappingType: mappingType, + referenceType: referenceType + }; + + }, + + // Parse mapping and material data in FBXTree.Objects.Geometry.LayerElementMaterial if it exists + parseMaterialIndices: function ( MaterialNode ) { + + var mappingType = MaterialNode.MappingInformationType; + var referenceType = MaterialNode.ReferenceInformationType; + + if ( mappingType === 'NoMappingInformation' ) { + + return { + dataSize: 1, + buffer: [ 0 ], + indices: [ 0 ], + mappingType: 'AllSame', + referenceType: referenceType + }; + + } + + var materialIndexBuffer = MaterialNode.Materials.a; + + // Since materials are stored as indices, there's a bit of a mismatch between FBX and what + // we expect.So we create an intermediate buffer that points to the index in the buffer, + // for conforming with the other functions we've written for other data. + var materialIndices = []; + + for ( var i = 0; i < materialIndexBuffer.length; ++ i ) { + + materialIndices.push( i ); + + } + + return { + dataSize: 1, + buffer: materialIndexBuffer, + indices: materialIndices, + mappingType: mappingType, + referenceType: referenceType + }; + + }, + + // Generate a NurbGeometry from a node in FBXTree.Objects.Geometry + parseNurbsGeometry: function ( geoNode ) { + + if ( NURBSCurve === undefined ) { + + console.error( 'THREE.FBXLoader: The loader relies on NURBSCurve for any nurbs present in the model. Nurbs will show up as empty geometry.' ); + return new BufferGeometry(); + + } + + var order = parseInt( geoNode.Order ); + + if ( isNaN( order ) ) { + + console.error( 'THREE.FBXLoader: Invalid Order %s given for geometry ID: %s', geoNode.Order, geoNode.id ); + return new BufferGeometry(); + + } + + var degree = order - 1; + + var knots = geoNode.KnotVector.a; + var controlPoints = []; + var pointsValues = geoNode.Points.a; + + for ( var i = 0, l = pointsValues.length; i < l; i += 4 ) { + + controlPoints.push( new Vector4().fromArray( pointsValues, i ) ); + + } + + var startKnot, endKnot; + + if ( geoNode.Form === 'Closed' ) { + + controlPoints.push( controlPoints[ 0 ] ); + + } else if ( geoNode.Form === 'Periodic' ) { + + startKnot = degree; + endKnot = knots.length - 1 - startKnot; + + for ( var i = 0; i < degree; ++ i ) { + + controlPoints.push( controlPoints[ i ] ); + + } + + } + + var curve = new NURBSCurve( degree, knots, controlPoints, startKnot, endKnot ); + var vertices = curve.getPoints( controlPoints.length * 7 ); + + var positions = new Float32Array( vertices.length * 3 ); + + vertices.forEach( function ( vertex, i ) { + + vertex.toArray( positions, i * 3 ); + + } ); + + var geometry = new BufferGeometry(); + geometry.addAttribute( 'position', new BufferAttribute( positions, 3 ) ); + + return geometry; + + }, + +}; + +// parse animation data from FBXTree +function AnimationParser() {} + +AnimationParser.prototype = { + + constructor: AnimationParser, + + // take raw animation clips and turn them into three.js animation clips + parse: function () { + + var animationClips = []; + + var rawClips = this.parseClips(); + + if ( rawClips === undefined ) return; + + for ( var key in rawClips ) { + + var rawClip = rawClips[ key ]; + + var clip = this.addClip( rawClip ); + + animationClips.push( clip ); + + } + + return animationClips; + + }, + + parseClips: function () { + + // since the actual transformation data is stored in FBXTree.Objects.AnimationCurve, + // if this is undefined we can safely assume there are no animations + if ( fbxTree.Objects.AnimationCurve === undefined ) return undefined; + + var curveNodesMap = this.parseAnimationCurveNodes(); + + this.parseAnimationCurves( curveNodesMap ); + + var layersMap = this.parseAnimationLayers( curveNodesMap ); + var rawClips = this.parseAnimStacks( layersMap ); + + return rawClips; + + }, + + // parse nodes in FBXTree.Objects.AnimationCurveNode + // each AnimationCurveNode holds data for an animation transform for a model (e.g. left arm rotation ) + // and is referenced by an AnimationLayer + parseAnimationCurveNodes: function () { + + var rawCurveNodes = fbxTree.Objects.AnimationCurveNode; + + var curveNodesMap = new Map(); + + for ( var nodeID in rawCurveNodes ) { + + var rawCurveNode = rawCurveNodes[ nodeID ]; + + if ( rawCurveNode.attrName.match( /S|R|T|DeformPercent/ ) !== null ) { + + var curveNode = { + + id: rawCurveNode.id, + attr: rawCurveNode.attrName, + curves: {}, + + }; + + curveNodesMap.set( curveNode.id, curveNode ); + + } + + } + + return curveNodesMap; + + }, + + // parse nodes in FBXTree.Objects.AnimationCurve and connect them up to + // previously parsed AnimationCurveNodes. Each AnimationCurve holds data for a single animated + // axis ( e.g. times and values of x rotation) + parseAnimationCurves: function ( curveNodesMap ) { + + var rawCurves = fbxTree.Objects.AnimationCurve; + + // TODO: Many values are identical up to roundoff error, but won't be optimised + // e.g. position times: [0, 0.4, 0. 8] + // position values: [7.23538335023477e-7, 93.67518615722656, -0.9982695579528809, 7.23538335023477e-7, 93.67518615722656, -0.9982695579528809, 7.235384487103147e-7, 93.67520904541016, -0.9982695579528809] + // clearly, this should be optimised to + // times: [0], positions [7.23538335023477e-7, 93.67518615722656, -0.9982695579528809] + // this shows up in nearly every FBX file, and generally time array is length > 100 + + for ( var nodeID in rawCurves ) { + + var animationCurve = { + + id: rawCurves[ nodeID ].id, + times: rawCurves[ nodeID ].KeyTime.a.map( convertFBXTimeToSeconds ), + values: rawCurves[ nodeID ].KeyValueFloat.a, + + }; + + var relationships = connections.get( animationCurve.id ); + + if ( relationships !== undefined ) { + + var animationCurveID = relationships.parents[ 0 ].ID; + var animationCurveRelationship = relationships.parents[ 0 ].relationship; + + if ( animationCurveRelationship.match( /X/ ) ) { + + curveNodesMap.get( animationCurveID ).curves[ 'x' ] = animationCurve; + + } else if ( animationCurveRelationship.match( /Y/ ) ) { + + curveNodesMap.get( animationCurveID ).curves[ 'y' ] = animationCurve; + + } else if ( animationCurveRelationship.match( /Z/ ) ) { + + curveNodesMap.get( animationCurveID ).curves[ 'z' ] = animationCurve; + + } else if ( animationCurveRelationship.match( /d|DeformPercent/ ) && curveNodesMap.has( animationCurveID ) ) { + + curveNodesMap.get( animationCurveID ).curves[ 'morph' ] = animationCurve; + + } + + } + + } + + }, + + // parse nodes in FBXTree.Objects.AnimationLayer. Each layers holds references + // to various AnimationCurveNodes and is referenced by an AnimationStack node + // note: theoretically a stack can have multiple layers, however in practice there always seems to be one per stack + parseAnimationLayers: function ( curveNodesMap ) { + + var rawLayers = fbxTree.Objects.AnimationLayer; + + var layersMap = new Map(); + + for ( var nodeID in rawLayers ) { + + var layerCurveNodes = []; + + var connection = connections.get( parseInt( nodeID ) ); + + if ( connection !== undefined ) { + + // all the animationCurveNodes used in the layer + var children = connection.children; + + children.forEach( function ( child, i ) { + + if ( curveNodesMap.has( child.ID ) ) { + + var curveNode = curveNodesMap.get( child.ID ); + + // check that the curves are defined for at least one axis, otherwise ignore the curveNode + if ( curveNode.curves.x !== undefined || curveNode.curves.y !== undefined || curveNode.curves.z !== undefined ) { + + if ( layerCurveNodes[ i ] === undefined ) { + + var modelID = connections.get( child.ID ).parents.filter( function ( parent ) { + + return parent.relationship !== undefined; + + } )[ 0 ].ID; + + if ( modelID !== undefined ) { + + var rawModel = fbxTree.Objects.Model[ modelID.toString() ]; + + var node = { + + modelName: PropertyBinding.sanitizeNodeName( rawModel.attrName ), + ID: rawModel.id, + initialPosition: [ 0, 0, 0 ], + initialRotation: [ 0, 0, 0 ], + initialScale: [ 1, 1, 1 ], + + }; + + sceneGraph.traverse( function ( child ) { + + if ( child.ID = rawModel.id ) { + + node.transform = child.matrix; + + if ( child.userData.transformData ) node.eulerOrder = child.userData.transformData.eulerOrder; + + } + + } ); + + if ( ! node.transform ) node.transform = new Matrix4(); + + // if the animated model is pre rotated, we'll have to apply the pre rotations to every + // animation value as well + if ( 'PreRotation' in rawModel ) node.preRotation = rawModel.PreRotation.value; + if ( 'PostRotation' in rawModel ) node.postRotation = rawModel.PostRotation.value; + + layerCurveNodes[ i ] = node; + + } + + } + + if ( layerCurveNodes[ i ] ) layerCurveNodes[ i ][ curveNode.attr ] = curveNode; + + } else if ( curveNode.curves.morph !== undefined ) { + + if ( layerCurveNodes[ i ] === undefined ) { + + var deformerID = connections.get( child.ID ).parents.filter( function ( parent ) { + + return parent.relationship !== undefined; + + } )[ 0 ].ID; + + var morpherID = connections.get( deformerID ).parents[ 0 ].ID; + var geoID = connections.get( morpherID ).parents[ 0 ].ID; + + // assuming geometry is not used in more than one model + var modelID = connections.get( geoID ).parents[ 0 ].ID; + + var rawModel = fbxTree.Objects.Model[ modelID ]; + + var node = { + + modelName: PropertyBinding.sanitizeNodeName( rawModel.attrName ), + morphName: fbxTree.Objects.Deformer[ deformerID ].attrName, + + }; + + layerCurveNodes[ i ] = node; + + } + + layerCurveNodes[ i ][ curveNode.attr ] = curveNode; + + } + + } + + } ); + + layersMap.set( parseInt( nodeID ), layerCurveNodes ); + + } + + } + + return layersMap; + + }, + + // parse nodes in FBXTree.Objects.AnimationStack. These are the top level node in the animation + // hierarchy. Each Stack node will be used to create a THREE.AnimationClip + parseAnimStacks: function ( layersMap ) { + + var rawStacks = fbxTree.Objects.AnimationStack; + + // connect the stacks (clips) up to the layers + var rawClips = {}; + + for ( var nodeID in rawStacks ) { + + var children = connections.get( parseInt( nodeID ) ).children; + + if ( children.length > 1 ) { + + // it seems like stacks will always be associated with a single layer. But just in case there are files + // where there are multiple layers per stack, we'll display a warning + console.warn( 'THREE.FBXLoader: Encountered an animation stack with multiple layers, this is currently not supported. Ignoring subsequent layers.' ); + + } + + var layer = layersMap.get( children[ 0 ].ID ); + + rawClips[ nodeID ] = { + + name: rawStacks[ nodeID ].attrName, + layer: layer, + + }; + + } + + return rawClips; + + }, + + addClip: function ( rawClip ) { + + var tracks = []; + + var self = this; + rawClip.layer.forEach( function ( rawTracks ) { + + tracks = tracks.concat( self.generateTracks( rawTracks ) ); + + } ); + + return new AnimationClip( rawClip.name, - 1, tracks ); + + }, + + generateTracks: function ( rawTracks ) { + + var tracks = []; + + var initialPosition = new Vector3(); + var initialRotation = new Quaternion(); + var initialScale = new Vector3(); + + if ( rawTracks.transform ) rawTracks.transform.decompose( initialPosition, initialRotation, initialScale ); + + initialPosition = initialPosition.toArray(); + initialRotation = new Euler().setFromQuaternion( initialRotation, rawTracks.eulerOrder ).toArray(); + initialScale = initialScale.toArray(); + + if ( rawTracks.T !== undefined && Object.keys( rawTracks.T.curves ).length > 0 ) { + + var positionTrack = this.generateVectorTrack( rawTracks.modelName, rawTracks.T.curves, initialPosition, 'position' ); + if ( positionTrack !== undefined ) tracks.push( positionTrack ); + + } + + if ( rawTracks.R !== undefined && Object.keys( rawTracks.R.curves ).length > 0 ) { + + var rotationTrack = this.generateRotationTrack( rawTracks.modelName, rawTracks.R.curves, initialRotation, rawTracks.preRotation, rawTracks.postRotation, rawTracks.eulerOrder ); + if ( rotationTrack !== undefined ) tracks.push( rotationTrack ); + + } + + if ( rawTracks.S !== undefined && Object.keys( rawTracks.S.curves ).length > 0 ) { + + var scaleTrack = this.generateVectorTrack( rawTracks.modelName, rawTracks.S.curves, initialScale, 'scale' ); + if ( scaleTrack !== undefined ) tracks.push( scaleTrack ); + + } + + if ( rawTracks.DeformPercent !== undefined ) { + + var morphTrack = this.generateMorphTrack( rawTracks ); + if ( morphTrack !== undefined ) tracks.push( morphTrack ); + + } + + return tracks; + + }, + + generateVectorTrack: function ( modelName, curves, initialValue, type ) { + + var times = this.getTimesForAllAxes( curves ); + var values = this.getKeyframeTrackValues( times, curves, initialValue ); + + return new VectorKeyframeTrack( modelName + '.' + type, times, values ); + + }, + + generateRotationTrack: function ( modelName, curves, initialValue, preRotation, postRotation, eulerOrder ) { + + if ( curves.x !== undefined ) { + + this.interpolateRotations( curves.x ); + curves.x.values = curves.x.values.map( Math.degToRad ); + + } + if ( curves.y !== undefined ) { + + this.interpolateRotations( curves.y ); + curves.y.values = curves.y.values.map( Math.degToRad ); + + } + if ( curves.z !== undefined ) { + + this.interpolateRotations( curves.z ); + curves.z.values = curves.z.values.map( Math.degToRad ); + + } + + var times = this.getTimesForAllAxes( curves ); + var values = this.getKeyframeTrackValues( times, curves, initialValue ); + + if ( preRotation !== undefined ) { + + preRotation = preRotation.map( Math.degToRad ); + preRotation.push( eulerOrder ); + + preRotation = new Euler().fromArray( preRotation ); + preRotation = new Quaternion().setFromEuler( preRotation ); + + } + + if ( postRotation !== undefined ) { + + postRotation = postRotation.map( Math.degToRad ); + postRotation.push( eulerOrder ); + + postRotation = new Euler().fromArray( postRotation ); + postRotation = new Quaternion().setFromEuler( postRotation ).inverse(); + + } + + var quaternion = new Quaternion(); + var euler = new Euler(); + + var quaternionValues = []; + + for ( var i = 0; i < values.length; i += 3 ) { + + euler.set( values[ i ], values[ i + 1 ], values[ i + 2 ], eulerOrder ); + + quaternion.setFromEuler( euler ); + + if ( preRotation !== undefined ) quaternion.premultiply( preRotation ); + if ( postRotation !== undefined ) quaternion.multiply( postRotation ); + + quaternion.toArray( quaternionValues, ( i / 3 ) * 4 ); + + } + + return new QuaternionKeyframeTrack( modelName + '.quaternion', times, quaternionValues ); + + }, + + generateMorphTrack: function ( rawTracks ) { + + var curves = rawTracks.DeformPercent.curves.morph; + var values = curves.values.map( function ( val ) { + + return val / 100; + + } ); + + var morphNum = sceneGraph.getObjectByName( rawTracks.modelName ).morphTargetDictionary[ rawTracks.morphName ]; + + return new NumberKeyframeTrack( rawTracks.modelName + '.morphTargetInfluences[' + morphNum + ']', curves.times, values ); + + }, + + // For all animated objects, times are defined separately for each axis + // Here we'll combine the times into one sorted array without duplicates + getTimesForAllAxes: function ( curves ) { + + var times = []; + + // first join together the times for each axis, if defined + if ( curves.x !== undefined ) times = times.concat( curves.x.times ); + if ( curves.y !== undefined ) times = times.concat( curves.y.times ); + if ( curves.z !== undefined ) times = times.concat( curves.z.times ); + + // then sort them and remove duplicates + times = times.sort( function ( a, b ) { + + return a - b; + + } ).filter( function ( elem, index, array ) { + + return array.indexOf( elem ) == index; + + } ); + + return times; + + }, + + getKeyframeTrackValues: function ( times, curves, initialValue ) { + + var prevValue = initialValue; + + var values = []; + + var xIndex = - 1; + var yIndex = - 1; + var zIndex = - 1; + + times.forEach( function ( time ) { + + if ( curves.x ) xIndex = curves.x.times.indexOf( time ); + if ( curves.y ) yIndex = curves.y.times.indexOf( time ); + if ( curves.z ) zIndex = curves.z.times.indexOf( time ); + + // if there is an x value defined for this frame, use that + if ( xIndex !== - 1 ) { + + var xValue = curves.x.values[ xIndex ]; + values.push( xValue ); + prevValue[ 0 ] = xValue; + + } else { + + // otherwise use the x value from the previous frame + values.push( prevValue[ 0 ] ); + + } + + if ( yIndex !== - 1 ) { + + var yValue = curves.y.values[ yIndex ]; + values.push( yValue ); + prevValue[ 1 ] = yValue; + + } else { + + values.push( prevValue[ 1 ] ); + + } + + if ( zIndex !== - 1 ) { + + var zValue = curves.z.values[ zIndex ]; + values.push( zValue ); + prevValue[ 2 ] = zValue; + + } else { + + values.push( prevValue[ 2 ] ); + + } + + } ); + + return values; + + }, + + // Rotations are defined as Euler angles which can have values of any size + // These will be converted to quaternions which don't support values greater than + // PI, so we'll interpolate large rotations + interpolateRotations: function ( curve ) { + + for ( var i = 1; i < curve.values.length; i ++ ) { + + var initialValue = curve.values[ i - 1 ]; + var valuesSpan = curve.values[ i ] - initialValue; + + var absoluteSpan = Math.abs( valuesSpan ); + + if ( absoluteSpan >= 180 ) { + + var numSubIntervals = absoluteSpan / 180; + + var step = valuesSpan / numSubIntervals; + var nextValue = initialValue + step; + + var initialTime = curve.times[ i - 1 ]; + var timeSpan = curve.times[ i ] - initialTime; + var interval = timeSpan / numSubIntervals; + var nextTime = initialTime + interval; + + var interpolatedTimes = []; + var interpolatedValues = []; + + while ( nextTime < curve.times[ i ] ) { + + interpolatedTimes.push( nextTime ); + nextTime += interval; + + interpolatedValues.push( nextValue ); + nextValue += step; + + } + + curve.times = inject( curve.times, i, interpolatedTimes ); + curve.values = inject( curve.values, i, interpolatedValues ); + + } + + } + + }, + +}; + +// parse an FBX file in ASCII format +function TextParser() {} + +TextParser.prototype = { + + constructor: TextParser, + + getPrevNode: function () { + + return this.nodeStack[ this.currentIndent - 2 ]; + + }, + + getCurrentNode: function () { + + return this.nodeStack[ this.currentIndent - 1 ]; + + }, + + getCurrentProp: function () { + + return this.currentProp; + + }, + + pushStack: function ( node ) { + + this.nodeStack.push( node ); + this.currentIndent += 1; + + }, + + popStack: function () { + + this.nodeStack.pop(); + this.currentIndent -= 1; + + }, + + setCurrentProp: function ( val, name ) { + + this.currentProp = val; + this.currentPropName = name; + + }, + + parse: function ( text ) { + + this.currentIndent = 0; + + this.allNodes = new FBXTree(); + this.nodeStack = []; + this.currentProp = []; + this.currentPropName = ''; + + var self = this; + + var split = text.split( /[\r\n]+/ ); + + split.forEach( function ( line, i ) { + + var matchComment = line.match( /^[\s\t]*;/ ); + var matchEmpty = line.match( /^[\s\t]*$/ ); + + if ( matchComment || matchEmpty ) return; + + var matchBeginning = line.match( '^\\t{' + self.currentIndent + '}(\\w+):(.*){', '' ); + var matchProperty = line.match( '^\\t{' + ( self.currentIndent ) + '}(\\w+):[\\s\\t\\r\\n](.*)' ); + var matchEnd = line.match( '^\\t{' + ( self.currentIndent - 1 ) + '}}' ); + + if ( matchBeginning ) { + + self.parseNodeBegin( line, matchBeginning ); + + } else if ( matchProperty ) { + + self.parseNodeProperty( line, matchProperty, split[ ++ i ] ); + + } else if ( matchEnd ) { + + self.popStack(); + + } else if ( line.match( /^[^\s\t}]/ ) ) { + + // large arrays are split over multiple lines terminated with a ',' character + // if this is encountered the line needs to be joined to the previous line + self.parseNodePropertyContinued( line ); + + } + + } ); + + return this.allNodes; + + }, + + parseNodeBegin: function ( line, property ) { + + var nodeName = property[ 1 ].trim().replace( /^"/, '' ).replace( /"$/, '' ); + + var nodeAttrs = property[ 2 ].split( ',' ).map( function ( attr ) { + + return attr.trim().replace( /^"/, '' ).replace( /"$/, '' ); + + } ); + + var node = { name: nodeName }; + var attrs = this.parseNodeAttr( nodeAttrs ); + + var currentNode = this.getCurrentNode(); + + // a top node + if ( this.currentIndent === 0 ) { + + this.allNodes.add( nodeName, node ); + + } else { // a subnode + + // if the subnode already exists, append it + if ( nodeName in currentNode ) { + + // special case Pose needs PoseNodes as an array + if ( nodeName === 'PoseNode' ) { + + currentNode.PoseNode.push( node ); + + } else if ( currentNode[ nodeName ].id !== undefined ) { + + currentNode[ nodeName ] = {}; + currentNode[ nodeName ][ currentNode[ nodeName ].id ] = currentNode[ nodeName ]; + + } + + if ( attrs.id !== '' ) currentNode[ nodeName ][ attrs.id ] = node; + + } else if ( typeof attrs.id === 'number' ) { + + currentNode[ nodeName ] = {}; + currentNode[ nodeName ][ attrs.id ] = node; + + } else if ( nodeName !== 'Properties70' ) { + + if ( nodeName === 'PoseNode' ) currentNode[ nodeName ] = [ node ]; + else currentNode[ nodeName ] = node; + + } + + } + + if ( typeof attrs.id === 'number' ) node.id = attrs.id; + if ( attrs.name !== '' ) node.attrName = attrs.name; + if ( attrs.type !== '' ) node.attrType = attrs.type; + + this.pushStack( node ); + + }, + + parseNodeAttr: function ( attrs ) { + + var id = attrs[ 0 ]; + + if ( attrs[ 0 ] !== '' ) { + + id = parseInt( attrs[ 0 ] ); + + if ( isNaN( id ) ) { + + id = attrs[ 0 ]; + + } + + } + + var name = '', type = ''; + + if ( attrs.length > 1 ) { + + name = attrs[ 1 ].replace( /^(\w+)::/, '' ); + type = attrs[ 2 ]; + + } + + return { id: id, name: name, type: type }; + + }, + + parseNodeProperty: function ( line, property, contentLine ) { + + var propName = property[ 1 ].replace( /^"/, '' ).replace( /"$/, '' ).trim(); + var propValue = property[ 2 ].replace( /^"/, '' ).replace( /"$/, '' ).trim(); + + // for special case: base64 image data follows "Content: ," line + // Content: , + // "/9j/4RDaRXhpZgAATU0A..." + if ( propName === 'Content' && propValue === ',' ) { + + propValue = contentLine.replace( /"/g, '' ).replace( /,$/, '' ).trim(); + + } + + var currentNode = this.getCurrentNode(); + var parentName = currentNode.name; + + if ( parentName === 'Properties70' ) { + + this.parseNodeSpecialProperty( line, propName, propValue ); + return; + + } + + // Connections + if ( propName === 'C' ) { + + var connProps = propValue.split( ',' ).slice( 1 ); + var from = parseInt( connProps[ 0 ] ); + var to = parseInt( connProps[ 1 ] ); + + var rest = propValue.split( ',' ).slice( 3 ); + + rest = rest.map( function ( elem ) { + + return elem.trim().replace( /^"/, '' ); + + } ); + + propName = 'connections'; + propValue = [ from, to ]; + append( propValue, rest ); + + if ( currentNode[ propName ] === undefined ) { + + currentNode[ propName ] = []; + + } + + } + + // Node + if ( propName === 'Node' ) currentNode.id = propValue; + + // connections + if ( propName in currentNode && Array.isArray( currentNode[ propName ] ) ) { + + currentNode[ propName ].push( propValue ); + + } else { + + if ( propName !== 'a' ) currentNode[ propName ] = propValue; + else currentNode.a = propValue; + + } + + this.setCurrentProp( currentNode, propName ); + + // convert string to array, unless it ends in ',' in which case more will be added to it + if ( propName === 'a' && propValue.slice( - 1 ) !== ',' ) { + + currentNode.a = parseNumberArray( propValue ); + + } + + }, + + parseNodePropertyContinued: function ( line ) { + + var currentNode = this.getCurrentNode(); + + currentNode.a += line; + + // if the line doesn't end in ',' we have reached the end of the property value + // so convert the string to an array + if ( line.slice( - 1 ) !== ',' ) { + + currentNode.a = parseNumberArray( currentNode.a ); + + } + + }, + + // parse "Property70" + parseNodeSpecialProperty: function ( line, propName, propValue ) { + + // split this + // P: "Lcl Scaling", "Lcl Scaling", "", "A",1,1,1 + // into array like below + // ["Lcl Scaling", "Lcl Scaling", "", "A", "1,1,1" ] + var props = propValue.split( '",' ).map( function ( prop ) { + + return prop.trim().replace( /^\"/, '' ).replace( /\s/, '_' ); + + } ); + + var innerPropName = props[ 0 ]; + var innerPropType1 = props[ 1 ]; + var innerPropType2 = props[ 2 ]; + var innerPropFlag = props[ 3 ]; + var innerPropValue = props[ 4 ]; + + // cast values where needed, otherwise leave as strings + switch ( innerPropType1 ) { + + case 'int': + case 'enum': + case 'bool': + case 'ULongLong': + case 'double': + case 'Number': + case 'FieldOfView': + innerPropValue = parseFloat( innerPropValue ); + break; + + case 'Color': + case 'ColorRGB': + case 'Vector3D': + case 'Lcl_Translation': + case 'Lcl_Rotation': + case 'Lcl_Scaling': + innerPropValue = parseNumberArray( innerPropValue ); + break; + + } + + // CAUTION: these props must append to parent's parent + this.getPrevNode()[ innerPropName ] = { + + 'type': innerPropType1, + 'type2': innerPropType2, + 'flag': innerPropFlag, + 'value': innerPropValue + + }; + + this.setCurrentProp( this.getPrevNode(), innerPropName ); + + }, + +}; + +// Parse an FBX file in Binary format +function BinaryParser() {} + +BinaryParser.prototype = { + + constructor: BinaryParser, + + parse: function ( buffer ) { + + var reader = new BinaryReader( buffer ); + reader.skip( 23 ); // skip magic 23 bytes + + var version = reader.getUint32(); + + console.log( 'THREE.FBXLoader: FBX binary version: ' + version ); + + var allNodes = new FBXTree(); + + while ( ! this.endOfContent( reader ) ) { + + var node = this.parseNode( reader, version ); + if ( node !== null ) allNodes.add( node.name, node ); + + } + + return allNodes; + + }, + + // Check if reader has reached the end of content. + endOfContent: function ( reader ) { + + // footer size: 160bytes + 16-byte alignment padding + // - 16bytes: magic + // - padding til 16-byte alignment (at least 1byte?) + // (seems like some exporters embed fixed 15 or 16bytes?) + // - 4bytes: magic + // - 4bytes: version + // - 120bytes: zero + // - 16bytes: magic + if ( reader.size() % 16 === 0 ) { + + return ( ( reader.getOffset() + 160 + 16 ) & ~ 0xf ) >= reader.size(); + + } else { + + return reader.getOffset() + 160 + 16 >= reader.size(); + + } + + }, + + // recursively parse nodes until the end of the file is reached + parseNode: function ( reader, version ) { + + var node = {}; + + // The first three data sizes depends on version. + var endOffset = ( version >= 7500 ) ? reader.getUint64() : reader.getUint32(); + var numProperties = ( version >= 7500 ) ? reader.getUint64() : reader.getUint32(); + + // note: do not remove this even if you get a linter warning as it moves the buffer forward + var propertyListLen = ( version >= 7500 ) ? reader.getUint64() : reader.getUint32(); + + var nameLen = reader.getUint8(); + var name = reader.getString( nameLen ); + + // Regards this node as NULL-record if endOffset is zero + if ( endOffset === 0 ) return null; + + var propertyList = []; + + for ( var i = 0; i < numProperties; i ++ ) { + + propertyList.push( this.parseProperty( reader ) ); + + } + + // Regards the first three elements in propertyList as id, attrName, and attrType + var id = propertyList.length > 0 ? propertyList[ 0 ] : ''; + var attrName = propertyList.length > 1 ? propertyList[ 1 ] : ''; + var attrType = propertyList.length > 2 ? propertyList[ 2 ] : ''; + + // check if this node represents just a single property + // like (name, 0) set or (name2, [0, 1, 2]) set of {name: 0, name2: [0, 1, 2]} + node.singleProperty = ( numProperties === 1 && reader.getOffset() === endOffset ) ? true : false; + + while ( endOffset > reader.getOffset() ) { + + var subNode = this.parseNode( reader, version ); + + if ( subNode !== null ) this.parseSubNode( name, node, subNode ); + + } + + node.propertyList = propertyList; // raw property list used by parent + + if ( typeof id === 'number' ) node.id = id; + if ( attrName !== '' ) node.attrName = attrName; + if ( attrType !== '' ) node.attrType = attrType; + if ( name !== '' ) node.name = name; + + return node; + + }, + + parseSubNode: function ( name, node, subNode ) { + + // special case: child node is single property + if ( subNode.singleProperty === true ) { + + var value = subNode.propertyList[ 0 ]; + + if ( Array.isArray( value ) ) { + + node[ subNode.name ] = subNode; + + subNode.a = value; + + } else { + + node[ subNode.name ] = value; + + } + + } else if ( name === 'Connections' && subNode.name === 'C' ) { + + var array = []; + + subNode.propertyList.forEach( function ( property, i ) { + + // first Connection is FBX type (OO, OP, etc.). We'll discard these + if ( i !== 0 ) array.push( property ); + + } ); + + if ( node.connections === undefined ) { + + node.connections = []; + + } + + node.connections.push( array ); + + } else if ( subNode.name === 'Properties70' ) { + + var keys = Object.keys( subNode ); + + keys.forEach( function ( key ) { + + node[ key ] = subNode[ key ]; + + } ); + + } else if ( name === 'Properties70' && subNode.name === 'P' ) { + + var innerPropName = subNode.propertyList[ 0 ]; + var innerPropType1 = subNode.propertyList[ 1 ]; + var innerPropType2 = subNode.propertyList[ 2 ]; + var innerPropFlag = subNode.propertyList[ 3 ]; + var innerPropValue; + + if ( innerPropName.indexOf( 'Lcl ' ) === 0 ) innerPropName = innerPropName.replace( 'Lcl ', 'Lcl_' ); + if ( innerPropType1.indexOf( 'Lcl ' ) === 0 ) innerPropType1 = innerPropType1.replace( 'Lcl ', 'Lcl_' ); + + if ( innerPropType1 === 'Color' || innerPropType1 === 'ColorRGB' || innerPropType1 === 'Vector' || innerPropType1 === 'Vector3D' || innerPropType1.indexOf( 'Lcl_' ) === 0 ) { + + innerPropValue = [ + subNode.propertyList[ 4 ], + subNode.propertyList[ 5 ], + subNode.propertyList[ 6 ] + ]; + + } else { + + innerPropValue = subNode.propertyList[ 4 ]; + + } + + // this will be copied to parent, see above + node[ innerPropName ] = { + + 'type': innerPropType1, + 'type2': innerPropType2, + 'flag': innerPropFlag, + 'value': innerPropValue + + }; + + } else if ( node[ subNode.name ] === undefined ) { + + if ( typeof subNode.id === 'number' ) { + + node[ subNode.name ] = {}; + node[ subNode.name ][ subNode.id ] = subNode; + + } else { + + node[ subNode.name ] = subNode; + + } + + } else { + + if ( subNode.name === 'PoseNode' ) { + + if ( ! Array.isArray( node[ subNode.name ] ) ) { + + node[ subNode.name ] = [ node[ subNode.name ] ]; + + } + + node[ subNode.name ].push( subNode ); + + } else if ( node[ subNode.name ][ subNode.id ] === undefined ) { + + node[ subNode.name ][ subNode.id ] = subNode; + + } + + } + + }, + + parseProperty: function ( reader ) { + + var type = reader.getString( 1 ); + + switch ( type ) { + + case 'C': + return reader.getBoolean(); + + case 'D': + return reader.getFloat64(); + + case 'F': + return reader.getFloat32(); + + case 'I': + return reader.getInt32(); + + case 'L': + return reader.getInt64(); + + case 'R': + var length = reader.getUint32(); + return reader.getArrayBuffer( length ); + + case 'S': + var length = reader.getUint32(); + return reader.getString( length ); + + case 'Y': + return reader.getInt16(); + + case 'b': + case 'c': + case 'd': + case 'f': + case 'i': + case 'l': + + var arrayLength = reader.getUint32(); + var encoding = reader.getUint32(); // 0: non-compressed, 1: compressed + var compressedLength = reader.getUint32(); + + if ( encoding === 0 ) { + + switch ( type ) { + + case 'b': + case 'c': + return reader.getBooleanArray( arrayLength ); + + case 'd': + return reader.getFloat64Array( arrayLength ); + + case 'f': + return reader.getFloat32Array( arrayLength ); + + case 'i': + return reader.getInt32Array( arrayLength ); + + case 'l': + return reader.getInt64Array( arrayLength ); + + } + + } + + if ( typeof Zlib === 'undefined' ) { + + console.error( 'THREE.FBXLoader: External library Inflate.min.js required, obtain or import from https://github.com/imaya/zlib.js' ); + + } + + var inflate = new Zlib.Inflate( new Uint8Array( reader.getArrayBuffer( compressedLength ) ) ); // eslint-disable-line no-undef + var reader2 = new BinaryReader( inflate.decompress().buffer ); + + switch ( type ) { + + case 'b': + case 'c': + return reader2.getBooleanArray( arrayLength ); + + case 'd': + return reader2.getFloat64Array( arrayLength ); + + case 'f': + return reader2.getFloat32Array( arrayLength ); + + case 'i': + return reader2.getInt32Array( arrayLength ); + + case 'l': + return reader2.getInt64Array( arrayLength ); + + } + + default: + throw new Error( 'THREE.FBXLoader: Unknown property type ' + type ); + + } + + } + +}; + +function BinaryReader( buffer, littleEndian ) { + + this.dv = new DataView( buffer ); + this.offset = 0; + this.littleEndian = ( littleEndian !== undefined ) ? littleEndian : true; + +} + +BinaryReader.prototype = { + + constructor: BinaryReader, + + getOffset: function () { + + return this.offset; + + }, + + size: function () { + + return this.dv.buffer.byteLength; + + }, + + skip: function ( length ) { + + this.offset += length; + + }, + + // seems like true/false representation depends on exporter. + // true: 1 or 'Y'(=0x59), false: 0 or 'T'(=0x54) + // then sees LSB. + getBoolean: function () { + + return ( this.getUint8() & 1 ) === 1; + + }, + + getBooleanArray: function ( size ) { + + var a = []; + + for ( var i = 0; i < size; i ++ ) { + + a.push( this.getBoolean() ); + + } + + return a; + + }, + + getUint8: function () { + + var value = this.dv.getUint8( this.offset ); + this.offset += 1; + return value; + + }, + + getInt16: function () { + + var value = this.dv.getInt16( this.offset, this.littleEndian ); + this.offset += 2; + return value; + + }, + + getInt32: function () { + + var value = this.dv.getInt32( this.offset, this.littleEndian ); + this.offset += 4; + return value; + + }, + + getInt32Array: function ( size ) { + + var a = []; + + for ( var i = 0; i < size; i ++ ) { + + a.push( this.getInt32() ); + + } + + return a; + + }, + + getUint32: function () { + + var value = this.dv.getUint32( this.offset, this.littleEndian ); + this.offset += 4; + return value; + + }, + + // JavaScript doesn't support 64-bit integer so calculate this here + // 1 << 32 will return 1 so using multiply operation instead here. + // There's a possibility that this method returns wrong value if the value + // is out of the range between Number.MAX_SAFE_INTEGER and Number.MIN_SAFE_INTEGER. + // TODO: safely handle 64-bit integer + getInt64: function () { + + var low, high; + + if ( this.littleEndian ) { + + low = this.getUint32(); + high = this.getUint32(); + + } else { + + high = this.getUint32(); + low = this.getUint32(); + + } + + // calculate negative value + if ( high & 0x80000000 ) { + + high = ~ high & 0xFFFFFFFF; + low = ~ low & 0xFFFFFFFF; + + if ( low === 0xFFFFFFFF ) high = ( high + 1 ) & 0xFFFFFFFF; + + low = ( low + 1 ) & 0xFFFFFFFF; + + return - ( high * 0x100000000 + low ); + + } + + return high * 0x100000000 + low; + + }, + + getInt64Array: function ( size ) { + + var a = []; + + for ( var i = 0; i < size; i ++ ) { + + a.push( this.getInt64() ); + + } + + return a; + + }, + + // Note: see getInt64() comment + getUint64: function () { + + var low, high; + + if ( this.littleEndian ) { + + low = this.getUint32(); + high = this.getUint32(); + + } else { + + high = this.getUint32(); + low = this.getUint32(); + + } + + return high * 0x100000000 + low; + + }, + + getFloat32: function () { + + var value = this.dv.getFloat32( this.offset, this.littleEndian ); + this.offset += 4; + return value; + + }, + + getFloat32Array: function ( size ) { + + var a = []; + + for ( var i = 0; i < size; i ++ ) { + + a.push( this.getFloat32() ); + + } + + return a; + + }, + + getFloat64: function () { + + var value = this.dv.getFloat64( this.offset, this.littleEndian ); + this.offset += 8; + return value; + + }, + + getFloat64Array: function ( size ) { + + var a = []; + + for ( var i = 0; i < size; i ++ ) { + + a.push( this.getFloat64() ); + + } + + return a; + + }, + + getArrayBuffer: function ( size ) { + + var value = this.dv.buffer.slice( this.offset, this.offset + size ); + this.offset += size; + return value; + + }, + + getString: function ( size ) { + + // note: safari 9 doesn't support Uint8Array.indexOf; create intermediate array instead + var a = []; + + for ( var i = 0; i < size; i ++ ) { + + a[ i ] = this.getUint8(); + + } + + var nullByte = a.indexOf( 0 ); + if ( nullByte >= 0 ) a = a.slice( 0, nullByte ); + + return LoaderUtils.decodeText( new Uint8Array( a ) ); + + } + +}; + +// FBXTree holds a representation of the FBX data, returned by the TextParser ( FBX ASCII format) +// and BinaryParser( FBX Binary format) +function FBXTree() {} + +FBXTree.prototype = { + + constructor: FBXTree, + + add: function ( key, val ) { + + this[ key ] = val; + + }, + +}; + +// ************** UTILITY FUNCTIONS ************** + +function isFbxFormatBinary( buffer ) { + + var CORRECT = 'Kaydara FBX Binary \0'; + + return buffer.byteLength >= CORRECT.length && CORRECT === convertArrayBufferToString( buffer, 0, CORRECT.length ); + +} + +function isFbxFormatASCII( text ) { + + var CORRECT = [ 'K', 'a', 'y', 'd', 'a', 'r', 'a', '\\', 'F', 'B', 'X', '\\', 'B', 'i', 'n', 'a', 'r', 'y', '\\', '\\' ]; + + var cursor = 0; + + function read( offset ) { + + var result = text[ offset - 1 ]; + text = text.slice( cursor + offset ); + cursor ++; + return result; + + } + + for ( var i = 0; i < CORRECT.length; ++ i ) { + + var num = read( 1 ); + if ( num === CORRECT[ i ] ) { + + return false; + + } + + } + + return true; + +} + +function getFbxVersion( text ) { + + var versionRegExp = /FBXVersion: (\d+)/; + var match = text.match( versionRegExp ); + if ( match ) { + + var version = parseInt( match[ 1 ] ); + return version; + + } + throw new Error( 'THREE.FBXLoader: Cannot find the version number for the file given.' ); + +} + +// Converts FBX ticks into real time seconds. +function convertFBXTimeToSeconds( time ) { + + return time / 46186158000; + +} + +var dataArray = []; + +// extracts the data from the correct position in the FBX array based on indexing type +function getData( polygonVertexIndex, polygonIndex, vertexIndex, infoObject ) { + + var index; + + switch ( infoObject.mappingType ) { + + case 'ByPolygonVertex' : + index = polygonVertexIndex; + break; + case 'ByPolygon' : + index = polygonIndex; + break; + case 'ByVertice' : + index = vertexIndex; + break; + case 'AllSame' : + index = infoObject.indices[ 0 ]; + break; + default : + console.warn( 'THREE.FBXLoader: unknown attribute mapping type ' + infoObject.mappingType ); + + } + + if ( infoObject.referenceType === 'IndexToDirect' ) index = infoObject.indices[ index ]; + + var from = index * infoObject.dataSize; + var to = from + infoObject.dataSize; + + return slice( dataArray, infoObject.buffer, from, to ); + +} + +var tempEuler = new Euler(); +var tempVec = new Vector3(); + +// generate transformation from FBX transform data +// ref: https://help.autodesk.com/view/FBX/2017/ENU/?guid=__files_GUID_10CDD63C_79C1_4F2D_BB28_AD2BE65A02ED_htm +// ref: http://docs.autodesk.com/FBX/2014/ENU/FBX-SDK-Documentation/index.html?url=cpp_ref/_transformations_2main_8cxx-example.html,topicNumber=cpp_ref__transformations_2main_8cxx_example_htmlfc10a1e1-b18d-4e72-9dc0-70d0f1959f5e +function generateTransform( transformData ) { + + var lTranslationM = new Matrix4(); + var lPreRotationM = new Matrix4(); + var lRotationM = new Matrix4(); + var lPostRotationM = new Matrix4(); + + var lScalingM = new Matrix4(); + var lScalingPivotM = new Matrix4(); + var lScalingOffsetM = new Matrix4(); + var lRotationOffsetM = new Matrix4(); + var lRotationPivotM = new Matrix4(); + + var lParentGX = new Matrix4(); + var lGlobalT = new Matrix4(); + + var inheritType = ( transformData.inheritType ) ? transformData.inheritType : 0; + + if ( transformData.translation ) lTranslationM.setPosition( tempVec.fromArray( transformData.translation ) ); + + if ( transformData.preRotation ) { + + var array = transformData.preRotation.map( Math.degToRad ); + array.push( transformData.eulerOrder ); + lPreRotationM.makeRotationFromEuler( tempEuler.fromArray( array ) ); + + } + + if ( transformData.rotation ) { + + var array = transformData.rotation.map( Math.degToRad ); + array.push( transformData.eulerOrder ); + lRotationM.makeRotationFromEuler( tempEuler.fromArray( array ) ); + + } + + if ( transformData.postRotation ) { + + var array = transformData.postRotation.map( Math.degToRad ); + array.push( transformData.eulerOrder ); + lPostRotationM.makeRotationFromEuler( tempEuler.fromArray( array ) ); + + } + + if ( transformData.scale ) lScalingM.scale( tempVec.fromArray( transformData.scale ) ); + + // Pivots and offsets + if ( transformData.scalingOffset ) lScalingOffsetM.setPosition( tempVec.fromArray( transformData.scalingOffset ) ); + if ( transformData.scalingPivot ) lScalingPivotM.setPosition( tempVec.fromArray( transformData.scalingPivot ) ); + if ( transformData.rotationOffset ) lRotationOffsetM.setPosition( tempVec.fromArray( transformData.rotationOffset ) ); + if ( transformData.rotationPivot ) lRotationPivotM.setPosition( tempVec.fromArray( transformData.rotationPivot ) ); + + // parent transform + if ( transformData.parentMatrixWorld ) lParentGX = transformData.parentMatrixWorld; + + // Global Rotation + var lLRM = lPreRotationM.multiply( lRotationM ).multiply( lPostRotationM ); + var lParentGRM = new Matrix4(); + lParentGX.extractRotation( lParentGRM ); + + // Global Shear*Scaling + var lParentTM = new Matrix4(); + var lLSM; + var lParentGSM; + var lParentGRSM; + + lParentTM.copyPosition( lParentGX ); + lParentGRSM = lParentTM.getInverse( lParentTM ).multiply( lParentGX ); + lParentGSM = lParentGRM.getInverse( lParentGRM ).multiply( lParentGRSM ); + lLSM = lScalingM; + + var lGlobalRS; + if ( inheritType === 0 ) { + + lGlobalRS = lParentGRM.multiply( lLRM ).multiply( lParentGSM ).multiply( lLSM ); + + } else if ( inheritType === 1 ) { + + lGlobalRS = lParentGRM.multiply( lParentGSM ).multiply( lLRM ).multiply( lLSM ); + + } else { + + var lParentLSM = new Matrix4().copy( lScalingM ); + + var lParentGSM_noLocal = lParentGSM.multiply( lParentLSM.getInverse( lParentLSM ) ); + + lGlobalRS = lParentGRM.multiply( lLRM ).multiply( lParentGSM_noLocal ).multiply( lLSM ); + + } + + // Calculate the local transform matrix + var lTransform = lTranslationM.multiply( lRotationOffsetM ).multiply( lRotationPivotM ).multiply( lPreRotationM ).multiply( lRotationM ).multiply( lPostRotationM ).multiply( lRotationPivotM.getInverse( lRotationPivotM ) ).multiply( lScalingOffsetM ).multiply( lScalingPivotM ).multiply( lScalingM ).multiply( lScalingPivotM.getInverse( lScalingPivotM ) ); + + var lLocalTWithAllPivotAndOffsetInfo = new Matrix4().copyPosition( lTransform ); + + var lGlobalTranslation = lParentGX.multiply( lLocalTWithAllPivotAndOffsetInfo ); + lGlobalT.copyPosition( lGlobalTranslation ); + + lTransform = lGlobalT.multiply( lGlobalRS ); + + return lTransform; + +} + +// Returns the three.js intrinsic Euler order corresponding to FBX extrinsic Euler order +// ref: http://help.autodesk.com/view/FBX/2017/ENU/?guid=__cpp_ref_class_fbx_euler_html +function getEulerOrder( order ) { + + order = order || 0; + + var enums = [ + 'ZYX', // -> XYZ extrinsic + 'YZX', // -> XZY extrinsic + 'XZY', // -> YZX extrinsic + 'ZXY', // -> YXZ extrinsic + 'YXZ', // -> ZXY extrinsic + 'XYZ', // -> ZYX extrinsic + //'SphericXYZ', // not possible to support + ]; + + if ( order === 6 ) { + + console.warn( 'THREE.FBXLoader: unsupported Euler Order: Spherical XYZ. Animations and rotations may be incorrect.' ); + return enums[ 0 ]; + + } + + return enums[ order ]; + +} + +// Parses comma separated list of numbers and returns them an array. +// Used internally by the TextParser +function parseNumberArray( value ) { + + var array = value.split( ',' ).map( function ( val ) { + + return parseFloat( val ); + + } ); + + return array; + +} + +function convertArrayBufferToString( buffer, from, to ) { + + if ( from === undefined ) from = 0; + if ( to === undefined ) to = buffer.byteLength; + + return LoaderUtils.decodeText( new Uint8Array( buffer, from, to ) ); + +} + +function append( a, b ) { + + for ( var i = 0, j = a.length, l = b.length; i < l; i ++, j ++ ) { + + a[ j ] = b[ i ]; + + } + +} + +function slice( a, b, from, to ) { + + for ( var i = from, j = 0; i < to; i ++, j ++ ) { + + a[ j ] = b[ i ]; + + } + + return a; + +} + +// inject array a2 into array a1 at index +function inject( a1, index, a2 ) { + + return a1.slice( 0, index ).concat( a2 ).concat( a1.slice( index ) ); + +} + +export { FBXLoader }; diff --git a/examples/jsm/loaders/GCodeLoader.js b/examples/jsm/loaders/GCodeLoader.js new file mode 100644 index 00000000000000..b712b4162e5eb4 --- /dev/null +++ b/examples/jsm/loaders/GCodeLoader.js @@ -0,0 +1,236 @@ +import { + BufferGeometry, + DefaultLoadingManager, + Euler, + FileLoader, + Float32BufferAttribute, + Group, + LineBasicMaterial, + LineSegments +} from "../../../build/three.module.js"; + +/** + * GCodeLoader is used to load gcode files usually used for 3D printing or CNC applications. + * + * Gcode files are composed by commands used by machines to create objects. + * + * @class THREE.GCodeLoader + * @param {Manager} manager Loading manager. + * @author tentone + * @author joewalnes + */ +var GCodeLoader = function ( manager ) { + + this.manager = ( manager !== undefined ) ? manager : DefaultLoadingManager; + + this.splitLayer = false; + +}; + +GCodeLoader.prototype.load = function ( url, onLoad, onProgress, onError ) { + + var self = this; + + var loader = new FileLoader( self.manager ); + loader.setPath( self.path ); + loader.load( url, function ( text ) { + + onLoad( self.parse( text ) ); + + }, onProgress, onError ); + +}; + +GCodeLoader.prototype.setPath = function ( value ) { + + this.path = value; + return this; + +}; + +GCodeLoader.prototype.parse = function ( data ) { + + var state = { x: 0, y: 0, z: 0, e: 0, f: 0, extruding: false, relative: false }; + var layers = []; + + var currentLayer = undefined; + + var pathMaterial = new LineBasicMaterial( { color: 0xFF0000 } ); + pathMaterial.name = 'path'; + + var extrudingMaterial = new LineBasicMaterial( { color: 0x00FF00 } ); + extrudingMaterial.name = 'extruded'; + + function newLayer( line ) { + + currentLayer = { vertex: [], pathVertex: [], z: line.z }; + layers.push( currentLayer ); + + } + + //Create lie segment between p1 and p2 + function addSegment( p1, p2 ) { + + if ( currentLayer === undefined ) { + + newLayer( p1 ); + + } + + if ( line.extruding ) { + + currentLayer.vertex.push( p1.x, p1.y, p1.z ); + currentLayer.vertex.push( p2.x, p2.y, p2.z ); + + } else { + + currentLayer.pathVertex.push( p1.x, p1.y, p1.z ); + currentLayer.pathVertex.push( p2.x, p2.y, p2.z ); + + } + + } + + function delta( v1, v2 ) { + + return state.relative ? v2 : v2 - v1; + + } + + function absolute( v1, v2 ) { + + return state.relative ? v1 + v2 : v2; + + } + + var lines = data.replace( /;.+/g, '' ).split( '\n' ); + + for ( var i = 0; i < lines.length; i ++ ) { + + var tokens = lines[ i ].split( ' ' ); + var cmd = tokens[ 0 ].toUpperCase(); + + //Argumments + var args = {}; + tokens.splice( 1 ).forEach( function ( token ) { + + if ( token[ 0 ] !== undefined ) { + + var key = token[ 0 ].toLowerCase(); + var value = parseFloat( token.substring( 1 ) ); + args[ key ] = value; + + } + + } ); + + //Process commands + //G0/G1 – Linear Movement + if ( cmd === 'G0' || cmd === 'G1' ) { + + var line = { + x: args.x !== undefined ? absolute( state.x, args.x ) : state.x, + y: args.y !== undefined ? absolute( state.y, args.y ) : state.y, + z: args.z !== undefined ? absolute( state.z, args.z ) : state.z, + e: args.e !== undefined ? absolute( state.e, args.e ) : state.e, + f: args.f !== undefined ? absolute( state.f, args.f ) : state.f, + }; + + //Layer change detection is or made by watching Z, it's made by watching when we extrude at a new Z position + if ( delta( state.e, line.e ) > 0 ) { + + line.extruding = delta( state.e, line.e ) > 0; + + if ( currentLayer == undefined || line.z != currentLayer.z ) { + + newLayer( line ); + + } + + } + + addSegment( state, line ); + state = line; + + } else if ( cmd === 'G2' || cmd === 'G3' ) { + + //G2/G3 - Arc Movement ( G2 clock wise and G3 counter clock wise ) + console.warn( 'THREE.GCodeLoader: Arc command not supported' ); + + } else if ( cmd === 'G90' ) { + + //G90: Set to Absolute Positioning + state.relative = false; + + } else if ( cmd === 'G91' ) { + + //G91: Set to state.relative Positioning + state.relative = true; + + } else if ( cmd === 'G92' ) { + + //G92: Set Position + var line = state; + line.x = args.x !== undefined ? args.x : line.x; + line.y = args.y !== undefined ? args.y : line.y; + line.z = args.z !== undefined ? args.z : line.z; + line.e = args.e !== undefined ? args.e : line.e; + state = line; + + } else { + + console.warn( 'THREE.GCodeLoader: Command not supported:' + cmd ); + + } + + } + + function addObject( vertex, extruding ) { + + var geometry = new BufferGeometry(); + geometry.addAttribute( 'position', new Float32BufferAttribute( vertex, 3 ) ); + + var segments = new LineSegments( geometry, extruding ? extrudingMaterial : pathMaterial ); + segments.name = 'layer' + i; + object.add( segments ); + + } + + var object = new Group(); + object.name = 'gcode'; + + if ( this.splitLayer ) { + + for ( var i = 0; i < layers.length; i ++ ) { + + var layer = layers[ i ]; + addObject( layer.vertex, true ); + addObject( layer.pathVertex, false ); + + } + + } else { + + var vertex = [], pathVertex = []; + + for ( var i = 0; i < layers.length; i ++ ) { + + var layer = layers[ i ]; + + vertex = vertex.concat( layer.vertex ); + pathVertex = pathVertex.concat( layer.pathVertex ); + + } + + addObject( vertex, true ); + addObject( pathVertex, false ); + + } + + object.quaternion.setFromEuler( new Euler( - Math.PI / 2, 0, 0 ) ); + + return object; + +}; + +export { GCodeLoader }; diff --git a/examples/jsm/loaders/GLTFLoader.js b/examples/jsm/loaders/GLTFLoader.js index c71a1a7e8b9ff4..bf112370df7726 100644 --- a/examples/jsm/loaders/GLTFLoader.js +++ b/examples/jsm/loaders/GLTFLoader.js @@ -99,6 +99,8 @@ import { ZeroFactor, sRGBEncoding } from "../../../build/three.module.js"; +import { DDSLoader } from "./DDSLoader"; +import { DRACOLoader } from "./DRACOLoader"; var GLTFLoader = ( function () { @@ -393,14 +395,8 @@ var GLTFLoader = ( function () { */ function GLTFTextureDDSExtension() { - if ( ! THREE.DDSLoader ) { - - throw new Error( 'THREE.GLTFLoader: Attempting to load .dds texture without importing THREE.DDSLoader' ); - - } - this.name = EXTENSIONS.MSFT_TEXTURE_DDS; - this.ddsLoader = new THREE.DDSLoader(); + this.ddsLoader = new DDSLoader(); } @@ -602,7 +598,7 @@ var GLTFLoader = ( function () { this.name = EXTENSIONS.KHR_DRACO_MESH_COMPRESSION; this.json = json; this.dracoLoader = dracoLoader; - THREE.DRACOLoader.getDecoderModule(); + DRACOLoader.getDecoderModule(); } diff --git a/examples/jsm/loaders/HDRCubeTextureLoader.js b/examples/jsm/loaders/HDRCubeTextureLoader.js new file mode 100644 index 00000000000000..1e5bf7ee0a9b03 --- /dev/null +++ b/examples/jsm/loaders/HDRCubeTextureLoader.js @@ -0,0 +1,214 @@ +/** + * @author Prashant Sharma / spidersharma03 + * @author Ben Houston / http://clara.io / bhouston + */ + +import { + CubeTexture, + DataTexture, + DefaultLoadingManager, + FileLoader, + FloatType, + HalfFloatType, + LinearEncoding, + LinearFilter, + NearestFilter, + RGBAFormat, + RGBEEncoding, + RGBFormat, + UnsignedByteType, +} from "../../../build/three.module.js"; + +import { RGBELoader } from './RGBELoader'; + +var HDRCubeTextureLoader = function ( manager ) { + + this.manager = ( manager !== undefined ) ? manager : DefaultLoadingManager; + // override in sub classes + this.hdrLoader = new RGBELoader(); + +}; + +HDRCubeTextureLoader.prototype.load = function ( type, urls, onLoad, onProgress, onError ) { + + var RGBEByteToRGBFloat = function ( sourceArray, sourceOffset, destArray, destOffset ) { + + var e = sourceArray[ sourceOffset + 3 ]; + var scale = Math.pow( 2.0, e - 128.0 ) / 255.0; + + destArray[ destOffset + 0 ] = sourceArray[ sourceOffset + 0 ] * scale; + destArray[ destOffset + 1 ] = sourceArray[ sourceOffset + 1 ] * scale; + destArray[ destOffset + 2 ] = sourceArray[ sourceOffset + 2 ] * scale; + + }; + + var RGBEByteToRGBHalf = ( function () { + + // Source: http://gamedev.stackexchange.com/questions/17326/conversion-of-a-number-from-single-precision-floating-point-representation-to-a/17410#17410 + + var floatView = new Float32Array( 1 ); + var int32View = new Int32Array( floatView.buffer ); + + /* This method is faster than the OpenEXR implementation (very often + * used, eg. in Ogre), with the additional benefit of rounding, inspired + * by James Tursa?s half-precision code. */ + function toHalf( val ) { + + floatView[ 0 ] = val; + var x = int32View[ 0 ]; + + var bits = ( x >> 16 ) & 0x8000; /* Get the sign */ + var m = ( x >> 12 ) & 0x07ff; /* Keep one extra bit for rounding */ + var e = ( x >> 23 ) & 0xff; /* Using int is faster here */ + + /* If zero, or denormal, or exponent underflows too much for a denormal + * half, return signed zero. */ + if ( e < 103 ) return bits; + + /* If NaN, return NaN. If Inf or exponent overflow, return Inf. */ + if ( e > 142 ) { + + bits |= 0x7c00; + /* If exponent was 0xff and one mantissa bit was set, it means NaN, + * not Inf, so make sure we set one mantissa bit too. */ + bits |= ( ( e == 255 ) ? 0 : 1 ) && ( x & 0x007fffff ); + return bits; + + } + + /* If exponent underflows but not too much, return a denormal */ + if ( e < 113 ) { + + m |= 0x0800; + /* Extra rounding may overflow and set mantissa to 0 and exponent + * to 1, which is OK. */ + bits |= ( m >> ( 114 - e ) ) + ( ( m >> ( 113 - e ) ) & 1 ); + return bits; + + } + + bits |= ( ( e - 112 ) << 10 ) | ( m >> 1 ); + /* Extra rounding. An overflow will set mantissa to 0 and increment + * the exponent, which is OK. */ + bits += m & 1; + return bits; + + } + + return function ( sourceArray, sourceOffset, destArray, destOffset ) { + + var e = sourceArray[ sourceOffset + 3 ]; + var scale = Math.pow( 2.0, e - 128.0 ) / 255.0; + + destArray[ destOffset + 0 ] = toHalf( sourceArray[ sourceOffset + 0 ] * scale ); + destArray[ destOffset + 1 ] = toHalf( sourceArray[ sourceOffset + 1 ] * scale ); + destArray[ destOffset + 2 ] = toHalf( sourceArray[ sourceOffset + 2 ] * scale ); + + }; + + } )(); + + // + + var texture = new CubeTexture(); + + texture.type = type; + texture.encoding = ( type === UnsignedByteType ) ? RGBEEncoding : LinearEncoding; + texture.format = ( type === UnsignedByteType ) ? RGBAFormat : RGBFormat; + texture.minFilter = ( texture.encoding === RGBEEncoding ) ? NearestFilter : LinearFilter; + texture.magFilter = ( texture.encoding === RGBEEncoding ) ? NearestFilter : LinearFilter; + texture.generateMipmaps = ( texture.encoding !== RGBEEncoding ); + texture.anisotropy = 0; + + var scope = this; + + var loaded = 0; + + function loadHDRData( i, onLoad, onProgress, onError ) { + + var loader = new FileLoader( scope.manager ); + loader.setPath( scope.path ); + loader.setResponseType( 'arraybuffer' ); + loader.load( urls[ i ], function ( buffer ) { + + loaded ++; + + var texData = scope.hdrLoader._parser( buffer ); + + if ( ! texData ) return; + + if ( type === FloatType ) { + + var numElements = ( texData.data.length / 4 ) * 3; + var floatdata = new Float32Array( numElements ); + + for ( var j = 0; j < numElements; j ++ ) { + + RGBEByteToRGBFloat( texData.data, j * 4, floatdata, j * 3 ); + + } + + texData.data = floatdata; + + } else if ( type === HalfFloatType ) { + + var numElements = ( texData.data.length / 4 ) * 3; + var halfdata = new Uint16Array( numElements ); + + for ( var j = 0; j < numElements; j ++ ) { + + RGBEByteToRGBHalf( texData.data, j * 4, halfdata, j * 3 ); + + } + + texData.data = halfdata; + + } + + if ( texData.image !== undefined ) { + + texture[ i ].images = texData.image; + + } else if ( texData.data !== undefined ) { + + var dataTexture = new DataTexture( texData.data, texData.width, texData.height ); + dataTexture.format = texture.format; + dataTexture.type = texture.type; + dataTexture.encoding = texture.encoding; + dataTexture.minFilter = texture.minFilter; + dataTexture.magFilter = texture.magFilter; + dataTexture.generateMipmaps = texture.generateMipmaps; + + texture.images[ i ] = dataTexture; + + } + + if ( loaded === 6 ) { + + texture.needsUpdate = true; + if ( onLoad ) onLoad( texture ); + + } + + }, onProgress, onError ); + + } + + for ( var i = 0; i < urls.length; i ++ ) { + + loadHDRData( i, onLoad, onProgress, onError ); + + } + + return texture; + +}; + +HDRCubeTextureLoader.prototype.setPath = function ( value ) { + + this.path = value; + return this; + +}; + +export { HDRCubeTextureLoader }; diff --git a/examples/jsm/loaders/KMZLoader.js b/examples/jsm/loaders/KMZLoader.js new file mode 100644 index 00000000000000..3fdff384111c25 --- /dev/null +++ b/examples/jsm/loaders/KMZLoader.js @@ -0,0 +1,122 @@ +/** + * @author mrdoob / http://mrdoob.com/ + */ + +import { + DefaultLoadingManager, + FileLoader, + Group, + LoadingManager +} from "../../../build/three.module.js"; +import { ColladaLoader } from "./ColladaLoader"; + +var KMZLoader = function ( manager ) { + + this.manager = ( manager !== undefined ) ? manager : DefaultLoadingManager; + +}; + +KMZLoader.prototype = { + + constructor: KMZLoader, + + load: function ( url, onLoad, onProgress, onError ) { + + var scope = this; + + var loader = new FileLoader( scope.manager ); + loader.setPath( scope.path ); + loader.setResponseType( 'arraybuffer' ); + loader.load( url, function ( text ) { + + onLoad( scope.parse( text ) ); + + }, onProgress, onError ); + + }, + + setPath: function ( value ) { + + this.path = value; + return this; + + }, + + parse: function ( data ) { + + function findFile( url ) { + + for ( var path in zip.files ) { + + if ( path.substr( - url.length ) === url ) { + + return zip.files[ path ]; + + } + + } + + } + + var manager = new LoadingManager(); + manager.setURLModifier( function ( url ) { + + var image = findFile( url ); + + if ( image ) { + + console.log( 'Loading', url ); + + var blob = new Blob( [ image.asArrayBuffer() ], { type: 'application/octet-stream' } ); + return URL.createObjectURL( blob ); + + } + + return url; + + } ); + + // + + var zip = new JSZip( data ); // eslint-disable-line no-undef + + if ( zip.files[ 'doc.kml' ] ) { + + var xml = new DOMParser().parseFromString( zip.files[ 'doc.kml' ].asText(), 'application/xml' ); + + var model = xml.querySelector( 'Placemark Model Link href' ); + + if ( model ) { + + var loader = new ColladaLoader( manager ); + return loader.parse( zip.files[ model.textContent ].asText() ); + + } + + } else { + + console.warn( 'KMZLoader: Missing doc.kml file.' ); + + for ( var path in zip.files ) { + + var extension = path.split( '.' ).pop().toLowerCase(); + + if ( extension === 'dae' ) { + + var loader = new ColladaLoader( manager ); + return loader.parse( zip.files[ path ].asText() ); + + } + + } + + } + + console.error( 'KMZLoader: Couldn\'t find .dae file.' ); + return { scene: new Group() }; + + } + +}; + +export { KMZLoader }; diff --git a/examples/jsm/loaders/KTXLoader.js b/examples/jsm/loaders/KTXLoader.js new file mode 100644 index 00000000000000..53a439b21d3aa5 --- /dev/null +++ b/examples/jsm/loaders/KTXLoader.js @@ -0,0 +1,171 @@ +/** + * @author amakaseev / https://github.com/amakaseev + * + * for description see https://www.khronos.org/opengles/sdk/tools/KTX/ + * for file layout see https://www.khronos.org/opengles/sdk/tools/KTX/file_format_spec/ + * + * ported from https://github.com/BabylonJS/Babylon.js/blob/master/src/Tools/babylon.khronosTextureContainer.ts + */ + +import { CompressedTextureLoader } from "../../../build/three.module.js"; + +var KTXLoader = function ( manager ) { + + CompressedTextureLoader.call( this, manager ); + + this._parser = KTXLoader.parse; + +}; + +KTXLoader.prototype = Object.create( CompressedTextureLoader.prototype ); +KTXLoader.prototype.constructor = KTXLoader; + +KTXLoader.parse = function ( buffer, loadMipmaps ) { + + var ktx = new KhronosTextureContainer( buffer, 1 ); + + return { + mipmaps: ktx.mipmaps( loadMipmaps ), + width: ktx.pixelWidth, + height: ktx.pixelHeight, + format: ktx.glInternalFormat, + isCubemap: ktx.numberOfFaces === 6, + mipmapCount: ktx.numberOfMipmapLevels + }; + +}; + +var KhronosTextureContainer = ( function () { + + /** + * @param {ArrayBuffer} arrayBuffer- contents of the KTX container file + * @param {number} facesExpected- should be either 1 or 6, based whether a cube texture or or + * @param {boolean} threeDExpected- provision for indicating that data should be a 3D texture, not implemented + * @param {boolean} textureArrayExpected- provision for indicating that data should be a texture array, not implemented + */ + function KhronosTextureContainer( arrayBuffer, facesExpected, threeDExpected, textureArrayExpected ) { + + this.arrayBuffer = arrayBuffer; + + // Test that it is a ktx formatted file, based on the first 12 bytes, character representation is: + // '´', 'K', 'T', 'X', ' ', '1', '1', 'ª', '\r', '\n', '\x1A', '\n' + // 0xAB, 0x4B, 0x54, 0x58, 0x20, 0x31, 0x31, 0xBB, 0x0D, 0x0A, 0x1A, 0x0A + var identifier = new Uint8Array( this.arrayBuffer, 0, 12 ); + if ( identifier[ 0 ] !== 0xAB || + identifier[ 1 ] !== 0x4B || + identifier[ 2 ] !== 0x54 || + identifier[ 3 ] !== 0x58 || + identifier[ 4 ] !== 0x20 || + identifier[ 5 ] !== 0x31 || + identifier[ 6 ] !== 0x31 || + identifier[ 7 ] !== 0xBB || + identifier[ 8 ] !== 0x0D || + identifier[ 9 ] !== 0x0A || + identifier[ 10 ] !== 0x1A || + identifier[ 11 ] !== 0x0A ) { + + console.error( 'texture missing KTX identifier' ); + return; + + } + + // load the reset of the header in native 32 bit uint + var dataSize = Uint32Array.BYTES_PER_ELEMENT; + var headerDataView = new DataView( this.arrayBuffer, 12, 13 * dataSize ); + var endianness = headerDataView.getUint32( 0, true ); + var littleEndian = endianness === 0x04030201; + + this.glType = headerDataView.getUint32( 1 * dataSize, littleEndian ); // must be 0 for compressed textures + this.glTypeSize = headerDataView.getUint32( 2 * dataSize, littleEndian ); // must be 1 for compressed textures + this.glFormat = headerDataView.getUint32( 3 * dataSize, littleEndian ); // must be 0 for compressed textures + this.glInternalFormat = headerDataView.getUint32( 4 * dataSize, littleEndian ); // the value of arg passed to gl.compressedTexImage2D(,,x,,,,) + this.glBaseInternalFormat = headerDataView.getUint32( 5 * dataSize, littleEndian ); // specify GL_RGB, GL_RGBA, GL_ALPHA, etc (un-compressed only) + this.pixelWidth = headerDataView.getUint32( 6 * dataSize, littleEndian ); // level 0 value of arg passed to gl.compressedTexImage2D(,,,x,,,) + this.pixelHeight = headerDataView.getUint32( 7 * dataSize, littleEndian ); // level 0 value of arg passed to gl.compressedTexImage2D(,,,,x,,) + this.pixelDepth = headerDataView.getUint32( 8 * dataSize, littleEndian ); // level 0 value of arg passed to gl.compressedTexImage3D(,,,,,x,,) + this.numberOfArrayElements = headerDataView.getUint32( 9 * dataSize, littleEndian ); // used for texture arrays + this.numberOfFaces = headerDataView.getUint32( 10 * dataSize, littleEndian ); // used for cubemap textures, should either be 1 or 6 + this.numberOfMipmapLevels = headerDataView.getUint32( 11 * dataSize, littleEndian ); // number of levels; disregard possibility of 0 for compressed textures + this.bytesOfKeyValueData = headerDataView.getUint32( 12 * dataSize, littleEndian ); // the amount of space after the header for meta-data + + // Make sure we have a compressed type. Not only reduces work, but probably better to let dev know they are not compressing. + if ( this.glType !== 0 ) { + + console.warn( 'only compressed formats currently supported' ); + return; + + } else { + + // value of zero is an indication to generate mipmaps @ runtime. Not usually allowed for compressed, so disregard. + this.numberOfMipmapLevels = Math.max( 1, this.numberOfMipmapLevels ); + + } + if ( this.pixelHeight === 0 || this.pixelDepth !== 0 ) { + + console.warn( 'only 2D textures currently supported' ); + return; + + } + if ( this.numberOfArrayElements !== 0 ) { + + console.warn( 'texture arrays not currently supported' ); + return; + + } + if ( this.numberOfFaces !== facesExpected ) { + + console.warn( 'number of faces expected' + facesExpected + ', but found ' + this.numberOfFaces ); + return; + + } + // we now have a completely validated file, so could use existence of loadType as success + // would need to make this more elaborate & adjust checks above to support more than one load type + this.loadType = KhronosTextureContainer.COMPRESSED_2D; + + } + + // return mipmaps for THREE.js + KhronosTextureContainer.prototype.mipmaps = function ( loadMipmaps ) { + + var mipmaps = []; + + // initialize width & height for level 1 + var dataOffset = KhronosTextureContainer.HEADER_LEN + this.bytesOfKeyValueData; + var width = this.pixelWidth; + var height = this.pixelHeight; + var mipmapCount = loadMipmaps ? this.numberOfMipmapLevels : 1; + + for ( var level = 0; level < mipmapCount; level ++ ) { + + var imageSize = new Int32Array( this.arrayBuffer, dataOffset, 1 )[ 0 ]; // size per face, since not supporting array cubemaps + for ( var face = 0; face < this.numberOfFaces; face ++ ) { + + var byteArray = new Uint8Array( this.arrayBuffer, dataOffset + 4, imageSize ); + + mipmaps.push( { "data": byteArray, "width": width, "height": height } ); + + dataOffset += imageSize + 4; // size of the image + 4 for the imageSize field + dataOffset += 3 - ( ( imageSize + 3 ) % 4 ); // add padding for odd sized image + + } + width = Math.max( 1.0, width * 0.5 ); + height = Math.max( 1.0, height * 0.5 ); + + } + + return mipmaps; + + }; + + KhronosTextureContainer.HEADER_LEN = 12 + ( 13 * 4 ); // identifier + header elements (not including key value meta-data pairs) + // load types + KhronosTextureContainer.COMPRESSED_2D = 0; // uses a gl.compressedTexImage2D() + KhronosTextureContainer.COMPRESSED_3D = 1; // uses a gl.compressedTexImage3D() + KhronosTextureContainer.TEX_2D = 2; // uses a gl.texImage2D() + KhronosTextureContainer.TEX_3D = 3; // uses a gl.texImage3D() + + return KhronosTextureContainer; + +}() ); + +export { KTXLoader }; diff --git a/examples/jsm/loaders/LDrawLoader.js b/examples/jsm/loaders/LDrawLoader.js new file mode 100644 index 00000000000000..fa7310c0053a00 --- /dev/null +++ b/examples/jsm/loaders/LDrawLoader.js @@ -0,0 +1,1356 @@ +/** + * @author mrdoob / http://mrdoob.com/ + * @author yomboprime / https://github.com/yomboprime/ + * + * + */ + +import { + BufferGeometry, + Color, + DefaultLoadingManager, + DoubleSide, + FileLoader, + Float32BufferAttribute, + Group, + LineBasicMaterial, + LineSegments, + Matrix4, + Mesh, + MeshLambertMaterial, + MeshPhongMaterial, + MeshStandardMaterial, + Vector3, +} from "../../../build/three.module.js"; + +function LineParser( line, lineNumber ) { + + this.line = line; + this.lineLength = line.length; + this.currentCharIndex = 0; + this.currentChar = ' '; + this.lineNumber = lineNumber; + +} + +LineParser.prototype = { + + constructor: LineParser, + + seekNonSpace: function () { + + while ( this.currentCharIndex < this.lineLength ) { + + this.currentChar = this.line.charAt( this.currentCharIndex ); + + if ( this.currentChar !== ' ' && this.currentChar !== '\t' ) { + + return; + + } + + this.currentCharIndex ++; + + } + + }, + + getToken: function () { + + var pos0 = this.currentCharIndex ++; + + // Seek space + while ( this.currentCharIndex < this.lineLength ) { + + this.currentChar = this.line.charAt( this.currentCharIndex ); + + if ( this.currentChar === ' ' || this.currentChar === '\t' ) { + + break; + + } + + this.currentCharIndex ++; + + } + + var pos1 = this.currentCharIndex; + + this.seekNonSpace(); + + return this.line.substring( pos0, pos1 ); + + }, + + getRemainingString: function () { + + return this.line.substring( this.currentCharIndex, this.lineLength ); + + }, + + isAtTheEnd: function () { + + return this.currentCharIndex >= this.lineLength; + + }, + + setToEnd: function () { + + this.currentCharIndex = this.lineLength; + + }, + + getLineNumberString: function () { + + return this.lineNumber >= 0 ? " at line " + this.lineNumber : ""; + + } + + +}; + +function sortByMaterial( a, b ) { + + if ( a.colourCode === b.colourCode ) { + + return 0; + + } + if ( a.colourCode < b.colourCode ) { + + return - 1; + + } + return 1; + +} + +function createObject( elements, elementSize ) { + + // Creates a THREE.LineSegments (elementSize = 2) or a THREE.Mesh (elementSize = 3 ) + // With per face / segment material, implemented with mesh groups and materials array + + // Sort the triangles or line segments by colour code to make later the mesh groups + elements.sort( sortByMaterial ); + + var vertices = []; + var materials = []; + + var bufferGeometry = new BufferGeometry(); + bufferGeometry.clearGroups(); + var prevMaterial = null; + var index0 = 0; + var numGroupVerts = 0; + + for ( var iElem = 0, nElem = elements.length; iElem < nElem; iElem ++ ) { + + var elem = elements[ iElem ]; + var v0 = elem.v0; + var v1 = elem.v1; + // Note that LDraw coordinate system is rotated 180 deg. in the X axis w.r.t. Three.js's one + vertices.push( v0.x, v0.y, v0.z, v1.x, v1.y, v1.z ); + if ( elementSize === 3 ) { + + vertices.push( elem.v2.x, elem.v2.y, elem.v2.z ); + + } + + if ( prevMaterial !== elem.material ) { + + if ( prevMaterial !== null ) { + + bufferGeometry.addGroup( index0, numGroupVerts, materials.length - 1 ); + + } + + materials.push( elem.material ); + + prevMaterial = elem.material; + index0 = iElem * elementSize; + numGroupVerts = elementSize; + + } else { + + numGroupVerts += elementSize; + + } + + } + if ( numGroupVerts > 0 ) { + + bufferGeometry.addGroup( index0, Infinity, materials.length - 1 ); + + } + + bufferGeometry.addAttribute( 'position', new Float32BufferAttribute( vertices, 3 ) ); + + var object3d = null; + if ( elementSize === 2 ) { + + object3d = new LineSegments( bufferGeometry, materials ); + + } else if ( elementSize === 3 ) { + + bufferGeometry.computeVertexNormals(); + + object3d = new Mesh( bufferGeometry, materials ); + + } + + return object3d; + +} + +// + +function LDrawLoader( manager ) { + + this.manager = ( manager !== undefined ) ? manager : DefaultLoadingManager; + + // This is a stack of 'parse scopes' with one level per subobject loaded file. + // Each level contains a material lib and also other runtime variables passed between parent and child subobjects + // When searching for a material code, the stack is read from top of the stack to bottom + // Each material library is an object map keyed by colour codes. + this.parseScopesStack = null; + + this.path = ''; + + // Array of THREE.Material + this.materials = []; + + // Not using THREE.Cache here because it returns the previous HTML error response instead of calling onError() + // This also allows to handle the embedded text files ("0 FILE" lines) + this.subobjectCache = {}; + + // This object is a map from file names to paths. It agilizes the paths search. If it is not set then files will be searched by trial and error. + this.fileMap = null; + + // Add default main triangle and line edge materials (used in piecess that can be coloured with a main color) + this.setMaterials( [ + this.parseColourMetaDirective( new LineParser( "Main_Colour CODE 16 VALUE #FF8080 EDGE #333333" ) ), + this.parseColourMetaDirective( new LineParser( "Edge_Colour CODE 24 VALUE #A0A0A0 EDGE #333333" ) ) + ] ); + + // If this flag is set to true, each subobject will be a THREE.Object. + // If not (the default), only one object which contains all the merged primitives will be created. + this.separateObjects = false; + + // Current merged object and primitives + this.currentGroupObject = null; + this.currentTriangles = null; + this.currentLineSegments = null; + +} + +// Special surface finish tag types. +// Note: "MATERIAL" tag (e.g. GLITTER, SPECKLE) is not implemented +LDrawLoader.FINISH_TYPE_DEFAULT = 0; +LDrawLoader.FINISH_TYPE_CHROME = 1; +LDrawLoader.FINISH_TYPE_PEARLESCENT = 2; +LDrawLoader.FINISH_TYPE_RUBBER = 3; +LDrawLoader.FINISH_TYPE_MATTE_METALLIC = 4; +LDrawLoader.FINISH_TYPE_METAL = 5; + +// State machine to search a subobject path. +// The LDraw standard establishes these various possible subfolders. +LDrawLoader.FILE_LOCATION_AS_IS = 0; +LDrawLoader.FILE_LOCATION_TRY_PARTS = 1; +LDrawLoader.FILE_LOCATION_TRY_P = 2; +LDrawLoader.FILE_LOCATION_TRY_MODELS = 3; +LDrawLoader.FILE_LOCATION_TRY_RELATIVE = 4; +LDrawLoader.FILE_LOCATION_TRY_ABSOLUTE = 5; +LDrawLoader.FILE_LOCATION_NOT_FOUND = 6; + +LDrawLoader.prototype = { + + constructor: LDrawLoader, + + load: function ( url, onLoad, onProgress, onError ) { + + if ( ! this.fileMap ) { + + this.fileMap = {}; + + } + + var scope = this; + + var fileLoader = new FileLoader( this.manager ); + fileLoader.setPath( this.path ); + fileLoader.load( url, function ( text ) { + + processObject( text, onLoad ); + + }, onProgress, onError ); + + function processObject( text, onProcessed ) { + + var parseScope = scope.newParseScopeLevel(); + parseScope.url = url; + + var parentParseScope = scope.getParentParseScope(); + + // Add to cache + var currentFileName = parentParseScope.currentFileName; + if ( scope.subobjectCache[ currentFileName ] === undefined ) { + + scope.subobjectCache[ currentFileName ] = text; + + + } + + // Parse the object (returns a Group) + var objGroup = scope.parse( text ); + + // Load subobjects + parseScope.subobjects = objGroup.userData.subobjects; + parseScope.numSubobjects = parseScope.subobjects.length; + parseScope.subobjectIndex = 0; + + if ( parseScope.numSubobjects > 0 ) { + + // Load the first subobject + var subobjectGroup = loadSubobject( parseScope.subobjects[ 0 ], true ); + + // Optimization for loading pack: If subobjects are obtained from cache, keep loading them iteratively rather than recursively + if ( subobjectGroup ) { + + while ( subobjectGroup && parseScope.subobjectIndex < parseScope.numSubobjects - 1 ) { + + subobjectGroup = loadSubobject( parseScope.subobjects[ ++ parseScope.subobjectIndex ], true ); + + } + + if ( subobjectGroup ) { + + finalizeObject(); + + } + + } + + } else { + + // No subobjects, finish object + finalizeObject(); + + } + + return objGroup; + + function finalizeObject() { + + if ( ! scope.separateObjects && ! parentParseScope.isFromParse ) { + + // We are finalizing the root object and merging primitives is activated, so create the entire Mesh and LineSegments objects now + if ( scope.currentLineSegments.length > 0 ) { + + objGroup.add( createObject( scope.currentLineSegments, 2 ) ); + + } + + if ( scope.currentTriangles.length > 0 ) { + + objGroup.add( createObject( scope.currentTriangles, 3 ) ); + + } + + } + + scope.removeScopeLevel(); + + if ( onProcessed ) { + + onProcessed( objGroup ); + + } + + } + + function loadSubobject( subobject, sync ) { + + parseScope.mainColourCode = subobject.material.userData.code; + parseScope.mainEdgeColourCode = subobject.material.userData.edgeMaterial.userData.code; + parseScope.currentFileName = subobject.originalFileName; + + if ( ! scope.separateObjects ) { + + // Set current matrix + parseScope.currentMatrix.multiplyMatrices( parentParseScope.currentMatrix, subobject.matrix ); + + } + + // If subobject was cached previously, use the cached one + var cached = scope.subobjectCache[ subobject.originalFileName ]; + if ( cached ) { + + var subobjectGroup = processObject( cached, sync ? undefined : onSubobjectLoaded ); + if ( sync ) { + + addSubobject( subobject, subobjectGroup ); + return subobjectGroup; + + } + + return; + + } + + // Adjust file name to locate the subobject file path in standard locations (always under directory scope.path) + // Update also subobject.locationState for the next try if this load fails. + var subobjectURL = subobject.fileName; + var newLocationState = LDrawLoader.FILE_LOCATION_NOT_FOUND; + + switch ( subobject.locationState ) { + + case LDrawLoader.FILE_LOCATION_AS_IS: + newLocationState = subobject.locationState + 1; + break; + + case LDrawLoader.FILE_LOCATION_TRY_PARTS: + subobjectURL = 'parts/' + subobjectURL; + newLocationState = subobject.locationState + 1; + break; + + case LDrawLoader.FILE_LOCATION_TRY_P: + subobjectURL = 'p/' + subobjectURL; + newLocationState = subobject.locationState + 1; + break; + + case LDrawLoader.FILE_LOCATION_TRY_MODELS: + subobjectURL = 'models/' + subobjectURL; + newLocationState = subobject.locationState + 1; + break; + + case LDrawLoader.FILE_LOCATION_TRY_RELATIVE: + subobjectURL = url.substring( 0, url.lastIndexOf( "/" ) + 1 ) + subobjectURL; + newLocationState = subobject.locationState + 1; + break; + + case LDrawLoader.FILE_LOCATION_TRY_ABSOLUTE: + + if ( subobject.triedLowerCase ) { + + // Try absolute path + newLocationState = LDrawLoader.FILE_LOCATION_NOT_FOUND; + + } else { + + // Next attempt is lower case + subobject.fileName = subobject.fileName.toLowerCase(); + subobjectURL = subobject.fileName; + subobject.triedLowerCase = true; + newLocationState = LDrawLoader.FILE_LOCATION_AS_IS; + + } + break; + + case LDrawLoader.FILE_LOCATION_NOT_FOUND: + + // All location possibilities have been tried, give up loading this object + console.warn( 'LDrawLoader: Subobject "' + subobject.originalFileName + '" could not be found.' ); + + // Try to read the next subobject + parseScope.subobjectIndex ++; + + if ( parseScope.subobjectIndex >= parseScope.numSubobjects ) { + + // All subojects have been loaded. Finish parent object + scope.removeScopeLevel(); + onProcessed( objGroup ); + + } else { + + // Load next subobject + loadSubobject( parseScope.subobjects[ parseScope.subobjectIndex ] ); + + } + + return; + + } + + subobject.locationState = newLocationState; + subobject.url = subobjectURL; + + // Load the subobject + scope.load( subobjectURL, onSubobjectLoaded, undefined, onSubobjectError ); + + } + + function onSubobjectLoaded( subobjectGroup ) { + + var subobject = parseScope.subobjects[ parseScope.subobjectIndex ]; + + if ( subobjectGroup === null ) { + + // Try to reload + loadSubobject( subobject ); + return; + + } + + // Add the subobject just loaded + addSubobject( subobject, subobjectGroup ); + + // Proceed to load the next subobject, or finish the parent object + + parseScope.subobjectIndex ++; + + if ( parseScope.subobjectIndex < parseScope.numSubobjects ) { + + loadSubobject( parseScope.subobjects[ parseScope.subobjectIndex ] ); + + } else { + + finalizeObject(); + + } + + } + + function addSubobject( subobject, subobjectGroup ) { + + if ( scope.separateObjects ) { + + subobjectGroup.name = subobject.fileName; + objGroup.add( subobjectGroup ); + subobjectGroup.matrix.copy( subobject.matrix ); + subobjectGroup.matrixAutoUpdate = false; + + } + + scope.fileMap[ subobject.originalFileName ] = subobject.url; + + } + + function onSubobjectError( err ) { + + // Retry download from a different default possible location + loadSubobject( parseScope.subobjects[ parseScope.subobjectIndex ] ); + + } + + } + + }, + + setPath: function ( value ) { + + this.path = value; + + return this; + + }, + + setMaterials: function ( materials ) { + + // Clears parse scopes stack, adds new scope with material library + + this.parseScopesStack = []; + + this.newParseScopeLevel( materials ); + + this.getCurrentParseScope().isFromParse = false; + + this.materials = materials; + + this.currentGroupObject = null; + + return this; + + }, + + setFileMap: function ( fileMap ) { + + this.fileMap = fileMap; + + return this; + + }, + + newParseScopeLevel: function ( materials ) { + + // Adds a new scope level, assign materials to it and returns it + + var matLib = {}; + + if ( materials ) { + + for ( var i = 0, n = materials.length; i < n; i ++ ) { + + var material = materials[ i ]; + matLib[ material.userData.code ] = material; + + } + + } + + var topParseScope = this.getCurrentParseScope(); + + var parentParseScope = this.getParentParseScope(); + + var newParseScope = { + + lib: matLib, + url: null, + + // Subobjects + subobjects: null, + numSubobjects: 0, + subobjectIndex: 0, + + // Current subobject + currentFileName: null, + mainColourCode: topParseScope ? topParseScope.mainColourCode : '16', + mainEdgeColourCode: topParseScope ? topParseScope.mainEdgeColourCode : '24', + currentMatrix: new Matrix4(), + + // If false, it is a root material scope previous to parse + isFromParse: true + }; + + this.parseScopesStack.push( newParseScope ); + + return newParseScope; + + }, + + removeScopeLevel: function () { + + this.parseScopesStack.pop(); + + return this; + + }, + + addMaterial: function ( material ) { + + // Adds a material to the material library which is on top of the parse scopes stack. And also to the materials array + + var matLib = this.getCurrentParseScope().lib; + + if ( ! matLib[ material.userData.code ] ) { + + this.materials.push( material ); + + } + + matLib[ material.userData.code ] = material; + + return this; + + }, + + getMaterial: function ( colourCode ) { + + // Given a colour code search its material in the parse scopes stack + + if ( colourCode.startsWith( "0x2" ) ) { + + // Special 'direct' material value (RGB colour) + + var colour = colourCode.substring( 3 ); + + return this.parseColourMetaDirective( new LineParser( "Direct_Color_" + colour + " CODE -1 VALUE #" + colour + " EDGE #" + colour + "" ) ); + + } + + for ( var i = this.parseScopesStack.length - 1; i >= 0; i -- ) { + + var material = this.parseScopesStack[ i ].lib[ colourCode ]; + + if ( material ) { + + return material; + + } + + } + + // Material was not found + return null; + + }, + + getParentParseScope: function () { + + if ( this.parseScopesStack.length > 1 ) { + + return this.parseScopesStack[ this.parseScopesStack.length - 2 ]; + + } + + return null; + + }, + + getCurrentParseScope: function () { + + if ( this.parseScopesStack.length > 0 ) { + + return this.parseScopesStack[ this.parseScopesStack.length - 1 ]; + + } + + return null; + + }, + + parseColourMetaDirective: function ( lineParser ) { + + // Parses a colour definition and returns a THREE.Material or null if error + + var code = null; + + // Triangle and line colours + var colour = 0xFF00FF; + var edgeColour = 0xFF00FF; + + // Transparency + var alpha = 1; + var isTransparent = false; + // Self-illumination: + var luminance = 0; + + var finishType = LDrawLoader.FINISH_TYPE_DEFAULT; + var canHaveEnvMap = true; + + var edgeMaterial = null; + + var name = lineParser.getToken(); + if ( ! name ) { + + throw 'LDrawLoader: Material name was expected after "!COLOUR tag' + lineParser.getLineNumberString() + "."; + + } + + // Parse tag tokens and their parameters + var token = null; + while ( true ) { + + token = lineParser.getToken(); + + if ( ! token ) { + + break; + + } + + switch ( token.toUpperCase() ) { + + case "CODE": + + code = lineParser.getToken(); + break; + + case "VALUE": + + colour = lineParser.getToken(); + if ( colour.startsWith( '0x' ) ) { + + colour = '#' + colour.substring( 2 ); + + } else if ( ! colour.startsWith( '#' ) ) { + + throw 'LDrawLoader: Invalid colour while parsing material' + lineParser.getLineNumberString() + "."; + + } + break; + + case "EDGE": + + edgeColour = lineParser.getToken(); + if ( edgeColour.startsWith( '0x' ) ) { + + edgeColour = '#' + edgeColour.substring( 2 ); + + } else if ( ! edgeColour.startsWith( '#' ) ) { + + // Try to see if edge colour is a colour code + edgeMaterial = this.getMaterial( edgeColour ); + if ( ! edgeMaterial ) { + + throw 'LDrawLoader: Invalid edge colour while parsing material' + lineParser.getLineNumberString() + "."; + + } + + // Get the edge material for this triangle material + edgeMaterial = edgeMaterial.userData.edgeMaterial; + + } + break; + + case 'ALPHA': + + alpha = parseInt( lineParser.getToken() ); + + if ( isNaN( alpha ) ) { + + throw 'LDrawLoader: Invalid alpha value in material definition' + lineParser.getLineNumberString() + "."; + + } + + alpha = Math.max( 0, Math.min( 1, alpha / 255 ) ); + + if ( alpha < 1 ) { + + isTransparent = true; + + } + + break; + + case 'LUMINANCE': + + luminance = parseInt( lineParser.getToken() ); + + if ( isNaN( luminance ) ) { + + throw 'LDrawLoader: Invalid luminance value in material definition' + LineParser.getLineNumberString() + "."; + + } + + luminance = Math.max( 0, Math.min( 1, luminance / 255 ) ); + + break; + + case 'CHROME': + finishType = LDrawLoader.FINISH_TYPE_CHROME; + break; + + case 'PEARLESCENT': + finishType = LDrawLoader.FINISH_TYPE_PEARLESCENT; + break; + + case 'RUBBER': + finishType = LDrawLoader.FINISH_TYPE_RUBBER; + break; + + case 'MATTE_METALLIC': + finishType = LDrawLoader.FINISH_TYPE_MATTE_METALLIC; + break; + + case 'METAL': + finishType = LDrawLoader.FINISH_TYPE_METAL; + break; + + case 'MATERIAL': + // Not implemented + lineParser.setToEnd(); + break; + + default: + throw 'LDrawLoader: Unknown token "' + token + '" while parsing material' + lineParser.getLineNumberString() + "."; + break; + + } + + } + + var material = null; + + switch ( finishType ) { + + case LDrawLoader.FINISH_TYPE_DEFAULT: + case LDrawLoader.FINISH_TYPE_PEARLESCENT: + + var specular = new Color( colour ); + var shininess = 35; + var hsl = specular.getHSL( { h: 0, s: 0, l: 0 } ); + + if ( finishType === LDrawLoader.FINISH_TYPE_DEFAULT ) { + + // Default plastic material with shiny specular + hsl.l = Math.min( 1, hsl.l + ( 1 - hsl.l ) * 0.12 ); + + } else { + + // Try to imitate pearlescency by setting the specular to the complementary of the color, and low shininess + hsl.h = ( hsl.h + 0.5 ) % 1; + hsl.l = Math.min( 1, hsl.l + ( 1 - hsl.l ) * 0.7 ); + shininess = 10; + + } + + specular.setHSL( hsl.h, hsl.s, hsl.l ); + + material = new MeshPhongMaterial( { color: colour, specular: specular, shininess: shininess, reflectivity: 0.3 } ); + break; + + case LDrawLoader.FINISH_TYPE_CHROME: + + // Mirror finish surface + material = new MeshStandardMaterial( { color: colour, roughness: 0, metalness: 1 } ); + break; + + case LDrawLoader.FINISH_TYPE_RUBBER: + + // Rubber is best simulated with Lambert + material = new MeshLambertMaterial( { color: colour } ); + canHaveEnvMap = false; + break; + + case LDrawLoader.FINISH_TYPE_MATTE_METALLIC: + + // Brushed metal finish + material = new MeshStandardMaterial( { color: colour, roughness: 0.8, metalness: 0.4 } ); + break; + + case LDrawLoader.FINISH_TYPE_METAL: + + // Average metal finish + material = new MeshStandardMaterial( { color: colour, roughness: 0.2, metalness: 0.85 } ); + break; + + default: + // Should not happen + break; + + } + + // BFC (Back Face Culling) LDraw language meta extension is not implemented, so set all materials double-sided: + material.side = DoubleSide; + + material.transparent = isTransparent; + material.opacity = alpha; + + material.userData.canHaveEnvMap = canHaveEnvMap; + + if ( luminance !== 0 ) { + + material.emissive.set( material.color ).multiplyScalar( luminance ); + + } + + if ( ! edgeMaterial ) { + + // This is the material used for edges + edgeMaterial = new LineBasicMaterial( { color: edgeColour } ); + edgeMaterial.userData.code = code; + edgeMaterial.name = name + " - Edge"; + edgeMaterial.userData.canHaveEnvMap = false; + + } + + material.userData.code = code; + material.name = name; + + material.userData.edgeMaterial = edgeMaterial; + + return material; + + }, + + // + + parse: function ( text ) { + + //console.time( 'LDrawLoader' ); + + // Retrieve data from the parent parse scope + var parentParseScope = this.getParentParseScope(); + + // Main colour codes passed to this subobject (or default codes 16 and 24 if it is the root object) + var mainColourCode = parentParseScope.mainColourCode; + var mainEdgeColourCode = parentParseScope.mainEdgeColourCode; + + var url = parentParseScope.url; + + var currentParseScope = this.getCurrentParseScope(); + + // Parse result variables + var triangles; + var lineSegments; + + if ( this.separateObjects ) { + + triangles = []; + lineSegments = []; + + } else { + + if ( this.currentGroupObject === null ) { + + this.currentGroupObject = new Group(); + this.currentTriangles = []; + this.currentLineSegments = []; + + } + + triangles = this.currentTriangles; + lineSegments = this.currentLineSegments; + + } + + var subobjects = []; + + var category = null; + var keywords = null; + + if ( text.indexOf( '\r\n' ) !== - 1 ) { + + // This is faster than String.split with regex that splits on both + text = text.replace( /\r\n/g, '\n' ); + + } + + var lines = text.split( '\n' ); + var numLines = lines.length; + var lineIndex = 0; + + var parsingEmbeddedFiles = false; + var currentEmbeddedFileName = null; + var currentEmbeddedText = null; + + var scope = this; + function parseColourCode( lineParser, forEdge ) { + + // Parses next colour code and returns a THREE.Material + + var colourCode = lineParser.getToken(); + + if ( ! forEdge && colourCode === '16' ) { + + colourCode = mainColourCode; + + } + if ( forEdge && colourCode === '24' ) { + + colourCode = mainEdgeColourCode; + + } + + var material = scope.getMaterial( colourCode ); + + if ( ! material ) { + + throw 'LDrawLoader: Unknown colour code "' + colourCode + '" is used' + lineParser.getLineNumberString() + ' but it was not defined previously.'; + + } + + return material; + + } + + function parseVector( lp ) { + + var v = new Vector3( parseFloat( lp.getToken() ), parseFloat( lp.getToken() ), parseFloat( lp.getToken() ) ); + + if ( ! scope.separateObjects ) { + + v.applyMatrix4( parentParseScope.currentMatrix ); + + } + + return v; + + } + + function findSubobject( fileName ) { + + for ( var i = 0, n = subobjects.length; i < n; i ++ ) { + + if ( subobjects[ i ].fileName === fileName ) { + + return subobjects[ i ]; + + } + + return null; + + } + + } + + // Parse all line commands + for ( lineIndex = 0; lineIndex < numLines; lineIndex ++ ) { + + line = lines[ lineIndex ]; + + if ( line.length === 0 ) continue; + + if ( parsingEmbeddedFiles ) { + + if ( line.startsWith( '0 FILE ' ) ) { + + // Save previous embedded file in the cache + this.subobjectCache[ currentEmbeddedFileName ] = currentEmbeddedText; + + // New embedded text file + currentEmbeddedFileName = line.substring( 7 ); + currentEmbeddedText = ''; + + } else { + + currentEmbeddedText += line + '\n'; + + } + + continue; + + } + + var lp = new LineParser( line, lineIndex + 1 ); + + lp.seekNonSpace(); + + if ( lp.isAtTheEnd() ) { + + // Empty line + continue; + + } + + // Parse the line type + var lineType = lp.getToken(); + + switch ( lineType ) { + + // Line type 0: Comment or META + case '0': + + // Parse meta directive + var meta = lp.getToken(); + + if ( meta ) { + + switch ( meta ) { + + case '!COLOUR': + + var material = this.parseColourMetaDirective( lp ); + if ( material ) { + + this.addMaterial( material ); + + } else { + + console.warn( 'LDrawLoader: Error parsing material' + lineParser.getLineNumberString() ); + + } + break; + + case '!CATEGORY': + + category = lp.getToken(); + break; + + case '!KEYWORDS': + + var newKeywords = lp.getRemainingString().split( ',' ); + if ( newKeywords.length > 0 ) { + + if ( ! keywords ) { + + keywords = []; + + } + + newKeywords.forEach( function ( keyword ) { + + keywords.push( keyword.trim() ); + + } ); + + } + break; + + case 'FILE': + + if ( lineIndex > 0 ) { + + // Start embedded text files parsing + parsingEmbeddedFiles = true; + currentEmbeddedFileName = lp.getRemainingString(); + currentEmbeddedText = ''; + + } + + break; + + default: + // Other meta directives are not implemented + break; + + } + + } + + break; + + // Line type 1: Sub-object file + case '1': + + var material = parseColourCode( lp ); + + var posX = parseFloat( lp.getToken() ); + var posY = parseFloat( lp.getToken() ); + var posZ = parseFloat( lp.getToken() ); + var m0 = parseFloat( lp.getToken() ); + var m1 = parseFloat( lp.getToken() ); + var m2 = parseFloat( lp.getToken() ); + var m3 = parseFloat( lp.getToken() ); + var m4 = parseFloat( lp.getToken() ); + var m5 = parseFloat( lp.getToken() ); + var m6 = parseFloat( lp.getToken() ); + var m7 = parseFloat( lp.getToken() ); + var m8 = parseFloat( lp.getToken() ); + + var matrix = new Matrix4().set( + m0, m1, m2, posX, + m3, m4, m5, posY, + m6, m7, m8, posZ, + 0, 0, 0, 1 + ); + + var fileName = lp.getRemainingString().trim().replace( "\\", "/" ); + + if ( scope.fileMap[ fileName ] ) { + + // Found the subobject path in the preloaded file path map + fileName = scope.fileMap[ fileName ]; + + } else { + + // Standardized subfolders + if ( fileName.startsWith( 's/' ) ) { + + fileName = 'parts/' + fileName; + + } else if ( fileName.startsWith( '48/' ) ) { + + fileName = 'p/' + fileName; + + } + + } + + subobjects.push( { + material: material, + matrix: matrix, + fileName: fileName, + originalFileName: fileName, + locationState: LDrawLoader.FILE_LOCATION_AS_IS, + url: null, + triedLowerCase: false + } ); + + break; + + // Line type 2: Line segment + case '2': + + var material = parseColourCode( lp, true ); + + lineSegments.push( { + material: material.userData.edgeMaterial, + colourCode: material.userData.code, + v0: parseVector( lp ), + v1: parseVector( lp ) + } ); + + break; + + // Line type 3: Triangle + case '3': + + var material = parseColourCode( lp ); + + triangles.push( { + material: material, + colourCode: material.userData.code, + v0: parseVector( lp ), + v1: parseVector( lp ), + v2: parseVector( lp ) + } ); + + break; + + // Line type 4: Quadrilateral + case '4': + + var material = parseColourCode( lp ); + + var v0 = parseVector( lp ); + var v1 = parseVector( lp ); + var v2 = parseVector( lp ); + var v3 = parseVector( lp ); + + triangles.push( { + material: material, + colourCode: material.userData.code, + v0: v0, + v1: v1, + v2: v2 + } ); + + triangles.push( { + material: material, + colourCode: material.userData.code, + v0: v0, + v1: v2, + v2: v3 + } ); + + break; + + // Line type 5: Optional line + case '5': + // Line type 5 is not implemented + break; + + default: + throw 'LDrawLoader: Unknown line type "' + lineType + '"' + lp.getLineNumberString() + '.'; + break; + + } + + } + + if ( parsingEmbeddedFiles ) { + + this.subobjectCache[ currentEmbeddedFileName ] = currentEmbeddedText; + + } + + // + + var groupObject = null; + + if ( this.separateObjects ) { + + groupObject = new Group(); + + if ( lineSegments.length > 0 ) { + + groupObject.add( createObject( lineSegments, 2 ) ); + + + } + + if ( triangles.length > 0 ) { + + groupObject.add( createObject( triangles, 3 ) ); + + } + + } else { + + groupObject = this.currentGroupObject; + + } + + groupObject.userData.category = category; + groupObject.userData.keywords = keywords; + groupObject.userData.subobjects = subobjects; + + //console.timeEnd( 'LDrawLoader' ); + + return groupObject; + + } + +}; + +export { LDrawLoader }; diff --git a/examples/jsm/loaders/LoaderSupport.js b/examples/jsm/loaders/LoaderSupport.js new file mode 100644 index 00000000000000..f27c9f2bf9e3fe --- /dev/null +++ b/examples/jsm/loaders/LoaderSupport.js @@ -0,0 +1,1909 @@ +/** + * @author Kai Salmen / https://kaisalmen.de + * Development repository: https://github.com/kaisalmen/WWOBJLoader + */ + +import { + BufferAttribute, + BufferGeometry, + DefaultLoadingManager, + FileLoader, + LineBasicMaterial, + LineSegments, + MaterialLoader, + Mesh, + MeshStandardMaterial, + Points, + PointsMaterial, + VertexColors, +} from "../../../build/three.module.js"; + +var LoaderSupport = {}; + +/** + * Validation functions. + * @class + */ +LoaderSupport.Validator = { + /** + * If given input is null or undefined, false is returned otherwise true. + * + * @param input Can be anything + * @returns {boolean} + */ + isValid: function ( input ) { + + return ( input !== null && input !== undefined ); + + }, + /** + * If given input is null or undefined, the defaultValue is returned otherwise the given input. + * + * @param input Can be anything + * @param defaultValue Can be anything + * @returns {*} + */ + verifyInput: function ( input, defaultValue ) { + + return ( input === null || input === undefined ) ? defaultValue : input; + + } +}; + + +/** + * Callbacks utilized by loaders and builders. + * @class + */ +LoaderSupport.Callbacks = function () { + + this.onProgress = null; + this.onReportError = null; + this.onMeshAlter = null; + this.onLoad = null; + this.onLoadMaterials = null; + +}; + +LoaderSupport.Callbacks.prototype = { + + constructor: LoaderSupport.Callbacks, + + /** + * Register callback function that is invoked by internal function "announceProgress" to print feedback. + * + * @param {callback} callbackOnProgress Callback function for described functionality + */ + setCallbackOnProgress: function ( callbackOnProgress ) { + + this.onProgress = LoaderSupport.Validator.verifyInput( callbackOnProgress, this.onProgress ); + + }, + + /** + * Register callback function that is invoked when an error is reported. + * + * @param {callback} callbackOnReportError Callback function for described functionality + */ + setCallbackOnReportError: function ( callbackOnReportError ) { + + this.onReportError = LoaderSupport.Validator.verifyInput( callbackOnReportError, this.onReportError ); + + }, + + /** + * Register callback function that is called every time a mesh was loaded. + * Use {@link LoaderSupport.LoadedMeshUserOverride} for alteration instructions (geometry, material or disregard mesh). + * + * @param {callback} callbackOnMeshAlter Callback function for described functionality + */ + setCallbackOnMeshAlter: function ( callbackOnMeshAlter ) { + + this.onMeshAlter = LoaderSupport.Validator.verifyInput( callbackOnMeshAlter, this.onMeshAlter ); + + }, + + /** + * Register callback function that is called once loading of the complete OBJ file is completed. + * + * @param {callback} callbackOnLoad Callback function for described functionality + */ + setCallbackOnLoad: function ( callbackOnLoad ) { + + this.onLoad = LoaderSupport.Validator.verifyInput( callbackOnLoad, this.onLoad ); + + }, + + /** + * Register callback function that is called when materials have been loaded. + * + * @param {callback} callbackOnLoadMaterials Callback function for described functionality + */ + setCallbackOnLoadMaterials: function ( callbackOnLoadMaterials ) { + + this.onLoadMaterials = LoaderSupport.Validator.verifyInput( callbackOnLoadMaterials, this.onLoadMaterials ); + + } + +}; + + +/** + * Object to return by callback onMeshAlter. Used to disregard a certain mesh or to return one to many meshes. + * @class + * + * @param {boolean} disregardMesh=false Tell implementation to completely disregard this mesh + * @param {boolean} disregardMesh=false Tell implementation that mesh(es) have been altered or added + */ +LoaderSupport.LoadedMeshUserOverride = function ( disregardMesh, alteredMesh ) { + + this.disregardMesh = disregardMesh === true; + this.alteredMesh = alteredMesh === true; + this.meshes = []; + +}; + +LoaderSupport.LoadedMeshUserOverride.prototype = { + + constructor: LoaderSupport.LoadedMeshUserOverride, + + /** + * Add a mesh created within callback. + * + * @param {THREE.Mesh} mesh + */ + addMesh: function ( mesh ) { + + this.meshes.push( mesh ); + this.alteredMesh = true; + + }, + + /** + * Answers if mesh shall be disregarded completely. + * + * @returns {boolean} + */ + isDisregardMesh: function () { + + return this.disregardMesh; + + }, + + /** + * Answers if new mesh(es) were created. + * + * @returns {boolean} + */ + providesAlteredMeshes: function () { + + return this.alteredMesh; + + } + +}; + + +/** + * A resource description used by {@link LoaderSupport.PrepData} and others. + * @class + * + * @param {string} url URL to the file + * @param {string} extension The file extension (type) + */ +LoaderSupport.ResourceDescriptor = function ( url, extension ) { + + var urlParts = url.split( '/' ); + + this.path; + this.resourcePath; + this.name = url; + this.url = url; + if ( urlParts.length >= 2 ) { + + this.path = LoaderSupport.Validator.verifyInput( urlParts.slice( 0, urlParts.length - 1 ).join( '/' ) + '/', this.path ); + this.name = urlParts[ urlParts.length - 1 ]; + this.url = url; + + } + this.name = LoaderSupport.Validator.verifyInput( this.name, 'Unnamed_Resource' ); + this.extension = LoaderSupport.Validator.verifyInput( extension, 'default' ); + this.extension = this.extension.trim(); + this.content = null; + +}; + +LoaderSupport.ResourceDescriptor.prototype = { + + constructor: LoaderSupport.ResourceDescriptor, + + /** + * Set the content of this resource + * + * @param {Object} content The file content as arraybuffer or text + */ + setContent: function ( content ) { + + this.content = LoaderSupport.Validator.verifyInput( content, null ); + + }, + + /** + * Allows to specify resourcePath for dependencies of specified resource. + * @param {string} resourcePath + */ + setResourcePath: function ( resourcePath ) { + + this.resourcePath = LoaderSupport.Validator.verifyInput( resourcePath, this.resourcePath ); + + } +}; + + +/** + * Configuration instructions to be used by run method. + * @class + */ +LoaderSupport.PrepData = function ( modelName ) { + + this.logging = { + enabled: true, + debug: false + }; + this.modelName = LoaderSupport.Validator.verifyInput( modelName, '' ); + this.resources = []; + this.callbacks = new LoaderSupport.Callbacks(); + +}; + +LoaderSupport.PrepData.prototype = { + + constructor: LoaderSupport.PrepData, + + /** + * Enable or disable logging in general (except warn and error), plus enable or disable debug logging. + * + * @param {boolean} enabled True or false. + * @param {boolean} debug True or false. + */ + setLogging: function ( enabled, debug ) { + + this.logging.enabled = enabled === true; + this.logging.debug = debug === true; + + }, + + /** + * Returns all callbacks as {@link LoaderSupport.Callbacks} + * + * @returns {LoaderSupport.Callbacks} + */ + getCallbacks: function () { + + return this.callbacks; + + }, + + /** + * Add a resource description. + * + * @param {LoaderSupport.ResourceDescriptor} Adds a {@link LoaderSupport.ResourceDescriptor} + */ + addResource: function ( resource ) { + + this.resources.push( resource ); + + }, + + /** + * Clones this object and returns it afterwards. Callbacks and resources are not cloned deep (references!). + * + * @returns {@link LoaderSupport.PrepData} + */ + clone: function () { + + var clone = new LoaderSupport.PrepData( this.modelName ); + clone.logging.enabled = this.logging.enabled; + clone.logging.debug = this.logging.debug; + clone.resources = this.resources; + clone.callbacks = this.callbacks; + + var property, value; + for ( property in this ) { + + value = this[ property ]; + if ( ! clone.hasOwnProperty( property ) && typeof this[ property ] !== 'function' ) { + + clone[ property ] = value; + + } + + } + + return clone; + + }, + + /** + * Identify files or content of interest from an Array of {@link LoaderSupport.ResourceDescriptor}. + * + * @param {LoaderSupport.ResourceDescriptor[]} resources Array of {@link LoaderSupport.ResourceDescriptor} + * @param Object fileDesc Object describing which resources are of interest (ext, type (string or UInt8Array) and ignore (boolean)) + * @returns {{}} Object with each "ext" and the corresponding {@link LoaderSupport.ResourceDescriptor} + */ + checkResourceDescriptorFiles: function ( resources, fileDesc ) { + + var resource, triple, i, found; + var result = {}; + + for ( var index in resources ) { + + resource = resources[ index ]; + found = false; + if ( ! LoaderSupport.Validator.isValid( resource.name ) ) continue; + if ( LoaderSupport.Validator.isValid( resource.content ) ) { + + for ( i = 0; i < fileDesc.length && ! found; i ++ ) { + + triple = fileDesc[ i ]; + if ( resource.extension.toLowerCase() === triple.ext.toLowerCase() ) { + + if ( triple.ignore ) { + + found = true; + + } else if ( triple.type === "ArrayBuffer" ) { + + // fast-fail on bad type + if ( ! ( resource.content instanceof ArrayBuffer || resource.content instanceof Uint8Array ) ) throw 'Provided content is not of type ArrayBuffer! Aborting...'; + result[ triple.ext ] = resource; + found = true; + + } else if ( triple.type === "String" ) { + + if ( ! ( typeof ( resource.content ) === 'string' || resource.content instanceof String ) ) throw 'Provided content is not of type String! Aborting...'; + result[ triple.ext ] = resource; + found = true; + + } + + } + + } + if ( ! found ) throw 'Unidentified resource "' + resource.name + '": ' + resource.url; + + } else { + + // fast-fail on bad type + if ( ! ( typeof ( resource.name ) === 'string' || resource.name instanceof String ) ) throw 'Provided file is not properly defined! Aborting...'; + for ( i = 0; i < fileDesc.length && ! found; i ++ ) { + + triple = fileDesc[ i ]; + if ( resource.extension.toLowerCase() === triple.ext.toLowerCase() ) { + + if ( ! triple.ignore ) result[ triple.ext ] = resource; + found = true; + + } + + } + if ( ! found ) throw 'Unidentified resource "' + resource.name + '": ' + resource.url; + + } + + } + + return result; + + } +}; + +/** + * Builds one or many THREE.Mesh from one raw set of Arraybuffers, materialGroup descriptions and further parameters. + * Supports vertex, vertexColor, normal, uv and index buffers. + * @class + */ +LoaderSupport.MeshBuilder = function () { + + console.info( 'Using LoaderSupport.MeshBuilder version: ' + LoaderSupport.MeshBuilder.LOADER_MESH_BUILDER_VERSION ); + this.validator = LoaderSupport.Validator; + + this.logging = { + enabled: true, + debug: false + }; + + this.callbacks = new LoaderSupport.Callbacks(); + this.materials = []; + +}; +LoaderSupport.MeshBuilder.LOADER_MESH_BUILDER_VERSION = '1.3.0'; + +LoaderSupport.MeshBuilder.prototype = { + + constructor: LoaderSupport.MeshBuilder, + + /** + * Enable or disable logging in general (except warn and error), plus enable or disable debug logging. + * + * @param {boolean} enabled True or false. + * @param {boolean} debug True or false. + */ + setLogging: function ( enabled, debug ) { + + this.logging.enabled = enabled === true; + this.logging.debug = debug === true; + + }, + + /** + * Initializes the MeshBuilder (currently only default material initialisation). + * + */ + init: function () { + + var defaultMaterial = new MeshStandardMaterial( { color: 0xDCF1FF } ); + defaultMaterial.name = 'defaultMaterial'; + + var defaultVertexColorMaterial = new MeshStandardMaterial( { color: 0xDCF1FF } ); + defaultVertexColorMaterial.name = 'defaultVertexColorMaterial'; + defaultVertexColorMaterial.vertexColors = VertexColors; + + var defaultLineMaterial = new LineBasicMaterial(); + defaultLineMaterial.name = 'defaultLineMaterial'; + + var defaultPointMaterial = new PointsMaterial( { size: 1 } ); + defaultPointMaterial.name = 'defaultPointMaterial'; + + var runtimeMaterials = {}; + runtimeMaterials[ defaultMaterial.name ] = defaultMaterial; + runtimeMaterials[ defaultVertexColorMaterial.name ] = defaultVertexColorMaterial; + runtimeMaterials[ defaultLineMaterial.name ] = defaultLineMaterial; + runtimeMaterials[ defaultPointMaterial.name ] = defaultPointMaterial; + + this.updateMaterials( + { + cmd: 'materialData', + materials: { + materialCloneInstructions: null, + serializedMaterials: null, + runtimeMaterials: runtimeMaterials + } + } + ); + + }, + + /** + * Set materials loaded by any supplier of an Array of {@link THREE.Material}. + * + * @param {THREE.Material[]} materials Array of {@link THREE.Material} + */ + setMaterials: function ( materials ) { + + var payload = { + cmd: 'materialData', + materials: { + materialCloneInstructions: null, + serializedMaterials: null, + runtimeMaterials: this.validator.isValid( this.callbacks.onLoadMaterials ) ? this.callbacks.onLoadMaterials( materials ) : materials + } + }; + this.updateMaterials( payload ); + + }, + + _setCallbacks: function ( callbacks ) { + + if ( this.validator.isValid( callbacks.onProgress ) ) this.callbacks.setCallbackOnProgress( callbacks.onProgress ); + if ( this.validator.isValid( callbacks.onReportError ) ) this.callbacks.setCallbackOnReportError( callbacks.onReportError ); + if ( this.validator.isValid( callbacks.onMeshAlter ) ) this.callbacks.setCallbackOnMeshAlter( callbacks.onMeshAlter ); + if ( this.validator.isValid( callbacks.onLoad ) ) this.callbacks.setCallbackOnLoad( callbacks.onLoad ); + if ( this.validator.isValid( callbacks.onLoadMaterials ) ) this.callbacks.setCallbackOnLoadMaterials( callbacks.onLoadMaterials ); + + }, + + /** + * Delegates processing of the payload (mesh building or material update) to the corresponding functions (BW-compatibility). + * + * @param {Object} payload Raw Mesh or Material descriptions. + * @returns {THREE.Mesh[]} mesh Array of {@link THREE.Mesh} or null in case of material update + */ + processPayload: function ( payload ) { + + if ( payload.cmd === 'meshData' ) { + + return this.buildMeshes( payload ); + + } else if ( payload.cmd === 'materialData' ) { + + this.updateMaterials( payload ); + return null; + + } + + }, + + /** + * Builds one or multiple meshes from the data described in the payload (buffers, params, material info). + * + * @param {Object} meshPayload Raw mesh description (buffers, params, materials) used to build one to many meshes. + * @returns {THREE.Mesh[]} mesh Array of {@link THREE.Mesh} + */ + buildMeshes: function ( meshPayload ) { + + var meshName = meshPayload.params.meshName; + + var bufferGeometry = new BufferGeometry(); + bufferGeometry.addAttribute( 'position', new BufferAttribute( new Float32Array( meshPayload.buffers.vertices ), 3 ) ); + if ( this.validator.isValid( meshPayload.buffers.indices ) ) { + + bufferGeometry.setIndex( new BufferAttribute( new Uint32Array( meshPayload.buffers.indices ), 1 ) ); + + } + var haveVertexColors = this.validator.isValid( meshPayload.buffers.colors ); + if ( haveVertexColors ) { + + bufferGeometry.addAttribute( 'color', new BufferAttribute( new Float32Array( meshPayload.buffers.colors ), 3 ) ); + + } + if ( this.validator.isValid( meshPayload.buffers.normals ) ) { + + bufferGeometry.addAttribute( 'normal', new BufferAttribute( new Float32Array( meshPayload.buffers.normals ), 3 ) ); + + } else { + + bufferGeometry.computeVertexNormals(); + + } + if ( this.validator.isValid( meshPayload.buffers.uvs ) ) { + + bufferGeometry.addAttribute( 'uv', new BufferAttribute( new Float32Array( meshPayload.buffers.uvs ), 2 ) ); + + } + + var material, materialName, key; + var materialNames = meshPayload.materials.materialNames; + var createMultiMaterial = meshPayload.materials.multiMaterial; + var multiMaterials = []; + for ( key in materialNames ) { + + materialName = materialNames[ key ]; + material = this.materials[ materialName ]; + if ( createMultiMaterial ) multiMaterials.push( material ); + + } + if ( createMultiMaterial ) { + + material = multiMaterials; + var materialGroups = meshPayload.materials.materialGroups; + var materialGroup; + for ( key in materialGroups ) { + + materialGroup = materialGroups[ key ]; + bufferGeometry.addGroup( materialGroup.start, materialGroup.count, materialGroup.index ); + + } + + } + + var meshes = []; + var mesh; + var callbackOnMeshAlter = this.callbacks.onMeshAlter; + var callbackOnMeshAlterResult; + var useOrgMesh = true; + var geometryType = this.validator.verifyInput( meshPayload.geometryType, 0 ); + if ( this.validator.isValid( callbackOnMeshAlter ) ) { + + callbackOnMeshAlterResult = callbackOnMeshAlter( + { + detail: { + meshName: meshName, + bufferGeometry: bufferGeometry, + material: material, + geometryType: geometryType + } + } + ); + if ( this.validator.isValid( callbackOnMeshAlterResult ) ) { + + if ( callbackOnMeshAlterResult.isDisregardMesh() ) { + + useOrgMesh = false; + + } else if ( callbackOnMeshAlterResult.providesAlteredMeshes() ) { + + for ( var i in callbackOnMeshAlterResult.meshes ) { + + meshes.push( callbackOnMeshAlterResult.meshes[ i ] ); + + } + useOrgMesh = false; + + } + + } + + } + if ( useOrgMesh ) { + + if ( meshPayload.computeBoundingSphere ) bufferGeometry.computeBoundingSphere(); + if ( geometryType === 0 ) { + + mesh = new Mesh( bufferGeometry, material ); + + } else if ( geometryType === 1 ) { + + mesh = new LineSegments( bufferGeometry, material ); + + } else { + + mesh = new Points( bufferGeometry, material ); + + } + mesh.name = meshName; + meshes.push( mesh ); + + } + + var progressMessage; + if ( this.validator.isValid( meshes ) && meshes.length > 0 ) { + + var meshNames = []; + for ( var i in meshes ) { + + mesh = meshes[ i ]; + meshNames[ i ] = mesh.name; + + } + progressMessage = 'Adding mesh(es) (' + meshNames.length + ': ' + meshNames + ') from input mesh: ' + meshName; + progressMessage += ' (' + ( meshPayload.progress.numericalValue * 100 ).toFixed( 2 ) + '%)'; + + } else { + + progressMessage = 'Not adding mesh: ' + meshName; + progressMessage += ' (' + ( meshPayload.progress.numericalValue * 100 ).toFixed( 2 ) + '%)'; + + } + var callbackOnProgress = this.callbacks.onProgress; + if ( this.validator.isValid( callbackOnProgress ) ) { + + var event = new CustomEvent( 'MeshBuilderEvent', { + detail: { + type: 'progress', + modelName: meshPayload.params.meshName, + text: progressMessage, + numericalValue: meshPayload.progress.numericalValue + } + } ); + callbackOnProgress( event ); + + } + + return meshes; + + }, + + /** + * Updates the materials with contained material objects (sync) or from alteration instructions (async). + * + * @param {Object} materialPayload Material update instructions + */ + updateMaterials: function ( materialPayload ) { + + var material, materialName; + var materialCloneInstructions = materialPayload.materials.materialCloneInstructions; + if ( this.validator.isValid( materialCloneInstructions ) ) { + + var materialNameOrg = materialCloneInstructions.materialNameOrg; + var materialOrg = this.materials[ materialNameOrg ]; + + if ( this.validator.isValid( materialNameOrg ) ) { + + material = materialOrg.clone(); + + materialName = materialCloneInstructions.materialName; + material.name = materialName; + + var materialProperties = materialCloneInstructions.materialProperties; + for ( var key in materialProperties ) { + + if ( material.hasOwnProperty( key ) && materialProperties.hasOwnProperty( key ) ) material[ key ] = materialProperties[ key ]; + + } + this.materials[ materialName ] = material; + + } else { + + console.warn( 'Requested material "' + materialNameOrg + '" is not available!' ); + + } + + } + + var materials = materialPayload.materials.serializedMaterials; + if ( this.validator.isValid( materials ) && Object.keys( materials ).length > 0 ) { + + var loader = new MaterialLoader(); + var materialJson; + for ( materialName in materials ) { + + materialJson = materials[ materialName ]; + if ( this.validator.isValid( materialJson ) ) { + + material = loader.parse( materialJson ); + if ( this.logging.enabled ) console.info( 'De-serialized material with name "' + materialName + '" will be added.' ); + this.materials[ materialName ] = material; + + } + + } + + } + + materials = materialPayload.materials.runtimeMaterials; + if ( this.validator.isValid( materials ) && Object.keys( materials ).length > 0 ) { + + for ( materialName in materials ) { + + material = materials[ materialName ]; + if ( this.logging.enabled ) console.info( 'Material with name "' + materialName + '" will be added.' ); + this.materials[ materialName ] = material; + + } + + } + + }, + + /** + * Returns the mapping object of material name and corresponding jsonified material. + * + * @returns {Object} Map of Materials in JSON representation + */ + getMaterialsJSON: function () { + + var materialsJSON = {}; + var material; + for ( var materialName in this.materials ) { + + material = this.materials[ materialName ]; + materialsJSON[ materialName ] = material.toJSON(); + + } + + return materialsJSON; + + }, + + /** + * Returns the mapping object of material name and corresponding material. + * + * @returns {Object} Map of {@link THREE.Material} + */ + getMaterials: function () { + + return this.materials; + + } + +}; + +/** + * This class provides means to transform existing parser code into a web worker. It defines a simple communication protocol + * which allows to configure the worker and receive raw mesh data during execution. + * @class + */ +LoaderSupport.WorkerSupport = function () { + + console.info( 'Using LoaderSupport.WorkerSupport version: ' + LoaderSupport.WorkerSupport.WORKER_SUPPORT_VERSION ); + this.logging = { + enabled: true, + debug: false + }; + + //Choose implementation of worker based on environment + this.loaderWorker = typeof window !== "undefined" ? new LoaderSupport.WorkerSupport.LoaderWorker() : new LoaderSupport.WorkerSupport.NodeLoaderWorker(); + +}; + +LoaderSupport.WorkerSupport.WORKER_SUPPORT_VERSION = '2.3.0'; + +LoaderSupport.WorkerSupport.prototype = { + + constructor: LoaderSupport.WorkerSupport, + + /** + * Enable or disable logging in general (except warn and error), plus enable or disable debug logging. + * + * @param {boolean} enabled True or false. + * @param {boolean} debug True or false. + */ + setLogging: function ( enabled, debug ) { + + this.logging.enabled = enabled === true; + this.logging.debug = debug === true; + this.loaderWorker.setLogging( this.logging.enabled, this.logging.debug ); + + }, + + /** + * Forces all ArrayBuffers to be transferred to worker to be copied. + * + * @param {boolean} forceWorkerDataCopy True or false. + */ + setForceWorkerDataCopy: function ( forceWorkerDataCopy ) { + + this.loaderWorker.setForceCopy( forceWorkerDataCopy ); + + }, + + /** + * Validate the status of worker code and the derived worker. + * + * @param {Function} functionCodeBuilder Function that is invoked with funcBuildObject and funcBuildSingleton that allows stringification of objects and singletons. + * @param {String} parserName Name of the Parser object + * @param {String[]} libLocations URL of libraries that shall be added to worker code relative to libPath + * @param {String} libPath Base path used for loading libraries + * @param {LoaderSupport.WorkerRunnerRefImpl} runnerImpl The default worker parser wrapper implementation (communication and execution). An extended class could be passed here. + */ + validate: function ( functionCodeBuilder, parserName, libLocations, libPath, runnerImpl ) { + + if ( LoaderSupport.Validator.isValid( this.loaderWorker.worker ) ) return; + + if ( this.logging.enabled ) { + + console.info( 'WorkerSupport: Building worker code...' ); + console.time( 'buildWebWorkerCode' ); + + } + if ( LoaderSupport.Validator.isValid( runnerImpl ) ) { + + if ( this.logging.enabled ) console.info( 'WorkerSupport: Using "' + runnerImpl.runnerName + '" as Runner class for worker.' ); + + // Browser implementation + + } else if ( typeof window !== "undefined" ) { + + runnerImpl = LoaderSupport.WorkerRunnerRefImpl; + if ( this.logging.enabled ) console.info( 'WorkerSupport: Using DEFAULT "LoaderSupport.WorkerRunnerRefImpl" as Runner class for worker.' ); + + // NodeJS implementation + + } else { + + runnerImpl = LoaderSupport.NodeWorkerRunnerRefImpl; + if ( this.logging.enabled ) console.info( 'WorkerSupport: Using DEFAULT "LoaderSupport.NodeWorkerRunnerRefImpl" as Runner class for worker.' ); + + } + var userWorkerCode = functionCodeBuilder( LoaderSupport.WorkerSupport.CodeSerializer ); + userWorkerCode += 'var Parser = ' + parserName + ';\n\n'; + userWorkerCode += LoaderSupport.WorkerSupport.CodeSerializer.serializeClass( runnerImpl.runnerName, runnerImpl ); + userWorkerCode += 'new ' + runnerImpl.runnerName + '();\n\n'; + + var scope = this; + if ( LoaderSupport.Validator.isValid( libLocations ) && libLocations.length > 0 ) { + + var libsContent = ''; + var loadAllLibraries = function ( path, locations ) { + + if ( locations.length === 0 ) { + + scope.loaderWorker.initWorker( libsContent + userWorkerCode, runnerImpl.runnerName ); + if ( scope.logging.enabled ) console.timeEnd( 'buildWebWorkerCode' ); + + } else { + + var loadedLib = function ( contentAsString ) { + + libsContent += contentAsString; + loadAllLibraries( path, locations ); + + }; + + var fileLoader = new FileLoader(); + fileLoader.setPath( path ); + fileLoader.setResponseType( 'text' ); + fileLoader.load( locations[ 0 ], loadedLib ); + locations.shift(); + + } + + }; + loadAllLibraries( libPath, libLocations ); + + } else { + + this.loaderWorker.initWorker( userWorkerCode, runnerImpl.runnerName ); + if ( this.logging.enabled ) console.timeEnd( 'buildWebWorkerCode' ); + + } + + }, + + /** + * Specify functions that should be build when new raw mesh data becomes available and when the parser is finished. + * + * @param {Function} meshBuilder The mesh builder function. Default is {@link LoaderSupport.MeshBuilder}. + * @param {Function} onLoad The function that is called when parsing is complete. + */ + setCallbacks: function ( meshBuilder, onLoad ) { + + this.loaderWorker.setCallbacks( meshBuilder, onLoad ); + + }, + + /** + * Runs the parser with the provided configuration. + * + * @param {Object} payload Raw mesh description (buffers, params, materials) used to build one to many meshes. + */ + run: function ( payload ) { + + this.loaderWorker.run( payload ); + + }, + + /** + * Request termination of worker once parser is finished. + * + * @param {boolean} terminateRequested True or false. + */ + setTerminateRequested: function ( terminateRequested ) { + + this.loaderWorker.setTerminateRequested( terminateRequested ); + + } + +}; + + +LoaderSupport.WorkerSupport.LoaderWorker = function () { + + this._reset(); + +}; + +LoaderSupport.WorkerSupport.LoaderWorker.prototype = { + + constructor: LoaderSupport.WorkerSupport.LoaderWorker, + + _reset: function () { + + this.logging = { + enabled: true, + debug: false + }; + this.worker = null; + this.runnerImplName = null; + this.callbacks = { + meshBuilder: null, + onLoad: null + }; + this.terminateRequested = false; + this.queuedMessage = null; + this.started = false; + this.forceCopy = false; + + }, + + /** + * Check support for Workers and other necessary features returning + * reason if the environment is unsupported + * + * @returns {string|undefined} Returns undefined if supported, or + * string with error if not supported + */ + checkSupport: function () { + + if ( window.Worker === undefined ) return "This browser does not support web workers!"; + if ( window.Blob === undefined ) return "This browser does not support Blob!"; + if ( typeof window.URL.createObjectURL !== 'function' ) return "This browser does not support Object creation from URL!"; + + }, + + setLogging: function ( enabled, debug ) { + + this.logging.enabled = enabled === true; + this.logging.debug = debug === true; + + }, + + setForceCopy: function ( forceCopy ) { + + this.forceCopy = forceCopy === true; + + }, + + initWorker: function ( code, runnerImplName ) { + + var supportError = this.checkSupport(); + if ( supportError ) { + + throw supportError; + + } + this.runnerImplName = runnerImplName; + + var blob = new Blob( [ code ], { type: 'application/javascript' } ); + this.worker = new Worker( window.URL.createObjectURL( blob ) ); + + this.worker.onmessage = this._receiveWorkerMessage; + + // set referemce to this, then processing in worker scope within "_receiveWorkerMessage" can access members + this.worker.runtimeRef = this; + + // process stored queuedMessage + this._postMessage(); + + }, + + /** + * Executed in worker scope + */ + _receiveWorkerMessage: function ( e ) { + + var payload = e.data; + switch ( payload.cmd ) { + + case 'meshData': + case 'materialData': + case 'imageData': + this.runtimeRef.callbacks.meshBuilder( payload ); + break; + + case 'complete': + this.runtimeRef.queuedMessage = null; + this.started = false; + this.runtimeRef.callbacks.onLoad( payload.msg ); + + if ( this.runtimeRef.terminateRequested ) { + + if ( this.runtimeRef.logging.enabled ) console.info( 'WorkerSupport [' + this.runtimeRef.runnerImplName + ']: Run is complete. Terminating application on request!' ); + this.runtimeRef._terminate(); + + } + break; + + case 'error': + console.error( 'WorkerSupport [' + this.runtimeRef.runnerImplName + ']: Reported error: ' + payload.msg ); + this.runtimeRef.queuedMessage = null; + this.started = false; + this.runtimeRef.callbacks.onLoad( payload.msg ); + + if ( this.runtimeRef.terminateRequested ) { + + if ( this.runtimeRef.logging.enabled ) console.info( 'WorkerSupport [' + this.runtimeRef.runnerImplName + ']: Run reported error. Terminating application on request!' ); + this.runtimeRef._terminate(); + + } + break; + + default: + console.error( 'WorkerSupport [' + this.runtimeRef.runnerImplName + ']: Received unknown command: ' + payload.cmd ); + break; + + } + + }, + + setCallbacks: function ( meshBuilder, onLoad ) { + + this.callbacks.meshBuilder = LoaderSupport.Validator.verifyInput( meshBuilder, this.callbacks.meshBuilder ); + this.callbacks.onLoad = LoaderSupport.Validator.verifyInput( onLoad, this.callbacks.onLoad ); + + }, + + run: function ( payload ) { + + if ( LoaderSupport.Validator.isValid( this.queuedMessage ) ) { + + console.warn( 'Already processing message. Rejecting new run instruction' ); + return; + + } else { + + this.queuedMessage = payload; + this.started = true; + + } + if ( ! LoaderSupport.Validator.isValid( this.callbacks.meshBuilder ) ) throw 'Unable to run as no "MeshBuilder" callback is set.'; + if ( ! LoaderSupport.Validator.isValid( this.callbacks.onLoad ) ) throw 'Unable to run as no "onLoad" callback is set.'; + if ( payload.cmd !== 'run' ) payload.cmd = 'run'; + if ( LoaderSupport.Validator.isValid( payload.logging ) ) { + + payload.logging.enabled = payload.logging.enabled === true; + payload.logging.debug = payload.logging.debug === true; + + } else { + + payload.logging = { + enabled: true, + debug: false + }; + + } + this._postMessage(); + + }, + + _postMessage: function () { + + if ( LoaderSupport.Validator.isValid( this.queuedMessage ) && LoaderSupport.Validator.isValid( this.worker ) ) { + + if ( this.queuedMessage.data.input instanceof ArrayBuffer ) { + + var content; + if ( this.forceCopy ) { + + content = this.queuedMessage.data.input.slice( 0 ); + + } else { + + content = this.queuedMessage.data.input; + + } + this.worker.postMessage( this.queuedMessage, [ content ] ); + + } else { + + this.worker.postMessage( this.queuedMessage ); + + } + + } + + }, + + setTerminateRequested: function ( terminateRequested ) { + + this.terminateRequested = terminateRequested === true; + if ( this.terminateRequested && LoaderSupport.Validator.isValid( this.worker ) && ! LoaderSupport.Validator.isValid( this.queuedMessage ) && this.started ) { + + if ( this.logging.enabled ) console.info( 'Worker is terminated immediately as it is not running!' ); + this._terminate(); + + } + + }, + + _terminate: function () { + + this.worker.terminate(); + this._reset(); + + } +}; + + +LoaderSupport.WorkerSupport.CodeSerializer = { + + /** + * + * @param fullName + * @param object + * @returns {string} + */ + serializeObject: function ( fullName, object ) { + + var objectString = fullName + ' = {\n\n'; + var part; + for ( var name in object ) { + + part = object[ name ]; + if ( typeof ( part ) === 'string' || part instanceof String ) { + + part = part.replace( '\n', '\\n' ); + part = part.replace( '\r', '\\r' ); + objectString += '\t' + name + ': "' + part + '",\n'; + + } else if ( part instanceof Array ) { + + objectString += '\t' + name + ': [' + part + '],\n'; + + } else if ( typeof part === 'object' ) { + + // TODO: Short-cut for now. Recursion required? + objectString += '\t' + name + ': {},\n'; + + } else { + + objectString += '\t' + name + ': ' + part + ',\n'; + + } + + } + objectString += '}\n\n'; + + return objectString; + + }, + + /** + * + * @param fullName + * @param object + * @param basePrototypeName + * @param ignoreFunctions + * @returns {string} + */ + serializeClass: function ( fullName, object, constructorName, basePrototypeName, ignoreFunctions, includeFunctions, overrideFunctions ) { + + var valueString, objectPart, constructorString, i, funcOverride; + var prototypeFunctions = []; + var objectProperties = []; + var objectFunctions = []; + var isExtended = ( basePrototypeName !== null && basePrototypeName !== undefined ); + + if ( ! Array.isArray( ignoreFunctions ) ) ignoreFunctions = []; + if ( ! Array.isArray( includeFunctions ) ) includeFunctions = null; + if ( ! Array.isArray( overrideFunctions ) ) overrideFunctions = []; + + for ( var name in object.prototype ) { + + objectPart = object.prototype[ name ]; + valueString = objectPart.toString(); + if ( name === 'constructor' ) { + + constructorString = fullName + ' = ' + valueString + ';\n\n'; + + } else if ( typeof objectPart === 'function' ) { + + if ( ignoreFunctions.indexOf( name ) < 0 && ( includeFunctions === null || includeFunctions.indexOf( name ) >= 0 ) ) { + + funcOverride = overrideFunctions[ name ]; + if ( funcOverride && funcOverride.fullName === fullName + '.prototype.' + name ) { + + valueString = funcOverride.code; + + } + if ( isExtended ) { + + prototypeFunctions.push( fullName + '.prototype.' + name + ' = ' + valueString + ';\n\n' ); + + } else { + + prototypeFunctions.push( '\t' + name + ': ' + valueString + ',\n\n' ); + + } + + } + + } + + } + for ( var name in object ) { + + objectPart = object[ name ]; + + if ( typeof objectPart === 'function' ) { + + if ( ignoreFunctions.indexOf( name ) < 0 && ( includeFunctions === null || includeFunctions.indexOf( name ) >= 0 ) ) { + + funcOverride = overrideFunctions[ name ]; + if ( funcOverride && funcOverride.fullName === fullName + '.' + name ) { + + valueString = funcOverride.code; + + } else { + + valueString = objectPart.toString(); + + } + objectFunctions.push( fullName + '.' + name + ' = ' + valueString + ';\n\n' ); + + } + + } else { + + if ( typeof ( objectPart ) === 'string' || objectPart instanceof String ) { + + valueString = '\"' + objectPart.toString() + '\"'; + + } else if ( typeof objectPart === 'object' ) { + + // TODO: Short-cut for now. Recursion required? + valueString = "{}"; + + } else { + + valueString = objectPart; + + } + objectProperties.push( fullName + '.' + name + ' = ' + valueString + ';\n' ); + + } + + } + if ( ( constructorString === undefined || constructorString === null ) && typeof object.prototype.constructor === 'function' ) { + + constructorString = fullName + ' = ' + object.prototype.constructor.toString().replace( constructorName, '' ); + + } + var objectString = constructorString + '\n\n'; + if ( isExtended ) { + + objectString += fullName + '.prototype = Object.create( ' + basePrototypeName + '.prototype );\n'; + + } + objectString += fullName + '.prototype.constructor = ' + fullName + ';\n'; + objectString += '\n\n'; + + for ( i = 0; i < objectProperties.length; i ++ ) objectString += objectProperties[ i ]; + objectString += '\n\n'; + + for ( i = 0; i < objectFunctions.length; i ++ ) objectString += objectFunctions[ i ]; + objectString += '\n\n'; + + if ( isExtended ) { + + for ( i = 0; i < prototypeFunctions.length; i ++ ) objectString += prototypeFunctions[ i ]; + + } else { + + objectString += fullName + '.prototype = {\n\n'; + for ( i = 0; i < prototypeFunctions.length; i ++ ) objectString += prototypeFunctions[ i ]; + objectString += '\n};'; + + } + objectString += '\n\n'; + + return objectString; + + }, +}; + +/** + * Default implementation of the WorkerRunner responsible for creation and configuration of the parser within the worker. + * + * @class + */ +LoaderSupport.WorkerRunnerRefImpl = function () { + + var scopedRunner = function ( event ) { + + this.processMessage( event.data ); + + }; + this.getParentScope().addEventListener( 'message', scopedRunner.bind( this ) ); + +}; + +LoaderSupport.WorkerRunnerRefImpl.runnerName = 'LoaderSupport.WorkerRunnerRefImpl'; + +LoaderSupport.WorkerRunnerRefImpl.prototype = { + + constructor: LoaderSupport.WorkerRunnerRefImpl, + + /** + * Returns the parent scope that this worker was spawned in. + * + * @returns {WorkerGlobalScope|Object} Returns a references + * to the parent global scope or compatible type. + */ + getParentScope: function () { + + return self; + + }, + + /** + * Applies values from parameter object via set functions or via direct assignment. + * + * @param {Object} parser The parser instance + * @param {Object} params The parameter object + */ + applyProperties: function ( parser, params ) { + + var property, funcName, values; + for ( property in params ) { + + funcName = 'set' + property.substring( 0, 1 ).toLocaleUpperCase() + property.substring( 1 ); + values = params[ property ]; + + if ( typeof parser[ funcName ] === 'function' ) { + + parser[ funcName ]( values ); + + } else if ( parser.hasOwnProperty( property ) ) { + + parser[ property ] = values; + + } + + } + + }, + + /** + * Configures the Parser implementation according the supplied configuration object. + * + * @param {Object} payload Raw mesh description (buffers, params, materials) used to build one to many meshes. + */ + processMessage: function ( payload ) { + + if ( payload.cmd === 'run' ) { + + var self = this.getParentScope(); + var callbacks = { + callbackMeshBuilder: function ( payload ) { + + self.postMessage( payload ); + + }, + callbackProgress: function ( text ) { + + if ( payload.logging.enabled && payload.logging.debug ) console.debug( 'WorkerRunner: progress: ' + text ); + + } + }; + + // Parser is expected to be named as such + var parser = new Parser(); + if ( typeof parser[ 'setLogging' ] === 'function' ) parser.setLogging( payload.logging.enabled, payload.logging.debug ); + this.applyProperties( parser, payload.params ); + this.applyProperties( parser, payload.materials ); + this.applyProperties( parser, callbacks ); + parser.workerScope = self; + parser.parse( payload.data.input, payload.data.options ); + + if ( payload.logging.enabled ) console.log( 'WorkerRunner: Run complete!' ); + + callbacks.callbackMeshBuilder( { + cmd: 'complete', + msg: 'WorkerRunner completed run.' + } ); + + } else { + + console.error( 'WorkerRunner: Received unknown command: ' + payload.cmd ); + + } + + } +}; + + +/** + * This class provides the NodeJS implementation of the WorkerRunnerRefImpl + * @class + * @extends LoaderSupport.WorkerRunnerRefImpl + */ +LoaderSupport.NodeWorkerRunnerRefImpl = function () { + + this.runnerName = 'LoaderSupport.NodeWorkerRunnerRefImpl'; + // No call to super because super class only binds to processMessage + // In NodeJS, there is no addEventListener so use onmessage. + // Also, the message object can be passed directly to + // processMessage() as it isn't an `Event`, but a plain object + // with the data + this.getParentScope().onmessage = this.processMessage.bind( this ); + +}; + +LoaderSupport.NodeWorkerRunnerRefImpl.prototype = Object.create( LoaderSupport.WorkerRunnerRefImpl.prototype ); +LoaderSupport.NodeWorkerRunnerRefImpl.prototype.constructor = LoaderSupport.NodeWorkerRunnerRefImpl; +LoaderSupport.NodeWorkerRunnerRefImpl.runnerName = 'LoaderSupport.NodeWorkerRunnerRefImpl'; + +LoaderSupport.NodeWorkerRunnerRefImpl.prototype = { + + getParentScope: function () { + + // Work around webpack builds failing with NodeJS requires + // (placing it outside this function will fail because + // this class is passed to the worker as a string!) + var _require = eval( 'require' ); + return _require( 'worker_threads' ).parentPort; + + } +}; + + +/** + * This class provides the NodeJS implementation of LoaderWorker + * @class + * @extends LoaderWorker + */ +LoaderSupport.WorkerSupport.NodeLoaderWorker = function () { + + LoaderSupport.WorkerSupport.LoaderWorker.call( this ); + +}; + +LoaderSupport.WorkerSupport.NodeLoaderWorker.prototype = Object.create( LoaderSupport.WorkerSupport.LoaderWorker.prototype ); +LoaderSupport.WorkerSupport.NodeLoaderWorker.prototype.constructor = LoaderSupport.WorkerSupport.NodeLoaderWorker; + +/** + * @inheritdoc + */ +LoaderSupport.WorkerSupport.NodeLoaderWorker.checkSupport = function () { + + try { + + // Work around webpack builds failing with NodeJS requires + var _require = eval( 'require' ); + _require.resolve( 'worker_threads' ); + + } catch ( e ) { + + return 'This version of Node does not support web workers!'; + + } + +}; + +/** + * @inheritdoc + */ +LoaderSupport.WorkerSupport.NodeLoaderWorker.prototype.initWorker = function ( code, runnerImplName ) { + + var supportError = this.checkSupport(); + if ( supportError ) { + + throw supportError; + + } + this.runnerImplName = runnerImplName; + + // Work around webpack builds failing with NodeJS requires + var _require = eval( 'require' ); + var Worker = _require( 'worker_threads' ).Worker; + this.worker = new Worker( code, { eval: true } ); + + this.worker.onmessage = this._receiveWorkerMessage; + + // set referemce to this, then processing in worker scope within "_receiveWorkerMessage" can access members + this.worker.runtimeRef = this; + + // process stored queuedMessage + this._postMessage(); + +}; + +/** + * Orchestrate loading of multiple OBJ files/data from an instruction queue with a configurable amount of workers (1-16). + * Workflow: + * prepareWorkers + * enqueueForRun + * processQueue + * tearDown (to force stop) + * + * @class + * + * @param {string} classDef Class definition to be used for construction + */ +LoaderSupport.WorkerDirector = function ( classDef ) { + + console.info( 'Using LoaderSupport.WorkerDirector version: ' + LoaderSupport.WorkerDirector.LOADER_WORKER_DIRECTOR_VERSION ); + this.logging = { + enabled: true, + debug: false + }; + + this.maxQueueSize = LoaderSupport.WorkerDirector.MAX_QUEUE_SIZE; + this.maxWebWorkers = LoaderSupport.WorkerDirector.MAX_WEB_WORKER; + this.crossOrigin = null; + + if ( ! LoaderSupport.Validator.isValid( classDef ) ) throw 'Provided invalid classDef: ' + classDef; + + this.workerDescription = { + classDef: classDef, + globalCallbacks: {}, + workerSupports: {}, + forceWorkerDataCopy: true + }; + this.objectsCompleted = 0; + this.instructionQueue = []; + this.instructionQueuePointer = 0; + + this.callbackOnFinishedProcessing = null; + +}; + + +LoaderSupport.WorkerDirector.LOADER_WORKER_DIRECTOR_VERSION = '2.3.0'; +LoaderSupport.WorkerDirector.MAX_WEB_WORKER = 16; +LoaderSupport.WorkerDirector.MAX_QUEUE_SIZE = 2048; + +LoaderSupport.WorkerDirector.prototype = { + + constructor: LoaderSupport.WorkerDirector, + /** + * Enable or disable logging in general (except warn and error), plus enable or disable debug logging. + * + * @param {boolean} enabled True or false. + * @param {boolean} debug True or false. + */ + setLogging: function ( enabled, debug ) { + + this.logging.enabled = enabled === true; + this.logging.debug = debug === true; + + }, + + /** + * Returns the maximum length of the instruction queue. + * + * @returns {number} + */ + getMaxQueueSize: function () { + + return this.maxQueueSize; + + }, + + /** + * Returns the maximum number of workers. + * + * @returns {number} + */ + getMaxWebWorkers: function () { + + return this.maxWebWorkers; + + }, + + /** + * Sets the CORS string to be used. + * + * @param {string} crossOrigin CORS value + */ + setCrossOrigin: function ( crossOrigin ) { + + this.crossOrigin = crossOrigin; + + }, + + /** + * Forces all ArrayBuffers to be transferred to worker to be copied. + * + * @param {boolean} forceWorkerDataCopy True or false. + */ + setForceWorkerDataCopy: function ( forceWorkerDataCopy ) { + + this.workerDescription.forceWorkerDataCopy = forceWorkerDataCopy === true; + + }, + + /** + * Create or destroy workers according limits. Set the name and register callbacks for dynamically created web workers. + * + * @param {THREE.OBJLoader2.WWOBJLoader2.PrepDataCallbacks} globalCallbacks Register global callbacks used by all web workers + * @param {number} maxQueueSize Set the maximum size of the instruction queue (1-1024) + * @param {number} maxWebWorkers Set the maximum amount of workers (1-16) + */ + prepareWorkers: function ( globalCallbacks, maxQueueSize, maxWebWorkers ) { + + if ( LoaderSupport.Validator.isValid( globalCallbacks ) ) this.workerDescription.globalCallbacks = globalCallbacks; + this.maxQueueSize = Math.min( maxQueueSize, LoaderSupport.WorkerDirector.MAX_QUEUE_SIZE ); + this.maxWebWorkers = Math.min( maxWebWorkers, LoaderSupport.WorkerDirector.MAX_WEB_WORKER ); + this.maxWebWorkers = Math.min( this.maxWebWorkers, this.maxQueueSize ); + this.objectsCompleted = 0; + this.instructionQueue = []; + this.instructionQueuePointer = 0; + + for ( var instanceNo = 0; instanceNo < this.maxWebWorkers; instanceNo ++ ) { + + var workerSupport = new LoaderSupport.WorkerSupport(); + workerSupport.setLogging( this.logging.enabled, this.logging.debug ); + workerSupport.setForceWorkerDataCopy( this.workerDescription.forceWorkerDataCopy ); + this.workerDescription.workerSupports[ instanceNo ] = { + instanceNo: instanceNo, + inUse: false, + terminateRequested: false, + workerSupport: workerSupport, + loader: null + }; + + } + + }, + + /** + * Store run instructions in internal instructionQueue. + * + * @param {LoaderSupport.PrepData} prepData + */ + enqueueForRun: function ( prepData ) { + + if ( this.instructionQueue.length < this.maxQueueSize ) { + + this.instructionQueue.push( prepData ); + + } + + }, + + /** + * Returns if any workers are running. + * + * @returns {boolean} + */ + isRunning: function () { + + var wsKeys = Object.keys( this.workerDescription.workerSupports ); + return ( ( this.instructionQueue.length > 0 && this.instructionQueuePointer < this.instructionQueue.length ) || wsKeys.length > 0 ); + + }, + + /** + * Process the instructionQueue until it is depleted. + */ + processQueue: function () { + + var prepData, supportDesc; + for ( var instanceNo in this.workerDescription.workerSupports ) { + + supportDesc = this.workerDescription.workerSupports[ instanceNo ]; + if ( ! supportDesc.inUse ) { + + if ( this.instructionQueuePointer < this.instructionQueue.length ) { + + prepData = this.instructionQueue[ this.instructionQueuePointer ]; + this._kickWorkerRun( prepData, supportDesc ); + this.instructionQueuePointer ++; + + } else { + + this._deregister( supportDesc ); + + } + + } + + } + + if ( ! this.isRunning() && this.callbackOnFinishedProcessing !== null ) { + + this.callbackOnFinishedProcessing(); + this.callbackOnFinishedProcessing = null; + + } + + }, + + _kickWorkerRun: function ( prepData, supportDesc ) { + + supportDesc.inUse = true; + supportDesc.workerSupport.setTerminateRequested( supportDesc.terminateRequested ); + + if ( this.logging.enabled ) console.info( '\nAssigning next item from queue to worker (queue length: ' + this.instructionQueue.length + ')\n\n' ); + + var validator = LoaderSupport.Validator; + var scope = this; + var prepDataCallbacks = prepData.getCallbacks(); + var globalCallbacks = this.workerDescription.globalCallbacks; + var wrapperOnLoad = function ( event ) { + + if ( validator.isValid( globalCallbacks.onLoad ) ) globalCallbacks.onLoad( event ); + if ( validator.isValid( prepDataCallbacks.onLoad ) ) prepDataCallbacks.onLoad( event ); + scope.objectsCompleted ++; + supportDesc.inUse = false; + + scope.processQueue(); + + }; + + var wrapperOnProgress = function ( event ) { + + if ( validator.isValid( globalCallbacks.onProgress ) ) globalCallbacks.onProgress( event ); + if ( validator.isValid( prepDataCallbacks.onProgress ) ) prepDataCallbacks.onProgress( event ); + + }; + + var wrapperOnMeshAlter = function ( event, override ) { + + if ( validator.isValid( globalCallbacks.onMeshAlter ) ) override = globalCallbacks.onMeshAlter( event, override ); + if ( validator.isValid( prepDataCallbacks.onMeshAlter ) ) override = globalCallbacks.onMeshAlter( event, override ); + return override; + + }; + + var wrapperOnLoadMaterials = function ( materials ) { + + if ( validator.isValid( globalCallbacks.onLoadMaterials ) ) materials = globalCallbacks.onLoadMaterials( materials ); + if ( validator.isValid( prepDataCallbacks.onLoadMaterials ) ) materials = prepDataCallbacks.onLoadMaterials( materials ); + return materials; + + }; + + var wrapperOnReportError = function ( errorMessage ) { + + var continueProcessing = true; + if ( validator.isValid( globalCallbacks.onReportError ) ) continueProcessing = globalCallbacks.onReportError( supportDesc, errorMessage ); + if ( validator.isValid( prepDataCallbacks.onReportError ) ) continueProcessing = prepDataCallbacks.onReportError( supportDesc, errorMessage ); + + if ( ! validator.isValid( globalCallbacks.onReportError ) && ! validator.isValid( prepDataCallbacks.onReportError ) ) { + + console.error( 'Loader reported an error: ' ); + console.error( errorMessage ); + + } + if ( continueProcessing ) { + + supportDesc.inUse = false; + scope.processQueue(); + + } + + }; + + supportDesc.loader = this._buildLoader( supportDesc.instanceNo ); + + var updatedCallbacks = new LoaderSupport.Callbacks(); + updatedCallbacks.setCallbackOnLoad( wrapperOnLoad ); + updatedCallbacks.setCallbackOnProgress( wrapperOnProgress ); + updatedCallbacks.setCallbackOnReportError( wrapperOnReportError ); + updatedCallbacks.setCallbackOnMeshAlter( wrapperOnMeshAlter ); + updatedCallbacks.setCallbackOnLoadMaterials( wrapperOnLoadMaterials ); + prepData.callbacks = updatedCallbacks; + + supportDesc.loader.run( prepData, supportDesc.workerSupport ); + + }, + + _buildLoader: function ( instanceNo ) { + + var classDef = this.workerDescription.classDef; + var loader = Object.create( classDef.prototype ); + classDef.call( loader, DefaultLoadingManager ); + + // verify that all required functions are implemented + if ( ! loader.hasOwnProperty( 'instanceNo' ) ) throw classDef.name + ' has no property "instanceNo".'; + loader.instanceNo = instanceNo; + + if ( ! loader.hasOwnProperty( 'workerSupport' ) ) { + + throw classDef.name + ' has no property "workerSupport".'; + + } + if ( typeof loader.run !== 'function' ) throw classDef.name + ' has no function "run".'; + if ( ! loader.hasOwnProperty( 'callbacks' ) || ! LoaderSupport.Validator.isValid( loader.callbacks ) ) { + + console.warn( classDef.name + ' has an invalid property "callbacks". Will change to "LoaderSupport.Callbacks"' ); + loader.callbacks = new LoaderSupport.Callbacks(); + + } + + return loader; + + }, + + _deregister: function ( supportDesc ) { + + if ( LoaderSupport.Validator.isValid( supportDesc ) ) { + + supportDesc.workerSupport.setTerminateRequested( true ); + if ( this.logging.enabled ) console.info( 'Requested termination of worker #' + supportDesc.instanceNo + '.' ); + + var loaderCallbacks = supportDesc.loader.callbacks; + if ( LoaderSupport.Validator.isValid( loaderCallbacks.onProgress ) ) loaderCallbacks.onProgress( { detail: { text: '' } } ); + delete this.workerDescription.workerSupports[ supportDesc.instanceNo ]; + + } + + }, + + /** + * Terminate all workers. + * + * @param {callback} callbackOnFinishedProcessing Function called once all workers finished processing. + */ + tearDown: function ( callbackOnFinishedProcessing ) { + + if ( this.logging.enabled ) console.info( 'WorkerDirector received the deregister call. Terminating all workers!' ); + + this.instructionQueuePointer = this.instructionQueue.length; + this.callbackOnFinishedProcessing = LoaderSupport.Validator.verifyInput( callbackOnFinishedProcessing, null ); + + for ( var name in this.workerDescription.workerSupports ) { + + this.workerDescription.workerSupports[ name ].terminateRequested = true; + + } + + } + +}; + +export { LoaderSupport }; diff --git a/examples/jsm/loaders/MD2Loader.js b/examples/jsm/loaders/MD2Loader.js new file mode 100644 index 00000000000000..331386cbbfe043 --- /dev/null +++ b/examples/jsm/loaders/MD2Loader.js @@ -0,0 +1,398 @@ +/** + * @author mrdoob / http://mrdoob.com/ + */ + +import { + AnimationClip, + BufferGeometry, + DefaultLoadingManager, + FileLoader, + Float32BufferAttribute, + Vector3, +} from "../../../build/three.module.js"; + +var MD2Loader = function ( manager ) { + + this.manager = ( manager !== undefined ) ? manager : DefaultLoadingManager; + +}; + +MD2Loader.prototype = { + + constructor: MD2Loader, + + load: function ( url, onLoad, onProgress, onError ) { + + var scope = this; + + var loader = new FileLoader( scope.manager ); + loader.setPath( scope.path ); + loader.setResponseType( 'arraybuffer' ); + loader.load( url, function ( buffer ) { + + onLoad( scope.parse( buffer ) ); + + }, onProgress, onError ); + + }, + + setPath: function ( value ) { + + this.path = value; + return this; + + }, + + parse: ( function () { + + var normalData = [ + [ - 0.525731, 0.000000, 0.850651 ], [ - 0.442863, 0.238856, 0.864188 ], + [ - 0.295242, 0.000000, 0.955423 ], [ - 0.309017, 0.500000, 0.809017 ], + [ - 0.162460, 0.262866, 0.951056 ], [ 0.000000, 0.000000, 1.000000 ], + [ 0.000000, 0.850651, 0.525731 ], [ - 0.147621, 0.716567, 0.681718 ], + [ 0.147621, 0.716567, 0.681718 ], [ 0.000000, 0.525731, 0.850651 ], + [ 0.309017, 0.500000, 0.809017 ], [ 0.525731, 0.000000, 0.850651 ], + [ 0.295242, 0.000000, 0.955423 ], [ 0.442863, 0.238856, 0.864188 ], + [ 0.162460, 0.262866, 0.951056 ], [ - 0.681718, 0.147621, 0.716567 ], + [ - 0.809017, 0.309017, 0.500000 ], [ - 0.587785, 0.425325, 0.688191 ], + [ - 0.850651, 0.525731, 0.000000 ], [ - 0.864188, 0.442863, 0.238856 ], + [ - 0.716567, 0.681718, 0.147621 ], [ - 0.688191, 0.587785, 0.425325 ], + [ - 0.500000, 0.809017, 0.309017 ], [ - 0.238856, 0.864188, 0.442863 ], + [ - 0.425325, 0.688191, 0.587785 ], [ - 0.716567, 0.681718, - 0.147621 ], + [ - 0.500000, 0.809017, - 0.309017 ], [ - 0.525731, 0.850651, 0.000000 ], + [ 0.000000, 0.850651, - 0.525731 ], [ - 0.238856, 0.864188, - 0.442863 ], + [ 0.000000, 0.955423, - 0.295242 ], [ - 0.262866, 0.951056, - 0.162460 ], + [ 0.000000, 1.000000, 0.000000 ], [ 0.000000, 0.955423, 0.295242 ], + [ - 0.262866, 0.951056, 0.162460 ], [ 0.238856, 0.864188, 0.442863 ], + [ 0.262866, 0.951056, 0.162460 ], [ 0.500000, 0.809017, 0.309017 ], + [ 0.238856, 0.864188, - 0.442863 ], [ 0.262866, 0.951056, - 0.162460 ], + [ 0.500000, 0.809017, - 0.309017 ], [ 0.850651, 0.525731, 0.000000 ], + [ 0.716567, 0.681718, 0.147621 ], [ 0.716567, 0.681718, - 0.147621 ], + [ 0.525731, 0.850651, 0.000000 ], [ 0.425325, 0.688191, 0.587785 ], + [ 0.864188, 0.442863, 0.238856 ], [ 0.688191, 0.587785, 0.425325 ], + [ 0.809017, 0.309017, 0.500000 ], [ 0.681718, 0.147621, 0.716567 ], + [ 0.587785, 0.425325, 0.688191 ], [ 0.955423, 0.295242, 0.000000 ], + [ 1.000000, 0.000000, 0.000000 ], [ 0.951056, 0.162460, 0.262866 ], + [ 0.850651, - 0.525731, 0.000000 ], [ 0.955423, - 0.295242, 0.000000 ], + [ 0.864188, - 0.442863, 0.238856 ], [ 0.951056, - 0.162460, 0.262866 ], + [ 0.809017, - 0.309017, 0.500000 ], [ 0.681718, - 0.147621, 0.716567 ], + [ 0.850651, 0.000000, 0.525731 ], [ 0.864188, 0.442863, - 0.238856 ], + [ 0.809017, 0.309017, - 0.500000 ], [ 0.951056, 0.162460, - 0.262866 ], + [ 0.525731, 0.000000, - 0.850651 ], [ 0.681718, 0.147621, - 0.716567 ], + [ 0.681718, - 0.147621, - 0.716567 ], [ 0.850651, 0.000000, - 0.525731 ], + [ 0.809017, - 0.309017, - 0.500000 ], [ 0.864188, - 0.442863, - 0.238856 ], + [ 0.951056, - 0.162460, - 0.262866 ], [ 0.147621, 0.716567, - 0.681718 ], + [ 0.309017, 0.500000, - 0.809017 ], [ 0.425325, 0.688191, - 0.587785 ], + [ 0.442863, 0.238856, - 0.864188 ], [ 0.587785, 0.425325, - 0.688191 ], + [ 0.688191, 0.587785, - 0.425325 ], [ - 0.147621, 0.716567, - 0.681718 ], + [ - 0.309017, 0.500000, - 0.809017 ], [ 0.000000, 0.525731, - 0.850651 ], + [ - 0.525731, 0.000000, - 0.850651 ], [ - 0.442863, 0.238856, - 0.864188 ], + [ - 0.295242, 0.000000, - 0.955423 ], [ - 0.162460, 0.262866, - 0.951056 ], + [ 0.000000, 0.000000, - 1.000000 ], [ 0.295242, 0.000000, - 0.955423 ], + [ 0.162460, 0.262866, - 0.951056 ], [ - 0.442863, - 0.238856, - 0.864188 ], + [ - 0.309017, - 0.500000, - 0.809017 ], [ - 0.162460, - 0.262866, - 0.951056 ], + [ 0.000000, - 0.850651, - 0.525731 ], [ - 0.147621, - 0.716567, - 0.681718 ], + [ 0.147621, - 0.716567, - 0.681718 ], [ 0.000000, - 0.525731, - 0.850651 ], + [ 0.309017, - 0.500000, - 0.809017 ], [ 0.442863, - 0.238856, - 0.864188 ], + [ 0.162460, - 0.262866, - 0.951056 ], [ 0.238856, - 0.864188, - 0.442863 ], + [ 0.500000, - 0.809017, - 0.309017 ], [ 0.425325, - 0.688191, - 0.587785 ], + [ 0.716567, - 0.681718, - 0.147621 ], [ 0.688191, - 0.587785, - 0.425325 ], + [ 0.587785, - 0.425325, - 0.688191 ], [ 0.000000, - 0.955423, - 0.295242 ], + [ 0.000000, - 1.000000, 0.000000 ], [ 0.262866, - 0.951056, - 0.162460 ], + [ 0.000000, - 0.850651, 0.525731 ], [ 0.000000, - 0.955423, 0.295242 ], + [ 0.238856, - 0.864188, 0.442863 ], [ 0.262866, - 0.951056, 0.162460 ], + [ 0.500000, - 0.809017, 0.309017 ], [ 0.716567, - 0.681718, 0.147621 ], + [ 0.525731, - 0.850651, 0.000000 ], [ - 0.238856, - 0.864188, - 0.442863 ], + [ - 0.500000, - 0.809017, - 0.309017 ], [ - 0.262866, - 0.951056, - 0.162460 ], + [ - 0.850651, - 0.525731, 0.000000 ], [ - 0.716567, - 0.681718, - 0.147621 ], + [ - 0.716567, - 0.681718, 0.147621 ], [ - 0.525731, - 0.850651, 0.000000 ], + [ - 0.500000, - 0.809017, 0.309017 ], [ - 0.238856, - 0.864188, 0.442863 ], + [ - 0.262866, - 0.951056, 0.162460 ], [ - 0.864188, - 0.442863, 0.238856 ], + [ - 0.809017, - 0.309017, 0.500000 ], [ - 0.688191, - 0.587785, 0.425325 ], + [ - 0.681718, - 0.147621, 0.716567 ], [ - 0.442863, - 0.238856, 0.864188 ], + [ - 0.587785, - 0.425325, 0.688191 ], [ - 0.309017, - 0.500000, 0.809017 ], + [ - 0.147621, - 0.716567, 0.681718 ], [ - 0.425325, - 0.688191, 0.587785 ], + [ - 0.162460, - 0.262866, 0.951056 ], [ 0.442863, - 0.238856, 0.864188 ], + [ 0.162460, - 0.262866, 0.951056 ], [ 0.309017, - 0.500000, 0.809017 ], + [ 0.147621, - 0.716567, 0.681718 ], [ 0.000000, - 0.525731, 0.850651 ], + [ 0.425325, - 0.688191, 0.587785 ], [ 0.587785, - 0.425325, 0.688191 ], + [ 0.688191, - 0.587785, 0.425325 ], [ - 0.955423, 0.295242, 0.000000 ], + [ - 0.951056, 0.162460, 0.262866 ], [ - 1.000000, 0.000000, 0.000000 ], + [ - 0.850651, 0.000000, 0.525731 ], [ - 0.955423, - 0.295242, 0.000000 ], + [ - 0.951056, - 0.162460, 0.262866 ], [ - 0.864188, 0.442863, - 0.238856 ], + [ - 0.951056, 0.162460, - 0.262866 ], [ - 0.809017, 0.309017, - 0.500000 ], + [ - 0.864188, - 0.442863, - 0.238856 ], [ - 0.951056, - 0.162460, - 0.262866 ], + [ - 0.809017, - 0.309017, - 0.500000 ], [ - 0.681718, 0.147621, - 0.716567 ], + [ - 0.681718, - 0.147621, - 0.716567 ], [ - 0.850651, 0.000000, - 0.525731 ], + [ - 0.688191, 0.587785, - 0.425325 ], [ - 0.587785, 0.425325, - 0.688191 ], + [ - 0.425325, 0.688191, - 0.587785 ], [ - 0.425325, - 0.688191, - 0.587785 ], + [ - 0.587785, - 0.425325, - 0.688191 ], [ - 0.688191, - 0.587785, - 0.425325 ] + ]; + + return function ( buffer ) { + + console.time( 'MD2Loader' ); + + var data = new DataView( buffer ); + + // http://tfc.duke.free.fr/coding/md2-specs-en.html + + var header = {}; + var headerNames = [ + 'ident', 'version', + 'skinwidth', 'skinheight', + 'framesize', + 'num_skins', 'num_vertices', 'num_st', 'num_tris', 'num_glcmds', 'num_frames', + 'offset_skins', 'offset_st', 'offset_tris', 'offset_frames', 'offset_glcmds', 'offset_end' + ]; + + for ( var i = 0; i < headerNames.length; i ++ ) { + + header[ headerNames[ i ] ] = data.getInt32( i * 4, true ); + + } + + if ( header.ident !== 844121161 || header.version !== 8 ) { + + console.error( 'Not a valid MD2 file' ); + return; + + } + + if ( header.offset_end !== data.byteLength ) { + + console.error( 'Corrupted MD2 file' ); + return; + + } + + // + + var geometry = new BufferGeometry(); + + // uvs + + var uvsTemp = []; + var offset = header.offset_st; + + for ( var i = 0, l = header.num_st; i < l; i ++ ) { + + var u = data.getInt16( offset + 0, true ); + var v = data.getInt16( offset + 2, true ); + + uvsTemp.push( u / header.skinwidth, 1 - ( v / header.skinheight ) ); + + offset += 4; + + } + + // triangles + + offset = header.offset_tris; + + var vertexIndices = []; + var uvIndices = []; + + for ( var i = 0, l = header.num_tris; i < l; i ++ ) { + + vertexIndices.push( + data.getUint16( offset + 0, true ), + data.getUint16( offset + 2, true ), + data.getUint16( offset + 4, true ) + ); + + uvIndices.push( + data.getUint16( offset + 6, true ), + data.getUint16( offset + 8, true ), + data.getUint16( offset + 10, true ) + ); + + offset += 12; + + } + + // frames + + var translation = new Vector3(); + var scale = new Vector3(); + var string = []; + + var frames = []; + + offset = header.offset_frames; + + for ( var i = 0, l = header.num_frames; i < l; i ++ ) { + + scale.set( + data.getFloat32( offset + 0, true ), + data.getFloat32( offset + 4, true ), + data.getFloat32( offset + 8, true ) + ); + + translation.set( + data.getFloat32( offset + 12, true ), + data.getFloat32( offset + 16, true ), + data.getFloat32( offset + 20, true ) + ); + + offset += 24; + + for ( var j = 0; j < 16; j ++ ) { + + var character = data.getUint8( offset + j, true ); + if ( character === 0 ) break; + + string[ j ] = character; + + } + + var frame = { + name: String.fromCharCode.apply( null, string ), + vertices: [], + normals: [] + }; + + offset += 16; + + for ( var j = 0; j < header.num_vertices; j ++ ) { + + var x = data.getUint8( offset ++, true ); + var y = data.getUint8( offset ++, true ); + var z = data.getUint8( offset ++, true ); + var n = normalData[ data.getUint8( offset ++, true ) ]; + + x = x * scale.x + translation.x; + y = y * scale.y + translation.y; + z = z * scale.z + translation.z; + + frame.vertices.push( x, z, y ); // convert to Y-up + frame.normals.push( n[ 0 ], n[ 2 ], n[ 1 ] ); // convert to Y-up + + } + + frames.push( frame ); + + } + + // static + + var positions = []; + var normals = []; + var uvs = []; + + var verticesTemp = frames[ 0 ].vertices; + var normalsTemp = frames[ 0 ].normals; + + for ( var i = 0, l = vertexIndices.length; i < l; i ++ ) { + + var vertexIndex = vertexIndices[ i ]; + var stride = vertexIndex * 3; + + // + + var x = verticesTemp[ stride ]; + var y = verticesTemp[ stride + 1 ]; + var z = verticesTemp[ stride + 2 ]; + + positions.push( x, y, z ); + + // + + var nx = normalsTemp[ stride ]; + var ny = normalsTemp[ stride + 1 ]; + var nz = normalsTemp[ stride + 2 ]; + + normals.push( nx, ny, nz ); + + // + + var uvIndex = uvIndices[ i ]; + stride = uvIndex * 2; + + var u = uvsTemp[ stride ]; + var v = uvsTemp[ stride + 1 ]; + + uvs.push( u, v ); + + } + + geometry.addAttribute( 'position', new Float32BufferAttribute( positions, 3 ) ); + geometry.addAttribute( 'normal', new Float32BufferAttribute( normals, 3 ) ); + geometry.addAttribute( 'uv', new Float32BufferAttribute( uvs, 2 ) ); + + // animation + + var morphPositions = []; + var morphNormals = []; + + for ( var i = 0, l = frames.length; i < l; i ++ ) { + + var frame = frames[ i ]; + var attributeName = frame.name; + + if ( frame.vertices.length > 0 ) { + + var positions = []; + + for ( var j = 0, jl = vertexIndices.length; j < jl; j ++ ) { + + var vertexIndex = vertexIndices[ j ]; + var stride = vertexIndex * 3; + + var x = frame.vertices[ stride ]; + var y = frame.vertices[ stride + 1 ]; + var z = frame.vertices[ stride + 2 ]; + + positions.push( x, y, z ); + + } + + var positionAttribute = new Float32BufferAttribute( positions, 3 ); + positionAttribute.name = attributeName; + + morphPositions.push( positionAttribute ); + + } + + if ( frame.normals.length > 0 ) { + + var normals = []; + + for ( var j = 0, jl = vertexIndices.length; j < jl; j ++ ) { + + var vertexIndex = vertexIndices[ j ]; + var stride = vertexIndex * 3; + + var nx = frame.normals[ stride ]; + var ny = frame.normals[ stride + 1 ]; + var nz = frame.normals[ stride + 2 ]; + + normals.push( nx, ny, nz ); + + } + + var normalAttribute = new Float32BufferAttribute( normals, 3 ); + normalAttribute.name = attributeName; + + morphNormals.push( normalAttribute ); + + } + + } + + geometry.morphAttributes.position = morphPositions; + geometry.morphAttributes.normal = morphNormals; + + geometry.animations = AnimationClip.CreateClipsFromMorphTargetSequences( frames, 10 ); + + console.timeEnd( 'MD2Loader' ); + + return geometry; + + }; + + } )() + +}; + +export { MD2Loader }; diff --git a/examples/jsm/loaders/MMDLoader.js b/examples/jsm/loaders/MMDLoader.js new file mode 100644 index 00000000000000..8e8cbd453ae6eb --- /dev/null +++ b/examples/jsm/loaders/MMDLoader.js @@ -0,0 +1,2053 @@ +/** + * @author takahiro / https://github.com/takahirox + * + * Dependencies + * - mmd-parser https://github.com/takahirox/mmd-parser + * - TGALoader + * - OutlineEffect + * + * MMDLoader creates Three.js Objects from MMD resources as + * PMD, PMX, VMD, and VPD files. + * + * PMD/PMX is a model data format, VMD is a motion data format + * VPD is a posing data format used in MMD(Miku Miku Dance). + * + * MMD official site + * - http://www.geocities.jp/higuchuu4/index_e.htm + * + * PMD, VMD format (in Japanese) + * - http://blog.goo.ne.jp/torisu_tetosuki/e/209ad341d3ece2b1b4df24abf619d6e4 + * + * PMX format + * - https://gist.github.com/felixjones/f8a06bd48f9da9a4539f + * + * TODO + * - light motion in vmd support. + * - SDEF support. + * - uv/material/bone morphing support. + * - more precise grant skinning support. + * - shadow support. + */ + +import { Parser as MMDParser } from 'mmd-parser'; +import { + AddOperation, + AnimationClip, + Bone, + BufferGeometry, + Color, + CustomBlending, + DefaultLoadingManager, + DoubleSide, + DstAlphaFactor, + Euler, + FileLoader, + Float32BufferAttribute, + FrontSide, + Interpolant, + Loader, + LoaderUtils as THRLoaderUtils, + MultiplyOperation, + NumberKeyframeTrack, + OneMinusSrcAlphaFactor, + Quaternion, + QuaternionKeyframeTrack, + RepeatWrapping, + Skeleton, + SkinnedMesh, + SphericalReflectionMapping, + SrcAlphaFactor, + TextureLoader, + Uint16BufferAttribute, + Vector3, + VectorKeyframeTrack +} from "../../../build/three.module.js"; + +import { TGALoader } from "./TGALoader"; +import { MeshToonMaterial } from "../../../build/three"; + +/** + * @param {LoadingManager} manager + */ +function MMDLoader( manager ) { + + this.manager = ( manager !== undefined ) ? manager : DefaultLoadingManager; + + this.loader = new FileLoader( this.manager ); + + this.parser = null; // lazy generation + this.meshBuilder = new MeshBuilder( this.manager ); + this.animationBuilder = new AnimationBuilder(); + +} + +MMDLoader.prototype = { + + constructor: MMDLoader, + + crossOrigin: 'anonymous', + + /** + * @param {string} crossOrigin + * @return {MMDLoader} + */ + setCrossOrigin: function ( crossOrigin ) { + + this.crossOrigin = crossOrigin; + return this; + + }, + + /** + * @param {string} animationPath + * @return {MMDLoader} + */ + setAnimationPath: function ( animationPath ) { + + this.animationPath = animationPath; + return this; + + }, + + /** + * @param {string} path + * @return {MMDLoader} + */ + setPath: function ( path ) { + + this.path = path; + return this; + + }, + + /** + * @param {string} resourcePath + * @return {MMDLoader} + */ + setResoucePath: function ( resourcePath ) { + + this.resourcePath = resourcePath; + return this; + + }, + + // Load MMD assets as Three.js Object + + /** + * Loads Model file (.pmd or .pmx) as a SkinnedMesh. + * + * @param {string} url - url to Model(.pmd or .pmx) file + * @param {function} onLoad + * @param {function} onProgress + * @param {function} onError + */ + load: function ( url, onLoad, onProgress, onError ) { + + var builder = this.meshBuilder.setCrossOrigin( this.crossOrigin ); + + // resource path + + var resourcePath; + + if ( this.resourcePath !== undefined ) { + + resourcePath = this.resourcePath; + + } else if ( this.path !== undefined ) { + + resourcePath = this.path; + + } else { + + resourcePath = THRLoaderUtils.extractUrlBase( url ); + + } + + var modelExtension = this._extractExtension( url ).toLowerCase(); + + // Should I detect by seeing header? + if ( modelExtension !== 'pmd' && modelExtension !== 'pmx' ) { + + if ( onError ) onError( new Error( 'THREE.MMDLoader: Unknown model file extension .' + modelExtension + '.' ) ); + + return; + + } + + this[ modelExtension === 'pmd' ? 'loadPMD' : 'loadPMX' ]( url, function ( data ) { + + onLoad( builder.build( data, resourcePath, onProgress, onError ) ); + + }, onProgress, onError ); + + }, + + /** + * Loads Motion file(s) (.vmd) as a AnimationClip. + * If two or more files are specified, they'll be merged. + * + * @param {string|Array} url - url(s) to animation(.vmd) file(s) + * @param {SkinnedMesh|Camera} object - tracks will be fitting to this object + * @param {function} onLoad + * @param {function} onProgress + * @param {function} onError + */ + loadAnimation: function ( url, object, onLoad, onProgress, onError ) { + + var builder = this.animationBuilder; + + this.loadVMD( url, function ( vmd ) { + + onLoad( object.isCamera + ? builder.buildCameraAnimation( vmd ) + : builder.build( vmd, object ) ); + + }, onProgress, onError ); + + }, + + /** + * Loads mode file and motion file(s) as an object containing + * a SkinnedMesh and a AnimationClip. + * Tracks of AnimationClip are fitting to the model. + * + * @param {string} modelUrl - url to Model(.pmd or .pmx) file + * @param {string|Array{string}} vmdUrl - url(s) to animation(.vmd) file + * @param {function} onLoad + * @param {function} onProgress + * @param {function} onError + */ + loadWithAnimation: function ( modelUrl, vmdUrl, onLoad, onProgress, onError ) { + + var scope = this; + + this.load( modelUrl, function ( mesh ) { + + scope.loadAnimation( vmdUrl, mesh, function ( animation ) { + + onLoad( { + mesh: mesh, + animation: animation + } ); + + }, onProgress, onError ); + + }, onProgress, onError ); + + }, + + // Load MMD assets as Object data parsed by MMDParser + + /** + * Loads .pmd file as an Object. + * + * @param {string} url - url to .pmd file + * @param {function} onLoad + * @param {function} onProgress + * @param {function} onError + */ + loadPMD: function ( url, onLoad, onProgress, onError ) { + + var parser = this._getParser(); + + this.loader + .setMimeType( undefined ) + .setPath( this.path ) + .setResponseType( 'arraybuffer' ) + .load( url, function ( buffer ) { + + onLoad( parser.parsePmd( buffer, true ) ); + + }, onProgress, onError ); + + }, + + /** + * Loads .pmx file as an Object. + * + * @param {string} url - url to .pmx file + * @param {function} onLoad + * @param {function} onProgress + * @param {function} onError + */ + loadPMX: function ( url, onLoad, onProgress, onError ) { + + var parser = this._getParser(); + + this.loader + .setMimeType( undefined ) + .setPath( this.path ) + .setResponseType( 'arraybuffer' ) + .load( url, function ( buffer ) { + + onLoad( parser.parsePmx( buffer, true ) ); + + }, onProgress, onError ); + + }, + + /** + * Loads .vmd file as an Object. If two or more files are specified + * they'll be merged. + * + * @param {string|Array} url - url(s) to .vmd file(s) + * @param {function} onLoad + * @param {function} onProgress + * @param {function} onError + */ + loadVMD: function ( url, onLoad, onProgress, onError ) { + + var urls = Array.isArray( url ) ? url : [ url ]; + + var vmds = []; + var vmdNum = urls.length; + + var parser = this._getParser(); + + this.loader + .setMimeType( undefined ) + .setPath( this.animationPath ) + .setResponseType( 'arraybuffer' ); + + for ( var i = 0, il = urls.length; i < il; i ++ ) { + + this.loader.load( urls[ i ], function ( buffer ) { + + vmds.push( parser.parseVmd( buffer, true ) ); + + if ( vmds.length === vmdNum ) onLoad( parser.mergeVmds( vmds ) ); + + }, onProgress, onError ); + + } + + }, + + /** + * Loads .vpd file as an Object. + * + * @param {string} url - url to .vpd file + * @param {boolean} isUnicode + * @param {function} onLoad + * @param {function} onProgress + * @param {function} onError + */ + loadVPD: function ( url, isUnicode, onLoad, onProgress, onError ) { + + var parser = this._getParser(); + + this.loader + .setMimeType( isUnicode ? undefined : 'text/plain; charset=shift_jis' ) + .setPath( this.animationPath ) + .setResponseType( 'text' ) + .load( url, function ( text ) { + + onLoad( parser.parseVpd( text, true ) ); + + }, onProgress, onError ); + + }, + + // private methods + + _extractExtension: function ( url ) { + + var index = url.lastIndexOf( '.' ); + return index < 0 ? '' : url.slice( index + 1 ); + + }, + + _getParser: function () { + + if ( this.parser === null ) { + + if ( typeof MMDParser === 'undefined' ) { + + throw new Error( 'THREE.MMDLoader: Import MMDParser https://github.com/takahirox/mmd-parser' ); + + } + + this.parser = new MMDParser(); + + } + + return this.parser; + + } + +}; + +// Utilities + +/* + * base64 encoded defalut toon textures toon00.bmp - toon10.bmp. + * We don't need to request external toon image files. + * This idea is from http://www20.atpages.jp/katwat/three.js_r58/examples/mytest37/mmd.three.js + */ +var DEFAULT_TOON_TEXTURES = [ + 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAL0lEQVRYR+3QQREAAAzCsOFfNJPBJ1XQS9r2hsUAAQIECBAgQIAAAQIECBAgsBZ4MUx/ofm2I/kAAAAASUVORK5CYII=', + 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAN0lEQVRYR+3WQREAMBACsZ5/bWiiMvgEBTt5cW37hjsBBAgQIECAwFwgyfYPCCBAgAABAgTWAh8aBHZBl14e8wAAAABJRU5ErkJggg==', + 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAOUlEQVRYR+3WMREAMAwDsYY/yoDI7MLwIiP40+RJklfcCCBAgAABAgTqArfb/QMCCBAgQIAAgbbAB3z/e0F3js2cAAAAAElFTkSuQmCC', + 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAN0lEQVRYR+3WQREAMBACsZ5/B5ilMvgEBTt5cW37hjsBBAgQIECAwFwgyfYPCCBAgAABAgTWAh81dWyx0gFwKAAAAABJRU5ErkJggg==', + 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAOklEQVRYR+3WoREAMAwDsWb/UQtCy9wxTOQJ/oQ8SXKKGwEECBAgQIBAXeDt7f4BAQQIECBAgEBb4AOz8Hzx7WLY4wAAAABJRU5ErkJggg==', + 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAABPUlEQVRYR+1XwW7CMAy1+f9fZOMysSEOEweEOPRNdm3HbdOyIhAcklPrOs/PLy9RygBALxzcCDQFmgJNgaZAU6Ap0BR4PwX8gsRMVLssMRH5HcpzJEaWL7EVg9F1IHRlyqQohgVr4FGUlUcMJSjcUlDw0zvjeun70cLWmneoyf7NgBTQSniBTQQSuJAZsOnnaczjIMb5hCiuHKxokCrJfVnrctyZL0PkJAJe1HMil4nxeyi3Ypfn1kX51jpPvo/JeCNC4PhVdHdJw2XjBR8brF8PEIhNVn12AgP7uHsTBguBn53MUZCqv7Lp07Pn5k1Ro+uWmUNn7D+M57rtk7aG0Vo73xyF/fbFf0bPJjDXngnGocDTdFhygZjwUQrMNrDcmZlQT50VJ/g/UwNyHpu778+yW+/ksOz/BFo54P4AsUXMfRq7XWsAAAAASUVORK5CYII=', + 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAACMElEQVRYR+2Xv4pTQRTGf2dubhLdICiii2KnYKHVolhauKWPoGAnNr6BD6CvIVaihYuI2i1ia0BY0MZGRHQXjZj/mSPnnskfNWiWZUlzJ5k7M2cm833nO5Mziej2DWWJRUoCpQKlAntSQCqgw39/iUWAGmh37jrRnVsKlgpiqmkoGVABA7E57fvY+pJDdgKqF6HzFCSADkDq+F6AHABtQ+UMVE5D7zXod7fFNhTEckTbj5XQgHzNN+5tQvc5NG7C6BNkp6D3EmpXHDR+dQAjFLchW3VS9rlw3JBh+B7ys5Cf9z0GW1C/7P32AyBAOAz1q4jGliIH3YPuBnSfQX4OGreTIgEYQb/pBDtPnEQ4CivXYPAWBk13oHrB54yA9QuSn2H4AcKRpEILDt0BUzj+RLR1V5EqjD66NPRBVpLcQwjHoHYJOhsQv6U4mnzmrIXJCFr4LDwm/xBUoboG9XX4cc9VKdYoSA2yk5NQLJaKDUjTBoveG3Z2TElTxwjNK4M3LEZgUdDdruvcXzKBpStgp2NPiWi3ks9ZXxIoFVi+AvHLdc9TqtjL3/aYjpPlrzOcEnK62Szhimdd7xX232zFDTgtxezOu3WNMRLjiKgjtOhHVMd1loynVHvOgjuIIJMaELEqhJAV/RCSLbWTcfPFakFgFlALTRRvx+ok6Hlp/Q+v3fmx90bMyUzaEAhmM3KvHlXTL5DxnbGf/1M8RNNACLL5MNtPxP/mypJAqcDSFfgFhpYqWUzhTEAAAAAASUVORK5CYII=', + 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAL0lEQVRYR+3QQREAAAzCsOFfNJPBJ1XQS9r2hsUAAQIECBAgQIAAAQIECBAgsBZ4MUx/ofm2I/kAAAAASUVORK5CYII=', + 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAL0lEQVRYR+3QQREAAAzCsOFfNJPBJ1XQS9r2hsUAAQIECBAgQIAAAQIECBAgsBZ4MUx/ofm2I/kAAAAASUVORK5CYII=', + 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAL0lEQVRYR+3QQREAAAzCsOFfNJPBJ1XQS9r2hsUAAQIECBAgQIAAAQIECBAgsBZ4MUx/ofm2I/kAAAAASUVORK5CYII=', + 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAL0lEQVRYR+3QQREAAAzCsOFfNJPBJ1XQS9r2hsUAAQIECBAgQIAAAQIECBAgsBZ4MUx/ofm2I/kAAAAASUVORK5CYII=' +]; + +// Builders. They build Three.js object from Object data parsed by MMDParser. + +/** + * @param {LoadingManager} manager + */ +function MeshBuilder( manager ) { + + this.geometryBuilder = new GeometryBuilder(); + this.materialBuilder = new MaterialBuilder( manager ); + +} + +MeshBuilder.prototype = { + + constructor: MeshBuilder, + + crossOrigin: 'anonymous', + + /** + * @param {string} crossOrigin + * @return {MeshBuilder} + */ + setCrossOrigin: function ( crossOrigin ) { + + this.crossOrigin = crossOrigin; + return this; + + }, + + /** + * @param {Object} data - parsed PMD/PMX data + * @param {string} resourcePath + * @param {function} onProgress + * @param {function} onError + * @return {SkinnedMesh} + */ + build: function ( data, resourcePath, onProgress, onError ) { + + var geometry = this.geometryBuilder.build( data ); + var material = this.materialBuilder + .setCrossOrigin( this.crossOrigin ) + .setResourcePath( resourcePath ) + .build( data, geometry, onProgress, onError ); + + var mesh = new SkinnedMesh( geometry, material ); + + var skeleton = new Skeleton( initBones( mesh ) ); + mesh.bind( skeleton ); + + // console.log( mesh ); // for console debug + + return mesh; + + } + +}; + +// TODO: Try to remove this function + +function initBones( mesh ) { + + var geometry = mesh.geometry; + + var bones = [], bone, gbone; + var i, il; + + if ( geometry && geometry.bones !== undefined ) { + + // first, create array of 'Bone' objects from geometry data + + for ( i = 0, il = geometry.bones.length; i < il; i ++ ) { + + gbone = geometry.bones[ i ]; + + // create new 'Bone' object + + bone = new Bone(); + bones.push( bone ); + + // apply values + + bone.name = gbone.name; + bone.position.fromArray( gbone.pos ); + bone.quaternion.fromArray( gbone.rotq ); + if ( gbone.scl !== undefined ) bone.scale.fromArray( gbone.scl ); + + } + + // second, create bone hierarchy + + for ( i = 0, il = geometry.bones.length; i < il; i ++ ) { + + gbone = geometry.bones[ i ]; + + if ( ( gbone.parent !== - 1 ) && ( gbone.parent !== null ) && ( bones[ gbone.parent ] !== undefined ) ) { + + // subsequent bones in the hierarchy + + bones[ gbone.parent ].add( bones[ i ] ); + + } else { + + // topmost bone, immediate child of the skinned mesh + + mesh.add( bones[ i ] ); + + } + + } + + } + + // now the bones are part of the scene graph and children of the skinned mesh. + // let's update the corresponding matrices + + mesh.updateMatrixWorld( true ); + + return bones; + +} + +// + +function GeometryBuilder() { + +} + +GeometryBuilder.prototype = { + + constructor: GeometryBuilder, + + /** + * @param {Object} data - parsed PMD/PMX data + * @return {BufferGeometry} + */ + build: function ( data ) { + + // for geometry + var positions = []; + var uvs = []; + var normals = []; + + var indices = []; + + var groups = []; + + var bones = []; + var skinIndices = []; + var skinWeights = []; + + var morphTargets = []; + var morphPositions = []; + + var iks = []; + var grants = []; + + var rigidBodies = []; + var constraints = []; + + // for work + var offset = 0; + var boneTypeTable = {}; + + // positions, normals, uvs, skinIndices, skinWeights + + for ( var i = 0; i < data.metadata.vertexCount; i ++ ) { + + var v = data.vertices[ i ]; + + for ( var j = 0, jl = v.position.length; j < jl; j ++ ) { + + positions.push( v.position[ j ] ); + + } + + for ( var j = 0, jl = v.normal.length; j < jl; j ++ ) { + + normals.push( v.normal[ j ] ); + + } + + for ( var j = 0, jl = v.uv.length; j < jl; j ++ ) { + + uvs.push( v.uv[ j ] ); + + } + + for ( var j = 0; j < 4; j ++ ) { + + skinIndices.push( v.skinIndices.length - 1 >= j ? v.skinIndices[ j ] : 0.0 ); + + } + + for ( var j = 0; j < 4; j ++ ) { + + skinWeights.push( v.skinWeights.length - 1 >= j ? v.skinWeights[ j ] : 0.0 ); + + } + + } + + // indices + + for ( var i = 0; i < data.metadata.faceCount; i ++ ) { + + var face = data.faces[ i ]; + + for ( var j = 0, jl = face.indices.length; j < jl; j ++ ) { + + indices.push( face.indices[ j ] ); + + } + + } + + // groups + + for ( var i = 0; i < data.metadata.materialCount; i ++ ) { + + var material = data.materials[ i ]; + + groups.push( { + offset: offset * 3, + count: material.faceCount * 3 + } ); + + offset += material.faceCount; + + } + + // bones + + for ( var i = 0; i < data.metadata.rigidBodyCount; i ++ ) { + + var body = data.rigidBodies[ i ]; + var value = boneTypeTable[ body.boneIndex ]; + + // keeps greater number if already value is set without any special reasons + value = value === undefined ? body.type : Math.max( body.type, value ); + + boneTypeTable[ body.boneIndex ] = value; + + } + + for ( var i = 0; i < data.metadata.boneCount; i ++ ) { + + var boneData = data.bones[ i ]; + + var bone = { + parent: boneData.parentIndex, + name: boneData.name, + pos: boneData.position.slice( 0, 3 ), + rotq: [ 0, 0, 0, 1 ], + scl: [ 1, 1, 1 ], + rigidBodyType: boneTypeTable[ i ] !== undefined ? boneTypeTable[ i ] : - 1 + }; + + if ( bone.parent !== - 1 ) { + + bone.pos[ 0 ] -= data.bones[ bone.parent ].position[ 0 ]; + bone.pos[ 1 ] -= data.bones[ bone.parent ].position[ 1 ]; + bone.pos[ 2 ] -= data.bones[ bone.parent ].position[ 2 ]; + + } + + bones.push( bone ); + + } + + // iks + + // TODO: remove duplicated codes between PMD and PMX + if ( data.metadata.format === 'pmd' ) { + + for ( var i = 0; i < data.metadata.ikCount; i ++ ) { + + var ik = data.iks[ i ]; + + var param = { + target: ik.target, + effector: ik.effector, + iteration: ik.iteration, + maxAngle: ik.maxAngle * 4, + links: [] + }; + + for ( var j = 0, jl = ik.links.length; j < jl; j ++ ) { + + var link = {}; + link.index = ik.links[ j ].index; + link.enabled = true; + + if ( data.bones[ link.index ].name.indexOf( 'ひざ' ) >= 0 ) { + + link.limitation = new Vector3( 1.0, 0.0, 0.0 ); + + } + + param.links.push( link ); + + } + + iks.push( param ); + + } + + } else { + + for ( var i = 0; i < data.metadata.boneCount; i ++ ) { + + var ik = data.bones[ i ].ik; + + if ( ik === undefined ) continue; + + var param = { + target: i, + effector: ik.effector, + iteration: ik.iteration, + maxAngle: ik.maxAngle, + links: [] + }; + + for ( var j = 0, jl = ik.links.length; j < jl; j ++ ) { + + var link = {}; + link.index = ik.links[ j ].index; + link.enabled = true; + + if ( ik.links[ j ].angleLimitation === 1 ) { + + // Revert if rotationMin/Max doesn't work well + // link.limitation = new THREE.Vector3( 1.0, 0.0, 0.0 ); + + var rotationMin = ik.links[ j ].lowerLimitationAngle; + var rotationMax = ik.links[ j ].upperLimitationAngle; + + // Convert Left to Right coordinate by myself because + // MMDParser doesn't convert. It's a MMDParser's bug + + var tmp1 = - rotationMax[ 0 ]; + var tmp2 = - rotationMax[ 1 ]; + rotationMax[ 0 ] = - rotationMin[ 0 ]; + rotationMax[ 1 ] = - rotationMin[ 1 ]; + rotationMin[ 0 ] = tmp1; + rotationMin[ 1 ] = tmp2; + + link.rotationMin = new Vector3().fromArray( rotationMin ); + link.rotationMax = new Vector3().fromArray( rotationMax ); + + } + + param.links.push( link ); + + } + + iks.push( param ); + + } + + } + + // grants + + if ( data.metadata.format === 'pmx' ) { + + for ( var i = 0; i < data.metadata.boneCount; i ++ ) { + + var boneData = data.bones[ i ]; + var grant = boneData.grant; + + if ( grant === undefined ) continue; + + var param = { + index: i, + parentIndex: grant.parentIndex, + ratio: grant.ratio, + isLocal: grant.isLocal, + affectRotation: grant.affectRotation, + affectPosition: grant.affectPosition, + transformationClass: boneData.transformationClass + }; + + grants.push( param ); + + } + + grants.sort( function ( a, b ) { + + return a.transformationClass - b.transformationClass; + + } ); + + } + + // morph + + function updateAttributes( attribute, morph, ratio ) { + + for ( var i = 0; i < morph.elementCount; i ++ ) { + + var element = morph.elements[ i ]; + + var index; + + if ( data.metadata.format === 'pmd' ) { + + index = data.morphs[ 0 ].elements[ element.index ].index; + + } else { + + index = element.index; + + } + + attribute.array[ index * 3 + 0 ] += element.position[ 0 ] * ratio; + attribute.array[ index * 3 + 1 ] += element.position[ 1 ] * ratio; + attribute.array[ index * 3 + 2 ] += element.position[ 2 ] * ratio; + + } + + } + + for ( var i = 0; i < data.metadata.morphCount; i ++ ) { + + var morph = data.morphs[ i ]; + var params = { name: morph.name }; + + var attribute = new Float32BufferAttribute( data.metadata.vertexCount * 3, 3 ); + attribute.name = morph.name; + + for ( var j = 0; j < data.metadata.vertexCount * 3; j ++ ) { + + attribute.array[ j ] = positions[ j ]; + + } + + if ( data.metadata.format === 'pmd' ) { + + if ( i !== 0 ) { + + updateAttributes( attribute, morph, 1.0 ); + + } + + } else { + + if ( morph.type === 0 ) { // group + + for ( var j = 0; j < morph.elementCount; j ++ ) { + + var morph2 = data.morphs[ morph.elements[ j ].index ]; + var ratio = morph.elements[ j ].ratio; + + if ( morph2.type === 1 ) { + + updateAttributes( attribute, morph2, ratio ); + + } else { + + // TODO: implement + + } + + } + + } else if ( morph.type === 1 ) { // vertex + + updateAttributes( attribute, morph, 1.0 ); + + } else if ( morph.type === 2 ) { // bone + + // TODO: implement + + } else if ( morph.type === 3 ) { // uv + + // TODO: implement + + } else if ( morph.type === 4 ) { // additional uv1 + + // TODO: implement + + } else if ( morph.type === 5 ) { // additional uv2 + + // TODO: implement + + } else if ( morph.type === 6 ) { // additional uv3 + + // TODO: implement + + } else if ( morph.type === 7 ) { // additional uv4 + + // TODO: implement + + } else if ( morph.type === 8 ) { // material + + // TODO: implement + + } + + } + + morphTargets.push( params ); + morphPositions.push( attribute ); + + } + + // rigid bodies from rigidBodies field. + + for ( var i = 0; i < data.metadata.rigidBodyCount; i ++ ) { + + var rigidBody = data.rigidBodies[ i ]; + var params = {}; + + for ( var key in rigidBody ) { + + params[ key ] = rigidBody[ key ]; + + } + + /* + * RigidBody position parameter in PMX seems global position + * while the one in PMD seems offset from corresponding bone. + * So unify being offset. + */ + if ( data.metadata.format === 'pmx' ) { + + if ( params.boneIndex !== - 1 ) { + + var bone = data.bones[ params.boneIndex ]; + params.position[ 0 ] -= bone.position[ 0 ]; + params.position[ 1 ] -= bone.position[ 1 ]; + params.position[ 2 ] -= bone.position[ 2 ]; + + } + + } + + rigidBodies.push( params ); + + } + + // constraints from constraints field. + + for ( var i = 0; i < data.metadata.constraintCount; i ++ ) { + + var constraint = data.constraints[ i ]; + var params = {}; + + for ( var key in constraint ) { + + params[ key ] = constraint[ key ]; + + } + + var bodyA = rigidBodies[ params.rigidBodyIndex1 ]; + var bodyB = rigidBodies[ params.rigidBodyIndex2 ]; + + // Refer to http://www20.atpages.jp/katwat/wp/?p=4135 + if ( bodyA.type !== 0 && bodyB.type === 2 ) { + + if ( bodyA.boneIndex !== - 1 && bodyB.boneIndex !== - 1 && + data.bones[ bodyB.boneIndex ].parentIndex === bodyA.boneIndex ) { + + bodyB.type = 1; + + } + + } + + constraints.push( params ); + + } + + // build BufferGeometry. + + var geometry = new BufferGeometry(); + + geometry.addAttribute( 'position', new Float32BufferAttribute( positions, 3 ) ); + geometry.addAttribute( 'normal', new Float32BufferAttribute( normals, 3 ) ); + geometry.addAttribute( 'uv', new Float32BufferAttribute( uvs, 2 ) ); + geometry.addAttribute( 'skinIndex', new Uint16BufferAttribute( skinIndices, 4 ) ); + geometry.addAttribute( 'skinWeight', new Float32BufferAttribute( skinWeights, 4 ) ); + geometry.setIndex( indices ); + + for ( var i = 0, il = groups.length; i < il; i ++ ) { + + geometry.addGroup( groups[ i ].offset, groups[ i ].count, i ); + + } + + geometry.bones = bones; + + geometry.morphTargets = morphTargets; + geometry.morphAttributes.position = morphPositions; + + geometry.userData.MMD = { + bones: bones, + iks: iks, + grants: grants, + rigidBodies: rigidBodies, + constraints: constraints, + format: data.metadata.format + }; + + geometry.computeBoundingSphere(); + + return geometry; + + } + +}; + +// + +/** + * @param {THREE.LoadingManager} manager + */ +function MaterialBuilder( manager ) { + + this.manager = manager; + + this.textureLoader = new TextureLoader( this.manager ); + this.tgaLoader = null; // lazy generation + +} + +MaterialBuilder.prototype = { + + constructor: MaterialBuilder, + + crossOrigin: 'anonymous', + + resourcePath: undefined, + + /** + * @param {string} crossOrigin + * @return {MaterialBuilder} + */ + setCrossOrigin: function ( crossOrigin ) { + + this.crossOrigin = crossOrigin; + return this; + + }, + + /** + * @param {string} resourcePath + * @return {MaterialBuilder} + */ + setResourcePath: function ( resourcePath ) { + + this.resourcePath = resourcePath; + return this; + + }, + + /** + * @param {Object} data - parsed PMD/PMX data + * @param {THREE.BufferGeometry} geometry - some properties are dependend on geometry + * @param {function} onProgress + * @param {function} onError + * @return {Array} + */ + build: function ( data, geometry, onProgress, onError ) { + + var materials = []; + + var textures = {}; + + this.textureLoader.setCrossOrigin( this.crossOrigin ); + + // materials + + for ( var i = 0; i < data.metadata.materialCount; i ++ ) { + + var material = data.materials[ i ]; + + var params = { userData: {} }; + + if ( material.name !== undefined ) params.name = material.name; + + /* + * Color + * + * MMD MeshToonMaterial + * diffuse - color + * specular - specular + * ambient - emissive * a + * (a = 1.0 without map texture or 0.2 with map texture) + * + * MeshToonMaterial doesn't have ambient. Set it to emissive instead. + * It'll be too bright if material has map texture so using coef 0.2. + */ + params.color = new Color().fromArray( material.diffuse ); + params.opacity = material.diffuse[ 3 ]; + params.specular = new Color().fromArray( material.specular ); + params.emissive = new Color().fromArray( material.ambient ); + params.shininess = Math.max( material.shininess, 1e-4 ); // to prevent pow( 0.0, 0.0 ) + params.transparent = params.opacity !== 1.0; + + // + + params.skinning = geometry.bones.length > 0 ? true : false; + params.morphTargets = geometry.morphTargets.length > 0 ? true : false; + params.lights = true; + params.fog = true; + + // blend + + params.blending = CustomBlending; + params.blendSrc = SrcAlphaFactor; + params.blendDst = OneMinusSrcAlphaFactor; + params.blendSrcAlpha = SrcAlphaFactor; + params.blendDstAlpha = DstAlphaFactor; + + // side + + if ( data.metadata.format === 'pmx' && ( material.flag & 0x1 ) === 1 ) { + + params.side = DoubleSide; + + } else { + + params.side = params.opacity === 1.0 ? FrontSide : DoubleSide; + + } + + if ( data.metadata.format === 'pmd' ) { + + // map, envMap + + if ( material.fileName ) { + + var fileName = material.fileName; + var fileNames = fileName.split( '*' ); + + // fileNames[ 0 ]: mapFileName + // fileNames[ 1 ]: envMapFileName( optional ) + + params.map = this._loadTexture( fileNames[ 0 ], textures ); + + if ( fileNames.length > 1 ) { + + var extension = fileNames[ 1 ].slice( - 4 ).toLowerCase(); + + params.envMap = this._loadTexture( + fileNames[ 1 ], + textures, + { sphericalReflectionMapping: true } + ); + + params.combine = extension === '.sph' + ? MultiplyOperation + : AddOperation; + + } + + } + + // gradientMap + + var toonFileName = ( material.toonIndex === - 1 ) + ? 'toon00.bmp' + : data.toonTextures[ material.toonIndex ].fileName; + + params.gradientMap = this._loadTexture( + toonFileName, + textures, + { + isToonTexture: true, + isDefaultToonTexture: this._isDefaultToonTexture( toonFileName ) + } + ); + + // parameters for OutlineEffect + + params.userData.outlineParameters = { + thickness: material.edgeFlag === 1 ? 0.003 : 0.0, + color: [ 0, 0, 0 ], + alpha: 1.0, + visible: material.edgeFlag === 1 + }; + + } else { + + // map + + if ( material.textureIndex !== - 1 ) { + + params.map = this._loadTexture( data.textures[ material.textureIndex ], textures ); + + } + + // envMap TODO: support m.envFlag === 3 + + if ( material.envTextureIndex !== - 1 && ( material.envFlag === 1 || material.envFlag == 2 ) ) { + + params.envMap = this._loadTexture( + data.textures[ material.envTextureIndex ], + textures, { sphericalReflectionMapping: true } + ); + + params.combine = material.envFlag === 1 + ? MultiplyOperation + : AddOperation; + + } + + // gradientMap + + var toonFileName, isDefaultToon; + + if ( material.toonIndex === - 1 || material.toonFlag !== 0 ) { + + toonFileName = 'toon' + ( '0' + ( material.toonIndex + 1 ) ).slice( - 2 ) + '.bmp'; + isDefaultToon = true; + + } else { + + toonFileName = data.textures[ material.toonIndex ]; + isDefaultToon = false; + + } + + params.gradientMap = this._loadTexture( + toonFileName, + textures, + { + isToonTexture: true, + isDefaultToonTexture: isDefaultToon + } + ); + + // parameters for OutlineEffect + params.userData.outlineParameters = { + thickness: material.edgeSize / 300, // TODO: better calculation? + color: material.edgeColor.slice( 0, 3 ), + alpha: material.edgeColor[ 3 ], + visible: ( material.flag & 0x10 ) !== 0 && material.edgeSize > 0.0 + }; + + } + + if ( params.map !== undefined ) { + + if ( ! params.transparent ) { + + this._checkImageTransparency( params.map, geometry, i ); + + } + + params.emissive.multiplyScalar( 0.2 ); + + } + + materials.push( new MeshToonMaterial( params ) ); + + } + + if ( data.metadata.format === 'pmx' ) { + + // set transparent true if alpha morph is defined. + + function checkAlphaMorph( elements, materials ) { + + for ( var i = 0, il = elements.length; i < il; i ++ ) { + + var element = elements[ i ]; + + if ( element.index === - 1 ) continue; + + var material = materials[ element.index ]; + + if ( material.opacity !== element.diffuse[ 3 ] ) { + + material.transparent = true; + + } + + } + + } + + for ( var i = 0, il = data.morphs.length; i < il; i ++ ) { + + var morph = data.morphs[ i ]; + var elements = morph.elements; + + if ( morph.type === 0 ) { + + for ( var j = 0, jl = elements.length; j < jl; j ++ ) { + + var morph2 = data.morphs[ elements[ j ].index ]; + + if ( morph2.type !== 8 ) continue; + + checkAlphaMorph( morph2.elements, materials ); + + } + + } else if ( morph.type === 8 ) { + + checkAlphaMorph( elements, materials ); + + } + + } + + } + + return materials; + + }, + + // private methods + + _getTGALoader: function () { + + if ( this.tgaLoader === null ) { + + if ( TGALoader === undefined ) { + + throw new Error( 'THREE.MMDLoader: Import THREE.TGALoader' ); + + } + + this.tgaLoader = new TGALoader( this.manager ); + + } + + return this.tgaLoader; + + }, + + _isDefaultToonTexture: function ( name ) { + + if ( name.length !== 10 ) return false; + + return /toon(10|0[0-9])\.bmp/.test( name ); + + }, + + _loadTexture: function ( filePath, textures, params, onProgress, onError ) { + + params = params || {}; + + var scope = this; + + var fullPath; + + if ( params.isDefaultToonTexture === true ) { + + var index; + + try { + + index = parseInt( filePath.match( 'toon([0-9]{2})\.bmp$' )[ 1 ] ); + + } catch ( e ) { + + console.warn( 'THREE.MMDLoader: ' + filePath + ' seems like a ' + + 'not right default texture path. Using toon00.bmp instead.' ); + + index = 0; + + } + + fullPath = DEFAULT_TOON_TEXTURES[ index ]; + + } else { + + fullPath = this.resourcePath + filePath; + + } + + if ( textures[ fullPath ] !== undefined ) return textures[ fullPath ]; + + var loader = Loader.Handlers.get( fullPath ); + + if ( loader === null ) { + + loader = ( filePath.slice( - 4 ).toLowerCase() === '.tga' ) + ? this._getTGALoader() + : this.textureLoader; + + } + + var texture = loader.load( fullPath, function ( t ) { + + // MMD toon texture is Axis-Y oriented + // but Three.js gradient map is Axis-X oriented. + // So here replaces the toon texture image with the rotated one. + if ( params.isToonTexture === true ) { + + t.image = scope._getRotatedImage( t.image ); + + } + + t.flipY = false; + t.wrapS = RepeatWrapping; + t.wrapT = RepeatWrapping; + + for ( var i = 0; i < texture.readyCallbacks.length; i ++ ) { + + texture.readyCallbacks[ i ]( texture ); + + } + + delete texture.readyCallbacks; + + }, onProgress, onError ); + + if ( params.sphericalReflectionMapping === true ) { + + texture.mapping = SphericalReflectionMapping; + + } + + texture.readyCallbacks = []; + + textures[ fullPath ] = texture; + + return texture; + + }, + + _getRotatedImage: function ( image ) { + + var canvas = document.createElement( 'canvas' ); + var context = canvas.getContext( '2d' ); + + var width = image.width; + var height = image.height; + + canvas.width = width; + canvas.height = height; + + context.clearRect( 0, 0, width, height ); + context.translate( width / 2.0, height / 2.0 ); + context.rotate( 0.5 * Math.PI ); // 90.0 * Math.PI / 180.0 + context.translate( - width / 2.0, - height / 2.0 ); + context.drawImage( image, 0, 0 ); + + return context.getImageData( 0, 0, width, height ); + + }, + + // Check if the partial image area used by the texture is transparent. + _checkImageTransparency: function ( map, geometry, groupIndex ) { + + map.readyCallbacks.push( function ( texture ) { + + // Is there any efficient ways? + function createImageData( image ) { + + var canvas = document.createElement( 'canvas' ); + canvas.width = image.width; + canvas.height = image.height; + + var context = canvas.getContext( '2d' ); + context.drawImage( image, 0, 0 ); + + return context.getImageData( 0, 0, canvas.width, canvas.height ); + + } + + function detectImageTransparency( image, uvs, indices ) { + + var width = image.width; + var height = image.height; + var data = image.data; + var threshold = 253; + + if ( data.length / ( width * height ) !== 4 ) return false; + + for ( var i = 0; i < indices.length; i += 3 ) { + + var centerUV = { x: 0.0, y: 0.0 }; + + for ( var j = 0; j < 3; j ++ ) { + + var index = indices[ i * 3 + j ]; + var uv = { x: uvs[ index * 2 + 0 ], y: uvs[ index * 2 + 1 ] }; + + if ( getAlphaByUv( image, uv ) < threshold ) return true; + + centerUV.x += uv.x; + centerUV.y += uv.y; + + } + + centerUV.x /= 3; + centerUV.y /= 3; + + if ( getAlphaByUv( image, centerUV ) < threshold ) return true; + + } + + return false; + + } + + /* + * This method expects + * texture.flipY = false + * texture.wrapS = THREE.RepeatWrapping + * texture.wrapT = THREE.RepeatWrapping + * TODO: more precise + */ + function getAlphaByUv( image, uv ) { + + var width = image.width; + var height = image.height; + + var x = Math.round( uv.x * width ) % width; + var y = Math.round( uv.y * height ) % height; + + if ( x < 0 ) x += width; + if ( y < 0 ) y += height; + + var index = y * width + x; + + return image.data[ index * 4 + 3 ]; + + } + + var imageData = texture.image.data !== undefined + ? texture.image + : createImageData( texture.image ); + + var group = geometry.groups[ groupIndex ]; + + if ( detectImageTransparency( + imageData, + geometry.attributes.uv.array, + geometry.index.array.slice( group.start, group.start + group.count ) ) ) { + + map.transparent = true; + + } + + } ); + + } + +}; + +// + +function AnimationBuilder() { + +} + +AnimationBuilder.prototype = { + + constructor: AnimationBuilder, + + /** + * @param {Object} vmd - parsed VMD data + * @param {SkinnedMesh} mesh - tracks will be fitting to mesh + * @return {AnimationClip} + */ + build: function ( vmd, mesh ) { + + // combine skeletal and morph animations + + var tracks = this.buildSkeletalAnimation( vmd, mesh ).tracks; + var tracks2 = this.buildMorphAnimation( vmd, mesh ).tracks; + + for ( var i = 0, il = tracks2.length; i < il; i ++ ) { + + tracks.push( tracks2[ i ] ); + + } + + return new AnimationClip( '', - 1, tracks ); + + }, + + /** + * @param {Object} vmd - parsed VMD data + * @param {SkinnedMesh} mesh - tracks will be fitting to mesh + * @return {AnimationClip} + */ + buildSkeletalAnimation: function ( vmd, mesh ) { + + function pushInterpolation( array, interpolation, index ) { + + array.push( interpolation[ index + 0 ] / 127 ); // x1 + array.push( interpolation[ index + 8 ] / 127 ); // x2 + array.push( interpolation[ index + 4 ] / 127 ); // y1 + array.push( interpolation[ index + 12 ] / 127 ); // y2 + + } + + var tracks = []; + + var motions = {}; + var bones = mesh.skeleton.bones; + var boneNameDictionary = {}; + + for ( var i = 0, il = bones.length; i < il; i ++ ) { + + boneNameDictionary[ bones[ i ].name ] = true; + + } + + for ( var i = 0; i < vmd.metadata.motionCount; i ++ ) { + + var motion = vmd.motions[ i ]; + var boneName = motion.boneName; + + if ( boneNameDictionary[ boneName ] === undefined ) continue; + + motions[ boneName ] = motions[ boneName ] || []; + motions[ boneName ].push( motion ); + + } + + for ( var key in motions ) { + + var array = motions[ key ]; + + array.sort( function ( a, b ) { + + return a.frameNum - b.frameNum; + + } ); + + var times = []; + var positions = []; + var rotations = []; + var pInterpolations = []; + var rInterpolations = []; + + var basePosition = mesh.skeleton.getBoneByName( key ).position.toArray(); + + for ( var i = 0, il = array.length; i < il; i ++ ) { + + var time = array[ i ].frameNum / 30; + var position = array[ i ].position; + var rotation = array[ i ].rotation; + var interpolation = array[ i ].interpolation; + + times.push( time ); + + for ( var j = 0; j < 3; j ++ ) positions.push( basePosition[ j ] + position[ j ] ); + for ( var j = 0; j < 4; j ++ ) rotations.push( rotation[ j ] ); + for ( var j = 0; j < 3; j ++ ) pushInterpolation( pInterpolations, interpolation, j ); + + pushInterpolation( rInterpolations, interpolation, 3 ); + + } + + var targetName = '.bones[' + key + ']'; + + tracks.push( this._createTrack( targetName + '.position', VectorKeyframeTrack, times, positions, pInterpolations ) ); + tracks.push( this._createTrack( targetName + '.quaternion', QuaternionKeyframeTrack, times, rotations, rInterpolations ) ); + + } + + return new AnimationClip( '', - 1, tracks ); + + }, + + /** + * @param {Object} vmd - parsed VMD data + * @param {SkinnedMesh} mesh - tracks will be fitting to mesh + * @return {AnimationClip} + */ + buildMorphAnimation: function ( vmd, mesh ) { + + var tracks = []; + + var morphs = {}; + var morphTargetDictionary = mesh.morphTargetDictionary; + + for ( var i = 0; i < vmd.metadata.morphCount; i ++ ) { + + var morph = vmd.morphs[ i ]; + var morphName = morph.morphName; + + if ( morphTargetDictionary[ morphName ] === undefined ) continue; + + morphs[ morphName ] = morphs[ morphName ] || []; + morphs[ morphName ].push( morph ); + + } + + for ( var key in morphs ) { + + var array = morphs[ key ]; + + array.sort( function ( a, b ) { + + return a.frameNum - b.frameNum; + + } ); + + var times = []; + var values = []; + + for ( var i = 0, il = array.length; i < il; i ++ ) { + + times.push( array[ i ].frameNum / 30 ); + values.push( array[ i ].weight ); + + } + + tracks.push( new NumberKeyframeTrack( '.morphTargetInfluences[' + morphTargetDictionary[ key ] + ']', times, values ) ); + + } + + return new AnimationClip( '', - 1, tracks ); + + }, + + /** + * @param {Object} vmd - parsed VMD data + * @return {AnimationClip} + */ + buildCameraAnimation: function ( vmd ) { + + function pushVector3( array, vec ) { + + array.push( vec.x ); + array.push( vec.y ); + array.push( vec.z ); + + } + + function pushQuaternion( array, q ) { + + array.push( q.x ); + array.push( q.y ); + array.push( q.z ); + array.push( q.w ); + + } + + function pushInterpolation( array, interpolation, index ) { + + array.push( interpolation[ index * 4 + 0 ] / 127 ); // x1 + array.push( interpolation[ index * 4 + 1 ] / 127 ); // x2 + array.push( interpolation[ index * 4 + 2 ] / 127 ); // y1 + array.push( interpolation[ index * 4 + 3 ] / 127 ); // y2 + + } + + var tracks = []; + + var cameras = vmd.cameras === undefined ? [] : vmd.cameras.slice(); + + cameras.sort( function ( a, b ) { + + return a.frameNum - b.frameNum; + + } ); + + var times = []; + var centers = []; + var quaternions = []; + var positions = []; + var fovs = []; + + var cInterpolations = []; + var qInterpolations = []; + var pInterpolations = []; + var fInterpolations = []; + + var quaternion = new Quaternion(); + var euler = new Euler(); + var position = new Vector3(); + var center = new Vector3(); + + for ( var i = 0, il = cameras.length; i < il; i ++ ) { + + var motion = cameras[ i ]; + + var time = motion.frameNum / 30; + var pos = motion.position; + var rot = motion.rotation; + var distance = motion.distance; + var fov = motion.fov; + var interpolation = motion.interpolation; + + times.push( time ); + + position.set( 0, 0, - distance ); + center.set( pos[ 0 ], pos[ 1 ], pos[ 2 ] ); + + euler.set( - rot[ 0 ], - rot[ 1 ], - rot[ 2 ] ); + quaternion.setFromEuler( euler ); + + position.add( center ); + position.applyQuaternion( quaternion ); + + pushVector3( centers, center ); + pushQuaternion( quaternions, quaternion ); + pushVector3( positions, position ); + + fovs.push( fov ); + + for ( var j = 0; j < 3; j ++ ) { + + pushInterpolation( cInterpolations, interpolation, j ); + + } + + pushInterpolation( qInterpolations, interpolation, 3 ); + + // use the same parameter for x, y, z axis. + for ( var j = 0; j < 3; j ++ ) { + + pushInterpolation( pInterpolations, interpolation, 4 ); + + } + + pushInterpolation( fInterpolations, interpolation, 5 ); + + } + + var tracks = []; + + // I expect an object whose name 'target' exists under THREE.Camera + tracks.push( this._createTrack( 'target.position', VectorKeyframeTrack, times, centers, cInterpolations ) ); + + tracks.push( this._createTrack( '.quaternion', QuaternionKeyframeTrack, times, quaternions, qInterpolations ) ); + tracks.push( this._createTrack( '.position', VectorKeyframeTrack, times, positions, pInterpolations ) ); + tracks.push( this._createTrack( '.fov', NumberKeyframeTrack, times, fovs, fInterpolations ) ); + + return new AnimationClip( '', - 1, tracks ); + + }, + + // private method + + _createTrack: function ( node, typedKeyframeTrack, times, values, interpolations ) { + + /* + * optimizes here not to let KeyframeTrackPrototype optimize + * because KeyframeTrackPrototype optimizes times and values but + * doesn't optimize interpolations. + */ + if ( times.length > 2 ) { + + times = times.slice(); + values = values.slice(); + interpolations = interpolations.slice(); + + var stride = values.length / times.length; + var interpolateStride = interpolations.length / times.length; + + var index = 1; + + for ( var aheadIndex = 2, endIndex = times.length; aheadIndex < endIndex; aheadIndex ++ ) { + + for ( var i = 0; i < stride; i ++ ) { + + if ( values[ index * stride + i ] !== values[ ( index - 1 ) * stride + i ] || + values[ index * stride + i ] !== values[ aheadIndex * stride + i ] ) { + + index ++; + break; + + } + + } + + if ( aheadIndex > index ) { + + times[ index ] = times[ aheadIndex ]; + + for ( var i = 0; i < stride; i ++ ) { + + values[ index * stride + i ] = values[ aheadIndex * stride + i ]; + + } + + for ( var i = 0; i < interpolateStride; i ++ ) { + + interpolations[ index * interpolateStride + i ] = interpolations[ aheadIndex * interpolateStride + i ]; + + } + + } + + } + + times.length = index + 1; + values.length = ( index + 1 ) * stride; + interpolations.length = ( index + 1 ) * interpolateStride; + + } + + var track = new typedKeyframeTrack( node, times, values ); + + track.createInterpolant = function InterpolantFactoryMethodCubicBezier( result ) { + + return new CubicBezierInterpolation( this.times, this.values, this.getValueSize(), result, new Float32Array( interpolations ) ); + + }; + + return track; + + } + +}; + +// interpolation + +function CubicBezierInterpolation( parameterPositions, sampleValues, sampleSize, resultBuffer, params ) { + + Interpolant.call( this, parameterPositions, sampleValues, sampleSize, resultBuffer ); + + this.interpolationParams = params; + +} + +CubicBezierInterpolation.prototype = Object.assign( Object.create( Interpolant.prototype ), { + + constructor: CubicBezierInterpolation, + + interpolate_: function ( i1, t0, t, t1 ) { + + var result = this.resultBuffer; + var values = this.sampleValues; + var stride = this.valueSize; + var params = this.interpolationParams; + + var offset1 = i1 * stride; + var offset0 = offset1 - stride; + + // No interpolation if next key frame is in one frame in 30fps. + // This is from MMD animation spec. + // '1.5' is for precision loss. times are Float32 in Three.js Animation system. + var weight1 = ( ( t1 - t0 ) < 1 / 30 * 1.5 ) ? 0.0 : ( t - t0 ) / ( t1 - t0 ); + + if ( stride === 4 ) { // Quaternion + + var x1 = params[ i1 * 4 + 0 ]; + var x2 = params[ i1 * 4 + 1 ]; + var y1 = params[ i1 * 4 + 2 ]; + var y2 = params[ i1 * 4 + 3 ]; + + var ratio = this._calculate( x1, x2, y1, y2, weight1 ); + + Quaternion.slerpFlat( result, 0, values, offset0, values, offset1, ratio ); + + } else if ( stride === 3 ) { // Vector3 + + for ( var i = 0; i !== stride; ++ i ) { + + var x1 = params[ i1 * 12 + i * 4 + 0 ]; + var x2 = params[ i1 * 12 + i * 4 + 1 ]; + var y1 = params[ i1 * 12 + i * 4 + 2 ]; + var y2 = params[ i1 * 12 + i * 4 + 3 ]; + + var ratio = this._calculate( x1, x2, y1, y2, weight1 ); + + result[ i ] = values[ offset0 + i ] * ( 1 - ratio ) + values[ offset1 + i ] * ratio; + + } + + } else { // Number + + var x1 = params[ i1 * 4 + 0 ]; + var x2 = params[ i1 * 4 + 1 ]; + var y1 = params[ i1 * 4 + 2 ]; + var y2 = params[ i1 * 4 + 3 ]; + + var ratio = this._calculate( x1, x2, y1, y2, weight1 ); + + result[ 0 ] = values[ offset0 ] * ( 1 - ratio ) + values[ offset1 ] * ratio; + + } + + return result; + + }, + + _calculate: function ( x1, x2, y1, y2, x ) { + + /* + * Cubic Bezier curves + * https://en.wikipedia.org/wiki/B%C3%A9zier_curve#Cubic_B.C3.A9zier_curves + * + * B(t) = ( 1 - t ) ^ 3 * P0 + * + 3 * ( 1 - t ) ^ 2 * t * P1 + * + 3 * ( 1 - t ) * t^2 * P2 + * + t ^ 3 * P3 + * ( 0 <= t <= 1 ) + * + * MMD uses Cubic Bezier curves for bone and camera animation interpolation. + * http://d.hatena.ne.jp/edvakf/20111016/1318716097 + * + * x = ( 1 - t ) ^ 3 * x0 + * + 3 * ( 1 - t ) ^ 2 * t * x1 + * + 3 * ( 1 - t ) * t^2 * x2 + * + t ^ 3 * x3 + * y = ( 1 - t ) ^ 3 * y0 + * + 3 * ( 1 - t ) ^ 2 * t * y1 + * + 3 * ( 1 - t ) * t^2 * y2 + * + t ^ 3 * y3 + * ( x0 = 0, y0 = 0 ) + * ( x3 = 1, y3 = 1 ) + * ( 0 <= t, x1, x2, y1, y2 <= 1 ) + * + * Here solves this equation with Bisection method, + * https://en.wikipedia.org/wiki/Bisection_method + * gets t, and then calculate y. + * + * f(t) = 3 * ( 1 - t ) ^ 2 * t * x1 + * + 3 * ( 1 - t ) * t^2 * x2 + * + t ^ 3 - x = 0 + * + * (Another option: Newton's method + * https://en.wikipedia.org/wiki/Newton%27s_method) + */ + + var c = 0.5; + var t = c; + var s = 1.0 - t; + var loop = 15; + var eps = 1e-5; + var math = Math; + + var sst3, stt3, ttt; + + for ( var i = 0; i < loop; i ++ ) { + + sst3 = 3.0 * s * s * t; + stt3 = 3.0 * s * t * t; + ttt = t * t * t; + + var ft = ( sst3 * x1 ) + ( stt3 * x2 ) + ( ttt ) - x; + + if ( math.abs( ft ) < eps ) break; + + c /= 2.0; + + t += ( ft < 0 ) ? c : - c; + s = 1.0 - t; + + } + + return ( sst3 * y1 ) + ( stt3 * y2 ) + ttt; + + } + +} ); + +export { MMDLoader }; diff --git a/examples/jsm/loaders/MTLLoader.js b/examples/jsm/loaders/MTLLoader.js new file mode 100644 index 00000000000000..8edeaaef77a335 --- /dev/null +++ b/examples/jsm/loaders/MTLLoader.js @@ -0,0 +1,597 @@ +/** + * Loads a Wavefront .mtl file specifying materials + * + * @author angelxuanchang + */ + +import { + Color, + DefaultLoadingManager, + FileLoader, + FrontSide, + LoaderUtils, + MeshPhongMaterial, + RepeatWrapping, + TextureLoader, + Vector2, +} from "../../../build/three.module.js"; + +var MTLLoader = function ( manager ) { + + this.manager = ( manager !== undefined ) ? manager : DefaultLoadingManager; + +}; + +MTLLoader.prototype = { + + constructor: MTLLoader, + + /** + * Loads and parses a MTL asset from a URL. + * + * @param {String} url - URL to the MTL file. + * @param {Function} [onLoad] - Callback invoked with the loaded object. + * @param {Function} [onProgress] - Callback for download progress. + * @param {Function} [onError] - Callback for download errors. + * + * @see setPath setResourcePath + * + * @note In order for relative texture references to resolve correctly + * you must call setResourcePath() explicitly prior to load. + */ + load: function ( url, onLoad, onProgress, onError ) { + + var scope = this; + + var path = ( this.path === undefined ) ? LoaderUtils.extractUrlBase( url ) : this.path; + + var loader = new FileLoader( this.manager ); + loader.setPath( this.path ); + loader.load( url, function ( text ) { + + onLoad( scope.parse( text, path ) ); + + }, onProgress, onError ); + + }, + + /** + * Set base path for resolving references. + * If set this path will be prepended to each loaded and found reference. + * + * @see setResourcePath + * @param {String} path + * @return {MTLLoader} + * + * @example + * mtlLoader.setPath( 'assets/obj/' ); + * mtlLoader.load( 'my.mtl', ... ); + */ + setPath: function ( path ) { + + this.path = path; + return this; + + }, + + /** + * Set base path for additional resources like textures. + * + * @see setPath + * @param {String} path + * @return {MTLLoader} + * + * @example + * mtlLoader.setPath( 'assets/obj/' ); + * mtlLoader.setResourcePath( 'assets/textures/' ); + * mtlLoader.load( 'my.mtl', ... ); + */ + setResourcePath: function ( path ) { + + this.resourcePath = path; + return this; + + }, + + setTexturePath: function ( path ) { + + console.warn( 'MTLLoader: .setTexturePath() has been renamed to .setResourcePath().' ); + return this.setResourcePath( path ); + + }, + + setCrossOrigin: function ( value ) { + + this.crossOrigin = value; + return this; + + }, + + setMaterialOptions: function ( value ) { + + this.materialOptions = value; + return this; + + }, + + /** + * Parses a MTL file. + * + * @param {String} text - Content of MTL file + * @return {MTLLoader.MaterialCreator} + * + * @see setPath setResourcePath + * + * @note In order for relative texture references to resolve correctly + * you must call setResourcePath() explicitly prior to parse. + */ + parse: function ( text, path ) { + + var lines = text.split( '\n' ); + var info = {}; + var delimiter_pattern = /\s+/; + var materialsInfo = {}; + + for ( var i = 0; i < lines.length; i ++ ) { + + var line = lines[ i ]; + line = line.trim(); + + if ( line.length === 0 || line.charAt( 0 ) === '#' ) { + + // Blank line or comment ignore + continue; + + } + + var pos = line.indexOf( ' ' ); + + var key = ( pos >= 0 ) ? line.substring( 0, pos ) : line; + key = key.toLowerCase(); + + var value = ( pos >= 0 ) ? line.substring( pos + 1 ) : ''; + value = value.trim(); + + if ( key === 'newmtl' ) { + + // New material + + info = { name: value }; + materialsInfo[ value ] = info; + + } else { + + if ( key === 'ka' || key === 'kd' || key === 'ks' || key === 'ke' ) { + + var ss = value.split( delimiter_pattern, 3 ); + info[ key ] = [ parseFloat( ss[ 0 ] ), parseFloat( ss[ 1 ] ), parseFloat( ss[ 2 ] ) ]; + + } else { + + info[ key ] = value; + + } + + } + + } + + var materialCreator = new MTLLoader.MaterialCreator( this.resourcePath || path, this.materialOptions ); + materialCreator.setCrossOrigin( this.crossOrigin ); + materialCreator.setManager( this.manager ); + materialCreator.setMaterials( materialsInfo ); + return materialCreator; + + } + +}; + +/** + * Create a new THREE-MTLLoader.MaterialCreator + * @param baseUrl - Url relative to which textures are loaded + * @param options - Set of options on how to construct the materials + * side: Which side to apply the material + * FrontSide (default), BackSide, DoubleSide + * wrap: What type of wrapping to apply for textures + * RepeatWrapping (default), ClampToEdgeWrapping, MirroredRepeatWrapping + * normalizeRGB: RGBs need to be normalized to 0-1 from 0-255 + * Default: false, assumed to be already normalized + * ignoreZeroRGBs: Ignore values of RGBs (Ka,Kd,Ks) that are all 0's + * Default: false + * @constructor + */ + +MTLLoader.MaterialCreator = function ( baseUrl, options ) { + + this.baseUrl = baseUrl || ''; + this.options = options; + this.materialsInfo = {}; + this.materials = {}; + this.materialsArray = []; + this.nameLookup = {}; + + this.side = ( this.options && this.options.side ) ? this.options.side : FrontSide; + this.wrap = ( this.options && this.options.wrap ) ? this.options.wrap : RepeatWrapping; + +}; + +MTLLoader.MaterialCreator.prototype = { + + constructor: MTLLoader.MaterialCreator, + + crossOrigin: 'anonymous', + + setCrossOrigin: function ( value ) { + + this.crossOrigin = value; + return this; + + }, + + setManager: function ( value ) { + + this.manager = value; + + }, + + setMaterials: function ( materialsInfo ) { + + this.materialsInfo = this.convert( materialsInfo ); + this.materials = {}; + this.materialsArray = []; + this.nameLookup = {}; + + }, + + convert: function ( materialsInfo ) { + + if ( ! this.options ) return materialsInfo; + + var converted = {}; + + for ( var mn in materialsInfo ) { + + // Convert materials info into normalized form based on options + + var mat = materialsInfo[ mn ]; + + var covmat = {}; + + converted[ mn ] = covmat; + + for ( var prop in mat ) { + + var save = true; + var value = mat[ prop ]; + var lprop = prop.toLowerCase(); + + switch ( lprop ) { + + case 'kd': + case 'ka': + case 'ks': + + // Diffuse color (color under white light) using RGB values + + if ( this.options && this.options.normalizeRGB ) { + + value = [ value[ 0 ] / 255, value[ 1 ] / 255, value[ 2 ] / 255 ]; + + } + + if ( this.options && this.options.ignoreZeroRGBs ) { + + if ( value[ 0 ] === 0 && value[ 1 ] === 0 && value[ 2 ] === 0 ) { + + // ignore + + save = false; + + } + + } + + break; + + default: + + break; + + } + + if ( save ) { + + covmat[ lprop ] = value; + + } + + } + + } + + return converted; + + }, + + preload: function () { + + for ( var mn in this.materialsInfo ) { + + this.create( mn ); + + } + + }, + + getIndex: function ( materialName ) { + + return this.nameLookup[ materialName ]; + + }, + + getAsArray: function () { + + var index = 0; + + for ( var mn in this.materialsInfo ) { + + this.materialsArray[ index ] = this.create( mn ); + this.nameLookup[ mn ] = index; + index ++; + + } + + return this.materialsArray; + + }, + + create: function ( materialName ) { + + if ( this.materials[ materialName ] === undefined ) { + + this.createMaterial_( materialName ); + + } + + return this.materials[ materialName ]; + + }, + + createMaterial_: function ( materialName ) { + + // Create material + + var scope = this; + var mat = this.materialsInfo[ materialName ]; + var params = { + + name: materialName, + side: this.side + + }; + + function resolveURL( baseUrl, url ) { + + if ( typeof url !== 'string' || url === '' ) + return ''; + + // Absolute URL + if ( /^https?:\/\//i.test( url ) ) return url; + + return baseUrl + url; + + } + + function setMapForType( mapType, value ) { + + if ( params[ mapType ] ) return; // Keep the first encountered texture + + var texParams = scope.getTextureParams( value, params ); + var map = scope.loadTexture( resolveURL( scope.baseUrl, texParams.url ) ); + + map.repeat.copy( texParams.scale ); + map.offset.copy( texParams.offset ); + + map.wrapS = scope.wrap; + map.wrapT = scope.wrap; + + params[ mapType ] = map; + + } + + for ( var prop in mat ) { + + var value = mat[ prop ]; + var n; + + if ( value === '' ) continue; + + switch ( prop.toLowerCase() ) { + + // Ns is material specular exponent + + case 'kd': + + // Diffuse color (color under white light) using RGB values + + params.color = new Color().fromArray( value ); + + break; + + case 'ks': + + // Specular color (color when light is reflected from shiny surface) using RGB values + params.specular = new Color().fromArray( value ); + + break; + + case 'ke': + + // Emissive using RGB values + params.emissive = new Color().fromArray( value ); + + break; + + case 'map_kd': + + // Diffuse texture map + + setMapForType( "map", value ); + + break; + + case 'map_ks': + + // Specular map + + setMapForType( "specularMap", value ); + + break; + + case 'map_ke': + + // Emissive map + + setMapForType( "emissiveMap", value ); + + break; + + case 'norm': + + setMapForType( "normalMap", value ); + + break; + + case 'map_bump': + case 'bump': + + // Bump texture map + + setMapForType( "bumpMap", value ); + + break; + + case 'map_d': + + // Alpha map + + setMapForType( "alphaMap", value ); + params.transparent = true; + + break; + + case 'ns': + + // The specular exponent (defines the focus of the specular highlight) + // A high exponent results in a tight, concentrated highlight. Ns values normally range from 0 to 1000. + + params.shininess = parseFloat( value ); + + break; + + case 'd': + n = parseFloat( value ); + + if ( n < 1 ) { + + params.opacity = n; + params.transparent = true; + + } + + break; + + case 'tr': + n = parseFloat( value ); + + if ( this.options && this.options.invertTrProperty ) n = 1 - n; + + if ( n > 0 ) { + + params.opacity = 1 - n; + params.transparent = true; + + } + + break; + + default: + break; + + } + + } + + this.materials[ materialName ] = new MeshPhongMaterial( params ); + return this.materials[ materialName ]; + + }, + + getTextureParams: function ( value, matParams ) { + + var texParams = { + + scale: new Vector2( 1, 1 ), + offset: new Vector2( 0, 0 ) + + }; + + var items = value.split( /\s+/ ); + var pos; + + pos = items.indexOf( '-bm' ); + + if ( pos >= 0 ) { + + matParams.bumpScale = parseFloat( items[ pos + 1 ] ); + items.splice( pos, 2 ); + + } + + pos = items.indexOf( '-s' ); + + if ( pos >= 0 ) { + + texParams.scale.set( parseFloat( items[ pos + 1 ] ), parseFloat( items[ pos + 2 ] ) ); + items.splice( pos, 4 ); // we expect 3 parameters here! + + } + + pos = items.indexOf( '-o' ); + + if ( pos >= 0 ) { + + texParams.offset.set( parseFloat( items[ pos + 1 ] ), parseFloat( items[ pos + 2 ] ) ); + items.splice( pos, 4 ); // we expect 3 parameters here! + + } + + texParams.url = items.join( ' ' ).trim(); + return texParams; + + }, + + loadTexture: function ( url, mapping, onLoad, onProgress, onError ) { + + var texture; + var loader = Loader.Handlers.get( url ); + var manager = ( this.manager !== undefined ) ? this.manager : DefaultLoadingManager; + + if ( loader === null ) { + + loader = new TextureLoader( manager ); + + } + + if ( loader.setCrossOrigin ) loader.setCrossOrigin( this.crossOrigin ); + texture = loader.load( url, onLoad, onProgress, onError ); + + if ( mapping !== undefined ) texture.mapping = mapping; + + return texture; + + } + +}; + +export { MTLLoader }; diff --git a/examples/jsm/loaders/NRRDLoader.js b/examples/jsm/loaders/NRRDLoader.js new file mode 100644 index 00000000000000..a4214cd91dcf2f --- /dev/null +++ b/examples/jsm/loaders/NRRDLoader.js @@ -0,0 +1,607 @@ +import { + DefaultLoadingManager, + FileLoader +} from "../../../build/three.module.js"; +import { Volume } from '../Volume'; + +var NRRDLoader = function ( manager ) { + + this.manager = ( manager !== undefined ) ? manager : DefaultLoadingManager; + + +}; + +NRRDLoader.prototype = { + + constructor: NRRDLoader, + + load: function ( url, onLoad, onProgress, onError ) { + + var scope = this; + + var loader = new FileLoader( scope.manager ); + loader.setPath( scope.path ); + loader.setResponseType( 'arraybuffer' ); + loader.load( url, function ( data ) { + + onLoad( scope.parse( data ) ); + + }, onProgress, onError ); + + }, + + setPath: function ( value ) { + + this.path = value; + return this; + + }, + + parse: function ( data ) { + + // this parser is largely inspired from the XTK NRRD parser : https://github.com/xtk/X + + var _data = data; + + var _dataPointer = 0; + + var _nativeLittleEndian = new Int8Array( new Int16Array( [ 1 ] ).buffer )[ 0 ] > 0; + + var _littleEndian = true; + + var headerObject = {}; + + function scan( type, chunks ) { + + if ( chunks === undefined || chunks === null ) { + + chunks = 1; + + } + + var _chunkSize = 1; + var _array_type = Uint8Array; + + switch ( type ) { + + // 1 byte data types + case 'uchar': + break; + case 'schar': + _array_type = Int8Array; + break; + // 2 byte data types + case 'ushort': + _array_type = Uint16Array; + _chunkSize = 2; + break; + case 'sshort': + _array_type = Int16Array; + _chunkSize = 2; + break; + // 4 byte data types + case 'uint': + _array_type = Uint32Array; + _chunkSize = 4; + break; + case 'sint': + _array_type = Int32Array; + _chunkSize = 4; + break; + case 'float': + _array_type = Float32Array; + _chunkSize = 4; + break; + case 'complex': + _array_type = Float64Array; + _chunkSize = 8; + break; + case 'double': + _array_type = Float64Array; + _chunkSize = 8; + break; + + } + + // increase the data pointer in-place + var _bytes = new _array_type( _data.slice( _dataPointer, + _dataPointer += chunks * _chunkSize ) ); + + // if required, flip the endianness of the bytes + if ( _nativeLittleEndian != _littleEndian ) { + + // we need to flip here since the format doesn't match the native endianness + _bytes = flipEndianness( _bytes, _chunkSize ); + + } + + if ( chunks == 1 ) { + + // if only one chunk was requested, just return one value + return _bytes[ 0 ]; + + } + + // return the byte array + return _bytes; + + } + + //Flips typed array endianness in-place. Based on https://github.com/kig/DataStream.js/blob/master/DataStream.js. + + function flipEndianness( array, chunkSize ) { + + var u8 = new Uint8Array( array.buffer, array.byteOffset, array.byteLength ); + for ( var i = 0; i < array.byteLength; i += chunkSize ) { + + for ( var j = i + chunkSize - 1, k = i; j > k; j --, k ++ ) { + + var tmp = u8[ k ]; + u8[ k ] = u8[ j ]; + u8[ j ] = tmp; + + } + + } + + return array; + + } + + //parse the header + function parseHeader( header ) { + + var data, field, fn, i, l, lines, m, _i, _len; + lines = header.split( /\r?\n/ ); + for ( _i = 0, _len = lines.length; _i < _len; _i ++ ) { + + l = lines[ _i ]; + if ( l.match( /NRRD\d+/ ) ) { + + headerObject.isNrrd = true; + + } else if ( l.match( /^#/ ) ) { + } else if ( m = l.match( /(.*):(.*)/ ) ) { + + field = m[ 1 ].trim(); + data = m[ 2 ].trim(); + fn = NRRDLoader.prototype.fieldFunctions[ field ]; + if ( fn ) { + + fn.call( headerObject, data ); + + } else { + + headerObject[ field ] = data; + + } + + } + + } + if ( ! headerObject.isNrrd ) { + + throw new Error( 'Not an NRRD file' ); + + } + if ( headerObject.encoding === 'bz2' || headerObject.encoding === 'bzip2' ) { + + throw new Error( 'Bzip is not supported' ); + + } + if ( ! headerObject.vectors ) { + + //if no space direction is set, let's use the identity + headerObject.vectors = [ new THREE.Vector3( 1, 0, 0 ), new THREE.Vector3( 0, 1, 0 ), new THREE.Vector3( 0, 0, 1 ) ]; + //apply spacing if defined + if ( headerObject.spacings ) { + + for ( i = 0; i <= 2; i ++ ) { + + if ( ! isNaN( headerObject.spacings[ i ] ) ) { + + headerObject.vectors[ i ].multiplyScalar( headerObject.spacings[ i ] ); + + } + + } + + } + + } + + } + + //parse the data when registred as one of this type : 'text', 'ascii', 'txt' + function parseDataAsText( data, start, end ) { + + var number = ''; + start = start || 0; + end = end || data.length; + var value; + //length of the result is the product of the sizes + var lengthOfTheResult = headerObject.sizes.reduce( function ( previous, current ) { + + return previous * current; + + }, 1 ); + + var base = 10; + if ( headerObject.encoding === 'hex' ) { + + base = 16; + + } + + var result = new headerObject.__array( lengthOfTheResult ); + var resultIndex = 0; + var parsingFunction = parseInt; + if ( headerObject.__array === Float32Array || headerObject.__array === Float64Array ) { + + parsingFunction = parseFloat; + + } + for ( var i = start; i < end; i ++ ) { + + value = data[ i ]; + //if value is not a space + if ( ( value < 9 || value > 13 ) && value !== 32 ) { + + number += String.fromCharCode( value ); + + } else { + + if ( number !== '' ) { + + result[ resultIndex ] = parsingFunction( number, base ); + resultIndex ++; + + } + number = ''; + + } + + } + if ( number !== '' ) { + + result[ resultIndex ] = parsingFunction( number, base ); + resultIndex ++; + + } + return result; + + } + + var _bytes = scan( 'uchar', data.byteLength ); + var _length = _bytes.length; + var _header = null; + var _data_start = 0; + var i; + for ( i = 1; i < _length; i ++ ) { + + if ( _bytes[ i - 1 ] == 10 && _bytes[ i ] == 10 ) { + + // we found two line breaks in a row + // now we know what the header is + _header = this.parseChars( _bytes, 0, i - 2 ); + // this is were the data starts + _data_start = i + 1; + break; + + } + + } + // parse the header + parseHeader( _header ); + + var _data = _bytes.subarray( _data_start ); // the data without header + if ( headerObject.encoding === 'gzip' || headerObject.encoding === 'gz' ) { + + // we need to decompress the datastream + // here we start the unzipping and get a typed Uint8Array back + var inflate = new Zlib.Gunzip( new Uint8Array( _data ) ); // eslint-disable-line no-undef + _data = inflate.decompress(); + + } else if ( headerObject.encoding === 'ascii' || headerObject.encoding === 'text' || headerObject.encoding === 'txt' || headerObject.encoding === 'hex' ) { + + _data = parseDataAsText( _data ); + + } else if ( headerObject.encoding === 'raw' ) { + + //we need to copy the array to create a new array buffer, else we retrieve the original arraybuffer with the header + var _copy = new Uint8Array( _data.length ); + + for ( var i = 0; i < _data.length; i ++ ) { + + _copy[ i ] = _data[ i ]; + + } + + _data = _copy; + + } + // .. let's use the underlying array buffer + _data = _data.buffer; + + var volume = new Volume(); + volume.header = headerObject; + // + // parse the (unzipped) data to a datastream of the correct type + // + volume.data = new headerObject.__array( _data ); + // get the min and max intensities + var min_max = volume.computeMinMax(); + var min = min_max[ 0 ]; + var max = min_max[ 1 ]; + // attach the scalar range to the volume + volume.windowLow = min; + volume.windowHigh = max; + + // get the image dimensions + volume.dimensions = [ headerObject.sizes[ 0 ], headerObject.sizes[ 1 ], headerObject.sizes[ 2 ] ]; + volume.xLength = volume.dimensions[ 0 ]; + volume.yLength = volume.dimensions[ 1 ]; + volume.zLength = volume.dimensions[ 2 ]; + // spacing + var spacingX = ( new THREE.Vector3( headerObject.vectors[ 0 ][ 0 ], headerObject.vectors[ 0 ][ 1 ], + headerObject.vectors[ 0 ][ 2 ] ) ).length(); + var spacingY = ( new THREE.Vector3( headerObject.vectors[ 1 ][ 0 ], headerObject.vectors[ 1 ][ 1 ], + headerObject.vectors[ 1 ][ 2 ] ) ).length(); + var spacingZ = ( new THREE.Vector3( headerObject.vectors[ 2 ][ 0 ], headerObject.vectors[ 2 ][ 1 ], + headerObject.vectors[ 2 ][ 2 ] ) ).length(); + volume.spacing = [ spacingX, spacingY, spacingZ ]; + + + // Create IJKtoRAS matrix + volume.matrix = new THREE.Matrix4(); + + var _spaceX = 1; + var _spaceY = 1; + var _spaceZ = 1; + + if ( headerObject.space == "left-posterior-superior" ) { + + _spaceX = - 1; + _spaceY = - 1; + + } else if ( headerObject.space === 'left-anterior-superior' ) { + + _spaceX = - 1; + + } + + + if ( ! headerObject.vectors ) { + + volume.matrix.set( + _spaceX, 0, 0, 0, + 0, _spaceY, 0, 0, + 0, 0, _spaceZ, 0, + 0, 0, 0, 1 ); + + } else { + + var v = headerObject.vectors; + + volume.matrix.set( + _spaceX * v[ 0 ][ 0 ], _spaceX * v[ 1 ][ 0 ], _spaceX * v[ 2 ][ 0 ], 0, + _spaceY * v[ 0 ][ 1 ], _spaceY * v[ 1 ][ 1 ], _spaceY * v[ 2 ][ 1 ], 0, + _spaceZ * v[ 0 ][ 2 ], _spaceZ * v[ 1 ][ 2 ], _spaceZ * v[ 2 ][ 2 ], 0, + 0, 0, 0, 1 ); + + } + + volume.inverseMatrix = new THREE.Matrix4(); + volume.inverseMatrix.getInverse( volume.matrix ); + volume.RASDimensions = ( new THREE.Vector3( volume.xLength, volume.yLength, volume.zLength ) ).applyMatrix4( volume.matrix ).round().toArray().map( Math.abs ); + + // .. and set the default threshold + // only if the threshold was not already set + if ( volume.lowerThreshold === - Infinity ) { + + volume.lowerThreshold = min; + + } + if ( volume.upperThreshold === Infinity ) { + + volume.upperThreshold = max; + + } + + return volume; + + }, + + parseChars: function ( array, start, end ) { + + // without borders, use the whole array + if ( start === undefined ) { + + start = 0; + + } + if ( end === undefined ) { + + end = array.length; + + } + + var output = ''; + // create and append the chars + var i = 0; + for ( i = start; i < end; ++ i ) { + + output += String.fromCharCode( array[ i ] ); + + } + + return output; + + }, + + fieldFunctions: { + + type: function ( data ) { + + switch ( data ) { + + case 'uchar': + case 'unsigned char': + case 'uint8': + case 'uint8_t': + this.__array = Uint8Array; + break; + case 'signed char': + case 'int8': + case 'int8_t': + this.__array = Int8Array; + break; + case 'short': + case 'short int': + case 'signed short': + case 'signed short int': + case 'int16': + case 'int16_t': + this.__array = Int16Array; + break; + case 'ushort': + case 'unsigned short': + case 'unsigned short int': + case 'uint16': + case 'uint16_t': + this.__array = Uint16Array; + break; + case 'int': + case 'signed int': + case 'int32': + case 'int32_t': + this.__array = Int32Array; + break; + case 'uint': + case 'unsigned int': + case 'uint32': + case 'uint32_t': + this.__array = Uint32Array; + break; + case 'float': + this.__array = Float32Array; + break; + case 'double': + this.__array = Float64Array; + break; + default: + throw new Error( 'Unsupported NRRD data type: ' + data ); + + } + + return this.type = data; + + }, + + endian: function ( data ) { + + return this.endian = data; + + }, + + encoding: function ( data ) { + + return this.encoding = data; + + }, + + dimension: function ( data ) { + + return this.dim = parseInt( data, 10 ); + + }, + + sizes: function ( data ) { + + var i; + return this.sizes = ( function () { + + var _i, _len, _ref, _results; + _ref = data.split( /\s+/ ); + _results = []; + for ( _i = 0, _len = _ref.length; _i < _len; _i ++ ) { + + i = _ref[ _i ]; + _results.push( parseInt( i, 10 ) ); + + } + return _results; + + } )(); + + }, + + space: function ( data ) { + + return this.space = data; + + }, + + 'space origin': function ( data ) { + + return this.space_origin = data.split( "(" )[ 1 ].split( ")" )[ 0 ].split( "," ); + + }, + + 'space directions': function ( data ) { + + var f, parts, v; + parts = data.match( /\(.*?\)/g ); + return this.vectors = ( function () { + + var _i, _len, _results; + _results = []; + for ( _i = 0, _len = parts.length; _i < _len; _i ++ ) { + + v = parts[ _i ]; + _results.push( ( function () { + + var _j, _len2, _ref, _results2; + _ref = v.slice( 1, - 1 ).split( /,/ ); + _results2 = []; + for ( _j = 0, _len2 = _ref.length; _j < _len2; _j ++ ) { + + f = _ref[ _j ]; + _results2.push( parseFloat( f ) ); + + } + return _results2; + + } )() ); + + } + return _results; + + } )(); + + }, + + spacings: function ( data ) { + + var f, parts; + parts = data.split( /\s+/ ); + return this.spacings = ( function () { + + var _i, _len, _results = []; + + for ( _i = 0, _len = parts.length; _i < _len; _i ++ ) { + + f = parts[ _i ]; + _results.push( parseFloat( f ) ); + + } + return _results; + + } )(); + + } + } + +}; + +export { NRRDLoader }; diff --git a/examples/jsm/loaders/NodeMaterialLoader.js b/examples/jsm/loaders/NodeMaterialLoader.js new file mode 100644 index 00000000000000..dc292430b0436c --- /dev/null +++ b/examples/jsm/loaders/NodeMaterialLoader.js @@ -0,0 +1,272 @@ +/** + * @author sunag / http://www.sunag.com.br/ + */ + +import { + DefaultLoadingManager, + FileLoader, +} from "../../../build/three.module.js"; + +var NodeMaterialLoader = function ( manager, library ) { + + this.manager = ( manager !== undefined ) ? manager : DefaultLoadingManager; + + this.nodes = {}; + this.materials = {}; + this.passes = {}; + this.names = {}; + this.library = library || {}; + +}; + +var NodeMaterialLoaderUtils = { + + replaceUUIDObject: function ( object, uuid, value, recursive ) { + + recursive = recursive !== undefined ? recursive : true; + + if ( typeof uuid === "object" ) uuid = uuid.uuid; + + if ( typeof object === "object" ) { + + var keys = Object.keys( object ); + + for ( var i = 0; i < keys.length; i ++ ) { + + var key = keys[ i ]; + + if ( recursive ) { + + object[ key ] = this.replaceUUIDObject( object[ key ], uuid, value ); + + } + + if ( key === uuid ) { + + object[ uuid ] = object[ key ]; + + delete object[ key ]; + + } + + } + + } + + return object === uuid ? value : object; + + }, + + replaceUUID: function ( json, uuid, value ) { + + this.replaceUUIDObject( json, uuid, value, false ); + this.replaceUUIDObject( json.nodes, uuid, value ); + this.replaceUUIDObject( json.materials, uuid, value ); + this.replaceUUIDObject( json.passes, uuid, value ); + this.replaceUUIDObject( json.library, uuid, value, false ); + + return json; + + } + +}; + +Object.assign( NodeMaterialLoader.prototype, { + + load: function ( url, onLoad, onProgress, onError ) { + + var scope = this; + + var loader = new FileLoader( scope.manager ); + loader.setPath( scope.path ); + loader.load( url, function ( text ) { + + onLoad( scope.parse( JSON.parse( text ) ) ); + + }, onProgress, onError ); + + return this; + + }, + + setPath: function ( value ) { + + this.path = value; + return this; + + }, + + getObjectByName: function ( uuid ) { + + return this.names[ uuid ]; + + }, + + getObjectById: function ( uuid ) { + + return this.library[ uuid ] || + this.nodes[ uuid ] || + this.materials[ uuid ] || + this.passes[ uuid ] || + this.names[ uuid ]; + + }, + + getNode: function ( uuid ) { + + var object = this.getObjectById( uuid ); + + if ( ! object ) { + + console.warn( "Node \"" + uuid + "\" not found." ); + + } + + return object; + + }, + + resolve: function ( json ) { + + switch ( typeof json ) { + + case "boolean": + case "number": + + return json; + + case "string": + + if ( /^\w{8}-\w{4}-\w{4}-\w{4}-\w{12}$/i.test( json ) || this.library[ json ] ) { + + return this.getNode( json ); + + } + + return json; + + default: + + if ( Array.isArray( json ) ) { + + for ( var i = 0; i < json.length; i ++ ) { + + json[ i ] = this.resolve( json[ i ] ); + + } + + } else { + + for ( var prop in json ) { + + if ( prop === "uuid" ) continue; + + json[ prop ] = this.resolve( json[ prop ] ); + + } + + } + + } + + return json; + + }, + + declare: function ( json ) { + + var uuid, node, object; + + for ( uuid in json.nodes ) { + + node = json.nodes[ uuid ]; + + object = new THREE[ node.nodeType + "Node" ](); + + if ( node.name ) { + + object.name = node.name; + + this.names[ object.name ] = object; + + } + + this.nodes[ uuid ] = object; + + } + + for ( uuid in json.materials ) { + + node = json.materials[ uuid ]; + + object = new THREE[ node.type ](); + + if ( node.name ) { + + object.name = node.name; + + this.names[ object.name ] = object; + + } + + this.materials[ uuid ] = object; + + } + + for ( uuid in json.passes ) { + + node = json.passes[ uuid ]; + + object = new THREE[ node.type ](); + + if ( node.name ) { + + object.name = node.name; + + this.names[ object.name ] = object; + + } + + this.passes[ uuid ] = object; + + } + + if ( json.material ) this.material = this.materials[ json.material ]; + + if ( json.pass ) this.pass = this.passes[ json.pass ]; + + return json; + + }, + + parse: function ( json ) { + + var uuid; + + json = this.resolve( this.declare( json ) ); + + for ( uuid in json.nodes ) { + + this.nodes[ uuid ].copy( json.nodes[ uuid ] ); + + } + + for ( uuid in json.materials ) { + + this.materials[ uuid ].copy( json.materials[ uuid ] ); + + } + + for ( uuid in json.passes ) { + + this.passes[ uuid ].copy( json.passes[ uuid ] ); + + } + + return this.material || this.pass || this; + + } + +} ); + +export { NodeMaterialLoader, NodeMaterialLoaderUtils }; diff --git a/examples/jsm/loaders/OBJLoader2.js b/examples/jsm/loaders/OBJLoader2.js new file mode 100644 index 00000000000000..4c7691de129ae4 --- /dev/null +++ b/examples/jsm/loaders/OBJLoader2.js @@ -0,0 +1,1610 @@ +/** + * @author Kai Salmen / https://kaisalmen.de + * Development repository: https://github.com/kaisalmen/WWOBJLoader + */ + +import { + DefaultLoadingManager, + LoaderUtils, + FileLoader, + Group +} from "../../../build/three.module.js"; +import { LoaderSupport } from './LoaderSupport'; +import { MTLLoader } from "./MTLLoader"; + +var OBJLoader2 = {}; + + +/** + * Use this class to load OBJ data from files or to parse OBJ data from an arraybuffer + * @class + * + * @param {DefaultLoadingManager} [manager] The loadingManager for the loader to use. Default is {@link DefaultLoadingManager} + */ +OBJLoader2 = function ( manager ) { + + console.info( 'Using OBJLoader2 version: ' + OBJLoader2.OBJLOADER2_VERSION ); + + this.manager = LoaderSupport.Validator.verifyInput( manager, DefaultLoadingManager ); + this.logging = { + enabled: true, + debug: false + }; + + this.modelName = ''; + this.instanceNo = 0; + this.path; + this.resourcePath; + this.useIndices = false; + this.disregardNormals = false; + this.materialPerSmoothingGroup = false; + this.useOAsMesh = false; + this.loaderRootNode = new Group(); + + this.meshBuilder = new LoaderSupport.MeshBuilder(); + this.callbacks = new LoaderSupport.Callbacks(); + this.workerSupport = new LoaderSupport.WorkerSupport(); + this.terminateWorkerOnLoad = true; + +}; + +OBJLoader2.OBJLOADER2_VERSION = '2.5.0'; + +OBJLoader2.prototype = { + + constructor: OBJLoader2, + + /** + * Enable or disable logging in general (except warn and error), plus enable or disable debug logging. + * + * @param {boolean} enabled True or false. + * @param {boolean} debug True or false. + */ + setLogging: function ( enabled, debug ) { + + this.logging.enabled = enabled === true; + this.logging.debug = debug === true; + this.meshBuilder.setLogging( this.logging.enabled, this.logging.debug ); + + }, + + /** + * Set the name of the model. + * + * @param {string} modelName + */ + setModelName: function ( modelName ) { + + this.modelName = LoaderSupport.Validator.verifyInput( modelName, this.modelName ); + + }, + + /** + * The URL of the base path. + * + * @param {string} path URL + */ + setPath: function ( path ) { + + this.path = LoaderSupport.Validator.verifyInput( path, this.path ); + + }, + + /** + * Allows to specify resourcePath for dependencies of specified resource. + * @param {string} resourcePath + */ + setResourcePath: function ( resourcePath ) { + + this.resourcePath = LoaderSupport.Validator.verifyInput( resourcePath, this.resourcePath ); + + }, + + /** + * Set the node where the loaded objects will be attached directly. + * + * @param {Object3D} streamMeshesTo Object already attached to scenegraph where new meshes will be attached to + */ + setStreamMeshesTo: function ( streamMeshesTo ) { + + this.loaderRootNode = LoaderSupport.Validator.verifyInput( streamMeshesTo, this.loaderRootNode ); + + }, + + /** + * Set materials loaded by MTLLoader or any other supplier of an Array of {@link Material}. + * + * @param {Material[]} materials Array of {@link Material} + */ + setMaterials: function ( materials ) { + + this.meshBuilder.setMaterials( materials ); + + }, + + /** + * Instructs loaders to create indexed {@link BufferGeometry}. + * + * @param {boolean} useIndices=false + */ + setUseIndices: function ( useIndices ) { + + this.useIndices = useIndices === true; + + }, + + /** + * Tells whether normals should be completely disregarded and regenerated. + * + * @param {boolean} disregardNormals=false + */ + setDisregardNormals: function ( disregardNormals ) { + + this.disregardNormals = disregardNormals === true; + + }, + + /** + * Tells whether a material shall be created per smoothing group. + * + * @param {boolean} materialPerSmoothingGroup=false + */ + setMaterialPerSmoothingGroup: function ( materialPerSmoothingGroup ) { + + this.materialPerSmoothingGroup = materialPerSmoothingGroup === true; + + }, + + /** + * Usually 'o' is meta-information and does not result in creation of new meshes, but mesh creation on occurrence of "o" can be enforced. + * + * @param {boolean} useOAsMesh=false + */ + setUseOAsMesh: function ( useOAsMesh ) { + + this.useOAsMesh = useOAsMesh === true; + + }, + + _setCallbacks: function ( callbacks ) { + + if ( LoaderSupport.Validator.isValid( callbacks.onProgress ) ) this.callbacks.setCallbackOnProgress( callbacks.onProgress ); + if ( LoaderSupport.Validator.isValid( callbacks.onReportError ) ) this.callbacks.setCallbackOnReportError( callbacks.onReportError ); + if ( LoaderSupport.Validator.isValid( callbacks.onMeshAlter ) ) this.callbacks.setCallbackOnMeshAlter( callbacks.onMeshAlter ); + if ( LoaderSupport.Validator.isValid( callbacks.onLoad ) ) this.callbacks.setCallbackOnLoad( callbacks.onLoad ); + if ( LoaderSupport.Validator.isValid( callbacks.onLoadMaterials ) ) this.callbacks.setCallbackOnLoadMaterials( callbacks.onLoadMaterials ); + + this.meshBuilder._setCallbacks( this.callbacks ); + + }, + + /** + * Announce feedback which is give to the registered callbacks. + * @private + * + * @param {string} type The type of event + * @param {string} text Textual description of the event + * @param {number} numericalValue Numerical value describing the progress + */ + onProgress: function ( type, text, numericalValue ) { + + var content = LoaderSupport.Validator.isValid( text ) ? text : ''; + var event = { + detail: { + type: type, + modelName: this.modelName, + instanceNo: this.instanceNo, + text: content, + numericalValue: numericalValue + } + }; + + if ( LoaderSupport.Validator.isValid( this.callbacks.onProgress ) ) this.callbacks.onProgress( event ); + + if ( this.logging.enabled && this.logging.debug ) console.debug( content ); + + }, + + _onError: function ( event ) { + + var output = 'Error occurred while downloading!'; + + if ( event.currentTarget && event.currentTarget.statusText !== null ) { + + output += '\nurl: ' + event.currentTarget.responseURL + '\nstatus: ' + event.currentTarget.statusText; + + } + this.onProgress( 'error', output, - 1 ); + this._throwError( output ); + + }, + + _throwError: function ( errorMessage ) { + + if ( LoaderSupport.Validator.isValid( this.callbacks.onReportError ) ) { + + this.callbacks.onReportError( errorMessage ); + + } else { + + throw errorMessage; + + } + + }, + + /** + * Use this convenient method to load a file at the given URL. By default the fileLoader uses an ArrayBuffer. + * + * @param {string} url A string containing the path/URL of the file to be loaded. + * @param {callback} onLoad A function to be called after loading is successfully completed. The function receives loaded Object3D as an argument. + * @param {callback} [onProgress] A function to be called while the loading is in progress. The argument will be the XMLHttpRequest instance, which contains total and Integer bytes. + * @param {callback} [onError] A function to be called if an error occurs during loading. The function receives the error as an argument. + * @param {callback} [onMeshAlter] A function to be called after a new mesh raw data becomes available for alteration. + * @param {boolean} [useAsync] If true, uses async loading with worker, if false loads data synchronously. + */ + load: function ( url, onLoad, onProgress, onError, onMeshAlter, useAsync ) { + + var resource = new LoaderSupport.ResourceDescriptor( url, 'OBJ' ); + this._loadObj( resource, onLoad, onProgress, onError, onMeshAlter, useAsync ); + + }, + + _loadObj: function ( resource, onLoad, onProgress, onError, onMeshAlter, useAsync ) { + + var scope = this; + if ( ! LoaderSupport.Validator.isValid( onError ) ) { + + onError = function ( event ) { + + scope._onError( event ); + + }; + + } + + // fast-fail + if ( ! LoaderSupport.Validator.isValid( resource ) ) onError( 'An invalid ResourceDescriptor was provided. Unable to continue!' ); + var fileLoaderOnLoad = function ( content ) { + + resource.content = content; + if ( useAsync ) { + + scope.parseAsync( content, onLoad ); + + } else { + + var callbacks = new LoaderSupport.Callbacks(); + callbacks.setCallbackOnMeshAlter( onMeshAlter ); + scope._setCallbacks( callbacks ); + onLoad( + { + detail: { + loaderRootNode: scope.parse( content ), + modelName: scope.modelName, + instanceNo: scope.instanceNo + } + } + ); + + } + + }; + this.setPath( resource.path ); + this.setResourcePath( resource.resourcePath ); + + // fast-fail + if ( ! LoaderSupport.Validator.isValid( resource.url ) || LoaderSupport.Validator.isValid( resource.content ) ) { + + fileLoaderOnLoad( LoaderSupport.Validator.isValid( resource.content ) ? resource.content : null ); + + } else { + + if ( ! LoaderSupport.Validator.isValid( onProgress ) ) { + + var numericalValueRef = 0; + var numericalValue = 0; + onProgress = function ( event ) { + + if ( ! event.lengthComputable ) return; + + numericalValue = event.loaded / event.total; + if ( numericalValue > numericalValueRef ) { + + numericalValueRef = numericalValue; + var output = 'Download of "' + resource.url + '": ' + ( numericalValue * 100 ).toFixed( 2 ) + '%'; + scope.onProgress( 'progressLoad', output, numericalValue ); + + } + + }; + + } + + + var fileLoader = new FileLoader( this.manager ); + fileLoader.setPath( this.path || this.resourcePath ); + fileLoader.setResponseType( 'arraybuffer' ); + fileLoader.load( resource.name, fileLoaderOnLoad, onProgress, onError ); + + } + + }, + + /** + * Run the loader according the provided instructions. + * + * @param {LoaderSupport.PrepData} prepData All parameters and resources required for execution + * @param {LoaderSupport.WorkerSupport} [workerSupportExternal] Use pre-existing WorkerSupport + */ + run: function ( prepData, workerSupportExternal ) { + + this._applyPrepData( prepData ); + var available = prepData.checkResourceDescriptorFiles( prepData.resources, + [ + { ext: "obj", type: "ArrayBuffer", ignore: false }, + { ext: "mtl", type: "String", ignore: false }, + { ext: "zip", type: "String", ignore: true } + ] + ); + if ( LoaderSupport.Validator.isValid( workerSupportExternal ) ) { + + this.terminateWorkerOnLoad = false; + this.workerSupport = workerSupportExternal; + this.logging.enabled = this.workerSupport.logging.enabled; + this.logging.debug = this.workerSupport.logging.debug; + + } + var scope = this; + var onMaterialsLoaded = function ( materials ) { + + if ( materials !== null ) scope.meshBuilder.setMaterials( materials ); + scope._loadObj( available.obj, scope.callbacks.onLoad, null, null, scope.callbacks.onMeshAlter, prepData.useAsync ); + + }; + this._loadMtl( available.mtl, onMaterialsLoaded, null, null, prepData.crossOrigin, prepData.materialOptions ); + + }, + + _applyPrepData: function ( prepData ) { + + if ( LoaderSupport.Validator.isValid( prepData ) ) { + + this.setLogging( prepData.logging.enabled, prepData.logging.debug ); + this.setModelName( prepData.modelName ); + this.setStreamMeshesTo( prepData.streamMeshesTo ); + this.meshBuilder.setMaterials( prepData.materials ); + this.setUseIndices( prepData.useIndices ); + this.setDisregardNormals( prepData.disregardNormals ); + this.setMaterialPerSmoothingGroup( prepData.materialPerSmoothingGroup ); + this.setUseOAsMesh( prepData.useOAsMesh ); + + this._setCallbacks( prepData.getCallbacks() ); + + } + + }, + + /** + * Parses OBJ data synchronously from arraybuffer or string. + * + * @param {arraybuffer|string} content OBJ data as Uint8Array or String + */ + parse: function ( content ) { + + // fast-fail in case of illegal data + if ( ! LoaderSupport.Validator.isValid( content ) ) { + + console.warn( 'Provided content is not a valid ArrayBuffer or String.' ); + return this.loaderRootNode; + + } + if ( this.logging.enabled ) console.time( 'OBJLoader2 parse: ' + this.modelName ); + this.meshBuilder.init(); + + var parser = new OBJLoader2.Parser(); + parser.setLogging( this.logging.enabled, this.logging.debug ); + parser.setMaterialPerSmoothingGroup( this.materialPerSmoothingGroup ); + parser.setUseOAsMesh( this.useOAsMesh ); + parser.setUseIndices( this.useIndices ); + parser.setDisregardNormals( this.disregardNormals ); + // sync code works directly on the material references + parser.setMaterials( this.meshBuilder.getMaterials() ); + + var scope = this; + var onMeshLoaded = function ( payload ) { + + var meshes = scope.meshBuilder.processPayload( payload ); + var mesh; + for ( var i in meshes ) { + + mesh = meshes[ i ]; + scope.loaderRootNode.add( mesh ); + + } + + }; + parser.setCallbackMeshBuilder( onMeshLoaded ); + var onProgressScoped = function ( text, numericalValue ) { + + scope.onProgress( 'progressParse', text, numericalValue ); + + }; + parser.setCallbackProgress( onProgressScoped ); + + if ( content instanceof ArrayBuffer || content instanceof Uint8Array ) { + + if ( this.logging.enabled ) console.info( 'Parsing arrayBuffer...' ); + parser.parse( content ); + + } else if ( typeof ( content ) === 'string' || content instanceof String ) { + + if ( this.logging.enabled ) console.info( 'Parsing text...' ); + parser.parseText( content ); + + } else { + + this._throwError( 'Provided content was neither of type String nor Uint8Array! Aborting...' ); + + } + if ( this.logging.enabled ) console.timeEnd( 'OBJLoader2 parse: ' + this.modelName ); + + return this.loaderRootNode; + + }, + + /** + * Parses OBJ content asynchronously from arraybuffer. + * + * @param {arraybuffer} content OBJ data as Uint8Array + * @param {callback} onLoad Called after worker successfully completed loading + */ + parseAsync: function ( content, onLoad ) { + + var scope = this; + var measureTime = false; + var scopedOnLoad = function () { + + onLoad( + { + detail: { + loaderRootNode: scope.loaderRootNode, + modelName: scope.modelName, + instanceNo: scope.instanceNo + } + } + ); + if ( measureTime && scope.logging.enabled ) console.timeEnd( 'OBJLoader2 parseAsync: ' + scope.modelName ); + + }; + // fast-fail in case of illegal data + if ( ! LoaderSupport.Validator.isValid( content ) ) { + + console.warn( 'Provided content is not a valid ArrayBuffer.' ); + scopedOnLoad(); + + } else { + + measureTime = true; + + } + if ( measureTime && this.logging.enabled ) console.time( 'OBJLoader2 parseAsync: ' + this.modelName ); + this.meshBuilder.init(); + + var scopedOnMeshLoaded = function ( payload ) { + + var meshes = scope.meshBuilder.processPayload( payload ); + var mesh; + for ( var i in meshes ) { + + mesh = meshes[ i ]; + scope.loaderRootNode.add( mesh ); + + } + + }; + var buildCode = function ( codeSerializer ) { + + var workerCode = ''; + workerCode += '/**\n'; + workerCode += ' * This code was constructed by OBJLoader2 buildCode.\n'; + workerCode += ' */\n\n'; + workerCode += 'THREE = { LoaderSupport: {}, OBJLoader2: {} };\n\n'; + workerCode += codeSerializer.serializeObject( 'LoaderSupport.Validator', LoaderSupport.Validator ); + workerCode += codeSerializer.serializeClass( 'OBJLoader2.Parser', OBJLoader2.Parser ); + + return workerCode; + + }; + this.workerSupport.validate( buildCode, 'OBJLoader2.Parser' ); + this.workerSupport.setCallbacks( scopedOnMeshLoaded, scopedOnLoad ); + if ( scope.terminateWorkerOnLoad ) this.workerSupport.setTerminateRequested( true ); + + var materialNames = {}; + var materials = this.meshBuilder.getMaterials(); + for ( var materialName in materials ) { + + materialNames[ materialName ] = materialName; + + } + this.workerSupport.run( + { + params: { + useAsync: true, + materialPerSmoothingGroup: this.materialPerSmoothingGroup, + useOAsMesh: this.useOAsMesh, + useIndices: this.useIndices, + disregardNormals: this.disregardNormals + }, + logging: { + enabled: this.logging.enabled, + debug: this.logging.debug + }, + materials: { + // in async case only material names are supplied to parser + materials: materialNames + }, + data: { + input: content, + options: null + } + } + ); + + }, + + /** + * Utility method for loading an mtl file according resource description. Provide url or content. + * + * @param {string} url URL to the file + * @param {Object} content The file content as arraybuffer or text + * @param {function} onLoad Callback to be called after successful load + * @param {callback} [onProgress] A function to be called while the loading is in progress. The argument will be the XMLHttpRequest instance, which contains total and Integer bytes. + * @param {callback} [onError] A function to be called if an error occurs during loading. The function receives the error as an argument. + * @param {string} [crossOrigin] CORS value + * @param {Object} [materialOptions] Set material loading options for MTLLoader + */ + loadMtl: function ( url, content, onLoad, onProgress, onError, crossOrigin, materialOptions ) { + + var resource = new LoaderSupport.ResourceDescriptor( url, 'MTL' ); + resource.setContent( content ); + this._loadMtl( resource, onLoad, onProgress, onError, crossOrigin, materialOptions ); + + }, + + _loadMtl: function ( resource, onLoad, onProgress, onError, crossOrigin, materialOptions ) { + + if ( LoaderSupport.Validator.isValid( resource ) && this.logging.enabled ) console.time( 'Loading MTL: ' + resource.name ); + + var materials = []; + var scope = this; + var processMaterials = function ( materialCreator ) { + + var materialCreatorMaterials = []; + if ( LoaderSupport.Validator.isValid( materialCreator ) ) { + + materialCreator.preload(); + materialCreatorMaterials = materialCreator.materials; + for ( var materialName in materialCreatorMaterials ) { + + if ( materialCreatorMaterials.hasOwnProperty( materialName ) ) { + + materials[ materialName ] = materialCreatorMaterials[ materialName ]; + + } + + } + + } + + if ( LoaderSupport.Validator.isValid( resource ) && scope.logging.enabled ) console.timeEnd( 'Loading MTL: ' + resource.name ); + onLoad( materials, materialCreator ); + + }; + + // fast-fail + if ( ! LoaderSupport.Validator.isValid( resource ) || ( ! LoaderSupport.Validator.isValid( resource.content ) && ! LoaderSupport.Validator.isValid( resource.url ) ) ) { + + processMaterials(); + + } else { + + var mtlLoader = new MTLLoader( this.manager ); + crossOrigin = LoaderSupport.Validator.verifyInput( crossOrigin, 'anonymous' ); + mtlLoader.setCrossOrigin( crossOrigin ); + mtlLoader.setResourcePath( resource.resourcePath || resource.path ); + if ( LoaderSupport.Validator.isValid( materialOptions ) ) mtlLoader.setMaterialOptions( materialOptions ); + + var parseTextWithMtlLoader = function ( content ) { + + var contentAsText = content; + if ( typeof ( content ) !== 'string' && ! ( content instanceof String ) ) { + + if ( content.length > 0 || content.byteLength > 0 ) { + + contentAsText = LoaderUtils.decodeText( content ); + + } else { + + this._throwError( 'Unable to parse mtl as it it seems to be neither a String, an Array or an ArrayBuffer!' ); + + } + + } + processMaterials( mtlLoader.parse( contentAsText ) ); + + }; + + if ( LoaderSupport.Validator.isValid( resource.content ) ) { + + parseTextWithMtlLoader( resource.content ); + + } else if ( LoaderSupport.Validator.isValid( resource.url ) ) { + + var fileLoader = new FileLoader( this.manager ); + if ( ! LoaderSupport.Validator.isValid( onError ) ) { + + onError = function ( event ) { + + scope._onError( event ); + + }; + + } + if ( ! LoaderSupport.Validator.isValid( onProgress ) ) { + + var numericalValueRef = 0; + var numericalValue = 0; + onProgress = function ( event ) { + + if ( ! event.lengthComputable ) return; + + numericalValue = event.loaded / event.total; + if ( numericalValue > numericalValueRef ) { + + numericalValueRef = numericalValue; + var output = 'Download of "' + resource.url + '": ' + ( numericalValue * 100 ).toFixed( 2 ) + '%'; + scope.onProgress( 'progressLoad', output, numericalValue ); + + } + + }; + + } + + fileLoader.load( resource.url, parseTextWithMtlLoader, onProgress, onError ); + + } + + } + + } +}; + + +/** + * Parse OBJ data either from ArrayBuffer or string + * @class + */ +OBJLoader2.Parser = function () { + + this.callbackProgress = null; + this.callbackMeshBuilder = null; + this.contentRef = null; + this.legacyMode = false; + + this.materials = {}; + this.useAsync = false; + this.materialPerSmoothingGroup = false; + this.useOAsMesh = false; + this.useIndices = false; + this.disregardNormals = false; + + this.vertices = []; + this.colors = []; + this.normals = []; + this.uvs = []; + + this.rawMesh = { + objectName: '', + groupName: '', + activeMtlName: '', + mtllibName: '', + + // reset with new mesh + faceType: - 1, + subGroups: [], + subGroupInUse: null, + smoothingGroup: { + splitMaterials: false, + normalized: - 1, + real: - 1 + }, + counts: { + doubleIndicesCount: 0, + faceCount: 0, + mtlCount: 0, + smoothingGroupCount: 0 + } + }; + + this.inputObjectCount = 1; + this.outputObjectCount = 1; + this.globalCounts = { + vertices: 0, + faces: 0, + doubleIndicesCount: 0, + lineByte: 0, + currentByte: 0, + totalBytes: 0 + }; + + this.logging = { + enabled: true, + debug: false + }; + +}; + + +OBJLoader2.Parser.prototype = { + + constructor: OBJLoader2.Parser, + + resetRawMesh: function () { + + // faces are stored according combined index of group, material and smoothingGroup (0 or not) + this.rawMesh.subGroups = []; + this.rawMesh.subGroupInUse = null; + this.rawMesh.smoothingGroup.normalized = - 1; + this.rawMesh.smoothingGroup.real = - 1; + + // this default index is required as it is possible to define faces without 'g' or 'usemtl' + this.pushSmoothingGroup( 1 ); + + this.rawMesh.counts.doubleIndicesCount = 0; + this.rawMesh.counts.faceCount = 0; + this.rawMesh.counts.mtlCount = 0; + this.rawMesh.counts.smoothingGroupCount = 0; + + }, + + setUseAsync: function ( useAsync ) { + + this.useAsync = useAsync; + + }, + + setMaterialPerSmoothingGroup: function ( materialPerSmoothingGroup ) { + + this.materialPerSmoothingGroup = materialPerSmoothingGroup; + + }, + + setUseOAsMesh: function ( useOAsMesh ) { + + this.useOAsMesh = useOAsMesh; + + }, + + setUseIndices: function ( useIndices ) { + + this.useIndices = useIndices; + + }, + + setDisregardNormals: function ( disregardNormals ) { + + this.disregardNormals = disregardNormals; + + }, + + setMaterials: function ( materials ) { + + this.materials = LoaderSupport.Validator.verifyInput( materials, this.materials ); + this.materials = LoaderSupport.Validator.verifyInput( this.materials, {} ); + + }, + + setCallbackMeshBuilder: function ( callbackMeshBuilder ) { + + if ( ! LoaderSupport.Validator.isValid( callbackMeshBuilder ) ) { + + this._throwError( 'Unable to run as no "MeshBuilder" callback is set.' ); + + } + this.callbackMeshBuilder = callbackMeshBuilder; + + }, + + setCallbackProgress: function ( callbackProgress ) { + + this.callbackProgress = callbackProgress; + + }, + + setLogging: function ( enabled, debug ) { + + this.logging.enabled = enabled === true; + this.logging.debug = debug === true; + + }, + + configure: function () { + + this.pushSmoothingGroup( 1 ); + + if ( this.logging.enabled ) { + + var matKeys = Object.keys( this.materials ); + var matNames = ( matKeys.length > 0 ) ? '\n\tmaterialNames:\n\t\t- ' + matKeys.join( '\n\t\t- ' ) : '\n\tmaterialNames: None'; + var printedConfig = 'OBJLoader2.Parser configuration:' + + matNames + + '\n\tuseAsync: ' + this.useAsync + + '\n\tmaterialPerSmoothingGroup: ' + this.materialPerSmoothingGroup + + '\n\tuseOAsMesh: ' + this.useOAsMesh + + '\n\tuseIndices: ' + this.useIndices + + '\n\tdisregardNormals: ' + this.disregardNormals + + '\n\tcallbackMeshBuilderName: ' + this.callbackMeshBuilder.name + + '\n\tcallbackProgressName: ' + this.callbackProgress.name; + console.info( printedConfig ); + + } + + }, + + /** + * Parse the provided arraybuffer + * + * @param {Uint8Array} arrayBuffer OBJ data as Uint8Array + */ + parse: function ( arrayBuffer ) { + + if ( this.logging.enabled ) console.time( 'OBJLoader2.Parser.parse' ); + this.configure(); + + var arrayBufferView = new Uint8Array( arrayBuffer ); + this.contentRef = arrayBufferView; + var length = arrayBufferView.byteLength; + this.globalCounts.totalBytes = length; + var buffer = new Array( 128 ); + + for ( var code, word = '', bufferPointer = 0, slashesCount = 0, i = 0; i < length; i ++ ) { + + code = arrayBufferView[ i ]; + switch ( code ) { + + // space + case 32: + if ( word.length > 0 ) buffer[ bufferPointer ++ ] = word; + word = ''; + break; + // slash + case 47: + if ( word.length > 0 ) buffer[ bufferPointer ++ ] = word; + slashesCount ++; + word = ''; + break; + + // LF + case 10: + if ( word.length > 0 ) buffer[ bufferPointer ++ ] = word; + word = ''; + this.globalCounts.lineByte = this.globalCounts.currentByte; + this.globalCounts.currentByte = i; + this.processLine( buffer, bufferPointer, slashesCount ); + bufferPointer = 0; + slashesCount = 0; + break; + + // CR + case 13: + break; + + default: + word += String.fromCharCode( code ); + break; + + } + + } + this.finalizeParsing(); + if ( this.logging.enabled ) console.timeEnd( 'OBJLoader2.Parser.parse' ); + + }, + + /** + * Parse the provided text + * + * @param {string} text OBJ data as string + */ + parseText: function ( text ) { + + if ( this.logging.enabled ) console.time( 'OBJLoader2.Parser.parseText' ); + this.configure(); + this.legacyMode = true; + this.contentRef = text; + var length = text.length; + this.globalCounts.totalBytes = length; + var buffer = new Array( 128 ); + + for ( var char, word = '', bufferPointer = 0, slashesCount = 0, i = 0; i < length; i ++ ) { + + char = text[ i ]; + switch ( char ) { + + case ' ': + if ( word.length > 0 ) buffer[ bufferPointer ++ ] = word; + word = ''; + break; + + case '/': + if ( word.length > 0 ) buffer[ bufferPointer ++ ] = word; + slashesCount ++; + word = ''; + break; + + case '\n': + if ( word.length > 0 ) buffer[ bufferPointer ++ ] = word; + word = ''; + this.globalCounts.lineByte = this.globalCounts.currentByte; + this.globalCounts.currentByte = i; + this.processLine( buffer, bufferPointer, slashesCount ); + bufferPointer = 0; + slashesCount = 0; + break; + + case '\r': + break; + + default: + word += char; + + } + + } + this.finalizeParsing(); + if ( this.logging.enabled ) console.timeEnd( 'OBJLoader2.Parser.parseText' ); + + }, + + processLine: function ( buffer, bufferPointer, slashesCount ) { + + if ( bufferPointer < 1 ) return; + + var reconstructString = function ( content, legacyMode, start, stop ) { + + var line = ''; + if ( stop > start ) { + + var i; + if ( legacyMode ) { + + for ( i = start; i < stop; i ++ ) line += content[ i ]; + + } else { + + + for ( i = start; i < stop; i ++ ) line += String.fromCharCode( content[ i ] ); + + } + line = line.trim(); + + } + return line; + + }; + + var bufferLength, length, i, lineDesignation; + lineDesignation = buffer[ 0 ]; + switch ( lineDesignation ) { + + case 'v': + this.vertices.push( parseFloat( buffer[ 1 ] ) ); + this.vertices.push( parseFloat( buffer[ 2 ] ) ); + this.vertices.push( parseFloat( buffer[ 3 ] ) ); + if ( bufferPointer > 4 ) { + + this.colors.push( parseFloat( buffer[ 4 ] ) ); + this.colors.push( parseFloat( buffer[ 5 ] ) ); + this.colors.push( parseFloat( buffer[ 6 ] ) ); + + } + break; + + case 'vt': + this.uvs.push( parseFloat( buffer[ 1 ] ) ); + this.uvs.push( parseFloat( buffer[ 2 ] ) ); + break; + + case 'vn': + this.normals.push( parseFloat( buffer[ 1 ] ) ); + this.normals.push( parseFloat( buffer[ 2 ] ) ); + this.normals.push( parseFloat( buffer[ 3 ] ) ); + break; + + case 'f': + bufferLength = bufferPointer - 1; + + // "f vertex ..." + if ( slashesCount === 0 ) { + + this.checkFaceType( 0 ); + for ( i = 2, length = bufferLength; i < length; i ++ ) { + + this.buildFace( buffer[ 1 ] ); + this.buildFace( buffer[ i ] ); + this.buildFace( buffer[ i + 1 ] ); + + } + + // "f vertex/uv ..." + + } else if ( bufferLength === slashesCount * 2 ) { + + this.checkFaceType( 1 ); + for ( i = 3, length = bufferLength - 2; i < length; i += 2 ) { + + this.buildFace( buffer[ 1 ], buffer[ 2 ] ); + this.buildFace( buffer[ i ], buffer[ i + 1 ] ); + this.buildFace( buffer[ i + 2 ], buffer[ i + 3 ] ); + + } + + // "f vertex/uv/normal ..." + + } else if ( bufferLength * 2 === slashesCount * 3 ) { + + this.checkFaceType( 2 ); + for ( i = 4, length = bufferLength - 3; i < length; i += 3 ) { + + this.buildFace( buffer[ 1 ], buffer[ 2 ], buffer[ 3 ] ); + this.buildFace( buffer[ i ], buffer[ i + 1 ], buffer[ i + 2 ] ); + this.buildFace( buffer[ i + 3 ], buffer[ i + 4 ], buffer[ i + 5 ] ); + + } + + // "f vertex//normal ..." + + } else { + + this.checkFaceType( 3 ); + for ( i = 3, length = bufferLength - 2; i < length; i += 2 ) { + + this.buildFace( buffer[ 1 ], undefined, buffer[ 2 ] ); + this.buildFace( buffer[ i ], undefined, buffer[ i + 1 ] ); + this.buildFace( buffer[ i + 2 ], undefined, buffer[ i + 3 ] ); + + } + + } + break; + + case 'l': + case 'p': + bufferLength = bufferPointer - 1; + if ( bufferLength === slashesCount * 2 ) { + + this.checkFaceType( 4 ); + for ( i = 1, length = bufferLength + 1; i < length; i += 2 ) this.buildFace( buffer[ i ], buffer[ i + 1 ] ); + + } else { + + this.checkFaceType( ( lineDesignation === 'l' ) ? 5 : 6 ); + for ( i = 1, length = bufferLength + 1; i < length; i ++ ) this.buildFace( buffer[ i ] ); + + } + break; + + case 's': + this.pushSmoothingGroup( buffer[ 1 ] ); + break; + + case 'g': + // 'g' leads to creation of mesh if valid data (faces declaration was done before), otherwise only groupName gets set + this.processCompletedMesh(); + this.rawMesh.groupName = reconstructString( this.contentRef, this.legacyMode, this.globalCounts.lineByte + 2, this.globalCounts.currentByte ); + break; + + case 'o': + // 'o' is meta-information and usually does not result in creation of new meshes, but can be enforced with "useOAsMesh" + if ( this.useOAsMesh ) this.processCompletedMesh(); + this.rawMesh.objectName = reconstructString( this.contentRef, this.legacyMode, this.globalCounts.lineByte + 2, this.globalCounts.currentByte ); + break; + + case 'mtllib': + this.rawMesh.mtllibName = reconstructString( this.contentRef, this.legacyMode, this.globalCounts.lineByte + 7, this.globalCounts.currentByte ); + break; + + case 'usemtl': + var mtlName = reconstructString( this.contentRef, this.legacyMode, this.globalCounts.lineByte + 7, this.globalCounts.currentByte ); + if ( mtlName !== '' && this.rawMesh.activeMtlName !== mtlName ) { + + this.rawMesh.activeMtlName = mtlName; + this.rawMesh.counts.mtlCount ++; + this.checkSubGroup(); + + } + break; + + default: + break; + + } + + }, + + pushSmoothingGroup: function ( smoothingGroup ) { + + var smoothingGroupInt = parseInt( smoothingGroup ); + if ( isNaN( smoothingGroupInt ) ) { + + smoothingGroupInt = smoothingGroup === "off" ? 0 : 1; + + } + + var smoothCheck = this.rawMesh.smoothingGroup.normalized; + this.rawMesh.smoothingGroup.normalized = this.rawMesh.smoothingGroup.splitMaterials ? smoothingGroupInt : ( smoothingGroupInt === 0 ) ? 0 : 1; + this.rawMesh.smoothingGroup.real = smoothingGroupInt; + + if ( smoothCheck !== smoothingGroupInt ) { + + this.rawMesh.counts.smoothingGroupCount ++; + this.checkSubGroup(); + + } + + }, + + /** + * Expanded faceTypes include all four face types, both line types and the point type + * faceType = 0: "f vertex ..." + * faceType = 1: "f vertex/uv ..." + * faceType = 2: "f vertex/uv/normal ..." + * faceType = 3: "f vertex//normal ..." + * faceType = 4: "l vertex/uv ..." or "l vertex ..." + * faceType = 5: "l vertex ..." + * faceType = 6: "p vertex ..." + */ + checkFaceType: function ( faceType ) { + + if ( this.rawMesh.faceType !== faceType ) { + + this.processCompletedMesh(); + this.rawMesh.faceType = faceType; + this.checkSubGroup(); + + } + + }, + + checkSubGroup: function () { + + var index = this.rawMesh.activeMtlName + '|' + this.rawMesh.smoothingGroup.normalized; + this.rawMesh.subGroupInUse = this.rawMesh.subGroups[ index ]; + + if ( ! LoaderSupport.Validator.isValid( this.rawMesh.subGroupInUse ) ) { + + this.rawMesh.subGroupInUse = { + index: index, + objectName: this.rawMesh.objectName, + groupName: this.rawMesh.groupName, + materialName: this.rawMesh.activeMtlName, + smoothingGroup: this.rawMesh.smoothingGroup.normalized, + vertices: [], + indexMappingsCount: 0, + indexMappings: [], + indices: [], + colors: [], + uvs: [], + normals: [] + }; + this.rawMesh.subGroups[ index ] = this.rawMesh.subGroupInUse; + + } + + }, + + buildFace: function ( faceIndexV, faceIndexU, faceIndexN ) { + + if ( this.disregardNormals ) faceIndexN = undefined; + var scope = this; + var updateSubGroupInUse = function () { + + var faceIndexVi = parseInt( faceIndexV ); + var indexPointerV = 3 * ( faceIndexVi > 0 ? faceIndexVi - 1 : faceIndexVi + scope.vertices.length / 3 ); + var indexPointerC = scope.colors.length > 0 ? indexPointerV : null; + + var vertices = scope.rawMesh.subGroupInUse.vertices; + vertices.push( scope.vertices[ indexPointerV ++ ] ); + vertices.push( scope.vertices[ indexPointerV ++ ] ); + vertices.push( scope.vertices[ indexPointerV ] ); + + if ( indexPointerC !== null ) { + + var colors = scope.rawMesh.subGroupInUse.colors; + colors.push( scope.colors[ indexPointerC ++ ] ); + colors.push( scope.colors[ indexPointerC ++ ] ); + colors.push( scope.colors[ indexPointerC ] ); + + } + if ( faceIndexU ) { + + var faceIndexUi = parseInt( faceIndexU ); + var indexPointerU = 2 * ( faceIndexUi > 0 ? faceIndexUi - 1 : faceIndexUi + scope.uvs.length / 2 ); + var uvs = scope.rawMesh.subGroupInUse.uvs; + uvs.push( scope.uvs[ indexPointerU ++ ] ); + uvs.push( scope.uvs[ indexPointerU ] ); + + } + if ( faceIndexN ) { + + var faceIndexNi = parseInt( faceIndexN ); + var indexPointerN = 3 * ( faceIndexNi > 0 ? faceIndexNi - 1 : faceIndexNi + scope.normals.length / 3 ); + var normals = scope.rawMesh.subGroupInUse.normals; + normals.push( scope.normals[ indexPointerN ++ ] ); + normals.push( scope.normals[ indexPointerN ++ ] ); + normals.push( scope.normals[ indexPointerN ] ); + + } + + }; + + if ( this.useIndices ) { + + var mappingName = faceIndexV + ( faceIndexU ? '_' + faceIndexU : '_n' ) + ( faceIndexN ? '_' + faceIndexN : '_n' ); + var indicesPointer = this.rawMesh.subGroupInUse.indexMappings[ mappingName ]; + if ( LoaderSupport.Validator.isValid( indicesPointer ) ) { + + this.rawMesh.counts.doubleIndicesCount ++; + + } else { + + indicesPointer = this.rawMesh.subGroupInUse.vertices.length / 3; + updateSubGroupInUse(); + this.rawMesh.subGroupInUse.indexMappings[ mappingName ] = indicesPointer; + this.rawMesh.subGroupInUse.indexMappingsCount ++; + + } + this.rawMesh.subGroupInUse.indices.push( indicesPointer ); + + } else { + + updateSubGroupInUse(); + + } + this.rawMesh.counts.faceCount ++; + + }, + + createRawMeshReport: function ( inputObjectCount ) { + + return 'Input Object number: ' + inputObjectCount + + '\n\tObject name: ' + this.rawMesh.objectName + + '\n\tGroup name: ' + this.rawMesh.groupName + + '\n\tMtllib name: ' + this.rawMesh.mtllibName + + '\n\tVertex count: ' + this.vertices.length / 3 + + '\n\tNormal count: ' + this.normals.length / 3 + + '\n\tUV count: ' + this.uvs.length / 2 + + '\n\tSmoothingGroup count: ' + this.rawMesh.counts.smoothingGroupCount + + '\n\tMaterial count: ' + this.rawMesh.counts.mtlCount + + '\n\tReal MeshOutputGroup count: ' + this.rawMesh.subGroups.length; + + }, + + /** + * Clear any empty subGroup and calculate absolute vertex, normal and uv counts + */ + finalizeRawMesh: function () { + + var meshOutputGroupTemp = []; + var meshOutputGroup; + var absoluteVertexCount = 0; + var absoluteIndexMappingsCount = 0; + var absoluteIndexCount = 0; + var absoluteColorCount = 0; + var absoluteNormalCount = 0; + var absoluteUvCount = 0; + var indices; + for ( var name in this.rawMesh.subGroups ) { + + meshOutputGroup = this.rawMesh.subGroups[ name ]; + if ( meshOutputGroup.vertices.length > 0 ) { + + indices = meshOutputGroup.indices; + if ( indices.length > 0 && absoluteIndexMappingsCount > 0 ) { + + for ( var i in indices ) indices[ i ] = indices[ i ] + absoluteIndexMappingsCount; + + } + meshOutputGroupTemp.push( meshOutputGroup ); + absoluteVertexCount += meshOutputGroup.vertices.length; + absoluteIndexMappingsCount += meshOutputGroup.indexMappingsCount; + absoluteIndexCount += meshOutputGroup.indices.length; + absoluteColorCount += meshOutputGroup.colors.length; + absoluteUvCount += meshOutputGroup.uvs.length; + absoluteNormalCount += meshOutputGroup.normals.length; + + } + + } + + // do not continue if no result + var result = null; + if ( meshOutputGroupTemp.length > 0 ) { + + result = { + name: this.rawMesh.groupName !== '' ? this.rawMesh.groupName : this.rawMesh.objectName, + subGroups: meshOutputGroupTemp, + absoluteVertexCount: absoluteVertexCount, + absoluteIndexCount: absoluteIndexCount, + absoluteColorCount: absoluteColorCount, + absoluteNormalCount: absoluteNormalCount, + absoluteUvCount: absoluteUvCount, + faceCount: this.rawMesh.counts.faceCount, + doubleIndicesCount: this.rawMesh.counts.doubleIndicesCount + }; + + } + return result; + + }, + + processCompletedMesh: function () { + + var result = this.finalizeRawMesh(); + if ( LoaderSupport.Validator.isValid( result ) ) { + + if ( this.colors.length > 0 && this.colors.length !== this.vertices.length ) { + + this._throwError( 'Vertex Colors were detected, but vertex count and color count do not match!' ); + + } + if ( this.logging.enabled && this.logging.debug ) console.debug( this.createRawMeshReport( this.inputObjectCount ) ); + this.inputObjectCount ++; + + this.buildMesh( result ); + var progressBytesPercent = this.globalCounts.currentByte / this.globalCounts.totalBytes; + this.callbackProgress( 'Completed [o: ' + this.rawMesh.objectName + ' g:' + this.rawMesh.groupName + '] Total progress: ' + ( progressBytesPercent * 100 ).toFixed( 2 ) + '%', progressBytesPercent ); + this.resetRawMesh(); + return true; + + } else { + + return false; + + } + + }, + + /** + * SubGroups are transformed to too intermediate format that is forwarded to the MeshBuilder. + * It is ensured that SubGroups only contain objects with vertices (no need to check). + * + * @param result + */ + buildMesh: function ( result ) { + + var meshOutputGroups = result.subGroups; + + var vertexFA = new Float32Array( result.absoluteVertexCount ); + this.globalCounts.vertices += result.absoluteVertexCount / 3; + this.globalCounts.faces += result.faceCount; + this.globalCounts.doubleIndicesCount += result.doubleIndicesCount; + var indexUA = ( result.absoluteIndexCount > 0 ) ? new Uint32Array( result.absoluteIndexCount ) : null; + var colorFA = ( result.absoluteColorCount > 0 ) ? new Float32Array( result.absoluteColorCount ) : null; + var normalFA = ( result.absoluteNormalCount > 0 ) ? new Float32Array( result.absoluteNormalCount ) : null; + var uvFA = ( result.absoluteUvCount > 0 ) ? new Float32Array( result.absoluteUvCount ) : null; + var haveVertexColors = LoaderSupport.Validator.isValid( colorFA ); + + var meshOutputGroup; + var materialNames = []; + + var createMultiMaterial = ( meshOutputGroups.length > 1 ); + var materialIndex = 0; + var materialIndexMapping = []; + var selectedMaterialIndex; + var materialGroup; + var materialGroups = []; + + var vertexFAOffset = 0; + var indexUAOffset = 0; + var colorFAOffset = 0; + var normalFAOffset = 0; + var uvFAOffset = 0; + var materialGroupOffset = 0; + var materialGroupLength = 0; + + var materialOrg, material, materialName, materialNameOrg; + // only one specific face type + for ( var oodIndex in meshOutputGroups ) { + + if ( ! meshOutputGroups.hasOwnProperty( oodIndex ) ) continue; + meshOutputGroup = meshOutputGroups[ oodIndex ]; + + materialNameOrg = meshOutputGroup.materialName; + if ( this.rawMesh.faceType < 4 ) { + + materialName = materialNameOrg + ( haveVertexColors ? '_vertexColor' : '' ) + ( meshOutputGroup.smoothingGroup === 0 ? '_flat' : '' ); + + + } else { + + materialName = this.rawMesh.faceType === 6 ? 'defaultPointMaterial' : 'defaultLineMaterial'; + + } + materialOrg = this.materials[ materialNameOrg ]; + material = this.materials[ materialName ]; + + // both original and derived names do not lead to an existing material => need to use a default material + if ( ! LoaderSupport.Validator.isValid( materialOrg ) && ! LoaderSupport.Validator.isValid( material ) ) { + + var defaultMaterialName = haveVertexColors ? 'defaultVertexColorMaterial' : 'defaultMaterial'; + materialOrg = this.materials[ defaultMaterialName ]; + if ( this.logging.enabled ) console.warn( 'object_group "' + meshOutputGroup.objectName + '_' + + meshOutputGroup.groupName + '" was defined with unresolvable material "' + + materialNameOrg + '"! Assigning "' + defaultMaterialName + '".' ); + materialNameOrg = defaultMaterialName; + + // if names are identical then there is no need for later manipulation + if ( materialNameOrg === materialName ) { + + material = materialOrg; + materialName = defaultMaterialName; + + } + + } + if ( ! LoaderSupport.Validator.isValid( material ) ) { + + var materialCloneInstructions = { + materialNameOrg: materialNameOrg, + materialName: materialName, + materialProperties: { + vertexColors: haveVertexColors ? 2 : 0, + flatShading: meshOutputGroup.smoothingGroup === 0 + } + }; + var payload = { + cmd: 'materialData', + materials: { + materialCloneInstructions: materialCloneInstructions + } + }; + this.callbackMeshBuilder( payload ); + + // fake entry for async; sync Parser always works on material references (Builder update directly visible here) + if ( this.useAsync ) this.materials[ materialName ] = materialCloneInstructions; + + } + + if ( createMultiMaterial ) { + + // re-use material if already used before. Reduces materials array size and eliminates duplicates + selectedMaterialIndex = materialIndexMapping[ materialName ]; + if ( ! selectedMaterialIndex ) { + + selectedMaterialIndex = materialIndex; + materialIndexMapping[ materialName ] = materialIndex; + materialNames.push( materialName ); + materialIndex ++; + + } + materialGroupLength = this.useIndices ? meshOutputGroup.indices.length : meshOutputGroup.vertices.length / 3; + materialGroup = { + start: materialGroupOffset, + count: materialGroupLength, + index: selectedMaterialIndex + }; + materialGroups.push( materialGroup ); + materialGroupOffset += materialGroupLength; + + } else { + + materialNames.push( materialName ); + + } + + vertexFA.set( meshOutputGroup.vertices, vertexFAOffset ); + vertexFAOffset += meshOutputGroup.vertices.length; + + if ( indexUA ) { + + indexUA.set( meshOutputGroup.indices, indexUAOffset ); + indexUAOffset += meshOutputGroup.indices.length; + + } + + if ( colorFA ) { + + colorFA.set( meshOutputGroup.colors, colorFAOffset ); + colorFAOffset += meshOutputGroup.colors.length; + + } + + if ( normalFA ) { + + normalFA.set( meshOutputGroup.normals, normalFAOffset ); + normalFAOffset += meshOutputGroup.normals.length; + + } + if ( uvFA ) { + + uvFA.set( meshOutputGroup.uvs, uvFAOffset ); + uvFAOffset += meshOutputGroup.uvs.length; + + } + + if ( this.logging.enabled && this.logging.debug ) { + + var materialIndexLine = LoaderSupport.Validator.isValid( selectedMaterialIndex ) ? '\n\t\tmaterialIndex: ' + selectedMaterialIndex : ''; + var createdReport = '\tOutput Object no.: ' + this.outputObjectCount + + '\n\t\tgroupName: ' + meshOutputGroup.groupName + + '\n\t\tIndex: ' + meshOutputGroup.index + + '\n\t\tfaceType: ' + this.rawMesh.faceType + + '\n\t\tmaterialName: ' + meshOutputGroup.materialName + + '\n\t\tsmoothingGroup: ' + meshOutputGroup.smoothingGroup + + materialIndexLine + + '\n\t\tobjectName: ' + meshOutputGroup.objectName + + '\n\t\t#vertices: ' + meshOutputGroup.vertices.length / 3 + + '\n\t\t#indices: ' + meshOutputGroup.indices.length + + '\n\t\t#colors: ' + meshOutputGroup.colors.length / 3 + + '\n\t\t#uvs: ' + meshOutputGroup.uvs.length / 2 + + '\n\t\t#normals: ' + meshOutputGroup.normals.length / 3; + console.debug( createdReport ); + + } + + } + + this.outputObjectCount ++; + this.callbackMeshBuilder( + { + cmd: 'meshData', + progress: { + numericalValue: this.globalCounts.currentByte / this.globalCounts.totalBytes + }, + params: { + meshName: result.name + }, + materials: { + multiMaterial: createMultiMaterial, + materialNames: materialNames, + materialGroups: materialGroups + }, + buffers: { + vertices: vertexFA, + indices: indexUA, + colors: colorFA, + normals: normalFA, + uvs: uvFA + }, + // 0: mesh, 1: line, 2: point + geometryType: this.rawMesh.faceType < 4 ? 0 : ( this.rawMesh.faceType === 6 ) ? 2 : 1 + }, + [ vertexFA.buffer ], + LoaderSupport.Validator.isValid( indexUA ) ? [ indexUA.buffer ] : null, + LoaderSupport.Validator.isValid( colorFA ) ? [ colorFA.buffer ] : null, + LoaderSupport.Validator.isValid( normalFA ) ? [ normalFA.buffer ] : null, + LoaderSupport.Validator.isValid( uvFA ) ? [ uvFA.buffer ] : null + ); + + }, + + finalizeParsing: function () { + + if ( this.logging.enabled ) console.info( 'Global output object count: ' + this.outputObjectCount ); + if ( this.processCompletedMesh() && this.logging.enabled ) { + + var parserFinalReport = 'Overall counts: ' + + '\n\tVertices: ' + this.globalCounts.vertices + + '\n\tFaces: ' + this.globalCounts.faces + + '\n\tMultiple definitions: ' + this.globalCounts.doubleIndicesCount; + console.info( parserFinalReport ); + + } + + } +}; + +export { OBJLoader2 }; diff --git a/examples/jsm/loaders/PCDLoader.js b/examples/jsm/loaders/PCDLoader.js new file mode 100644 index 00000000000000..4aa13dc111be30 --- /dev/null +++ b/examples/jsm/loaders/PCDLoader.js @@ -0,0 +1,321 @@ +/** + * @author Filipe Caixeta / http://filipecaixeta.com.br + * @author Mugen87 / https://github.com/Mugen87 + * + * Description: A THREE loader for PCD ascii and binary files. + * + * Limitations: Compressed binary files are not supported. + * + */ + +import { + BufferGeometry, + DefaultLoadingManager, + FileLoader, + Float32BufferAttribute, + LoaderUtils, + Points, + PointsMaterial, + VertexColors, +} from "../../../build/three.module.js"; + +var PCDLoader = function ( manager ) { + + this.manager = ( manager !== undefined ) ? manager : DefaultLoadingManager; + this.littleEndian = true; + +}; + + +PCDLoader.prototype = { + + constructor: PCDLoader, + + load: function ( url, onLoad, onProgress, onError ) { + + var scope = this; + + var loader = new FileLoader( scope.manager ); + loader.setPath( scope.path ); + loader.setResponseType( 'arraybuffer' ); + loader.load( url, function ( data ) { + + try { + + onLoad( scope.parse( data, url ) ); + + } catch ( e ) { + + if ( onError ) { + + onError( e ); + + } else { + + throw e; + + } + + } + + }, onProgress, onError ); + + }, + + setPath: function ( value ) { + + this.path = value; + return this; + + }, + + parse: function ( data, url ) { + + function parseHeader( data ) { + + var PCDheader = {}; + var result1 = data.search( /[\r\n]DATA\s(\S*)\s/i ); + var result2 = /[\r\n]DATA\s(\S*)\s/i.exec( data.substr( result1 - 1 ) ); + + PCDheader.data = result2[ 1 ]; + PCDheader.headerLen = result2[ 0 ].length + result1; + PCDheader.str = data.substr( 0, PCDheader.headerLen ); + + // remove comments + + PCDheader.str = PCDheader.str.replace( /\#.*/gi, '' ); + + // parse + + PCDheader.version = /VERSION (.*)/i.exec( PCDheader.str ); + PCDheader.fields = /FIELDS (.*)/i.exec( PCDheader.str ); + PCDheader.size = /SIZE (.*)/i.exec( PCDheader.str ); + PCDheader.type = /TYPE (.*)/i.exec( PCDheader.str ); + PCDheader.count = /COUNT (.*)/i.exec( PCDheader.str ); + PCDheader.width = /WIDTH (.*)/i.exec( PCDheader.str ); + PCDheader.height = /HEIGHT (.*)/i.exec( PCDheader.str ); + PCDheader.viewpoint = /VIEWPOINT (.*)/i.exec( PCDheader.str ); + PCDheader.points = /POINTS (.*)/i.exec( PCDheader.str ); + + // evaluate + + if ( PCDheader.version !== null ) + PCDheader.version = parseFloat( PCDheader.version[ 1 ] ); + + if ( PCDheader.fields !== null ) + PCDheader.fields = PCDheader.fields[ 1 ].split( ' ' ); + + if ( PCDheader.type !== null ) + PCDheader.type = PCDheader.type[ 1 ].split( ' ' ); + + if ( PCDheader.width !== null ) + PCDheader.width = parseInt( PCDheader.width[ 1 ] ); + + if ( PCDheader.height !== null ) + PCDheader.height = parseInt( PCDheader.height[ 1 ] ); + + if ( PCDheader.viewpoint !== null ) + PCDheader.viewpoint = PCDheader.viewpoint[ 1 ]; + + if ( PCDheader.points !== null ) + PCDheader.points = parseInt( PCDheader.points[ 1 ], 10 ); + + if ( PCDheader.points === null ) + PCDheader.points = PCDheader.width * PCDheader.height; + + if ( PCDheader.size !== null ) { + + PCDheader.size = PCDheader.size[ 1 ].split( ' ' ).map( function ( x ) { + + return parseInt( x, 10 ); + + } ); + + } + + if ( PCDheader.count !== null ) { + + PCDheader.count = PCDheader.count[ 1 ].split( ' ' ).map( function ( x ) { + + return parseInt( x, 10 ); + + } ); + + } else { + + PCDheader.count = []; + + for ( var i = 0, l = PCDheader.fields.length; i < l; i ++ ) { + + PCDheader.count.push( 1 ); + + } + + } + + PCDheader.offset = {}; + + var sizeSum = 0; + + for ( var i = 0, l = PCDheader.fields.length; i < l; i ++ ) { + + if ( PCDheader.data === 'ascii' ) { + + PCDheader.offset[ PCDheader.fields[ i ] ] = i; + + } else { + + PCDheader.offset[ PCDheader.fields[ i ] ] = sizeSum; + sizeSum += PCDheader.size[ i ]; + + } + + } + + // for binary only + + PCDheader.rowSize = sizeSum; + + return PCDheader; + + } + + var textData = LoaderUtils.decodeText( data ); + + // parse header (always ascii format) + + var PCDheader = parseHeader( textData ); + + // parse data + + var position = []; + var normal = []; + var color = []; + + // ascii + + if ( PCDheader.data === 'ascii' ) { + + var offset = PCDheader.offset; + var pcdData = textData.substr( PCDheader.headerLen ); + var lines = pcdData.split( '\n' ); + + for ( var i = 0, l = lines.length; i < l; i ++ ) { + + if ( lines[ i ] === '' ) continue; + + var line = lines[ i ].split( ' ' ); + + if ( offset.x !== undefined ) { + + position.push( parseFloat( line[ offset.x ] ) ); + position.push( parseFloat( line[ offset.y ] ) ); + position.push( parseFloat( line[ offset.z ] ) ); + + } + + if ( offset.rgb !== undefined ) { + + var rgb = parseFloat( line[ offset.rgb ] ); + var r = ( rgb >> 16 ) & 0x0000ff; + var g = ( rgb >> 8 ) & 0x0000ff; + var b = ( rgb >> 0 ) & 0x0000ff; + color.push( r / 255, g / 255, b / 255 ); + + } + + if ( offset.normal_x !== undefined ) { + + normal.push( parseFloat( line[ offset.normal_x ] ) ); + normal.push( parseFloat( line[ offset.normal_y ] ) ); + normal.push( parseFloat( line[ offset.normal_z ] ) ); + + } + + } + + } + + // binary + + if ( PCDheader.data === 'binary_compressed' ) { + + console.error( 'THREE.PCDLoader: binary_compressed files are not supported' ); + return; + + } + + if ( PCDheader.data === 'binary' ) { + + var dataview = new DataView( data, PCDheader.headerLen ); + var offset = PCDheader.offset; + + for ( var i = 0, row = 0; i < PCDheader.points; i ++, row += PCDheader.rowSize ) { + + if ( offset.x !== undefined ) { + + position.push( dataview.getFloat32( row + offset.x, this.littleEndian ) ); + position.push( dataview.getFloat32( row + offset.y, this.littleEndian ) ); + position.push( dataview.getFloat32( row + offset.z, this.littleEndian ) ); + + } + + if ( offset.rgb !== undefined ) { + + color.push( dataview.getUint8( row + offset.rgb + 2 ) / 255.0 ); + color.push( dataview.getUint8( row + offset.rgb + 1 ) / 255.0 ); + color.push( dataview.getUint8( row + offset.rgb + 0 ) / 255.0 ); + + } + + if ( offset.normal_x !== undefined ) { + + normal.push( dataview.getFloat32( row + offset.normal_x, this.littleEndian ) ); + normal.push( dataview.getFloat32( row + offset.normal_y, this.littleEndian ) ); + normal.push( dataview.getFloat32( row + offset.normal_z, this.littleEndian ) ); + + } + + } + + } + + // build geometry + + var geometry = new BufferGeometry(); + + if ( position.length > 0 ) geometry.addAttribute( 'position', new Float32BufferAttribute( position, 3 ) ); + if ( normal.length > 0 ) geometry.addAttribute( 'normal', new Float32BufferAttribute( normal, 3 ) ); + if ( color.length > 0 ) geometry.addAttribute( 'color', new Float32BufferAttribute( color, 3 ) ); + + geometry.computeBoundingSphere(); + + // build material + + var material = new PointsMaterial( { size: 0.005 } ); + + if ( color.length > 0 ) { + + material.vertexColors = VertexColors; + + } else { + + material.color.setHex( Math.random() * 0xffffff ); + + } + + // build mesh + + var mesh = new Points( geometry, material ); + var name = url.split( '' ).reverse().join( '' ); + name = /([^\/]*)/.exec( name ); + name = name[ 1 ].split( '' ).reverse().join( '' ); + mesh.name = name; + + return mesh; + + } + +}; + +export { PCDLoader }; diff --git a/examples/jsm/loaders/PDBLoader.js b/examples/jsm/loaders/PDBLoader.js new file mode 100644 index 00000000000000..f0ba967054301a --- /dev/null +++ b/examples/jsm/loaders/PDBLoader.js @@ -0,0 +1,226 @@ +/** + * @author alteredq / http://alteredqualia.com/ + * @author Mugen87 / https://github.com/Mugen87 + */ + +import { + BufferGeometry, + DefaultLoadingManager, + FileLoader, + Float32BufferAttribute +} from "../../../build/three.module.js"; + +var PDBLoader = function ( manager ) { + + this.manager = ( manager !== undefined ) ? manager : DefaultLoadingManager; + +}; + +PDBLoader.prototype = { + + constructor: PDBLoader, + + load: function ( url, onLoad, onProgress, onError ) { + + var scope = this; + + var loader = new FileLoader( scope.manager ); + loader.setPath( scope.path ); + loader.load( url, function ( text ) { + + onLoad( scope.parse( text ) ); + + }, onProgress, onError ); + + }, + + setPath: function ( value ) { + + this.path = value; + return this; + + }, + + // Based on CanvasMol PDB parser + + parse: function ( text ) { + + function trim( text ) { + + return text.replace( /^\s\s*/, '' ).replace( /\s\s*$/, '' ); + + } + + function capitalize( text ) { + + return text.charAt( 0 ).toUpperCase() + text.substr( 1 ).toLowerCase(); + + } + + function hash( s, e ) { + + return 's' + Math.min( s, e ) + 'e' + Math.max( s, e ); + + } + + function parseBond( start, length ) { + + var eatom = parseInt( lines[ i ].substr( start, length ) ); + + if ( eatom ) { + + var h = hash( satom, eatom ); + + if ( bhash[ h ] === undefined ) { + + bonds.push( [ satom - 1, eatom - 1, 1 ] ); + bhash[ h ] = bonds.length - 1; + + } else { + + // doesn't really work as almost all PDBs + // have just normal bonds appearing multiple + // times instead of being double/triple bonds + // bonds[bhash[h]][2] += 1; + + } + + } + + } + + function buildGeometry() { + + var build = { + geometryAtoms: new BufferGeometry(), + geometryBonds: new BufferGeometry(), + json: { + atoms: atoms, + bonds: bonds + } + }; + + var geometryAtoms = build.geometryAtoms; + var geometryBonds = build.geometryBonds; + + var i, l; + + var verticesAtoms = []; + var colorsAtoms = []; + var verticesBonds = []; + + // atoms + + for ( i = 0, l = atoms.length; i < l; i ++ ) { + + var atom = atoms[ i ]; + + var x = atom[ 0 ]; + var y = atom[ 1 ]; + var z = atom[ 2 ]; + + verticesAtoms.push( x, y, z ); + + var r = atom[ 3 ][ 0 ] / 255; + var g = atom[ 3 ][ 1 ] / 255; + var b = atom[ 3 ][ 2 ] / 255; + + colorsAtoms.push( r, g, b ); + + } + + // bonds + + for ( i = 0, l = bonds.length; i < l; i ++ ) { + + var bond = bonds[ i ]; + + var start = bond[ 0 ]; + var end = bond[ 1 ]; + + verticesBonds.push( verticesAtoms[ ( start * 3 ) + 0 ] ); + verticesBonds.push( verticesAtoms[ ( start * 3 ) + 1 ] ); + verticesBonds.push( verticesAtoms[ ( start * 3 ) + 2 ] ); + + verticesBonds.push( verticesAtoms[ ( end * 3 ) + 0 ] ); + verticesBonds.push( verticesAtoms[ ( end * 3 ) + 1 ] ); + verticesBonds.push( verticesAtoms[ ( end * 3 ) + 2 ] ); + + } + + // build geometry + + geometryAtoms.addAttribute( 'position', new Float32BufferAttribute( verticesAtoms, 3 ) ); + geometryAtoms.addAttribute( 'color', new Float32BufferAttribute( colorsAtoms, 3 ) ); + + geometryBonds.addAttribute( 'position', new Float32BufferAttribute( verticesBonds, 3 ) ); + + return build; + + } + + var CPK = { h: [ 255, 255, 255 ], he: [ 217, 255, 255 ], li: [ 204, 128, 255 ], be: [ 194, 255, 0 ], b: [ 255, 181, 181 ], c: [ 144, 144, 144 ], n: [ 48, 80, 248 ], o: [ 255, 13, 13 ], f: [ 144, 224, 80 ], ne: [ 179, 227, 245 ], na: [ 171, 92, 242 ], mg: [ 138, 255, 0 ], al: [ 191, 166, 166 ], si: [ 240, 200, 160 ], p: [ 255, 128, 0 ], s: [ 255, 255, 48 ], cl: [ 31, 240, 31 ], ar: [ 128, 209, 227 ], k: [ 143, 64, 212 ], ca: [ 61, 255, 0 ], sc: [ 230, 230, 230 ], ti: [ 191, 194, 199 ], v: [ 166, 166, 171 ], cr: [ 138, 153, 199 ], mn: [ 156, 122, 199 ], fe: [ 224, 102, 51 ], co: [ 240, 144, 160 ], ni: [ 80, 208, 80 ], cu: [ 200, 128, 51 ], zn: [ 125, 128, 176 ], ga: [ 194, 143, 143 ], ge: [ 102, 143, 143 ], as: [ 189, 128, 227 ], se: [ 255, 161, 0 ], br: [ 166, 41, 41 ], kr: [ 92, 184, 209 ], rb: [ 112, 46, 176 ], sr: [ 0, 255, 0 ], y: [ 148, 255, 255 ], zr: [ 148, 224, 224 ], nb: [ 115, 194, 201 ], mo: [ 84, 181, 181 ], tc: [ 59, 158, 158 ], ru: [ 36, 143, 143 ], rh: [ 10, 125, 140 ], pd: [ 0, 105, 133 ], ag: [ 192, 192, 192 ], cd: [ 255, 217, 143 ], in: [ 166, 117, 115 ], sn: [ 102, 128, 128 ], sb: [ 158, 99, 181 ], te: [ 212, 122, 0 ], i: [ 148, 0, 148 ], xe: [ 66, 158, 176 ], cs: [ 87, 23, 143 ], ba: [ 0, 201, 0 ], la: [ 112, 212, 255 ], ce: [ 255, 255, 199 ], pr: [ 217, 255, 199 ], nd: [ 199, 255, 199 ], pm: [ 163, 255, 199 ], sm: [ 143, 255, 199 ], eu: [ 97, 255, 199 ], gd: [ 69, 255, 199 ], tb: [ 48, 255, 199 ], dy: [ 31, 255, 199 ], ho: [ 0, 255, 156 ], er: [ 0, 230, 117 ], tm: [ 0, 212, 82 ], yb: [ 0, 191, 56 ], lu: [ 0, 171, 36 ], hf: [ 77, 194, 255 ], ta: [ 77, 166, 255 ], w: [ 33, 148, 214 ], re: [ 38, 125, 171 ], os: [ 38, 102, 150 ], ir: [ 23, 84, 135 ], pt: [ 208, 208, 224 ], au: [ 255, 209, 35 ], hg: [ 184, 184, 208 ], tl: [ 166, 84, 77 ], pb: [ 87, 89, 97 ], bi: [ 158, 79, 181 ], po: [ 171, 92, 0 ], at: [ 117, 79, 69 ], rn: [ 66, 130, 150 ], fr: [ 66, 0, 102 ], ra: [ 0, 125, 0 ], ac: [ 112, 171, 250 ], th: [ 0, 186, 255 ], pa: [ 0, 161, 255 ], u: [ 0, 143, 255 ], np: [ 0, 128, 255 ], pu: [ 0, 107, 255 ], am: [ 84, 92, 242 ], cm: [ 120, 92, 227 ], bk: [ 138, 79, 227 ], cf: [ 161, 54, 212 ], es: [ 179, 31, 212 ], fm: [ 179, 31, 186 ], md: [ 179, 13, 166 ], no: [ 189, 13, 135 ], lr: [ 199, 0, 102 ], rf: [ 204, 0, 89 ], db: [ 209, 0, 79 ], sg: [ 217, 0, 69 ], bh: [ 224, 0, 56 ], hs: [ 230, 0, 46 ], mt: [ 235, 0, 38 ], ds: [ 235, 0, 38 ], rg: [ 235, 0, 38 ], cn: [ 235, 0, 38 ], uut: [ 235, 0, 38 ], uuq: [ 235, 0, 38 ], uup: [ 235, 0, 38 ], uuh: [ 235, 0, 38 ], uus: [ 235, 0, 38 ], uuo: [ 235, 0, 38 ] }; + + var atoms = []; + var bonds = []; + var histogram = {}; + + var bhash = {}; + + var x, y, z, index, e; + + // parse + + var lines = text.split( '\n' ); + + for ( var i = 0, l = lines.length; i < l; i ++ ) { + + if ( lines[ i ].substr( 0, 4 ) === 'ATOM' || lines[ i ].substr( 0, 6 ) === 'HETATM' ) { + + x = parseFloat( lines[ i ].substr( 30, 7 ) ); + y = parseFloat( lines[ i ].substr( 38, 7 ) ); + z = parseFloat( lines[ i ].substr( 46, 7 ) ); + index = parseInt( lines[ i ].substr( 6, 5 ) ) - 1; + + e = trim( lines[ i ].substr( 76, 2 ) ).toLowerCase(); + + if ( e === '' ) { + + e = trim( lines[ i ].substr( 12, 2 ) ).toLowerCase(); + + } + + atoms[ index ] = [ x, y, z, CPK[ e ], capitalize( e ) ]; + + if ( histogram[ e ] === undefined ) { + + histogram[ e ] = 1; + + } else { + + histogram[ e ] += 1; + + } + + } else if ( lines[ i ].substr( 0, 6 ) === 'CONECT' ) { + + var satom = parseInt( lines[ i ].substr( 6, 5 ) ); + + parseBond( 11, 5 ); + parseBond( 16, 5 ); + parseBond( 21, 5 ); + parseBond( 26, 5 ); + + } + + } + + // build and return geometry + + return buildGeometry(); + + } + +}; + +export { PDBLoader }; diff --git a/examples/jsm/loaders/PLYLoader.js b/examples/jsm/loaders/PLYLoader.js new file mode 100644 index 00000000000000..e1825056dc8293 --- /dev/null +++ b/examples/jsm/loaders/PLYLoader.js @@ -0,0 +1,515 @@ +/** + * @author Wei Meng / http://about.me/menway + * + * Description: A THREE loader for PLY ASCII files (known as the Polygon + * File Format or the Stanford Triangle Format). + * + * Limitations: ASCII decoding assumes file is UTF-8. + * + * Usage: + * var loader = new PLYLoader(); + * loader.load('./models/ply/ascii/dolphins.ply', function (geometry) { + * + * scene.add( new Mesh( geometry ) ); + * + * } ); + * + * If the PLY file uses non standard property names, they can be mapped while + * loading. For example, the following maps the properties + * “diffuse_(red|green|blue)” in the file to standard color names. + * + * loader.setPropertyNameMapping( { + * diffuse_red: 'red', + * diffuse_green: 'green', + * diffuse_blue: 'blue' + * } ); + * + */ + + +import { + BufferGeometry, + DefaultLoadingManager, + FileLoader, + Float32BufferAttribute, + LoaderUtils, +} from "../../../build/three.module.js"; + +var PLYLoader = function ( manager ) { + + this.manager = ( manager !== undefined ) ? manager : DefaultLoadingManager; + + this.propertyNameMapping = {}; + +}; + +PLYLoader.prototype = { + + constructor: PLYLoader, + + load: function ( url, onLoad, onProgress, onError ) { + + var scope = this; + + var loader = new FileLoader( this.manager ); + loader.setPath( this.path ); + loader.setResponseType( 'arraybuffer' ); + loader.load( url, function ( text ) { + + onLoad( scope.parse( text ) ); + + }, onProgress, onError ); + + }, + + setPath: function ( value ) { + + this.path = value; + return this; + + }, + + setPropertyNameMapping: function ( mapping ) { + + this.propertyNameMapping = mapping; + + }, + + parse: function ( data ) { + + function parseHeader( data ) { + + var patternHeader = /ply([\s\S]*)end_header\r?\n/; + var headerText = ''; + var headerLength = 0; + var result = patternHeader.exec( data ); + + if ( result !== null ) { + + headerText = result[ 1 ]; + headerLength = result[ 0 ].length; + + } + + var header = { + comments: [], + elements: [], + headerLength: headerLength + }; + + var lines = headerText.split( '\n' ); + var currentElement; + var lineType, lineValues; + + function make_ply_element_property( propertValues, propertyNameMapping ) { + + var property = { type: propertValues[ 0 ] }; + + if ( property.type === 'list' ) { + + property.name = propertValues[ 3 ]; + property.countType = propertValues[ 1 ]; + property.itemType = propertValues[ 2 ]; + + } else { + + property.name = propertValues[ 1 ]; + + } + + if ( property.name in propertyNameMapping ) { + + property.name = propertyNameMapping[ property.name ]; + + } + + return property; + + } + + for ( var i = 0; i < lines.length; i ++ ) { + + var line = lines[ i ]; + line = line.trim(); + + if ( line === '' ) continue; + + lineValues = line.split( /\s+/ ); + lineType = lineValues.shift(); + line = lineValues.join( ' ' ); + + switch ( lineType ) { + + case 'format': + + header.format = lineValues[ 0 ]; + header.version = lineValues[ 1 ]; + + break; + + case 'comment': + + header.comments.push( line ); + + break; + + case 'element': + + if ( currentElement !== undefined ) { + + header.elements.push( currentElement ); + + } + + currentElement = {}; + currentElement.name = lineValues[ 0 ]; + currentElement.count = parseInt( lineValues[ 1 ] ); + currentElement.properties = []; + + break; + + case 'property': + + currentElement.properties.push( make_ply_element_property( lineValues, scope.propertyNameMapping ) ); + + break; + + + default: + + console.log( 'unhandled', lineType, lineValues ); + + } + + } + + if ( currentElement !== undefined ) { + + header.elements.push( currentElement ); + + } + + return header; + + } + + function parseASCIINumber( n, type ) { + + switch ( type ) { + + case 'char': case 'uchar': case 'short': case 'ushort': case 'int': case 'uint': + case 'int8': case 'uint8': case 'int16': case 'uint16': case 'int32': case 'uint32': + + return parseInt( n ); + + case 'float': case 'double': case 'float32': case 'float64': + + return parseFloat( n ); + + } + + } + + function parseASCIIElement( properties, line ) { + + var values = line.split( /\s+/ ); + + var element = {}; + + for ( var i = 0; i < properties.length; i ++ ) { + + if ( properties[ i ].type === 'list' ) { + + var list = []; + var n = parseASCIINumber( values.shift(), properties[ i ].countType ); + + for ( var j = 0; j < n; j ++ ) { + + list.push( parseASCIINumber( values.shift(), properties[ i ].itemType ) ); + + } + + element[ properties[ i ].name ] = list; + + } else { + + element[ properties[ i ].name ] = parseASCIINumber( values.shift(), properties[ i ].type ); + + } + + } + + return element; + + } + + function parseASCII( data, header ) { + + // PLY ascii format specification, as per http://en.wikipedia.org/wiki/PLY_(file_format) + + var buffer = { + indices: [], + vertices: [], + normals: [], + uvs: [], + faceVertexUvs: [], + colors: [] + }; + + var result; + + var patternBody = /end_header\s([\s\S]*)$/; + var body = ''; + if ( ( result = patternBody.exec( data ) ) !== null ) { + + body = result[ 1 ]; + + } + + var lines = body.split( '\n' ); + var currentElement = 0; + var currentElementCount = 0; + + for ( var i = 0; i < lines.length; i ++ ) { + + var line = lines[ i ]; + line = line.trim(); + if ( line === '' ) { + + continue; + + } + + if ( currentElementCount >= header.elements[ currentElement ].count ) { + + currentElement ++; + currentElementCount = 0; + + } + + var element = parseASCIIElement( header.elements[ currentElement ].properties, line ); + + handleElement( buffer, header.elements[ currentElement ].name, element ); + + currentElementCount ++; + + } + + return postProcess( buffer ); + + } + + function postProcess( buffer ) { + + var geometry = new BufferGeometry(); + + // mandatory buffer data + + if ( buffer.indices.length > 0 ) { + + geometry.setIndex( buffer.indices ); + + } + + geometry.addAttribute( 'position', new Float32BufferAttribute( buffer.vertices, 3 ) ); + + // optional buffer data + + if ( buffer.normals.length > 0 ) { + + geometry.addAttribute( 'normal', new Float32BufferAttribute( buffer.normals, 3 ) ); + + } + + if ( buffer.uvs.length > 0 ) { + + geometry.addAttribute( 'uv', new Float32BufferAttribute( buffer.uvs, 2 ) ); + + } + + if ( buffer.colors.length > 0 ) { + + geometry.addAttribute( 'color', new Float32BufferAttribute( buffer.colors, 3 ) ); + + } + + if ( buffer.faceVertexUvs.length > 0 ) { + + geometry = geometry.toNonIndexed(); + geometry.addAttribute( 'uv', new Float32BufferAttribute( buffer.faceVertexUvs, 2 ) ); + + } + + geometry.computeBoundingSphere(); + + return geometry; + + } + + function handleElement( buffer, elementName, element ) { + + if ( elementName === 'vertex' ) { + + buffer.vertices.push( element.x, element.y, element.z ); + + if ( 'nx' in element && 'ny' in element && 'nz' in element ) { + + buffer.normals.push( element.nx, element.ny, element.nz ); + + } + + if ( 's' in element && 't' in element ) { + + buffer.uvs.push( element.s, element.t ); + + } + + if ( 'red' in element && 'green' in element && 'blue' in element ) { + + buffer.colors.push( element.red / 255.0, element.green / 255.0, element.blue / 255.0 ); + + } + + } else if ( elementName === 'face' ) { + + var vertex_indices = element.vertex_indices || element.vertex_index; // issue #9338 + var texcoord = element.texcoord; + + if ( vertex_indices.length === 3 ) { + + buffer.indices.push( vertex_indices[ 0 ], vertex_indices[ 1 ], vertex_indices[ 2 ] ); + + if ( texcoord && texcoord.length === 6 ) { + + buffer.faceVertexUvs.push( texcoord[ 0 ], texcoord[ 1 ] ); + buffer.faceVertexUvs.push( texcoord[ 2 ], texcoord[ 3 ] ); + buffer.faceVertexUvs.push( texcoord[ 4 ], texcoord[ 5 ] ); + + } + + } else if ( vertex_indices.length === 4 ) { + + buffer.indices.push( vertex_indices[ 0 ], vertex_indices[ 1 ], vertex_indices[ 3 ] ); + buffer.indices.push( vertex_indices[ 1 ], vertex_indices[ 2 ], vertex_indices[ 3 ] ); + + } + + } + + } + + function binaryRead( dataview, at, type, little_endian ) { + + switch ( type ) { + + // corespondences for non-specific length types here match rply: + case 'int8': case 'char': return [ dataview.getInt8( at ), 1 ]; + case 'uint8': case 'uchar': return [ dataview.getUint8( at ), 1 ]; + case 'int16': case 'short': return [ dataview.getInt16( at, little_endian ), 2 ]; + case 'uint16': case 'ushort': return [ dataview.getUint16( at, little_endian ), 2 ]; + case 'int32': case 'int': return [ dataview.getInt32( at, little_endian ), 4 ]; + case 'uint32': case 'uint': return [ dataview.getUint32( at, little_endian ), 4 ]; + case 'float32': case 'float': return [ dataview.getFloat32( at, little_endian ), 4 ]; + case 'float64': case 'double': return [ dataview.getFloat64( at, little_endian ), 8 ]; + + } + + } + + function binaryReadElement( dataview, at, properties, little_endian ) { + + var element = {}; + var result, read = 0; + + for ( var i = 0; i < properties.length; i ++ ) { + + if ( properties[ i ].type === 'list' ) { + + var list = []; + + result = binaryRead( dataview, at + read, properties[ i ].countType, little_endian ); + var n = result[ 0 ]; + read += result[ 1 ]; + + for ( var j = 0; j < n; j ++ ) { + + result = binaryRead( dataview, at + read, properties[ i ].itemType, little_endian ); + list.push( result[ 0 ] ); + read += result[ 1 ]; + + } + + element[ properties[ i ].name ] = list; + + } else { + + result = binaryRead( dataview, at + read, properties[ i ].type, little_endian ); + element[ properties[ i ].name ] = result[ 0 ]; + read += result[ 1 ]; + + } + + } + + return [ element, read ]; + + } + + function parseBinary( data, header ) { + + var buffer = { + indices: [], + vertices: [], + normals: [], + uvs: [], + faceVertexUvs: [], + colors: [] + }; + + var little_endian = ( header.format === 'binary_little_endian' ); + var body = new DataView( data, header.headerLength ); + var result, loc = 0; + + for ( var currentElement = 0; currentElement < header.elements.length; currentElement ++ ) { + + for ( var currentElementCount = 0; currentElementCount < header.elements[ currentElement ].count; currentElementCount ++ ) { + + result = binaryReadElement( body, loc, header.elements[ currentElement ].properties, little_endian ); + loc += result[ 1 ]; + var element = result[ 0 ]; + + handleElement( buffer, header.elements[ currentElement ].name, element ); + + } + + } + + return postProcess( buffer ); + + } + + // + + var geometry; + var scope = this; + + if ( data instanceof ArrayBuffer ) { + + var text = LoaderUtils.decodeText( new Uint8Array( data ) ); + var header = parseHeader( text ); + + geometry = header.format === 'ascii' ? parseASCII( text, header ) : parseBinary( data, header ); + + } else { + + geometry = parseASCII( data, parseHeader( data ) ); + + } + + return geometry; + + } + +}; + +export { PLYLoader }; diff --git a/examples/jsm/loaders/PRWMLoader.js b/examples/jsm/loaders/PRWMLoader.js new file mode 100644 index 00000000000000..79f21ff038e261 --- /dev/null +++ b/examples/jsm/loaders/PRWMLoader.js @@ -0,0 +1,302 @@ +/** + * @author Kevin Chapelier / https://github.com/kchapelier + * See https://github.com/kchapelier/PRWM for more informations about this file format + */ + +import { + BufferAttribute, + BufferGeometry, + DefaultLoadingManager, + FileLoader +} from "../../../build/three.module.js"; + +var bigEndianPlatform = null; + +/** + * Check if the endianness of the platform is big-endian (most significant bit first) + * @returns {boolean} True if big-endian, false if little-endian + */ +function isBigEndianPlatform() { + + if ( bigEndianPlatform === null ) { + + var buffer = new ArrayBuffer( 2 ), + uint8Array = new Uint8Array( buffer ), + uint16Array = new Uint16Array( buffer ); + + uint8Array[ 0 ] = 0xAA; // set first byte + uint8Array[ 1 ] = 0xBB; // set second byte + bigEndianPlatform = ( uint16Array[ 0 ] === 0xAABB ); + + } + + return bigEndianPlatform; + +} + +// match the values defined in the spec to the TypedArray types +var InvertedEncodingTypes = [ + null, + Float32Array, + null, + Int8Array, + Int16Array, + null, + Int32Array, + Uint8Array, + Uint16Array, + null, + Uint32Array +]; + +// define the method to use on a DataView, corresponding the TypedArray type +var getMethods = { + Uint16Array: 'getUint16', + Uint32Array: 'getUint32', + Int16Array: 'getInt16', + Int32Array: 'getInt32', + Float32Array: 'getFloat32', + Float64Array: 'getFloat64' +}; + + +function copyFromBuffer( sourceArrayBuffer, viewType, position, length, fromBigEndian ) { + + var bytesPerElement = viewType.BYTES_PER_ELEMENT, + result; + + if ( fromBigEndian === isBigEndianPlatform() || bytesPerElement === 1 ) { + + result = new viewType( sourceArrayBuffer, position, length ); + + } else { + + var readView = new DataView( sourceArrayBuffer, position, length * bytesPerElement ), + getMethod = getMethods[ viewType.name ], + littleEndian = ! fromBigEndian, + i = 0; + + result = new viewType( length ); + + for ( ; i < length; i ++ ) { + + result[ i ] = readView[ getMethod ]( i * bytesPerElement, littleEndian ); + + } + + } + + return result; + +} + + +function decodePrwm( buffer ) { + + var array = new Uint8Array( buffer ), + version = array[ 0 ], + flags = array[ 1 ], + indexedGeometry = !! ( flags >> 7 & 0x01 ), + indicesType = flags >> 6 & 0x01, + bigEndian = ( flags >> 5 & 0x01 ) === 1, + attributesNumber = flags & 0x1F, + valuesNumber = 0, + indicesNumber = 0; + + if ( bigEndian ) { + + valuesNumber = ( array[ 2 ] << 16 ) + ( array[ 3 ] << 8 ) + array[ 4 ]; + indicesNumber = ( array[ 5 ] << 16 ) + ( array[ 6 ] << 8 ) + array[ 7 ]; + + } else { + + valuesNumber = array[ 2 ] + ( array[ 3 ] << 8 ) + ( array[ 4 ] << 16 ); + indicesNumber = array[ 5 ] + ( array[ 6 ] << 8 ) + ( array[ 7 ] << 16 ); + + } + + /** PRELIMINARY CHECKS **/ + + if ( version === 0 ) { + + throw new Error( 'PRWM decoder: Invalid format version: 0' ); + + } else if ( version !== 1 ) { + + throw new Error( 'PRWM decoder: Unsupported format version: ' + version ); + + } + + if ( ! indexedGeometry ) { + + if ( indicesType !== 0 ) { + + throw new Error( 'PRWM decoder: Indices type must be set to 0 for non-indexed geometries' ); + + } else if ( indicesNumber !== 0 ) { + + throw new Error( 'PRWM decoder: Number of indices must be set to 0 for non-indexed geometries' ); + + } + + } + + /** PARSING **/ + + var pos = 8; + + var attributes = {}, + attributeName, + char, + attributeType, + cardinality, + encodingType, + arrayType, + values, + indices, + i; + + for ( i = 0; i < attributesNumber; i ++ ) { + + attributeName = ''; + + while ( pos < array.length ) { + + char = array[ pos ]; + pos ++; + + if ( char === 0 ) { + + break; + + } else { + + attributeName += String.fromCharCode( char ); + + } + + } + + flags = array[ pos ]; + + attributeType = flags >> 7 & 0x01; + cardinality = ( flags >> 4 & 0x03 ) + 1; + encodingType = flags & 0x0F; + arrayType = InvertedEncodingTypes[ encodingType ]; + + pos ++; + + // padding to next multiple of 4 + pos = Math.ceil( pos / 4 ) * 4; + + values = copyFromBuffer( buffer, arrayType, pos, cardinality * valuesNumber, bigEndian ); + + pos += arrayType.BYTES_PER_ELEMENT * cardinality * valuesNumber; + + attributes[ attributeName ] = { + type: attributeType, + cardinality: cardinality, + values: values + }; + + } + + pos = Math.ceil( pos / 4 ) * 4; + + indices = null; + + if ( indexedGeometry ) { + + indices = copyFromBuffer( + buffer, + indicesType === 1 ? Uint32Array : Uint16Array, + pos, + indicesNumber, + bigEndian + ); + + } + + return { + version: version, + attributes: attributes, + indices: indices + }; + +} + +// Define the public interface + +var PRWMLoader = function PRWMLoader( manager ) { + + this.manager = ( manager !== undefined ) ? manager : DefaultLoadingManager; + +}; + +PRWMLoader.prototype = { + + constructor: PRWMLoader, + + load: function ( url, onLoad, onProgress, onError ) { + + var scope = this; + + var loader = new FileLoader( scope.manager ); + loader.setPath( scope.path ); + loader.setResponseType( 'arraybuffer' ); + + url = url.replace( /\*/g, isBigEndianPlatform() ? 'be' : 'le' ); + + loader.load( url, function ( arrayBuffer ) { + + onLoad( scope.parse( arrayBuffer ) ); + + }, onProgress, onError ); + + }, + + setPath: function ( value ) { + + this.path = value; + return this; + + }, + + parse: function ( arrayBuffer ) { + + console.time( 'PRWMLoader' ); + + var data = decodePrwm( arrayBuffer ), + attributesKey = Object.keys( data.attributes ), + bufferGeometry = new BufferGeometry(), + attribute, + i; + + for ( i = 0; i < attributesKey.length; i ++ ) { + + attribute = data.attributes[ attributesKey[ i ] ]; + bufferGeometry.addAttribute( attributesKey[ i ], new BufferAttribute( attribute.values, attribute.cardinality, attribute.normalized ) ); + + } + + if ( data.indices !== null ) { + + bufferGeometry.setIndex( new BufferAttribute( data.indices, 1 ) ); + + } + + console.timeEnd( 'PRWMLoader' ); + + return bufferGeometry; + + } + +}; + +PRWMLoader.isBigEndianPlatform = function () { + + return isBigEndianPlatform(); + +}; + +export { PRWMLoader }; diff --git a/examples/jsm/loaders/PVRLoader.js b/examples/jsm/loaders/PVRLoader.js new file mode 100644 index 00000000000000..2a3f0d9f6f2aff --- /dev/null +++ b/examples/jsm/loaders/PVRLoader.js @@ -0,0 +1,256 @@ +/* + * PVRLoader + * Author: pierre lepers + * Date: 17/09/2014 11:09 + * + * PVR v2 (legacy) parser + * TODO : Add Support for PVR v3 format + * TODO : implement loadMipmaps option + */ + +import { + CompressedTextureLoader, + RGB_PVRTC_2BPPV1_Format, + RGB_PVRTC_4BPPV1_Format, + RGBA_PVRTC_2BPPV1_Format, + RGBA_PVRTC_4BPPV1_Format, +} from "../../../build/three.module.js"; + +var PVRLoader = function ( manager ) { + + CompressedTextureLoader.call( this, manager ); + + this._parser = PVRLoader.parse; + +}; + +PVRLoader.prototype = Object.create( CompressedTextureLoader.prototype ); +PVRLoader.prototype.constructor = PVRLoader; + + +PVRLoader.parse = function ( buffer, loadMipmaps ) { + + var headerLengthInt = 13; + var header = new Uint32Array( buffer, 0, headerLengthInt ); + + var pvrDatas = { + buffer: buffer, + header: header, + loadMipmaps: loadMipmaps + }; + + if ( header[ 0 ] === 0x03525650 ) { + + // PVR v3 + + return PVRLoader._parseV3( pvrDatas ); + + } else if ( header[ 11 ] === 0x21525650 ) { + + // PVR v2 + + return PVRLoader._parseV2( pvrDatas ); + + } else { + + console.error( 'PVRLoader: Unknown PVR format.' ); + + } + +}; + +PVRLoader._parseV3 = function ( pvrDatas ) { + + var header = pvrDatas.header; + var bpp, format; + + + var metaLen = header[ 12 ], + pixelFormat = header[ 2 ], + height = header[ 6 ], + width = header[ 7 ], + // numSurfs = header[ 9 ], + numFaces = header[ 10 ], + numMipmaps = header[ 11 ]; + + switch ( pixelFormat ) { + + case 0 : // PVRTC 2bpp RGB + bpp = 2; + format = RGB_PVRTC_2BPPV1_Format; + break; + + case 1 : // PVRTC 2bpp RGBA + bpp = 2; + format = RGBA_PVRTC_2BPPV1_Format; + break; + + case 2 : // PVRTC 4bpp RGB + bpp = 4; + format = RGB_PVRTC_4BPPV1_Format; + break; + + case 3 : // PVRTC 4bpp RGBA + bpp = 4; + format = RGBA_PVRTC_4BPPV1_Format; + break; + + default : + console.error( 'PVRLoader: Unsupported PVR format:', pixelFormat ); + + } + + pvrDatas.dataPtr = 52 + metaLen; + pvrDatas.bpp = bpp; + pvrDatas.format = format; + pvrDatas.width = width; + pvrDatas.height = height; + pvrDatas.numSurfaces = numFaces; + pvrDatas.numMipmaps = numMipmaps; + pvrDatas.isCubemap = ( numFaces === 6 ); + + return PVRLoader._extract( pvrDatas ); + +}; + +PVRLoader._parseV2 = function ( pvrDatas ) { + + var header = pvrDatas.header; + + var headerLength = header[ 0 ], + height = header[ 1 ], + width = header[ 2 ], + numMipmaps = header[ 3 ], + flags = header[ 4 ], + // dataLength = header[ 5 ], + // bpp = header[ 6 ], + // bitmaskRed = header[ 7 ], + // bitmaskGreen = header[ 8 ], + // bitmaskBlue = header[ 9 ], + bitmaskAlpha = header[ 10 ], + // pvrTag = header[ 11 ], + numSurfs = header[ 12 ]; + + + var TYPE_MASK = 0xff; + var PVRTC_2 = 24, + PVRTC_4 = 25; + + var formatFlags = flags & TYPE_MASK; + + var bpp, format; + var _hasAlpha = bitmaskAlpha > 0; + + if ( formatFlags === PVRTC_4 ) { + + format = _hasAlpha ? RGBA_PVRTC_4BPPV1_Format : RGB_PVRTC_4BPPV1_Format; + bpp = 4; + + } else if ( formatFlags === PVRTC_2 ) { + + format = _hasAlpha ? RGBA_PVRTC_2BPPV1_Format : RGB_PVRTC_2BPPV1_Format; + bpp = 2; + + } else { + + console.error( 'PVRLoader: Unknown PVR format:', formatFlags ); + + } + + pvrDatas.dataPtr = headerLength; + pvrDatas.bpp = bpp; + pvrDatas.format = format; + pvrDatas.width = width; + pvrDatas.height = height; + pvrDatas.numSurfaces = numSurfs; + pvrDatas.numMipmaps = numMipmaps + 1; + + // guess cubemap type seems tricky in v2 + // it juste a pvr containing 6 surface (no explicit cubemap type) + pvrDatas.isCubemap = ( numSurfs === 6 ); + + return PVRLoader._extract( pvrDatas ); + +}; + + +PVRLoader._extract = function ( pvrDatas ) { + + var pvr = { + mipmaps: [], + width: pvrDatas.width, + height: pvrDatas.height, + format: pvrDatas.format, + mipmapCount: pvrDatas.numMipmaps, + isCubemap: pvrDatas.isCubemap + }; + + var buffer = pvrDatas.buffer; + + var dataOffset = pvrDatas.dataPtr, + bpp = pvrDatas.bpp, + numSurfs = pvrDatas.numSurfaces, + dataSize = 0, + blockSize = 0, + blockWidth = 0, + blockHeight = 0, + widthBlocks = 0, + heightBlocks = 0; + + if ( bpp === 2 ) { + + blockWidth = 8; + blockHeight = 4; + + } else { + + blockWidth = 4; + blockHeight = 4; + + } + + blockSize = ( blockWidth * blockHeight ) * bpp / 8; + + pvr.mipmaps.length = pvrDatas.numMipmaps * numSurfs; + + var mipLevel = 0; + + while ( mipLevel < pvrDatas.numMipmaps ) { + + var sWidth = pvrDatas.width >> mipLevel, + sHeight = pvrDatas.height >> mipLevel; + + widthBlocks = sWidth / blockWidth; + heightBlocks = sHeight / blockHeight; + + // Clamp to minimum number of blocks + if ( widthBlocks < 2 ) widthBlocks = 2; + if ( heightBlocks < 2 ) heightBlocks = 2; + + dataSize = widthBlocks * heightBlocks * blockSize; + + for ( var surfIndex = 0; surfIndex < numSurfs; surfIndex ++ ) { + + var byteArray = new Uint8Array( buffer, dataOffset, dataSize ); + + var mipmap = { + data: byteArray, + width: sWidth, + height: sHeight + }; + + pvr.mipmaps[ surfIndex * pvrDatas.numMipmaps + mipLevel ] = mipmap; + + dataOffset += dataSize; + + } + + mipLevel ++; + + } + + return pvr; + +}; + +export { PVRLoader }; diff --git a/examples/jsm/loaders/PlayCanvasLoader.js b/examples/jsm/loaders/PlayCanvasLoader.js new file mode 100644 index 00000000000000..a957cdccfe3899 --- /dev/null +++ b/examples/jsm/loaders/PlayCanvasLoader.js @@ -0,0 +1,218 @@ +/** + * @author mrdoob / http://mrdoob.com/ + * @author Mugen87 / https://github.com/Mugen87 + */ + +import { + BufferGeometry, + DefaultLoadingManager, + Euler, + FileLoader, + Float32BufferAttribute, + Group, + Mesh, + MeshPhongMaterial, + Uint16BufferAttribute, + Uint8BufferAttribute, +} from "../../../build/three.module.js"; + +var PlayCanvasLoader = function ( manager ) { + + this.manager = ( manager !== undefined ) ? manager : DefaultLoadingManager; + +}; + +PlayCanvasLoader.prototype = { + + constructor: PlayCanvasLoader, + + load: function ( url, onLoad, onProgress, onError ) { + + var scope = this; + + var loader = new FileLoader( scope.manager ); + loader.setPath( scope.path ); + loader.load( url, function ( text ) { + + onLoad( scope.parse( JSON.parse( text ) ) ); + + }, onProgress, onError ); + + }, + + setPath: function ( value ) { + + this.path = value; + return this; + + }, + + parse: function ( json ) { + + function parseVertices( data ) { + + var attributes = {}; + + // create a buffer attribute for each array that contains vertex information + + for ( var name in data ) { + + var array = data[ name ]; + + var type = array.type; + var size = array.components; + + var attribute; + + switch ( type ) { + + case 'float32': + attribute = new Float32BufferAttribute( array.data, size ); + break; + + case 'uint8': + attribute = new Uint8BufferAttribute( array.data, size ); + break; + + case 'uint16': + attribute = new Uint16BufferAttribute( array.data, size ); + break; + + default: + console.log( 'THREE.PlayCanvasLoader: Array type "%s" not yet supported.', type ); + + } + + attributes[ name ] = attribute; + + } + + data._attributes = attributes; + + } + + function parseMeshes( data ) { + + // create buffer geometry + + var geometry = new BufferGeometry(); + + geometry.setIndex( data.indices ); + + var attributes = model.vertices[ data.vertices ]._attributes; + + for ( var name in attributes ) { + + var attribute = attributes[ name ]; + + if ( name === 'texCoord0' ) name = 'uv'; + + geometry.addAttribute( name, attribute ); + + } + + data._geometry = geometry; + + } + + function parseMeshInstances( data ) { + + var node = model.nodes[ data.node ]; + var mesh = model.meshes[ data.mesh ]; + + if ( node._geometries === undefined ) { + + node._geometries = []; + + } + + node._geometries.push( mesh._geometry ); + + } + + function parseNodes( data ) { + + var object = new Group(); + + var geometries = data._geometries; + + if ( geometries !== undefined ) { + + var material = new MeshPhongMaterial(); + + for ( var i = 0, l = geometries.length; i < l; i ++ ) { + + var geometry = geometries[ i ]; + + object.add( new Mesh( geometry, material ) ); + + } + + } + + for ( var i = 0, l = data.rotation.length; i < l; i ++ ) { + + data.rotation[ i ] *= Math.PI / 180; + + } + + // + + object.name = data.name; + + object.position.fromArray( data.position ); + object.quaternion.setFromEuler( new Euler().fromArray( data.rotation ) ); + object.scale.fromArray( data.scale ); + + data._object = object; + + } + + // + + var model = json.model; + + for ( var i = 0, l = model.vertices.length; i < l; i ++ ) { + + parseVertices( model.vertices[ i ] ); + + } + + for ( var i = 0, l = model.meshes.length; i < l; i ++ ) { + + parseMeshes( model.meshes[ i ] ); + + } + + for ( var i = 0, l = model.meshInstances.length; i < l; i ++ ) { + + parseMeshInstances( model.meshInstances[ i ] ); + + } + + for ( var i = 0, l = model.nodes.length; i < l; i ++ ) { + + parseNodes( model.nodes[ i ] ); + + } + + // setup scene hierarchy + + for ( var i = 0, l = model.parents.length; i < l; i ++ ) { + + var parent = model.parents[ i ]; + + if ( parent === - 1 ) continue; + + model.nodes[ parent ]._object.add( model.nodes[ i ]._object ); + + + } + + return model.nodes[ 0 ]._object; + + } + +}; + +export { PlayCanvasLoader }; diff --git a/examples/jsm/loaders/RGBELoader.js b/examples/jsm/loaders/RGBELoader.js new file mode 100644 index 00000000000000..8fc6fe990c18e2 --- /dev/null +++ b/examples/jsm/loaders/RGBELoader.js @@ -0,0 +1,405 @@ +/** + * @author Nikos M. / https://github.com/foo123/ + */ + +import { + DataTextureLoader, + DefaultLoadingManager, + FloatType, + RGBEFormat, + RGBFormat, + UnsignedByteType, +} from "../../../build/three.module.js"; + +// https://github.com/mrdoob/three.js/issues/5552 +// http://en.wikipedia.org/wiki/RGBE_image_format + +var RGBELoader = function ( manager ) { + + this.manager = ( manager !== undefined ) ? manager : DefaultLoadingManager; + this.type = UnsignedByteType; + +}; + +// extend THREE.DataTextureLoader +RGBELoader.prototype = Object.create( DataTextureLoader.prototype ); + +// adapted from http://www.graphics.cornell.edu/~bjw/rgbe.html +RGBELoader.prototype._parser = function ( buffer ) { + + var + /* return codes for rgbe routines */ + RGBE_RETURN_SUCCESS = 0, + RGBE_RETURN_FAILURE = - 1, + + /* default error routine. change this to change error handling */ + rgbe_read_error = 1, + rgbe_write_error = 2, + rgbe_format_error = 3, + rgbe_memory_error = 4, + rgbe_error = function ( rgbe_error_code, msg ) { + + switch ( rgbe_error_code ) { + + case rgbe_read_error: console.error( "THREE.RGBELoader Read Error: " + ( msg || '' ) ); + break; + case rgbe_write_error: console.error( "THREE.RGBELoader Write Error: " + ( msg || '' ) ); + break; + case rgbe_format_error: console.error( "THREE.RGBELoader Bad File Format: " + ( msg || '' ) ); + break; + default: + case rgbe_memory_error: console.error( "THREE.RGBELoader: Error: " + ( msg || '' ) ); + + } + return RGBE_RETURN_FAILURE; + + }, + + /* offsets to red, green, and blue components in a data (float) pixel */ + RGBE_DATA_RED = 0, + RGBE_DATA_GREEN = 1, + RGBE_DATA_BLUE = 2, + + /* number of floats per pixel, use 4 since stored in rgba image format */ + RGBE_DATA_SIZE = 4, + + /* flags indicating which fields in an rgbe_header_info are valid */ + RGBE_VALID_PROGRAMTYPE = 1, + RGBE_VALID_FORMAT = 2, + RGBE_VALID_DIMENSIONS = 4, + + NEWLINE = "\n", + + fgets = function ( buffer, lineLimit, consume ) { + + lineLimit = ! lineLimit ? 1024 : lineLimit; + var p = buffer.pos, + i = - 1, len = 0, s = '', chunkSize = 128, + chunk = String.fromCharCode.apply( null, new Uint16Array( buffer.subarray( p, p + chunkSize ) ) ) + ; + while ( ( 0 > ( i = chunk.indexOf( NEWLINE ) ) ) && ( len < lineLimit ) && ( p < buffer.byteLength ) ) { + + s += chunk; len += chunk.length; + p += chunkSize; + chunk += String.fromCharCode.apply( null, new Uint16Array( buffer.subarray( p, p + chunkSize ) ) ); + + } + + if ( - 1 < i ) { + + /*for (i=l-1; i>=0; i--) { + byteCode = m.charCodeAt(i); + if (byteCode > 0x7f && byteCode <= 0x7ff) byteLen++; + else if (byteCode > 0x7ff && byteCode <= 0xffff) byteLen += 2; + if (byteCode >= 0xDC00 && byteCode <= 0xDFFF) i--; //trail surrogate + }*/ + if ( false !== consume ) buffer.pos += len + i + 1; + return s + chunk.slice( 0, i ); + + } + return false; + + }, + + /* minimal header reading. modify if you want to parse more information */ + RGBE_ReadHeader = function ( buffer ) { + + var line, match, + + // regexes to parse header info fields + magic_token_re = /^#\?(\S+)$/, + gamma_re = /^\s*GAMMA\s*=\s*(\d+(\.\d+)?)\s*$/, + exposure_re = /^\s*EXPOSURE\s*=\s*(\d+(\.\d+)?)\s*$/, + format_re = /^\s*FORMAT=(\S+)\s*$/, + dimensions_re = /^\s*\-Y\s+(\d+)\s+\+X\s+(\d+)\s*$/, + + // RGBE format header struct + header = { + + valid: 0, /* indicate which fields are valid */ + + string: '', /* the actual header string */ + + comments: '', /* comments found in header */ + + programtype: 'RGBE', /* listed at beginning of file to identify it after "#?". defaults to "RGBE" */ + + format: '', /* RGBE format, default 32-bit_rle_rgbe */ + + gamma: 1.0, /* image has already been gamma corrected with given gamma. defaults to 1.0 (no correction) */ + + exposure: 1.0, /* a value of 1.0 in an image corresponds to watts/steradian/m^2. defaults to 1.0 */ + + width: 0, height: 0 /* image dimensions, width/height */ + + }; + + if ( buffer.pos >= buffer.byteLength || ! ( line = fgets( buffer ) ) ) { + + return rgbe_error( rgbe_read_error, "no header found" ); + + } + /* if you want to require the magic token then uncomment the next line */ + if ( ! ( match = line.match( magic_token_re ) ) ) { + + return rgbe_error( rgbe_format_error, "bad initial token" ); + + } + header.valid |= RGBE_VALID_PROGRAMTYPE; + header.programtype = match[ 1 ]; + header.string += line + "\n"; + + while ( true ) { + + line = fgets( buffer ); + if ( false === line ) break; + header.string += line + "\n"; + + if ( '#' === line.charAt( 0 ) ) { + + header.comments += line + "\n"; + continue; // comment line + + } + + if ( match = line.match( gamma_re ) ) { + + header.gamma = parseFloat( match[ 1 ], 10 ); + + } + if ( match = line.match( exposure_re ) ) { + + header.exposure = parseFloat( match[ 1 ], 10 ); + + } + if ( match = line.match( format_re ) ) { + + header.valid |= RGBE_VALID_FORMAT; + header.format = match[ 1 ];//'32-bit_rle_rgbe'; + + } + if ( match = line.match( dimensions_re ) ) { + + header.valid |= RGBE_VALID_DIMENSIONS; + header.height = parseInt( match[ 1 ], 10 ); + header.width = parseInt( match[ 2 ], 10 ); + + } + + if ( ( header.valid & RGBE_VALID_FORMAT ) && ( header.valid & RGBE_VALID_DIMENSIONS ) ) break; + + } + + if ( ! ( header.valid & RGBE_VALID_FORMAT ) ) { + + return rgbe_error( rgbe_format_error, "missing format specifier" ); + + } + if ( ! ( header.valid & RGBE_VALID_DIMENSIONS ) ) { + + return rgbe_error( rgbe_format_error, "missing image size specifier" ); + + } + + return header; + + }, + + RGBE_ReadPixels_RLE = function ( buffer, w, h ) { + + var data_rgba, offset, pos, count, byteValue, + scanline_buffer, ptr, ptr_end, i, l, off, isEncodedRun, + scanline_width = w, num_scanlines = h, rgbeStart + ; + + if ( + // run length encoding is not allowed so read flat + ( ( scanline_width < 8 ) || ( scanline_width > 0x7fff ) ) || + // this file is not run length encoded + ( ( 2 !== buffer[ 0 ] ) || ( 2 !== buffer[ 1 ] ) || ( buffer[ 2 ] & 0x80 ) ) + ) { + + // return the flat buffer + return new Uint8Array( buffer ); + + } + + if ( scanline_width !== ( ( buffer[ 2 ] << 8 ) | buffer[ 3 ] ) ) { + + return rgbe_error( rgbe_format_error, "wrong scanline width" ); + + } + + data_rgba = new Uint8Array( 4 * w * h ); + + if ( ! data_rgba || ! data_rgba.length ) { + + return rgbe_error( rgbe_memory_error, "unable to allocate buffer space" ); + + } + + offset = 0; pos = 0; ptr_end = 4 * scanline_width; + rgbeStart = new Uint8Array( 4 ); + scanline_buffer = new Uint8Array( ptr_end ); + + // read in each successive scanline + while ( ( num_scanlines > 0 ) && ( pos < buffer.byteLength ) ) { + + if ( pos + 4 > buffer.byteLength ) { + + return rgbe_error( rgbe_read_error ); + + } + + rgbeStart[ 0 ] = buffer[ pos ++ ]; + rgbeStart[ 1 ] = buffer[ pos ++ ]; + rgbeStart[ 2 ] = buffer[ pos ++ ]; + rgbeStart[ 3 ] = buffer[ pos ++ ]; + + if ( ( 2 != rgbeStart[ 0 ] ) || ( 2 != rgbeStart[ 1 ] ) || ( ( ( rgbeStart[ 2 ] << 8 ) | rgbeStart[ 3 ] ) != scanline_width ) ) { + + return rgbe_error( rgbe_format_error, "bad rgbe scanline format" ); + + } + + // read each of the four channels for the scanline into the buffer + // first red, then green, then blue, then exponent + ptr = 0; + while ( ( ptr < ptr_end ) && ( pos < buffer.byteLength ) ) { + + count = buffer[ pos ++ ]; + isEncodedRun = count > 128; + if ( isEncodedRun ) count -= 128; + + if ( ( 0 === count ) || ( ptr + count > ptr_end ) ) { + + return rgbe_error( rgbe_format_error, "bad scanline data" ); + + } + + if ( isEncodedRun ) { + + // a (encoded) run of the same value + byteValue = buffer[ pos ++ ]; + for ( i = 0; i < count; i ++ ) { + + scanline_buffer[ ptr ++ ] = byteValue; + + } + //ptr += count; + + } else { + + // a literal-run + scanline_buffer.set( buffer.subarray( pos, pos + count ), ptr ); + ptr += count; pos += count; + + } + + } + + + // now convert data from buffer into rgba + // first red, then green, then blue, then exponent (alpha) + l = scanline_width; //scanline_buffer.byteLength; + for ( i = 0; i < l; i ++ ) { + + off = 0; + data_rgba[ offset ] = scanline_buffer[ i + off ]; + off += scanline_width; //1; + data_rgba[ offset + 1 ] = scanline_buffer[ i + off ]; + off += scanline_width; //1; + data_rgba[ offset + 2 ] = scanline_buffer[ i + off ]; + off += scanline_width; //1; + data_rgba[ offset + 3 ] = scanline_buffer[ i + off ]; + offset += 4; + + } + + num_scanlines --; + + } + + return data_rgba; + + } + ; + + var byteArray = new Uint8Array( buffer ), + byteLength = byteArray.byteLength; + byteArray.pos = 0; + var rgbe_header_info = RGBE_ReadHeader( byteArray ); + + if ( RGBE_RETURN_FAILURE !== rgbe_header_info ) { + + var w = rgbe_header_info.width, + h = rgbe_header_info.height, + image_rgba_data = RGBE_ReadPixels_RLE( byteArray.subarray( byteArray.pos ), w, h ) + ; + if ( RGBE_RETURN_FAILURE !== image_rgba_data ) { + + if ( this.type === UnsignedByteType ) { + + var data = image_rgba_data; + var format = RGBEFormat; // handled as THREE.RGBAFormat in shaders + var type = UnsignedByteType; + + } else if ( this.type === FloatType ) { + + var RGBEByteToRGBFloat = function ( sourceArray, sourceOffset, destArray, destOffset ) { + + var e = sourceArray[ sourceOffset + 3 ]; + var scale = Math.pow( 2.0, e - 128.0 ) / 255.0; + + destArray[ destOffset + 0 ] = sourceArray[ sourceOffset + 0 ] * scale; + destArray[ destOffset + 1 ] = sourceArray[ sourceOffset + 1 ] * scale; + destArray[ destOffset + 2 ] = sourceArray[ sourceOffset + 2 ] * scale; + + }; + + var numElements = ( image_rgba_data.length / 4 ) * 3; + var floatArray = new Float32Array( numElements ); + + for ( var j = 0; j < numElements; j ++ ) { + + RGBEByteToRGBFloat( image_rgba_data, j * 4, floatArray, j * 3 ); + + } + + var data = floatArray; + var format = RGBFormat; + var type = FloatType; + + + } else { + + console.error( 'THREE.RGBELoader: unsupported type: ', this.type ); + + } + + return { + width: w, height: h, + data: data, + header: rgbe_header_info.string, + gamma: rgbe_header_info.gamma, + exposure: rgbe_header_info.exposure, + format: format, + type: type + }; + + } + + } + + return null; + +}; + +RGBELoader.prototype.setType = function ( value ) { + + this.type = value; + return this; + +}; + +export { RGBELoader }; diff --git a/examples/jsm/loaders/STLLoader.js b/examples/jsm/loaders/STLLoader.js new file mode 100644 index 00000000000000..a67ef06f32936a --- /dev/null +++ b/examples/jsm/loaders/STLLoader.js @@ -0,0 +1,353 @@ +/** + * @author aleeper / http://adamleeper.com/ + * @author mrdoob / http://mrdoob.com/ + * @author gero3 / https://github.com/gero3 + * @author Mugen87 / https://github.com/Mugen87 + * + * Description: A THREE loader for STL ASCII files, as created by Solidworks and other CAD programs. + * + * Supports both binary and ASCII encoded files, with automatic detection of type. + * + * The loader returns a non-indexed buffer geometry. + * + * Limitations: + * Binary decoding supports "Magics" color format (http://en.wikipedia.org/wiki/STL_(file_format)#Color_in_binary_STL). + * There is perhaps some question as to how valid it is to always assume little-endian-ness. + * ASCII decoding assumes file is UTF-8. + * + * Usage: + * var loader = new THREE.STLLoader(); + * loader.load( './models/stl/slotted_disk.stl', function ( geometry ) { + * scene.add( new THREE.Mesh( geometry ) ); + * }); + * + * For binary STLs geometry might contain colors for vertices. To use it: + * // use the same code to load STL as above + * if (geometry.hasColors) { + * material = new THREE.MeshPhongMaterial({ opacity: geometry.alpha, vertexColors: THREE.VertexColors }); + * } else { .... } + * var mesh = new THREE.Mesh( geometry, material ); + */ + +import { + BufferAttribute, + BufferGeometry, + DefaultLoadingManager, + FileLoader, + Float32BufferAttribute, + LoaderUtils, + Vector3, +} from "../../../build/three.module.js"; + +var STLLoader = function ( manager ) { + + this.manager = ( manager !== undefined ) ? manager : DefaultLoadingManager; + +}; + +STLLoader.prototype = { + + constructor: STLLoader, + + load: function ( url, onLoad, onProgress, onError ) { + + var scope = this; + + var loader = new FileLoader( scope.manager ); + loader.setPath( scope.path ); + loader.setResponseType( 'arraybuffer' ); + loader.load( url, function ( text ) { + + try { + + onLoad( scope.parse( text ) ); + + } catch ( exception ) { + + if ( onError ) { + + onError( exception ); + + } + + } + + }, onProgress, onError ); + + }, + + setPath: function ( value ) { + + this.path = value; + return this; + + }, + + parse: function ( data ) { + + function isBinary( data ) { + + var expect, face_size, n_faces, reader; + reader = new DataView( data ); + face_size = ( 32 / 8 * 3 ) + ( ( 32 / 8 * 3 ) * 3 ) + ( 16 / 8 ); + n_faces = reader.getUint32( 80, true ); + expect = 80 + ( 32 / 8 ) + ( n_faces * face_size ); + + if ( expect === reader.byteLength ) { + + return true; + + } + + // An ASCII STL data must begin with 'solid ' as the first six bytes. + // However, ASCII STLs lacking the SPACE after the 'd' are known to be + // plentiful. So, check the first 5 bytes for 'solid'. + + // Several encodings, such as UTF-8, precede the text with up to 5 bytes: + // https://en.wikipedia.org/wiki/Byte_order_mark#Byte_order_marks_by_encoding + // Search for "solid" to start anywhere after those prefixes. + + // US-ASCII ordinal values for 's', 'o', 'l', 'i', 'd' + + var solid = [ 115, 111, 108, 105, 100 ]; + + for ( var off = 0; off < 5; off ++ ) { + + // If "solid" text is matched to the current offset, declare it to be an ASCII STL. + + if ( matchDataViewAt( solid, reader, off ) ) return false; + + } + + // Couldn't find "solid" text at the beginning; it is binary STL. + + return true; + + } + + function matchDataViewAt( query, reader, offset ) { + + // Check if each byte in query matches the corresponding byte from the current offset + + for ( var i = 0, il = query.length; i < il; i ++ ) { + + if ( query[ i ] !== reader.getUint8( offset + i, false ) ) return false; + + } + + return true; + + } + + function parseBinary( data ) { + + var reader = new DataView( data ); + var faces = reader.getUint32( 80, true ); + + var r, g, b, hasColors = false, colors; + var defaultR, defaultG, defaultB, alpha; + + // process STL header + // check for default color in header ("COLOR=rgba" sequence). + + for ( var index = 0; index < 80 - 10; index ++ ) { + + if ( ( reader.getUint32( index, false ) == 0x434F4C4F /*COLO*/ ) && + ( reader.getUint8( index + 4 ) == 0x52 /*'R'*/ ) && + ( reader.getUint8( index + 5 ) == 0x3D /*'='*/ ) ) { + + hasColors = true; + colors = []; + + defaultR = reader.getUint8( index + 6 ) / 255; + defaultG = reader.getUint8( index + 7 ) / 255; + defaultB = reader.getUint8( index + 8 ) / 255; + alpha = reader.getUint8( index + 9 ) / 255; + + } + + } + + var dataOffset = 84; + var faceLength = 12 * 4 + 2; + + var geometry = new BufferGeometry(); + + var vertices = []; + var normals = []; + + for ( var face = 0; face < faces; face ++ ) { + + var start = dataOffset + face * faceLength; + var normalX = reader.getFloat32( start, true ); + var normalY = reader.getFloat32( start + 4, true ); + var normalZ = reader.getFloat32( start + 8, true ); + + if ( hasColors ) { + + var packedColor = reader.getUint16( start + 48, true ); + + if ( ( packedColor & 0x8000 ) === 0 ) { + + // facet has its own unique color + + r = ( packedColor & 0x1F ) / 31; + g = ( ( packedColor >> 5 ) & 0x1F ) / 31; + b = ( ( packedColor >> 10 ) & 0x1F ) / 31; + + } else { + + r = defaultR; + g = defaultG; + b = defaultB; + + } + + } + + for ( var i = 1; i <= 3; i ++ ) { + + var vertexstart = start + i * 12; + + vertices.push( reader.getFloat32( vertexstart, true ) ); + vertices.push( reader.getFloat32( vertexstart + 4, true ) ); + vertices.push( reader.getFloat32( vertexstart + 8, true ) ); + + normals.push( normalX, normalY, normalZ ); + + if ( hasColors ) { + + colors.push( r, g, b ); + + } + + } + + } + + geometry.addAttribute( 'position', new BufferAttribute( new Float32Array( vertices ), 3 ) ); + geometry.addAttribute( 'normal', new BufferAttribute( new Float32Array( normals ), 3 ) ); + + if ( hasColors ) { + + geometry.addAttribute( 'color', new BufferAttribute( new Float32Array( colors ), 3 ) ); + geometry.hasColors = true; + geometry.alpha = alpha; + + } + + return geometry; + + } + + function parseASCII( data ) { + + var geometry = new BufferGeometry(); + var patternFace = /facet([\s\S]*?)endfacet/g; + var faceCounter = 0; + + var patternFloat = /[\s]+([+-]?(?:\d*)(?:\.\d*)?(?:[eE][+-]?\d+)?)/.source; + var patternVertex = new RegExp( 'vertex' + patternFloat + patternFloat + patternFloat, 'g' ); + var patternNormal = new RegExp( 'normal' + patternFloat + patternFloat + patternFloat, 'g' ); + + var vertices = []; + var normals = []; + + var normal = new Vector3(); + + var result; + + while ( ( result = patternFace.exec( data ) ) !== null ) { + + var vertexCountPerFace = 0; + var normalCountPerFace = 0; + + var text = result[ 0 ]; + + while ( ( result = patternNormal.exec( text ) ) !== null ) { + + normal.x = parseFloat( result[ 1 ] ); + normal.y = parseFloat( result[ 2 ] ); + normal.z = parseFloat( result[ 3 ] ); + normalCountPerFace ++; + + } + + while ( ( result = patternVertex.exec( text ) ) !== null ) { + + vertices.push( parseFloat( result[ 1 ] ), parseFloat( result[ 2 ] ), parseFloat( result[ 3 ] ) ); + normals.push( normal.x, normal.y, normal.z ); + vertexCountPerFace ++; + + } + + // every face have to own ONE valid normal + + if ( normalCountPerFace !== 1 ) { + + console.error( 'THREE.STLLoader: Something isn\'t right with the normal of face number ' + faceCounter ); + + } + + // each face have to own THREE valid vertices + + if ( vertexCountPerFace !== 3 ) { + + console.error( 'THREE.STLLoader: Something isn\'t right with the vertices of face number ' + faceCounter ); + + } + + faceCounter ++; + + } + + geometry.addAttribute( 'position', new Float32BufferAttribute( vertices, 3 ) ); + geometry.addAttribute( 'normal', new Float32BufferAttribute( normals, 3 ) ); + + return geometry; + + } + + function ensureString( buffer ) { + + if ( typeof buffer !== 'string' ) { + + return LoaderUtils.decodeText( new Uint8Array( buffer ) ); + + } + + return buffer; + + } + + function ensureBinary( buffer ) { + + if ( typeof buffer === 'string' ) { + + var array_buffer = new Uint8Array( buffer.length ); + for ( var i = 0; i < buffer.length; i ++ ) { + + array_buffer[ i ] = buffer.charCodeAt( i ) & 0xff; // implicitly assumes little-endian + + } + return array_buffer.buffer || array_buffer; + + } else { + + return buffer; + + } + + } + + // start + + var binData = ensureBinary( data ); + + return isBinary( binData ) ? parseBinary( binData ) : parseASCII( ensureString( data ) ); + + } + +}; + +export { STLLoader }; diff --git a/examples/jsm/loaders/SVGLoader.js b/examples/jsm/loaders/SVGLoader.js new file mode 100644 index 00000000000000..7cb5513b8a322f --- /dev/null +++ b/examples/jsm/loaders/SVGLoader.js @@ -0,0 +1,1095 @@ +/** + * @author mrdoob / http://mrdoob.com/ + * @author zz85 / http://joshuakoo.com/ + * @author yomboprime / https://yombo.org + */ + +import { + DefaultLoadingManager, + FileLoader, + Matrix3, + Path, + ShapePath, + Vector2, + Vector3 +} from "../../../build/three.module.js"; + +var SVGLoader = function ( manager ) { + + this.manager = ( manager !== undefined ) ? manager : DefaultLoadingManager; + +}; + +SVGLoader.prototype = { + + constructor: SVGLoader, + + load: function ( url, onLoad, onProgress, onError ) { + + var scope = this; + + var loader = new FileLoader( scope.manager ); + loader.setPath( scope.path ); + loader.load( url, function ( text ) { + + onLoad( scope.parse( text ) ); + + }, onProgress, onError ); + + }, + + setPath: function ( value ) { + + this.path = value; + return this; + + }, + + parse: function ( text ) { + + function parseNode( node, style ) { + + if ( node.nodeType !== 1 ) return; + + var transform = getNodeTransform( node ); + + var path = null; + + switch ( node.nodeName ) { + + case 'svg': + break; + + case 'g': + style = parseStyle( node, style ); + break; + + case 'path': + style = parseStyle( node, style ); + if ( node.hasAttribute( 'd' ) && isVisible( style ) ) path = parsePathNode( node, style ); + break; + + case 'rect': + style = parseStyle( node, style ); + if ( isVisible( style ) ) path = parseRectNode( node, style ); + break; + + case 'polygon': + style = parseStyle( node, style ); + if ( isVisible( style ) ) path = parsePolygonNode( node, style ); + break; + + case 'polyline': + style = parseStyle( node, style ); + if ( isVisible( style ) ) path = parsePolylineNode( node, style ); + break; + + case 'circle': + style = parseStyle( node, style ); + if ( isVisible( style ) ) path = parseCircleNode( node, style ); + break; + + case 'ellipse': + style = parseStyle( node, style ); + if ( isVisible( style ) ) path = parseEllipseNode( node, style ); + break; + + case 'line': + style = parseStyle( node, style ); + if ( isVisible( style ) ) path = parseLineNode( node, style ); + break; + + default: + console.log( node ); + + } + + if ( path ) { + + transformPath( path, currentTransform ); + + paths.push( path ); + + } + + var nodes = node.childNodes; + + for ( var i = 0; i < nodes.length; i ++ ) { + + parseNode( nodes[ i ], style ); + + } + + if ( transform ) { + + currentTransform.copy( transformStack.pop() ); + + } + + } + + function parsePathNode( node, style ) { + + var path = new ShapePath(); + path.color.setStyle( style.fill ); + + var point = new Vector2(); + var control = new Vector2(); + + var firstPoint = new Vector2(); + var isFirstPoint = true; + var doSetFirstPoint = false; + + var d = node.getAttribute( 'd' ); + + // console.log( d ); + + var commands = d.match( /[a-df-z][^a-df-z]*/ig ); + + for ( var i = 0, l = commands.length; i < l; i ++ ) { + + var command = commands[ i ]; + + var type = command.charAt( 0 ); + var data = command.substr( 1 ).trim(); + + if ( isFirstPoint === true ) { + + doSetFirstPoint = true; + isFirstPoint = false; + + } + + switch ( type ) { + + case 'M': + var numbers = parseFloats( data ); + for ( var j = 0, jl = numbers.length; j < jl; j += 2 ) { + + point.x = numbers[ j + 0 ]; + point.y = numbers[ j + 1 ]; + control.x = point.x; + control.y = point.y; + if ( j === 0 ) { + + path.moveTo( point.x, point.y ); + + } else { + + path.lineTo( point.x, point.y ); + + } + + } + break; + + case 'H': + var numbers = parseFloats( data ); + for ( var j = 0, jl = numbers.length; j < jl; j ++ ) { + + point.x = numbers[ j ]; + control.x = point.x; + control.y = point.y; + path.lineTo( point.x, point.y ); + + } + break; + + case 'V': + var numbers = parseFloats( data ); + for ( var j = 0, jl = numbers.length; j < jl; j ++ ) { + + point.y = numbers[ j ]; + control.x = point.x; + control.y = point.y; + path.lineTo( point.x, point.y ); + + } + break; + + case 'L': + var numbers = parseFloats( data ); + for ( var j = 0, jl = numbers.length; j < jl; j += 2 ) { + + point.x = numbers[ j + 0 ]; + point.y = numbers[ j + 1 ]; + control.x = point.x; + control.y = point.y; + path.lineTo( point.x, point.y ); + + } + break; + + case 'C': + var numbers = parseFloats( data ); + for ( var j = 0, jl = numbers.length; j < jl; j += 6 ) { + + path.bezierCurveTo( + numbers[ j + 0 ], + numbers[ j + 1 ], + numbers[ j + 2 ], + numbers[ j + 3 ], + numbers[ j + 4 ], + numbers[ j + 5 ] + ); + control.x = numbers[ j + 2 ]; + control.y = numbers[ j + 3 ]; + point.x = numbers[ j + 4 ]; + point.y = numbers[ j + 5 ]; + + } + break; + + case 'S': + var numbers = parseFloats( data ); + for ( var j = 0, jl = numbers.length; j < jl; j += 4 ) { + + path.bezierCurveTo( + getReflection( point.x, control.x ), + getReflection( point.y, control.y ), + numbers[ j + 0 ], + numbers[ j + 1 ], + numbers[ j + 2 ], + numbers[ j + 3 ] + ); + control.x = numbers[ j + 0 ]; + control.y = numbers[ j + 1 ]; + point.x = numbers[ j + 2 ]; + point.y = numbers[ j + 3 ]; + + } + break; + + case 'Q': + var numbers = parseFloats( data ); + for ( var j = 0, jl = numbers.length; j < jl; j += 4 ) { + + path.quadraticCurveTo( + numbers[ j + 0 ], + numbers[ j + 1 ], + numbers[ j + 2 ], + numbers[ j + 3 ] + ); + control.x = numbers[ j + 0 ]; + control.y = numbers[ j + 1 ]; + point.x = numbers[ j + 2 ]; + point.y = numbers[ j + 3 ]; + + } + break; + + case 'T': + var numbers = parseFloats( data ); + for ( var j = 0, jl = numbers.length; j < jl; j += 2 ) { + + var rx = getReflection( point.x, control.x ); + var ry = getReflection( point.y, control.y ); + path.quadraticCurveTo( + rx, + ry, + numbers[ j + 0 ], + numbers[ j + 1 ] + ); + control.x = rx; + control.y = ry; + point.x = numbers[ j + 0 ]; + point.y = numbers[ j + 1 ]; + + } + break; + + case 'A': + var numbers = parseFloats( data ); + for ( var j = 0, jl = numbers.length; j < jl; j += 7 ) { + + var start = point.clone(); + point.x = numbers[ j + 5 ]; + point.y = numbers[ j + 6 ]; + control.x = point.x; + control.y = point.y; + parseArcCommand( + path, numbers[ j ], numbers[ j + 1 ], numbers[ j + 2 ], numbers[ j + 3 ], numbers[ j + 4 ], start, point + ); + + } + break; + + // + + case 'm': + var numbers = parseFloats( data ); + for ( var j = 0, jl = numbers.length; j < jl; j += 2 ) { + + point.x += numbers[ j + 0 ]; + point.y += numbers[ j + 1 ]; + control.x = point.x; + control.y = point.y; + if ( j === 0 ) { + + path.moveTo( point.x, point.y ); + + } else { + + path.lineTo( point.x, point.y ); + + } + + } + break; + + case 'h': + var numbers = parseFloats( data ); + for ( var j = 0, jl = numbers.length; j < jl; j ++ ) { + + point.x += numbers[ j ]; + control.x = point.x; + control.y = point.y; + path.lineTo( point.x, point.y ); + + } + break; + + case 'v': + var numbers = parseFloats( data ); + for ( var j = 0, jl = numbers.length; j < jl; j ++ ) { + + point.y += numbers[ j ]; + control.x = point.x; + control.y = point.y; + path.lineTo( point.x, point.y ); + + } + break; + + case 'l': + var numbers = parseFloats( data ); + for ( var j = 0, jl = numbers.length; j < jl; j += 2 ) { + + point.x += numbers[ j + 0 ]; + point.y += numbers[ j + 1 ]; + control.x = point.x; + control.y = point.y; + path.lineTo( point.x, point.y ); + + } + break; + + case 'c': + var numbers = parseFloats( data ); + for ( var j = 0, jl = numbers.length; j < jl; j += 6 ) { + + path.bezierCurveTo( + point.x + numbers[ j + 0 ], + point.y + numbers[ j + 1 ], + point.x + numbers[ j + 2 ], + point.y + numbers[ j + 3 ], + point.x + numbers[ j + 4 ], + point.y + numbers[ j + 5 ] + ); + control.x = point.x + numbers[ j + 2 ]; + control.y = point.y + numbers[ j + 3 ]; + point.x += numbers[ j + 4 ]; + point.y += numbers[ j + 5 ]; + + } + break; + + case 's': + var numbers = parseFloats( data ); + for ( var j = 0, jl = numbers.length; j < jl; j += 4 ) { + + path.bezierCurveTo( + getReflection( point.x, control.x ), + getReflection( point.y, control.y ), + point.x + numbers[ j + 0 ], + point.y + numbers[ j + 1 ], + point.x + numbers[ j + 2 ], + point.y + numbers[ j + 3 ] + ); + control.x = point.x + numbers[ j + 0 ]; + control.y = point.y + numbers[ j + 1 ]; + point.x += numbers[ j + 2 ]; + point.y += numbers[ j + 3 ]; + + } + break; + + case 'q': + var numbers = parseFloats( data ); + for ( var j = 0, jl = numbers.length; j < jl; j += 4 ) { + + path.quadraticCurveTo( + point.x + numbers[ j + 0 ], + point.y + numbers[ j + 1 ], + point.x + numbers[ j + 2 ], + point.y + numbers[ j + 3 ] + ); + control.x = point.x + numbers[ j + 0 ]; + control.y = point.y + numbers[ j + 1 ]; + point.x += numbers[ j + 2 ]; + point.y += numbers[ j + 3 ]; + + } + break; + + case 't': + var numbers = parseFloats( data ); + for ( var j = 0, jl = numbers.length; j < jl; j += 2 ) { + + var rx = getReflection( point.x, control.x ); + var ry = getReflection( point.y, control.y ); + path.quadraticCurveTo( + rx, + ry, + point.x + numbers[ j + 0 ], + point.y + numbers[ j + 1 ] + ); + control.x = rx; + control.y = ry; + point.x = point.x + numbers[ j + 0 ]; + point.y = point.y + numbers[ j + 1 ]; + + } + break; + + case 'a': + var numbers = parseFloats( data ); + for ( var j = 0, jl = numbers.length; j < jl; j += 7 ) { + + var start = point.clone(); + point.x += numbers[ j + 5 ]; + point.y += numbers[ j + 6 ]; + control.x = point.x; + control.y = point.y; + parseArcCommand( + path, numbers[ j ], numbers[ j + 1 ], numbers[ j + 2 ], numbers[ j + 3 ], numbers[ j + 4 ], start, point + ); + + } + break; + + // + + case 'Z': + case 'z': + path.currentPath.autoClose = true; + if ( path.currentPath.curves.length > 0 ) { + + // Reset point to beginning of Path + point.copy( firstPoint ); + path.currentPath.currentPoint.copy( point ); + isFirstPoint = true; + + } + break; + + default: + console.warn( command ); + + } + + // console.log( type, parseFloats( data ), parseFloats( data ).length ) + + if ( doSetFirstPoint === true ) { + + firstPoint.copy( point ); + doSetFirstPoint = false; + + } + + } + + return path; + + } + + /** + * https://www.w3.org/TR/SVG/implnote.html#ArcImplementationNotes + * https://mortoray.com/2017/02/16/rendering-an-svg-elliptical-arc-as-bezier-curves/ Appendix: Endpoint to center arc conversion + * From + * rx ry x-axis-rotation large-arc-flag sweep-flag x y + * To + * aX, aY, xRadius, yRadius, aStartAngle, aEndAngle, aClockwise, aRotation + */ + + function parseArcCommand( path, rx, ry, x_axis_rotation, large_arc_flag, sweep_flag, start, end ) { + + x_axis_rotation = x_axis_rotation * Math.PI / 180; + + // Ensure radii are positive + rx = Math.abs( rx ); + ry = Math.abs( ry ); + + // Compute (x1′, y1′) + var dx2 = ( start.x - end.x ) / 2.0; + var dy2 = ( start.y - end.y ) / 2.0; + var x1p = Math.cos( x_axis_rotation ) * dx2 + Math.sin( x_axis_rotation ) * dy2; + var y1p = - Math.sin( x_axis_rotation ) * dx2 + Math.cos( x_axis_rotation ) * dy2; + + // Compute (cx′, cy′) + var rxs = rx * rx; + var rys = ry * ry; + var x1ps = x1p * x1p; + var y1ps = y1p * y1p; + + // Ensure radii are large enough + var cr = x1ps / rxs + y1ps / rys; + + if ( cr > 1 ) { + + // scale up rx,ry equally so cr == 1 + var s = Math.sqrt( cr ); + rx = s * rx; + ry = s * ry; + rxs = rx * rx; + rys = ry * ry; + + } + + var dq = ( rxs * y1ps + rys * x1ps ); + var pq = ( rxs * rys - dq ) / dq; + var q = Math.sqrt( Math.max( 0, pq ) ); + if ( large_arc_flag === sweep_flag ) q = - q; + var cxp = q * rx * y1p / ry; + var cyp = - q * ry * x1p / rx; + + // Step 3: Compute (cx, cy) from (cx′, cy′) + var cx = Math.cos( x_axis_rotation ) * cxp - Math.sin( x_axis_rotation ) * cyp + ( start.x + end.x ) / 2; + var cy = Math.sin( x_axis_rotation ) * cxp + Math.cos( x_axis_rotation ) * cyp + ( start.y + end.y ) / 2; + + // Step 4: Compute θ1 and Δθ + var theta = svgAngle( 1, 0, ( x1p - cxp ) / rx, ( y1p - cyp ) / ry ); + var delta = svgAngle( ( x1p - cxp ) / rx, ( y1p - cyp ) / ry, ( - x1p - cxp ) / rx, ( - y1p - cyp ) / ry ) % ( Math.PI * 2 ); + + path.currentPath.absellipse( cx, cy, rx, ry, theta, theta + delta, sweep_flag === 0, x_axis_rotation ); + + } + + function svgAngle( ux, uy, vx, vy ) { + + var dot = ux * vx + uy * vy; + var len = Math.sqrt( ux * ux + uy * uy ) * Math.sqrt( vx * vx + vy * vy ); + var ang = Math.acos( Math.max( - 1, Math.min( 1, dot / len ) ) ); // floating point precision, slightly over values appear + if ( ( ux * vy - uy * vx ) < 0 ) ang = - ang; + return ang; + + } + + /* + * According to https://www.w3.org/TR/SVG/shapes.html#RectElementRXAttribute + * rounded corner should be rendered to elliptical arc, but bezier curve does the job well enough + */ + function parseRectNode( node, style ) { + + var x = parseFloat( node.getAttribute( 'x' ) || 0 ); + var y = parseFloat( node.getAttribute( 'y' ) || 0 ); + var rx = parseFloat( node.getAttribute( 'rx' ) || 0 ); + var ry = parseFloat( node.getAttribute( 'ry' ) || 0 ); + var w = parseFloat( node.getAttribute( 'width' ) ); + var h = parseFloat( node.getAttribute( 'height' ) ); + + var path = new ShapePath(); + path.color.setStyle( style.fill ); + path.moveTo( x + 2 * rx, y ); + path.lineTo( x + w - 2 * rx, y ); + if ( rx !== 0 || ry !== 0 ) path.bezierCurveTo( x + w, y, x + w, y, x + w, y + 2 * ry ); + path.lineTo( x + w, y + h - 2 * ry ); + if ( rx !== 0 || ry !== 0 ) path.bezierCurveTo( x + w, y + h, x + w, y + h, x + w - 2 * rx, y + h ); + path.lineTo( x + 2 * rx, y + h ); + + if ( rx !== 0 || ry !== 0 ) { + + path.bezierCurveTo( x, y + h, x, y + h, x, y + h - 2 * ry ); + + } + + path.lineTo( x, y + 2 * ry ); + + if ( rx !== 0 || ry !== 0 ) { + + path.bezierCurveTo( x, y, x, y, x + 2 * rx, y ); + + } + + return path; + + } + + function parsePolygonNode( node, style ) { + + function iterator( match, a, b ) { + + var x = parseFloat( a ); + var y = parseFloat( b ); + + if ( index === 0 ) { + + path.moveTo( x, y ); + + } else { + + path.lineTo( x, y ); + + } + + index ++; + + } + + var regex = /(-?[\d\.?]+)[,|\s](-?[\d\.?]+)/g; + + var path = new ShapePath(); + path.color.setStyle( style.fill ); + + var index = 0; + + node.getAttribute( 'points' ).replace( regex, iterator ); + + path.currentPath.autoClose = true; + + return path; + + } + + function parsePolylineNode( node, style ) { + + function iterator( match, a, b ) { + + var x = parseFloat( a ); + var y = parseFloat( b ); + + if ( index === 0 ) { + + path.moveTo( x, y ); + + } else { + + path.lineTo( x, y ); + + } + + index ++; + + } + + var regex = /(-?[\d\.?]+)[,|\s](-?[\d\.?]+)/g; + + var path = new ShapePath(); + path.color.setStyle( style.fill ); + + var index = 0; + + node.getAttribute( 'points' ).replace( regex, iterator ); + + path.currentPath.autoClose = false; + + return path; + + } + + function parseCircleNode( node, style ) { + + var x = parseFloat( node.getAttribute( 'cx' ) ); + var y = parseFloat( node.getAttribute( 'cy' ) ); + var r = parseFloat( node.getAttribute( 'r' ) ); + + var subpath = new Path(); + subpath.absarc( x, y, r, 0, Math.PI * 2 ); + + var path = new ShapePath(); + path.color.setStyle( style.fill ); + path.subPaths.push( subpath ); + + return path; + + } + + function parseEllipseNode( node, style ) { + + var x = parseFloat( node.getAttribute( 'cx' ) ); + var y = parseFloat( node.getAttribute( 'cy' ) ); + var rx = parseFloat( node.getAttribute( 'rx' ) ); + var ry = parseFloat( node.getAttribute( 'ry' ) ); + + var subpath = new Path(); + subpath.absellipse( x, y, rx, ry, 0, Math.PI * 2 ); + + var path = new ShapePath(); + path.color.setStyle( style.fill ); + path.subPaths.push( subpath ); + + return path; + + } + + function parseLineNode( node, style ) { + + var x1 = parseFloat( node.getAttribute( 'x1' ) ); + var y1 = parseFloat( node.getAttribute( 'y1' ) ); + var x2 = parseFloat( node.getAttribute( 'x2' ) ); + var y2 = parseFloat( node.getAttribute( 'y2' ) ); + + var path = new ShapePath(); + path.moveTo( x1, y1 ); + path.lineTo( x2, y2 ); + path.currentPath.autoClose = false; + + return path; + + } + + // + + function parseStyle( node, style ) { + + style = Object.assign( {}, style ); // clone style + + if ( node.hasAttribute( 'fill' ) ) style.fill = node.getAttribute( 'fill' ); + if ( node.style.fill !== '' ) style.fill = node.style.fill; + + return style; + + } + + function isVisible( style ) { + + return style.fill !== 'none' && style.fill !== 'transparent'; + + } + + // http://www.w3.org/TR/SVG11/implnote.html#PathElementImplementationNotes + + function getReflection( a, b ) { + + return a - ( b - a ); + + } + + function parseFloats( string ) { + + var array = string.split( /[\s,]+|(?=\s?[+\-])/ ); + + for ( var i = 0; i < array.length; i ++ ) { + + var number = array[ i ]; + + // Handle values like 48.6037.7.8 + // TODO Find a regex for this + + if ( number.indexOf( '.' ) !== number.lastIndexOf( '.' ) ) { + + var split = number.split( '.' ); + + for ( var s = 2; s < split.length; s ++ ) { + + array.splice( i + s - 1, 0, '0.' + split[ s ] ); + + } + + } + + array[ i ] = parseFloat( number ); + + } + + return array; + + + } + + function getNodeTransform( node ) { + + if ( ! node.hasAttribute( 'transform' ) ) { + + return null; + + } + + var transform = parseTransformNode( node ); + + if ( transform ) { + + if ( transformStack.length > 0 ) { + + transform.premultiply( transformStack[ transformStack.length - 1 ] ); + + } + + currentTransform.copy( transform ); + transformStack.push( transform ); + + } + + return transform; + + } + + function parseTransformNode( node ) { + + var transform = new Matrix3(); + var currentTransform = tempTransform0; + var transformsTexts = node.getAttribute( 'transform' ).split( ' ' ); + + for ( var tIndex = transformsTexts.length - 1; tIndex >= 0; tIndex -- ) { + + var transformText = transformsTexts[ tIndex ]; + var openParPos = transformText.indexOf( "(" ); + var closeParPos = transformText.indexOf( ")" ); + + if ( openParPos > 0 && openParPos < closeParPos ) { + + var transformType = transformText.substr( 0, openParPos ); + + var array = parseFloats( transformText.substr( openParPos + 1, closeParPos - openParPos - 1 ) ); + + currentTransform.identity(); + + switch ( transformType ) { + + case "translate": + + if ( array.length >= 1 ) { + + var tx = array[ 0 ]; + var ty = tx; + + if ( array.length >= 2 ) { + + ty = array[ 1 ]; + + } + + currentTransform.translate( tx, ty ); + + } + + break; + + case "rotate": + + if ( array.length >= 1 ) { + + var angle = 0; + var cx = 0; + var cy = 0; + + // Angle + angle = - array[ 0 ] * Math.PI / 180; + + if ( array.length >= 3 ) { + + // Center x, y + cx = array[ 1 ]; + cy = array[ 2 ]; + + } + + // Rotate around center (cx, cy) + tempTransform1.identity().translate( - cx, - cy ); + tempTransform2.identity().rotate( angle ); + tempTransform3.multiplyMatrices( tempTransform2, tempTransform1 ); + tempTransform1.identity().translate( cx, cy ); + currentTransform.multiplyMatrices( tempTransform1, tempTransform3 ); + + } + + break; + + case "scale": + + if ( array.length >= 1 ) { + + var scaleX = array[ 0 ]; + var scaleY = scaleX; + + if ( array.length >= 2 ) { + + scaleY = array[ 1 ]; + + } + + currentTransform.scale( scaleX, scaleY ); + + } + + break; + + case "skewX": + + if ( array.length === 1 ) { + + currentTransform.set( + 1, Math.tan( array[ 0 ] * Math.PI / 180 ), 0, + 0, 1, 0, + 0, 0, 1 + ); + + } + + break; + + case "skewY": + + if ( array.length === 1 ) { + + currentTransform.set( + 1, 0, 0, + Math.tan( array[ 0 ] * Math.PI / 180 ), 1, 0, + 0, 0, 1 + ); + + } + + break; + + case "matrix": + + if ( array.length === 6 ) { + + currentTransform.set( + array[ 0 ], array[ 2 ], array[ 4 ], + array[ 1 ], array[ 3 ], array[ 5 ], + 0, 0, 1 + ); + + } + + break; + + } + + } + + transform.premultiply( currentTransform ); + + } + + return transform; + + } + + function transformPath( path, m ) { + + function transfVec2( v2 ) { + + tempV3.set( v2.x, v2.y, 1 ).applyMatrix3( m ); + + v2.set( tempV3.x, tempV3.y ); + + } + + var isRotated = isTransformRotated( m ); + + var tempV2 = new Vector2(); + var tempV3 = new Vector3(); + + var subPaths = path.subPaths; + + for ( var i = 0, n = subPaths.length; i < n; i ++ ) { + + var subPath = subPaths[ i ]; + var curves = subPath.curves; + + for ( var j = 0; j < curves.length; j ++ ) { + + var curve = curves[ j ]; + + if ( curve.isLineCurve ) { + + transfVec2( curve.v1 ); + transfVec2( curve.v2 ); + + } else if ( curve.isCubicBezierCurve ) { + + transfVec2( curve.v0 ); + transfVec2( curve.v1 ); + transfVec2( curve.v2 ); + transfVec2( curve.v3 ); + + } else if ( curve.isQuadraticBezierCurve ) { + + transfVec2( curve.v0 ); + transfVec2( curve.v1 ); + transfVec2( curve.v2 ); + + } else if ( curve.isEllipseCurve ) { + + if ( isRotated ) { + + console.warn( "SVGLoader: Elliptic arc or ellipse rotation or skewing is not implemented." ); + + } + + tempV2.set( curve.aX, curve.aY ); + transfVec2( tempV2 ); + curve.aX = tempV2.x; + curve.aY = tempV2.y; + + curve.xRadius *= getTransformScaleX( m ); + curve.yRadius *= getTransformScaleY( m ); + + } + + } + + } + + } + + function isTransformRotated( m ) { + + return m.elements[ 1 ] !== 0 || m.elements[ 3 ] !== 0; + + } + + function getTransformScaleX( m ) { + + var te = m.elements; + return Math.sqrt( te[ 0 ] * te[ 0 ] + te[ 1 ] * te[ 1 ] ); + + } + + function getTransformScaleY( m ) { + + var te = m.elements; + return Math.sqrt( te[ 3 ] * te[ 3 ] + te[ 4 ] * te[ 4 ] ); + + } + + // + + console.log( 'THREE.SVGLoader' ); + + var paths = []; + + var transformStack = []; + + var tempTransform0 = new Matrix3(); + var tempTransform1 = new Matrix3(); + var tempTransform2 = new Matrix3(); + var tempTransform3 = new Matrix3(); + + var currentTransform = new Matrix3(); + + console.time( 'THREE.SVGLoader: DOMParser' ); + + var xml = new DOMParser().parseFromString( text, 'image/svg+xml' ); // application/xml + + console.timeEnd( 'THREE.SVGLoader: DOMParser' ); + + console.time( 'THREE.SVGLoader: Parse' ); + + parseNode( xml.documentElement, { fill: '#000' } ); + + // console.log( paths ); + + + console.timeEnd( 'THREE.SVGLoader: Parse' ); + + return paths; + + } + +}; + +export { SVGLoader }; diff --git a/examples/jsm/loaders/TDSLoader.js b/examples/jsm/loaders/TDSLoader.js new file mode 100644 index 00000000000000..a509a25dc8c7ea --- /dev/null +++ b/examples/jsm/loaders/TDSLoader.js @@ -0,0 +1,1143 @@ +/* + * Autodesk 3DS three.js file loader, based on lib3ds. + * + * Loads geometry with uv and materials basic properties with texture support. + * + * @author @tentone + * @author @timknip + * @class TDSLoader + * @constructor + */ + +import { + AdditiveBlending, + BufferGeometry, + Color, + DefaultLoadingManager, + DoubleSide, + FileLoader, + Float32BufferAttribute, + Group, + LoaderUtils, + Matrix4, + Mesh, + MeshPhongMaterial, + TextureLoader, +} from "../../../build/three.module.js"; + +var NULL_CHUNK = 0x0000; +var M3DMAGIC = 0x4D4D; +var SMAGIC = 0x2D2D; +var LMAGIC = 0x2D3D; +var MLIBMAGIC = 0x3DAA; +var MATMAGIC = 0x3DFF; +var CMAGIC = 0xC23D; +var M3D_VERSION = 0x0002; +var M3D_KFVERSION = 0x0005; +var COLOR_F = 0x0010; +var COLOR_24 = 0x0011; +var LIN_COLOR_24 = 0x0012; +var LIN_COLOR_F = 0x0013; +var INT_PERCENTAGE = 0x0030; +var FLOAT_PERCENTAGE = 0x0031; +var MDATA = 0x3D3D; +var MESH_VERSION = 0x3D3E; +var MASTER_SCALE = 0x0100; +var LO_SHADOW_BIAS = 0x1400; +var HI_SHADOW_BIAS = 0x1410; +var SHADOW_MAP_SIZE = 0x1420; +var SHADOW_SAMPLES = 0x1430; +var SHADOW_RANGE = 0x1440; +var SHADOW_FILTER = 0x1450; +var RAY_BIAS = 0x1460; +var O_CONSTS = 0x1500; +var AMBIENT_LIGHT = 0x2100; +var BIT_MAP = 0x1100; +var SOLID_BGND = 0x1200; +var V_GRADIENT = 0x1300; +var USE_BIT_MAP = 0x1101; +var USE_SOLID_BGND = 0x1201; +var USE_V_GRADIENT = 0x1301; +var FOG = 0x2200; +var FOG_BGND = 0x2210; +var LAYER_FOG = 0x2302; +var DISTANCE_CUE = 0x2300; +var DCUE_BGND = 0x2310; +var USE_FOG = 0x2201; +var USE_LAYER_FOG = 0x2303; +var USE_DISTANCE_CUE = 0x2301; +var MAT_ENTRY = 0xAFFF; +var MAT_NAME = 0xA000; +var MAT_AMBIENT = 0xA010; +var MAT_DIFFUSE = 0xA020; +var MAT_SPECULAR = 0xA030; +var MAT_SHININESS = 0xA040; +var MAT_SHIN2PCT = 0xA041; +var MAT_TRANSPARENCY = 0xA050; +var MAT_XPFALL = 0xA052; +var MAT_USE_XPFALL = 0xA240; +var MAT_REFBLUR = 0xA053; +var MAT_SHADING = 0xA100; +var MAT_USE_REFBLUR = 0xA250; +var MAT_SELF_ILLUM = 0xA084; +var MAT_TWO_SIDE = 0xA081; +var MAT_DECAL = 0xA082; +var MAT_ADDITIVE = 0xA083; +var MAT_WIRE = 0xA085; +var MAT_FACEMAP = 0xA088; +var MAT_TRANSFALLOFF_IN = 0xA08A; +var MAT_PHONGSOFT = 0xA08C; +var MAT_WIREABS = 0xA08E; +var MAT_WIRE_SIZE = 0xA087; +var MAT_TEXMAP = 0xA200; +var MAT_SXP_TEXT_DATA = 0xA320; +var MAT_TEXMASK = 0xA33E; +var MAT_SXP_TEXTMASK_DATA = 0xA32A; +var MAT_TEX2MAP = 0xA33A; +var MAT_SXP_TEXT2_DATA = 0xA321; +var MAT_TEX2MASK = 0xA340; +var MAT_SXP_TEXT2MASK_DATA = 0xA32C; +var MAT_OPACMAP = 0xA210; +var MAT_SXP_OPAC_DATA = 0xA322; +var MAT_OPACMASK = 0xA342; +var MAT_SXP_OPACMASK_DATA = 0xA32E; +var MAT_BUMPMAP = 0xA230; +var MAT_SXP_BUMP_DATA = 0xA324; +var MAT_BUMPMASK = 0xA344; +var MAT_SXP_BUMPMASK_DATA = 0xA330; +var MAT_SPECMAP = 0xA204; +var MAT_SXP_SPEC_DATA = 0xA325; +var MAT_SPECMASK = 0xA348; +var MAT_SXP_SPECMASK_DATA = 0xA332; +var MAT_SHINMAP = 0xA33C; +var MAT_SXP_SHIN_DATA = 0xA326; +var MAT_SHINMASK = 0xA346; +var MAT_SXP_SHINMASK_DATA = 0xA334; +var MAT_SELFIMAP = 0xA33D; +var MAT_SXP_SELFI_DATA = 0xA328; +var MAT_SELFIMASK = 0xA34A; +var MAT_SXP_SELFIMASK_DATA = 0xA336; +var MAT_REFLMAP = 0xA220; +var MAT_REFLMASK = 0xA34C; +var MAT_SXP_REFLMASK_DATA = 0xA338; +var MAT_ACUBIC = 0xA310; +var MAT_MAPNAME = 0xA300; +var MAT_MAP_TILING = 0xA351; +var MAT_MAP_TEXBLUR = 0xA353; +var MAT_MAP_USCALE = 0xA354; +var MAT_MAP_VSCALE = 0xA356; +var MAT_MAP_UOFFSET = 0xA358; +var MAT_MAP_VOFFSET = 0xA35A; +var MAT_MAP_ANG = 0xA35C; +var MAT_MAP_COL1 = 0xA360; +var MAT_MAP_COL2 = 0xA362; +var MAT_MAP_RCOL = 0xA364; +var MAT_MAP_GCOL = 0xA366; +var MAT_MAP_BCOL = 0xA368; +var NAMED_OBJECT = 0x4000; +var N_DIRECT_LIGHT = 0x4600; +var DL_OFF = 0x4620; +var DL_OUTER_RANGE = 0x465A; +var DL_INNER_RANGE = 0x4659; +var DL_MULTIPLIER = 0x465B; +var DL_EXCLUDE = 0x4654; +var DL_ATTENUATE = 0x4625; +var DL_SPOTLIGHT = 0x4610; +var DL_SPOT_ROLL = 0x4656; +var DL_SHADOWED = 0x4630; +var DL_LOCAL_SHADOW2 = 0x4641; +var DL_SEE_CONE = 0x4650; +var DL_SPOT_RECTANGULAR = 0x4651; +var DL_SPOT_ASPECT = 0x4657; +var DL_SPOT_PROJECTOR = 0x4653; +var DL_SPOT_OVERSHOOT = 0x4652; +var DL_RAY_BIAS = 0x4658; +var DL_RAYSHAD = 0x4627; +var N_CAMERA = 0x4700; +var CAM_SEE_CONE = 0x4710; +var CAM_RANGES = 0x4720; +var OBJ_HIDDEN = 0x4010; +var OBJ_VIS_LOFTER = 0x4011; +var OBJ_DOESNT_CAST = 0x4012; +var OBJ_DONT_RECVSHADOW = 0x4017; +var OBJ_MATTE = 0x4013; +var OBJ_FAST = 0x4014; +var OBJ_PROCEDURAL = 0x4015; +var OBJ_FROZEN = 0x4016; +var N_TRI_OBJECT = 0x4100; +var POINT_ARRAY = 0x4110; +var POINT_FLAG_ARRAY = 0x4111; +var FACE_ARRAY = 0x4120; +var MSH_MAT_GROUP = 0x4130; +var SMOOTH_GROUP = 0x4150; +var MSH_BOXMAP = 0x4190; +var TEX_VERTS = 0x4140; +var MESH_MATRIX = 0x4160; +var MESH_COLOR = 0x4165; +var MESH_TEXTURE_INFO = 0x4170; +var KFDATA = 0xB000; +var KFHDR = 0xB00A; +var KFSEG = 0xB008; +var KFCURTIME = 0xB009; +var AMBIENT_NODE_TAG = 0xB001; +var OBJECT_NODE_TAG = 0xB002; +var CAMERA_NODE_TAG = 0xB003; +var TARGET_NODE_TAG = 0xB004; +var LIGHT_NODE_TAG = 0xB005; +var L_TARGET_NODE_TAG = 0xB006; +var SPOTLIGHT_NODE_TAG = 0xB007; +var NODE_ID = 0xB030; +var NODE_HDR = 0xB010; +var PIVOT = 0xB013; +var INSTANCE_NAME = 0xB011; +var MORPH_SMOOTH = 0xB015; +var BOUNDBOX = 0xB014; +var POS_TRACK_TAG = 0xB020; +var COL_TRACK_TAG = 0xB025; +var ROT_TRACK_TAG = 0xB021; +var SCL_TRACK_TAG = 0xB022; +var MORPH_TRACK_TAG = 0xB026; +var FOV_TRACK_TAG = 0xB023; +var ROLL_TRACK_TAG = 0xB024; +var HOT_TRACK_TAG = 0xB027; +var FALL_TRACK_TAG = 0xB028; +var HIDE_TRACK_TAG = 0xB029; +var POLY_2D = 0x5000; +var SHAPE_OK = 0x5010; +var SHAPE_NOT_OK = 0x5011; +var SHAPE_HOOK = 0x5020; +var PATH_3D = 0x6000; +var PATH_MATRIX = 0x6005; +var SHAPE_2D = 0x6010; +var M_SCALE = 0x6020; +var M_TWIST = 0x6030; +var M_TEETER = 0x6040; +var M_FIT = 0x6050; +var M_BEVEL = 0x6060; +var XZ_CURVE = 0x6070; +var YZ_CURVE = 0x6080; +var INTERPCT = 0x6090; +var DEFORM_LIMIT = 0x60A0; +var USE_CONTOUR = 0x6100; +var USE_TWEEN = 0x6110; +var USE_SCALE = 0x6120; +var USE_TWIST = 0x6130; +var USE_TEETER = 0x6140; +var USE_FIT = 0x6150; +var USE_BEVEL = 0x6160; +var DEFAULT_VIEW = 0x3000; +var VIEW_TOP = 0x3010; +var VIEW_BOTTOM = 0x3020; +var VIEW_LEFT = 0x3030; +var VIEW_RIGHT = 0x3040; +var VIEW_FRONT = 0x3050; +var VIEW_BACK = 0x3060; +var VIEW_USER = 0x3070; +var VIEW_CAMERA = 0x3080; +var VIEW_WINDOW = 0x3090; +var VIEWPORT_LAYOUT_OLD = 0x7000; +var VIEWPORT_DATA_OLD = 0x7010; +var VIEWPORT_LAYOUT = 0x7001; +var VIEWPORT_DATA = 0x7011; +var VIEWPORT_DATA_3 = 0x7012; +var VIEWPORT_SIZE = 0x7020; +var NETWORK_VIEW = 0x7030; + +var TDSLoader = function ( manager ) { + + this.manager = ( manager !== undefined ) ? manager : DefaultLoadingManager; + this.debug = false; + + this.group = null; + this.position = 0; + + this.materials = []; + this.meshes = []; + +}; + +TDSLoader.prototype = { + + constructor: TDSLoader, + + crossOrigin: 'anonymous', + + /** + * Load 3ds file from url. + * + * @method load + * @param {[type]} url URL for the file. + * @param {Function} onLoad onLoad callback, receives group Object3D as argument. + * @param {Function} onProgress onProgress callback. + * @param {Function} onError onError callback. + */ + load: function ( url, onLoad, onProgress, onError ) { + + var scope = this; + + var path = this.path !== undefined ? this.path : LoaderUtils.extractUrlBase( url ); + + var loader = new FileLoader( this.manager ); + loader.setPath( this.path ); + loader.setResponseType( 'arraybuffer' ); + + loader.load( url, function ( data ) { + + onLoad( scope.parse( data, path ) ); + + }, onProgress, onError ); + + }, + + /** + * Parse arraybuffer data and load 3ds file. + * + * @method parse + * @param {ArrayBuffer} arraybuffer Arraybuffer data to be loaded. + * @param {String} path Path for external resources. + * @return {Object3D} Group loaded from 3ds file. + */ + parse: function ( arraybuffer, path ) { + + this.group = new Group(); + this.position = 0; + this.materials = []; + this.meshes = []; + + this.readFile( arraybuffer, path ); + + for ( var i = 0; i < this.meshes.length; i ++ ) { + + this.group.add( this.meshes[ i ] ); + + } + + return this.group; + + }, + + /** + * Decode file content to read 3ds data. + * + * @method readFile + * @param {ArrayBuffer} arraybuffer Arraybuffer data to be loaded. + */ + readFile: function ( arraybuffer, path ) { + + var data = new DataView( arraybuffer ); + var chunk = this.readChunk( data ); + + if ( chunk.id === MLIBMAGIC || chunk.id === CMAGIC || chunk.id === M3DMAGIC ) { + + var next = this.nextChunk( data, chunk ); + + while ( next !== 0 ) { + + if ( next === M3D_VERSION ) { + + var version = this.readDWord( data ); + this.debugMessage( '3DS file version: ' + version ); + + } else if ( next === MDATA ) { + + this.resetPosition( data ); + this.readMeshData( data, path ); + + } else { + + this.debugMessage( 'Unknown main chunk: ' + next.toString( 16 ) ); + + } + + next = this.nextChunk( data, chunk ); + + } + + } + + this.debugMessage( 'Parsed ' + this.meshes.length + ' meshes' ); + + }, + + /** + * Read mesh data chunk. + * + * @method readMeshData + * @param {Dataview} data Dataview in use. + */ + readMeshData: function ( data, path ) { + + var chunk = this.readChunk( data ); + var next = this.nextChunk( data, chunk ); + + while ( next !== 0 ) { + + if ( next === MESH_VERSION ) { + + var version = + this.readDWord( data ); + this.debugMessage( 'Mesh Version: ' + version ); + + } else if ( next === MASTER_SCALE ) { + + var scale = this.readFloat( data ); + this.debugMessage( 'Master scale: ' + scale ); + this.group.scale.set( scale, scale, scale ); + + } else if ( next === NAMED_OBJECT ) { + + this.debugMessage( 'Named Object' ); + this.resetPosition( data ); + this.readNamedObject( data ); + + } else if ( next === MAT_ENTRY ) { + + this.debugMessage( 'Material' ); + this.resetPosition( data ); + this.readMaterialEntry( data, path ); + + } else { + + this.debugMessage( 'Unknown MDATA chunk: ' + next.toString( 16 ) ); + + } + + next = this.nextChunk( data, chunk ); + + } + + }, + + /** + * Read named object chunk. + * + * @method readNamedObject + * @param {Dataview} data Dataview in use. + */ + readNamedObject: function ( data ) { + + var chunk = this.readChunk( data ); + var name = this.readString( data, 64 ); + chunk.cur = this.position; + + var next = this.nextChunk( data, chunk ); + while ( next !== 0 ) { + + if ( next === N_TRI_OBJECT ) { + + this.resetPosition( data ); + var mesh = this.readMesh( data ); + mesh.name = name; + this.meshes.push( mesh ); + + } else { + + this.debugMessage( 'Unknown named object chunk: ' + next.toString( 16 ) ); + + } + + next = this.nextChunk( data, chunk ); + + } + + this.endChunk( chunk ); + + }, + + /** + * Read material data chunk and add it to the material list. + * + * @method readMaterialEntry + * @param {Dataview} data Dataview in use. + */ + readMaterialEntry: function ( data, path ) { + + var chunk = this.readChunk( data ); + var next = this.nextChunk( data, chunk ); + var material = new MeshPhongMaterial(); + + while ( next !== 0 ) { + + if ( next === MAT_NAME ) { + + material.name = this.readString( data, 64 ); + this.debugMessage( ' Name: ' + material.name ); + + } else if ( next === MAT_WIRE ) { + + this.debugMessage( ' Wireframe' ); + material.wireframe = true; + + } else if ( next === MAT_WIRE_SIZE ) { + + var value = this.readByte( data ); + material.wireframeLinewidth = value; + this.debugMessage( ' Wireframe Thickness: ' + value ); + + } else if ( next === MAT_TWO_SIDE ) { + + material.side = DoubleSide; + this.debugMessage( ' DoubleSided' ); + + } else if ( next === MAT_ADDITIVE ) { + + this.debugMessage( ' Additive Blending' ); + material.blending = AdditiveBlending; + + } else if ( next === MAT_DIFFUSE ) { + + this.debugMessage( ' Diffuse Color' ); + material.color = this.readColor( data ); + + } else if ( next === MAT_SPECULAR ) { + + this.debugMessage( ' Specular Color' ); + material.specular = this.readColor( data ); + + } else if ( next === MAT_AMBIENT ) { + + this.debugMessage( ' Ambient color' ); + material.color = this.readColor( data ); + + } else if ( next === MAT_SHININESS ) { + + var shininess = this.readWord( data ); + material.shininess = shininess; + this.debugMessage( ' Shininess : ' + shininess ); + + } else if ( next === MAT_TEXMAP ) { + + this.debugMessage( ' ColorMap' ); + this.resetPosition( data ); + material.map = this.readMap( data, path ); + + } else if ( next === MAT_BUMPMAP ) { + + this.debugMessage( ' BumpMap' ); + this.resetPosition( data ); + material.bumpMap = this.readMap( data, path ); + + } else if ( next === MAT_OPACMAP ) { + + this.debugMessage( ' OpacityMap' ); + this.resetPosition( data ); + material.alphaMap = this.readMap( data, path ); + + } else if ( next === MAT_SPECMAP ) { + + this.debugMessage( ' SpecularMap' ); + this.resetPosition( data ); + material.specularMap = this.readMap( data, path ); + + } else { + + this.debugMessage( ' Unknown material chunk: ' + next.toString( 16 ) ); + + } + + next = this.nextChunk( data, chunk ); + + } + + this.endChunk( chunk ); + + this.materials[ material.name ] = material; + + }, + + /** + * Read mesh data chunk. + * + * @method readMesh + * @param {Dataview} data Dataview in use. + */ + readMesh: function ( data ) { + + var chunk = this.readChunk( data ); + var next = this.nextChunk( data, chunk ); + + var geometry = new BufferGeometry(); + var uvs = []; + + var material = new MeshPhongMaterial(); + var mesh = new Mesh( geometry, material ); + mesh.name = 'mesh'; + + while ( next !== 0 ) { + + if ( next === POINT_ARRAY ) { + + var points = this.readWord( data ); + + this.debugMessage( ' Vertex: ' + points ); + + //BufferGeometry + + var vertices = []; + + for ( var i = 0; i < points; i ++ ) { + + vertices.push( this.readFloat( data ) ); + vertices.push( this.readFloat( data ) ); + vertices.push( this.readFloat( data ) ); + + } + + geometry.addAttribute( 'position', new Float32BufferAttribute( vertices, 3 ) ); + + } else if ( next === FACE_ARRAY ) { + + this.resetPosition( data ); + this.readFaceArray( data, mesh ); + + } else if ( next === TEX_VERTS ) { + + var texels = this.readWord( data ); + + this.debugMessage( ' UV: ' + texels ); + + //BufferGeometry + + var uvs = []; + + for ( var i = 0; i < texels; i ++ ) { + + uvs.push( this.readFloat( data ) ); + uvs.push( this.readFloat( data ) ); + + } + + geometry.addAttribute( 'uv', new Float32BufferAttribute( uvs, 2 ) ); + + + } else if ( next === MESH_MATRIX ) { + + this.debugMessage( ' Tranformation Matrix (TODO)' ); + + var values = []; + for ( var i = 0; i < 12; i ++ ) { + + values[ i ] = this.readFloat( data ); + + } + + var matrix = new Matrix4(); + + //X Line + matrix.elements[ 0 ] = values[ 0 ]; + matrix.elements[ 1 ] = values[ 6 ]; + matrix.elements[ 2 ] = values[ 3 ]; + matrix.elements[ 3 ] = values[ 9 ]; + + //Y Line + matrix.elements[ 4 ] = values[ 2 ]; + matrix.elements[ 5 ] = values[ 8 ]; + matrix.elements[ 6 ] = values[ 5 ]; + matrix.elements[ 7 ] = values[ 11 ]; + + //Z Line + matrix.elements[ 8 ] = values[ 1 ]; + matrix.elements[ 9 ] = values[ 7 ]; + matrix.elements[ 10 ] = values[ 4 ]; + matrix.elements[ 11 ] = values[ 10 ]; + + //W Line + matrix.elements[ 12 ] = 0; + matrix.elements[ 13 ] = 0; + matrix.elements[ 14 ] = 0; + matrix.elements[ 15 ] = 1; + + matrix.transpose(); + + var inverse = new Matrix4(); + inverse.getInverse( matrix, true ); + geometry.applyMatrix( inverse ); + + matrix.decompose( mesh.position, mesh.quaternion, mesh.scale ); + + } else { + + this.debugMessage( ' Unknown mesh chunk: ' + next.toString( 16 ) ); + + } + + next = this.nextChunk( data, chunk ); + + } + + this.endChunk( chunk ); + + geometry.computeVertexNormals(); + + return mesh; + + }, + + /** + * Read face array data chunk. + * + * @method readFaceArray + * @param {Dataview} data Dataview in use. + * @param {Mesh} mesh Mesh to be filled with the data read. + */ + readFaceArray: function ( data, mesh ) { + + var chunk = this.readChunk( data ); + var faces = this.readWord( data ); + + this.debugMessage( ' Faces: ' + faces ); + + var index = []; + + for ( var i = 0; i < faces; ++ i ) { + + index.push( this.readWord( data ), this.readWord( data ), this.readWord( data ) ); + + var visibility = this.readWord( data ); + + } + + mesh.geometry.setIndex( index ); + + //The rest of the FACE_ARRAY chunk is subchunks + + while ( this.position < chunk.end ) { + + var chunk = this.readChunk( data ); + + if ( chunk.id === MSH_MAT_GROUP ) { + + this.debugMessage( ' Material Group' ); + + this.resetPosition( data ); + + var group = this.readMaterialGroup( data ); + + var material = this.materials[ group.name ]; + + if ( material !== undefined ) { + + mesh.material = material; + + if ( material.name === '' ) { + + material.name = mesh.name; + + } + + } + + } else { + + this.debugMessage( ' Unknown face array chunk: ' + chunk.toString( 16 ) ); + + } + + this.endChunk( chunk ); + + } + + this.endChunk( chunk ); + + }, + + /** + * Read texture map data chunk. + * + * @method readMap + * @param {Dataview} data Dataview in use. + * @return {Texture} Texture read from this data chunk. + */ + readMap: function ( data, path ) { + + var chunk = this.readChunk( data ); + var next = this.nextChunk( data, chunk ); + var texture = {}; + + var loader = new TextureLoader( this.manager ); + loader.setPath( this.resourcePath || path ).setCrossOrigin( this.crossOrigin ); + + while ( next !== 0 ) { + + if ( next === MAT_MAPNAME ) { + + var name = this.readString( data, 128 ); + texture = loader.load( name ); + + this.debugMessage( ' File: ' + path + name ); + + } else if ( next === MAT_MAP_UOFFSET ) { + + texture.offset.x = this.readFloat( data ); + this.debugMessage( ' OffsetX: ' + texture.offset.x ); + + } else if ( next === MAT_MAP_VOFFSET ) { + + texture.offset.y = this.readFloat( data ); + this.debugMessage( ' OffsetY: ' + texture.offset.y ); + + } else if ( next === MAT_MAP_USCALE ) { + + texture.repeat.x = this.readFloat( data ); + this.debugMessage( ' RepeatX: ' + texture.repeat.x ); + + } else if ( next === MAT_MAP_VSCALE ) { + + texture.repeat.y = this.readFloat( data ); + this.debugMessage( ' RepeatY: ' + texture.repeat.y ); + + } else { + + this.debugMessage( ' Unknown map chunk: ' + next.toString( 16 ) ); + + } + + next = this.nextChunk( data, chunk ); + + } + + this.endChunk( chunk ); + + return texture; + + }, + + /** + * Read material group data chunk. + * + * @method readMaterialGroup + * @param {Dataview} data Dataview in use. + * @return {Object} Object with name and index of the object. + */ + readMaterialGroup: function ( data ) { + + var chunk = this.readChunk( data ); + var name = this.readString( data, 64 ); + var numFaces = this.readWord( data ); + + this.debugMessage( ' Name: ' + name ); + this.debugMessage( ' Faces: ' + numFaces ); + + var index = []; + for ( var i = 0; i < numFaces; ++ i ) { + + index.push( this.readWord( data ) ); + + } + + return { name: name, index: index }; + + }, + + /** + * Read a color value. + * + * @method readColor + * @param {DataView} data Dataview. + * @return {Color} Color value read.. + */ + readColor: function ( data ) { + + var chunk = this.readChunk( data ); + var color = new Color(); + + if ( chunk.id === COLOR_24 || chunk.id === LIN_COLOR_24 ) { + + var r = this.readByte( data ); + var g = this.readByte( data ); + var b = this.readByte( data ); + + color.setRGB( r / 255, g / 255, b / 255 ); + + this.debugMessage( ' Color: ' + color.r + ', ' + color.g + ', ' + color.b ); + + } else if ( chunk.id === COLOR_F || chunk.id === LIN_COLOR_F ) { + + var r = this.readFloat( data ); + var g = this.readFloat( data ); + var b = this.readFloat( data ); + + color.setRGB( r, g, b ); + + this.debugMessage( ' Color: ' + color.r + ', ' + color.g + ', ' + color.b ); + + } else { + + this.debugMessage( ' Unknown color chunk: ' + chunk.toString( 16 ) ); + + } + + this.endChunk( chunk ); + return color; + + }, + + /** + * Read next chunk of data. + * + * @method readChunk + * @param {DataView} data Dataview. + * @return {Object} Chunk of data read. + */ + readChunk: function ( data ) { + + var chunk = {}; + + chunk.cur = this.position; + chunk.id = this.readWord( data ); + chunk.size = this.readDWord( data ); + chunk.end = chunk.cur + chunk.size; + chunk.cur += 6; + + return chunk; + + }, + + /** + * Set position to the end of the current chunk of data. + * + * @method endChunk + * @param {Object} chunk Data chunk. + */ + endChunk: function ( chunk ) { + + this.position = chunk.end; + + }, + + /** + * Move to the next data chunk. + * + * @method nextChunk + * @param {DataView} data Dataview. + * @param {Object} chunk Data chunk. + */ + nextChunk: function ( data, chunk ) { + + if ( chunk.cur >= chunk.end ) { + + return 0; + + } + + this.position = chunk.cur; + + try { + + var next = this.readChunk( data ); + chunk.cur += next.size; + return next.id; + + } catch ( e ) { + + this.debugMessage( 'Unable to read chunk at ' + this.position ); + return 0; + + } + + }, + + /** + * Reset dataview position. + * + * @method resetPosition + * @param {DataView} data Dataview. + */ + resetPosition: function () { + + this.position -= 6; + + }, + + /** + * Read byte value. + * + * @method readByte + * @param {DataView} data Dataview to read data from. + * @return {Number} Data read from the dataview. + */ + readByte: function ( data ) { + + var v = data.getUint8( this.position, true ); + this.position += 1; + return v; + + }, + + /** + * Read 32 bit float value. + * + * @method readFloat + * @param {DataView} data Dataview to read data from. + * @return {Number} Data read from the dataview. + */ + readFloat: function ( data ) { + + try { + + var v = data.getFloat32( this.position, true ); + this.position += 4; + return v; + + } catch ( e ) { + + this.debugMessage( e + ' ' + this.position + ' ' + data.byteLength ); + + } + + }, + + /** + * Read 32 bit signed integer value. + * + * @method readInt + * @param {DataView} data Dataview to read data from. + * @return {Number} Data read from the dataview. + */ + readInt: function ( data ) { + + var v = data.getInt32( this.position, true ); + this.position += 4; + return v; + + }, + + /** + * Read 16 bit signed integer value. + * + * @method readShort + * @param {DataView} data Dataview to read data from. + * @return {Number} Data read from the dataview. + */ + readShort: function ( data ) { + + var v = data.getInt16( this.position, true ); + this.position += 2; + return v; + + }, + + /** + * Read 64 bit unsigned integer value. + * + * @method readDWord + * @param {DataView} data Dataview to read data from. + * @return {Number} Data read from the dataview. + */ + readDWord: function ( data ) { + + var v = data.getUint32( this.position, true ); + this.position += 4; + return v; + + }, + + /** + * Read 32 bit unsigned integer value. + * + * @method readWord + * @param {DataView} data Dataview to read data from. + * @return {Number} Data read from the dataview. + */ + readWord: function ( data ) { + + var v = data.getUint16( this.position, true ); + this.position += 2; + return v; + + }, + + /** + * Read string value. + * + * @method readString + * @param {DataView} data Dataview to read data from. + * @param {Number} maxLength Max size of the string to be read. + * @return {String} Data read from the dataview. + */ + readString: function ( data, maxLength ) { + + var s = ''; + + for ( var i = 0; i < maxLength; i ++ ) { + + var c = this.readByte( data ); + if ( ! c ) { + + break; + + } + + s += String.fromCharCode( c ); + + } + + return s; + + }, + + /** + * Set path to adjust the path to the original 3ds file. + * + * @method setPath + * @param {String} path Path to file. + * @return Self for chaining. + */ + setPath: function ( path ) { + + this.path = path; + + return this; + + }, + + /** + * Set resource path used to determine the path to attached resources like textures. + * + * @method setResourcePath + * @param {String} resourcePath Path to resources. + * @return Self for chaining. + */ + setResourcePath: function ( resourcePath ) { + + this.resourcePath = resourcePath; + + return this; + + }, + + /** + * Set crossOrigin value to configure CORS settings + * for the image loading process. + * + * @method setCrossOrigin + * @param {String} crossOrigin crossOrigin string. + * @return Self for chaining. + */ + setCrossOrigin: function ( crossOrigin ) { + + this.crossOrigin = crossOrigin; + + return this; + + }, + + /** + * Print debug message to the console. + * + * Is controlled by a flag to show or hide debug messages. + * + * @method debugMessage + * @param {Object} message Debug message to print to the console. + */ + debugMessage: function ( message ) { + + if ( this.debug ) { + + console.log( message ); + + } + + } +}; + +export { TDSLoader }; diff --git a/examples/jsm/loaders/TGALoader.js b/examples/jsm/loaders/TGALoader.js new file mode 100644 index 00000000000000..99351f67821d68 --- /dev/null +++ b/examples/jsm/loaders/TGALoader.js @@ -0,0 +1,556 @@ +/* + * @author Daosheng Mu / https://github.com/DaoshengMu/ + * @author mrdoob / http://mrdoob.com/ + * @author takahirox / https://github.com/takahirox/ + */ + +import { + DefaultLoadingManager, + FileLoader, + Texture, +} from "../../../build/three.module.js"; + +var TGALoader = function ( manager ) { + + this.manager = ( manager !== undefined ) ? manager : DefaultLoadingManager; + +}; + +TGALoader.prototype = { + + constructor: TGALoader, + + load: function ( url, onLoad, onProgress, onError ) { + + var scope = this; + + var texture = new Texture(); + + var loader = new FileLoader( this.manager ); + loader.setResponseType( 'arraybuffer' ); + loader.setPath( this.path ); + + loader.load( url, function ( buffer ) { + + texture.image = scope.parse( buffer ); + texture.needsUpdate = true; + + if ( onLoad !== undefined ) { + + onLoad( texture ); + + } + + }, onProgress, onError ); + + return texture; + + }, + + parse: function ( buffer ) { + + // reference from vthibault, https://github.com/vthibault/roBrowser/blob/master/src/Loaders/Targa.js + + function tgaCheckHeader( header ) { + + switch ( header.image_type ) { + + // check indexed type + + case TGA_TYPE_INDEXED: + case TGA_TYPE_RLE_INDEXED: + if ( header.colormap_length > 256 || header.colormap_size !== 24 || header.colormap_type !== 1 ) { + + console.error( 'THREE.TGALoader: Invalid type colormap data for indexed type.' ); + + } + break; + + // check colormap type + + case TGA_TYPE_RGB: + case TGA_TYPE_GREY: + case TGA_TYPE_RLE_RGB: + case TGA_TYPE_RLE_GREY: + if ( header.colormap_type ) { + + console.error( 'THREE.TGALoader: Invalid type colormap data for colormap type.' ); + + } + break; + + // What the need of a file without data ? + + case TGA_TYPE_NO_DATA: + console.error( 'THREE.TGALoader: No data.' ); + + // Invalid type ? + + default: + console.error( 'THREE.TGALoader: Invalid type "%s".', header.image_type ); + + } + + // check image width and height + + if ( header.width <= 0 || header.height <= 0 ) { + + console.error( 'THREE.TGALoader: Invalid image size.' ); + + } + + // check image pixel size + + if ( header.pixel_size !== 8 && header.pixel_size !== 16 && + header.pixel_size !== 24 && header.pixel_size !== 32 ) { + + console.error( 'THREE.TGALoader: Invalid pixel size "%s".', header.pixel_size ); + + } + + } + + // parse tga image buffer + + function tgaParse( use_rle, use_pal, header, offset, data ) { + + var pixel_data, + pixel_size, + pixel_total, + palettes; + + pixel_size = header.pixel_size >> 3; + pixel_total = header.width * header.height * pixel_size; + + // read palettes + + if ( use_pal ) { + + palettes = data.subarray( offset, offset += header.colormap_length * ( header.colormap_size >> 3 ) ); + + } + + // read RLE + + if ( use_rle ) { + + pixel_data = new Uint8Array( pixel_total ); + + var c, count, i; + var shift = 0; + var pixels = new Uint8Array( pixel_size ); + + while ( shift < pixel_total ) { + + c = data[ offset ++ ]; + count = ( c & 0x7f ) + 1; + + // RLE pixels + + if ( c & 0x80 ) { + + // bind pixel tmp array + + for ( i = 0; i < pixel_size; ++ i ) { + + pixels[ i ] = data[ offset ++ ]; + + } + + // copy pixel array + + for ( i = 0; i < count; ++ i ) { + + pixel_data.set( pixels, shift + i * pixel_size ); + + } + + shift += pixel_size * count; + + } else { + + // raw pixels + + count *= pixel_size; + for ( i = 0; i < count; ++ i ) { + + pixel_data[ shift + i ] = data[ offset ++ ]; + + } + shift += count; + + } + + } + + } else { + + // raw pixels + + pixel_data = data.subarray( + offset, offset += ( use_pal ? header.width * header.height : pixel_total ) + ); + + } + + return { + pixel_data: pixel_data, + palettes: palettes + }; + + } + + function tgaGetImageData8bits( imageData, y_start, y_step, y_end, x_start, x_step, x_end, image, palettes ) { + + var colormap = palettes; + var color, i = 0, x, y; + var width = header.width; + + for ( y = y_start; y !== y_end; y += y_step ) { + + for ( x = x_start; x !== x_end; x += x_step, i ++ ) { + + color = image[ i ]; + imageData[ ( x + width * y ) * 4 + 3 ] = 255; + imageData[ ( x + width * y ) * 4 + 2 ] = colormap[ ( color * 3 ) + 0 ]; + imageData[ ( x + width * y ) * 4 + 1 ] = colormap[ ( color * 3 ) + 1 ]; + imageData[ ( x + width * y ) * 4 + 0 ] = colormap[ ( color * 3 ) + 2 ]; + + } + + } + + return imageData; + + } + + function tgaGetImageData16bits( imageData, y_start, y_step, y_end, x_start, x_step, x_end, image ) { + + var color, i = 0, x, y; + var width = header.width; + + for ( y = y_start; y !== y_end; y += y_step ) { + + for ( x = x_start; x !== x_end; x += x_step, i += 2 ) { + + color = image[ i + 0 ] + ( image[ i + 1 ] << 8 ); // Inversed ? + imageData[ ( x + width * y ) * 4 + 0 ] = ( color & 0x7C00 ) >> 7; + imageData[ ( x + width * y ) * 4 + 1 ] = ( color & 0x03E0 ) >> 2; + imageData[ ( x + width * y ) * 4 + 2 ] = ( color & 0x001F ) >> 3; + imageData[ ( x + width * y ) * 4 + 3 ] = ( color & 0x8000 ) ? 0 : 255; + + } + + } + + return imageData; + + } + + function tgaGetImageData24bits( imageData, y_start, y_step, y_end, x_start, x_step, x_end, image ) { + + var i = 0, x, y; + var width = header.width; + + for ( y = y_start; y !== y_end; y += y_step ) { + + for ( x = x_start; x !== x_end; x += x_step, i += 3 ) { + + imageData[ ( x + width * y ) * 4 + 3 ] = 255; + imageData[ ( x + width * y ) * 4 + 2 ] = image[ i + 0 ]; + imageData[ ( x + width * y ) * 4 + 1 ] = image[ i + 1 ]; + imageData[ ( x + width * y ) * 4 + 0 ] = image[ i + 2 ]; + + } + + } + + return imageData; + + } + + function tgaGetImageData32bits( imageData, y_start, y_step, y_end, x_start, x_step, x_end, image ) { + + var i = 0, x, y; + var width = header.width; + + for ( y = y_start; y !== y_end; y += y_step ) { + + for ( x = x_start; x !== x_end; x += x_step, i += 4 ) { + + imageData[ ( x + width * y ) * 4 + 2 ] = image[ i + 0 ]; + imageData[ ( x + width * y ) * 4 + 1 ] = image[ i + 1 ]; + imageData[ ( x + width * y ) * 4 + 0 ] = image[ i + 2 ]; + imageData[ ( x + width * y ) * 4 + 3 ] = image[ i + 3 ]; + + } + + } + + return imageData; + + } + + function tgaGetImageDataGrey8bits( imageData, y_start, y_step, y_end, x_start, x_step, x_end, image ) { + + var color, i = 0, x, y; + var width = header.width; + + for ( y = y_start; y !== y_end; y += y_step ) { + + for ( x = x_start; x !== x_end; x += x_step, i ++ ) { + + color = image[ i ]; + imageData[ ( x + width * y ) * 4 + 0 ] = color; + imageData[ ( x + width * y ) * 4 + 1 ] = color; + imageData[ ( x + width * y ) * 4 + 2 ] = color; + imageData[ ( x + width * y ) * 4 + 3 ] = 255; + + } + + } + + return imageData; + + } + + function tgaGetImageDataGrey16bits( imageData, y_start, y_step, y_end, x_start, x_step, x_end, image ) { + + var i = 0, x, y; + var width = header.width; + + for ( y = y_start; y !== y_end; y += y_step ) { + + for ( x = x_start; x !== x_end; x += x_step, i += 2 ) { + + imageData[ ( x + width * y ) * 4 + 0 ] = image[ i + 0 ]; + imageData[ ( x + width * y ) * 4 + 1 ] = image[ i + 0 ]; + imageData[ ( x + width * y ) * 4 + 2 ] = image[ i + 0 ]; + imageData[ ( x + width * y ) * 4 + 3 ] = image[ i + 1 ]; + + } + + } + + return imageData; + + } + + function getTgaRGBA( data, width, height, image, palette ) { + + var x_start, + y_start, + x_step, + y_step, + x_end, + y_end; + + switch ( ( header.flags & TGA_ORIGIN_MASK ) >> TGA_ORIGIN_SHIFT ) { + + default: + case TGA_ORIGIN_UL: + x_start = 0; + x_step = 1; + x_end = width; + y_start = 0; + y_step = 1; + y_end = height; + break; + + case TGA_ORIGIN_BL: + x_start = 0; + x_step = 1; + x_end = width; + y_start = height - 1; + y_step = - 1; + y_end = - 1; + break; + + case TGA_ORIGIN_UR: + x_start = width - 1; + x_step = - 1; + x_end = - 1; + y_start = 0; + y_step = 1; + y_end = height; + break; + + case TGA_ORIGIN_BR: + x_start = width - 1; + x_step = - 1; + x_end = - 1; + y_start = height - 1; + y_step = - 1; + y_end = - 1; + break; + + } + + if ( use_grey ) { + + switch ( header.pixel_size ) { + + case 8: + tgaGetImageDataGrey8bits( data, y_start, y_step, y_end, x_start, x_step, x_end, image ); + break; + + case 16: + tgaGetImageDataGrey16bits( data, y_start, y_step, y_end, x_start, x_step, x_end, image ); + break; + + default: + console.error( 'THREE.TGALoader: Format not supported.' ); + break; + + } + + } else { + + switch ( header.pixel_size ) { + + case 8: + tgaGetImageData8bits( data, y_start, y_step, y_end, x_start, x_step, x_end, image, palette ); + break; + + case 16: + tgaGetImageData16bits( data, y_start, y_step, y_end, x_start, x_step, x_end, image ); + break; + + case 24: + tgaGetImageData24bits( data, y_start, y_step, y_end, x_start, x_step, x_end, image ); + break; + + case 32: + tgaGetImageData32bits( data, y_start, y_step, y_end, x_start, x_step, x_end, image ); + break; + + default: + console.error( 'THREE.TGALoader: Format not supported.' ); + break; + + } + + } + + // Load image data according to specific method + // var func = 'tgaGetImageData' + (use_grey ? 'Grey' : '') + (header.pixel_size) + 'bits'; + // func(data, y_start, y_step, y_end, x_start, x_step, x_end, width, image, palette ); + return data; + + } + + // TGA constants + + var TGA_TYPE_NO_DATA = 0, + TGA_TYPE_INDEXED = 1, + TGA_TYPE_RGB = 2, + TGA_TYPE_GREY = 3, + TGA_TYPE_RLE_INDEXED = 9, + TGA_TYPE_RLE_RGB = 10, + TGA_TYPE_RLE_GREY = 11, + + TGA_ORIGIN_MASK = 0x30, + TGA_ORIGIN_SHIFT = 0x04, + TGA_ORIGIN_BL = 0x00, + TGA_ORIGIN_BR = 0x01, + TGA_ORIGIN_UL = 0x02, + TGA_ORIGIN_UR = 0x03; + + if ( buffer.length < 19 ) console.error( 'THREE.TGALoader: Not enough data to contain header.' ); + + var content = new Uint8Array( buffer ), + offset = 0, + header = { + id_length: content[ offset ++ ], + colormap_type: content[ offset ++ ], + image_type: content[ offset ++ ], + colormap_index: content[ offset ++ ] | content[ offset ++ ] << 8, + colormap_length: content[ offset ++ ] | content[ offset ++ ] << 8, + colormap_size: content[ offset ++ ], + origin: [ + content[ offset ++ ] | content[ offset ++ ] << 8, + content[ offset ++ ] | content[ offset ++ ] << 8 + ], + width: content[ offset ++ ] | content[ offset ++ ] << 8, + height: content[ offset ++ ] | content[ offset ++ ] << 8, + pixel_size: content[ offset ++ ], + flags: content[ offset ++ ] + }; + + // check tga if it is valid format + + tgaCheckHeader( header ); + + if ( header.id_length + offset > buffer.length ) { + + console.error( 'THREE.TGALoader: No data.' ); + + } + + // skip the needn't data + + offset += header.id_length; + + // get targa information about RLE compression and palette + + var use_rle = false, + use_pal = false, + use_grey = false; + + switch ( header.image_type ) { + + case TGA_TYPE_RLE_INDEXED: + use_rle = true; + use_pal = true; + break; + + case TGA_TYPE_INDEXED: + use_pal = true; + break; + + case TGA_TYPE_RLE_RGB: + use_rle = true; + break; + + case TGA_TYPE_RGB: + break; + + case TGA_TYPE_RLE_GREY: + use_rle = true; + use_grey = true; + break; + + case TGA_TYPE_GREY: + use_grey = true; + break; + + } + + // + + var canvas = document.createElement( 'canvas' ); + canvas.width = header.width; + canvas.height = header.height; + + var context = canvas.getContext( '2d' ); + var imageData = context.createImageData( header.width, header.height ); + + var result = tgaParse( use_rle, use_pal, header, offset, content ); + var rgbaData = getTgaRGBA( imageData.data, header.width, header.height, result.pixel_data, result.palettes ); + + context.putImageData( imageData, 0, 0 ); + + return canvas; + + }, + + setPath: function ( value ) { + + this.path = value; + return this; + + } + +}; + +export { TGALoader }; diff --git a/examples/jsm/loaders/TTFLoader.js b/examples/jsm/loaders/TTFLoader.js new file mode 100644 index 00000000000000..d0d1a2a410b279 --- /dev/null +++ b/examples/jsm/loaders/TTFLoader.js @@ -0,0 +1,207 @@ +/** + * @author gero3 / https://github.com/gero3 + * @author tentone / https://github.com/tentone + * + * Requires opentype.js to be included in the project. + * Loads TTF files and converts them into typeface JSON that can be used directly + * to create THREE.Font objects. + */ + +import { + DefaultLoadingManager, + FileLoader +} from "../../../build/three.module.js"; + +var TTFLoader = function (manager ) { + + this.manager = ( manager !== undefined ) ? manager : DefaultLoadingManager; + this.reversed = false; + +}; + +TTFLoader.prototype = { + + constructor: TTFLoader, + + load: function ( url, onLoad, onProgress, onError ) { + + var scope = this; + + var loader = new FileLoader( this.manager ); + loader.setPath( this.path ); + loader.setResponseType( 'arraybuffer' ); + loader.load( url, function ( buffer ) { + + onLoad( scope.parse( buffer ) ); + + }, onProgress, onError ); + + }, + + setPath: function ( value ) { + + this.path = value; + return this; + + }, + + parse: function ( arraybuffer ) { + + function convert( font, reversed ) { + + var round = Math.round; + + var glyphs = {}; + var scale = ( 100000 ) / ( ( font.unitsPerEm || 2048 ) * 72 ); + + for ( var i = 0; i < font.glyphs.length; i ++ ) { + + var glyph = font.glyphs.glyphs[ i ]; + + if ( glyph.unicode !== undefined ) { + + var token = { + ha: round( glyph.advanceWidth * scale ), + x_min: round( glyph.xMin * scale ), + x_max: round( glyph.xMax * scale ), + o: '' + }; + + if ( reversed ) { + + glyph.path.commands = reverseCommands( glyph.path.commands ); + + } + + glyph.path.commands.forEach( function ( command, i ) { + + if ( command.type.toLowerCase() === 'c' ) { + + command.type = 'b'; + + } + + token.o += command.type.toLowerCase() + ' '; + + if ( command.x !== undefined && command.y !== undefined ) { + + token.o += round( command.x * scale ) + ' ' + round( command.y * scale ) + ' '; + + } + + if ( command.x1 !== undefined && command.y1 !== undefined ) { + + token.o += round( command.x1 * scale ) + ' ' + round( command.y1 * scale ) + ' '; + + } + + if ( command.x2 !== undefined && command.y2 !== undefined ) { + + token.o += round( command.x2 * scale ) + ' ' + round( command.y2 * scale ) + ' '; + + } + + } ); + + glyphs[ String.fromCharCode( glyph.unicode ) ] = token; + + } + + } + + return { + glyphs: glyphs, + familyName: font.familyName, + ascender: round( font.ascender * scale ), + descender: round( font.descender * scale ), + underlinePosition: font.tables.post.underlinePosition, + underlineThickness: font.tables.post.underlineThickness, + boundingBox: { + xMin: font.tables.head.xMin, + xMax: font.tables.head.xMax, + yMin: font.tables.head.yMin, + yMax: font.tables.head.yMax + }, + resolution: 1000, + original_font_information: font.tables.name + }; + + } + + function reverseCommands( commands ) { + + var paths = []; + var path; + + commands.forEach( function ( c ) { + + if ( c.type.toLowerCase() === 'm' ) { + + path = [ c ]; + paths.push( path ); + + } else if ( c.type.toLowerCase() !== 'z' ) { + + path.push( c ); + + } + + } ); + + var reversed = []; + + paths.forEach( function ( p ) { + + var result = { + type: 'm', + x: p[ p.length - 1 ].x, + y: p[ p.length - 1 ].y + }; + + reversed.push( result ); + + for ( var i = p.length - 1; i > 0; i -- ) { + + var command = p[ i ]; + var result = { type: command.type }; + + if ( command.x2 !== undefined && command.y2 !== undefined ) { + + result.x1 = command.x2; + result.y1 = command.y2; + result.x2 = command.x1; + result.y2 = command.y1; + + } else if ( command.x1 !== undefined && command.y1 !== undefined ) { + + result.x1 = command.x1; + result.y1 = command.y1; + + } + + result.x = p[ i - 1 ].x; + result.y = p[ i - 1 ].y; + reversed.push( result ); + + } + + } ); + + return reversed; + + } + + if ( typeof opentype === 'undefined' ) { + + console.warn( 'THREE.TTFLoader: The loader requires opentype.js. Make sure it\'s included before using the loader.' ); + return null; + + } + + return convert( opentype.parse( arraybuffer ), this.reversed ); + + } + +}; + +export { TTFLoader }; diff --git a/examples/jsm/loaders/VRMLLoader.js b/examples/jsm/loaders/VRMLLoader.js new file mode 100644 index 00000000000000..9318ad2eca031b --- /dev/null +++ b/examples/jsm/loaders/VRMLLoader.js @@ -0,0 +1,1326 @@ +/** + * @author mrdoob / http://mrdoob.com/ + */ + +import { + AmbientLight, + BackSide, + BoxBufferGeometry, + BufferAttribute, + BufferGeometry, + Color, + CylinderBufferGeometry, + DefaultLoadingManager, + DoubleSide, + FileLoader, + Float32BufferAttribute, + LoaderUtils as THRLoaderUtils, + Mesh, + MeshBasicMaterial, + MeshPhongMaterial, + Object3D, + PointLight, + Scene, + SphereBufferGeometry, + SpotLight, + TextureLoader, + Vector3, + VertexColors, +} from "../../../build/three.module.js"; + +var VRMLLoader = function ( manager ) { + + this.manager = ( manager !== undefined ) ? manager : DefaultLoadingManager; + +}; + +VRMLLoader.prototype = { + + constructor: THREE.VRMLLoader, + + // for IndexedFaceSet support + isRecordingPoints: false, + isRecordingFaces: false, + points: [], + indexes: [], + + // for Background support + isRecordingAngles: false, + isRecordingColors: false, + angles: [], + colors: [], + + recordingFieldname: null, + + crossOrigin: 'anonymous', + + load: function ( url, onLoad, onProgress, onError ) { + + var scope = this; + + var path = ( scope.path === undefined ) ? THRLoaderUtils.extractUrlBase( url ) : scope.path; + + var loader = new FileLoader( this.manager ); + loader.setPath( scope.path ); + loader.load( url, function ( text ) { + + onLoad( scope.parse( text, path ) ); + + }, onProgress, onError ); + + }, + + setPath: function ( value ) { + + this.path = value; + return this; + + }, + + setResourcePath: function ( value ) { + + this.resourcePath = value; + return this; + + }, + + setCrossOrigin: function ( value ) { + + this.crossOrigin = value; + return this; + + }, + + parse: function ( data, path ) { + + var scope = this; + + var textureLoader = new TextureLoader( this.manager ); + textureLoader.setPath( this.resourcePath || path ).setCrossOrigin( this.crossOrigin ); + + function parseV2( lines, scene ) { + + var defines = {}; + var float_pattern = /(\b|\-|\+)([\d\.e]+)/; + var float2_pattern = /([\d\.\+\-e]+)\s+([\d\.\+\-e]+)/g; + var float3_pattern = /([\d\.\+\-e]+)\s+([\d\.\+\-e]+)\s+([\d\.\+\-e]+)/g; + + /** + * Vertically paints the faces interpolating between the + * specified colors at the specified angels. This is used for the Background + * node, but could be applied to other nodes with multiple faces as well. + * + * When used with the Background node, default is directionIsDown is true if + * interpolating the skyColor down from the Zenith. When interpolationg up from + * the Nadir i.e. interpolating the groundColor, the directionIsDown is false. + * + * The first angle is never specified, it is the Zenith (0 rad). Angles are specified + * in radians. The geometry is thought a sphere, but could be anything. The color interpolation + * is linear along the Y axis in any case. + * + * You must specify one more color than you have angles at the beginning of the colors array. + * This is the color of the Zenith (the top of the shape). + * + * @param geometry + * @param radius + * @param angles + * @param colors + * @param boolean topDown Whether to work top down or bottom up. + */ + function paintFaces( geometry, radius, angles, colors, topDown ) { + + var direction = ( topDown === true ) ? 1 : - 1; + + var coord = [], A = {}, B = {}, applyColor = false; + + for ( var k = 0; k < angles.length; k ++ ) { + + // push the vector at which the color changes + + var vec = { + x: direction * ( Math.cos( angles[ k ] ) * radius ), + y: direction * ( Math.sin( angles[ k ] ) * radius ) + }; + + coord.push( vec ); + + } + + var index = geometry.index; + var positionAttribute = geometry.attributes.position; + var colorAttribute = new BufferAttribute( new Float32Array( geometry.attributes.position.count * 3 ), 3 ); + + var position = new Vector3(); + var color = new Color(); + + for ( var i = 0; i < index.count; i ++ ) { + + var vertexIndex = index.getX( i ); + + position.fromBufferAttribute( positionAttribute, vertexIndex ); + + for ( var j = 0; j < colors.length; j ++ ) { + + // linear interpolation between aColor and bColor, calculate proportion + // A is previous point (angle) + + if ( j === 0 ) { + + A.x = 0; + A.y = ( topDown === true ) ? radius : - 1 * radius; + + } else { + + A.x = coord[ j - 1 ].x; + A.y = coord[ j - 1 ].y; + + } + + // B is current point (angle) + + B = coord[ j ]; + + if ( B !== undefined ) { + + // p has to be between the points A and B which we interpolate + + applyColor = ( topDown === true ) ? ( position.y <= A.y && position.y > B.y ) : ( position.y >= A.y && position.y < B.y ); + + if ( applyColor === true ) { + + var aColor = colors[ j ]; + var bColor = colors[ j + 1 ]; + + // below is simple linear interpolation + + var t = Math.abs( position.y - A.y ) / ( A.y - B.y ); + + // to make it faster, you can only calculate this if the y coord changes, the color is the same for points with the same y + + color.copy( aColor ).lerp( bColor, t ); + + colorAttribute.setXYZ( vertexIndex, color.r, color.g, color.b ); + + } else { + + var colorIndex = ( topDown === true ) ? colors.length - 1 : 0; + var c = colors[ colorIndex ]; + colorAttribute.setXYZ( vertexIndex, c.r, c.g, c.b ); + + } + + } + + } + + } + + geometry.addAttribute( 'color', colorAttribute ); + + } + + var index = []; + + function parseProperty( node, line ) { + + var parts = [], part, property = {}, fieldName; + + /** + * Expression for matching relevant information, such as a name or value, but not the separators + * @type {RegExp} + */ + var regex = /[^\s,\[\]]+/g; + + var point; + + while ( null !== ( part = regex.exec( line ) ) ) { + + parts.push( part[ 0 ] ); + + } + + fieldName = parts[ 0 ]; + + + // trigger several recorders + switch ( fieldName ) { + + case 'skyAngle': + case 'groundAngle': + scope.recordingFieldname = fieldName; + scope.isRecordingAngles = true; + scope.angles = []; + break; + + case 'color': + case 'skyColor': + case 'groundColor': + scope.recordingFieldname = fieldName; + scope.isRecordingColors = true; + scope.colors = []; + break; + + case 'point': + case 'vector': + scope.recordingFieldname = fieldName; + scope.isRecordingPoints = true; + scope.points = []; + break; + + case 'colorIndex': + case 'coordIndex': + case 'normalIndex': + case 'texCoordIndex': + scope.recordingFieldname = fieldName; + scope.isRecordingFaces = true; + scope.indexes = []; + break; + + } + + if ( scope.isRecordingFaces ) { + + // the parts hold the indexes as strings + if ( parts.length > 0 ) { + + for ( var ind = 0; ind < parts.length; ind ++ ) { + + // the part should either be positive integer or -1 + if ( ! /(-?\d+)/.test( parts[ ind ] ) ) { + + continue; + + } + + // end of current face + if ( parts[ ind ] === '-1' ) { + + if ( index.length > 0 ) { + + scope.indexes.push( index ); + + } + + // start new one + index = []; + + } else { + + index.push( parseInt( parts[ ind ] ) ); + + } + + } + + } + + // end + if ( /]/.exec( line ) ) { + + if ( index.length > 0 ) { + + scope.indexes.push( index ); + + } + + // start new one + index = []; + + scope.isRecordingFaces = false; + node[ scope.recordingFieldname ] = scope.indexes; + + } + + } else if ( scope.isRecordingPoints ) { + + if ( node.nodeType == 'Coordinate' ) { + + while ( null !== ( parts = float3_pattern.exec( line ) ) ) { + + point = { + x: parseFloat( parts[ 1 ] ), + y: parseFloat( parts[ 2 ] ), + z: parseFloat( parts[ 3 ] ) + }; + + scope.points.push( point ); + + } + + } + + if ( node.nodeType == 'Normal' ) { + + while ( null !== ( parts = float3_pattern.exec( line ) ) ) { + + point = { + x: parseFloat( parts[ 1 ] ), + y: parseFloat( parts[ 2 ] ), + z: parseFloat( parts[ 3 ] ) + }; + + scope.points.push( point ); + + } + + } + + if ( node.nodeType == 'TextureCoordinate' ) { + + while ( null !== ( parts = float2_pattern.exec( line ) ) ) { + + point = { + x: parseFloat( parts[ 1 ] ), + y: parseFloat( parts[ 2 ] ) + }; + + scope.points.push( point ); + + } + + } + + // end + if ( /]/.exec( line ) ) { + + scope.isRecordingPoints = false; + node.points = scope.points; + + } + + } else if ( scope.isRecordingAngles ) { + + // the parts hold the angles as strings + if ( parts.length > 0 ) { + + for ( var ind = 0; ind < parts.length; ind ++ ) { + + // the part should be a float + if ( ! float_pattern.test( parts[ ind ] ) ) { + + continue; + + } + + scope.angles.push( parseFloat( parts[ ind ] ) ); + + } + + } + + // end + if ( /]/.exec( line ) ) { + + scope.isRecordingAngles = false; + node[ scope.recordingFieldname ] = scope.angles; + + } + + } else if ( scope.isRecordingColors ) { + + while ( null !== ( parts = float3_pattern.exec( line ) ) ) { + + var color = { + r: parseFloat( parts[ 1 ] ), + g: parseFloat( parts[ 2 ] ), + b: parseFloat( parts[ 3 ] ) + }; + + scope.colors.push( color ); + + } + + // end + if ( /]/.exec( line ) ) { + + scope.isRecordingColors = false; + node[ scope.recordingFieldname ] = scope.colors; + + } + + } else if ( parts[ parts.length - 1 ] !== 'NULL' && fieldName !== 'children' ) { + + switch ( fieldName ) { + + case 'diffuseColor': + case 'emissiveColor': + case 'specularColor': + case 'color': + + if ( parts.length !== 4 ) { + + console.warn( 'THREE.VRMLLoader: Invalid color format detected for %s.', fieldName ); + break; + + } + + property = { + r: parseFloat( parts[ 1 ] ), + g: parseFloat( parts[ 2 ] ), + b: parseFloat( parts[ 3 ] ) + }; + + break; + + case 'location': + case 'direction': + case 'translation': + case 'scale': + case 'size': + if ( parts.length !== 4 ) { + + console.warn( 'THREE.VRMLLoader: Invalid vector format detected for %s.', fieldName ); + break; + + } + + property = { + x: parseFloat( parts[ 1 ] ), + y: parseFloat( parts[ 2 ] ), + z: parseFloat( parts[ 3 ] ) + }; + + break; + + case 'intensity': + case 'cutOffAngle': + case 'radius': + case 'topRadius': + case 'bottomRadius': + case 'height': + case 'transparency': + case 'shininess': + case 'ambientIntensity': + case 'creaseAngle': + if ( parts.length !== 2 ) { + + console.warn( 'THREE.VRMLLoader: Invalid single float value specification detected for %s.', fieldName ); + break; + + } + + property = parseFloat( parts[ 1 ] ); + + break; + + case 'rotation': + if ( parts.length !== 5 ) { + + console.warn( 'THREE.VRMLLoader: Invalid quaternion format detected for %s.', fieldName ); + break; + + } + + property = { + x: parseFloat( parts[ 1 ] ), + y: parseFloat( parts[ 2 ] ), + z: parseFloat( parts[ 3 ] ), + w: parseFloat( parts[ 4 ] ) + }; + + break; + + case 'on': + case 'ccw': + case 'solid': + case 'colorPerVertex': + case 'convex': + if ( parts.length !== 2 ) { + + console.warn( 'THREE.VRMLLoader: Invalid format detected for %s.', fieldName ); + break; + + } + + property = parts[ 1 ] === 'TRUE' ? true : false; + + break; + + } + + node[ fieldName ] = property; + + } + + return property; + + } + + function getTree( lines ) { + + var tree = { 'string': 'Scene', children: [] }; + var current = tree; + var matches; + var specification; + + for ( var i = 0; i < lines.length; i ++ ) { + + var comment = ''; + + var line = lines[ i ]; + + // omit whitespace only lines + if ( null !== ( /^\s+?$/g.exec( line ) ) ) { + + continue; + + } + + line = line.trim(); + + // skip empty lines + if ( line === '' ) { + + continue; + + } + + if ( /#/.exec( line ) ) { + + var parts = line.split( '#' ); + + // discard everything after the #, it is a comment + line = parts[ 0 ]; + + // well, let's also keep the comment + comment = parts[ 1 ]; + + } + + if ( matches = /([^\s]*){1}(?:\s+)?{/.exec( line ) ) { + + // first subpattern should match the Node name + + var block = { 'nodeType': matches[ 1 ], 'string': line, 'parent': current, 'children': [], 'comment': comment }; + current.children.push( block ); + current = block; + + if ( /}/.exec( line ) ) { + + // example: geometry Box { size 1 1 1 } # all on the same line + specification = /{(.*)}/.exec( line )[ 1 ]; + + // todo: remove once new parsing is complete? + block.children.push( specification ); + + parseProperty( current, specification ); + + current = current.parent; + + } + + } else if ( /}/.exec( line ) ) { + + current = current.parent; + + } else if ( line !== '' ) { + + parseProperty( current, line ); + // todo: remove once new parsing is complete? we still do not parse geometry and appearance the new way + current.children.push( line ); + + } + + } + + return tree; + + } + + function parseNode( data, parent ) { + + var object; + + if ( typeof data === 'string' ) { + + if ( /USE/.exec( data ) ) { + + var defineKey = /USE\s+?([^\s]+)/.exec( data )[ 1 ]; + + if ( undefined == defines[ defineKey ] ) { + + console.warn( 'THREE.VRMLLoader: %s is not defined.', defineKey ); + + } else { + + if ( /appearance/.exec( data ) && defineKey ) { + + parent.material = defines[ defineKey ].clone(); + + } else if ( /geometry/.exec( data ) && defineKey ) { + + parent.geometry = defines[ defineKey ].clone(); + + // the solid property is not cloned with clone(), is only needed for VRML loading, so we need to transfer it + if ( defines[ defineKey ].solid !== undefined && defines[ defineKey ].solid === false ) { + + parent.geometry.solid = false; + parent.material.side = DoubleSide; + + } + + } else if ( defineKey ) { + + object = defines[ defineKey ].clone(); + parent.add( object ); + + } + + } + + } + + return; + + } + + object = parent; + + if ( data.string.indexOf( 'AmbientLight' ) > - 1 && data.nodeType === 'PointLight' ) { + + data.nodeType = 'AmbientLight'; + + } + + var l_visible = data.on !== undefined ? data.on : true; + var l_intensity = data.intensity !== undefined ? data.intensity : 1; + var l_color = new Color(); + + if ( data.color ) { + + l_color.copy( data.color ); + + } + + if ( data.nodeType === 'AmbientLight' ) { + + object = new AmbientLight( l_color, l_intensity ); + object.visible = l_visible; + + parent.add( object ); + + } else if ( data.nodeType === 'PointLight' ) { + + var l_distance = 0; + + if ( data.radius !== undefined && data.radius < 1000 ) { + + l_distance = data.radius; + + } + + object = new PointLight( l_color, l_intensity, l_distance ); + object.visible = l_visible; + + parent.add( object ); + + } else if ( data.nodeType === 'SpotLight' ) { + + var l_intensity = 1; + var l_distance = 0; + var l_angle = Math.PI / 3; + var l_penumbra = 0; + var l_visible = true; + + if ( data.radius !== undefined && data.radius < 1000 ) { + + l_distance = data.radius; + + } + + if ( data.cutOffAngle !== undefined ) { + + l_angle = data.cutOffAngle; + + } + + object = new SpotLight( l_color, l_intensity, l_distance, l_angle, l_penumbra ); + object.visible = l_visible; + + parent.add( object ); + + } else if ( data.nodeType === 'Transform' || data.nodeType === 'Group' ) { + + object = new Object3D(); + + if ( /DEF/.exec( data.string ) ) { + + object.name = /DEF\s+([^\s]+)/.exec( data.string )[ 1 ]; + defines[ object.name ] = object; + + } + + if ( data.translation !== undefined ) { + + var t = data.translation; + + object.position.set( t.x, t.y, t.z ); + + } + + if ( data.rotation !== undefined ) { + + var r = data.rotation; + + object.quaternion.setFromAxisAngle( new THREE.Vector3( r.x, r.y, r.z ), r.w ); + + } + + if ( data.scale !== undefined ) { + + var s = data.scale; + + object.scale.set( s.x, s.y, s.z ); + + } + + parent.add( object ); + + } else if ( data.nodeType === 'Shape' ) { + + object = new THREE.Mesh(); + + if ( /DEF/.exec( data.string ) ) { + + object.name = /DEF\s+([^\s]+)/.exec( data.string )[ 1 ]; + + defines[ object.name ] = object; + + } + + parent.add( object ); + + } else if ( data.nodeType === 'Background' ) { + + var segments = 20; + + // sky (full sphere): + + var radius = 2e4; + + var skyGeometry = new SphereBufferGeometry( radius, segments, segments ); + var skyMaterial = new MeshBasicMaterial( { fog: false, side: THREE.BackSide } ); + + if ( data.skyColor.length > 1 ) { + + paintFaces( skyGeometry, radius, data.skyAngle, data.skyColor, true ); + + skyMaterial.vertexColors = VertexColors; + + } else { + + var color = data.skyColor[ 0 ]; + skyMaterial.color.setRGB( color.r, color.b, color.g ); + + } + + scene.add( new Mesh( skyGeometry, skyMaterial ) ); + + // ground (half sphere): + + if ( data.groundColor !== undefined ) { + + radius = 1.2e4; + + var groundGeometry = new SphereBufferGeometry( radius, segments, segments, 0, 2 * Math.PI, 0.5 * Math.PI, 1.5 * Math.PI ); + var groundMaterial = new MeshBasicMaterial( { fog: false, side: BackSide, vertexColors: VertexColors } ); + + paintFaces( groundGeometry, radius, data.groundAngle, data.groundColor, false ); + + scene.add( new THREE.Mesh( groundGeometry, groundMaterial ) ); + + } + + } else if ( /geometry/.exec( data.string ) ) { + + if ( data.nodeType === 'Box' ) { + + var s = data.size; + + parent.geometry = new BoxBufferGeometry( s.x, s.y, s.z ); + + } else if ( data.nodeType === 'Cylinder' ) { + + parent.geometry = new CylinderBufferGeometry( data.radius, data.radius, data.height ); + + } else if ( data.nodeType === 'Cone' ) { + + parent.geometry = new CylinderBufferGeometry( data.topRadius, data.bottomRadius, data.height ); + + } else if ( data.nodeType === 'Sphere' ) { + + parent.geometry = new SphereBufferGeometry( data.radius ); + + } else if ( data.nodeType === 'IndexedFaceSet' ) { + + var geometry = new BufferGeometry(); + + var positions = []; + var colors = []; + var normals = []; + var uvs = []; + + var position, color, normal, uv; + + var i, il, j, jl; + + for ( i = 0, il = data.children.length; i < il; i ++ ) { + + var child = data.children[ i ]; + + // uvs + + if ( child.nodeType === 'TextureCoordinate' ) { + + if ( child.points ) { + + for ( j = 0, jl = child.points.length; j < jl; j ++ ) { + + uv = child.points[ j ]; + uvs.push( uv.x, uv.y ); + + } + + } + + } + + // normals + + if ( child.nodeType === 'Normal' ) { + + if ( child.points ) { + + for ( j = 0, jl = child.points.length; j < jl; j ++ ) { + + normal = child.points[ j ]; + normals.push( normal.x, normal.y, normal.z ); + + } + + } + + } + + // colors + + if ( child.nodeType === 'Color' ) { + + if ( child.color ) { + + for ( j = 0, jl = child.color.length; j < jl; j ++ ) { + + color = child.color[ j ]; + colors.push( color.r, color.g, color.b ); + + } + + } + + } + + // positions + + if ( child.nodeType === 'Coordinate' ) { + + if ( child.points ) { + + for ( j = 0, jl = child.points.length; j < jl; j ++ ) { + + position = child.points[ j ]; + positions.push( position.x, position.y, position.z ); + + } + + } + + if ( child.string.indexOf( 'DEF' ) > - 1 ) { + + var name = /DEF\s+([^\s]+)/.exec( child.string )[ 1 ]; + + defines[ name ] = positions.slice( 0 ); + + } + + if ( child.string.indexOf( 'USE' ) > - 1 ) { + + var defineKey = /USE\s+([^\s]+)/.exec( child.string )[ 1 ]; + + positions = defines[ defineKey ]; + + } + + } + + } + + // some shapes only have vertices for use in other shapes + + if ( data.coordIndex ) { + + function triangulateIndexArray( indexArray, ccw ) { + + if ( ccw === undefined ) { + + // ccw is true by default + ccw = true; + + } + + var triangulatedIndexArray = []; + var skip = 0; + + for ( i = 0, il = indexArray.length; i < il; i ++ ) { + + var indexedFace = indexArray[ i ]; + + // VRML support multipoint indexed face sets (more then 3 vertices). You must calculate the composing triangles here + + skip = 0; + + while ( indexedFace.length >= 3 && skip < ( indexedFace.length - 2 ) ) { + + var i1 = indexedFace[ 0 ]; + var i2 = indexedFace[ skip + ( ccw ? 1 : 2 ) ]; + var i3 = indexedFace[ skip + ( ccw ? 2 : 1 ) ]; + + triangulatedIndexArray.push( i1, i2, i3 ); + + skip ++; + + } + + } + + return triangulatedIndexArray; + + } + + var positionIndexes = data.coordIndex ? triangulateIndexArray( data.coordIndex, data.ccw ) : []; + var normalIndexes = data.normalIndex ? triangulateIndexArray( data.normalIndex, data.ccw ) : positionIndexes; + var colorIndexes = data.colorIndex ? triangulateIndexArray( data.colorIndex, data.ccw ) : positionIndexes; + var uvIndexes = data.texCoordIndex ? triangulateIndexArray( data.texCoordIndex, data.ccw ) : positionIndexes; + + var newIndexes = []; + var newPositions = []; + var newNormals = []; + var newColors = []; + var newUvs = []; + + // if any other index array does not match the coordinate indexes, split any points that differ + + var pointMap = Object.create( null ); + + for ( i = 0; i < positionIndexes.length; i ++ ) { + + var pointAttributes = []; + + var positionIndex = positionIndexes[ i ]; + var normalIndex = normalIndexes[ i ]; + var colorIndex = colorIndexes[ i ]; + var uvIndex = uvIndexes[ i ]; + + var base = 10; // which base to use to represent each value + + pointAttributes.push( positionIndex.toString( base ) ); + + if ( normalIndex !== undefined ) { + + pointAttributes.push( normalIndex.toString( base ) ); + + } + + if ( colorIndex !== undefined ) { + + pointAttributes.push( colorIndex.toString( base ) ); + + } + + if ( uvIndex !== undefined ) { + + pointAttributes.push( uvIndex.toString( base ) ); + + } + + var pointId = pointAttributes.join( ',' ); + var newIndex = pointMap[ pointId ]; + + if ( newIndex === undefined ) { + + newIndex = newPositions.length / 3; + pointMap[ pointId ] = newIndex; + + newPositions.push( + positions[ positionIndex * 3 ], + positions[ positionIndex * 3 + 1 ], + positions[ positionIndex * 3 + 2 ] + ); + + if ( normalIndex !== undefined && normals.length > 0 ) { + + newNormals.push( + normals[ normalIndex * 3 ], + normals[ normalIndex * 3 + 1 ], + normals[ normalIndex * 3 + 2 ] + ); + + } + + if ( colorIndex !== undefined && colors.length > 0 ) { + + newColors.push( + colors[ colorIndex * 3 ], + colors[ colorIndex * 3 + 1 ], + colors[ colorIndex * 3 + 2 ] + ); + + } + + if ( uvIndex !== undefined && uvs.length > 0 ) { + + newUvs.push( + uvs[ uvIndex * 2 ], + uvs[ uvIndex * 2 + 1 ] + ); + + } + + } + + newIndexes.push( newIndex ); + + } + + positions = newPositions; + normals = newNormals; + colors = newColors; + uvs = newUvs; + + geometry.setIndex( newIndexes ); + + } else { + + // do not add dummy mesh to the scene + + parent.parent.remove( parent ); + + } + + if ( false === data.solid ) { + + parent.material.side = DoubleSide; + + } + + // we need to store it on the geometry for use with defines + geometry.solid = data.solid; + + geometry.addAttribute( 'position', new Float32BufferAttribute( positions, 3 ) ); + + if ( colors.length > 0 ) { + + geometry.addAttribute( 'color', new Float32BufferAttribute( colors, 3 ) ); + + } + + if ( uvs.length > 0 ) { + + geometry.addAttribute( 'uv', new Float32BufferAttribute( uvs, 2 ) ); + + } + + if ( normals.length > 0 ) { + + geometry.addAttribute( 'normal', new Float32BufferAttribute( normals, 3 ) ); + + } else { + + // convert geometry to non-indexed to get sharp normals + geometry = geometry.toNonIndexed(); + geometry.computeVertexNormals(); + + } + + geometry.computeBoundingSphere(); + + // see if it's a define + if ( /DEF/.exec( data.string ) ) { + + geometry.name = /DEF ([^\s]+)/.exec( data.string )[ 1 ]; + defines[ geometry.name ] = geometry; + + } + + parent.geometry = geometry; + + } + + return; + + } else if ( /appearance/.exec( data.string ) ) { + + for ( var i = 0; i < data.children.length; i ++ ) { + + var child = data.children[ i ]; + + if ( child.nodeType === 'Material' ) { + + var material = new MeshPhongMaterial(); + + if ( child.diffuseColor !== undefined ) { + + var d = child.diffuseColor; + + material.color.setRGB( d.r, d.g, d.b ); + + } + + if ( child.emissiveColor !== undefined ) { + + var e = child.emissiveColor; + + material.emissive.setRGB( e.r, e.g, e.b ); + + } + + if ( child.specularColor !== undefined ) { + + var s = child.specularColor; + + material.specular.setRGB( s.r, s.g, s.b ); + + } + + if ( child.transparency !== undefined ) { + + var t = child.transparency; + + // transparency is opposite of opacity + material.opacity = Math.abs( 1 - t ); + + material.transparent = true; + + } + + if ( /DEF/.exec( data.string ) ) { + + material.name = /DEF ([^\s]+)/.exec( data.string )[ 1 ]; + + defines[ material.name ] = material; + + } + + parent.material = material; + + } + + if ( child.nodeType === 'ImageTexture' ) { + + var textureName = /"([^"]+)"/.exec( child.children[ 0 ] ); + + if ( textureName ) { + + parent.material.name = textureName[ 1 ]; + + parent.material.map = textureLoader.load( textureName[ 1 ] ); + + } + + } + + } + + return; + + } + + for ( var i = 0, l = data.children.length; i < l; i ++ ) { + + parseNode( data.children[ i ], object ); + + } + + } + + parseNode( getTree( lines ), scene ); + + } + + var scene = new Scene(); + + var lines = data.split( '\n' ); + + // some lines do not have breaks + + for ( var i = lines.length - 1; i > - 1; i -- ) { + + var line = lines[ i ]; + + // The # symbol indicates that all subsequent text, until the end of the line is a comment, + // and should be ignored. (see http://gun.teipir.gr/VRML-amgem/spec/part1/grammar.html) + line = line.replace( /(#.*)/, '' ); + + // split lines with {..{ or {..[ - some have both + if ( /{.*[{\[]/.test( line ) ) { + + var parts = line.split( '{' ).join( '{\n' ).split( '\n' ); + parts.unshift( 1 ); + parts.unshift( i ); + lines.splice.apply( lines, parts ); + + } else if ( /\].*}/.test( line ) ) { + + // split lines with ]..} + var parts = line.split( ']' ).join( ']\n' ).split( '\n' ); + parts.unshift( 1 ); + parts.unshift( i ); + lines.splice.apply( lines, parts ); + + } + + if ( /}.*}/.test( line ) ) { + + // split lines with }..} + var parts = line.split( '}' ).join( '}\n' ).split( '\n' ); + parts.unshift( 1 ); + parts.unshift( i ); + lines.splice.apply( lines, parts ); + + } + + if ( /^\b[^\s]+\b$/.test( line.trim() ) ) { + + // prevent lines with single words like "coord" or "geometry", see #12209 + lines[ i + 1 ] = line + ' ' + lines[ i + 1 ].trim(); + lines.splice( i, 1 ); + + } else if ( ( line.indexOf( 'coord' ) > - 1 ) && ( line.indexOf( '[' ) < 0 ) && ( line.indexOf( '{' ) < 0 ) ) { + + // force the parser to create Coordinate node for empty coords + // coord USE something -> coord USE something Coordinate {} + + lines[ i ] += ' Coordinate {}'; + + } + + } + + var header = lines.shift(); + + if ( /V1.0/.exec( header ) ) { + + console.warn( 'THREE.VRMLLoader: V1.0 not supported yet.' ); + + } else if ( /V2.0/.exec( header ) ) { + + parseV2( lines, scene ); + + } + + return scene; + + } + +}; + +export { VRMLLoader }; diff --git a/examples/jsm/loaders/VRMLoader.js b/examples/jsm/loaders/VRMLoader.js new file mode 100644 index 00000000000000..6b2762a3cdb734 --- /dev/null +++ b/examples/jsm/loaders/VRMLoader.js @@ -0,0 +1,86 @@ +/** + * @author Takahiro / https://github.com/takahirox + */ + +// VRM Specification: https://dwango.github.io/vrm/vrm_spec/ +// +// VRM is based on glTF 2.0 and VRM extension is defined +// in top-level json.extensions.VRM + +import { GLTFLoader } from "./GLTFLoader"; +import { DefaultLoadingManager } from "../../../build/three.module.js"; + +function VRMLoader( manager ) { + + if ( GLTFLoader === undefined ) { + + throw new Error( 'THREE.VRMLoader: Import GLTFLoader.' ); + + } + + this.manager = ( manager !== undefined ) ? manager : DefaultLoadingManager; + this.gltfLoader = new GLTFLoader( this.manager ); + +} + +VRMLoader.prototype = { + + constructor: VRMLoader, + + crossOrigin: 'anonymous', + + load: function ( url, onLoad, onProgress, onError ) { + + var scope = this; + + this.gltfLoader.load( url, function ( gltf ) { + + scope.parse( gltf, onLoad ); + + }, onProgress, onError ); + + }, + + setCrossOrigin: function ( value ) { + + this.glTFLoader.setCrossOrigin( value ); + return this; + + }, + + setPath: function ( value ) { + + this.glTFLoader.setPath( value ); + return this; + + }, + + setResourcePath: function ( value ) { + + this.glTFLoader.setResourcePath( value ); + return this; + + }, + + setDRACOLoader: function ( dracoLoader ) { + + this.glTFLoader.setDRACOLoader( dracoLoader ); + return this; + + }, + + parse: function ( gltf, onLoad ) { + + var gltfParser = gltf.parser; + var gltfExtensions = gltf.userData.gltfExtensions || {}; + var vrmExtension = gltfExtensions.VRM || {}; + + // handle VRM Extension here + + onLoad( gltf ); + + } + +}; + +export { VRMLoader }; diff --git a/examples/jsm/loaders/VTKLoader.js b/examples/jsm/loaders/VTKLoader.js new file mode 100644 index 00000000000000..9f68678b6ebef8 --- /dev/null +++ b/examples/jsm/loaders/VTKLoader.js @@ -0,0 +1,1174 @@ +/** + * @author mrdoob / http://mrdoob.com/ + * @author Alex Pletzer + * + * Updated on 22.03.2017 + * VTK header is now parsed and used to extract all the compressed data + * @author Andrii Iudin https://github.com/andreyyudin + * @author Paul Kibet Korir https://github.com/polarise + * @author Sriram Somasundharam https://github.com/raamssundar + */ + +import { + BufferAttribute, + BufferGeometry, + DefaultLoadingManager, + EventDispatcher, + FileLoader, + Float32BufferAttribute, + LoaderUtils, +} from "../../../build/three.module.js"; + +var VTKLoader = function ( manager ) { + + this.manager = ( manager !== undefined ) ? manager : DefaultLoadingManager; + +}; + +Object.assign( VTKLoader.prototype, EventDispatcher.prototype, { + + load: function ( url, onLoad, onProgress, onError ) { + + var scope = this; + + var loader = new FileLoader( scope.manager ); + loader.setPath( scope.path ); + loader.setResponseType( 'arraybuffer' ); + loader.load( url, function ( text ) { + + onLoad( scope.parse( text ) ); + + }, onProgress, onError ); + + }, + + setPath: function ( value ) { + + this.path = value; + return this; + + }, + + parse: function ( data ) { + + function parseASCII( data ) { + + // connectivity of the triangles + var indices = []; + + // triangles vertices + var positions = []; + + // red, green, blue colors in the range 0 to 1 + var colors = []; + + // normal vector, one per vertex + var normals = []; + + var result; + + // pattern for reading vertices, 3 floats or integers + var pat3Floats = /(\-?\d+\.?[\d\-\+e]*)\s+(\-?\d+\.?[\d\-\+e]*)\s+(\-?\d+\.?[\d\-\+e]*)/g; + + // pattern for connectivity, an integer followed by any number of ints + // the first integer is the number of polygon nodes + var patConnectivity = /^(\d+)\s+([\s\d]*)/; + + // indicates start of vertex data section + var patPOINTS = /^POINTS /; + + // indicates start of polygon connectivity section + var patPOLYGONS = /^POLYGONS /; + + // indicates start of triangle strips section + var patTRIANGLE_STRIPS = /^TRIANGLE_STRIPS /; + + // POINT_DATA number_of_values + var patPOINT_DATA = /^POINT_DATA[ ]+(\d+)/; + + // CELL_DATA number_of_polys + var patCELL_DATA = /^CELL_DATA[ ]+(\d+)/; + + // Start of color section + var patCOLOR_SCALARS = /^COLOR_SCALARS[ ]+(\w+)[ ]+3/; + + // NORMALS Normals float + var patNORMALS = /^NORMALS[ ]+(\w+)[ ]+(\w+)/; + + var inPointsSection = false; + var inPolygonsSection = false; + var inTriangleStripSection = false; + var inPointDataSection = false; + var inCellDataSection = false; + var inColorSection = false; + var inNormalsSection = false; + + var lines = data.split( '\n' ); + + for ( var i in lines ) { + + var line = lines[ i ]; + + if ( inPointsSection ) { + + // get the vertices + while ( ( result = pat3Floats.exec( line ) ) !== null ) { + + var x = parseFloat( result[ 1 ] ); + var y = parseFloat( result[ 2 ] ); + var z = parseFloat( result[ 3 ] ); + positions.push( x, y, z ); + + } + + } else if ( inPolygonsSection ) { + + if ( ( result = patConnectivity.exec( line ) ) !== null ) { + + // numVertices i0 i1 i2 ... + var numVertices = parseInt( result[ 1 ] ); + var inds = result[ 2 ].split( /\s+/ ); + + if ( numVertices >= 3 ) { + + var i0 = parseInt( inds[ 0 ] ); + var i1, i2; + var k = 1; + // split the polygon in numVertices - 2 triangles + for ( var j = 0; j < numVertices - 2; ++ j ) { + + i1 = parseInt( inds[ k ] ); + i2 = parseInt( inds[ k + 1 ] ); + indices.push( i0, i1, i2 ); + k ++; + + } + + } + + } + + } else if ( inTriangleStripSection ) { + + if ( ( result = patConnectivity.exec( line ) ) !== null ) { + + // numVertices i0 i1 i2 ... + var numVertices = parseInt( result[ 1 ] ); + var inds = result[ 2 ].split( /\s+/ ); + + if ( numVertices >= 3 ) { + + var i0, i1, i2; + // split the polygon in numVertices - 2 triangles + for ( var j = 0; j < numVertices - 2; j ++ ) { + + if ( j % 2 === 1 ) { + + i0 = parseInt( inds[ j ] ); + i1 = parseInt( inds[ j + 2 ] ); + i2 = parseInt( inds[ j + 1 ] ); + indices.push( i0, i1, i2 ); + + } else { + + i0 = parseInt( inds[ j ] ); + i1 = parseInt( inds[ j + 1 ] ); + i2 = parseInt( inds[ j + 2 ] ); + indices.push( i0, i1, i2 ); + + } + + } + + } + + } + + } else if ( inPointDataSection || inCellDataSection ) { + + if ( inColorSection ) { + + // Get the colors + + while ( ( result = pat3Floats.exec( line ) ) !== null ) { + + var r = parseFloat( result[ 1 ] ); + var g = parseFloat( result[ 2 ] ); + var b = parseFloat( result[ 3 ] ); + colors.push( r, g, b ); + + } + + } else if ( inNormalsSection ) { + + // Get the normal vectors + + while ( ( result = pat3Floats.exec( line ) ) !== null ) { + + var nx = parseFloat( result[ 1 ] ); + var ny = parseFloat( result[ 2 ] ); + var nz = parseFloat( result[ 3 ] ); + normals.push( nx, ny, nz ); + + } + + } + + } + + if ( patPOLYGONS.exec( line ) !== null ) { + + inPolygonsSection = true; + inPointsSection = false; + inTriangleStripSection = false; + + } else if ( patPOINTS.exec( line ) !== null ) { + + inPolygonsSection = false; + inPointsSection = true; + inTriangleStripSection = false; + + } else if ( patTRIANGLE_STRIPS.exec( line ) !== null ) { + + inPolygonsSection = false; + inPointsSection = false; + inTriangleStripSection = true; + + } else if ( patPOINT_DATA.exec( line ) !== null ) { + + inPointDataSection = true; + inPointsSection = false; + inPolygonsSection = false; + inTriangleStripSection = false; + + } else if ( patCELL_DATA.exec( line ) !== null ) { + + inCellDataSection = true; + inPointsSection = false; + inPolygonsSection = false; + inTriangleStripSection = false; + + } else if ( patCOLOR_SCALARS.exec( line ) !== null ) { + + inColorSection = true; + inNormalsSection = false; + inPointsSection = false; + inPolygonsSection = false; + inTriangleStripSection = false; + + } else if ( patNORMALS.exec( line ) !== null ) { + + inNormalsSection = true; + inColorSection = false; + inPointsSection = false; + inPolygonsSection = false; + inTriangleStripSection = false; + + } + + } + + var geometry = new BufferGeometry(); + geometry.setIndex( indices ); + geometry.addAttribute( 'position', new Float32BufferAttribute( positions, 3 ) ); + + if ( normals.length === positions.length ) { + + geometry.addAttribute( 'normal', new Float32BufferAttribute( normals, 3 ) ); + + } + + if ( colors.length !== indices.length ) { + + // stagger + + if ( colors.length === positions.length ) { + + geometry.addAttribute( 'color', new Float32BufferAttribute( colors, 3 ) ); + + } + + } else { + + // cell + + geometry = geometry.toNonIndexed(); + var numTriangles = geometry.attributes.position.count / 3; + + if ( colors.length === ( numTriangles * 3 ) ) { + + var newColors = []; + + for ( var i = 0; i < numTriangles; i ++ ) { + + var r = colors[ 3 * i + 0 ]; + var g = colors[ 3 * i + 1 ]; + var b = colors[ 3 * i + 2 ]; + + newColors.push( r, g, b ); + newColors.push( r, g, b ); + newColors.push( r, g, b ); + + } + + geometry.addAttribute( 'color', new Float32BufferAttribute( newColors, 3 ) ); + + } + + } + + return geometry; + + } + + function parseBinary( data ) { + + var count, pointIndex, i, numberOfPoints, s; + var buffer = new Uint8Array( data ); + var dataView = new DataView( data ); + + // Points and normals, by default, are empty + var points = []; + var normals = []; + var indices = []; + + // Going to make a big array of strings + var vtk = []; + var index = 0; + + function findString( buffer, start ) { + + var index = start; + var c = buffer[ index ]; + var s = []; + while ( c !== 10 ) { + + s.push( String.fromCharCode( c ) ); + index ++; + c = buffer[ index ]; + + } + + return { start: start, + end: index, + next: index + 1, + parsedString: s.join( '' ) }; + + } + + var state, line; + + while ( true ) { + + // Get a string + state = findString( buffer, index ); + line = state.parsedString; + + if ( line.indexOf( 'POINTS' ) === 0 ) { + + vtk.push( line ); + // Add the points + numberOfPoints = parseInt( line.split( ' ' )[ 1 ], 10 ); + + // Each point is 3 4-byte floats + count = numberOfPoints * 4 * 3; + + points = new Float32Array( numberOfPoints * 3 ); + + pointIndex = state.next; + for ( i = 0; i < numberOfPoints; i ++ ) { + + points[ 3 * i ] = dataView.getFloat32( pointIndex, false ); + points[ 3 * i + 1 ] = dataView.getFloat32( pointIndex + 4, false ); + points[ 3 * i + 2 ] = dataView.getFloat32( pointIndex + 8, false ); + pointIndex = pointIndex + 12; + + } + // increment our next pointer + state.next = state.next + count + 1; + + } else if ( line.indexOf( 'TRIANGLE_STRIPS' ) === 0 ) { + + var numberOfStrips = parseInt( line.split( ' ' )[ 1 ], 10 ); + var size = parseInt( line.split( ' ' )[ 2 ], 10 ); + // 4 byte integers + count = size * 4; + + indices = new Uint32Array( 3 * size - 9 * numberOfStrips ); + var indicesIndex = 0; + + pointIndex = state.next; + for ( i = 0; i < numberOfStrips; i ++ ) { + + // For each strip, read the first value, then record that many more points + var indexCount = dataView.getInt32( pointIndex, false ); + var strip = []; + pointIndex += 4; + for ( s = 0; s < indexCount; s ++ ) { + + strip.push( dataView.getInt32( pointIndex, false ) ); + pointIndex += 4; + + } + + // retrieves the n-2 triangles from the triangle strip + for ( var j = 0; j < indexCount - 2; j ++ ) { + + if ( j % 2 ) { + + indices[ indicesIndex ++ ] = strip[ j ]; + indices[ indicesIndex ++ ] = strip[ j + 2 ]; + indices[ indicesIndex ++ ] = strip[ j + 1 ]; + + } else { + + + indices[ indicesIndex ++ ] = strip[ j ]; + indices[ indicesIndex ++ ] = strip[ j + 1 ]; + indices[ indicesIndex ++ ] = strip[ j + 2 ]; + + } + + } + + } + // increment our next pointer + state.next = state.next + count + 1; + + } else if ( line.indexOf( 'POLYGONS' ) === 0 ) { + + var numberOfStrips = parseInt( line.split( ' ' )[ 1 ], 10 ); + var size = parseInt( line.split( ' ' )[ 2 ], 10 ); + // 4 byte integers + count = size * 4; + + indices = new Uint32Array( 3 * size - 9 * numberOfStrips ); + var indicesIndex = 0; + + pointIndex = state.next; + for ( i = 0; i < numberOfStrips; i ++ ) { + + // For each strip, read the first value, then record that many more points + var indexCount = dataView.getInt32( pointIndex, false ); + var strip = []; + pointIndex += 4; + for ( s = 0; s < indexCount; s ++ ) { + + strip.push( dataView.getInt32( pointIndex, false ) ); + pointIndex += 4; + + } + + // divide the polygon in n-2 triangle + for ( var j = 1; j < indexCount - 1; j ++ ) { + + indices[ indicesIndex ++ ] = strip[ 0 ]; + indices[ indicesIndex ++ ] = strip[ j ]; + indices[ indicesIndex ++ ] = strip[ j + 1 ]; + + } + + } + // increment our next pointer + state.next = state.next + count + 1; + + } else if ( line.indexOf( 'POINT_DATA' ) === 0 ) { + + numberOfPoints = parseInt( line.split( ' ' )[ 1 ], 10 ); + + // Grab the next line + state = findString( buffer, state.next ); + + // Now grab the binary data + count = numberOfPoints * 4 * 3; + + normals = new Float32Array( numberOfPoints * 3 ); + pointIndex = state.next; + for ( i = 0; i < numberOfPoints; i ++ ) { + + normals[ 3 * i ] = dataView.getFloat32( pointIndex, false ); + normals[ 3 * i + 1 ] = dataView.getFloat32( pointIndex + 4, false ); + normals[ 3 * i + 2 ] = dataView.getFloat32( pointIndex + 8, false ); + pointIndex += 12; + + } + + // Increment past our data + state.next = state.next + count; + + } + + // Increment index + index = state.next; + + if ( index >= buffer.byteLength ) { + + break; + + } + + } + + var geometry = new BufferGeometry(); + geometry.setIndex( new BufferAttribute( indices, 1 ) ); + geometry.addAttribute( 'position', new BufferAttribute( points, 3 ) ); + + if ( normals.length === points.length ) { + + geometry.addAttribute( 'normal', new BufferAttribute( normals, 3 ) ); + + } + + return geometry; + + } + + function Float32Concat( first, second ) { + + var firstLength = first.length, result = new Float32Array( firstLength + second.length ); + + result.set( first ); + result.set( second, firstLength ); + + return result; + + } + + function Int32Concat( first, second ) { + + var firstLength = first.length, result = new Int32Array( firstLength + second.length ); + + result.set( first ); + result.set( second, firstLength ); + + return result; + + } + + function parseXML( stringFile ) { + + // Changes XML to JSON, based on https://davidwalsh.name/convert-xml-json + + function xmlToJson( xml ) { + + // Create the return object + var obj = {}; + + if ( xml.nodeType === 1 ) { // element + + // do attributes + + if ( xml.attributes ) { + + if ( xml.attributes.length > 0 ) { + + obj[ 'attributes' ] = {}; + + for ( var j = 0; j < xml.attributes.length; j ++ ) { + + var attribute = xml.attributes.item( j ); + obj[ 'attributes' ][ attribute.nodeName ] = attribute.nodeValue.trim(); + + } + + } + + } + + } else if ( xml.nodeType === 3 ) { // text + + obj = xml.nodeValue.trim(); + + } + + // do children + if ( xml.hasChildNodes() ) { + + for ( var i = 0; i < xml.childNodes.length; i ++ ) { + + var item = xml.childNodes.item( i ); + var nodeName = item.nodeName; + + if ( typeof obj[ nodeName ] === 'undefined' ) { + + var tmp = xmlToJson( item ); + + if ( tmp !== '' ) obj[ nodeName ] = tmp; + + } else { + + if ( typeof obj[ nodeName ].push === 'undefined' ) { + + var old = obj[ nodeName ]; + obj[ nodeName ] = [ old ]; + + } + + var tmp = xmlToJson( item ); + + if ( tmp !== '' ) obj[ nodeName ].push( tmp ); + + } + + } + + } + + return obj; + + } + + // Taken from Base64-js + function Base64toByteArray( b64 ) { + + var Arr = typeof Uint8Array !== 'undefined' ? Uint8Array : Array; + var i; + var lookup = []; + var revLookup = []; + var code = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'; + var len = code.length; + + for ( i = 0; i < len; i ++ ) { + + lookup[ i ] = code[ i ]; + + } + + for ( i = 0; i < len; ++ i ) { + + revLookup[ code.charCodeAt( i ) ] = i; + + } + + revLookup[ '-'.charCodeAt( 0 ) ] = 62; + revLookup[ '_'.charCodeAt( 0 ) ] = 63; + + var j, l, tmp, placeHolders, arr; + var len = b64.length; + + if ( len % 4 > 0 ) { + + throw new Error( 'Invalid string. Length must be a multiple of 4' ); + + } + + placeHolders = b64[ len - 2 ] === '=' ? 2 : b64[ len - 1 ] === '=' ? 1 : 0; + arr = new Arr( len * 3 / 4 - placeHolders ); + l = placeHolders > 0 ? len - 4 : len; + + var L = 0; + + for ( i = 0, j = 0; i < l; i += 4, j += 3 ) { + + tmp = ( revLookup[ b64.charCodeAt( i ) ] << 18 ) | ( revLookup[ b64.charCodeAt( i + 1 ) ] << 12 ) | ( revLookup[ b64.charCodeAt( i + 2 ) ] << 6 ) | revLookup[ b64.charCodeAt( i + 3 ) ]; + arr[ L ++ ] = ( tmp & 0xFF0000 ) >> 16; + arr[ L ++ ] = ( tmp & 0xFF00 ) >> 8; + arr[ L ++ ] = tmp & 0xFF; + + } + + if ( placeHolders === 2 ) { + + tmp = ( revLookup[ b64.charCodeAt( i ) ] << 2 ) | ( revLookup[ b64.charCodeAt( i + 1 ) ] >> 4 ); + arr[ L ++ ] = tmp & 0xFF; + + } else if ( placeHolders === 1 ) { + + tmp = ( revLookup[ b64.charCodeAt( i ) ] << 10 ) | ( revLookup[ b64.charCodeAt( i + 1 ) ] << 4 ) | ( revLookup[ b64.charCodeAt( i + 2 ) ] >> 2 ); + arr[ L ++ ] = ( tmp >> 8 ) & 0xFF; + arr[ L ++ ] = tmp & 0xFF; + + } + + return arr; + + } + + function parseDataArray( ele, compressed ) { + + var numBytes = 0; + + if ( json.attributes.header_type === 'UInt64' ) { + + numBytes = 8; + + } else if ( json.attributes.header_type === 'UInt32' ) { + + numBytes = 4; + + } + + + // Check the format + if ( ele.attributes.format === 'binary' && compressed ) { + + var rawData, content, byteData, blocks, cSizeStart, headerSize, padding, dataOffsets, currentOffset; + + if ( ele.attributes.type === 'Float32' ) { + + var txt = new Float32Array( ); + + } else if ( ele.attributes.type === 'Int64' ) { + + var txt = new Int32Array( ); + + } + + // VTP data with the header has the following structure: + // [#blocks][#u-size][#p-size][#c-size-1][#c-size-2]...[#c-size-#blocks][DATA] + // + // Each token is an integer value whose type is specified by "header_type" at the top of the file (UInt32 if no type specified). The token meanings are: + // [#blocks] = Number of blocks + // [#u-size] = Block size before compression + // [#p-size] = Size of last partial block (zero if it not needed) + // [#c-size-i] = Size in bytes of block i after compression + // + // The [DATA] portion stores contiguously every block appended together. The offset from the beginning of the data section to the beginning of a block is + // computed by summing the compressed block sizes from preceding blocks according to the header. + + rawData = ele[ '#text' ]; + + byteData = Base64toByteArray( rawData ); + + blocks = byteData[ 0 ]; + for ( var i = 1; i < numBytes - 1; i ++ ) { + + blocks = blocks | ( byteData[ i ] << ( i * numBytes ) ); + + } + + headerSize = ( blocks + 3 ) * numBytes; + padding = ( ( headerSize % 3 ) > 0 ) ? 3 - ( headerSize % 3 ) : 0; + headerSize = headerSize + padding; + + dataOffsets = []; + currentOffset = headerSize; + dataOffsets.push( currentOffset ); + + // Get the blocks sizes after the compression. + // There are three blocks before c-size-i, so we skip 3*numBytes + cSizeStart = 3 * numBytes; + + for ( var i = 0; i < blocks; i ++ ) { + + var currentBlockSize = byteData[ i * numBytes + cSizeStart ]; + + for ( var j = 1; j < numBytes - 1; j ++ ) { + + // Each data point consists of 8 bytes regardless of the header type + currentBlockSize = currentBlockSize | ( byteData[ i * numBytes + cSizeStart + j ] << ( j * 8 ) ); + + } + + currentOffset = currentOffset + currentBlockSize; + dataOffsets.push( currentOffset ); + + } + + for ( var i = 0; i < dataOffsets.length - 1; i ++ ) { + + var inflate = new Zlib.Inflate( byteData.slice( dataOffsets[ i ], dataOffsets[ i + 1 ] ), { resize: true, verify: true } ); // eslint-disable-line no-undef + content = inflate.decompress(); + content = content.buffer; + + if ( ele.attributes.type === 'Float32' ) { + + content = new Float32Array( content ); + txt = Float32Concat( txt, content ); + + } else if ( ele.attributes.type === 'Int64' ) { + + content = new Int32Array( content ); + txt = Int32Concat( txt, content ); + + } + + } + + delete ele[ '#text' ]; + + if ( ele.attributes.type === 'Int64' ) { + + if ( ele.attributes.format === 'binary' ) { + + txt = txt.filter( function ( el, idx ) { + + if ( idx % 2 !== 1 ) return true; + + } ); + + } + + } + + } else { + + if ( ele.attributes.format === 'binary' && ! compressed ) { + + var content = Base64toByteArray( ele[ '#text' ] ); + + // VTP data for the uncompressed case has the following structure: + // [#bytes][DATA] + // where "[#bytes]" is an integer value specifying the number of bytes in the block of data following it. + content = content.slice( numBytes ).buffer; + + } else { + + if ( ele[ '#text' ] ) { + + var content = ele[ '#text' ].split( /\s+/ ).filter( function ( el ) { + + if ( el !== '' ) return el; + + } ); + + } else { + + var content = new Int32Array( 0 ).buffer; + + } + + } + + delete ele[ '#text' ]; + + // Get the content and optimize it + if ( ele.attributes.type === 'Float32' ) { + + var txt = new Float32Array( content ); + + } else if ( ele.attributes.type === 'Int32' ) { + + var txt = new Int32Array( content ); + + } else if ( ele.attributes.type === 'Int64' ) { + + var txt = new Int32Array( content ); + + if ( ele.attributes.format === 'binary' ) { + + txt = txt.filter( function ( el, idx ) { + + if ( idx % 2 !== 1 ) return true; + + } ); + + } + + } + + } // endif ( ele.attributes.format === 'binary' && compressed ) + + return txt; + + } + + // Main part + // Get Dom + var dom = null; + + if ( window.DOMParser ) { + + try { + + dom = ( new DOMParser() ).parseFromString( stringFile, 'text/xml' ); + + } catch ( e ) { + + dom = null; + + } + + } else if ( window.ActiveXObject ) { + + try { + + dom = new ActiveXObject( 'Microsoft.XMLDOM' ); // eslint-disable-line no-undef + dom.async = false; + + if ( ! dom.loadXML( /* xml */ ) ) { + + throw new Error( dom.parseError.reason + dom.parseError.srcText ); + + } + + } catch ( e ) { + + dom = null; + + } + + } else { + + throw new Error( 'Cannot parse xml string!' ); + + } + + // Get the doc + var doc = dom.documentElement; + // Convert to json + var json = xmlToJson( doc ); + var points = []; + var normals = []; + var indices = []; + + if ( json.PolyData ) { + + var piece = json.PolyData.Piece; + var compressed = json.attributes.hasOwnProperty( 'compressor' ); + + // Can be optimized + // Loop through the sections + var sections = [ 'PointData', 'Points', 'Strips', 'Polys' ];// +['CellData', 'Verts', 'Lines']; + var sectionIndex = 0, numberOfSections = sections.length; + + while ( sectionIndex < numberOfSections ) { + + var section = piece[ sections[ sectionIndex ] ]; + + // If it has a DataArray in it + + if ( section && section.DataArray ) { + + // Depending on the number of DataArrays + + if ( Object.prototype.toString.call( section.DataArray ) === '[object Array]' ) { + + var arr = section.DataArray; + + } else { + + var arr = [ section.DataArray ]; + + } + + var dataArrayIndex = 0, numberOfDataArrays = arr.length; + + while ( dataArrayIndex < numberOfDataArrays ) { + + // Parse the DataArray + if ( ( '#text' in arr[ dataArrayIndex ] ) && ( arr[ dataArrayIndex ][ '#text' ].length > 0 ) ) { + + arr[ dataArrayIndex ].text = parseDataArray( arr[ dataArrayIndex ], compressed ); + + } + + dataArrayIndex ++; + + } + + switch ( sections[ sectionIndex ] ) { + + // if iti is point data + case 'PointData': + + var numberOfPoints = parseInt( piece.attributes.NumberOfPoints ); + var normalsName = section.attributes.Normals; + + if ( numberOfPoints > 0 ) { + + for ( var i = 0, len = arr.length; i < len; i ++ ) { + + if ( normalsName === arr[ i ].attributes.Name ) { + + var components = arr[ i ].attributes.NumberOfComponents; + normals = new Float32Array( numberOfPoints * components ); + normals.set( arr[ i ].text, 0 ); + + } + + } + + } + + break; + + // if it is points + case 'Points': + + var numberOfPoints = parseInt( piece.attributes.NumberOfPoints ); + + if ( numberOfPoints > 0 ) { + + var components = section.DataArray.attributes.NumberOfComponents; + points = new Float32Array( numberOfPoints * components ); + points.set( section.DataArray.text, 0 ); + + } + + break; + + // if it is strips + case 'Strips': + + var numberOfStrips = parseInt( piece.attributes.NumberOfStrips ); + + if ( numberOfStrips > 0 ) { + + var connectivity = new Int32Array( section.DataArray[ 0 ].text.length ); + var offset = new Int32Array( section.DataArray[ 1 ].text.length ); + connectivity.set( section.DataArray[ 0 ].text, 0 ); + offset.set( section.DataArray[ 1 ].text, 0 ); + + var size = numberOfStrips + connectivity.length; + indices = new Uint32Array( 3 * size - 9 * numberOfStrips ); + + var indicesIndex = 0; + + for ( var i = 0, len = numberOfStrips; i < len; i ++ ) { + + var strip = []; + + for ( var s = 0, len1 = offset[ i ], len0 = 0; s < len1 - len0; s ++ ) { + + strip.push( connectivity[ s ] ); + + if ( i > 0 ) len0 = offset[ i - 1 ]; + + } + + for ( var j = 0, len1 = offset[ i ], len0 = 0; j < len1 - len0 - 2; j ++ ) { + + if ( j % 2 ) { + + indices[ indicesIndex ++ ] = strip[ j ]; + indices[ indicesIndex ++ ] = strip[ j + 2 ]; + indices[ indicesIndex ++ ] = strip[ j + 1 ]; + + } else { + + indices[ indicesIndex ++ ] = strip[ j ]; + indices[ indicesIndex ++ ] = strip[ j + 1 ]; + indices[ indicesIndex ++ ] = strip[ j + 2 ]; + + } + + if ( i > 0 ) len0 = offset[ i - 1 ]; + + } + + } + + } + + break; + + // if it is polys + case 'Polys': + + var numberOfPolys = parseInt( piece.attributes.NumberOfPolys ); + + if ( numberOfPolys > 0 ) { + + var connectivity = new Int32Array( section.DataArray[ 0 ].text.length ); + var offset = new Int32Array( section.DataArray[ 1 ].text.length ); + connectivity.set( section.DataArray[ 0 ].text, 0 ); + offset.set( section.DataArray[ 1 ].text, 0 ); + + var size = numberOfPolys + connectivity.length; + indices = new Uint32Array( 3 * size - 9 * numberOfPolys ); + var indicesIndex = 0, connectivityIndex = 0; + var i = 0, len = numberOfPolys, len0 = 0; + + while ( i < len ) { + + var poly = []; + var s = 0, len1 = offset[ i ]; + + while ( s < len1 - len0 ) { + + poly.push( connectivity[ connectivityIndex ++ ] ); + s ++; + + } + + var j = 1; + + while ( j < len1 - len0 - 1 ) { + + indices[ indicesIndex ++ ] = poly[ 0 ]; + indices[ indicesIndex ++ ] = poly[ j ]; + indices[ indicesIndex ++ ] = poly[ j + 1 ]; + j ++; + + } + + i ++; + len0 = offset[ i - 1 ]; + + } + + } + + break; + + default: + break; + + } + + } + + sectionIndex ++; + + } + + var geometry = new BufferGeometry(); + geometry.setIndex( new BufferAttribute( indices, 1 ) ); + geometry.addAttribute( 'position', new BufferAttribute( points, 3 ) ); + + if ( normals.length === points.length ) { + + geometry.addAttribute( 'normal', new BufferAttribute( normals, 3 ) ); + + } + + return geometry; + + } else { + + // TODO for vtu,vti,and other xml formats + + } + + } + + function getStringFile( data ) { + + var stringFile = ''; + var charArray = new Uint8Array( data ); + var i = 0; + var len = charArray.length; + + while ( len -- ) { + + stringFile += String.fromCharCode( charArray[ i ++ ] ); + + } + + return stringFile; + + } + + // get the 5 first lines of the files to check if there is the key word binary + var meta = LoaderUtils.decodeText( new Uint8Array( data, 0, 250 ) ).split( '\n' ); + + if ( meta[ 0 ].indexOf( 'xml' ) !== - 1 ) { + + return parseXML( getStringFile( data ) ); + + } else if ( meta[ 2 ].includes( 'ASCII' ) ) { + + return parseASCII( getStringFile( data ) ); + + } else { + + return parseBinary( data ); + + } + + } + +} ); + +export { VTKLoader }; diff --git a/examples/jsm/loaders/XLoader.js b/examples/jsm/loaders/XLoader.js new file mode 100644 index 00000000000000..44451655de4585 --- /dev/null +++ b/examples/jsm/loaders/XLoader.js @@ -0,0 +1,1698 @@ +/** + * @author adrs2002 / https://github.com/adrs2002 + */ + + +import { + AnimationClip, + AnimationMixer, + Bone, + BufferGeometry, + DefaultLoadingManager, + Float32BufferAttribute, + FrontSide, + LoaderUtils, + Matrix4, + Mesh, + MeshPhongMaterial, + Quaternion, + Skeleton, + SkinnedMesh, + TextureLoader, + Uint16BufferAttribute, + Vector2, + Vector3, +} from "../../../build/three.module.js"; + +var classCallCheck = function ( instance, Constructor ) { + + if ( ! ( instance instanceof Constructor ) ) { + + throw new TypeError( "Cannot call a class as a function" ); + + } + +}; + +var createClass = function () { + + function defineProperties( target, props ) { + + for ( var i = 0; i < props.length; i ++ ) { + + var descriptor = props[ i ]; + descriptor.enumerable = descriptor.enumerable || false; + descriptor.configurable = true; + if ( "value" in descriptor ) descriptor.writable = true; + Object.defineProperty( target, descriptor.key, descriptor ); + + } + + } + + return function ( Constructor, protoProps, staticProps ) { + + if ( protoProps ) defineProperties( Constructor.prototype, protoProps ); + if ( staticProps ) defineProperties( Constructor, staticProps ); + return Constructor; + + }; + +}(); + +var XboneInf = function XboneInf() { + + classCallCheck( this, XboneInf ); + + this.boneName = ""; + this.BoneIndex = 0; + this.Indeces = []; + this.Weights = []; + this.initMatrix = null; + this.OffsetMatrix = null; + +}; + +var XAnimationInfo = function XAnimationInfo() { + + classCallCheck( this, XAnimationInfo ); + + this.animeName = ""; + this.boneName = ""; + this.targetBone = null; + this.keyType = 4; + this.frameStartLv = 0; + this.keyFrames = []; + this.InverseMx = null; + +}; + +var XAnimationObj = function () { + + function XAnimationObj( _flags ) { + + classCallCheck( this, XAnimationObj ); + + this.fps = 30; + this.name = 'xanimation'; + this.length = 0; + this.hierarchy = []; + this.putFlags = _flags; + if ( this.putFlags.putPos === undefined ) { + + this.putFlags.putPos = true; + + } + if ( this.putFlags.putRot === undefined ) { + + this.putFlags.putRot = true; + + } + if ( this.putFlags.putScl === undefined ) { + + this.putFlags.putScl = true; + + } + + } + + createClass( XAnimationObj, [ { + key: "make", + value: function make( XAnimationInfoArray ) { + + for ( var i = 0; i < XAnimationInfoArray.length; i ++ ) { + + this.hierarchy.push( this.makeBonekeys( XAnimationInfoArray[ i ] ) ); + + } + this.length = this.hierarchy[ 0 ].keys[ this.hierarchy[ 0 ].keys.length - 1 ].time; + + } + }, { + key: "clone", + value: function clone() { + + return Object.assign( {}, this ); + + } + }, { + key: "makeBonekeys", + value: function makeBonekeys( XAnimationInfo ) { + + var refObj = {}; + refObj.name = XAnimationInfo.boneName; + refObj.parent = ""; + refObj.keys = this.keyFrameRefactor( XAnimationInfo ); + refObj.copy = function () { + + return Object.assign( {}, this ); + + }; + return refObj; + + } + }, { + key: "keyFrameRefactor", + value: function keyFrameRefactor( XAnimationInfo ) { + + var keys = []; + for ( var i = 0; i < XAnimationInfo.keyFrames.length; i ++ ) { + + var keyframe = {}; + keyframe.time = XAnimationInfo.keyFrames[ i ].time * this.fps; + if ( XAnimationInfo.keyFrames[ i ].pos && this.putFlags.putPos ) { + + keyframe.pos = XAnimationInfo.keyFrames[ i ].pos; + + } + if ( XAnimationInfo.keyFrames[ i ].rot && this.putFlags.putRot ) { + + keyframe.rot = XAnimationInfo.keyFrames[ i ].rot; + + } + if ( XAnimationInfo.keyFrames[ i ].scl && this.putFlags.putScl ) { + + keyframe.scl = XAnimationInfo.keyFrames[ i ].scl; + + } + if ( XAnimationInfo.keyFrames[ i ].matrix ) { + + keyframe.matrix = XAnimationInfo.keyFrames[ i ].matrix; + if ( this.putFlags.putPos ) { + + keyframe.pos = new Vector3().setFromMatrixPosition( keyframe.matrix ); + + } + if ( this.putFlags.putRot ) { + + keyframe.rot = new Quaternion().setFromRotationMatrix( keyframe.matrix ); + + } + if ( this.putFlags.putScl ) { + + keyframe.scl = new Vector3().setFromMatrixScale( keyframe.matrix ); + + } + + } + keys.push( keyframe ); + + } + return keys; + + } + } ] ); + return XAnimationObj; + +}(); + +var XKeyFrameInfo = function XKeyFrameInfo() { + + classCallCheck( this, XKeyFrameInfo ); + + this.index = 0; + this.Frame = 0; + this.time = 0.0; + this.matrix = null; + +}; + +var XLoader = function () { + + function XLoader( manager ) { + + classCallCheck( this, XLoader ); + + this.debug = false; + this.manager = manager !== undefined ? manager : new DefaultLoadingManager(); + this.texloader = new TextureLoader( this.manager ); + this.url = ""; + this._putMatLength = 0; + this._nowMat = null; + this._nowFrameName = ""; + this.frameHierarchie = []; + this.Hierarchies = {}; + this.HieStack = []; + this._currentObject = {}; + this._currentFrame = {}; + this._data = null; + this.onLoad = null; + this.IsUvYReverse = true; + this.Meshes = []; + this.animations = []; + this.animTicksPerSecond = 30; + this._currentGeo = null; + this._currentAnime = null; + this._currentAnimeFrames = null; + + } + + createClass( XLoader, [ { + key: 'crossOrigin', + value: 'anonymous' + }, { + key: '_setArgOption', + value: function _setArgOption( _arg ) { + + var _start = arguments.length > 1 && arguments[ 1 ] !== undefined ? arguments[ 1 ] : 0; + + if ( ! _arg ) { + + return; + + } + for ( var i = _start; i < _arg.length; i ++ ) { + + switch ( i ) { + + case 0: + this.url = _arg[ i ]; + break; + case 1: + this.options = _arg[ i ]; + break; + + } + + } + if ( this.options === undefined ) { + + this.options = {}; + + } + + } + }, { + key: 'load', + value: function load( _arg, onLoad, onProgress, onError ) { + + var _this = this; + + this._setArgOption( _arg ); + var loader = new FileLoader( this.manager ); + loader.setPath( this.path ); + loader.setResponseType( 'arraybuffer' ); + loader.load( this.url, function ( response ) { + + _this._parse( response, onLoad ); + + }, onProgress, onError ); + + } + }, { + key: 'setCrossOrigin', + value: function setCrossOrigin( value ) { + + this.crossOrigin = value; + return this; + + } + }, { + key: 'setPath', + value: function setPath( value ) { + + this.path = value; + return this; + + } + }, { + key: 'setResourcePath', + value: function setResourcePath( value ) { + + this.resourcePath = value; + return this; + + } + }, { + key: 'fromResponsedData', + value: function fromResponsedData( _data, _arg, onLoad ) { + + this._setArgOption( _arg ); + this._parse( _data, onLoad ); + + } + }, { + key: '_readLine', + value: function _readLine( line ) { + + var readed = 0; + while ( true ) { + + var find = - 1; + find = line.indexOf( '//', readed ); + if ( find === - 1 ) { + + find = line.indexOf( '#', readed ); + + } + if ( find > - 1 && find < 2 ) { + + var foundNewLine = - 1; + foundNewLine = line.indexOf( "\r\n", readed ); + if ( foundNewLine > 0 ) { + + readed = foundNewLine + 2; + + } else { + + foundNewLine = line.indexOf( "\r", readed ); + if ( foundNewLine > 0 ) { + + readed = foundNewLine + 1; + + } else { + + readed = line.indexOf( "\n", readed ) + 1; + + } + + } + + } else { + + break; + + } + + } + return line.substr( readed ); + + } + }, { + key: '_readLine', + value: function _readLine( line ) { + + var readed = 0; + while ( true ) { + + var find = - 1; + find = line.indexOf( '//', readed ); + if ( find === - 1 ) { + + find = line.indexOf( '#', readed ); + + } + if ( find > - 1 && find < 2 ) { + + var foundNewLine = - 1; + foundNewLine = line.indexOf( "\r\n", readed ); + if ( foundNewLine > 0 ) { + + readed = foundNewLine + 2; + + } else { + + foundNewLine = line.indexOf( "\r", readed ); + if ( foundNewLine > 0 ) { + + readed = foundNewLine + 1; + + } else { + + readed = line.indexOf( "\n", readed ) + 1; + + } + + } + + } else { + + break; + + } + + } + return line.substr( readed ); + + } + }, { + key: '_isBinary', + value: function _isBinary( binData ) { + + var reader = new DataView( binData ); + var face_size = 32 / 8 * 3 + 32 / 8 * 3 * 3 + 16 / 8; + var n_faces = reader.getUint32( 80, true ); + var expect = 80 + 32 / 8 + n_faces * face_size; + if ( expect === reader.byteLength ) { + + return true; + + } + var fileLength = reader.byteLength; + for ( var index = 0; index < fileLength; index ++ ) { + + if ( reader.getUint8( index, false ) > 127 ) { + + return true; + + } + + } + return false; + + } + }, { + key: 'ensureBinary', + value: function ensureBinary( buf ) { + + if ( typeof buf === "string" ) { + + var array_buffer = new Uint8Array( buf.length ); + for ( var i = 0; i < buf.length; i ++ ) { + + array_buffer[ i ] = buf.charCodeAt( i ) & 0xff; + + } + return array_buffer.buffer || array_buffer; + + } else { + + return buf; + + } + + } + }, { + key: 'ensureString', + value: function ensureString( buf ) { + + if ( typeof buf !== "string" ) { + + return LoaderUtils.decodeText( new Uint8Array( buf ) ); + + } else { + + return buf; + + } + + } + }, { + key: '_parse', + value: function _parse( data, onLoad ) { + + var binData = this.ensureBinary( data ); + this._data = this.ensureString( data ); + this.onLoad = onLoad; + return this._isBinary( binData ) ? this._parseBinary( binData ) : this._parseASCII(); + + } + }, { + key: '_parseBinary', + value: function _parseBinary( data ) { + + return this._parseASCII( LoaderUtils.decodeText( new Uint8Array( data ) ) ); + + } + }, { + key: '_parseASCII', + value: function _parseASCII() { + + var path; + + if ( this.resourcePath !== undefined ) { + + path = this.resourcePath; + + } else if ( this.path !== undefined ) { + + path = this.path; + + } else { + + path = LoaderUtils.extractUrlBase( this.url ); + + } + + this.texloader.setPath( path ).setCrossOrigin( this.crossOrigin ); + + var endRead = 16; + this.Hierarchies.children = []; + this._hierarchieParse( this.Hierarchies, endRead ); + this._changeRoot(); + this._currentObject = this.Hierarchies.children.shift(); + this.mainloop(); + + } + }, { + key: '_hierarchieParse', + value: function _hierarchieParse( _parent, _end ) { + + var endRead = _end; + while ( true ) { + + var find1 = this._data.indexOf( '{', endRead ) + 1; + var findEnd = this._data.indexOf( '}', endRead ); + var findNext = this._data.indexOf( '{', find1 ) + 1; + if ( find1 > 0 && findEnd > find1 ) { + + var _currentObject = {}; + _currentObject.children = []; + var nameData = this._readLine( this._data.substr( endRead, find1 - endRead - 1 ) ).trim(); + var word = nameData.split( / /g ); + if ( word.length > 0 ) { + + _currentObject.type = word[ 0 ]; + if ( word.length >= 2 ) { + + _currentObject.name = word[ 1 ]; + + } else { + + _currentObject.name = word[ 0 ] + this.Hierarchies.children.length; + + } + + } else { + + _currentObject.name = nameData; + _currentObject.type = ""; + + } + if ( _currentObject.type === "Animation" ) { + + _currentObject.data = this._data.substr( findNext, findEnd - findNext ).trim(); + var refs = this._hierarchieParse( _currentObject, findEnd + 1 ); + endRead = refs.end; + _currentObject.children = refs.parent.children; + + } else { + + var DataEnder = this._data.lastIndexOf( ';', findNext > 0 ? Math.min( findNext, findEnd ) : findEnd ); + _currentObject.data = this._data.substr( find1, DataEnder - find1 ).trim(); + if ( findNext <= 0 || findEnd < findNext ) { + + endRead = findEnd + 1; + + } else { + + var nextStart = Math.max( DataEnder + 1, find1 ); + var _refs = this._hierarchieParse( _currentObject, nextStart ); + endRead = _refs.end; + _currentObject.children = _refs.parent.children; + + } + + } + _currentObject.parent = _parent; + if ( _currentObject.type != "template" ) { + + _parent.children.push( _currentObject ); + + } + + } else { + + endRead = find1 === - 1 ? this._data.length : findEnd + 1; + break; + + } + + } + return { + parent: _parent, + end: endRead + }; + + } + }, { + key: 'mainloop', + value: function mainloop() { + + var _this2 = this; + + this.mainProc(); + if ( this._currentObject.parent || this._currentObject.children.length > 0 || ! this._currentObject.worked ) { + + setTimeout( function () { + + _this2.mainloop(); + + }, 1 ); + + } else { + + setTimeout( function () { + + _this2.onLoad( { + models: _this2.Meshes, + animations: _this2.animations + } ); + + }, 1 ); + + } + + } + }, { + key: 'mainProc', + value: function mainProc() { + + var breakFlag = false; + while ( true ) { + + if ( ! this._currentObject.worked ) { + + switch ( this._currentObject.type ) { + + case "template": + break; + case "AnimTicksPerSecond": + this.animTicksPerSecond = parseInt( this._currentObject.data ); + break; + case "Frame": + this._setFrame(); + break; + case "FrameTransformMatrix": + this._setFrameTransformMatrix(); + break; + case "Mesh": + this._changeRoot(); + this._currentGeo = {}; + this._currentGeo.name = this._currentObject.name.trim(); + this._currentGeo.parentName = this._getParentName( this._currentObject ).trim(); + this._currentGeo.VertexSetedBoneCount = []; + this._currentGeo.GeometryData = { + vertices: [], + normals: [], + uvs: [], + skinIndices: [], + skinWeights: [], + indices: [], + materialIndices: [] + }; + this._currentGeo.Materials = []; + this._currentGeo.normalVectors = []; + this._currentGeo.BoneInfs = []; + this._currentGeo.baseFrame = this._currentFrame; + this._makeBoneFrom_CurrentFrame(); + this._readVertexDatas(); + breakFlag = true; + break; + case "MeshNormals": + this._readVertexDatas(); + break; + case "MeshTextureCoords": + this._setMeshTextureCoords(); + break; + case "VertexDuplicationIndices": + break; + case "MeshMaterialList": + this._setMeshMaterialList(); + break; + case "Material": + this._setMaterial(); + break; + case "SkinWeights": + this._setSkinWeights(); + break; + case "AnimationSet": + this._changeRoot(); + this._currentAnime = {}; + this._currentAnime.name = this._currentObject.name.trim(); + this._currentAnime.AnimeFrames = []; + break; + case "Animation": + if ( this._currentAnimeFrames ) { + + this._currentAnime.AnimeFrames.push( this._currentAnimeFrames ); + + } + this._currentAnimeFrames = new XAnimationInfo(); + this._currentAnimeFrames.boneName = this._currentObject.data.trim(); + break; + case "AnimationKey": + this._readAnimationKey(); + breakFlag = true; + break; + + } + this._currentObject.worked = true; + + } + if ( this._currentObject.children.length > 0 ) { + + this._currentObject = this._currentObject.children.shift(); + if ( this.debug ) { + + console.log( 'processing ' + this._currentObject.name ); + + } + if ( breakFlag ) break; + + } else { + + if ( this._currentObject.worked ) { + + if ( this._currentObject.parent && ! this._currentObject.parent.parent ) { + + this._changeRoot(); + + } + + } + if ( this._currentObject.parent ) { + + this._currentObject = this._currentObject.parent; + + } else { + + breakFlag = true; + + } + if ( breakFlag ) break; + + } + + } + return; + + } + }, { + key: '_changeRoot', + value: function _changeRoot() { + + if ( this._currentGeo != null && this._currentGeo.name ) { + + this._makeOutputGeometry(); + + } + this._currentGeo = {}; + if ( this._currentAnime != null && this._currentAnime.name ) { + + if ( this._currentAnimeFrames ) { + + this._currentAnime.AnimeFrames.push( this._currentAnimeFrames ); + this._currentAnimeFrames = null; + + } + this._makeOutputAnimation(); + + } + this._currentAnime = {}; + + } + }, { + key: '_getParentName', + value: function _getParentName( _obj ) { + + if ( _obj.parent ) { + + if ( _obj.parent.name ) { + + return _obj.parent.name; + + } else { + + return this._getParentName( _obj.parent ); + + } + + } else { + + return ""; + + } + + } + }, { + key: '_setFrame', + value: function _setFrame() { + + this._nowFrameName = this._currentObject.name.trim(); + this._currentFrame = {}; + this._currentFrame.name = this._nowFrameName; + this._currentFrame.children = []; + if ( this._currentObject.parent && this._currentObject.parent.name ) { + + this._currentFrame.parentName = this._currentObject.parent.name; + + } + this.frameHierarchie.push( this._nowFrameName ); + this.HieStack[ this._nowFrameName ] = this._currentFrame; + + } + }, { + key: '_setFrameTransformMatrix', + value: function _setFrameTransformMatrix() { + + this._currentFrame.FrameTransformMatrix = new Matrix4(); + var data = this._currentObject.data.split( "," ); + this._ParseMatrixData( this._currentFrame.FrameTransformMatrix, data ); + this._makeBoneFrom_CurrentFrame(); + + } + }, { + key: '_makeBoneFrom_CurrentFrame', + value: function _makeBoneFrom_CurrentFrame() { + + if ( ! this._currentFrame.FrameTransformMatrix ) { + + return; + + } + var b = new Bone(); + b.name = this._currentFrame.name; + b.applyMatrix( this._currentFrame.FrameTransformMatrix ); + b.matrixWorld = b.matrix; + b.FrameTransformMatrix = this._currentFrame.FrameTransformMatrix; + this._currentFrame.putBone = b; + if ( this._currentFrame.parentName ) { + + for ( var frame in this.HieStack ) { + + if ( this.HieStack[ frame ].name === this._currentFrame.parentName ) { + + this.HieStack[ frame ].putBone.add( this._currentFrame.putBone ); + + } + + } + + } + + } + }, { + key: '_readVertexDatas', + value: function _readVertexDatas() { + + var endRead = 0; + var mode = 0; + var mode_local = 0; + var maxLength = 0; + while ( true ) { + + var changeMode = false; + if ( mode_local === 0 ) { + + var refO = this._readInt1( endRead ); + endRead = refO.endRead; + mode_local = 1; + maxLength = this._currentObject.data.indexOf( ';;', endRead ) + 1; + if ( maxLength <= 0 ) { + + maxLength = this._currentObject.data.length; + + } + + } else { + + var find = 0; + switch ( mode ) { + + case 0: + find = this._currentObject.data.indexOf( ',', endRead ) + 1; + break; + case 1: + find = this._currentObject.data.indexOf( ';,', endRead ) + 1; + break; + + } + if ( find === 0 || find > maxLength ) { + + find = maxLength; + mode_local = 0; + changeMode = true; + + } + switch ( this._currentObject.type ) { + + case "Mesh": + switch ( mode ) { + + case 0: + this._readVertex1( this._currentObject.data.substr( endRead, find - endRead ) ); + break; + case 1: + this._readFace1( this._currentObject.data.substr( endRead, find - endRead ) ); + break; + + } + break; + case "MeshNormals": + switch ( mode ) { + + case 0: + this._readNormalVector1( this._currentObject.data.substr( endRead, find - endRead ) ); + break; + + } + break; + + } + endRead = find + 1; + if ( changeMode ) { + + mode ++; + + } + + } + if ( endRead >= this._currentObject.data.length ) { + + break; + + } + + } + + } + }, { + key: '_readInt1', + value: function _readInt1( start ) { + + var find = this._currentObject.data.indexOf( ';', start ); + return { + refI: parseInt( this._currentObject.data.substr( start, find - start ) ), + endRead: find + 1 + }; + + } + }, { + key: '_readVertex1', + value: function _readVertex1( line ) { + + var data = this._readLine( line.trim() ).substr( 0, line.length - 2 ).split( ";" ); + this._currentGeo.GeometryData.vertices.push( parseFloat( data[ 0 ] ), parseFloat( data[ 1 ] ), parseFloat( data[ 2 ] ) ); + this._currentGeo.GeometryData.skinIndices.push( 0, 0, 0, 0 ); + this._currentGeo.GeometryData.skinWeights.push( 1, 0, 0, 0 ); + this._currentGeo.VertexSetedBoneCount.push( 0 ); + + } + }, { + key: '_readFace1', + value: function _readFace1( line ) { + + var data = this._readLine( line.trim() ).substr( 2, line.length - 4 ).split( "," ); + this._currentGeo.GeometryData.indices.push( parseInt( data[ 0 ], 10 ), parseInt( data[ 1 ], 10 ), parseInt( data[ 2 ], 10 ) ); + + } + }, { + key: '_readNormalVector1', + value: function _readNormalVector1( line ) { + + var data = this._readLine( line.trim() ).substr( 0, line.length - 2 ).split( ";" ); + this._currentGeo.GeometryData.normals.push( parseFloat( data[ 0 ] ), parseFloat( data[ 1 ] ), parseFloat( data[ 2 ] ) ); + + } + }, { + key: '_buildGeometry', + value: function _buildGeometry() { + + var bufferGeometry = new BufferGeometry(); + var position = []; + var normals = []; + var uvs = []; + var skinIndices = []; + var skinWeights = []; + + // + + var data = this._currentGeo.GeometryData; + + for ( var i = 0, l = data.indices.length; i < l; i ++ ) { + + var stride2 = data.indices[ i ] * 2; + var stride3 = data.indices[ i ] * 3; + var stride4 = data.indices[ i ] * 4; + + position.push( data.vertices[ stride3 ], data.vertices[ stride3 + 1 ], data.vertices[ stride3 + 2 ] ); + normals.push( data.normals[ stride3 ], data.normals[ stride3 + 1 ], data.normals[ stride3 + 2 ] ); + skinIndices.push( data.skinIndices[ stride4 ], data.skinIndices[ stride4 + 1 ], data.skinIndices[ stride4 + 2 ], data.skinIndices[ stride4 + 3 ] ); + skinWeights.push( data.skinWeights[ stride4 ], data.skinWeights[ stride4 + 1 ], data.skinWeights[ stride4 + 2 ], data.skinWeights[ stride4 + 3 ] ); + uvs.push( data.uvs[ stride2 ], data.uvs[ stride2 + 1 ] ); + + } + + // + + bufferGeometry.addAttribute( 'position', new Float32BufferAttribute( position, 3 ) ); + bufferGeometry.addAttribute( 'normal', new Float32BufferAttribute( normals, 3 ) ); + bufferGeometry.addAttribute( 'uv', new Float32BufferAttribute( uvs, 2 ) ); + bufferGeometry.addAttribute( 'skinIndex', new Uint16BufferAttribute( skinIndices, 4 ) ); + bufferGeometry.addAttribute( 'skinWeight', new Float32BufferAttribute( skinWeights, 4 ) ); + + this._computeGroups( bufferGeometry, data.materialIndices ); + + return bufferGeometry; + + } + }, { + key: '_computeGroups', + value: function _computeGroups( bufferGeometry, materialIndices ) { + + var group; + var groups = []; + var materialIndex = undefined; + + for ( var i = 0; i < materialIndices.length; i ++ ) { + + var currentMaterialIndex = materialIndices[ i ]; + + if ( currentMaterialIndex !== materialIndex ) { + + materialIndex = currentMaterialIndex; + + if ( group !== undefined ) { + + group.count = ( i * 3 ) - group.start; + groups.push( group ); + + } + + group = { + start: i * 3, + materialIndex: materialIndex + }; + + } + + } + + if ( group !== undefined ) { + + group.count = ( i * 3 ) - group.start; + groups.push( group ); + + } + + bufferGeometry.groups = groups; + + } + }, { + key: '_setMeshTextureCoords', + value: function _setMeshTextureCoords() { + + var endRead = 0; + var mode = 0; + var mode_local = 0; + while ( true ) { + + switch ( mode ) { + + case 0: + if ( mode_local === 0 ) { + + var refO = this._readInt1( 0 ); + endRead = refO.endRead; + mode_local = 1; + + } else { + + var find = this._currentObject.data.indexOf( ',', endRead ) + 1; + if ( find === 0 ) { + + find = this._currentObject.data.length; + mode = 2; + mode_local = 0; + + } + var line = this._currentObject.data.substr( endRead, find - endRead ); + var data = this._readLine( line.trim() ).split( ";" ); + if ( this.IsUvYReverse ) { + + this._currentGeo.GeometryData.uvs.push( parseFloat( data[ 0 ] ), 1 - parseFloat( data[ 1 ] ) ); + + } else { + + this._currentGeo.GeometryData.uvs.push( parseFloat( data[ 0 ] ), parseFloat( data[ 1 ] ) ); + + } + endRead = find + 1; + + } + break; + + } + if ( endRead >= this._currentObject.data.length ) { + + break; + + } + + } + + } + }, { + key: '_setMeshMaterialList', + value: function _setMeshMaterialList() { + + var endRead = 0; + var mode = 0; + var mode_local = 0; + while ( true ) { + + if ( mode_local < 2 ) { + + var refO = this._readInt1( endRead ); + endRead = refO.endRead; + mode_local ++; + + } else { + + var find = this._currentObject.data.indexOf( ';', endRead ); + if ( find === - 1 ) { + + find = this._currentObject.data.length; + mode = 3; + mode_local = 0; + + } + var line = this._currentObject.data.substr( endRead, find - endRead ); + var data = this._readLine( line.trim() ).split( "," ); + for ( var i = 0; i < data.length; i ++ ) { + + this._currentGeo.GeometryData.materialIndices[ i ] = parseInt( data[ i ] ); + + } + endRead = this._currentObject.data.length; + + } + if ( endRead >= this._currentObject.data.length || mode >= 3 ) { + + break; + + } + + } + + } + }, { + key: '_setMaterial', + value: function _setMaterial() { + + var _nowMat = new MeshPhongMaterial( { + color: Math.random() * 0xffffff + } ); + _nowMat.side = FrontSide; + _nowMat.name = this._currentObject.name; + var endRead = 0; + var find = this._currentObject.data.indexOf( ';;', endRead ); + var line = this._currentObject.data.substr( endRead, find - endRead ); + var data = this._readLine( line.trim() ).split( ";" ); + _nowMat.color.r = parseFloat( data[ 0 ] ); + _nowMat.color.g = parseFloat( data[ 1 ] ); + _nowMat.color.b = parseFloat( data[ 2 ] ); + endRead = find + 2; + find = this._currentObject.data.indexOf( ';', endRead ); + line = this._currentObject.data.substr( endRead, find - endRead ); + _nowMat.shininess = parseFloat( this._readLine( line ) ); + endRead = find + 1; + find = this._currentObject.data.indexOf( ';;', endRead ); + line = this._currentObject.data.substr( endRead, find - endRead ); + var data2 = this._readLine( line.trim() ).split( ";" ); + _nowMat.specular.r = parseFloat( data2[ 0 ] ); + _nowMat.specular.g = parseFloat( data2[ 1 ] ); + _nowMat.specular.b = parseFloat( data2[ 2 ] ); + endRead = find + 2; + find = this._currentObject.data.indexOf( ';;', endRead ); + if ( find === - 1 ) { + + find = this._currentObject.data.length; + + } + line = this._currentObject.data.substr( endRead, find - endRead ); + var data3 = this._readLine( line.trim() ).split( ";" ); + _nowMat.emissive.r = parseFloat( data3[ 0 ] ); + _nowMat.emissive.g = parseFloat( data3[ 1 ] ); + _nowMat.emissive.b = parseFloat( data3[ 2 ] ); + var localObject = null; + while ( true ) { + + if ( this._currentObject.children.length > 0 ) { + + localObject = this._currentObject.children.shift(); + if ( this.debug ) { + + console.log( 'processing ' + localObject.name ); + + } + var fileName = localObject.data.substr( 1, localObject.data.length - 2 ); + switch ( localObject.type ) { + + case "TextureFilename": + _nowMat.map = this.texloader.load( fileName ); + break; + case "BumpMapFilename": + _nowMat.bumpMap = this.texloader.load( fileName ); + _nowMat.bumpScale = 0.05; + break; + case "NormalMapFilename": + _nowMat.normalMap = this.texloader.load( fileName ); + _nowMat.normalScale = new Vector2( 2, 2 ); + break; + case "EmissiveMapFilename": + _nowMat.emissiveMap = this.texloader.load( fileName ); + break; + case "LightMapFilename": + _nowMat.lightMap = this.texloader.load( fileName ); + break; + + } + + } else { + + break; + + } + + } + this._currentGeo.Materials.push( _nowMat ); + + } + }, { + key: '_setSkinWeights', + value: function _setSkinWeights() { + + var boneInf = new XboneInf(); + var endRead = 0; + var find = this._currentObject.data.indexOf( ';', endRead ); + var line = this._currentObject.data.substr( endRead, find - endRead ); + endRead = find + 1; + boneInf.boneName = line.substr( 1, line.length - 2 ); + boneInf.BoneIndex = this._currentGeo.BoneInfs.length; + find = this._currentObject.data.indexOf( ';', endRead ); + endRead = find + 1; + find = this._currentObject.data.indexOf( ';', endRead ); + line = this._currentObject.data.substr( endRead, find - endRead ); + var data = this._readLine( line.trim() ).split( "," ); + for ( var i = 0; i < data.length; i ++ ) { + + boneInf.Indeces.push( parseInt( data[ i ] ) ); + + } + endRead = find + 1; + find = this._currentObject.data.indexOf( ';', endRead ); + line = this._currentObject.data.substr( endRead, find - endRead ); + var data2 = this._readLine( line.trim() ).split( "," ); + for ( var _i = 0; _i < data2.length; _i ++ ) { + + boneInf.Weights.push( parseFloat( data2[ _i ] ) ); + + } + endRead = find + 1; + find = this._currentObject.data.indexOf( ';', endRead ); + if ( find <= 0 ) { + + find = this._currentObject.data.length; + + } + line = this._currentObject.data.substr( endRead, find - endRead ); + var data3 = this._readLine( line.trim() ).split( "," ); + boneInf.OffsetMatrix = new Matrix4(); + this._ParseMatrixData( boneInf.OffsetMatrix, data3 ); + this._currentGeo.BoneInfs.push( boneInf ); + + } + }, { + key: '_makePutBoneList', + value: function _makePutBoneList( _RootName, _bones ) { + + var putting = false; + for ( var frame in this.HieStack ) { + + if ( this.HieStack[ frame ].name === _RootName || putting ) { + + putting = true; + var b = new Bone(); + b.name = this.HieStack[ frame ].name; + b.applyMatrix( this.HieStack[ frame ].FrameTransformMatrix ); + b.matrixWorld = b.matrix; + b.FrameTransformMatrix = this.HieStack[ frame ].FrameTransformMatrix; + b.pos = new Vector3().setFromMatrixPosition( b.FrameTransformMatrix ).toArray(); + b.rotq = new Quaternion().setFromRotationMatrix( b.FrameTransformMatrix ).toArray(); + b.scl = new Vector3().setFromMatrixScale( b.FrameTransformMatrix ).toArray(); + if ( this.HieStack[ frame ].parentName && this.HieStack[ frame ].parentName.length > 0 ) { + + for ( var i = 0; i < _bones.length; i ++ ) { + + if ( this.HieStack[ frame ].parentName === _bones[ i ].name ) { + + _bones[ i ].add( b ); + b.parent = i; + break; + + } + + } + + } + _bones.push( b ); + + } + + } + + } + }, { + key: '_makeOutputGeometry', + value: function _makeOutputGeometry() { + + var mesh = null; + if ( this._currentGeo.BoneInfs.length > 0 ) { + + var putBones = []; + this._makePutBoneList( this._currentGeo.baseFrame.parentName, putBones ); + for ( var bi = 0; bi < this._currentGeo.BoneInfs.length; bi ++ ) { + + var boneIndex = 0; + for ( var bb = 0; bb < putBones.length; bb ++ ) { + + if ( putBones[ bb ].name === this._currentGeo.BoneInfs[ bi ].boneName ) { + + boneIndex = bb; + putBones[ bb ].OffsetMatrix = new Matrix4(); + putBones[ bb ].OffsetMatrix.copy( this._currentGeo.BoneInfs[ bi ].OffsetMatrix ); + break; + + } + + } + for ( var vi = 0; vi < this._currentGeo.BoneInfs[ bi ].Indeces.length; vi ++ ) { + + var nowVertexID = this._currentGeo.BoneInfs[ bi ].Indeces[ vi ]; + var nowVal = this._currentGeo.BoneInfs[ bi ].Weights[ vi ]; + + var stride = nowVertexID * 4; + + switch ( this._currentGeo.VertexSetedBoneCount[ nowVertexID ] ) { + + case 0: + this._currentGeo.GeometryData.skinIndices[ stride ] = boneIndex; + this._currentGeo.GeometryData.skinWeights[ stride ] = nowVal; + break; + case 1: + this._currentGeo.GeometryData.skinIndices[ stride + 1 ] = boneIndex; + this._currentGeo.GeometryData.skinWeights[ stride + 1 ] = nowVal; + break; + case 2: + this._currentGeo.GeometryData.skinIndices[ stride + 2 ] = boneIndex; + this._currentGeo.GeometryData.skinWeights[ stride + 2 ] = nowVal; + break; + case 3: + this._currentGeo.GeometryData.skinIndices[ stride + 3 ] = boneIndex; + this._currentGeo.GeometryData.skinWeights[ stride + 3 ] = nowVal; + break; + + } + this._currentGeo.VertexSetedBoneCount[ nowVertexID ] ++; + if ( this._currentGeo.VertexSetedBoneCount[ nowVertexID ] > 4 ) { + + console.log( 'warn! over 4 bone weight! :' + nowVertexID ); + + } + + } + + } + for ( var sk = 0; sk < this._currentGeo.Materials.length; sk ++ ) { + + this._currentGeo.Materials[ sk ].skinning = true; + + } + var offsetList = []; + for ( var _bi = 0; _bi < putBones.length; _bi ++ ) { + + if ( putBones[ _bi ].OffsetMatrix ) { + + offsetList.push( putBones[ _bi ].OffsetMatrix ); + + } else { + + offsetList.push( new Matrix4() ); + + } + + } + + var bufferGeometry = this._buildGeometry(); + mesh = new SkinnedMesh( bufferGeometry, this._currentGeo.Materials.length === 1 ? this._currentGeo.Materials[ 0 ] : this._currentGeo.Materials ); + + this._initSkeleton( mesh, putBones, offsetList ); + + } else { + + var _bufferGeometry = this._buildGeometry(); + mesh = new Mesh( _bufferGeometry, this._currentGeo.Materials.length === 1 ? this._currentGeo.Materials[ 0 ] : this._currentGeo.Materials ); + + } + mesh.name = this._currentGeo.name; + var worldBaseMx = new Matrix4(); + var currentMxFrame = this._currentGeo.baseFrame.putBone; + if ( currentMxFrame && currentMxFrame.parent ) { + + while ( true ) { + + currentMxFrame = currentMxFrame.parent; + if ( currentMxFrame ) { + + worldBaseMx.multiply( currentMxFrame.FrameTransformMatrix ); + + } else { + + break; + + } + + } + mesh.applyMatrix( worldBaseMx ); + + } + this.Meshes.push( mesh ); + + } + }, { + key: '_initSkeleton', + value: function _initSkeleton( mesh, boneList, boneInverses ) { + + var bones = [], bone, gbone; + var i, il; + + for ( i = 0, il = boneList.length; i < il; i ++ ) { + + gbone = boneList[ i ]; + + bone = new Bone(); + bones.push( bone ); + + bone.name = gbone.name; + bone.position.fromArray( gbone.pos ); + bone.quaternion.fromArray( gbone.rotq ); + if ( gbone.scl !== undefined ) bone.scale.fromArray( gbone.scl ); + + } + + for ( i = 0, il = boneList.length; i < il; i ++ ) { + + gbone = boneList[ i ]; + + if ( ( gbone.parent !== - 1 ) && ( gbone.parent !== null ) && ( bones[ gbone.parent ] !== undefined ) ) { + + bones[ gbone.parent ].add( bones[ i ] ); + + } else { + + mesh.add( bones[ i ] ); + + } + + } + + mesh.updateMatrixWorld( true ); + + var skeleton = new Skeleton( bones, boneInverses ); + mesh.bind( skeleton, mesh.matrixWorld ); + + } + + }, { + key: '_readAnimationKey', + value: function _readAnimationKey() { + + var endRead = 0; + var find = this._currentObject.data.indexOf( ';', endRead ); + var line = this._currentObject.data.substr( endRead, find - endRead ); + endRead = find + 1; + var nowKeyType = parseInt( this._readLine( line ) ); + find = this._currentObject.data.indexOf( ';', endRead ); + endRead = find + 1; + line = this._currentObject.data.substr( endRead ); + var data = this._readLine( line.trim() ).split( ";;," ); + for ( var i = 0; i < data.length; i ++ ) { + + var data2 = data[ i ].split( ";" ); + var keyInfo = new XKeyFrameInfo(); + keyInfo.type = nowKeyType; + keyInfo.Frame = parseInt( data2[ 0 ] ); + keyInfo.index = this._currentAnimeFrames.keyFrames.length; + keyInfo.time = keyInfo.Frame; + if ( nowKeyType != 4 ) { + + var frameFound = false; + for ( var mm = 0; mm < this._currentAnimeFrames.keyFrames.length; mm ++ ) { + + if ( this._currentAnimeFrames.keyFrames[ mm ].Frame === keyInfo.Frame ) { + + keyInfo = this._currentAnimeFrames.keyFrames[ mm ]; + frameFound = true; + break; + + } + + } + var frameValue = data2[ 2 ].split( "," ); + switch ( nowKeyType ) { + + case 0: + keyInfo.rot = new Quaternion( parseFloat( frameValue[ 1 ] ), parseFloat( frameValue[ 2 ] ), parseFloat( frameValue[ 3 ] ), parseFloat( frameValue[ 0 ] ) * - 1 ); + break; + case 1: + keyInfo.scl = new Vector3( parseFloat( frameValue[ 0 ] ), parseFloat( frameValue[ 1 ] ), parseFloat( frameValue[ 2 ] ) ); + break; + case 2: + keyInfo.pos = new Vector3( parseFloat( frameValue[ 0 ] ), parseFloat( frameValue[ 1 ] ), parseFloat( frameValue[ 2 ] ) ); + break; + + } + if ( ! frameFound ) { + + this._currentAnimeFrames.keyFrames.push( keyInfo ); + + } + + } else { + + keyInfo.matrix = new Matrix4(); + this._ParseMatrixData( keyInfo.matrix, data2[ 2 ].split( "," ) ); + this._currentAnimeFrames.keyFrames.push( keyInfo ); + + } + + } + + } + }, { + key: '_makeOutputAnimation', + value: function _makeOutputAnimation() { + + var animationObj = new XAnimationObj( this.options ); + animationObj.fps = this.animTicksPerSecond; + animationObj.name = this._currentAnime.name; + animationObj.make( this._currentAnime.AnimeFrames ); + this.animations.push( animationObj ); + + } + }, { + key: 'assignAnimation', + value: function assignAnimation( _model, _animation, _isBind ) { + + var model = _model; + var animation = _animation; + if ( ! model ) { + + model = this.Meshes[ 0 ]; + + } + if ( ! animation ) { + + animation = this.animations[ 0 ]; + + } + if ( ! model || ! animation ) { + + return null; + + } + var put = {}; + put.fps = animation.fps; + put.name = animation.name; + put.length = animation.length; + put.hierarchy = []; + for ( var b = 0; b < model.skeleton.bones.length; b ++ ) { + + var findAnimation = false; + for ( var i = 0; i < animation.hierarchy.length; i ++ ) { + + if ( model.skeleton.bones[ b ].name === animation.hierarchy[ i ].name ) { + + findAnimation = true; + var c_key = animation.hierarchy[ i ].copy(); + c_key.parent = - 1; + if ( model.skeleton.bones[ b ].parent && model.skeleton.bones[ b ].parent.type === "Bone" ) { + + for ( var bb = 0; bb < put.hierarchy.length; bb ++ ) { + + if ( put.hierarchy[ bb ].name === model.skeleton.bones[ b ].parent.name ) { + + c_key.parent = bb; + c_key.parentName = model.skeleton.bones[ b ].parent.name; + + } + + } + + } + put.hierarchy.push( c_key ); + break; + + } + + } + if ( ! findAnimation ) { + + var _c_key = animation.hierarchy[ 0 ].copy(); + _c_key.name = model.skeleton.bones[ b ].name; + _c_key.parent = - 1; + for ( var k = 0; k < _c_key.keys.length; k ++ ) { + + if ( _c_key.keys[ k ].pos ) { + + _c_key.keys[ k ].pos.set( 0, 0, 0 ); + + } + if ( _c_key.keys[ k ].scl ) { + + _c_key.keys[ k ].scl.set( 1, 1, 1 ); + + } + if ( _c_key.keys[ k ].rot ) { + + _c_key.keys[ k ].rot.set( 0, 0, 0, 1 ); + + } + + } + put.hierarchy.push( _c_key ); + + } + + } + if ( ! model.geometry.animations ) { + + model.geometry.animations = []; + + } + + model.geometry.animations.push( AnimationClip.parseAnimation( put, model.skeleton.bones ) ); + if ( ! model.animationMixer ) { + + model.animationMixer = new AnimationMixer( model ); + + } + + return put; + + } + }, { + key: '_ParseMatrixData', + value: function _ParseMatrixData( targetMatrix, data ) { + + targetMatrix.set( parseFloat( data[ 0 ] ), parseFloat( data[ 4 ] ), parseFloat( data[ 8 ] ), parseFloat( data[ 12 ] ), parseFloat( data[ 1 ] ), parseFloat( data[ 5 ] ), parseFloat( data[ 9 ] ), parseFloat( data[ 13 ] ), parseFloat( data[ 2 ] ), parseFloat( data[ 6 ] ), parseFloat( data[ 10 ] ), parseFloat( data[ 14 ] ), parseFloat( data[ 3 ] ), parseFloat( data[ 7 ] ), parseFloat( data[ 11 ] ), parseFloat( data[ 15 ] ) ); + + } + } ] ); + return XLoader; + +}(); + +export { XLoader }; diff --git a/examples/jsm/loaders/deprecated/LegacyGLTFLoader.js b/examples/jsm/loaders/deprecated/LegacyGLTFLoader.js new file mode 100644 index 00000000000000..c1fc0dd12af630 --- /dev/null +++ b/examples/jsm/loaders/deprecated/LegacyGLTFLoader.js @@ -0,0 +1,2325 @@ +/** + * @author Rich Tibbett / https://github.com/richtr + * @author mrdoob / http://mrdoob.com/ + * @author Tony Parisi / http://www.tonyparisi.com/ + * @author Takahiro / https://github.com/takahirox + */ + +import { + AddEquation, + AlphaFormat, + AlwaysDepth, + AmbientLight, + AnimationClip, + AnimationUtils, + BackSide, + Bone, + BufferAttribute, + BufferGeometry, + ClampToEdgeWrapping, + Color, + CustomBlending, + DefaultLoadingManager, + DirectionalLight, + DstAlphaFactor, + DstColorFactor, + EqualDepth, + FileLoader, + FrontSide, + GreaterEqualDepth, + Group, + InterleavedBuffer, + InterleavedBufferAttribute, + InterpolateDiscrete, + InterpolateLinear, + LessDepth, + LessEqualDepth, + Line, + LinearFilter, + LinearMipMapLinearFilter, + LinearMipMapNearestFilter, + LineLoop, + LineSegments, + LoaderUtils, + LuminanceAlphaFormat, + LuminanceFormat, + Math, + Matrix3, + Matrix4, + Mesh, + MeshBasicMaterial, + MirroredRepeatWrapping, + NearestFilter, + NearestMipMapLinearFilter, + NearestMipMapNearestFilter, + NeverDepth, + NotEqualDepth, + NoBlending, + Object3D, + OneFactor, + OneMinusDstAlphaFactor, + OneMinusDstColorFactor, + OneMinusSrcAlphaFactor, + OneMinusSrcColorFactor, + OrthographicCamera, + PerspectiveCamera, + PointLight, + QuaternionKeyframeTrack, + RawShaderMaterial, + RepeatWrapping, + ReverseSubtractEquation, + RGBAFormat, + RGBFormat, + Scene, + Skeleton, + SkinnedMesh, + SpotLight, + SrcAlphaFactor, + SrcAlphaSaturateFactor, + SrcColorFactor, + SubtractEquation, + Texture, + TextureLoader, + UniformsUtils, + UnsignedByteType, + UnsignedShort4444Type, + UnsignedShort5551Type, + UnsignedShort565Type, + Vector2, + Vector3, + Vector4, + VectorKeyframeTrack, + ZeroFactor, +} from "../../../../build/three.module.js"; + +function LegacyGLTFLoader( manager ) { + + this.manager = ( manager !== undefined ) ? manager : DefaultLoadingManager; + +} + +LegacyGLTFLoader.prototype = { + + constructor: LegacyGLTFLoader, + + crossOrigin: 'anonymous', + + load: function ( url, onLoad, onProgress, onError ) { + + var scope = this; + + var resourcePath; + + if ( this.resourcePath !== undefined ) { + + resourcePath = this.resourcePath; + + } else if ( this.path !== undefined ) { + + resourcePath = this.path; + + } else { + + resourcePath = LoaderUtils.extractUrlBase( url ); + + } + + var loader = new FileLoader( scope.manager ); + + loader.setPath( this.path ); + loader.setResponseType( 'arraybuffer' ); + + loader.load( url, function ( data ) { + + scope.parse( data, resourcePath, onLoad ); + + }, onProgress, onError ); + + }, + + setCrossOrigin: function ( value ) { + + this.crossOrigin = value; + return this; + + }, + + setPath: function ( value ) { + + this.path = value; + + }, + + setResourcePath: function ( value ) { + + this.resourcePath = value; + return this; + + }, + + parse: function ( data, path, callback ) { + + var content; + var extensions = {}; + + var magic = LoaderUtils.decodeText( new Uint8Array( data, 0, 4 ) ); + + if ( magic === BINARY_EXTENSION_HEADER_DEFAULTS.magic ) { + + extensions[ EXTENSIONS.KHR_BINARY_GLTF ] = new GLTFBinaryExtension( data ); + content = extensions[ EXTENSIONS.KHR_BINARY_GLTF ].content; + + } else { + + content = LoaderUtils.decodeText( new Uint8Array( data ) ); + + } + + var json = JSON.parse( content ); + + if ( json.extensionsUsed && json.extensionsUsed.indexOf( EXTENSIONS.KHR_MATERIALS_COMMON ) >= 0 ) { + + extensions[ EXTENSIONS.KHR_MATERIALS_COMMON ] = new GLTFMaterialsCommonExtension( json ); + + } + + var parser = new GLTFParser( json, extensions, { + + crossOrigin: this.crossOrigin, + manager: this.manager, + path: path || this.resourcePath || '' + + } ); + + parser.parse( function ( scene, scenes, cameras, animations ) { + + var glTF = { + "scene": scene, + "scenes": scenes, + "cameras": cameras, + "animations": animations + }; + + callback( glTF ); + + } ); + + } + +}; + +/* GLTFREGISTRY */ + +function GLTFRegistry() { + + var objects = {}; + + return { + + get: function ( key ) { + + return objects[ key ]; + + }, + + add: function ( key, object ) { + + objects[ key ] = object; + + }, + + remove: function ( key ) { + + delete objects[ key ]; + + }, + + removeAll: function () { + + objects = {}; + + }, + + update: function ( scene, camera ) { + + for ( var name in objects ) { + + var object = objects[ name ]; + + if ( object.update ) { + + object.update( scene, camera ); + + } + + } + + } + + }; + +} + +/* GLTFSHADERS */ + +LegacyGLTFLoader.Shaders = { + + update: function () { + + console.warn( 'THREE.LegacyGLTFLoader.Shaders has been deprecated, and now updates automatically.' ); + + } + +}; + +/* GLTFSHADER */ + +function GLTFShader( targetNode, allNodes ) { + + var boundUniforms = {}; + + // bind each uniform to its source node + + var uniforms = targetNode.material.uniforms; + + for ( var uniformId in uniforms ) { + + var uniform = uniforms[ uniformId ]; + + if ( uniform.semantic ) { + + var sourceNodeRef = uniform.node; + + var sourceNode = targetNode; + + if ( sourceNodeRef ) { + + sourceNode = allNodes[ sourceNodeRef ]; + + } + + boundUniforms[ uniformId ] = { + semantic: uniform.semantic, + sourceNode: sourceNode, + targetNode: targetNode, + uniform: uniform + }; + + } + + } + + this.boundUniforms = boundUniforms; + this._m4 = new Matrix4(); + +} + +// Update - update all the uniform values +GLTFShader.prototype.update = function ( scene, camera ) { + + var boundUniforms = this.boundUniforms; + + for ( var name in boundUniforms ) { + + var boundUniform = boundUniforms[ name ]; + + switch ( boundUniform.semantic ) { + + case "MODELVIEW": + + var m4 = boundUniform.uniform.value; + m4.multiplyMatrices( camera.matrixWorldInverse, boundUniform.sourceNode.matrixWorld ); + break; + + case "MODELVIEWINVERSETRANSPOSE": + + var m3 = boundUniform.uniform.value; + this._m4.multiplyMatrices( camera.matrixWorldInverse, boundUniform.sourceNode.matrixWorld ); + m3.getNormalMatrix( this._m4 ); + break; + + case "PROJECTION": + + var m4 = boundUniform.uniform.value; + m4.copy( camera.projectionMatrix ); + break; + + case "JOINTMATRIX": + + var m4v = boundUniform.uniform.value; + + for ( var mi = 0; mi < m4v.length; mi ++ ) { + + // So it goes like this: + // SkinnedMesh world matrix is already baked into MODELVIEW; + // transform joints to local space, + // then transform using joint's inverse + m4v[ mi ] + .getInverse( boundUniform.sourceNode.matrixWorld ) + .multiply( boundUniform.targetNode.skeleton.bones[ mi ].matrixWorld ) + .multiply( boundUniform.targetNode.skeleton.boneInverses[ mi ] ) + .multiply( boundUniform.targetNode.bindMatrix ); + + } + + break; + + default : + + console.warn( "Unhandled shader semantic: " + boundUniform.semantic ); + break; + + } + + } + +}; + + +/* ANIMATION */ + +LegacyGLTFLoader.Animations = { + + update: function () { + + console.warn( 'THREE.LegacyGLTFLoader.Animation has been deprecated. Use THREE.AnimationMixer instead.' ); + + } + +}; + +/*********************************/ +/********** EXTENSIONS ***********/ +/*********************************/ + +var EXTENSIONS = { + KHR_BINARY_GLTF: 'KHR_binary_glTF', + KHR_MATERIALS_COMMON: 'KHR_materials_common' +}; + +/* MATERIALS COMMON EXTENSION */ + +function GLTFMaterialsCommonExtension( json ) { + + this.name = EXTENSIONS.KHR_MATERIALS_COMMON; + + this.lights = {}; + + var extension = ( json.extensions && json.extensions[ EXTENSIONS.KHR_MATERIALS_COMMON ] ) || {}; + var lights = extension.lights || {}; + + for ( var lightId in lights ) { + + var light = lights[ lightId ]; + var lightNode; + + var lightParams = light[ light.type ]; + var color = new Color().fromArray( lightParams.color ); + + switch ( light.type ) { + + case "directional": + lightNode = new DirectionalLight( color ); + lightNode.position.set( 0, 0, 1 ); + break; + + case "point": + lightNode = new PointLight( color ); + break; + + case "spot": + lightNode = new SpotLight( color ); + lightNode.position.set( 0, 0, 1 ); + break; + + case "ambient": + lightNode = new AmbientLight( color ); + break; + + } + + if ( lightNode ) { + + this.lights[ lightId ] = lightNode; + + } + + } + +} + +/* BINARY EXTENSION */ + +var BINARY_EXTENSION_BUFFER_NAME = 'binary_glTF'; + +var BINARY_EXTENSION_HEADER_DEFAULTS = { magic: 'glTF', version: 1, contentFormat: 0 }; + +var BINARY_EXTENSION_HEADER_LENGTH = 20; + +function GLTFBinaryExtension( data ) { + + this.name = EXTENSIONS.KHR_BINARY_GLTF; + + var headerView = new DataView( data, 0, BINARY_EXTENSION_HEADER_LENGTH ); + + var header = { + magic: LoaderUtils.decodeText( new Uint8Array( data.slice( 0, 4 ) ) ), + version: headerView.getUint32( 4, true ), + length: headerView.getUint32( 8, true ), + contentLength: headerView.getUint32( 12, true ), + contentFormat: headerView.getUint32( 16, true ) + }; + + for ( var key in BINARY_EXTENSION_HEADER_DEFAULTS ) { + + var value = BINARY_EXTENSION_HEADER_DEFAULTS[ key ]; + + if ( header[ key ] !== value ) { + + throw new Error( 'Unsupported glTF-Binary header: Expected "%s" to be "%s".', key, value ); + + } + + } + + var contentArray = new Uint8Array( data, BINARY_EXTENSION_HEADER_LENGTH, header.contentLength ); + + this.header = header; + this.content = LoaderUtils.decodeText( contentArray ); + this.body = data.slice( BINARY_EXTENSION_HEADER_LENGTH + header.contentLength, header.length ); + +} + +GLTFBinaryExtension.prototype.loadShader = function ( shader, bufferViews ) { + + var bufferView = bufferViews[ shader.extensions[ EXTENSIONS.KHR_BINARY_GLTF ].bufferView ]; + var array = new Uint8Array( bufferView ); + + return LoaderUtils.decodeText( array ); + +}; + +/*********************************/ +/********** INTERNALS ************/ +/*********************************/ + +/* CONSTANTS */ + +var WEBGL_CONSTANTS = { + FLOAT: 5126, + //FLOAT_MAT2: 35674, + FLOAT_MAT3: 35675, + FLOAT_MAT4: 35676, + FLOAT_VEC2: 35664, + FLOAT_VEC3: 35665, + FLOAT_VEC4: 35666, + LINEAR: 9729, + REPEAT: 10497, + SAMPLER_2D: 35678, + TRIANGLES: 4, + LINES: 1, + UNSIGNED_BYTE: 5121, + UNSIGNED_SHORT: 5123, + + VERTEX_SHADER: 35633, + FRAGMENT_SHADER: 35632 +}; + +var WEBGL_TYPE = { + 5126: Number, + //35674: THREE.Matrix2, + 35675: Matrix3, + 35676: Matrix4, + 35664: Vector2, + 35665: Vector3, + 35666: Vector4, + 35678: Texture +}; + +var WEBGL_COMPONENT_TYPES = { + 5120: Int8Array, + 5121: Uint8Array, + 5122: Int16Array, + 5123: Uint16Array, + 5125: Uint32Array, + 5126: Float32Array +}; + +var WEBGL_FILTERS = { + 9728: NearestFilter, + 9729: LinearFilter, + 9984: NearestMipMapNearestFilter, + 9985: LinearMipMapNearestFilter, + 9986: NearestMipMapLinearFilter, + 9987: LinearMipMapLinearFilter +}; + +var WEBGL_WRAPPINGS = { + 33071: ClampToEdgeWrapping, + 33648: MirroredRepeatWrapping, + 10497: RepeatWrapping +}; + +var WEBGL_TEXTURE_FORMATS = { + 6406: AlphaFormat, + 6407: RGBFormat, + 6408: RGBAFormat, + 6409: LuminanceFormat, + 6410: LuminanceAlphaFormat +}; + +var WEBGL_TEXTURE_DATATYPES = { + 5121: UnsignedByteType, + 32819: UnsignedShort4444Type, + 32820: UnsignedShort5551Type, + 33635: UnsignedShort565Type +}; + +var WEBGL_SIDES = { + 1028: BackSide, // Culling front + 1029: FrontSide // Culling back + //1032: THREE.NoSide // Culling front and back, what to do? +}; + +var WEBGL_DEPTH_FUNCS = { + 512: NeverDepth, + 513: LessDepth, + 514: EqualDepth, + 515: LessEqualDepth, + 516: GreaterEqualDepth, + 517: NotEqualDepth, + 518: GreaterEqualDepth, + 519: AlwaysDepth +}; + +var WEBGL_BLEND_EQUATIONS = { + 32774: AddEquation, + 32778: SubtractEquation, + 32779: ReverseSubtractEquation +}; + +var WEBGL_BLEND_FUNCS = { + 0: ZeroFactor, + 1: OneFactor, + 768: SrcColorFactor, + 769: OneMinusSrcColorFactor, + 770: SrcAlphaFactor, + 771: OneMinusSrcAlphaFactor, + 772: DstAlphaFactor, + 773: OneMinusDstAlphaFactor, + 774: DstColorFactor, + 775: OneMinusDstColorFactor, + 776: SrcAlphaSaturateFactor + // The followings are not supported by Three.js yet + //32769: CONSTANT_COLOR, + //32770: ONE_MINUS_CONSTANT_COLOR, + //32771: CONSTANT_ALPHA, + //32772: ONE_MINUS_CONSTANT_COLOR +}; + +var WEBGL_TYPE_SIZES = { + 'SCALAR': 1, + 'VEC2': 2, + 'VEC3': 3, + 'VEC4': 4, + 'MAT2': 4, + 'MAT3': 9, + 'MAT4': 16 +}; + +var PATH_PROPERTIES = { + scale: 'scale', + translation: 'position', + rotation: 'quaternion' +}; + +var INTERPOLATION = { + LINEAR: InterpolateLinear, + STEP: InterpolateDiscrete +}; + +var STATES_ENABLES = { + 2884: 'CULL_FACE', + 2929: 'DEPTH_TEST', + 3042: 'BLEND', + 3089: 'SCISSOR_TEST', + 32823: 'POLYGON_OFFSET_FILL', + 32926: 'SAMPLE_ALPHA_TO_COVERAGE' +}; + +/* UTILITY FUNCTIONS */ + +function _each( object, callback, thisObj ) { + + if ( ! object ) { + + return Promise.resolve(); + + } + + var results; + var fns = []; + + if ( Object.prototype.toString.call( object ) === '[object Array]' ) { + + results = []; + + var length = object.length; + + for ( var idx = 0; idx < length; idx ++ ) { + + var value = callback.call( thisObj || this, object[ idx ], idx ); + + if ( value ) { + + fns.push( value ); + + if ( value instanceof Promise ) { + + value.then( function ( key, value ) { + + results[ key ] = value; + + }.bind( this, idx ) ); + + } else { + + results[ idx ] = value; + + } + + } + + } + + } else { + + results = {}; + + for ( var key in object ) { + + if ( object.hasOwnProperty( key ) ) { + + var value = callback.call( thisObj || this, object[ key ], key ); + + if ( value ) { + + fns.push( value ); + + if ( value instanceof Promise ) { + + value.then( function ( key, value ) { + + results[ key ] = value; + + }.bind( this, key ) ); + + } else { + + results[ key ] = value; + + } + + } + + } + + } + + } + + return Promise.all( fns ).then( function () { + + return results; + + } ); + +} + +function resolveURL( url, path ) { + + // Invalid URL + if ( typeof url !== 'string' || url === '' ) + return ''; + + // Absolute URL http://,https://,// + if ( /^(https?:)?\/\//i.test( url ) ) { + + return url; + + } + + // Data URI + if ( /^data:.*,.*$/i.test( url ) ) { + + return url; + + } + + // Blob URL + if ( /^blob:.*$/i.test( url ) ) { + + return url; + + } + + // Relative URL + return ( path || '' ) + url; + +} + +// Three.js seems too dependent on attribute names so globally +// replace those in the shader code +function replaceTHREEShaderAttributes( shaderText, technique ) { + + // Expected technique attributes + var attributes = {}; + + for ( var attributeId in technique.attributes ) { + + var pname = technique.attributes[ attributeId ]; + + var param = technique.parameters[ pname ]; + var atype = param.type; + var semantic = param.semantic; + + attributes[ attributeId ] = { + type: atype, + semantic: semantic + }; + + } + + // Figure out which attributes to change in technique + + var shaderParams = technique.parameters; + var shaderAttributes = technique.attributes; + var params = {}; + + for ( var attributeId in attributes ) { + + var pname = shaderAttributes[ attributeId ]; + var shaderParam = shaderParams[ pname ]; + var semantic = shaderParam.semantic; + if ( semantic ) { + + params[ attributeId ] = shaderParam; + + } + + } + + for ( var pname in params ) { + + var param = params[ pname ]; + var semantic = param.semantic; + + var regEx = new RegExp( "\\b" + pname + "\\b", "g" ); + + switch ( semantic ) { + + case "POSITION": + + shaderText = shaderText.replace( regEx, 'position' ); + break; + + case "NORMAL": + + shaderText = shaderText.replace( regEx, 'normal' ); + break; + + case 'TEXCOORD_0': + case 'TEXCOORD0': + case 'TEXCOORD': + + shaderText = shaderText.replace( regEx, 'uv' ); + break; + + case 'TEXCOORD_1': + + shaderText = shaderText.replace( regEx, 'uv2' ); + break; + + case 'COLOR_0': + case 'COLOR0': + case 'COLOR': + + shaderText = shaderText.replace( regEx, 'color' ); + break; + + case "WEIGHT": + + shaderText = shaderText.replace( regEx, 'skinWeight' ); + break; + + case "JOINT": + + shaderText = shaderText.replace( regEx, 'skinIndex' ); + break; + + } + + } + + return shaderText; + +} + +function createDefaultMaterial() { + + return new MeshPhongMaterial( { + color: 0x00000, + emissive: 0x888888, + specular: 0x000000, + shininess: 0, + transparent: false, + depthTest: true, + side: FrontSide + } ); + +} + +// Deferred constructor for RawShaderMaterial types +function DeferredShaderMaterial( params ) { + + this.isDeferredShaderMaterial = true; + + this.params = params; + +} + +DeferredShaderMaterial.prototype.create = function () { + + var uniforms = UniformsUtils.clone( this.params.uniforms ); + + for ( var uniformId in this.params.uniforms ) { + + var originalUniform = this.params.uniforms[ uniformId ]; + + if ( originalUniform.value instanceof Texture ) { + + uniforms[ uniformId ].value = originalUniform.value; + uniforms[ uniformId ].value.needsUpdate = true; + + } + + uniforms[ uniformId ].semantic = originalUniform.semantic; + uniforms[ uniformId ].node = originalUniform.node; + + } + + this.params.uniforms = uniforms; + + return new RawShaderMaterial( this.params ); + +}; + +/* GLTF PARSER */ + +function GLTFParser( json, extensions, options ) { + + this.json = json || {}; + this.extensions = extensions || {}; + this.options = options || {}; + + // loader object cache + this.cache = new GLTFRegistry(); + +} + +GLTFParser.prototype._withDependencies = function ( dependencies ) { + + var _dependencies = {}; + + for ( var i = 0; i < dependencies.length; i ++ ) { + + var dependency = dependencies[ i ]; + var fnName = "load" + dependency.charAt( 0 ).toUpperCase() + dependency.slice( 1 ); + + var cached = this.cache.get( dependency ); + + if ( cached !== undefined ) { + + _dependencies[ dependency ] = cached; + + } else if ( this[ fnName ] ) { + + var fn = this[ fnName ](); + this.cache.add( dependency, fn ); + + _dependencies[ dependency ] = fn; + + } + + } + + return _each( _dependencies, function ( dependency ) { + + return dependency; + + } ); + +}; + +GLTFParser.prototype.parse = function ( callback ) { + + var json = this.json; + + // Clear the loader cache + this.cache.removeAll(); + + // Fire the callback on complete + this._withDependencies( [ + + "scenes", + "cameras", + "animations" + + ] ).then( function ( dependencies ) { + + var scenes = []; + + for ( var name in dependencies.scenes ) { + + scenes.push( dependencies.scenes[ name ] ); + + } + + var scene = json.scene !== undefined ? dependencies.scenes[ json.scene ] : scenes[ 0 ]; + + var cameras = []; + + for ( var name in dependencies.cameras ) { + + var camera = dependencies.cameras[ name ]; + cameras.push( camera ); + + } + + var animations = []; + + for ( var name in dependencies.animations ) { + + animations.push( dependencies.animations[ name ] ); + + } + + callback( scene, scenes, cameras, animations ); + + } ); + +}; + +GLTFParser.prototype.loadShaders = function () { + + var json = this.json; + var extensions = this.extensions; + var options = this.options; + + return this._withDependencies( [ + + "bufferViews" + + ] ).then( function ( dependencies ) { + + return _each( json.shaders, function ( shader ) { + + if ( shader.extensions && shader.extensions[ EXTENSIONS.KHR_BINARY_GLTF ] ) { + + return extensions[ EXTENSIONS.KHR_BINARY_GLTF ].loadShader( shader, dependencies.bufferViews ); + + } + + return new Promise( function ( resolve ) { + + var loader = new FileLoader( options.manager ); + loader.setResponseType( 'text' ); + loader.load( resolveURL( shader.uri, options.path ), function ( shaderText ) { + + resolve( shaderText ); + + } ); + + } ); + + } ); + + } ); + +}; + +GLTFParser.prototype.loadBuffers = function () { + + var json = this.json; + var extensions = this.extensions; + var options = this.options; + + return _each( json.buffers, function ( buffer, name ) { + + if ( name === BINARY_EXTENSION_BUFFER_NAME ) { + + return extensions[ EXTENSIONS.KHR_BINARY_GLTF ].body; + + } + + if ( buffer.type === 'arraybuffer' || buffer.type === undefined ) { + + return new Promise( function ( resolve ) { + + var loader = new FileLoader( options.manager ); + loader.setResponseType( 'arraybuffer' ); + loader.load( resolveURL( buffer.uri, options.path ), function ( buffer ) { + + resolve( buffer ); + + } ); + + } ); + + } else { + + console.warn( 'THREE.LegacyGLTFLoader: ' + buffer.type + ' buffer type is not supported' ); + + } + + } ); + +}; + +GLTFParser.prototype.loadBufferViews = function () { + + var json = this.json; + + return this._withDependencies( [ + + "buffers" + + ] ).then( function ( dependencies ) { + + return _each( json.bufferViews, function ( bufferView ) { + + var arraybuffer = dependencies.buffers[ bufferView.buffer ]; + + var byteLength = bufferView.byteLength !== undefined ? bufferView.byteLength : 0; + + return arraybuffer.slice( bufferView.byteOffset, bufferView.byteOffset + byteLength ); + + } ); + + } ); + +}; + +GLTFParser.prototype.loadAccessors = function () { + + var json = this.json; + + return this._withDependencies( [ + + "bufferViews" + + ] ).then( function ( dependencies ) { + + return _each( json.accessors, function ( accessor ) { + + var arraybuffer = dependencies.bufferViews[ accessor.bufferView ]; + var itemSize = WEBGL_TYPE_SIZES[ accessor.type ]; + var TypedArray = WEBGL_COMPONENT_TYPES[ accessor.componentType ]; + + // For VEC3: itemSize is 3, elementBytes is 4, itemBytes is 12. + var elementBytes = TypedArray.BYTES_PER_ELEMENT; + var itemBytes = elementBytes * itemSize; + + // The buffer is not interleaved if the stride is the item size in bytes. + if ( accessor.byteStride && accessor.byteStride !== itemBytes ) { + + // Use the full buffer if it's interleaved. + var array = new TypedArray( arraybuffer ); + + // Integer parameters to IB/IBA are in array elements, not bytes. + var ib = new InterleavedBuffer( array, accessor.byteStride / elementBytes ); + + return new InterleavedBufferAttribute( ib, itemSize, accessor.byteOffset / elementBytes ); + + } else { + + array = new TypedArray( arraybuffer, accessor.byteOffset, accessor.count * itemSize ); + + return new BufferAttribute( array, itemSize ); + + } + + } ); + + } ); + +}; + +GLTFParser.prototype.loadTextures = function () { + + var json = this.json; + var extensions = this.extensions; + var options = this.options; + + return this._withDependencies( [ + + "bufferViews" + + ] ).then( function ( dependencies ) { + + return _each( json.textures, function ( texture ) { + + if ( texture.source ) { + + return new Promise( function ( resolve ) { + + var source = json.images[ texture.source ]; + var sourceUri = source.uri; + var isObjectURL = false; + + if ( source.extensions && source.extensions[ EXTENSIONS.KHR_BINARY_GLTF ] ) { + + var metadata = source.extensions[ EXTENSIONS.KHR_BINARY_GLTF ]; + var bufferView = dependencies.bufferViews[ metadata.bufferView ]; + var blob = new Blob( [ bufferView ], { type: metadata.mimeType } ); + sourceUri = URL.createObjectURL( blob ); + isObjectURL = true; + + } + + var textureLoader = Loader.Handlers.get( sourceUri ); + + if ( textureLoader === null ) { + + textureLoader = new TextureLoader( options.manager ); + + } + + textureLoader.setCrossOrigin( options.crossOrigin ); + + textureLoader.load( resolveURL( sourceUri, options.path ), function ( _texture ) { + + if ( isObjectURL ) URL.revokeObjectURL( sourceUri ); + + _texture.flipY = false; + + if ( texture.name !== undefined ) _texture.name = texture.name; + + _texture.format = texture.format !== undefined ? WEBGL_TEXTURE_FORMATS[ texture.format ] : RGBAFormat; + + if ( texture.internalFormat !== undefined && _texture.format !== WEBGL_TEXTURE_FORMATS[ texture.internalFormat ] ) { + + console.warn( 'THREE.LegacyGLTFLoader: Three.js doesn\'t support texture internalFormat which is different from texture format. ' + + 'internalFormat will be forced to be the same value as format.' ); + + } + + _texture.type = texture.type !== undefined ? WEBGL_TEXTURE_DATATYPES[ texture.type ] : UnsignedByteType; + + if ( texture.sampler ) { + + var sampler = json.samplers[ texture.sampler ]; + + _texture.magFilter = WEBGL_FILTERS[ sampler.magFilter ] || LinearFilter; + _texture.minFilter = WEBGL_FILTERS[ sampler.minFilter ] || NearestMipMapLinearFilter; + _texture.wrapS = WEBGL_WRAPPINGS[ sampler.wrapS ] || RepeatWrapping; + _texture.wrapT = WEBGL_WRAPPINGS[ sampler.wrapT ] || RepeatWrapping; + + } + + resolve( _texture ); + + }, undefined, function () { + + if ( isObjectURL ) URL.revokeObjectURL( sourceUri ); + + resolve(); + + } ); + + } ); + + } + + } ); + + } ); + +}; + +GLTFParser.prototype.loadMaterials = function () { + + var json = this.json; + + return this._withDependencies( [ + + "shaders", + "textures" + + ] ).then( function ( dependencies ) { + + return _each( json.materials, function ( material ) { + + var materialType; + var materialValues = {}; + var materialParams = {}; + + var khr_material; + + if ( material.extensions && material.extensions[ EXTENSIONS.KHR_MATERIALS_COMMON ] ) { + + khr_material = material.extensions[ EXTENSIONS.KHR_MATERIALS_COMMON ]; + + } + + if ( khr_material ) { + + // don't copy over unused values to avoid material warning spam + var keys = [ 'ambient', 'emission', 'transparent', 'transparency', 'doubleSided' ]; + + switch ( khr_material.technique ) { + + case 'BLINN' : + case 'PHONG' : + materialType = MeshPhongMaterial; + keys.push( 'diffuse', 'specular', 'shininess' ); + break; + + case 'LAMBERT' : + materialType = MeshLambertMaterial; + keys.push( 'diffuse' ); + break; + + case 'CONSTANT' : + default : + materialType = MeshBasicMaterial; + break; + + } + + keys.forEach( function ( v ) { + + if ( khr_material.values[ v ] !== undefined ) materialValues[ v ] = khr_material.values[ v ]; + + } ); + + if ( khr_material.doubleSided || materialValues.doubleSided ) { + + materialParams.side = DoubleSide; + + } + + if ( khr_material.transparent || materialValues.transparent ) { + + materialParams.transparent = true; + materialParams.opacity = ( materialValues.transparency !== undefined ) ? materialValues.transparency : 1; + + } + + } else if ( material.technique === undefined ) { + + materialType = MeshPhongMaterial; + + Object.assign( materialValues, material.values ); + + } else { + + materialType = DeferredShaderMaterial; + + var technique = json.techniques[ material.technique ]; + + materialParams.uniforms = {}; + + var program = json.programs[ technique.program ]; + + if ( program ) { + + materialParams.fragmentShader = dependencies.shaders[ program.fragmentShader ]; + + if ( ! materialParams.fragmentShader ) { + + console.warn( "ERROR: Missing fragment shader definition:", program.fragmentShader ); + materialType = MeshPhongMaterial; + + } + + var vertexShader = dependencies.shaders[ program.vertexShader ]; + + if ( ! vertexShader ) { + + console.warn( "ERROR: Missing vertex shader definition:", program.vertexShader ); + materialType = MeshPhongMaterial; + + } + + // IMPORTANT: FIX VERTEX SHADER ATTRIBUTE DEFINITIONS + materialParams.vertexShader = replaceTHREEShaderAttributes( vertexShader, technique ); + + var uniforms = technique.uniforms; + + for ( var uniformId in uniforms ) { + + var pname = uniforms[ uniformId ]; + var shaderParam = technique.parameters[ pname ]; + + var ptype = shaderParam.type; + + if ( WEBGL_TYPE[ ptype ] ) { + + var pcount = shaderParam.count; + var value; + + if ( material.values !== undefined ) value = material.values[ pname ]; + + var uvalue = new WEBGL_TYPE[ ptype ](); + var usemantic = shaderParam.semantic; + var unode = shaderParam.node; + + switch ( ptype ) { + + case WEBGL_CONSTANTS.FLOAT: + + uvalue = shaderParam.value; + + if ( pname == "transparency" ) { + + materialParams.transparent = true; + + } + + if ( value !== undefined ) { + + uvalue = value; + + } + + break; + + case WEBGL_CONSTANTS.FLOAT_VEC2: + case WEBGL_CONSTANTS.FLOAT_VEC3: + case WEBGL_CONSTANTS.FLOAT_VEC4: + case WEBGL_CONSTANTS.FLOAT_MAT3: + + if ( shaderParam && shaderParam.value ) { + + uvalue.fromArray( shaderParam.value ); + + } + + if ( value ) { + + uvalue.fromArray( value ); + + } + + break; + + case WEBGL_CONSTANTS.FLOAT_MAT2: + + // what to do? + console.warn( "FLOAT_MAT2 is not a supported uniform type" ); + break; + + case WEBGL_CONSTANTS.FLOAT_MAT4: + + if ( pcount ) { + + uvalue = new Array( pcount ); + + for ( var mi = 0; mi < pcount; mi ++ ) { + + uvalue[ mi ] = new WEBGL_TYPE[ ptype ](); + + } + + if ( shaderParam && shaderParam.value ) { + + var m4v = shaderParam.value; + uvalue.fromArray( m4v ); + + } + + if ( value ) { + + uvalue.fromArray( value ); + + } + + } else { + + if ( shaderParam && shaderParam.value ) { + + var m4 = shaderParam.value; + uvalue.fromArray( m4 ); + + } + + if ( value ) { + + uvalue.fromArray( value ); + + } + + } + + break; + + case WEBGL_CONSTANTS.SAMPLER_2D: + + if ( value !== undefined ) { + + uvalue = dependencies.textures[ value ]; + + } else if ( shaderParam.value !== undefined ) { + + uvalue = dependencies.textures[ shaderParam.value ]; + + } else { + + uvalue = null; + + } + + break; + + } + + materialParams.uniforms[ uniformId ] = { + value: uvalue, + semantic: usemantic, + node: unode + }; + + } else { + + throw new Error( "Unknown shader uniform param type: " + ptype ); + + } + + } + + var states = technique.states || {}; + var enables = states.enable || []; + var functions = states.functions || {}; + + var enableCullFace = false; + var enableDepthTest = false; + var enableBlend = false; + + for ( var i = 0, il = enables.length; i < il; i ++ ) { + + var enable = enables[ i ]; + + switch ( STATES_ENABLES[ enable ] ) { + + case 'CULL_FACE': + + enableCullFace = true; + + break; + + case 'DEPTH_TEST': + + enableDepthTest = true; + + break; + + case 'BLEND': + + enableBlend = true; + + break; + + // TODO: implement + case 'SCISSOR_TEST': + case 'POLYGON_OFFSET_FILL': + case 'SAMPLE_ALPHA_TO_COVERAGE': + + break; + + default: + + throw new Error( "Unknown technique.states.enable: " + enable ); + + } + + } + + if ( enableCullFace ) { + + materialParams.side = functions.cullFace !== undefined ? WEBGL_SIDES[ functions.cullFace ] : FrontSide; + + } else { + + materialParams.side = DoubleSide; + + } + + materialParams.depthTest = enableDepthTest; + materialParams.depthFunc = functions.depthFunc !== undefined ? WEBGL_DEPTH_FUNCS[ functions.depthFunc ] : LessDepth; + materialParams.depthWrite = functions.depthMask !== undefined ? functions.depthMask[ 0 ] : true; + + materialParams.blending = enableBlend ? CustomBlending : NoBlending; + materialParams.transparent = enableBlend; + + var blendEquationSeparate = functions.blendEquationSeparate; + + if ( blendEquationSeparate !== undefined ) { + + materialParams.blendEquation = WEBGL_BLEND_EQUATIONS[ blendEquationSeparate[ 0 ] ]; + materialParams.blendEquationAlpha = WEBGL_BLEND_EQUATIONS[ blendEquationSeparate[ 1 ] ]; + + } else { + + materialParams.blendEquation = AddEquation; + materialParams.blendEquationAlpha = AddEquation; + + } + + var blendFuncSeparate = functions.blendFuncSeparate; + + if ( blendFuncSeparate !== undefined ) { + + materialParams.blendSrc = WEBGL_BLEND_FUNCS[ blendFuncSeparate[ 0 ] ]; + materialParams.blendDst = WEBGL_BLEND_FUNCS[ blendFuncSeparate[ 1 ] ]; + materialParams.blendSrcAlpha = WEBGL_BLEND_FUNCS[ blendFuncSeparate[ 2 ] ]; + materialParams.blendDstAlpha = WEBGL_BLEND_FUNCS[ blendFuncSeparate[ 3 ] ]; + + } else { + + materialParams.blendSrc = OneFactor; + materialParams.blendDst = ZeroFactor; + materialParams.blendSrcAlpha = OneFactor; + materialParams.blendDstAlpha = ZeroFactor; + + } + + } + + } + + if ( Array.isArray( materialValues.diffuse ) ) { + + materialParams.color = new Color().fromArray( materialValues.diffuse ); + + } else if ( typeof ( materialValues.diffuse ) === 'string' ) { + + materialParams.map = dependencies.textures[ materialValues.diffuse ]; + + } + + delete materialParams.diffuse; + + if ( typeof ( materialValues.reflective ) === 'string' ) { + + materialParams.envMap = dependencies.textures[ materialValues.reflective ]; + + } + + if ( typeof ( materialValues.bump ) === 'string' ) { + + materialParams.bumpMap = dependencies.textures[ materialValues.bump ]; + + } + + if ( Array.isArray( materialValues.emission ) ) { + + if ( materialType === MeshBasicMaterial ) { + + materialParams.color = new Color().fromArray( materialValues.emission ); + + } else { + + materialParams.emissive = new Color().fromArray( materialValues.emission ); + + } + + } else if ( typeof ( materialValues.emission ) === 'string' ) { + + if ( materialType === MeshBasicMaterial ) { + + materialParams.map = dependencies.textures[ materialValues.emission ]; + + } else { + + materialParams.emissiveMap = dependencies.textures[ materialValues.emission ]; + + } + + } + + if ( Array.isArray( materialValues.specular ) ) { + + materialParams.specular = new Color().fromArray( materialValues.specular ); + + } else if ( typeof ( materialValues.specular ) === 'string' ) { + + materialParams.specularMap = dependencies.textures[ materialValues.specular ]; + + } + + if ( materialValues.shininess !== undefined ) { + + materialParams.shininess = materialValues.shininess; + + } + + var _material = new materialType( materialParams ); + if ( material.name !== undefined ) _material.name = material.name; + + return _material; + + } ); + + } ); + +}; + +GLTFParser.prototype.loadMeshes = function () { + + var json = this.json; + + return this._withDependencies( [ + + "accessors", + "materials" + + ] ).then( function ( dependencies ) { + + return _each( json.meshes, function ( mesh ) { + + var group = new Group(); + if ( mesh.name !== undefined ) group.name = mesh.name; + + if ( mesh.extras ) group.userData = mesh.extras; + + var primitives = mesh.primitives || []; + + for ( var name in primitives ) { + + var primitive = primitives[ name ]; + + if ( primitive.mode === WEBGL_CONSTANTS.TRIANGLES || primitive.mode === undefined ) { + + var geometry = new BufferGeometry(); + + var attributes = primitive.attributes; + + for ( var attributeId in attributes ) { + + var attributeEntry = attributes[ attributeId ]; + + if ( ! attributeEntry ) return; + + var bufferAttribute = dependencies.accessors[ attributeEntry ]; + + switch ( attributeId ) { + + case 'POSITION': + geometry.addAttribute( 'position', bufferAttribute ); + break; + + case 'NORMAL': + geometry.addAttribute( 'normal', bufferAttribute ); + break; + + case 'TEXCOORD_0': + case 'TEXCOORD0': + case 'TEXCOORD': + geometry.addAttribute( 'uv', bufferAttribute ); + break; + + case 'TEXCOORD_1': + geometry.addAttribute( 'uv2', bufferAttribute ); + break; + + case 'COLOR_0': + case 'COLOR0': + case 'COLOR': + geometry.addAttribute( 'color', bufferAttribute ); + break; + + case 'WEIGHT': + geometry.addAttribute( 'skinWeight', bufferAttribute ); + break; + + case 'JOINT': + geometry.addAttribute( 'skinIndex', bufferAttribute ); + break; + + default: + + if ( ! primitive.material ) break; + + var material = json.materials[ primitive.material ]; + + if ( ! material.technique ) break; + + var parameters = json.techniques[ material.technique ].parameters || {}; + + for ( var attributeName in parameters ) { + + if ( parameters[ attributeName ][ 'semantic' ] === attributeId ) { + + geometry.addAttribute( attributeName, bufferAttribute ); + + } + + } + + } + + } + + if ( primitive.indices ) { + + geometry.setIndex( dependencies.accessors[ primitive.indices ] ); + + } + + var material = dependencies.materials !== undefined ? dependencies.materials[ primitive.material ] : createDefaultMaterial(); + + var meshNode = new Mesh( geometry, material ); + meshNode.castShadow = true; + meshNode.name = ( name === "0" ? group.name : group.name + name ); + + if ( primitive.extras ) meshNode.userData = primitive.extras; + + group.add( meshNode ); + + } else if ( primitive.mode === WEBGL_CONSTANTS.LINES ) { + + var geometry = new BufferGeometry(); + + var attributes = primitive.attributes; + + for ( var attributeId in attributes ) { + + var attributeEntry = attributes[ attributeId ]; + + if ( ! attributeEntry ) return; + + var bufferAttribute = dependencies.accessors[ attributeEntry ]; + + switch ( attributeId ) { + + case 'POSITION': + geometry.addAttribute( 'position', bufferAttribute ); + break; + + case 'COLOR_0': + case 'COLOR0': + case 'COLOR': + geometry.addAttribute( 'color', bufferAttribute ); + break; + + } + + } + + var material = dependencies.materials[ primitive.material ]; + + var meshNode; + + if ( primitive.indices ) { + + geometry.setIndex( dependencies.accessors[ primitive.indices ] ); + + meshNode = new LineSegments( geometry, material ); + + } else { + + meshNode = new Line( geometry, material ); + + } + + meshNode.name = ( name === "0" ? group.name : group.name + name ); + + if ( primitive.extras ) meshNode.userData = primitive.extras; + + group.add( meshNode ); + + } else { + + console.warn( "Only triangular and line primitives are supported" ); + + } + + } + + return group; + + } ); + + } ); + +}; + +GLTFParser.prototype.loadCameras = function () { + + var json = this.json; + + return _each( json.cameras, function ( camera ) { + + if ( camera.type == "perspective" && camera.perspective ) { + + var yfov = camera.perspective.yfov; + var aspectRatio = camera.perspective.aspectRatio !== undefined ? camera.perspective.aspectRatio : 1; + + // According to COLLADA spec... + // aspectRatio = xfov / yfov + var xfov = yfov * aspectRatio; + + var _camera = new PerspectiveCamera( Math.radToDeg( xfov ), aspectRatio, camera.perspective.znear || 1, camera.perspective.zfar || 2e6 ); + if ( camera.name !== undefined ) _camera.name = camera.name; + + if ( camera.extras ) _camera.userData = camera.extras; + + return _camera; + + } else if ( camera.type == "orthographic" && camera.orthographic ) { + + var _camera = new OrthographicCamera( window.innerWidth / - 2, window.innerWidth / 2, window.innerHeight / 2, window.innerHeight / - 2, camera.orthographic.znear, camera.orthographic.zfar ); + if ( camera.name !== undefined ) _camera.name = camera.name; + + if ( camera.extras ) _camera.userData = camera.extras; + + return _camera; + + } + + } ); + +}; + +GLTFParser.prototype.loadSkins = function () { + + var json = this.json; + + return this._withDependencies( [ + + "accessors" + + ] ).then( function ( dependencies ) { + + return _each( json.skins, function ( skin ) { + + var bindShapeMatrix = new Matrix4(); + + if ( skin.bindShapeMatrix !== undefined ) bindShapeMatrix.fromArray( skin.bindShapeMatrix ); + + var _skin = { + bindShapeMatrix: bindShapeMatrix, + jointNames: skin.jointNames, + inverseBindMatrices: dependencies.accessors[ skin.inverseBindMatrices ] + }; + + return _skin; + + } ); + + } ); + +}; + +GLTFParser.prototype.loadAnimations = function () { + + var json = this.json; + + return this._withDependencies( [ + + "accessors", + "nodes" + + ] ).then( function ( dependencies ) { + + return _each( json.animations, function ( animation, animationId ) { + + var tracks = []; + + for ( var channelId in animation.channels ) { + + var channel = animation.channels[ channelId ]; + var sampler = animation.samplers[ channel.sampler ]; + + if ( sampler ) { + + var target = channel.target; + var name = target.id; + var input = animation.parameters !== undefined ? animation.parameters[ sampler.input ] : sampler.input; + var output = animation.parameters !== undefined ? animation.parameters[ sampler.output ] : sampler.output; + + var inputAccessor = dependencies.accessors[ input ]; + var outputAccessor = dependencies.accessors[ output ]; + + var node = dependencies.nodes[ name ]; + + if ( node ) { + + node.updateMatrix(); + node.matrixAutoUpdate = true; + + var TypedKeyframeTrack = PATH_PROPERTIES[ target.path ] === PATH_PROPERTIES.rotation + ? QuaternionKeyframeTrack + : VectorKeyframeTrack; + + var targetName = node.name ? node.name : node.uuid; + var interpolation = sampler.interpolation !== undefined ? INTERPOLATION[ sampler.interpolation ] : InterpolateLinear; + + // KeyframeTrack.optimize() will modify given 'times' and 'values' + // buffers before creating a truncated copy to keep. Because buffers may + // be reused by other tracks, make copies here. + tracks.push( new TypedKeyframeTrack( + targetName + '.' + PATH_PROPERTIES[ target.path ], + AnimationUtils.arraySlice( inputAccessor.array, 0 ), + AnimationUtils.arraySlice( outputAccessor.array, 0 ), + interpolation + ) ); + + } + + } + + } + + var name = animation.name !== undefined ? animation.name : "animation_" + animationId; + + return new AnimationClip( name, undefined, tracks ); + + } ); + + } ); + +}; + +GLTFParser.prototype.loadNodes = function () { + + var json = this.json; + var extensions = this.extensions; + var scope = this; + + return _each( json.nodes, function ( node ) { + + var matrix = new Matrix4(); + + var _node; + + if ( node.jointName ) { + + _node = new Bone(); + _node.name = node.name !== undefined ? node.name : node.jointName; + _node.jointName = node.jointName; + + } else { + + _node = new Object3D(); + if ( node.name !== undefined ) _node.name = node.name; + + } + + if ( node.extras ) _node.userData = node.extras; + + if ( node.matrix !== undefined ) { + + matrix.fromArray( node.matrix ); + _node.applyMatrix( matrix ); + + } else { + + if ( node.translation !== undefined ) { + + _node.position.fromArray( node.translation ); + + } + + if ( node.rotation !== undefined ) { + + _node.quaternion.fromArray( node.rotation ); + + } + + if ( node.scale !== undefined ) { + + _node.scale.fromArray( node.scale ); + + } + + } + + return _node; + + } ).then( function ( __nodes ) { + + return scope._withDependencies( [ + + "meshes", + "skins", + "cameras" + + ] ).then( function ( dependencies ) { + + return _each( __nodes, function ( _node, nodeId ) { + + var node = json.nodes[ nodeId ]; + + if ( node.meshes !== undefined ) { + + for ( var meshId in node.meshes ) { + + var mesh = node.meshes[ meshId ]; + var group = dependencies.meshes[ mesh ]; + + if ( group === undefined ) { + + console.warn( 'LegacyGLTFLoader: Couldn\'t find node "' + mesh + '".' ); + continue; + + } + + for ( var childrenId in group.children ) { + + var child = group.children[ childrenId ]; + + // clone Mesh to add to _node + + var originalMaterial = child.material; + var originalGeometry = child.geometry; + var originalUserData = child.userData; + var originalName = child.name; + + var material; + + if ( originalMaterial.isDeferredShaderMaterial ) { + + originalMaterial = material = originalMaterial.create(); + + } else { + + material = originalMaterial; + + } + + switch ( child.type ) { + + case 'LineSegments': + child = new LineSegments( originalGeometry, material ); + break; + + case 'LineLoop': + child = new LineLoop( originalGeometry, material ); + break; + + case 'Line': + child = new Line( originalGeometry, material ); + break; + + default: + child = new Mesh( originalGeometry, material ); + + } + + child.castShadow = true; + child.userData = originalUserData; + child.name = originalName; + + var skinEntry; + + if ( node.skin ) { + + skinEntry = dependencies.skins[ node.skin ]; + + } + + // Replace Mesh with SkinnedMesh in library + if ( skinEntry ) { + + var getJointNode = function ( jointId ) { + + var keys = Object.keys( __nodes ); + + for ( var i = 0, il = keys.length; i < il; i ++ ) { + + var n = __nodes[ keys[ i ] ]; + + if ( n.jointName === jointId ) return n; + + } + + return null; + + }; + + var geometry = originalGeometry; + var material = originalMaterial; + material.skinning = true; + + child = new SkinnedMesh( geometry, material ); + child.castShadow = true; + child.userData = originalUserData; + child.name = originalName; + + var bones = []; + var boneInverses = []; + + for ( var i = 0, l = skinEntry.jointNames.length; i < l; i ++ ) { + + var jointId = skinEntry.jointNames[ i ]; + var jointNode = getJointNode( jointId ); + + if ( jointNode ) { + + bones.push( jointNode ); + + var m = skinEntry.inverseBindMatrices.array; + var mat = new Matrix4().fromArray( m, i * 16 ); + boneInverses.push( mat ); + + } else { + + console.warn( "WARNING: joint: '" + jointId + "' could not be found" ); + + } + + } + + child.bind( new Skeleton( bones, boneInverses ), skinEntry.bindShapeMatrix ); + + var buildBoneGraph = function ( parentJson, parentObject, property ) { + + var children = parentJson[ property ]; + + if ( children === undefined ) return; + + for ( var i = 0, il = children.length; i < il; i ++ ) { + + var nodeId = children[ i ]; + var bone = __nodes[ nodeId ]; + var boneJson = json.nodes[ nodeId ]; + + if ( bone !== undefined && bone.isBone === true && boneJson !== undefined ) { + + parentObject.add( bone ); + buildBoneGraph( boneJson, bone, 'children' ); + + } + + } + + }; + + buildBoneGraph( node, child, 'skeletons' ); + + } + + _node.add( child ); + + } + + } + + } + + if ( node.camera !== undefined ) { + + var camera = dependencies.cameras[ node.camera ]; + + _node.add( camera ); + + } + + if ( node.extensions + && node.extensions[ EXTENSIONS.KHR_MATERIALS_COMMON ] + && node.extensions[ EXTENSIONS.KHR_MATERIALS_COMMON ].light ) { + + var extensionLights = extensions[ EXTENSIONS.KHR_MATERIALS_COMMON ].lights; + var light = extensionLights[ node.extensions[ EXTENSIONS.KHR_MATERIALS_COMMON ].light ]; + + _node.add( light ); + + } + + return _node; + + } ); + + } ); + + } ); + +}; + +GLTFParser.prototype.loadScenes = function () { + + var json = this.json; + + // scene node hierachy builder + + function buildNodeHierachy( nodeId, parentObject, allNodes ) { + + var _node = allNodes[ nodeId ]; + parentObject.add( _node ); + + var node = json.nodes[ nodeId ]; + + if ( node.children ) { + + var children = node.children; + + for ( var i = 0, l = children.length; i < l; i ++ ) { + + var child = children[ i ]; + buildNodeHierachy( child, _node, allNodes ); + + } + + } + + } + + return this._withDependencies( [ + + "nodes" + + ] ).then( function ( dependencies ) { + + return _each( json.scenes, function ( scene ) { + + var _scene = new Scene(); + if ( scene.name !== undefined ) _scene.name = scene.name; + + if ( scene.extras ) _scene.userData = scene.extras; + + var nodes = scene.nodes || []; + + for ( var i = 0, l = nodes.length; i < l; i ++ ) { + + var nodeId = nodes[ i ]; + buildNodeHierachy( nodeId, _scene, dependencies.nodes ); + + } + + _scene.traverse( function ( child ) { + + // Register raw material meshes with LegacyGLTFLoader.Shaders + if ( child.material && child.material.isRawShaderMaterial ) { + + child.gltfShader = new GLTFShader( child, dependencies.nodes ); + child.onBeforeRender = function ( renderer, scene, camera ) { + + this.gltfShader.update( scene, camera ); + + }; + + } + + } ); + + return _scene; + + } ); + + } ); + +}; + +export { LegacyGLTFLoader }; diff --git a/examples/jsm/loaders/deprecated/LegacyJSONLoader.js b/examples/jsm/loaders/deprecated/LegacyJSONLoader.js new file mode 100644 index 00000000000000..65e7f7f51f4faf --- /dev/null +++ b/examples/jsm/loaders/deprecated/LegacyJSONLoader.js @@ -0,0 +1,587 @@ +/** + * @author mrdoob / http://mrdoob.com/ + * @author alteredq / http://alteredqualia.com/ + */ + +import { + AnimationClip, + Color, + DefaultLoadingManager, + Face3, + FileLoader, + Geometry, + LoaderUtils, + Vector2, + Vector3, + Vector4, +} from "../../../../build/three.module.js"; + +function LegacyJSONLoader( manager ) { + + if ( typeof manager === 'boolean' ) { + + console.warn( 'THREE.JSONLoader: showStatus parameter has been removed from constructor.' ); + manager = undefined; + + } + + this.manager = ( manager !== undefined ) ? manager : DefaultLoadingManager; + + this.withCredentials = false; + +} + +Object.assign( LegacyJSONLoader.prototype, { + + crossOrigin: 'anonymous', + + load: function ( url, onLoad, onProgress, onError ) { + + var scope = this; + + var path = ( this.path === undefined ) ? LoaderUtils.extractUrlBase( url ) : this.path; + + var loader = new FileLoader( this.manager ); + loader.setPath( this.path ); + loader.setWithCredentials( this.withCredentials ); + loader.load( url, function ( text ) { + + var json = JSON.parse( text ); + var metadata = json.metadata; + + if ( metadata !== undefined ) { + + var type = metadata.type; + + if ( type !== undefined ) { + + if ( type.toLowerCase() === 'object' ) { + + console.error( 'THREE.JSONLoader: ' + url + ' should be loaded with THREE.ObjectLoader instead.' ); + return; + + } + + } + + } + + var object = scope.parse( json, path ); + onLoad( object.geometry, object.materials ); + + }, onProgress, onError ); + + }, + + setPath: function ( value ) { + + this.path = value; + return this; + + }, + + setResourcePath: function ( value ) { + + this.resourcePath = value; + return this; + + }, + + setCrossOrigin: function ( value ) { + + this.crossOrigin = value; + return this; + + }, + + parse: ( function () { + + function parseModel( json, geometry ) { + + function isBitSet( value, position ) { + + return value & ( 1 << position ); + + } + + var i, j, fi, + + offset, zLength, + + colorIndex, normalIndex, uvIndex, materialIndex, + + type, + isQuad, + hasMaterial, + hasFaceVertexUv, + hasFaceNormal, hasFaceVertexNormal, + hasFaceColor, hasFaceVertexColor, + + vertex, face, faceA, faceB, hex, normal, + + uvLayer, uv, u, v, + + faces = json.faces, + vertices = json.vertices, + normals = json.normals, + colors = json.colors, + + scale = json.scale, + + nUvLayers = 0; + + + if ( json.uvs !== undefined ) { + + // disregard empty arrays + + for ( i = 0; i < json.uvs.length; i ++ ) { + + if ( json.uvs[ i ].length ) nUvLayers ++; + + } + + for ( i = 0; i < nUvLayers; i ++ ) { + + geometry.faceVertexUvs[ i ] = []; + + } + + } + + offset = 0; + zLength = vertices.length; + + while ( offset < zLength ) { + + vertex = new Vector3(); + + vertex.x = vertices[ offset ++ ] * scale; + vertex.y = vertices[ offset ++ ] * scale; + vertex.z = vertices[ offset ++ ] * scale; + + geometry.vertices.push( vertex ); + + } + + offset = 0; + zLength = faces.length; + + while ( offset < zLength ) { + + type = faces[ offset ++ ]; + + isQuad = isBitSet( type, 0 ); + hasMaterial = isBitSet( type, 1 ); + hasFaceVertexUv = isBitSet( type, 3 ); + hasFaceNormal = isBitSet( type, 4 ); + hasFaceVertexNormal = isBitSet( type, 5 ); + hasFaceColor = isBitSet( type, 6 ); + hasFaceVertexColor = isBitSet( type, 7 ); + + // console.log("type", type, "bits", isQuad, hasMaterial, hasFaceVertexUv, hasFaceNormal, hasFaceVertexNormal, hasFaceColor, hasFaceVertexColor); + + if ( isQuad ) { + + faceA = new Face3(); + faceA.a = faces[ offset ]; + faceA.b = faces[ offset + 1 ]; + faceA.c = faces[ offset + 3 ]; + + faceB = new Face3(); + faceB.a = faces[ offset + 1 ]; + faceB.b = faces[ offset + 2 ]; + faceB.c = faces[ offset + 3 ]; + + offset += 4; + + if ( hasMaterial ) { + + materialIndex = faces[ offset ++ ]; + faceA.materialIndex = materialIndex; + faceB.materialIndex = materialIndex; + + } + + // to get face <=> uv index correspondence + + fi = geometry.faces.length; + + if ( hasFaceVertexUv ) { + + for ( i = 0; i < nUvLayers; i ++ ) { + + uvLayer = json.uvs[ i ]; + + geometry.faceVertexUvs[ i ][ fi ] = []; + geometry.faceVertexUvs[ i ][ fi + 1 ] = []; + + for ( j = 0; j < 4; j ++ ) { + + uvIndex = faces[ offset ++ ]; + + u = uvLayer[ uvIndex * 2 ]; + v = uvLayer[ uvIndex * 2 + 1 ]; + + uv = new Vector2( u, v ); + + if ( j !== 2 ) geometry.faceVertexUvs[ i ][ fi ].push( uv ); + if ( j !== 0 ) geometry.faceVertexUvs[ i ][ fi + 1 ].push( uv ); + + } + + } + + } + + if ( hasFaceNormal ) { + + normalIndex = faces[ offset ++ ] * 3; + + faceA.normal.set( + normals[ normalIndex ++ ], + normals[ normalIndex ++ ], + normals[ normalIndex ] + ); + + faceB.normal.copy( faceA.normal ); + + } + + if ( hasFaceVertexNormal ) { + + for ( i = 0; i < 4; i ++ ) { + + normalIndex = faces[ offset ++ ] * 3; + + normal = new Vector3( + normals[ normalIndex ++ ], + normals[ normalIndex ++ ], + normals[ normalIndex ] + ); + + + if ( i !== 2 ) faceA.vertexNormals.push( normal ); + if ( i !== 0 ) faceB.vertexNormals.push( normal ); + + } + + } + + + if ( hasFaceColor ) { + + colorIndex = faces[ offset ++ ]; + hex = colors[ colorIndex ]; + + faceA.color.setHex( hex ); + faceB.color.setHex( hex ); + + } + + + if ( hasFaceVertexColor ) { + + for ( i = 0; i < 4; i ++ ) { + + colorIndex = faces[ offset ++ ]; + hex = colors[ colorIndex ]; + + if ( i !== 2 ) faceA.vertexColors.push( new Color( hex ) ); + if ( i !== 0 ) faceB.vertexColors.push( new Color( hex ) ); + + } + + } + + geometry.faces.push( faceA ); + geometry.faces.push( faceB ); + + } else { + + face = new Face3(); + face.a = faces[ offset ++ ]; + face.b = faces[ offset ++ ]; + face.c = faces[ offset ++ ]; + + if ( hasMaterial ) { + + materialIndex = faces[ offset ++ ]; + face.materialIndex = materialIndex; + + } + + // to get face <=> uv index correspondence + + fi = geometry.faces.length; + + if ( hasFaceVertexUv ) { + + for ( i = 0; i < nUvLayers; i ++ ) { + + uvLayer = json.uvs[ i ]; + + geometry.faceVertexUvs[ i ][ fi ] = []; + + for ( j = 0; j < 3; j ++ ) { + + uvIndex = faces[ offset ++ ]; + + u = uvLayer[ uvIndex * 2 ]; + v = uvLayer[ uvIndex * 2 + 1 ]; + + uv = new Vector2( u, v ); + + geometry.faceVertexUvs[ i ][ fi ].push( uv ); + + } + + } + + } + + if ( hasFaceNormal ) { + + normalIndex = faces[ offset ++ ] * 3; + + face.normal.set( + normals[ normalIndex ++ ], + normals[ normalIndex ++ ], + normals[ normalIndex ] + ); + + } + + if ( hasFaceVertexNormal ) { + + for ( i = 0; i < 3; i ++ ) { + + normalIndex = faces[ offset ++ ] * 3; + + normal = new Vector3( + normals[ normalIndex ++ ], + normals[ normalIndex ++ ], + normals[ normalIndex ] + ); + + face.vertexNormals.push( normal ); + + } + + } + + + if ( hasFaceColor ) { + + colorIndex = faces[ offset ++ ]; + face.color.setHex( colors[ colorIndex ] ); + + } + + + if ( hasFaceVertexColor ) { + + for ( i = 0; i < 3; i ++ ) { + + colorIndex = faces[ offset ++ ]; + face.vertexColors.push( new Color( colors[ colorIndex ] ) ); + + } + + } + + geometry.faces.push( face ); + + } + + } + + } + + function parseSkin( json, geometry ) { + + var influencesPerVertex = ( json.influencesPerVertex !== undefined ) ? json.influencesPerVertex : 2; + + if ( json.skinWeights ) { + + for ( var i = 0, l = json.skinWeights.length; i < l; i += influencesPerVertex ) { + + var x = json.skinWeights[ i ]; + var y = ( influencesPerVertex > 1 ) ? json.skinWeights[ i + 1 ] : 0; + var z = ( influencesPerVertex > 2 ) ? json.skinWeights[ i + 2 ] : 0; + var w = ( influencesPerVertex > 3 ) ? json.skinWeights[ i + 3 ] : 0; + + geometry.skinWeights.push( new Vector4( x, y, z, w ) ); + + } + + } + + if ( json.skinIndices ) { + + for ( var i = 0, l = json.skinIndices.length; i < l; i += influencesPerVertex ) { + + var a = json.skinIndices[ i ]; + var b = ( influencesPerVertex > 1 ) ? json.skinIndices[ i + 1 ] : 0; + var c = ( influencesPerVertex > 2 ) ? json.skinIndices[ i + 2 ] : 0; + var d = ( influencesPerVertex > 3 ) ? json.skinIndices[ i + 3 ] : 0; + + geometry.skinIndices.push( new Vector4( a, b, c, d ) ); + + } + + } + + geometry.bones = json.bones; + + if ( geometry.bones && geometry.bones.length > 0 && ( geometry.skinWeights.length !== geometry.skinIndices.length || geometry.skinIndices.length !== geometry.vertices.length ) ) { + + console.warn( 'When skinning, number of vertices (' + geometry.vertices.length + '), skinIndices (' + + geometry.skinIndices.length + '), and skinWeights (' + geometry.skinWeights.length + ') should match.' ); + + } + + } + + function parseMorphing( json, geometry ) { + + var scale = json.scale; + + if ( json.morphTargets !== undefined ) { + + for ( var i = 0, l = json.morphTargets.length; i < l; i ++ ) { + + geometry.morphTargets[ i ] = {}; + geometry.morphTargets[ i ].name = json.morphTargets[ i ].name; + geometry.morphTargets[ i ].vertices = []; + + var dstVertices = geometry.morphTargets[ i ].vertices; + var srcVertices = json.morphTargets[ i ].vertices; + + for ( var v = 0, vl = srcVertices.length; v < vl; v += 3 ) { + + var vertex = new Vector3(); + vertex.x = srcVertices[ v ] * scale; + vertex.y = srcVertices[ v + 1 ] * scale; + vertex.z = srcVertices[ v + 2 ] * scale; + + dstVertices.push( vertex ); + + } + + } + + } + + if ( json.morphColors !== undefined && json.morphColors.length > 0 ) { + + console.warn( 'THREE.JSONLoader: "morphColors" no longer supported. Using them as face colors.' ); + + var faces = geometry.faces; + var morphColors = json.morphColors[ 0 ].colors; + + for ( var i = 0, l = faces.length; i < l; i ++ ) { + + faces[ i ].color.fromArray( morphColors, i * 3 ); + + } + + } + + } + + function parseAnimations( json, geometry ) { + + var outputAnimations = []; + + // parse old style Bone/Hierarchy animations + var animations = []; + + if ( json.animation !== undefined ) { + + animations.push( json.animation ); + + } + + if ( json.animations !== undefined ) { + + if ( json.animations.length ) { + + animations = animations.concat( json.animations ); + + } else { + + animations.push( json.animations ); + + } + + } + + for ( var i = 0; i < animations.length; i ++ ) { + + var clip = AnimationClip.parseAnimation( animations[ i ], geometry.bones ); + if ( clip ) outputAnimations.push( clip ); + + } + + // parse implicit morph animations + if ( geometry.morphTargets ) { + + // TODO: Figure out what an appropraite FPS is for morph target animations -- defaulting to 10, but really it is completely arbitrary. + var morphAnimationClips = AnimationClip.CreateClipsFromMorphTargetSequences( geometry.morphTargets, 10 ); + outputAnimations = outputAnimations.concat( morphAnimationClips ); + + } + + if ( outputAnimations.length > 0 ) geometry.animations = outputAnimations; + + } + + return function parse( json, path ) { + + if ( json.data !== undefined ) { + + // Geometry 4.0 spec + json = json.data; + + } + + if ( json.scale !== undefined ) { + + json.scale = 1.0 / json.scale; + + } else { + + json.scale = 1.0; + + } + + var geometry = new Geometry(); + + parseModel( json, geometry ); + parseSkin( json, geometry ); + parseMorphing( json, geometry ); + parseAnimations( json, geometry ); + + geometry.computeFaceNormals(); + geometry.computeBoundingSphere(); + + if ( json.materials === undefined || json.materials.length === 0 ) { + + return { geometry: geometry }; + + } else { + + var materials = Loader.prototype.initMaterials( json.materials, this.resourcePath || path, this.crossOrigin ); + + return { geometry: geometry, materials: materials }; + + } + + }; + + } )() + +} ); + +export { LegacyJSONLoader }; diff --git a/package-lock.json b/package-lock.json index 2659c0d3387de3..7b07cc8a593a06 100644 --- a/package-lock.json +++ b/package-lock.json @@ -2496,6 +2496,11 @@ } } }, + "mmd-parser": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/mmd-parser/-/mmd-parser-1.0.3.tgz", + "integrity": "sha1-fk5HXQdnKeWSw+a76SmNe4Qe0+0=" + }, "ms": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", diff --git a/package.json b/package.json index f8d18fd2fcfb8c..1ec2317cc0277e 100644 --- a/package.json +++ b/package.json @@ -74,5 +74,8 @@ "build/three.module.js" ], "directories": {} + }, + "dependencies": { + "mmd-parser": "^1.0.3" } } From d951984ead0d5798ae63c01996ce33a4e4f50b47 Mon Sep 17 00:00:00 2001 From: Deep Date: Mon, 28 Jan 2019 16:46:03 +0530 Subject: [PATCH 2/3] Loaders: Clean up. Math and import fixes --- examples/jsm/libs/mmdparser.js | 11533 ++++++++++++++++ examples/jsm/loaders/3MFLoader.js | 1 + examples/jsm/loaders/BabylonLoader.js | 1 + examples/jsm/loaders/ColladaLoader.js | 10 +- examples/jsm/loaders/FBXLoader.js | 17 +- examples/jsm/loaders/GLTFLoader.js | 4 +- examples/jsm/loaders/LDrawLoader.js | 2 +- examples/jsm/loaders/MMDLoader.js | 9 +- examples/jsm/loaders/MTLLoader.js | 1 + examples/jsm/loaders/OBJLoader.js | 4 +- examples/jsm/loaders/XLoader.js | 1 + .../loaders/deprecated/LegacyGLTFLoader.js | 6 +- package-lock.json | 5 - package.json | 3 - 14 files changed, 11560 insertions(+), 37 deletions(-) create mode 100644 examples/jsm/libs/mmdparser.js diff --git a/examples/jsm/libs/mmdparser.js b/examples/jsm/libs/mmdparser.js new file mode 100644 index 00000000000000..8e5322ebbe8fd4 --- /dev/null +++ b/examples/jsm/libs/mmdparser.js @@ -0,0 +1,11533 @@ +/** + * @author Takahiro / https://github.com/takahirox + * + * Simple CharsetEncoder. + */ + +function CharsetEncoder() { +} + +/* + * Converts from Shift_JIS Uint8Array data to Unicode strings. + */ +CharsetEncoder.prototype.s2u = function ( uint8Array ) { + + var t = this.s2uTable; + var str = ''; + var p = 0; + + while ( p < uint8Array.length ) { + + var key = uint8Array[ p ++ ]; + + if ( ! ( ( key >= 0x00 && key <= 0x7e ) || + ( key >= 0xa1 && key <= 0xdf ) ) && + p < uint8Array.length ) { + + key = ( key << 8 ) | uint8Array[ p ++ ]; + + } + + if ( t[ key ] === undefined ) { + + throw 'unknown char code ' + key + '.'; + + } + + str += String.fromCharCode( t[ key ] ); + + } + + return str; + +}; + +CharsetEncoder.prototype.s2uTable = { + 0: 0, + 1: 1, + 2: 2, + 3: 3, + 4: 4, + 5: 5, + 6: 6, + 7: 7, + 8: 8, + 9: 9, + 10: 10, + 11: 11, + 12: 12, + 13: 13, + 14: 14, + 15: 15, + 16: 16, + 17: 17, + 18: 18, + 19: 19, + 20: 20, + 21: 21, + 22: 22, + 23: 23, + 24: 24, + 25: 25, + 26: 26, + 27: 27, + 28: 28, + 29: 29, + 30: 30, + 31: 31, + 32: 32, + 33: 33, + 34: 34, + 35: 35, + 36: 36, + 37: 37, + 38: 38, + 39: 39, + 40: 40, + 41: 41, + 42: 42, + 43: 43, + 44: 44, + 45: 45, + 46: 46, + 47: 47, + 48: 48, + 49: 49, + 50: 50, + 51: 51, + 52: 52, + 53: 53, + 54: 54, + 55: 55, + 56: 56, + 57: 57, + 58: 58, + 59: 59, + 60: 60, + 61: 61, + 62: 62, + 63: 63, + 64: 64, + 65: 65, + 66: 66, + 67: 67, + 68: 68, + 69: 69, + 70: 70, + 71: 71, + 72: 72, + 73: 73, + 74: 74, + 75: 75, + 76: 76, + 77: 77, + 78: 78, + 79: 79, + 80: 80, + 81: 81, + 82: 82, + 83: 83, + 84: 84, + 85: 85, + 86: 86, + 87: 87, + 88: 88, + 89: 89, + 90: 90, + 91: 91, + 92: 92, + 93: 93, + 94: 94, + 95: 95, + 96: 96, + 97: 97, + 98: 98, + 99: 99, + 100: 100, + 101: 101, + 102: 102, + 103: 103, + 104: 104, + 105: 105, + 106: 106, + 107: 107, + 108: 108, + 109: 109, + 110: 110, + 111: 111, + 112: 112, + 113: 113, + 114: 114, + 115: 115, + 116: 116, + 117: 117, + 118: 118, + 119: 119, + 120: 120, + 121: 121, + 122: 122, + 123: 123, + 124: 124, + 125: 125, + 126: 126, + 161: 65377, + 162: 65378, + 163: 65379, + 164: 65380, + 165: 65381, + 166: 65382, + 167: 65383, + 168: 65384, + 169: 65385, + 170: 65386, + 171: 65387, + 172: 65388, + 173: 65389, + 174: 65390, + 175: 65391, + 176: 65392, + 177: 65393, + 178: 65394, + 179: 65395, + 180: 65396, + 181: 65397, + 182: 65398, + 183: 65399, + 184: 65400, + 185: 65401, + 186: 65402, + 187: 65403, + 188: 65404, + 189: 65405, + 190: 65406, + 191: 65407, + 192: 65408, + 193: 65409, + 194: 65410, + 195: 65411, + 196: 65412, + 197: 65413, + 198: 65414, + 199: 65415, + 200: 65416, + 201: 65417, + 202: 65418, + 203: 65419, + 204: 65420, + 205: 65421, + 206: 65422, + 207: 65423, + 208: 65424, + 209: 65425, + 210: 65426, + 211: 65427, + 212: 65428, + 213: 65429, + 214: 65430, + 215: 65431, + 216: 65432, + 217: 65433, + 218: 65434, + 219: 65435, + 220: 65436, + 221: 65437, + 222: 65438, + 223: 65439, + 33088: 12288, + 33089: 12289, + 33090: 12290, + 33091: 65292, + 33092: 65294, + 33093: 12539, + 33094: 65306, + 33095: 65307, + 33096: 65311, + 33097: 65281, + 33098: 12443, + 33099: 12444, + 33100: 180, + 33101: 65344, + 33102: 168, + 33103: 65342, + 33104: 65507, + 33105: 65343, + 33106: 12541, + 33107: 12542, + 33108: 12445, + 33109: 12446, + 33110: 12291, + 33111: 20189, + 33112: 12293, + 33113: 12294, + 33114: 12295, + 33115: 12540, + 33116: 8213, + 33117: 8208, + 33118: 65295, + 33119: 65340, + 33120: 65374, + 33121: 8741, + 33122: 65372, + 33123: 8230, + 33124: 8229, + 33125: 8216, + 33126: 8217, + 33127: 8220, + 33128: 8221, + 33129: 65288, + 33130: 65289, + 33131: 12308, + 33132: 12309, + 33133: 65339, + 33134: 65341, + 33135: 65371, + 33136: 65373, + 33137: 12296, + 33138: 12297, + 33139: 12298, + 33140: 12299, + 33141: 12300, + 33142: 12301, + 33143: 12302, + 33144: 12303, + 33145: 12304, + 33146: 12305, + 33147: 65291, + 33148: 65293, + 33149: 177, + 33150: 215, + 33152: 247, + 33153: 65309, + 33154: 8800, + 33155: 65308, + 33156: 65310, + 33157: 8806, + 33158: 8807, + 33159: 8734, + 33160: 8756, + 33161: 9794, + 33162: 9792, + 33163: 176, + 33164: 8242, + 33165: 8243, + 33166: 8451, + 33167: 65509, + 33168: 65284, + 33169: 65504, + 33170: 65505, + 33171: 65285, + 33172: 65283, + 33173: 65286, + 33174: 65290, + 33175: 65312, + 33176: 167, + 33177: 9734, + 33178: 9733, + 33179: 9675, + 33180: 9679, + 33181: 9678, + 33182: 9671, + 33183: 9670, + 33184: 9633, + 33185: 9632, + 33186: 9651, + 33187: 9650, + 33188: 9661, + 33189: 9660, + 33190: 8251, + 33191: 12306, + 33192: 8594, + 33193: 8592, + 33194: 8593, + 33195: 8595, + 33196: 12307, + 33208: 8712, + 33209: 8715, + 33210: 8838, + 33211: 8839, + 33212: 8834, + 33213: 8835, + 33214: 8746, + 33215: 8745, + 33224: 8743, + 33225: 8744, + 33226: 65506, + 33227: 8658, + 33228: 8660, + 33229: 8704, + 33230: 8707, + 33242: 8736, + 33243: 8869, + 33244: 8978, + 33245: 8706, + 33246: 8711, + 33247: 8801, + 33248: 8786, + 33249: 8810, + 33250: 8811, + 33251: 8730, + 33252: 8765, + 33253: 8733, + 33254: 8757, + 33255: 8747, + 33256: 8748, + 33264: 8491, + 33265: 8240, + 33266: 9839, + 33267: 9837, + 33268: 9834, + 33269: 8224, + 33270: 8225, + 33271: 182, + 33276: 9711, + 33359: 65296, + 33360: 65297, + 33361: 65298, + 33362: 65299, + 33363: 65300, + 33364: 65301, + 33365: 65302, + 33366: 65303, + 33367: 65304, + 33368: 65305, + 33376: 65313, + 33377: 65314, + 33378: 65315, + 33379: 65316, + 33380: 65317, + 33381: 65318, + 33382: 65319, + 33383: 65320, + 33384: 65321, + 33385: 65322, + 33386: 65323, + 33387: 65324, + 33388: 65325, + 33389: 65326, + 33390: 65327, + 33391: 65328, + 33392: 65329, + 33393: 65330, + 33394: 65331, + 33395: 65332, + 33396: 65333, + 33397: 65334, + 33398: 65335, + 33399: 65336, + 33400: 65337, + 33401: 65338, + 33409: 65345, + 33410: 65346, + 33411: 65347, + 33412: 65348, + 33413: 65349, + 33414: 65350, + 33415: 65351, + 33416: 65352, + 33417: 65353, + 33418: 65354, + 33419: 65355, + 33420: 65356, + 33421: 65357, + 33422: 65358, + 33423: 65359, + 33424: 65360, + 33425: 65361, + 33426: 65362, + 33427: 65363, + 33428: 65364, + 33429: 65365, + 33430: 65366, + 33431: 65367, + 33432: 65368, + 33433: 65369, + 33434: 65370, + 33439: 12353, + 33440: 12354, + 33441: 12355, + 33442: 12356, + 33443: 12357, + 33444: 12358, + 33445: 12359, + 33446: 12360, + 33447: 12361, + 33448: 12362, + 33449: 12363, + 33450: 12364, + 33451: 12365, + 33452: 12366, + 33453: 12367, + 33454: 12368, + 33455: 12369, + 33456: 12370, + 33457: 12371, + 33458: 12372, + 33459: 12373, + 33460: 12374, + 33461: 12375, + 33462: 12376, + 33463: 12377, + 33464: 12378, + 33465: 12379, + 33466: 12380, + 33467: 12381, + 33468: 12382, + 33469: 12383, + 33470: 12384, + 33471: 12385, + 33472: 12386, + 33473: 12387, + 33474: 12388, + 33475: 12389, + 33476: 12390, + 33477: 12391, + 33478: 12392, + 33479: 12393, + 33480: 12394, + 33481: 12395, + 33482: 12396, + 33483: 12397, + 33484: 12398, + 33485: 12399, + 33486: 12400, + 33487: 12401, + 33488: 12402, + 33489: 12403, + 33490: 12404, + 33491: 12405, + 33492: 12406, + 33493: 12407, + 33494: 12408, + 33495: 12409, + 33496: 12410, + 33497: 12411, + 33498: 12412, + 33499: 12413, + 33500: 12414, + 33501: 12415, + 33502: 12416, + 33503: 12417, + 33504: 12418, + 33505: 12419, + 33506: 12420, + 33507: 12421, + 33508: 12422, + 33509: 12423, + 33510: 12424, + 33511: 12425, + 33512: 12426, + 33513: 12427, + 33514: 12428, + 33515: 12429, + 33516: 12430, + 33517: 12431, + 33518: 12432, + 33519: 12433, + 33520: 12434, + 33521: 12435, + 33600: 12449, + 33601: 12450, + 33602: 12451, + 33603: 12452, + 33604: 12453, + 33605: 12454, + 33606: 12455, + 33607: 12456, + 33608: 12457, + 33609: 12458, + 33610: 12459, + 33611: 12460, + 33612: 12461, + 33613: 12462, + 33614: 12463, + 33615: 12464, + 33616: 12465, + 33617: 12466, + 33618: 12467, + 33619: 12468, + 33620: 12469, + 33621: 12470, + 33622: 12471, + 33623: 12472, + 33624: 12473, + 33625: 12474, + 33626: 12475, + 33627: 12476, + 33628: 12477, + 33629: 12478, + 33630: 12479, + 33631: 12480, + 33632: 12481, + 33633: 12482, + 33634: 12483, + 33635: 12484, + 33636: 12485, + 33637: 12486, + 33638: 12487, + 33639: 12488, + 33640: 12489, + 33641: 12490, + 33642: 12491, + 33643: 12492, + 33644: 12493, + 33645: 12494, + 33646: 12495, + 33647: 12496, + 33648: 12497, + 33649: 12498, + 33650: 12499, + 33651: 12500, + 33652: 12501, + 33653: 12502, + 33654: 12503, + 33655: 12504, + 33656: 12505, + 33657: 12506, + 33658: 12507, + 33659: 12508, + 33660: 12509, + 33661: 12510, + 33662: 12511, + 33664: 12512, + 33665: 12513, + 33666: 12514, + 33667: 12515, + 33668: 12516, + 33669: 12517, + 33670: 12518, + 33671: 12519, + 33672: 12520, + 33673: 12521, + 33674: 12522, + 33675: 12523, + 33676: 12524, + 33677: 12525, + 33678: 12526, + 33679: 12527, + 33680: 12528, + 33681: 12529, + 33682: 12530, + 33683: 12531, + 33684: 12532, + 33685: 12533, + 33686: 12534, + 33695: 913, + 33696: 914, + 33697: 915, + 33698: 916, + 33699: 917, + 33700: 918, + 33701: 919, + 33702: 920, + 33703: 921, + 33704: 922, + 33705: 923, + 33706: 924, + 33707: 925, + 33708: 926, + 33709: 927, + 33710: 928, + 33711: 929, + 33712: 931, + 33713: 932, + 33714: 933, + 33715: 934, + 33716: 935, + 33717: 936, + 33718: 937, + 33727: 945, + 33728: 946, + 33729: 947, + 33730: 948, + 33731: 949, + 33732: 950, + 33733: 951, + 33734: 952, + 33735: 953, + 33736: 954, + 33737: 955, + 33738: 956, + 33739: 957, + 33740: 958, + 33741: 959, + 33742: 960, + 33743: 961, + 33744: 963, + 33745: 964, + 33746: 965, + 33747: 966, + 33748: 967, + 33749: 968, + 33750: 969, + 33856: 1040, + 33857: 1041, + 33858: 1042, + 33859: 1043, + 33860: 1044, + 33861: 1045, + 33862: 1025, + 33863: 1046, + 33864: 1047, + 33865: 1048, + 33866: 1049, + 33867: 1050, + 33868: 1051, + 33869: 1052, + 33870: 1053, + 33871: 1054, + 33872: 1055, + 33873: 1056, + 33874: 1057, + 33875: 1058, + 33876: 1059, + 33877: 1060, + 33878: 1061, + 33879: 1062, + 33880: 1063, + 33881: 1064, + 33882: 1065, + 33883: 1066, + 33884: 1067, + 33885: 1068, + 33886: 1069, + 33887: 1070, + 33888: 1071, + 33904: 1072, + 33905: 1073, + 33906: 1074, + 33907: 1075, + 33908: 1076, + 33909: 1077, + 33910: 1105, + 33911: 1078, + 33912: 1079, + 33913: 1080, + 33914: 1081, + 33915: 1082, + 33916: 1083, + 33917: 1084, + 33918: 1085, + 33920: 1086, + 33921: 1087, + 33922: 1088, + 33923: 1089, + 33924: 1090, + 33925: 1091, + 33926: 1092, + 33927: 1093, + 33928: 1094, + 33929: 1095, + 33930: 1096, + 33931: 1097, + 33932: 1098, + 33933: 1099, + 33934: 1100, + 33935: 1101, + 33936: 1102, + 33937: 1103, + 33951: 9472, + 33952: 9474, + 33953: 9484, + 33954: 9488, + 33955: 9496, + 33956: 9492, + 33957: 9500, + 33958: 9516, + 33959: 9508, + 33960: 9524, + 33961: 9532, + 33962: 9473, + 33963: 9475, + 33964: 9487, + 33965: 9491, + 33966: 9499, + 33967: 9495, + 33968: 9507, + 33969: 9523, + 33970: 9515, + 33971: 9531, + 33972: 9547, + 33973: 9504, + 33974: 9519, + 33975: 9512, + 33976: 9527, + 33977: 9535, + 33978: 9501, + 33979: 9520, + 33980: 9509, + 33981: 9528, + 33982: 9538, + 34624: 9312, + 34625: 9313, + 34626: 9314, + 34627: 9315, + 34628: 9316, + 34629: 9317, + 34630: 9318, + 34631: 9319, + 34632: 9320, + 34633: 9321, + 34634: 9322, + 34635: 9323, + 34636: 9324, + 34637: 9325, + 34638: 9326, + 34639: 9327, + 34640: 9328, + 34641: 9329, + 34642: 9330, + 34643: 9331, + 34644: 8544, + 34645: 8545, + 34646: 8546, + 34647: 8547, + 34648: 8548, + 34649: 8549, + 34650: 8550, + 34651: 8551, + 34652: 8552, + 34653: 8553, + 34655: 13129, + 34656: 13076, + 34657: 13090, + 34658: 13133, + 34659: 13080, + 34660: 13095, + 34661: 13059, + 34662: 13110, + 34663: 13137, + 34664: 13143, + 34665: 13069, + 34666: 13094, + 34667: 13091, + 34668: 13099, + 34669: 13130, + 34670: 13115, + 34671: 13212, + 34672: 13213, + 34673: 13214, + 34674: 13198, + 34675: 13199, + 34676: 13252, + 34677: 13217, + 34686: 13179, + 34688: 12317, + 34689: 12319, + 34690: 8470, + 34691: 13261, + 34692: 8481, + 34693: 12964, + 34694: 12965, + 34695: 12966, + 34696: 12967, + 34697: 12968, + 34698: 12849, + 34699: 12850, + 34700: 12857, + 34701: 13182, + 34702: 13181, + 34703: 13180, + 34704: 8786, + 34705: 8801, + 34706: 8747, + 34707: 8750, + 34708: 8721, + 34709: 8730, + 34710: 8869, + 34711: 8736, + 34712: 8735, + 34713: 8895, + 34714: 8757, + 34715: 8745, + 34716: 8746, + 34975: 20124, + 34976: 21782, + 34977: 23043, + 34978: 38463, + 34979: 21696, + 34980: 24859, + 34981: 25384, + 34982: 23030, + 34983: 36898, + 34984: 33909, + 34985: 33564, + 34986: 31312, + 34987: 24746, + 34988: 25569, + 34989: 28197, + 34990: 26093, + 34991: 33894, + 34992: 33446, + 34993: 39925, + 34994: 26771, + 34995: 22311, + 34996: 26017, + 34997: 25201, + 34998: 23451, + 34999: 22992, + 35000: 34427, + 35001: 39156, + 35002: 32098, + 35003: 32190, + 35004: 39822, + 35005: 25110, + 35006: 31903, + 35007: 34999, + 35008: 23433, + 35009: 24245, + 35010: 25353, + 35011: 26263, + 35012: 26696, + 35013: 38343, + 35014: 38797, + 35015: 26447, + 35016: 20197, + 35017: 20234, + 35018: 20301, + 35019: 20381, + 35020: 20553, + 35021: 22258, + 35022: 22839, + 35023: 22996, + 35024: 23041, + 35025: 23561, + 35026: 24799, + 35027: 24847, + 35028: 24944, + 35029: 26131, + 35030: 26885, + 35031: 28858, + 35032: 30031, + 35033: 30064, + 35034: 31227, + 35035: 32173, + 35036: 32239, + 35037: 32963, + 35038: 33806, + 35039: 34915, + 35040: 35586, + 35041: 36949, + 35042: 36986, + 35043: 21307, + 35044: 20117, + 35045: 20133, + 35046: 22495, + 35047: 32946, + 35048: 37057, + 35049: 30959, + 35050: 19968, + 35051: 22769, + 35052: 28322, + 35053: 36920, + 35054: 31282, + 35055: 33576, + 35056: 33419, + 35057: 39983, + 35058: 20801, + 35059: 21360, + 35060: 21693, + 35061: 21729, + 35062: 22240, + 35063: 23035, + 35064: 24341, + 35065: 39154, + 35066: 28139, + 35067: 32996, + 35068: 34093, + 35136: 38498, + 35137: 38512, + 35138: 38560, + 35139: 38907, + 35140: 21515, + 35141: 21491, + 35142: 23431, + 35143: 28879, + 35144: 32701, + 35145: 36802, + 35146: 38632, + 35147: 21359, + 35148: 40284, + 35149: 31418, + 35150: 19985, + 35151: 30867, + 35152: 33276, + 35153: 28198, + 35154: 22040, + 35155: 21764, + 35156: 27421, + 35157: 34074, + 35158: 39995, + 35159: 23013, + 35160: 21417, + 35161: 28006, + 35162: 29916, + 35163: 38287, + 35164: 22082, + 35165: 20113, + 35166: 36939, + 35167: 38642, + 35168: 33615, + 35169: 39180, + 35170: 21473, + 35171: 21942, + 35172: 23344, + 35173: 24433, + 35174: 26144, + 35175: 26355, + 35176: 26628, + 35177: 27704, + 35178: 27891, + 35179: 27945, + 35180: 29787, + 35181: 30408, + 35182: 31310, + 35183: 38964, + 35184: 33521, + 35185: 34907, + 35186: 35424, + 35187: 37613, + 35188: 28082, + 35189: 30123, + 35190: 30410, + 35191: 39365, + 35192: 24742, + 35193: 35585, + 35194: 36234, + 35195: 38322, + 35196: 27022, + 35197: 21421, + 35198: 20870, + 35200: 22290, + 35201: 22576, + 35202: 22852, + 35203: 23476, + 35204: 24310, + 35205: 24616, + 35206: 25513, + 35207: 25588, + 35208: 27839, + 35209: 28436, + 35210: 28814, + 35211: 28948, + 35212: 29017, + 35213: 29141, + 35214: 29503, + 35215: 32257, + 35216: 33398, + 35217: 33489, + 35218: 34199, + 35219: 36960, + 35220: 37467, + 35221: 40219, + 35222: 22633, + 35223: 26044, + 35224: 27738, + 35225: 29989, + 35226: 20985, + 35227: 22830, + 35228: 22885, + 35229: 24448, + 35230: 24540, + 35231: 25276, + 35232: 26106, + 35233: 27178, + 35234: 27431, + 35235: 27572, + 35236: 29579, + 35237: 32705, + 35238: 35158, + 35239: 40236, + 35240: 40206, + 35241: 40644, + 35242: 23713, + 35243: 27798, + 35244: 33659, + 35245: 20740, + 35246: 23627, + 35247: 25014, + 35248: 33222, + 35249: 26742, + 35250: 29281, + 35251: 20057, + 35252: 20474, + 35253: 21368, + 35254: 24681, + 35255: 28201, + 35256: 31311, + 35257: 38899, + 35258: 19979, + 35259: 21270, + 35260: 20206, + 35261: 20309, + 35262: 20285, + 35263: 20385, + 35264: 20339, + 35265: 21152, + 35266: 21487, + 35267: 22025, + 35268: 22799, + 35269: 23233, + 35270: 23478, + 35271: 23521, + 35272: 31185, + 35273: 26247, + 35274: 26524, + 35275: 26550, + 35276: 27468, + 35277: 27827, + 35278: 28779, + 35279: 29634, + 35280: 31117, + 35281: 31166, + 35282: 31292, + 35283: 31623, + 35284: 33457, + 35285: 33499, + 35286: 33540, + 35287: 33655, + 35288: 33775, + 35289: 33747, + 35290: 34662, + 35291: 35506, + 35292: 22057, + 35293: 36008, + 35294: 36838, + 35295: 36942, + 35296: 38686, + 35297: 34442, + 35298: 20420, + 35299: 23784, + 35300: 25105, + 35301: 29273, + 35302: 30011, + 35303: 33253, + 35304: 33469, + 35305: 34558, + 35306: 36032, + 35307: 38597, + 35308: 39187, + 35309: 39381, + 35310: 20171, + 35311: 20250, + 35312: 35299, + 35313: 22238, + 35314: 22602, + 35315: 22730, + 35316: 24315, + 35317: 24555, + 35318: 24618, + 35319: 24724, + 35320: 24674, + 35321: 25040, + 35322: 25106, + 35323: 25296, + 35324: 25913, + 35392: 39745, + 35393: 26214, + 35394: 26800, + 35395: 28023, + 35396: 28784, + 35397: 30028, + 35398: 30342, + 35399: 32117, + 35400: 33445, + 35401: 34809, + 35402: 38283, + 35403: 38542, + 35404: 35997, + 35405: 20977, + 35406: 21182, + 35407: 22806, + 35408: 21683, + 35409: 23475, + 35410: 23830, + 35411: 24936, + 35412: 27010, + 35413: 28079, + 35414: 30861, + 35415: 33995, + 35416: 34903, + 35417: 35442, + 35418: 37799, + 35419: 39608, + 35420: 28012, + 35421: 39336, + 35422: 34521, + 35423: 22435, + 35424: 26623, + 35425: 34510, + 35426: 37390, + 35427: 21123, + 35428: 22151, + 35429: 21508, + 35430: 24275, + 35431: 25313, + 35432: 25785, + 35433: 26684, + 35434: 26680, + 35435: 27579, + 35436: 29554, + 35437: 30906, + 35438: 31339, + 35439: 35226, + 35440: 35282, + 35441: 36203, + 35442: 36611, + 35443: 37101, + 35444: 38307, + 35445: 38548, + 35446: 38761, + 35447: 23398, + 35448: 23731, + 35449: 27005, + 35450: 38989, + 35451: 38990, + 35452: 25499, + 35453: 31520, + 35454: 27179, + 35456: 27263, + 35457: 26806, + 35458: 39949, + 35459: 28511, + 35460: 21106, + 35461: 21917, + 35462: 24688, + 35463: 25324, + 35464: 27963, + 35465: 28167, + 35466: 28369, + 35467: 33883, + 35468: 35088, + 35469: 36676, + 35470: 19988, + 35471: 39993, + 35472: 21494, + 35473: 26907, + 35474: 27194, + 35475: 38788, + 35476: 26666, + 35477: 20828, + 35478: 31427, + 35479: 33970, + 35480: 37340, + 35481: 37772, + 35482: 22107, + 35483: 40232, + 35484: 26658, + 35485: 33541, + 35486: 33841, + 35487: 31909, + 35488: 21000, + 35489: 33477, + 35490: 29926, + 35491: 20094, + 35492: 20355, + 35493: 20896, + 35494: 23506, + 35495: 21002, + 35496: 21208, + 35497: 21223, + 35498: 24059, + 35499: 21914, + 35500: 22570, + 35501: 23014, + 35502: 23436, + 35503: 23448, + 35504: 23515, + 35505: 24178, + 35506: 24185, + 35507: 24739, + 35508: 24863, + 35509: 24931, + 35510: 25022, + 35511: 25563, + 35512: 25954, + 35513: 26577, + 35514: 26707, + 35515: 26874, + 35516: 27454, + 35517: 27475, + 35518: 27735, + 35519: 28450, + 35520: 28567, + 35521: 28485, + 35522: 29872, + 35523: 29976, + 35524: 30435, + 35525: 30475, + 35526: 31487, + 35527: 31649, + 35528: 31777, + 35529: 32233, + 35530: 32566, + 35531: 32752, + 35532: 32925, + 35533: 33382, + 35534: 33694, + 35535: 35251, + 35536: 35532, + 35537: 36011, + 35538: 36996, + 35539: 37969, + 35540: 38291, + 35541: 38289, + 35542: 38306, + 35543: 38501, + 35544: 38867, + 35545: 39208, + 35546: 33304, + 35547: 20024, + 35548: 21547, + 35549: 23736, + 35550: 24012, + 35551: 29609, + 35552: 30284, + 35553: 30524, + 35554: 23721, + 35555: 32747, + 35556: 36107, + 35557: 38593, + 35558: 38929, + 35559: 38996, + 35560: 39000, + 35561: 20225, + 35562: 20238, + 35563: 21361, + 35564: 21916, + 35565: 22120, + 35566: 22522, + 35567: 22855, + 35568: 23305, + 35569: 23492, + 35570: 23696, + 35571: 24076, + 35572: 24190, + 35573: 24524, + 35574: 25582, + 35575: 26426, + 35576: 26071, + 35577: 26082, + 35578: 26399, + 35579: 26827, + 35580: 26820, + 35648: 27231, + 35649: 24112, + 35650: 27589, + 35651: 27671, + 35652: 27773, + 35653: 30079, + 35654: 31048, + 35655: 23395, + 35656: 31232, + 35657: 32000, + 35658: 24509, + 35659: 35215, + 35660: 35352, + 35661: 36020, + 35662: 36215, + 35663: 36556, + 35664: 36637, + 35665: 39138, + 35666: 39438, + 35667: 39740, + 35668: 20096, + 35669: 20605, + 35670: 20736, + 35671: 22931, + 35672: 23452, + 35673: 25135, + 35674: 25216, + 35675: 25836, + 35676: 27450, + 35677: 29344, + 35678: 30097, + 35679: 31047, + 35680: 32681, + 35681: 34811, + 35682: 35516, + 35683: 35696, + 35684: 25516, + 35685: 33738, + 35686: 38816, + 35687: 21513, + 35688: 21507, + 35689: 21931, + 35690: 26708, + 35691: 27224, + 35692: 35440, + 35693: 30759, + 35694: 26485, + 35695: 40653, + 35696: 21364, + 35697: 23458, + 35698: 33050, + 35699: 34384, + 35700: 36870, + 35701: 19992, + 35702: 20037, + 35703: 20167, + 35704: 20241, + 35705: 21450, + 35706: 21560, + 35707: 23470, + 35708: 24339, + 35709: 24613, + 35710: 25937, + 35712: 26429, + 35713: 27714, + 35714: 27762, + 35715: 27875, + 35716: 28792, + 35717: 29699, + 35718: 31350, + 35719: 31406, + 35720: 31496, + 35721: 32026, + 35722: 31998, + 35723: 32102, + 35724: 26087, + 35725: 29275, + 35726: 21435, + 35727: 23621, + 35728: 24040, + 35729: 25298, + 35730: 25312, + 35731: 25369, + 35732: 28192, + 35733: 34394, + 35734: 35377, + 35735: 36317, + 35736: 37624, + 35737: 28417, + 35738: 31142, + 35739: 39770, + 35740: 20136, + 35741: 20139, + 35742: 20140, + 35743: 20379, + 35744: 20384, + 35745: 20689, + 35746: 20807, + 35747: 31478, + 35748: 20849, + 35749: 20982, + 35750: 21332, + 35751: 21281, + 35752: 21375, + 35753: 21483, + 35754: 21932, + 35755: 22659, + 35756: 23777, + 35757: 24375, + 35758: 24394, + 35759: 24623, + 35760: 24656, + 35761: 24685, + 35762: 25375, + 35763: 25945, + 35764: 27211, + 35765: 27841, + 35766: 29378, + 35767: 29421, + 35768: 30703, + 35769: 33016, + 35770: 33029, + 35771: 33288, + 35772: 34126, + 35773: 37111, + 35774: 37857, + 35775: 38911, + 35776: 39255, + 35777: 39514, + 35778: 20208, + 35779: 20957, + 35780: 23597, + 35781: 26241, + 35782: 26989, + 35783: 23616, + 35784: 26354, + 35785: 26997, + 35786: 29577, + 35787: 26704, + 35788: 31873, + 35789: 20677, + 35790: 21220, + 35791: 22343, + 35792: 24062, + 35793: 37670, + 35794: 26020, + 35795: 27427, + 35796: 27453, + 35797: 29748, + 35798: 31105, + 35799: 31165, + 35800: 31563, + 35801: 32202, + 35802: 33465, + 35803: 33740, + 35804: 34943, + 35805: 35167, + 35806: 35641, + 35807: 36817, + 35808: 37329, + 35809: 21535, + 35810: 37504, + 35811: 20061, + 35812: 20534, + 35813: 21477, + 35814: 21306, + 35815: 29399, + 35816: 29590, + 35817: 30697, + 35818: 33510, + 35819: 36527, + 35820: 39366, + 35821: 39368, + 35822: 39378, + 35823: 20855, + 35824: 24858, + 35825: 34398, + 35826: 21936, + 35827: 31354, + 35828: 20598, + 35829: 23507, + 35830: 36935, + 35831: 38533, + 35832: 20018, + 35833: 27355, + 35834: 37351, + 35835: 23633, + 35836: 23624, + 35904: 25496, + 35905: 31391, + 35906: 27795, + 35907: 38772, + 35908: 36705, + 35909: 31402, + 35910: 29066, + 35911: 38536, + 35912: 31874, + 35913: 26647, + 35914: 32368, + 35915: 26705, + 35916: 37740, + 35917: 21234, + 35918: 21531, + 35919: 34219, + 35920: 35347, + 35921: 32676, + 35922: 36557, + 35923: 37089, + 35924: 21350, + 35925: 34952, + 35926: 31041, + 35927: 20418, + 35928: 20670, + 35929: 21009, + 35930: 20804, + 35931: 21843, + 35932: 22317, + 35933: 29674, + 35934: 22411, + 35935: 22865, + 35936: 24418, + 35937: 24452, + 35938: 24693, + 35939: 24950, + 35940: 24935, + 35941: 25001, + 35942: 25522, + 35943: 25658, + 35944: 25964, + 35945: 26223, + 35946: 26690, + 35947: 28179, + 35948: 30054, + 35949: 31293, + 35950: 31995, + 35951: 32076, + 35952: 32153, + 35953: 32331, + 35954: 32619, + 35955: 33550, + 35956: 33610, + 35957: 34509, + 35958: 35336, + 35959: 35427, + 35960: 35686, + 35961: 36605, + 35962: 38938, + 35963: 40335, + 35964: 33464, + 35965: 36814, + 35966: 39912, + 35968: 21127, + 35969: 25119, + 35970: 25731, + 35971: 28608, + 35972: 38553, + 35973: 26689, + 35974: 20625, + 35975: 27424, + 35976: 27770, + 35977: 28500, + 35978: 31348, + 35979: 32080, + 35980: 34880, + 35981: 35363, + 35982: 26376, + 35983: 20214, + 35984: 20537, + 35985: 20518, + 35986: 20581, + 35987: 20860, + 35988: 21048, + 35989: 21091, + 35990: 21927, + 35991: 22287, + 35992: 22533, + 35993: 23244, + 35994: 24314, + 35995: 25010, + 35996: 25080, + 35997: 25331, + 35998: 25458, + 35999: 26908, + 36000: 27177, + 36001: 29309, + 36002: 29356, + 36003: 29486, + 36004: 30740, + 36005: 30831, + 36006: 32121, + 36007: 30476, + 36008: 32937, + 36009: 35211, + 36010: 35609, + 36011: 36066, + 36012: 36562, + 36013: 36963, + 36014: 37749, + 36015: 38522, + 36016: 38997, + 36017: 39443, + 36018: 40568, + 36019: 20803, + 36020: 21407, + 36021: 21427, + 36022: 24187, + 36023: 24358, + 36024: 28187, + 36025: 28304, + 36026: 29572, + 36027: 29694, + 36028: 32067, + 36029: 33335, + 36030: 35328, + 36031: 35578, + 36032: 38480, + 36033: 20046, + 36034: 20491, + 36035: 21476, + 36036: 21628, + 36037: 22266, + 36038: 22993, + 36039: 23396, + 36040: 24049, + 36041: 24235, + 36042: 24359, + 36043: 25144, + 36044: 25925, + 36045: 26543, + 36046: 28246, + 36047: 29392, + 36048: 31946, + 36049: 34996, + 36050: 32929, + 36051: 32993, + 36052: 33776, + 36053: 34382, + 36054: 35463, + 36055: 36328, + 36056: 37431, + 36057: 38599, + 36058: 39015, + 36059: 40723, + 36060: 20116, + 36061: 20114, + 36062: 20237, + 36063: 21320, + 36064: 21577, + 36065: 21566, + 36066: 23087, + 36067: 24460, + 36068: 24481, + 36069: 24735, + 36070: 26791, + 36071: 27278, + 36072: 29786, + 36073: 30849, + 36074: 35486, + 36075: 35492, + 36076: 35703, + 36077: 37264, + 36078: 20062, + 36079: 39881, + 36080: 20132, + 36081: 20348, + 36082: 20399, + 36083: 20505, + 36084: 20502, + 36085: 20809, + 36086: 20844, + 36087: 21151, + 36088: 21177, + 36089: 21246, + 36090: 21402, + 36091: 21475, + 36092: 21521, + 36160: 21518, + 36161: 21897, + 36162: 22353, + 36163: 22434, + 36164: 22909, + 36165: 23380, + 36166: 23389, + 36167: 23439, + 36168: 24037, + 36169: 24039, + 36170: 24055, + 36171: 24184, + 36172: 24195, + 36173: 24218, + 36174: 24247, + 36175: 24344, + 36176: 24658, + 36177: 24908, + 36178: 25239, + 36179: 25304, + 36180: 25511, + 36181: 25915, + 36182: 26114, + 36183: 26179, + 36184: 26356, + 36185: 26477, + 36186: 26657, + 36187: 26775, + 36188: 27083, + 36189: 27743, + 36190: 27946, + 36191: 28009, + 36192: 28207, + 36193: 28317, + 36194: 30002, + 36195: 30343, + 36196: 30828, + 36197: 31295, + 36198: 31968, + 36199: 32005, + 36200: 32024, + 36201: 32094, + 36202: 32177, + 36203: 32789, + 36204: 32771, + 36205: 32943, + 36206: 32945, + 36207: 33108, + 36208: 33167, + 36209: 33322, + 36210: 33618, + 36211: 34892, + 36212: 34913, + 36213: 35611, + 36214: 36002, + 36215: 36092, + 36216: 37066, + 36217: 37237, + 36218: 37489, + 36219: 30783, + 36220: 37628, + 36221: 38308, + 36222: 38477, + 36224: 38917, + 36225: 39321, + 36226: 39640, + 36227: 40251, + 36228: 21083, + 36229: 21163, + 36230: 21495, + 36231: 21512, + 36232: 22741, + 36233: 25335, + 36234: 28640, + 36235: 35946, + 36236: 36703, + 36237: 40633, + 36238: 20811, + 36239: 21051, + 36240: 21578, + 36241: 22269, + 36242: 31296, + 36243: 37239, + 36244: 40288, + 36245: 40658, + 36246: 29508, + 36247: 28425, + 36248: 33136, + 36249: 29969, + 36250: 24573, + 36251: 24794, + 36252: 39592, + 36253: 29403, + 36254: 36796, + 36255: 27492, + 36256: 38915, + 36257: 20170, + 36258: 22256, + 36259: 22372, + 36260: 22718, + 36261: 23130, + 36262: 24680, + 36263: 25031, + 36264: 26127, + 36265: 26118, + 36266: 26681, + 36267: 26801, + 36268: 28151, + 36269: 30165, + 36270: 32058, + 36271: 33390, + 36272: 39746, + 36273: 20123, + 36274: 20304, + 36275: 21449, + 36276: 21766, + 36277: 23919, + 36278: 24038, + 36279: 24046, + 36280: 26619, + 36281: 27801, + 36282: 29811, + 36283: 30722, + 36284: 35408, + 36285: 37782, + 36286: 35039, + 36287: 22352, + 36288: 24231, + 36289: 25387, + 36290: 20661, + 36291: 20652, + 36292: 20877, + 36293: 26368, + 36294: 21705, + 36295: 22622, + 36296: 22971, + 36297: 23472, + 36298: 24425, + 36299: 25165, + 36300: 25505, + 36301: 26685, + 36302: 27507, + 36303: 28168, + 36304: 28797, + 36305: 37319, + 36306: 29312, + 36307: 30741, + 36308: 30758, + 36309: 31085, + 36310: 25998, + 36311: 32048, + 36312: 33756, + 36313: 35009, + 36314: 36617, + 36315: 38555, + 36316: 21092, + 36317: 22312, + 36318: 26448, + 36319: 32618, + 36320: 36001, + 36321: 20916, + 36322: 22338, + 36323: 38442, + 36324: 22586, + 36325: 27018, + 36326: 32948, + 36327: 21682, + 36328: 23822, + 36329: 22524, + 36330: 30869, + 36331: 40442, + 36332: 20316, + 36333: 21066, + 36334: 21643, + 36335: 25662, + 36336: 26152, + 36337: 26388, + 36338: 26613, + 36339: 31364, + 36340: 31574, + 36341: 32034, + 36342: 37679, + 36343: 26716, + 36344: 39853, + 36345: 31545, + 36346: 21273, + 36347: 20874, + 36348: 21047, + 36416: 23519, + 36417: 25334, + 36418: 25774, + 36419: 25830, + 36420: 26413, + 36421: 27578, + 36422: 34217, + 36423: 38609, + 36424: 30352, + 36425: 39894, + 36426: 25420, + 36427: 37638, + 36428: 39851, + 36429: 30399, + 36430: 26194, + 36431: 19977, + 36432: 20632, + 36433: 21442, + 36434: 23665, + 36435: 24808, + 36436: 25746, + 36437: 25955, + 36438: 26719, + 36439: 29158, + 36440: 29642, + 36441: 29987, + 36442: 31639, + 36443: 32386, + 36444: 34453, + 36445: 35715, + 36446: 36059, + 36447: 37240, + 36448: 39184, + 36449: 26028, + 36450: 26283, + 36451: 27531, + 36452: 20181, + 36453: 20180, + 36454: 20282, + 36455: 20351, + 36456: 21050, + 36457: 21496, + 36458: 21490, + 36459: 21987, + 36460: 22235, + 36461: 22763, + 36462: 22987, + 36463: 22985, + 36464: 23039, + 36465: 23376, + 36466: 23629, + 36467: 24066, + 36468: 24107, + 36469: 24535, + 36470: 24605, + 36471: 25351, + 36472: 25903, + 36473: 23388, + 36474: 26031, + 36475: 26045, + 36476: 26088, + 36477: 26525, + 36478: 27490, + 36480: 27515, + 36481: 27663, + 36482: 29509, + 36483: 31049, + 36484: 31169, + 36485: 31992, + 36486: 32025, + 36487: 32043, + 36488: 32930, + 36489: 33026, + 36490: 33267, + 36491: 35222, + 36492: 35422, + 36493: 35433, + 36494: 35430, + 36495: 35468, + 36496: 35566, + 36497: 36039, + 36498: 36060, + 36499: 38604, + 36500: 39164, + 36501: 27503, + 36502: 20107, + 36503: 20284, + 36504: 20365, + 36505: 20816, + 36506: 23383, + 36507: 23546, + 36508: 24904, + 36509: 25345, + 36510: 26178, + 36511: 27425, + 36512: 28363, + 36513: 27835, + 36514: 29246, + 36515: 29885, + 36516: 30164, + 36517: 30913, + 36518: 31034, + 36519: 32780, + 36520: 32819, + 36521: 33258, + 36522: 33940, + 36523: 36766, + 36524: 27728, + 36525: 40575, + 36526: 24335, + 36527: 35672, + 36528: 40235, + 36529: 31482, + 36530: 36600, + 36531: 23437, + 36532: 38635, + 36533: 19971, + 36534: 21489, + 36535: 22519, + 36536: 22833, + 36537: 23241, + 36538: 23460, + 36539: 24713, + 36540: 28287, + 36541: 28422, + 36542: 30142, + 36543: 36074, + 36544: 23455, + 36545: 34048, + 36546: 31712, + 36547: 20594, + 36548: 26612, + 36549: 33437, + 36550: 23649, + 36551: 34122, + 36552: 32286, + 36553: 33294, + 36554: 20889, + 36555: 23556, + 36556: 25448, + 36557: 36198, + 36558: 26012, + 36559: 29038, + 36560: 31038, + 36561: 32023, + 36562: 32773, + 36563: 35613, + 36564: 36554, + 36565: 36974, + 36566: 34503, + 36567: 37034, + 36568: 20511, + 36569: 21242, + 36570: 23610, + 36571: 26451, + 36572: 28796, + 36573: 29237, + 36574: 37196, + 36575: 37320, + 36576: 37675, + 36577: 33509, + 36578: 23490, + 36579: 24369, + 36580: 24825, + 36581: 20027, + 36582: 21462, + 36583: 23432, + 36584: 25163, + 36585: 26417, + 36586: 27530, + 36587: 29417, + 36588: 29664, + 36589: 31278, + 36590: 33131, + 36591: 36259, + 36592: 37202, + 36593: 39318, + 36594: 20754, + 36595: 21463, + 36596: 21610, + 36597: 23551, + 36598: 25480, + 36599: 27193, + 36600: 32172, + 36601: 38656, + 36602: 22234, + 36603: 21454, + 36604: 21608, + 36672: 23447, + 36673: 23601, + 36674: 24030, + 36675: 20462, + 36676: 24833, + 36677: 25342, + 36678: 27954, + 36679: 31168, + 36680: 31179, + 36681: 32066, + 36682: 32333, + 36683: 32722, + 36684: 33261, + 36685: 33311, + 36686: 33936, + 36687: 34886, + 36688: 35186, + 36689: 35728, + 36690: 36468, + 36691: 36655, + 36692: 36913, + 36693: 37195, + 36694: 37228, + 36695: 38598, + 36696: 37276, + 36697: 20160, + 36698: 20303, + 36699: 20805, + 36700: 21313, + 36701: 24467, + 36702: 25102, + 36703: 26580, + 36704: 27713, + 36705: 28171, + 36706: 29539, + 36707: 32294, + 36708: 37325, + 36709: 37507, + 36710: 21460, + 36711: 22809, + 36712: 23487, + 36713: 28113, + 36714: 31069, + 36715: 32302, + 36716: 31899, + 36717: 22654, + 36718: 29087, + 36719: 20986, + 36720: 34899, + 36721: 36848, + 36722: 20426, + 36723: 23803, + 36724: 26149, + 36725: 30636, + 36726: 31459, + 36727: 33308, + 36728: 39423, + 36729: 20934, + 36730: 24490, + 36731: 26092, + 36732: 26991, + 36733: 27529, + 36734: 28147, + 36736: 28310, + 36737: 28516, + 36738: 30462, + 36739: 32020, + 36740: 24033, + 36741: 36981, + 36742: 37255, + 36743: 38918, + 36744: 20966, + 36745: 21021, + 36746: 25152, + 36747: 26257, + 36748: 26329, + 36749: 28186, + 36750: 24246, + 36751: 32210, + 36752: 32626, + 36753: 26360, + 36754: 34223, + 36755: 34295, + 36756: 35576, + 36757: 21161, + 36758: 21465, + 36759: 22899, + 36760: 24207, + 36761: 24464, + 36762: 24661, + 36763: 37604, + 36764: 38500, + 36765: 20663, + 36766: 20767, + 36767: 21213, + 36768: 21280, + 36769: 21319, + 36770: 21484, + 36771: 21736, + 36772: 21830, + 36773: 21809, + 36774: 22039, + 36775: 22888, + 36776: 22974, + 36777: 23100, + 36778: 23477, + 36779: 23558, + 36780: 23567, + 36781: 23569, + 36782: 23578, + 36783: 24196, + 36784: 24202, + 36785: 24288, + 36786: 24432, + 36787: 25215, + 36788: 25220, + 36789: 25307, + 36790: 25484, + 36791: 25463, + 36792: 26119, + 36793: 26124, + 36794: 26157, + 36795: 26230, + 36796: 26494, + 36797: 26786, + 36798: 27167, + 36799: 27189, + 36800: 27836, + 36801: 28040, + 36802: 28169, + 36803: 28248, + 36804: 28988, + 36805: 28966, + 36806: 29031, + 36807: 30151, + 36808: 30465, + 36809: 30813, + 36810: 30977, + 36811: 31077, + 36812: 31216, + 36813: 31456, + 36814: 31505, + 36815: 31911, + 36816: 32057, + 36817: 32918, + 36818: 33750, + 36819: 33931, + 36820: 34121, + 36821: 34909, + 36822: 35059, + 36823: 35359, + 36824: 35388, + 36825: 35412, + 36826: 35443, + 36827: 35937, + 36828: 36062, + 36829: 37284, + 36830: 37478, + 36831: 37758, + 36832: 37912, + 36833: 38556, + 36834: 38808, + 36835: 19978, + 36836: 19976, + 36837: 19998, + 36838: 20055, + 36839: 20887, + 36840: 21104, + 36841: 22478, + 36842: 22580, + 36843: 22732, + 36844: 23330, + 36845: 24120, + 36846: 24773, + 36847: 25854, + 36848: 26465, + 36849: 26454, + 36850: 27972, + 36851: 29366, + 36852: 30067, + 36853: 31331, + 36854: 33976, + 36855: 35698, + 36856: 37304, + 36857: 37664, + 36858: 22065, + 36859: 22516, + 36860: 39166, + 36928: 25325, + 36929: 26893, + 36930: 27542, + 36931: 29165, + 36932: 32340, + 36933: 32887, + 36934: 33394, + 36935: 35302, + 36936: 39135, + 36937: 34645, + 36938: 36785, + 36939: 23611, + 36940: 20280, + 36941: 20449, + 36942: 20405, + 36943: 21767, + 36944: 23072, + 36945: 23517, + 36946: 23529, + 36947: 24515, + 36948: 24910, + 36949: 25391, + 36950: 26032, + 36951: 26187, + 36952: 26862, + 36953: 27035, + 36954: 28024, + 36955: 28145, + 36956: 30003, + 36957: 30137, + 36958: 30495, + 36959: 31070, + 36960: 31206, + 36961: 32051, + 36962: 33251, + 36963: 33455, + 36964: 34218, + 36965: 35242, + 36966: 35386, + 36967: 36523, + 36968: 36763, + 36969: 36914, + 36970: 37341, + 36971: 38663, + 36972: 20154, + 36973: 20161, + 36974: 20995, + 36975: 22645, + 36976: 22764, + 36977: 23563, + 36978: 29978, + 36979: 23613, + 36980: 33102, + 36981: 35338, + 36982: 36805, + 36983: 38499, + 36984: 38765, + 36985: 31525, + 36986: 35535, + 36987: 38920, + 36988: 37218, + 36989: 22259, + 36990: 21416, + 36992: 36887, + 36993: 21561, + 36994: 22402, + 36995: 24101, + 36996: 25512, + 36997: 27700, + 36998: 28810, + 36999: 30561, + 37000: 31883, + 37001: 32736, + 37002: 34928, + 37003: 36930, + 37004: 37204, + 37005: 37648, + 37006: 37656, + 37007: 38543, + 37008: 29790, + 37009: 39620, + 37010: 23815, + 37011: 23913, + 37012: 25968, + 37013: 26530, + 37014: 36264, + 37015: 38619, + 37016: 25454, + 37017: 26441, + 37018: 26905, + 37019: 33733, + 37020: 38935, + 37021: 38592, + 37022: 35070, + 37023: 28548, + 37024: 25722, + 37025: 23544, + 37026: 19990, + 37027: 28716, + 37028: 30045, + 37029: 26159, + 37030: 20932, + 37031: 21046, + 37032: 21218, + 37033: 22995, + 37034: 24449, + 37035: 24615, + 37036: 25104, + 37037: 25919, + 37038: 25972, + 37039: 26143, + 37040: 26228, + 37041: 26866, + 37042: 26646, + 37043: 27491, + 37044: 28165, + 37045: 29298, + 37046: 29983, + 37047: 30427, + 37048: 31934, + 37049: 32854, + 37050: 22768, + 37051: 35069, + 37052: 35199, + 37053: 35488, + 37054: 35475, + 37055: 35531, + 37056: 36893, + 37057: 37266, + 37058: 38738, + 37059: 38745, + 37060: 25993, + 37061: 31246, + 37062: 33030, + 37063: 38587, + 37064: 24109, + 37065: 24796, + 37066: 25114, + 37067: 26021, + 37068: 26132, + 37069: 26512, + 37070: 30707, + 37071: 31309, + 37072: 31821, + 37073: 32318, + 37074: 33034, + 37075: 36012, + 37076: 36196, + 37077: 36321, + 37078: 36447, + 37079: 30889, + 37080: 20999, + 37081: 25305, + 37082: 25509, + 37083: 25666, + 37084: 25240, + 37085: 35373, + 37086: 31363, + 37087: 31680, + 37088: 35500, + 37089: 38634, + 37090: 32118, + 37091: 33292, + 37092: 34633, + 37093: 20185, + 37094: 20808, + 37095: 21315, + 37096: 21344, + 37097: 23459, + 37098: 23554, + 37099: 23574, + 37100: 24029, + 37101: 25126, + 37102: 25159, + 37103: 25776, + 37104: 26643, + 37105: 26676, + 37106: 27849, + 37107: 27973, + 37108: 27927, + 37109: 26579, + 37110: 28508, + 37111: 29006, + 37112: 29053, + 37113: 26059, + 37114: 31359, + 37115: 31661, + 37116: 32218, + 37184: 32330, + 37185: 32680, + 37186: 33146, + 37187: 33307, + 37188: 33337, + 37189: 34214, + 37190: 35438, + 37191: 36046, + 37192: 36341, + 37193: 36984, + 37194: 36983, + 37195: 37549, + 37196: 37521, + 37197: 38275, + 37198: 39854, + 37199: 21069, + 37200: 21892, + 37201: 28472, + 37202: 28982, + 37203: 20840, + 37204: 31109, + 37205: 32341, + 37206: 33203, + 37207: 31950, + 37208: 22092, + 37209: 22609, + 37210: 23720, + 37211: 25514, + 37212: 26366, + 37213: 26365, + 37214: 26970, + 37215: 29401, + 37216: 30095, + 37217: 30094, + 37218: 30990, + 37219: 31062, + 37220: 31199, + 37221: 31895, + 37222: 32032, + 37223: 32068, + 37224: 34311, + 37225: 35380, + 37226: 38459, + 37227: 36961, + 37228: 40736, + 37229: 20711, + 37230: 21109, + 37231: 21452, + 37232: 21474, + 37233: 20489, + 37234: 21930, + 37235: 22766, + 37236: 22863, + 37237: 29245, + 37238: 23435, + 37239: 23652, + 37240: 21277, + 37241: 24803, + 37242: 24819, + 37243: 25436, + 37244: 25475, + 37245: 25407, + 37246: 25531, + 37248: 25805, + 37249: 26089, + 37250: 26361, + 37251: 24035, + 37252: 27085, + 37253: 27133, + 37254: 28437, + 37255: 29157, + 37256: 20105, + 37257: 30185, + 37258: 30456, + 37259: 31379, + 37260: 31967, + 37261: 32207, + 37262: 32156, + 37263: 32865, + 37264: 33609, + 37265: 33624, + 37266: 33900, + 37267: 33980, + 37268: 34299, + 37269: 35013, + 37270: 36208, + 37271: 36865, + 37272: 36973, + 37273: 37783, + 37274: 38684, + 37275: 39442, + 37276: 20687, + 37277: 22679, + 37278: 24974, + 37279: 33235, + 37280: 34101, + 37281: 36104, + 37282: 36896, + 37283: 20419, + 37284: 20596, + 37285: 21063, + 37286: 21363, + 37287: 24687, + 37288: 25417, + 37289: 26463, + 37290: 28204, + 37291: 36275, + 37292: 36895, + 37293: 20439, + 37294: 23646, + 37295: 36042, + 37296: 26063, + 37297: 32154, + 37298: 21330, + 37299: 34966, + 37300: 20854, + 37301: 25539, + 37302: 23384, + 37303: 23403, + 37304: 23562, + 37305: 25613, + 37306: 26449, + 37307: 36956, + 37308: 20182, + 37309: 22810, + 37310: 22826, + 37311: 27760, + 37312: 35409, + 37313: 21822, + 37314: 22549, + 37315: 22949, + 37316: 24816, + 37317: 25171, + 37318: 26561, + 37319: 33333, + 37320: 26965, + 37321: 38464, + 37322: 39364, + 37323: 39464, + 37324: 20307, + 37325: 22534, + 37326: 23550, + 37327: 32784, + 37328: 23729, + 37329: 24111, + 37330: 24453, + 37331: 24608, + 37332: 24907, + 37333: 25140, + 37334: 26367, + 37335: 27888, + 37336: 28382, + 37337: 32974, + 37338: 33151, + 37339: 33492, + 37340: 34955, + 37341: 36024, + 37342: 36864, + 37343: 36910, + 37344: 38538, + 37345: 40667, + 37346: 39899, + 37347: 20195, + 37348: 21488, + 37349: 22823, + 37350: 31532, + 37351: 37261, + 37352: 38988, + 37353: 40441, + 37354: 28381, + 37355: 28711, + 37356: 21331, + 37357: 21828, + 37358: 23429, + 37359: 25176, + 37360: 25246, + 37361: 25299, + 37362: 27810, + 37363: 28655, + 37364: 29730, + 37365: 35351, + 37366: 37944, + 37367: 28609, + 37368: 35582, + 37369: 33592, + 37370: 20967, + 37371: 34552, + 37372: 21482, + 37440: 21481, + 37441: 20294, + 37442: 36948, + 37443: 36784, + 37444: 22890, + 37445: 33073, + 37446: 24061, + 37447: 31466, + 37448: 36799, + 37449: 26842, + 37450: 35895, + 37451: 29432, + 37452: 40008, + 37453: 27197, + 37454: 35504, + 37455: 20025, + 37456: 21336, + 37457: 22022, + 37458: 22374, + 37459: 25285, + 37460: 25506, + 37461: 26086, + 37462: 27470, + 37463: 28129, + 37464: 28251, + 37465: 28845, + 37466: 30701, + 37467: 31471, + 37468: 31658, + 37469: 32187, + 37470: 32829, + 37471: 32966, + 37472: 34507, + 37473: 35477, + 37474: 37723, + 37475: 22243, + 37476: 22727, + 37477: 24382, + 37478: 26029, + 37479: 26262, + 37480: 27264, + 37481: 27573, + 37482: 30007, + 37483: 35527, + 37484: 20516, + 37485: 30693, + 37486: 22320, + 37487: 24347, + 37488: 24677, + 37489: 26234, + 37490: 27744, + 37491: 30196, + 37492: 31258, + 37493: 32622, + 37494: 33268, + 37495: 34584, + 37496: 36933, + 37497: 39347, + 37498: 31689, + 37499: 30044, + 37500: 31481, + 37501: 31569, + 37502: 33988, + 37504: 36880, + 37505: 31209, + 37506: 31378, + 37507: 33590, + 37508: 23265, + 37509: 30528, + 37510: 20013, + 37511: 20210, + 37512: 23449, + 37513: 24544, + 37514: 25277, + 37515: 26172, + 37516: 26609, + 37517: 27880, + 37518: 34411, + 37519: 34935, + 37520: 35387, + 37521: 37198, + 37522: 37619, + 37523: 39376, + 37524: 27159, + 37525: 28710, + 37526: 29482, + 37527: 33511, + 37528: 33879, + 37529: 36015, + 37530: 19969, + 37531: 20806, + 37532: 20939, + 37533: 21899, + 37534: 23541, + 37535: 24086, + 37536: 24115, + 37537: 24193, + 37538: 24340, + 37539: 24373, + 37540: 24427, + 37541: 24500, + 37542: 25074, + 37543: 25361, + 37544: 26274, + 37545: 26397, + 37546: 28526, + 37547: 29266, + 37548: 30010, + 37549: 30522, + 37550: 32884, + 37551: 33081, + 37552: 33144, + 37553: 34678, + 37554: 35519, + 37555: 35548, + 37556: 36229, + 37557: 36339, + 37558: 37530, + 37559: 38263, + 37560: 38914, + 37561: 40165, + 37562: 21189, + 37563: 25431, + 37564: 30452, + 37565: 26389, + 37566: 27784, + 37567: 29645, + 37568: 36035, + 37569: 37806, + 37570: 38515, + 37571: 27941, + 37572: 22684, + 37573: 26894, + 37574: 27084, + 37575: 36861, + 37576: 37786, + 37577: 30171, + 37578: 36890, + 37579: 22618, + 37580: 26626, + 37581: 25524, + 37582: 27131, + 37583: 20291, + 37584: 28460, + 37585: 26584, + 37586: 36795, + 37587: 34086, + 37588: 32180, + 37589: 37716, + 37590: 26943, + 37591: 28528, + 37592: 22378, + 37593: 22775, + 37594: 23340, + 37595: 32044, + 37596: 29226, + 37597: 21514, + 37598: 37347, + 37599: 40372, + 37600: 20141, + 37601: 20302, + 37602: 20572, + 37603: 20597, + 37604: 21059, + 37605: 35998, + 37606: 21576, + 37607: 22564, + 37608: 23450, + 37609: 24093, + 37610: 24213, + 37611: 24237, + 37612: 24311, + 37613: 24351, + 37614: 24716, + 37615: 25269, + 37616: 25402, + 37617: 25552, + 37618: 26799, + 37619: 27712, + 37620: 30855, + 37621: 31118, + 37622: 31243, + 37623: 32224, + 37624: 33351, + 37625: 35330, + 37626: 35558, + 37627: 36420, + 37628: 36883, + 37696: 37048, + 37697: 37165, + 37698: 37336, + 37699: 40718, + 37700: 27877, + 37701: 25688, + 37702: 25826, + 37703: 25973, + 37704: 28404, + 37705: 30340, + 37706: 31515, + 37707: 36969, + 37708: 37841, + 37709: 28346, + 37710: 21746, + 37711: 24505, + 37712: 25764, + 37713: 36685, + 37714: 36845, + 37715: 37444, + 37716: 20856, + 37717: 22635, + 37718: 22825, + 37719: 23637, + 37720: 24215, + 37721: 28155, + 37722: 32399, + 37723: 29980, + 37724: 36028, + 37725: 36578, + 37726: 39003, + 37727: 28857, + 37728: 20253, + 37729: 27583, + 37730: 28593, + 37731: 30000, + 37732: 38651, + 37733: 20814, + 37734: 21520, + 37735: 22581, + 37736: 22615, + 37737: 22956, + 37738: 23648, + 37739: 24466, + 37740: 26007, + 37741: 26460, + 37742: 28193, + 37743: 30331, + 37744: 33759, + 37745: 36077, + 37746: 36884, + 37747: 37117, + 37748: 37709, + 37749: 30757, + 37750: 30778, + 37751: 21162, + 37752: 24230, + 37753: 22303, + 37754: 22900, + 37755: 24594, + 37756: 20498, + 37757: 20826, + 37758: 20908, + 37760: 20941, + 37761: 20992, + 37762: 21776, + 37763: 22612, + 37764: 22616, + 37765: 22871, + 37766: 23445, + 37767: 23798, + 37768: 23947, + 37769: 24764, + 37770: 25237, + 37771: 25645, + 37772: 26481, + 37773: 26691, + 37774: 26812, + 37775: 26847, + 37776: 30423, + 37777: 28120, + 37778: 28271, + 37779: 28059, + 37780: 28783, + 37781: 29128, + 37782: 24403, + 37783: 30168, + 37784: 31095, + 37785: 31561, + 37786: 31572, + 37787: 31570, + 37788: 31958, + 37789: 32113, + 37790: 21040, + 37791: 33891, + 37792: 34153, + 37793: 34276, + 37794: 35342, + 37795: 35588, + 37796: 35910, + 37797: 36367, + 37798: 36867, + 37799: 36879, + 37800: 37913, + 37801: 38518, + 37802: 38957, + 37803: 39472, + 37804: 38360, + 37805: 20685, + 37806: 21205, + 37807: 21516, + 37808: 22530, + 37809: 23566, + 37810: 24999, + 37811: 25758, + 37812: 27934, + 37813: 30643, + 37814: 31461, + 37815: 33012, + 37816: 33796, + 37817: 36947, + 37818: 37509, + 37819: 23776, + 37820: 40199, + 37821: 21311, + 37822: 24471, + 37823: 24499, + 37824: 28060, + 37825: 29305, + 37826: 30563, + 37827: 31167, + 37828: 31716, + 37829: 27602, + 37830: 29420, + 37831: 35501, + 37832: 26627, + 37833: 27233, + 37834: 20984, + 37835: 31361, + 37836: 26932, + 37837: 23626, + 37838: 40182, + 37839: 33515, + 37840: 23493, + 37841: 37193, + 37842: 28702, + 37843: 22136, + 37844: 23663, + 37845: 24775, + 37846: 25958, + 37847: 27788, + 37848: 35930, + 37849: 36929, + 37850: 38931, + 37851: 21585, + 37852: 26311, + 37853: 37389, + 37854: 22856, + 37855: 37027, + 37856: 20869, + 37857: 20045, + 37858: 20970, + 37859: 34201, + 37860: 35598, + 37861: 28760, + 37862: 25466, + 37863: 37707, + 37864: 26978, + 37865: 39348, + 37866: 32260, + 37867: 30071, + 37868: 21335, + 37869: 26976, + 37870: 36575, + 37871: 38627, + 37872: 27741, + 37873: 20108, + 37874: 23612, + 37875: 24336, + 37876: 36841, + 37877: 21250, + 37878: 36049, + 37879: 32905, + 37880: 34425, + 37881: 24319, + 37882: 26085, + 37883: 20083, + 37884: 20837, + 37952: 22914, + 37953: 23615, + 37954: 38894, + 37955: 20219, + 37956: 22922, + 37957: 24525, + 37958: 35469, + 37959: 28641, + 37960: 31152, + 37961: 31074, + 37962: 23527, + 37963: 33905, + 37964: 29483, + 37965: 29105, + 37966: 24180, + 37967: 24565, + 37968: 25467, + 37969: 25754, + 37970: 29123, + 37971: 31896, + 37972: 20035, + 37973: 24316, + 37974: 20043, + 37975: 22492, + 37976: 22178, + 37977: 24745, + 37978: 28611, + 37979: 32013, + 37980: 33021, + 37981: 33075, + 37982: 33215, + 37983: 36786, + 37984: 35223, + 37985: 34468, + 37986: 24052, + 37987: 25226, + 37988: 25773, + 37989: 35207, + 37990: 26487, + 37991: 27874, + 37992: 27966, + 37993: 29750, + 37994: 30772, + 37995: 23110, + 37996: 32629, + 37997: 33453, + 37998: 39340, + 37999: 20467, + 38000: 24259, + 38001: 25309, + 38002: 25490, + 38003: 25943, + 38004: 26479, + 38005: 30403, + 38006: 29260, + 38007: 32972, + 38008: 32954, + 38009: 36649, + 38010: 37197, + 38011: 20493, + 38012: 22521, + 38013: 23186, + 38014: 26757, + 38016: 26995, + 38017: 29028, + 38018: 29437, + 38019: 36023, + 38020: 22770, + 38021: 36064, + 38022: 38506, + 38023: 36889, + 38024: 34687, + 38025: 31204, + 38026: 30695, + 38027: 33833, + 38028: 20271, + 38029: 21093, + 38030: 21338, + 38031: 25293, + 38032: 26575, + 38033: 27850, + 38034: 30333, + 38035: 31636, + 38036: 31893, + 38037: 33334, + 38038: 34180, + 38039: 36843, + 38040: 26333, + 38041: 28448, + 38042: 29190, + 38043: 32283, + 38044: 33707, + 38045: 39361, + 38046: 40614, + 38047: 20989, + 38048: 31665, + 38049: 30834, + 38050: 31672, + 38051: 32903, + 38052: 31560, + 38053: 27368, + 38054: 24161, + 38055: 32908, + 38056: 30033, + 38057: 30048, + 38058: 20843, + 38059: 37474, + 38060: 28300, + 38061: 30330, + 38062: 37271, + 38063: 39658, + 38064: 20240, + 38065: 32624, + 38066: 25244, + 38067: 31567, + 38068: 38309, + 38069: 40169, + 38070: 22138, + 38071: 22617, + 38072: 34532, + 38073: 38588, + 38074: 20276, + 38075: 21028, + 38076: 21322, + 38077: 21453, + 38078: 21467, + 38079: 24070, + 38080: 25644, + 38081: 26001, + 38082: 26495, + 38083: 27710, + 38084: 27726, + 38085: 29256, + 38086: 29359, + 38087: 29677, + 38088: 30036, + 38089: 32321, + 38090: 33324, + 38091: 34281, + 38092: 36009, + 38093: 31684, + 38094: 37318, + 38095: 29033, + 38096: 38930, + 38097: 39151, + 38098: 25405, + 38099: 26217, + 38100: 30058, + 38101: 30436, + 38102: 30928, + 38103: 34115, + 38104: 34542, + 38105: 21290, + 38106: 21329, + 38107: 21542, + 38108: 22915, + 38109: 24199, + 38110: 24444, + 38111: 24754, + 38112: 25161, + 38113: 25209, + 38114: 25259, + 38115: 26000, + 38116: 27604, + 38117: 27852, + 38118: 30130, + 38119: 30382, + 38120: 30865, + 38121: 31192, + 38122: 32203, + 38123: 32631, + 38124: 32933, + 38125: 34987, + 38126: 35513, + 38127: 36027, + 38128: 36991, + 38129: 38750, + 38130: 39131, + 38131: 27147, + 38132: 31800, + 38133: 20633, + 38134: 23614, + 38135: 24494, + 38136: 26503, + 38137: 27608, + 38138: 29749, + 38139: 30473, + 38140: 32654, + 38208: 40763, + 38209: 26570, + 38210: 31255, + 38211: 21305, + 38212: 30091, + 38213: 39661, + 38214: 24422, + 38215: 33181, + 38216: 33777, + 38217: 32920, + 38218: 24380, + 38219: 24517, + 38220: 30050, + 38221: 31558, + 38222: 36924, + 38223: 26727, + 38224: 23019, + 38225: 23195, + 38226: 32016, + 38227: 30334, + 38228: 35628, + 38229: 20469, + 38230: 24426, + 38231: 27161, + 38232: 27703, + 38233: 28418, + 38234: 29922, + 38235: 31080, + 38236: 34920, + 38237: 35413, + 38238: 35961, + 38239: 24287, + 38240: 25551, + 38241: 30149, + 38242: 31186, + 38243: 33495, + 38244: 37672, + 38245: 37618, + 38246: 33948, + 38247: 34541, + 38248: 39981, + 38249: 21697, + 38250: 24428, + 38251: 25996, + 38252: 27996, + 38253: 28693, + 38254: 36007, + 38255: 36051, + 38256: 38971, + 38257: 25935, + 38258: 29942, + 38259: 19981, + 38260: 20184, + 38261: 22496, + 38262: 22827, + 38263: 23142, + 38264: 23500, + 38265: 20904, + 38266: 24067, + 38267: 24220, + 38268: 24598, + 38269: 25206, + 38270: 25975, + 38272: 26023, + 38273: 26222, + 38274: 28014, + 38275: 29238, + 38276: 31526, + 38277: 33104, + 38278: 33178, + 38279: 33433, + 38280: 35676, + 38281: 36000, + 38282: 36070, + 38283: 36212, + 38284: 38428, + 38285: 38468, + 38286: 20398, + 38287: 25771, + 38288: 27494, + 38289: 33310, + 38290: 33889, + 38291: 34154, + 38292: 37096, + 38293: 23553, + 38294: 26963, + 38295: 39080, + 38296: 33914, + 38297: 34135, + 38298: 20239, + 38299: 21103, + 38300: 24489, + 38301: 24133, + 38302: 26381, + 38303: 31119, + 38304: 33145, + 38305: 35079, + 38306: 35206, + 38307: 28149, + 38308: 24343, + 38309: 25173, + 38310: 27832, + 38311: 20175, + 38312: 29289, + 38313: 39826, + 38314: 20998, + 38315: 21563, + 38316: 22132, + 38317: 22707, + 38318: 24996, + 38319: 25198, + 38320: 28954, + 38321: 22894, + 38322: 31881, + 38323: 31966, + 38324: 32027, + 38325: 38640, + 38326: 25991, + 38327: 32862, + 38328: 19993, + 38329: 20341, + 38330: 20853, + 38331: 22592, + 38332: 24163, + 38333: 24179, + 38334: 24330, + 38335: 26564, + 38336: 20006, + 38337: 34109, + 38338: 38281, + 38339: 38491, + 38340: 31859, + 38341: 38913, + 38342: 20731, + 38343: 22721, + 38344: 30294, + 38345: 30887, + 38346: 21029, + 38347: 30629, + 38348: 34065, + 38349: 31622, + 38350: 20559, + 38351: 22793, + 38352: 29255, + 38353: 31687, + 38354: 32232, + 38355: 36794, + 38356: 36820, + 38357: 36941, + 38358: 20415, + 38359: 21193, + 38360: 23081, + 38361: 24321, + 38362: 38829, + 38363: 20445, + 38364: 33303, + 38365: 37610, + 38366: 22275, + 38367: 25429, + 38368: 27497, + 38369: 29995, + 38370: 35036, + 38371: 36628, + 38372: 31298, + 38373: 21215, + 38374: 22675, + 38375: 24917, + 38376: 25098, + 38377: 26286, + 38378: 27597, + 38379: 31807, + 38380: 33769, + 38381: 20515, + 38382: 20472, + 38383: 21253, + 38384: 21574, + 38385: 22577, + 38386: 22857, + 38387: 23453, + 38388: 23792, + 38389: 23791, + 38390: 23849, + 38391: 24214, + 38392: 25265, + 38393: 25447, + 38394: 25918, + 38395: 26041, + 38396: 26379, + 38464: 27861, + 38465: 27873, + 38466: 28921, + 38467: 30770, + 38468: 32299, + 38469: 32990, + 38470: 33459, + 38471: 33804, + 38472: 34028, + 38473: 34562, + 38474: 35090, + 38475: 35370, + 38476: 35914, + 38477: 37030, + 38478: 37586, + 38479: 39165, + 38480: 40179, + 38481: 40300, + 38482: 20047, + 38483: 20129, + 38484: 20621, + 38485: 21078, + 38486: 22346, + 38487: 22952, + 38488: 24125, + 38489: 24536, + 38490: 24537, + 38491: 25151, + 38492: 26292, + 38493: 26395, + 38494: 26576, + 38495: 26834, + 38496: 20882, + 38497: 32033, + 38498: 32938, + 38499: 33192, + 38500: 35584, + 38501: 35980, + 38502: 36031, + 38503: 37502, + 38504: 38450, + 38505: 21536, + 38506: 38956, + 38507: 21271, + 38508: 20693, + 38509: 21340, + 38510: 22696, + 38511: 25778, + 38512: 26420, + 38513: 29287, + 38514: 30566, + 38515: 31302, + 38516: 37350, + 38517: 21187, + 38518: 27809, + 38519: 27526, + 38520: 22528, + 38521: 24140, + 38522: 22868, + 38523: 26412, + 38524: 32763, + 38525: 20961, + 38526: 30406, + 38528: 25705, + 38529: 30952, + 38530: 39764, + 38531: 40635, + 38532: 22475, + 38533: 22969, + 38534: 26151, + 38535: 26522, + 38536: 27598, + 38537: 21737, + 38538: 27097, + 38539: 24149, + 38540: 33180, + 38541: 26517, + 38542: 39850, + 38543: 26622, + 38544: 40018, + 38545: 26717, + 38546: 20134, + 38547: 20451, + 38548: 21448, + 38549: 25273, + 38550: 26411, + 38551: 27819, + 38552: 36804, + 38553: 20397, + 38554: 32365, + 38555: 40639, + 38556: 19975, + 38557: 24930, + 38558: 28288, + 38559: 28459, + 38560: 34067, + 38561: 21619, + 38562: 26410, + 38563: 39749, + 38564: 24051, + 38565: 31637, + 38566: 23724, + 38567: 23494, + 38568: 34588, + 38569: 28234, + 38570: 34001, + 38571: 31252, + 38572: 33032, + 38573: 22937, + 38574: 31885, + 38575: 27665, + 38576: 30496, + 38577: 21209, + 38578: 22818, + 38579: 28961, + 38580: 29279, + 38581: 30683, + 38582: 38695, + 38583: 40289, + 38584: 26891, + 38585: 23167, + 38586: 23064, + 38587: 20901, + 38588: 21517, + 38589: 21629, + 38590: 26126, + 38591: 30431, + 38592: 36855, + 38593: 37528, + 38594: 40180, + 38595: 23018, + 38596: 29277, + 38597: 28357, + 38598: 20813, + 38599: 26825, + 38600: 32191, + 38601: 32236, + 38602: 38754, + 38603: 40634, + 38604: 25720, + 38605: 27169, + 38606: 33538, + 38607: 22916, + 38608: 23391, + 38609: 27611, + 38610: 29467, + 38611: 30450, + 38612: 32178, + 38613: 32791, + 38614: 33945, + 38615: 20786, + 38616: 26408, + 38617: 40665, + 38618: 30446, + 38619: 26466, + 38620: 21247, + 38621: 39173, + 38622: 23588, + 38623: 25147, + 38624: 31870, + 38625: 36016, + 38626: 21839, + 38627: 24758, + 38628: 32011, + 38629: 38272, + 38630: 21249, + 38631: 20063, + 38632: 20918, + 38633: 22812, + 38634: 29242, + 38635: 32822, + 38636: 37326, + 38637: 24357, + 38638: 30690, + 38639: 21380, + 38640: 24441, + 38641: 32004, + 38642: 34220, + 38643: 35379, + 38644: 36493, + 38645: 38742, + 38646: 26611, + 38647: 34222, + 38648: 37971, + 38649: 24841, + 38650: 24840, + 38651: 27833, + 38652: 30290, + 38720: 35565, + 38721: 36664, + 38722: 21807, + 38723: 20305, + 38724: 20778, + 38725: 21191, + 38726: 21451, + 38727: 23461, + 38728: 24189, + 38729: 24736, + 38730: 24962, + 38731: 25558, + 38732: 26377, + 38733: 26586, + 38734: 28263, + 38735: 28044, + 38736: 29494, + 38737: 29495, + 38738: 30001, + 38739: 31056, + 38740: 35029, + 38741: 35480, + 38742: 36938, + 38743: 37009, + 38744: 37109, + 38745: 38596, + 38746: 34701, + 38747: 22805, + 38748: 20104, + 38749: 20313, + 38750: 19982, + 38751: 35465, + 38752: 36671, + 38753: 38928, + 38754: 20653, + 38755: 24188, + 38756: 22934, + 38757: 23481, + 38758: 24248, + 38759: 25562, + 38760: 25594, + 38761: 25793, + 38762: 26332, + 38763: 26954, + 38764: 27096, + 38765: 27915, + 38766: 28342, + 38767: 29076, + 38768: 29992, + 38769: 31407, + 38770: 32650, + 38771: 32768, + 38772: 33865, + 38773: 33993, + 38774: 35201, + 38775: 35617, + 38776: 36362, + 38777: 36965, + 38778: 38525, + 38779: 39178, + 38780: 24958, + 38781: 25233, + 38782: 27442, + 38784: 27779, + 38785: 28020, + 38786: 32716, + 38787: 32764, + 38788: 28096, + 38789: 32645, + 38790: 34746, + 38791: 35064, + 38792: 26469, + 38793: 33713, + 38794: 38972, + 38795: 38647, + 38796: 27931, + 38797: 32097, + 38798: 33853, + 38799: 37226, + 38800: 20081, + 38801: 21365, + 38802: 23888, + 38803: 27396, + 38804: 28651, + 38805: 34253, + 38806: 34349, + 38807: 35239, + 38808: 21033, + 38809: 21519, + 38810: 23653, + 38811: 26446, + 38812: 26792, + 38813: 29702, + 38814: 29827, + 38815: 30178, + 38816: 35023, + 38817: 35041, + 38818: 37324, + 38819: 38626, + 38820: 38520, + 38821: 24459, + 38822: 29575, + 38823: 31435, + 38824: 33870, + 38825: 25504, + 38826: 30053, + 38827: 21129, + 38828: 27969, + 38829: 28316, + 38830: 29705, + 38831: 30041, + 38832: 30827, + 38833: 31890, + 38834: 38534, + 38835: 31452, + 38836: 40845, + 38837: 20406, + 38838: 24942, + 38839: 26053, + 38840: 34396, + 38841: 20102, + 38842: 20142, + 38843: 20698, + 38844: 20001, + 38845: 20940, + 38846: 23534, + 38847: 26009, + 38848: 26753, + 38849: 28092, + 38850: 29471, + 38851: 30274, + 38852: 30637, + 38853: 31260, + 38854: 31975, + 38855: 33391, + 38856: 35538, + 38857: 36988, + 38858: 37327, + 38859: 38517, + 38860: 38936, + 38861: 21147, + 38862: 32209, + 38863: 20523, + 38864: 21400, + 38865: 26519, + 38866: 28107, + 38867: 29136, + 38868: 29747, + 38869: 33256, + 38870: 36650, + 38871: 38563, + 38872: 40023, + 38873: 40607, + 38874: 29792, + 38875: 22593, + 38876: 28057, + 38877: 32047, + 38878: 39006, + 38879: 20196, + 38880: 20278, + 38881: 20363, + 38882: 20919, + 38883: 21169, + 38884: 23994, + 38885: 24604, + 38886: 29618, + 38887: 31036, + 38888: 33491, + 38889: 37428, + 38890: 38583, + 38891: 38646, + 38892: 38666, + 38893: 40599, + 38894: 40802, + 38895: 26278, + 38896: 27508, + 38897: 21015, + 38898: 21155, + 38899: 28872, + 38900: 35010, + 38901: 24265, + 38902: 24651, + 38903: 24976, + 38904: 28451, + 38905: 29001, + 38906: 31806, + 38907: 32244, + 38908: 32879, + 38976: 34030, + 38977: 36899, + 38978: 37676, + 38979: 21570, + 38980: 39791, + 38981: 27347, + 38982: 28809, + 38983: 36034, + 38984: 36335, + 38985: 38706, + 38986: 21172, + 38987: 23105, + 38988: 24266, + 38989: 24324, + 38990: 26391, + 38991: 27004, + 38992: 27028, + 38993: 28010, + 38994: 28431, + 38995: 29282, + 38996: 29436, + 38997: 31725, + 38998: 32769, + 38999: 32894, + 39000: 34635, + 39001: 37070, + 39002: 20845, + 39003: 40595, + 39004: 31108, + 39005: 32907, + 39006: 37682, + 39007: 35542, + 39008: 20525, + 39009: 21644, + 39010: 35441, + 39011: 27498, + 39012: 36036, + 39013: 33031, + 39014: 24785, + 39015: 26528, + 39016: 40434, + 39017: 20121, + 39018: 20120, + 39019: 39952, + 39020: 35435, + 39021: 34241, + 39022: 34152, + 39023: 26880, + 39024: 28286, + 39025: 30871, + 39026: 33109, + 39071: 24332, + 39072: 19984, + 39073: 19989, + 39074: 20010, + 39075: 20017, + 39076: 20022, + 39077: 20028, + 39078: 20031, + 39079: 20034, + 39080: 20054, + 39081: 20056, + 39082: 20098, + 39083: 20101, + 39084: 35947, + 39085: 20106, + 39086: 33298, + 39087: 24333, + 39088: 20110, + 39089: 20126, + 39090: 20127, + 39091: 20128, + 39092: 20130, + 39093: 20144, + 39094: 20147, + 39095: 20150, + 39096: 20174, + 39097: 20173, + 39098: 20164, + 39099: 20166, + 39100: 20162, + 39101: 20183, + 39102: 20190, + 39103: 20205, + 39104: 20191, + 39105: 20215, + 39106: 20233, + 39107: 20314, + 39108: 20272, + 39109: 20315, + 39110: 20317, + 39111: 20311, + 39112: 20295, + 39113: 20342, + 39114: 20360, + 39115: 20367, + 39116: 20376, + 39117: 20347, + 39118: 20329, + 39119: 20336, + 39120: 20369, + 39121: 20335, + 39122: 20358, + 39123: 20374, + 39124: 20760, + 39125: 20436, + 39126: 20447, + 39127: 20430, + 39128: 20440, + 39129: 20443, + 39130: 20433, + 39131: 20442, + 39132: 20432, + 39133: 20452, + 39134: 20453, + 39135: 20506, + 39136: 20520, + 39137: 20500, + 39138: 20522, + 39139: 20517, + 39140: 20485, + 39141: 20252, + 39142: 20470, + 39143: 20513, + 39144: 20521, + 39145: 20524, + 39146: 20478, + 39147: 20463, + 39148: 20497, + 39149: 20486, + 39150: 20547, + 39151: 20551, + 39152: 26371, + 39153: 20565, + 39154: 20560, + 39155: 20552, + 39156: 20570, + 39157: 20566, + 39158: 20588, + 39159: 20600, + 39160: 20608, + 39161: 20634, + 39162: 20613, + 39163: 20660, + 39164: 20658, + 39232: 20681, + 39233: 20682, + 39234: 20659, + 39235: 20674, + 39236: 20694, + 39237: 20702, + 39238: 20709, + 39239: 20717, + 39240: 20707, + 39241: 20718, + 39242: 20729, + 39243: 20725, + 39244: 20745, + 39245: 20737, + 39246: 20738, + 39247: 20758, + 39248: 20757, + 39249: 20756, + 39250: 20762, + 39251: 20769, + 39252: 20794, + 39253: 20791, + 39254: 20796, + 39255: 20795, + 39256: 20799, + 39257: 20800, + 39258: 20818, + 39259: 20812, + 39260: 20820, + 39261: 20834, + 39262: 31480, + 39263: 20841, + 39264: 20842, + 39265: 20846, + 39266: 20864, + 39267: 20866, + 39268: 22232, + 39269: 20876, + 39270: 20873, + 39271: 20879, + 39272: 20881, + 39273: 20883, + 39274: 20885, + 39275: 20886, + 39276: 20900, + 39277: 20902, + 39278: 20898, + 39279: 20905, + 39280: 20906, + 39281: 20907, + 39282: 20915, + 39283: 20913, + 39284: 20914, + 39285: 20912, + 39286: 20917, + 39287: 20925, + 39288: 20933, + 39289: 20937, + 39290: 20955, + 39291: 20960, + 39292: 34389, + 39293: 20969, + 39294: 20973, + 39296: 20976, + 39297: 20981, + 39298: 20990, + 39299: 20996, + 39300: 21003, + 39301: 21012, + 39302: 21006, + 39303: 21031, + 39304: 21034, + 39305: 21038, + 39306: 21043, + 39307: 21049, + 39308: 21071, + 39309: 21060, + 39310: 21067, + 39311: 21068, + 39312: 21086, + 39313: 21076, + 39314: 21098, + 39315: 21108, + 39316: 21097, + 39317: 21107, + 39318: 21119, + 39319: 21117, + 39320: 21133, + 39321: 21140, + 39322: 21138, + 39323: 21105, + 39324: 21128, + 39325: 21137, + 39326: 36776, + 39327: 36775, + 39328: 21164, + 39329: 21165, + 39330: 21180, + 39331: 21173, + 39332: 21185, + 39333: 21197, + 39334: 21207, + 39335: 21214, + 39336: 21219, + 39337: 21222, + 39338: 39149, + 39339: 21216, + 39340: 21235, + 39341: 21237, + 39342: 21240, + 39343: 21241, + 39344: 21254, + 39345: 21256, + 39346: 30008, + 39347: 21261, + 39348: 21264, + 39349: 21263, + 39350: 21269, + 39351: 21274, + 39352: 21283, + 39353: 21295, + 39354: 21297, + 39355: 21299, + 39356: 21304, + 39357: 21312, + 39358: 21318, + 39359: 21317, + 39360: 19991, + 39361: 21321, + 39362: 21325, + 39363: 20950, + 39364: 21342, + 39365: 21353, + 39366: 21358, + 39367: 22808, + 39368: 21371, + 39369: 21367, + 39370: 21378, + 39371: 21398, + 39372: 21408, + 39373: 21414, + 39374: 21413, + 39375: 21422, + 39376: 21424, + 39377: 21430, + 39378: 21443, + 39379: 31762, + 39380: 38617, + 39381: 21471, + 39382: 26364, + 39383: 29166, + 39384: 21486, + 39385: 21480, + 39386: 21485, + 39387: 21498, + 39388: 21505, + 39389: 21565, + 39390: 21568, + 39391: 21548, + 39392: 21549, + 39393: 21564, + 39394: 21550, + 39395: 21558, + 39396: 21545, + 39397: 21533, + 39398: 21582, + 39399: 21647, + 39400: 21621, + 39401: 21646, + 39402: 21599, + 39403: 21617, + 39404: 21623, + 39405: 21616, + 39406: 21650, + 39407: 21627, + 39408: 21632, + 39409: 21622, + 39410: 21636, + 39411: 21648, + 39412: 21638, + 39413: 21703, + 39414: 21666, + 39415: 21688, + 39416: 21669, + 39417: 21676, + 39418: 21700, + 39419: 21704, + 39420: 21672, + 39488: 21675, + 39489: 21698, + 39490: 21668, + 39491: 21694, + 39492: 21692, + 39493: 21720, + 39494: 21733, + 39495: 21734, + 39496: 21775, + 39497: 21780, + 39498: 21757, + 39499: 21742, + 39500: 21741, + 39501: 21754, + 39502: 21730, + 39503: 21817, + 39504: 21824, + 39505: 21859, + 39506: 21836, + 39507: 21806, + 39508: 21852, + 39509: 21829, + 39510: 21846, + 39511: 21847, + 39512: 21816, + 39513: 21811, + 39514: 21853, + 39515: 21913, + 39516: 21888, + 39517: 21679, + 39518: 21898, + 39519: 21919, + 39520: 21883, + 39521: 21886, + 39522: 21912, + 39523: 21918, + 39524: 21934, + 39525: 21884, + 39526: 21891, + 39527: 21929, + 39528: 21895, + 39529: 21928, + 39530: 21978, + 39531: 21957, + 39532: 21983, + 39533: 21956, + 39534: 21980, + 39535: 21988, + 39536: 21972, + 39537: 22036, + 39538: 22007, + 39539: 22038, + 39540: 22014, + 39541: 22013, + 39542: 22043, + 39543: 22009, + 39544: 22094, + 39545: 22096, + 39546: 29151, + 39547: 22068, + 39548: 22070, + 39549: 22066, + 39550: 22072, + 39552: 22123, + 39553: 22116, + 39554: 22063, + 39555: 22124, + 39556: 22122, + 39557: 22150, + 39558: 22144, + 39559: 22154, + 39560: 22176, + 39561: 22164, + 39562: 22159, + 39563: 22181, + 39564: 22190, + 39565: 22198, + 39566: 22196, + 39567: 22210, + 39568: 22204, + 39569: 22209, + 39570: 22211, + 39571: 22208, + 39572: 22216, + 39573: 22222, + 39574: 22225, + 39575: 22227, + 39576: 22231, + 39577: 22254, + 39578: 22265, + 39579: 22272, + 39580: 22271, + 39581: 22276, + 39582: 22281, + 39583: 22280, + 39584: 22283, + 39585: 22285, + 39586: 22291, + 39587: 22296, + 39588: 22294, + 39589: 21959, + 39590: 22300, + 39591: 22310, + 39592: 22327, + 39593: 22328, + 39594: 22350, + 39595: 22331, + 39596: 22336, + 39597: 22351, + 39598: 22377, + 39599: 22464, + 39600: 22408, + 39601: 22369, + 39602: 22399, + 39603: 22409, + 39604: 22419, + 39605: 22432, + 39606: 22451, + 39607: 22436, + 39608: 22442, + 39609: 22448, + 39610: 22467, + 39611: 22470, + 39612: 22484, + 39613: 22482, + 39614: 22483, + 39615: 22538, + 39616: 22486, + 39617: 22499, + 39618: 22539, + 39619: 22553, + 39620: 22557, + 39621: 22642, + 39622: 22561, + 39623: 22626, + 39624: 22603, + 39625: 22640, + 39626: 27584, + 39627: 22610, + 39628: 22589, + 39629: 22649, + 39630: 22661, + 39631: 22713, + 39632: 22687, + 39633: 22699, + 39634: 22714, + 39635: 22750, + 39636: 22715, + 39637: 22712, + 39638: 22702, + 39639: 22725, + 39640: 22739, + 39641: 22737, + 39642: 22743, + 39643: 22745, + 39644: 22744, + 39645: 22757, + 39646: 22748, + 39647: 22756, + 39648: 22751, + 39649: 22767, + 39650: 22778, + 39651: 22777, + 39652: 22779, + 39653: 22780, + 39654: 22781, + 39655: 22786, + 39656: 22794, + 39657: 22800, + 39658: 22811, + 39659: 26790, + 39660: 22821, + 39661: 22828, + 39662: 22829, + 39663: 22834, + 39664: 22840, + 39665: 22846, + 39666: 31442, + 39667: 22869, + 39668: 22864, + 39669: 22862, + 39670: 22874, + 39671: 22872, + 39672: 22882, + 39673: 22880, + 39674: 22887, + 39675: 22892, + 39676: 22889, + 39744: 22904, + 39745: 22913, + 39746: 22941, + 39747: 20318, + 39748: 20395, + 39749: 22947, + 39750: 22962, + 39751: 22982, + 39752: 23016, + 39753: 23004, + 39754: 22925, + 39755: 23001, + 39756: 23002, + 39757: 23077, + 39758: 23071, + 39759: 23057, + 39760: 23068, + 39761: 23049, + 39762: 23066, + 39763: 23104, + 39764: 23148, + 39765: 23113, + 39766: 23093, + 39767: 23094, + 39768: 23138, + 39769: 23146, + 39770: 23194, + 39771: 23228, + 39772: 23230, + 39773: 23243, + 39774: 23234, + 39775: 23229, + 39776: 23267, + 39777: 23255, + 39778: 23270, + 39779: 23273, + 39780: 23254, + 39781: 23290, + 39782: 23291, + 39783: 23308, + 39784: 23307, + 39785: 23318, + 39786: 23346, + 39787: 23248, + 39788: 23338, + 39789: 23350, + 39790: 23358, + 39791: 23363, + 39792: 23365, + 39793: 23360, + 39794: 23377, + 39795: 23381, + 39796: 23386, + 39797: 23387, + 39798: 23397, + 39799: 23401, + 39800: 23408, + 39801: 23411, + 39802: 23413, + 39803: 23416, + 39804: 25992, + 39805: 23418, + 39806: 23424, + 39808: 23427, + 39809: 23462, + 39810: 23480, + 39811: 23491, + 39812: 23495, + 39813: 23497, + 39814: 23508, + 39815: 23504, + 39816: 23524, + 39817: 23526, + 39818: 23522, + 39819: 23518, + 39820: 23525, + 39821: 23531, + 39822: 23536, + 39823: 23542, + 39824: 23539, + 39825: 23557, + 39826: 23559, + 39827: 23560, + 39828: 23565, + 39829: 23571, + 39830: 23584, + 39831: 23586, + 39832: 23592, + 39833: 23608, + 39834: 23609, + 39835: 23617, + 39836: 23622, + 39837: 23630, + 39838: 23635, + 39839: 23632, + 39840: 23631, + 39841: 23409, + 39842: 23660, + 39843: 23662, + 39844: 20066, + 39845: 23670, + 39846: 23673, + 39847: 23692, + 39848: 23697, + 39849: 23700, + 39850: 22939, + 39851: 23723, + 39852: 23739, + 39853: 23734, + 39854: 23740, + 39855: 23735, + 39856: 23749, + 39857: 23742, + 39858: 23751, + 39859: 23769, + 39860: 23785, + 39861: 23805, + 39862: 23802, + 39863: 23789, + 39864: 23948, + 39865: 23786, + 39866: 23819, + 39867: 23829, + 39868: 23831, + 39869: 23900, + 39870: 23839, + 39871: 23835, + 39872: 23825, + 39873: 23828, + 39874: 23842, + 39875: 23834, + 39876: 23833, + 39877: 23832, + 39878: 23884, + 39879: 23890, + 39880: 23886, + 39881: 23883, + 39882: 23916, + 39883: 23923, + 39884: 23926, + 39885: 23943, + 39886: 23940, + 39887: 23938, + 39888: 23970, + 39889: 23965, + 39890: 23980, + 39891: 23982, + 39892: 23997, + 39893: 23952, + 39894: 23991, + 39895: 23996, + 39896: 24009, + 39897: 24013, + 39898: 24019, + 39899: 24018, + 39900: 24022, + 39901: 24027, + 39902: 24043, + 39903: 24050, + 39904: 24053, + 39905: 24075, + 39906: 24090, + 39907: 24089, + 39908: 24081, + 39909: 24091, + 39910: 24118, + 39911: 24119, + 39912: 24132, + 39913: 24131, + 39914: 24128, + 39915: 24142, + 39916: 24151, + 39917: 24148, + 39918: 24159, + 39919: 24162, + 39920: 24164, + 39921: 24135, + 39922: 24181, + 39923: 24182, + 39924: 24186, + 39925: 40636, + 39926: 24191, + 39927: 24224, + 39928: 24257, + 39929: 24258, + 39930: 24264, + 39931: 24272, + 39932: 24271, + 40000: 24278, + 40001: 24291, + 40002: 24285, + 40003: 24282, + 40004: 24283, + 40005: 24290, + 40006: 24289, + 40007: 24296, + 40008: 24297, + 40009: 24300, + 40010: 24305, + 40011: 24307, + 40012: 24304, + 40013: 24308, + 40014: 24312, + 40015: 24318, + 40016: 24323, + 40017: 24329, + 40018: 24413, + 40019: 24412, + 40020: 24331, + 40021: 24337, + 40022: 24342, + 40023: 24361, + 40024: 24365, + 40025: 24376, + 40026: 24385, + 40027: 24392, + 40028: 24396, + 40029: 24398, + 40030: 24367, + 40031: 24401, + 40032: 24406, + 40033: 24407, + 40034: 24409, + 40035: 24417, + 40036: 24429, + 40037: 24435, + 40038: 24439, + 40039: 24451, + 40040: 24450, + 40041: 24447, + 40042: 24458, + 40043: 24456, + 40044: 24465, + 40045: 24455, + 40046: 24478, + 40047: 24473, + 40048: 24472, + 40049: 24480, + 40050: 24488, + 40051: 24493, + 40052: 24508, + 40053: 24534, + 40054: 24571, + 40055: 24548, + 40056: 24568, + 40057: 24561, + 40058: 24541, + 40059: 24755, + 40060: 24575, + 40061: 24609, + 40062: 24672, + 40064: 24601, + 40065: 24592, + 40066: 24617, + 40067: 24590, + 40068: 24625, + 40069: 24603, + 40070: 24597, + 40071: 24619, + 40072: 24614, + 40073: 24591, + 40074: 24634, + 40075: 24666, + 40076: 24641, + 40077: 24682, + 40078: 24695, + 40079: 24671, + 40080: 24650, + 40081: 24646, + 40082: 24653, + 40083: 24675, + 40084: 24643, + 40085: 24676, + 40086: 24642, + 40087: 24684, + 40088: 24683, + 40089: 24665, + 40090: 24705, + 40091: 24717, + 40092: 24807, + 40093: 24707, + 40094: 24730, + 40095: 24708, + 40096: 24731, + 40097: 24726, + 40098: 24727, + 40099: 24722, + 40100: 24743, + 40101: 24715, + 40102: 24801, + 40103: 24760, + 40104: 24800, + 40105: 24787, + 40106: 24756, + 40107: 24560, + 40108: 24765, + 40109: 24774, + 40110: 24757, + 40111: 24792, + 40112: 24909, + 40113: 24853, + 40114: 24838, + 40115: 24822, + 40116: 24823, + 40117: 24832, + 40118: 24820, + 40119: 24826, + 40120: 24835, + 40121: 24865, + 40122: 24827, + 40123: 24817, + 40124: 24845, + 40125: 24846, + 40126: 24903, + 40127: 24894, + 40128: 24872, + 40129: 24871, + 40130: 24906, + 40131: 24895, + 40132: 24892, + 40133: 24876, + 40134: 24884, + 40135: 24893, + 40136: 24898, + 40137: 24900, + 40138: 24947, + 40139: 24951, + 40140: 24920, + 40141: 24921, + 40142: 24922, + 40143: 24939, + 40144: 24948, + 40145: 24943, + 40146: 24933, + 40147: 24945, + 40148: 24927, + 40149: 24925, + 40150: 24915, + 40151: 24949, + 40152: 24985, + 40153: 24982, + 40154: 24967, + 40155: 25004, + 40156: 24980, + 40157: 24986, + 40158: 24970, + 40159: 24977, + 40160: 25003, + 40161: 25006, + 40162: 25036, + 40163: 25034, + 40164: 25033, + 40165: 25079, + 40166: 25032, + 40167: 25027, + 40168: 25030, + 40169: 25018, + 40170: 25035, + 40171: 32633, + 40172: 25037, + 40173: 25062, + 40174: 25059, + 40175: 25078, + 40176: 25082, + 40177: 25076, + 40178: 25087, + 40179: 25085, + 40180: 25084, + 40181: 25086, + 40182: 25088, + 40183: 25096, + 40184: 25097, + 40185: 25101, + 40186: 25100, + 40187: 25108, + 40188: 25115, + 40256: 25118, + 40257: 25121, + 40258: 25130, + 40259: 25134, + 40260: 25136, + 40261: 25138, + 40262: 25139, + 40263: 25153, + 40264: 25166, + 40265: 25182, + 40266: 25187, + 40267: 25179, + 40268: 25184, + 40269: 25192, + 40270: 25212, + 40271: 25218, + 40272: 25225, + 40273: 25214, + 40274: 25234, + 40275: 25235, + 40276: 25238, + 40277: 25300, + 40278: 25219, + 40279: 25236, + 40280: 25303, + 40281: 25297, + 40282: 25275, + 40283: 25295, + 40284: 25343, + 40285: 25286, + 40286: 25812, + 40287: 25288, + 40288: 25308, + 40289: 25292, + 40290: 25290, + 40291: 25282, + 40292: 25287, + 40293: 25243, + 40294: 25289, + 40295: 25356, + 40296: 25326, + 40297: 25329, + 40298: 25383, + 40299: 25346, + 40300: 25352, + 40301: 25327, + 40302: 25333, + 40303: 25424, + 40304: 25406, + 40305: 25421, + 40306: 25628, + 40307: 25423, + 40308: 25494, + 40309: 25486, + 40310: 25472, + 40311: 25515, + 40312: 25462, + 40313: 25507, + 40314: 25487, + 40315: 25481, + 40316: 25503, + 40317: 25525, + 40318: 25451, + 40320: 25449, + 40321: 25534, + 40322: 25577, + 40323: 25536, + 40324: 25542, + 40325: 25571, + 40326: 25545, + 40327: 25554, + 40328: 25590, + 40329: 25540, + 40330: 25622, + 40331: 25652, + 40332: 25606, + 40333: 25619, + 40334: 25638, + 40335: 25654, + 40336: 25885, + 40337: 25623, + 40338: 25640, + 40339: 25615, + 40340: 25703, + 40341: 25711, + 40342: 25718, + 40343: 25678, + 40344: 25898, + 40345: 25749, + 40346: 25747, + 40347: 25765, + 40348: 25769, + 40349: 25736, + 40350: 25788, + 40351: 25818, + 40352: 25810, + 40353: 25797, + 40354: 25799, + 40355: 25787, + 40356: 25816, + 40357: 25794, + 40358: 25841, + 40359: 25831, + 40360: 33289, + 40361: 25824, + 40362: 25825, + 40363: 25260, + 40364: 25827, + 40365: 25839, + 40366: 25900, + 40367: 25846, + 40368: 25844, + 40369: 25842, + 40370: 25850, + 40371: 25856, + 40372: 25853, + 40373: 25880, + 40374: 25884, + 40375: 25861, + 40376: 25892, + 40377: 25891, + 40378: 25899, + 40379: 25908, + 40380: 25909, + 40381: 25911, + 40382: 25910, + 40383: 25912, + 40384: 30027, + 40385: 25928, + 40386: 25942, + 40387: 25941, + 40388: 25933, + 40389: 25944, + 40390: 25950, + 40391: 25949, + 40392: 25970, + 40393: 25976, + 40394: 25986, + 40395: 25987, + 40396: 35722, + 40397: 26011, + 40398: 26015, + 40399: 26027, + 40400: 26039, + 40401: 26051, + 40402: 26054, + 40403: 26049, + 40404: 26052, + 40405: 26060, + 40406: 26066, + 40407: 26075, + 40408: 26073, + 40409: 26080, + 40410: 26081, + 40411: 26097, + 40412: 26482, + 40413: 26122, + 40414: 26115, + 40415: 26107, + 40416: 26483, + 40417: 26165, + 40418: 26166, + 40419: 26164, + 40420: 26140, + 40421: 26191, + 40422: 26180, + 40423: 26185, + 40424: 26177, + 40425: 26206, + 40426: 26205, + 40427: 26212, + 40428: 26215, + 40429: 26216, + 40430: 26207, + 40431: 26210, + 40432: 26224, + 40433: 26243, + 40434: 26248, + 40435: 26254, + 40436: 26249, + 40437: 26244, + 40438: 26264, + 40439: 26269, + 40440: 26305, + 40441: 26297, + 40442: 26313, + 40443: 26302, + 40444: 26300, + 40512: 26308, + 40513: 26296, + 40514: 26326, + 40515: 26330, + 40516: 26336, + 40517: 26175, + 40518: 26342, + 40519: 26345, + 40520: 26352, + 40521: 26357, + 40522: 26359, + 40523: 26383, + 40524: 26390, + 40525: 26398, + 40526: 26406, + 40527: 26407, + 40528: 38712, + 40529: 26414, + 40530: 26431, + 40531: 26422, + 40532: 26433, + 40533: 26424, + 40534: 26423, + 40535: 26438, + 40536: 26462, + 40537: 26464, + 40538: 26457, + 40539: 26467, + 40540: 26468, + 40541: 26505, + 40542: 26480, + 40543: 26537, + 40544: 26492, + 40545: 26474, + 40546: 26508, + 40547: 26507, + 40548: 26534, + 40549: 26529, + 40550: 26501, + 40551: 26551, + 40552: 26607, + 40553: 26548, + 40554: 26604, + 40555: 26547, + 40556: 26601, + 40557: 26552, + 40558: 26596, + 40559: 26590, + 40560: 26589, + 40561: 26594, + 40562: 26606, + 40563: 26553, + 40564: 26574, + 40565: 26566, + 40566: 26599, + 40567: 27292, + 40568: 26654, + 40569: 26694, + 40570: 26665, + 40571: 26688, + 40572: 26701, + 40573: 26674, + 40574: 26702, + 40576: 26803, + 40577: 26667, + 40578: 26713, + 40579: 26723, + 40580: 26743, + 40581: 26751, + 40582: 26783, + 40583: 26767, + 40584: 26797, + 40585: 26772, + 40586: 26781, + 40587: 26779, + 40588: 26755, + 40589: 27310, + 40590: 26809, + 40591: 26740, + 40592: 26805, + 40593: 26784, + 40594: 26810, + 40595: 26895, + 40596: 26765, + 40597: 26750, + 40598: 26881, + 40599: 26826, + 40600: 26888, + 40601: 26840, + 40602: 26914, + 40603: 26918, + 40604: 26849, + 40605: 26892, + 40606: 26829, + 40607: 26836, + 40608: 26855, + 40609: 26837, + 40610: 26934, + 40611: 26898, + 40612: 26884, + 40613: 26839, + 40614: 26851, + 40615: 26917, + 40616: 26873, + 40617: 26848, + 40618: 26863, + 40619: 26920, + 40620: 26922, + 40621: 26906, + 40622: 26915, + 40623: 26913, + 40624: 26822, + 40625: 27001, + 40626: 26999, + 40627: 26972, + 40628: 27000, + 40629: 26987, + 40630: 26964, + 40631: 27006, + 40632: 26990, + 40633: 26937, + 40634: 26996, + 40635: 26941, + 40636: 26969, + 40637: 26928, + 40638: 26977, + 40639: 26974, + 40640: 26973, + 40641: 27009, + 40642: 26986, + 40643: 27058, + 40644: 27054, + 40645: 27088, + 40646: 27071, + 40647: 27073, + 40648: 27091, + 40649: 27070, + 40650: 27086, + 40651: 23528, + 40652: 27082, + 40653: 27101, + 40654: 27067, + 40655: 27075, + 40656: 27047, + 40657: 27182, + 40658: 27025, + 40659: 27040, + 40660: 27036, + 40661: 27029, + 40662: 27060, + 40663: 27102, + 40664: 27112, + 40665: 27138, + 40666: 27163, + 40667: 27135, + 40668: 27402, + 40669: 27129, + 40670: 27122, + 40671: 27111, + 40672: 27141, + 40673: 27057, + 40674: 27166, + 40675: 27117, + 40676: 27156, + 40677: 27115, + 40678: 27146, + 40679: 27154, + 40680: 27329, + 40681: 27171, + 40682: 27155, + 40683: 27204, + 40684: 27148, + 40685: 27250, + 40686: 27190, + 40687: 27256, + 40688: 27207, + 40689: 27234, + 40690: 27225, + 40691: 27238, + 40692: 27208, + 40693: 27192, + 40694: 27170, + 40695: 27280, + 40696: 27277, + 40697: 27296, + 40698: 27268, + 40699: 27298, + 40700: 27299, + 40768: 27287, + 40769: 34327, + 40770: 27323, + 40771: 27331, + 40772: 27330, + 40773: 27320, + 40774: 27315, + 40775: 27308, + 40776: 27358, + 40777: 27345, + 40778: 27359, + 40779: 27306, + 40780: 27354, + 40781: 27370, + 40782: 27387, + 40783: 27397, + 40784: 34326, + 40785: 27386, + 40786: 27410, + 40787: 27414, + 40788: 39729, + 40789: 27423, + 40790: 27448, + 40791: 27447, + 40792: 30428, + 40793: 27449, + 40794: 39150, + 40795: 27463, + 40796: 27459, + 40797: 27465, + 40798: 27472, + 40799: 27481, + 40800: 27476, + 40801: 27483, + 40802: 27487, + 40803: 27489, + 40804: 27512, + 40805: 27513, + 40806: 27519, + 40807: 27520, + 40808: 27524, + 40809: 27523, + 40810: 27533, + 40811: 27544, + 40812: 27541, + 40813: 27550, + 40814: 27556, + 40815: 27562, + 40816: 27563, + 40817: 27567, + 40818: 27570, + 40819: 27569, + 40820: 27571, + 40821: 27575, + 40822: 27580, + 40823: 27590, + 40824: 27595, + 40825: 27603, + 40826: 27615, + 40827: 27628, + 40828: 27627, + 40829: 27635, + 40830: 27631, + 40832: 40638, + 40833: 27656, + 40834: 27667, + 40835: 27668, + 40836: 27675, + 40837: 27684, + 40838: 27683, + 40839: 27742, + 40840: 27733, + 40841: 27746, + 40842: 27754, + 40843: 27778, + 40844: 27789, + 40845: 27802, + 40846: 27777, + 40847: 27803, + 40848: 27774, + 40849: 27752, + 40850: 27763, + 40851: 27794, + 40852: 27792, + 40853: 27844, + 40854: 27889, + 40855: 27859, + 40856: 27837, + 40857: 27863, + 40858: 27845, + 40859: 27869, + 40860: 27822, + 40861: 27825, + 40862: 27838, + 40863: 27834, + 40864: 27867, + 40865: 27887, + 40866: 27865, + 40867: 27882, + 40868: 27935, + 40869: 34893, + 40870: 27958, + 40871: 27947, + 40872: 27965, + 40873: 27960, + 40874: 27929, + 40875: 27957, + 40876: 27955, + 40877: 27922, + 40878: 27916, + 40879: 28003, + 40880: 28051, + 40881: 28004, + 40882: 27994, + 40883: 28025, + 40884: 27993, + 40885: 28046, + 40886: 28053, + 40887: 28644, + 40888: 28037, + 40889: 28153, + 40890: 28181, + 40891: 28170, + 40892: 28085, + 40893: 28103, + 40894: 28134, + 40895: 28088, + 40896: 28102, + 40897: 28140, + 40898: 28126, + 40899: 28108, + 40900: 28136, + 40901: 28114, + 40902: 28101, + 40903: 28154, + 40904: 28121, + 40905: 28132, + 40906: 28117, + 40907: 28138, + 40908: 28142, + 40909: 28205, + 40910: 28270, + 40911: 28206, + 40912: 28185, + 40913: 28274, + 40914: 28255, + 40915: 28222, + 40916: 28195, + 40917: 28267, + 40918: 28203, + 40919: 28278, + 40920: 28237, + 40921: 28191, + 40922: 28227, + 40923: 28218, + 40924: 28238, + 40925: 28196, + 40926: 28415, + 40927: 28189, + 40928: 28216, + 40929: 28290, + 40930: 28330, + 40931: 28312, + 40932: 28361, + 40933: 28343, + 40934: 28371, + 40935: 28349, + 40936: 28335, + 40937: 28356, + 40938: 28338, + 40939: 28372, + 40940: 28373, + 40941: 28303, + 40942: 28325, + 40943: 28354, + 40944: 28319, + 40945: 28481, + 40946: 28433, + 40947: 28748, + 40948: 28396, + 40949: 28408, + 40950: 28414, + 40951: 28479, + 40952: 28402, + 40953: 28465, + 40954: 28399, + 40955: 28466, + 40956: 28364, + 57408: 28478, + 57409: 28435, + 57410: 28407, + 57411: 28550, + 57412: 28538, + 57413: 28536, + 57414: 28545, + 57415: 28544, + 57416: 28527, + 57417: 28507, + 57418: 28659, + 57419: 28525, + 57420: 28546, + 57421: 28540, + 57422: 28504, + 57423: 28558, + 57424: 28561, + 57425: 28610, + 57426: 28518, + 57427: 28595, + 57428: 28579, + 57429: 28577, + 57430: 28580, + 57431: 28601, + 57432: 28614, + 57433: 28586, + 57434: 28639, + 57435: 28629, + 57436: 28652, + 57437: 28628, + 57438: 28632, + 57439: 28657, + 57440: 28654, + 57441: 28635, + 57442: 28681, + 57443: 28683, + 57444: 28666, + 57445: 28689, + 57446: 28673, + 57447: 28687, + 57448: 28670, + 57449: 28699, + 57450: 28698, + 57451: 28532, + 57452: 28701, + 57453: 28696, + 57454: 28703, + 57455: 28720, + 57456: 28734, + 57457: 28722, + 57458: 28753, + 57459: 28771, + 57460: 28825, + 57461: 28818, + 57462: 28847, + 57463: 28913, + 57464: 28844, + 57465: 28856, + 57466: 28851, + 57467: 28846, + 57468: 28895, + 57469: 28875, + 57470: 28893, + 57472: 28889, + 57473: 28937, + 57474: 28925, + 57475: 28956, + 57476: 28953, + 57477: 29029, + 57478: 29013, + 57479: 29064, + 57480: 29030, + 57481: 29026, + 57482: 29004, + 57483: 29014, + 57484: 29036, + 57485: 29071, + 57486: 29179, + 57487: 29060, + 57488: 29077, + 57489: 29096, + 57490: 29100, + 57491: 29143, + 57492: 29113, + 57493: 29118, + 57494: 29138, + 57495: 29129, + 57496: 29140, + 57497: 29134, + 57498: 29152, + 57499: 29164, + 57500: 29159, + 57501: 29173, + 57502: 29180, + 57503: 29177, + 57504: 29183, + 57505: 29197, + 57506: 29200, + 57507: 29211, + 57508: 29224, + 57509: 29229, + 57510: 29228, + 57511: 29232, + 57512: 29234, + 57513: 29243, + 57514: 29244, + 57515: 29247, + 57516: 29248, + 57517: 29254, + 57518: 29259, + 57519: 29272, + 57520: 29300, + 57521: 29310, + 57522: 29314, + 57523: 29313, + 57524: 29319, + 57525: 29330, + 57526: 29334, + 57527: 29346, + 57528: 29351, + 57529: 29369, + 57530: 29362, + 57531: 29379, + 57532: 29382, + 57533: 29380, + 57534: 29390, + 57535: 29394, + 57536: 29410, + 57537: 29408, + 57538: 29409, + 57539: 29433, + 57540: 29431, + 57541: 20495, + 57542: 29463, + 57543: 29450, + 57544: 29468, + 57545: 29462, + 57546: 29469, + 57547: 29492, + 57548: 29487, + 57549: 29481, + 57550: 29477, + 57551: 29502, + 57552: 29518, + 57553: 29519, + 57554: 40664, + 57555: 29527, + 57556: 29546, + 57557: 29544, + 57558: 29552, + 57559: 29560, + 57560: 29557, + 57561: 29563, + 57562: 29562, + 57563: 29640, + 57564: 29619, + 57565: 29646, + 57566: 29627, + 57567: 29632, + 57568: 29669, + 57569: 29678, + 57570: 29662, + 57571: 29858, + 57572: 29701, + 57573: 29807, + 57574: 29733, + 57575: 29688, + 57576: 29746, + 57577: 29754, + 57578: 29781, + 57579: 29759, + 57580: 29791, + 57581: 29785, + 57582: 29761, + 57583: 29788, + 57584: 29801, + 57585: 29808, + 57586: 29795, + 57587: 29802, + 57588: 29814, + 57589: 29822, + 57590: 29835, + 57591: 29854, + 57592: 29863, + 57593: 29898, + 57594: 29903, + 57595: 29908, + 57596: 29681, + 57664: 29920, + 57665: 29923, + 57666: 29927, + 57667: 29929, + 57668: 29934, + 57669: 29938, + 57670: 29936, + 57671: 29937, + 57672: 29944, + 57673: 29943, + 57674: 29956, + 57675: 29955, + 57676: 29957, + 57677: 29964, + 57678: 29966, + 57679: 29965, + 57680: 29973, + 57681: 29971, + 57682: 29982, + 57683: 29990, + 57684: 29996, + 57685: 30012, + 57686: 30020, + 57687: 30029, + 57688: 30026, + 57689: 30025, + 57690: 30043, + 57691: 30022, + 57692: 30042, + 57693: 30057, + 57694: 30052, + 57695: 30055, + 57696: 30059, + 57697: 30061, + 57698: 30072, + 57699: 30070, + 57700: 30086, + 57701: 30087, + 57702: 30068, + 57703: 30090, + 57704: 30089, + 57705: 30082, + 57706: 30100, + 57707: 30106, + 57708: 30109, + 57709: 30117, + 57710: 30115, + 57711: 30146, + 57712: 30131, + 57713: 30147, + 57714: 30133, + 57715: 30141, + 57716: 30136, + 57717: 30140, + 57718: 30129, + 57719: 30157, + 57720: 30154, + 57721: 30162, + 57722: 30169, + 57723: 30179, + 57724: 30174, + 57725: 30206, + 57726: 30207, + 57728: 30204, + 57729: 30209, + 57730: 30192, + 57731: 30202, + 57732: 30194, + 57733: 30195, + 57734: 30219, + 57735: 30221, + 57736: 30217, + 57737: 30239, + 57738: 30247, + 57739: 30240, + 57740: 30241, + 57741: 30242, + 57742: 30244, + 57743: 30260, + 57744: 30256, + 57745: 30267, + 57746: 30279, + 57747: 30280, + 57748: 30278, + 57749: 30300, + 57750: 30296, + 57751: 30305, + 57752: 30306, + 57753: 30312, + 57754: 30313, + 57755: 30314, + 57756: 30311, + 57757: 30316, + 57758: 30320, + 57759: 30322, + 57760: 30326, + 57761: 30328, + 57762: 30332, + 57763: 30336, + 57764: 30339, + 57765: 30344, + 57766: 30347, + 57767: 30350, + 57768: 30358, + 57769: 30355, + 57770: 30361, + 57771: 30362, + 57772: 30384, + 57773: 30388, + 57774: 30392, + 57775: 30393, + 57776: 30394, + 57777: 30402, + 57778: 30413, + 57779: 30422, + 57780: 30418, + 57781: 30430, + 57782: 30433, + 57783: 30437, + 57784: 30439, + 57785: 30442, + 57786: 34351, + 57787: 30459, + 57788: 30472, + 57789: 30471, + 57790: 30468, + 57791: 30505, + 57792: 30500, + 57793: 30494, + 57794: 30501, + 57795: 30502, + 57796: 30491, + 57797: 30519, + 57798: 30520, + 57799: 30535, + 57800: 30554, + 57801: 30568, + 57802: 30571, + 57803: 30555, + 57804: 30565, + 57805: 30591, + 57806: 30590, + 57807: 30585, + 57808: 30606, + 57809: 30603, + 57810: 30609, + 57811: 30624, + 57812: 30622, + 57813: 30640, + 57814: 30646, + 57815: 30649, + 57816: 30655, + 57817: 30652, + 57818: 30653, + 57819: 30651, + 57820: 30663, + 57821: 30669, + 57822: 30679, + 57823: 30682, + 57824: 30684, + 57825: 30691, + 57826: 30702, + 57827: 30716, + 57828: 30732, + 57829: 30738, + 57830: 31014, + 57831: 30752, + 57832: 31018, + 57833: 30789, + 57834: 30862, + 57835: 30836, + 57836: 30854, + 57837: 30844, + 57838: 30874, + 57839: 30860, + 57840: 30883, + 57841: 30901, + 57842: 30890, + 57843: 30895, + 57844: 30929, + 57845: 30918, + 57846: 30923, + 57847: 30932, + 57848: 30910, + 57849: 30908, + 57850: 30917, + 57851: 30922, + 57852: 30956, + 57920: 30951, + 57921: 30938, + 57922: 30973, + 57923: 30964, + 57924: 30983, + 57925: 30994, + 57926: 30993, + 57927: 31001, + 57928: 31020, + 57929: 31019, + 57930: 31040, + 57931: 31072, + 57932: 31063, + 57933: 31071, + 57934: 31066, + 57935: 31061, + 57936: 31059, + 57937: 31098, + 57938: 31103, + 57939: 31114, + 57940: 31133, + 57941: 31143, + 57942: 40779, + 57943: 31146, + 57944: 31150, + 57945: 31155, + 57946: 31161, + 57947: 31162, + 57948: 31177, + 57949: 31189, + 57950: 31207, + 57951: 31212, + 57952: 31201, + 57953: 31203, + 57954: 31240, + 57955: 31245, + 57956: 31256, + 57957: 31257, + 57958: 31264, + 57959: 31263, + 57960: 31104, + 57961: 31281, + 57962: 31291, + 57963: 31294, + 57964: 31287, + 57965: 31299, + 57966: 31319, + 57967: 31305, + 57968: 31329, + 57969: 31330, + 57970: 31337, + 57971: 40861, + 57972: 31344, + 57973: 31353, + 57974: 31357, + 57975: 31368, + 57976: 31383, + 57977: 31381, + 57978: 31384, + 57979: 31382, + 57980: 31401, + 57981: 31432, + 57982: 31408, + 57984: 31414, + 57985: 31429, + 57986: 31428, + 57987: 31423, + 57988: 36995, + 57989: 31431, + 57990: 31434, + 57991: 31437, + 57992: 31439, + 57993: 31445, + 57994: 31443, + 57995: 31449, + 57996: 31450, + 57997: 31453, + 57998: 31457, + 57999: 31458, + 58000: 31462, + 58001: 31469, + 58002: 31472, + 58003: 31490, + 58004: 31503, + 58005: 31498, + 58006: 31494, + 58007: 31539, + 58008: 31512, + 58009: 31513, + 58010: 31518, + 58011: 31541, + 58012: 31528, + 58013: 31542, + 58014: 31568, + 58015: 31610, + 58016: 31492, + 58017: 31565, + 58018: 31499, + 58019: 31564, + 58020: 31557, + 58021: 31605, + 58022: 31589, + 58023: 31604, + 58024: 31591, + 58025: 31600, + 58026: 31601, + 58027: 31596, + 58028: 31598, + 58029: 31645, + 58030: 31640, + 58031: 31647, + 58032: 31629, + 58033: 31644, + 58034: 31642, + 58035: 31627, + 58036: 31634, + 58037: 31631, + 58038: 31581, + 58039: 31641, + 58040: 31691, + 58041: 31681, + 58042: 31692, + 58043: 31695, + 58044: 31668, + 58045: 31686, + 58046: 31709, + 58047: 31721, + 58048: 31761, + 58049: 31764, + 58050: 31718, + 58051: 31717, + 58052: 31840, + 58053: 31744, + 58054: 31751, + 58055: 31763, + 58056: 31731, + 58057: 31735, + 58058: 31767, + 58059: 31757, + 58060: 31734, + 58061: 31779, + 58062: 31783, + 58063: 31786, + 58064: 31775, + 58065: 31799, + 58066: 31787, + 58067: 31805, + 58068: 31820, + 58069: 31811, + 58070: 31828, + 58071: 31823, + 58072: 31808, + 58073: 31824, + 58074: 31832, + 58075: 31839, + 58076: 31844, + 58077: 31830, + 58078: 31845, + 58079: 31852, + 58080: 31861, + 58081: 31875, + 58082: 31888, + 58083: 31908, + 58084: 31917, + 58085: 31906, + 58086: 31915, + 58087: 31905, + 58088: 31912, + 58089: 31923, + 58090: 31922, + 58091: 31921, + 58092: 31918, + 58093: 31929, + 58094: 31933, + 58095: 31936, + 58096: 31941, + 58097: 31938, + 58098: 31960, + 58099: 31954, + 58100: 31964, + 58101: 31970, + 58102: 39739, + 58103: 31983, + 58104: 31986, + 58105: 31988, + 58106: 31990, + 58107: 31994, + 58108: 32006, + 58176: 32002, + 58177: 32028, + 58178: 32021, + 58179: 32010, + 58180: 32069, + 58181: 32075, + 58182: 32046, + 58183: 32050, + 58184: 32063, + 58185: 32053, + 58186: 32070, + 58187: 32115, + 58188: 32086, + 58189: 32078, + 58190: 32114, + 58191: 32104, + 58192: 32110, + 58193: 32079, + 58194: 32099, + 58195: 32147, + 58196: 32137, + 58197: 32091, + 58198: 32143, + 58199: 32125, + 58200: 32155, + 58201: 32186, + 58202: 32174, + 58203: 32163, + 58204: 32181, + 58205: 32199, + 58206: 32189, + 58207: 32171, + 58208: 32317, + 58209: 32162, + 58210: 32175, + 58211: 32220, + 58212: 32184, + 58213: 32159, + 58214: 32176, + 58215: 32216, + 58216: 32221, + 58217: 32228, + 58218: 32222, + 58219: 32251, + 58220: 32242, + 58221: 32225, + 58222: 32261, + 58223: 32266, + 58224: 32291, + 58225: 32289, + 58226: 32274, + 58227: 32305, + 58228: 32287, + 58229: 32265, + 58230: 32267, + 58231: 32290, + 58232: 32326, + 58233: 32358, + 58234: 32315, + 58235: 32309, + 58236: 32313, + 58237: 32323, + 58238: 32311, + 58240: 32306, + 58241: 32314, + 58242: 32359, + 58243: 32349, + 58244: 32342, + 58245: 32350, + 58246: 32345, + 58247: 32346, + 58248: 32377, + 58249: 32362, + 58250: 32361, + 58251: 32380, + 58252: 32379, + 58253: 32387, + 58254: 32213, + 58255: 32381, + 58256: 36782, + 58257: 32383, + 58258: 32392, + 58259: 32393, + 58260: 32396, + 58261: 32402, + 58262: 32400, + 58263: 32403, + 58264: 32404, + 58265: 32406, + 58266: 32398, + 58267: 32411, + 58268: 32412, + 58269: 32568, + 58270: 32570, + 58271: 32581, + 58272: 32588, + 58273: 32589, + 58274: 32590, + 58275: 32592, + 58276: 32593, + 58277: 32597, + 58278: 32596, + 58279: 32600, + 58280: 32607, + 58281: 32608, + 58282: 32616, + 58283: 32617, + 58284: 32615, + 58285: 32632, + 58286: 32642, + 58287: 32646, + 58288: 32643, + 58289: 32648, + 58290: 32647, + 58291: 32652, + 58292: 32660, + 58293: 32670, + 58294: 32669, + 58295: 32666, + 58296: 32675, + 58297: 32687, + 58298: 32690, + 58299: 32697, + 58300: 32686, + 58301: 32694, + 58302: 32696, + 58303: 35697, + 58304: 32709, + 58305: 32710, + 58306: 32714, + 58307: 32725, + 58308: 32724, + 58309: 32737, + 58310: 32742, + 58311: 32745, + 58312: 32755, + 58313: 32761, + 58314: 39132, + 58315: 32774, + 58316: 32772, + 58317: 32779, + 58318: 32786, + 58319: 32792, + 58320: 32793, + 58321: 32796, + 58322: 32801, + 58323: 32808, + 58324: 32831, + 58325: 32827, + 58326: 32842, + 58327: 32838, + 58328: 32850, + 58329: 32856, + 58330: 32858, + 58331: 32863, + 58332: 32866, + 58333: 32872, + 58334: 32883, + 58335: 32882, + 58336: 32880, + 58337: 32886, + 58338: 32889, + 58339: 32893, + 58340: 32895, + 58341: 32900, + 58342: 32902, + 58343: 32901, + 58344: 32923, + 58345: 32915, + 58346: 32922, + 58347: 32941, + 58348: 20880, + 58349: 32940, + 58350: 32987, + 58351: 32997, + 58352: 32985, + 58353: 32989, + 58354: 32964, + 58355: 32986, + 58356: 32982, + 58357: 33033, + 58358: 33007, + 58359: 33009, + 58360: 33051, + 58361: 33065, + 58362: 33059, + 58363: 33071, + 58364: 33099, + 58432: 38539, + 58433: 33094, + 58434: 33086, + 58435: 33107, + 58436: 33105, + 58437: 33020, + 58438: 33137, + 58439: 33134, + 58440: 33125, + 58441: 33126, + 58442: 33140, + 58443: 33155, + 58444: 33160, + 58445: 33162, + 58446: 33152, + 58447: 33154, + 58448: 33184, + 58449: 33173, + 58450: 33188, + 58451: 33187, + 58452: 33119, + 58453: 33171, + 58454: 33193, + 58455: 33200, + 58456: 33205, + 58457: 33214, + 58458: 33208, + 58459: 33213, + 58460: 33216, + 58461: 33218, + 58462: 33210, + 58463: 33225, + 58464: 33229, + 58465: 33233, + 58466: 33241, + 58467: 33240, + 58468: 33224, + 58469: 33242, + 58470: 33247, + 58471: 33248, + 58472: 33255, + 58473: 33274, + 58474: 33275, + 58475: 33278, + 58476: 33281, + 58477: 33282, + 58478: 33285, + 58479: 33287, + 58480: 33290, + 58481: 33293, + 58482: 33296, + 58483: 33302, + 58484: 33321, + 58485: 33323, + 58486: 33336, + 58487: 33331, + 58488: 33344, + 58489: 33369, + 58490: 33368, + 58491: 33373, + 58492: 33370, + 58493: 33375, + 58494: 33380, + 58496: 33378, + 58497: 33384, + 58498: 33386, + 58499: 33387, + 58500: 33326, + 58501: 33393, + 58502: 33399, + 58503: 33400, + 58504: 33406, + 58505: 33421, + 58506: 33426, + 58507: 33451, + 58508: 33439, + 58509: 33467, + 58510: 33452, + 58511: 33505, + 58512: 33507, + 58513: 33503, + 58514: 33490, + 58515: 33524, + 58516: 33523, + 58517: 33530, + 58518: 33683, + 58519: 33539, + 58520: 33531, + 58521: 33529, + 58522: 33502, + 58523: 33542, + 58524: 33500, + 58525: 33545, + 58526: 33497, + 58527: 33589, + 58528: 33588, + 58529: 33558, + 58530: 33586, + 58531: 33585, + 58532: 33600, + 58533: 33593, + 58534: 33616, + 58535: 33605, + 58536: 33583, + 58537: 33579, + 58538: 33559, + 58539: 33560, + 58540: 33669, + 58541: 33690, + 58542: 33706, + 58543: 33695, + 58544: 33698, + 58545: 33686, + 58546: 33571, + 58547: 33678, + 58548: 33671, + 58549: 33674, + 58550: 33660, + 58551: 33717, + 58552: 33651, + 58553: 33653, + 58554: 33696, + 58555: 33673, + 58556: 33704, + 58557: 33780, + 58558: 33811, + 58559: 33771, + 58560: 33742, + 58561: 33789, + 58562: 33795, + 58563: 33752, + 58564: 33803, + 58565: 33729, + 58566: 33783, + 58567: 33799, + 58568: 33760, + 58569: 33778, + 58570: 33805, + 58571: 33826, + 58572: 33824, + 58573: 33725, + 58574: 33848, + 58575: 34054, + 58576: 33787, + 58577: 33901, + 58578: 33834, + 58579: 33852, + 58580: 34138, + 58581: 33924, + 58582: 33911, + 58583: 33899, + 58584: 33965, + 58585: 33902, + 58586: 33922, + 58587: 33897, + 58588: 33862, + 58589: 33836, + 58590: 33903, + 58591: 33913, + 58592: 33845, + 58593: 33994, + 58594: 33890, + 58595: 33977, + 58596: 33983, + 58597: 33951, + 58598: 34009, + 58599: 33997, + 58600: 33979, + 58601: 34010, + 58602: 34000, + 58603: 33985, + 58604: 33990, + 58605: 34006, + 58606: 33953, + 58607: 34081, + 58608: 34047, + 58609: 34036, + 58610: 34071, + 58611: 34072, + 58612: 34092, + 58613: 34079, + 58614: 34069, + 58615: 34068, + 58616: 34044, + 58617: 34112, + 58618: 34147, + 58619: 34136, + 58620: 34120, + 58688: 34113, + 58689: 34306, + 58690: 34123, + 58691: 34133, + 58692: 34176, + 58693: 34212, + 58694: 34184, + 58695: 34193, + 58696: 34186, + 58697: 34216, + 58698: 34157, + 58699: 34196, + 58700: 34203, + 58701: 34282, + 58702: 34183, + 58703: 34204, + 58704: 34167, + 58705: 34174, + 58706: 34192, + 58707: 34249, + 58708: 34234, + 58709: 34255, + 58710: 34233, + 58711: 34256, + 58712: 34261, + 58713: 34269, + 58714: 34277, + 58715: 34268, + 58716: 34297, + 58717: 34314, + 58718: 34323, + 58719: 34315, + 58720: 34302, + 58721: 34298, + 58722: 34310, + 58723: 34338, + 58724: 34330, + 58725: 34352, + 58726: 34367, + 58727: 34381, + 58728: 20053, + 58729: 34388, + 58730: 34399, + 58731: 34407, + 58732: 34417, + 58733: 34451, + 58734: 34467, + 58735: 34473, + 58736: 34474, + 58737: 34443, + 58738: 34444, + 58739: 34486, + 58740: 34479, + 58741: 34500, + 58742: 34502, + 58743: 34480, + 58744: 34505, + 58745: 34851, + 58746: 34475, + 58747: 34516, + 58748: 34526, + 58749: 34537, + 58750: 34540, + 58752: 34527, + 58753: 34523, + 58754: 34543, + 58755: 34578, + 58756: 34566, + 58757: 34568, + 58758: 34560, + 58759: 34563, + 58760: 34555, + 58761: 34577, + 58762: 34569, + 58763: 34573, + 58764: 34553, + 58765: 34570, + 58766: 34612, + 58767: 34623, + 58768: 34615, + 58769: 34619, + 58770: 34597, + 58771: 34601, + 58772: 34586, + 58773: 34656, + 58774: 34655, + 58775: 34680, + 58776: 34636, + 58777: 34638, + 58778: 34676, + 58779: 34647, + 58780: 34664, + 58781: 34670, + 58782: 34649, + 58783: 34643, + 58784: 34659, + 58785: 34666, + 58786: 34821, + 58787: 34722, + 58788: 34719, + 58789: 34690, + 58790: 34735, + 58791: 34763, + 58792: 34749, + 58793: 34752, + 58794: 34768, + 58795: 38614, + 58796: 34731, + 58797: 34756, + 58798: 34739, + 58799: 34759, + 58800: 34758, + 58801: 34747, + 58802: 34799, + 58803: 34802, + 58804: 34784, + 58805: 34831, + 58806: 34829, + 58807: 34814, + 58808: 34806, + 58809: 34807, + 58810: 34830, + 58811: 34770, + 58812: 34833, + 58813: 34838, + 58814: 34837, + 58815: 34850, + 58816: 34849, + 58817: 34865, + 58818: 34870, + 58819: 34873, + 58820: 34855, + 58821: 34875, + 58822: 34884, + 58823: 34882, + 58824: 34898, + 58825: 34905, + 58826: 34910, + 58827: 34914, + 58828: 34923, + 58829: 34945, + 58830: 34942, + 58831: 34974, + 58832: 34933, + 58833: 34941, + 58834: 34997, + 58835: 34930, + 58836: 34946, + 58837: 34967, + 58838: 34962, + 58839: 34990, + 58840: 34969, + 58841: 34978, + 58842: 34957, + 58843: 34980, + 58844: 34992, + 58845: 35007, + 58846: 34993, + 58847: 35011, + 58848: 35012, + 58849: 35028, + 58850: 35032, + 58851: 35033, + 58852: 35037, + 58853: 35065, + 58854: 35074, + 58855: 35068, + 58856: 35060, + 58857: 35048, + 58858: 35058, + 58859: 35076, + 58860: 35084, + 58861: 35082, + 58862: 35091, + 58863: 35139, + 58864: 35102, + 58865: 35109, + 58866: 35114, + 58867: 35115, + 58868: 35137, + 58869: 35140, + 58870: 35131, + 58871: 35126, + 58872: 35128, + 58873: 35148, + 58874: 35101, + 58875: 35168, + 58876: 35166, + 58944: 35174, + 58945: 35172, + 58946: 35181, + 58947: 35178, + 58948: 35183, + 58949: 35188, + 58950: 35191, + 58951: 35198, + 58952: 35203, + 58953: 35208, + 58954: 35210, + 58955: 35219, + 58956: 35224, + 58957: 35233, + 58958: 35241, + 58959: 35238, + 58960: 35244, + 58961: 35247, + 58962: 35250, + 58963: 35258, + 58964: 35261, + 58965: 35263, + 58966: 35264, + 58967: 35290, + 58968: 35292, + 58969: 35293, + 58970: 35303, + 58971: 35316, + 58972: 35320, + 58973: 35331, + 58974: 35350, + 58975: 35344, + 58976: 35340, + 58977: 35355, + 58978: 35357, + 58979: 35365, + 58980: 35382, + 58981: 35393, + 58982: 35419, + 58983: 35410, + 58984: 35398, + 58985: 35400, + 58986: 35452, + 58987: 35437, + 58988: 35436, + 58989: 35426, + 58990: 35461, + 58991: 35458, + 58992: 35460, + 58993: 35496, + 58994: 35489, + 58995: 35473, + 58996: 35493, + 58997: 35494, + 58998: 35482, + 58999: 35491, + 59000: 35524, + 59001: 35533, + 59002: 35522, + 59003: 35546, + 59004: 35563, + 59005: 35571, + 59006: 35559, + 59008: 35556, + 59009: 35569, + 59010: 35604, + 59011: 35552, + 59012: 35554, + 59013: 35575, + 59014: 35550, + 59015: 35547, + 59016: 35596, + 59017: 35591, + 59018: 35610, + 59019: 35553, + 59020: 35606, + 59021: 35600, + 59022: 35607, + 59023: 35616, + 59024: 35635, + 59025: 38827, + 59026: 35622, + 59027: 35627, + 59028: 35646, + 59029: 35624, + 59030: 35649, + 59031: 35660, + 59032: 35663, + 59033: 35662, + 59034: 35657, + 59035: 35670, + 59036: 35675, + 59037: 35674, + 59038: 35691, + 59039: 35679, + 59040: 35692, + 59041: 35695, + 59042: 35700, + 59043: 35709, + 59044: 35712, + 59045: 35724, + 59046: 35726, + 59047: 35730, + 59048: 35731, + 59049: 35734, + 59050: 35737, + 59051: 35738, + 59052: 35898, + 59053: 35905, + 59054: 35903, + 59055: 35912, + 59056: 35916, + 59057: 35918, + 59058: 35920, + 59059: 35925, + 59060: 35938, + 59061: 35948, + 59062: 35960, + 59063: 35962, + 59064: 35970, + 59065: 35977, + 59066: 35973, + 59067: 35978, + 59068: 35981, + 59069: 35982, + 59070: 35988, + 59071: 35964, + 59072: 35992, + 59073: 25117, + 59074: 36013, + 59075: 36010, + 59076: 36029, + 59077: 36018, + 59078: 36019, + 59079: 36014, + 59080: 36022, + 59081: 36040, + 59082: 36033, + 59083: 36068, + 59084: 36067, + 59085: 36058, + 59086: 36093, + 59087: 36090, + 59088: 36091, + 59089: 36100, + 59090: 36101, + 59091: 36106, + 59092: 36103, + 59093: 36111, + 59094: 36109, + 59095: 36112, + 59096: 40782, + 59097: 36115, + 59098: 36045, + 59099: 36116, + 59100: 36118, + 59101: 36199, + 59102: 36205, + 59103: 36209, + 59104: 36211, + 59105: 36225, + 59106: 36249, + 59107: 36290, + 59108: 36286, + 59109: 36282, + 59110: 36303, + 59111: 36314, + 59112: 36310, + 59113: 36300, + 59114: 36315, + 59115: 36299, + 59116: 36330, + 59117: 36331, + 59118: 36319, + 59119: 36323, + 59120: 36348, + 59121: 36360, + 59122: 36361, + 59123: 36351, + 59124: 36381, + 59125: 36382, + 59126: 36368, + 59127: 36383, + 59128: 36418, + 59129: 36405, + 59130: 36400, + 59131: 36404, + 59132: 36426, + 59200: 36423, + 59201: 36425, + 59202: 36428, + 59203: 36432, + 59204: 36424, + 59205: 36441, + 59206: 36452, + 59207: 36448, + 59208: 36394, + 59209: 36451, + 59210: 36437, + 59211: 36470, + 59212: 36466, + 59213: 36476, + 59214: 36481, + 59215: 36487, + 59216: 36485, + 59217: 36484, + 59218: 36491, + 59219: 36490, + 59220: 36499, + 59221: 36497, + 59222: 36500, + 59223: 36505, + 59224: 36522, + 59225: 36513, + 59226: 36524, + 59227: 36528, + 59228: 36550, + 59229: 36529, + 59230: 36542, + 59231: 36549, + 59232: 36552, + 59233: 36555, + 59234: 36571, + 59235: 36579, + 59236: 36604, + 59237: 36603, + 59238: 36587, + 59239: 36606, + 59240: 36618, + 59241: 36613, + 59242: 36629, + 59243: 36626, + 59244: 36633, + 59245: 36627, + 59246: 36636, + 59247: 36639, + 59248: 36635, + 59249: 36620, + 59250: 36646, + 59251: 36659, + 59252: 36667, + 59253: 36665, + 59254: 36677, + 59255: 36674, + 59256: 36670, + 59257: 36684, + 59258: 36681, + 59259: 36678, + 59260: 36686, + 59261: 36695, + 59262: 36700, + 59264: 36706, + 59265: 36707, + 59266: 36708, + 59267: 36764, + 59268: 36767, + 59269: 36771, + 59270: 36781, + 59271: 36783, + 59272: 36791, + 59273: 36826, + 59274: 36837, + 59275: 36834, + 59276: 36842, + 59277: 36847, + 59278: 36999, + 59279: 36852, + 59280: 36869, + 59281: 36857, + 59282: 36858, + 59283: 36881, + 59284: 36885, + 59285: 36897, + 59286: 36877, + 59287: 36894, + 59288: 36886, + 59289: 36875, + 59290: 36903, + 59291: 36918, + 59292: 36917, + 59293: 36921, + 59294: 36856, + 59295: 36943, + 59296: 36944, + 59297: 36945, + 59298: 36946, + 59299: 36878, + 59300: 36937, + 59301: 36926, + 59302: 36950, + 59303: 36952, + 59304: 36958, + 59305: 36968, + 59306: 36975, + 59307: 36982, + 59308: 38568, + 59309: 36978, + 59310: 36994, + 59311: 36989, + 59312: 36993, + 59313: 36992, + 59314: 37002, + 59315: 37001, + 59316: 37007, + 59317: 37032, + 59318: 37039, + 59319: 37041, + 59320: 37045, + 59321: 37090, + 59322: 37092, + 59323: 25160, + 59324: 37083, + 59325: 37122, + 59326: 37138, + 59327: 37145, + 59328: 37170, + 59329: 37168, + 59330: 37194, + 59331: 37206, + 59332: 37208, + 59333: 37219, + 59334: 37221, + 59335: 37225, + 59336: 37235, + 59337: 37234, + 59338: 37259, + 59339: 37257, + 59340: 37250, + 59341: 37282, + 59342: 37291, + 59343: 37295, + 59344: 37290, + 59345: 37301, + 59346: 37300, + 59347: 37306, + 59348: 37312, + 59349: 37313, + 59350: 37321, + 59351: 37323, + 59352: 37328, + 59353: 37334, + 59354: 37343, + 59355: 37345, + 59356: 37339, + 59357: 37372, + 59358: 37365, + 59359: 37366, + 59360: 37406, + 59361: 37375, + 59362: 37396, + 59363: 37420, + 59364: 37397, + 59365: 37393, + 59366: 37470, + 59367: 37463, + 59368: 37445, + 59369: 37449, + 59370: 37476, + 59371: 37448, + 59372: 37525, + 59373: 37439, + 59374: 37451, + 59375: 37456, + 59376: 37532, + 59377: 37526, + 59378: 37523, + 59379: 37531, + 59380: 37466, + 59381: 37583, + 59382: 37561, + 59383: 37559, + 59384: 37609, + 59385: 37647, + 59386: 37626, + 59387: 37700, + 59388: 37678, + 59456: 37657, + 59457: 37666, + 59458: 37658, + 59459: 37667, + 59460: 37690, + 59461: 37685, + 59462: 37691, + 59463: 37724, + 59464: 37728, + 59465: 37756, + 59466: 37742, + 59467: 37718, + 59468: 37808, + 59469: 37804, + 59470: 37805, + 59471: 37780, + 59472: 37817, + 59473: 37846, + 59474: 37847, + 59475: 37864, + 59476: 37861, + 59477: 37848, + 59478: 37827, + 59479: 37853, + 59480: 37840, + 59481: 37832, + 59482: 37860, + 59483: 37914, + 59484: 37908, + 59485: 37907, + 59486: 37891, + 59487: 37895, + 59488: 37904, + 59489: 37942, + 59490: 37931, + 59491: 37941, + 59492: 37921, + 59493: 37946, + 59494: 37953, + 59495: 37970, + 59496: 37956, + 59497: 37979, + 59498: 37984, + 59499: 37986, + 59500: 37982, + 59501: 37994, + 59502: 37417, + 59503: 38000, + 59504: 38005, + 59505: 38007, + 59506: 38013, + 59507: 37978, + 59508: 38012, + 59509: 38014, + 59510: 38017, + 59511: 38015, + 59512: 38274, + 59513: 38279, + 59514: 38282, + 59515: 38292, + 59516: 38294, + 59517: 38296, + 59518: 38297, + 59520: 38304, + 59521: 38312, + 59522: 38311, + 59523: 38317, + 59524: 38332, + 59525: 38331, + 59526: 38329, + 59527: 38334, + 59528: 38346, + 59529: 28662, + 59530: 38339, + 59531: 38349, + 59532: 38348, + 59533: 38357, + 59534: 38356, + 59535: 38358, + 59536: 38364, + 59537: 38369, + 59538: 38373, + 59539: 38370, + 59540: 38433, + 59541: 38440, + 59542: 38446, + 59543: 38447, + 59544: 38466, + 59545: 38476, + 59546: 38479, + 59547: 38475, + 59548: 38519, + 59549: 38492, + 59550: 38494, + 59551: 38493, + 59552: 38495, + 59553: 38502, + 59554: 38514, + 59555: 38508, + 59556: 38541, + 59557: 38552, + 59558: 38549, + 59559: 38551, + 59560: 38570, + 59561: 38567, + 59562: 38577, + 59563: 38578, + 59564: 38576, + 59565: 38580, + 59566: 38582, + 59567: 38584, + 59568: 38585, + 59569: 38606, + 59570: 38603, + 59571: 38601, + 59572: 38605, + 59573: 35149, + 59574: 38620, + 59575: 38669, + 59576: 38613, + 59577: 38649, + 59578: 38660, + 59579: 38662, + 59580: 38664, + 59581: 38675, + 59582: 38670, + 59583: 38673, + 59584: 38671, + 59585: 38678, + 59586: 38681, + 59587: 38692, + 59588: 38698, + 59589: 38704, + 59590: 38713, + 59591: 38717, + 59592: 38718, + 59593: 38724, + 59594: 38726, + 59595: 38728, + 59596: 38722, + 59597: 38729, + 59598: 38748, + 59599: 38752, + 59600: 38756, + 59601: 38758, + 59602: 38760, + 59603: 21202, + 59604: 38763, + 59605: 38769, + 59606: 38777, + 59607: 38789, + 59608: 38780, + 59609: 38785, + 59610: 38778, + 59611: 38790, + 59612: 38795, + 59613: 38799, + 59614: 38800, + 59615: 38812, + 59616: 38824, + 59617: 38822, + 59618: 38819, + 59619: 38835, + 59620: 38836, + 59621: 38851, + 59622: 38854, + 59623: 38856, + 59624: 38859, + 59625: 38876, + 59626: 38893, + 59627: 40783, + 59628: 38898, + 59629: 31455, + 59630: 38902, + 59631: 38901, + 59632: 38927, + 59633: 38924, + 59634: 38968, + 59635: 38948, + 59636: 38945, + 59637: 38967, + 59638: 38973, + 59639: 38982, + 59640: 38991, + 59641: 38987, + 59642: 39019, + 59643: 39023, + 59644: 39024, + 59712: 39025, + 59713: 39028, + 59714: 39027, + 59715: 39082, + 59716: 39087, + 59717: 39089, + 59718: 39094, + 59719: 39108, + 59720: 39107, + 59721: 39110, + 59722: 39145, + 59723: 39147, + 59724: 39171, + 59725: 39177, + 59726: 39186, + 59727: 39188, + 59728: 39192, + 59729: 39201, + 59730: 39197, + 59731: 39198, + 59732: 39204, + 59733: 39200, + 59734: 39212, + 59735: 39214, + 59736: 39229, + 59737: 39230, + 59738: 39234, + 59739: 39241, + 59740: 39237, + 59741: 39248, + 59742: 39243, + 59743: 39249, + 59744: 39250, + 59745: 39244, + 59746: 39253, + 59747: 39319, + 59748: 39320, + 59749: 39333, + 59750: 39341, + 59751: 39342, + 59752: 39356, + 59753: 39391, + 59754: 39387, + 59755: 39389, + 59756: 39384, + 59757: 39377, + 59758: 39405, + 59759: 39406, + 59760: 39409, + 59761: 39410, + 59762: 39419, + 59763: 39416, + 59764: 39425, + 59765: 39439, + 59766: 39429, + 59767: 39394, + 59768: 39449, + 59769: 39467, + 59770: 39479, + 59771: 39493, + 59772: 39490, + 59773: 39488, + 59774: 39491, + 59776: 39486, + 59777: 39509, + 59778: 39501, + 59779: 39515, + 59780: 39511, + 59781: 39519, + 59782: 39522, + 59783: 39525, + 59784: 39524, + 59785: 39529, + 59786: 39531, + 59787: 39530, + 59788: 39597, + 59789: 39600, + 59790: 39612, + 59791: 39616, + 59792: 39631, + 59793: 39633, + 59794: 39635, + 59795: 39636, + 59796: 39646, + 59797: 39647, + 59798: 39650, + 59799: 39651, + 59800: 39654, + 59801: 39663, + 59802: 39659, + 59803: 39662, + 59804: 39668, + 59805: 39665, + 59806: 39671, + 59807: 39675, + 59808: 39686, + 59809: 39704, + 59810: 39706, + 59811: 39711, + 59812: 39714, + 59813: 39715, + 59814: 39717, + 59815: 39719, + 59816: 39720, + 59817: 39721, + 59818: 39722, + 59819: 39726, + 59820: 39727, + 59821: 39730, + 59822: 39748, + 59823: 39747, + 59824: 39759, + 59825: 39757, + 59826: 39758, + 59827: 39761, + 59828: 39768, + 59829: 39796, + 59830: 39827, + 59831: 39811, + 59832: 39825, + 59833: 39830, + 59834: 39831, + 59835: 39839, + 59836: 39840, + 59837: 39848, + 59838: 39860, + 59839: 39872, + 59840: 39882, + 59841: 39865, + 59842: 39878, + 59843: 39887, + 59844: 39889, + 59845: 39890, + 59846: 39907, + 59847: 39906, + 59848: 39908, + 59849: 39892, + 59850: 39905, + 59851: 39994, + 59852: 39922, + 59853: 39921, + 59854: 39920, + 59855: 39957, + 59856: 39956, + 59857: 39945, + 59858: 39955, + 59859: 39948, + 59860: 39942, + 59861: 39944, + 59862: 39954, + 59863: 39946, + 59864: 39940, + 59865: 39982, + 59866: 39963, + 59867: 39973, + 59868: 39972, + 59869: 39969, + 59870: 39984, + 59871: 40007, + 59872: 39986, + 59873: 40006, + 59874: 39998, + 59875: 40026, + 59876: 40032, + 59877: 40039, + 59878: 40054, + 59879: 40056, + 59880: 40167, + 59881: 40172, + 59882: 40176, + 59883: 40201, + 59884: 40200, + 59885: 40171, + 59886: 40195, + 59887: 40198, + 59888: 40234, + 59889: 40230, + 59890: 40367, + 59891: 40227, + 59892: 40223, + 59893: 40260, + 59894: 40213, + 59895: 40210, + 59896: 40257, + 59897: 40255, + 59898: 40254, + 59899: 40262, + 59900: 40264, + 59968: 40285, + 59969: 40286, + 59970: 40292, + 59971: 40273, + 59972: 40272, + 59973: 40281, + 59974: 40306, + 59975: 40329, + 59976: 40327, + 59977: 40363, + 59978: 40303, + 59979: 40314, + 59980: 40346, + 59981: 40356, + 59982: 40361, + 59983: 40370, + 59984: 40388, + 59985: 40385, + 59986: 40379, + 59987: 40376, + 59988: 40378, + 59989: 40390, + 59990: 40399, + 59991: 40386, + 59992: 40409, + 59993: 40403, + 59994: 40440, + 59995: 40422, + 59996: 40429, + 59997: 40431, + 59998: 40445, + 59999: 40474, + 60000: 40475, + 60001: 40478, + 60002: 40565, + 60003: 40569, + 60004: 40573, + 60005: 40577, + 60006: 40584, + 60007: 40587, + 60008: 40588, + 60009: 40594, + 60010: 40597, + 60011: 40593, + 60012: 40605, + 60013: 40613, + 60014: 40617, + 60015: 40632, + 60016: 40618, + 60017: 40621, + 60018: 38753, + 60019: 40652, + 60020: 40654, + 60021: 40655, + 60022: 40656, + 60023: 40660, + 60024: 40668, + 60025: 40670, + 60026: 40669, + 60027: 40672, + 60028: 40677, + 60029: 40680, + 60030: 40687, + 60032: 40692, + 60033: 40694, + 60034: 40695, + 60035: 40697, + 60036: 40699, + 60037: 40700, + 60038: 40701, + 60039: 40711, + 60040: 40712, + 60041: 30391, + 60042: 40725, + 60043: 40737, + 60044: 40748, + 60045: 40766, + 60046: 40778, + 60047: 40786, + 60048: 40788, + 60049: 40803, + 60050: 40799, + 60051: 40800, + 60052: 40801, + 60053: 40806, + 60054: 40807, + 60055: 40812, + 60056: 40810, + 60057: 40823, + 60058: 40818, + 60059: 40822, + 60060: 40853, + 60061: 40860, + 60062: 40864, + 60063: 22575, + 60064: 27079, + 60065: 36953, + 60066: 29796, + 60067: 20956, + 60068: 29081, + 60736: 32394, + 60737: 35100, + 60738: 37704, + 60739: 37512, + 60740: 34012, + 60741: 20425, + 60742: 28859, + 60743: 26161, + 60744: 26824, + 60745: 37625, + 60746: 26363, + 60747: 24389, + 60748: 20008, + 60749: 20193, + 60750: 20220, + 60751: 20224, + 60752: 20227, + 60753: 20281, + 60754: 20310, + 60755: 20370, + 60756: 20362, + 60757: 20378, + 60758: 20372, + 60759: 20429, + 60760: 20544, + 60761: 20514, + 60762: 20479, + 60763: 20510, + 60764: 20550, + 60765: 20592, + 60766: 20546, + 60767: 20628, + 60768: 20724, + 60769: 20696, + 60770: 20810, + 60771: 20836, + 60772: 20893, + 60773: 20926, + 60774: 20972, + 60775: 21013, + 60776: 21148, + 60777: 21158, + 60778: 21184, + 60779: 21211, + 60780: 21248, + 60781: 21255, + 60782: 21284, + 60783: 21362, + 60784: 21395, + 60785: 21426, + 60786: 21469, + 60787: 64014, + 60788: 21660, + 60789: 21642, + 60790: 21673, + 60791: 21759, + 60792: 21894, + 60793: 22361, + 60794: 22373, + 60795: 22444, + 60796: 22472, + 60797: 22471, + 60798: 64015, + 60800: 64016, + 60801: 22686, + 60802: 22706, + 60803: 22795, + 60804: 22867, + 60805: 22875, + 60806: 22877, + 60807: 22883, + 60808: 22948, + 60809: 22970, + 60810: 23382, + 60811: 23488, + 60812: 29999, + 60813: 23512, + 60814: 23532, + 60815: 23582, + 60816: 23718, + 60817: 23738, + 60818: 23797, + 60819: 23847, + 60820: 23891, + 60821: 64017, + 60822: 23874, + 60823: 23917, + 60824: 23992, + 60825: 23993, + 60826: 24016, + 60827: 24353, + 60828: 24372, + 60829: 24423, + 60830: 24503, + 60831: 24542, + 60832: 24669, + 60833: 24709, + 60834: 24714, + 60835: 24798, + 60836: 24789, + 60837: 24864, + 60838: 24818, + 60839: 24849, + 60840: 24887, + 60841: 24880, + 60842: 24984, + 60843: 25107, + 60844: 25254, + 60845: 25589, + 60846: 25696, + 60847: 25757, + 60848: 25806, + 60849: 25934, + 60850: 26112, + 60851: 26133, + 60852: 26171, + 60853: 26121, + 60854: 26158, + 60855: 26142, + 60856: 26148, + 60857: 26213, + 60858: 26199, + 60859: 26201, + 60860: 64018, + 60861: 26227, + 60862: 26265, + 60863: 26272, + 60864: 26290, + 60865: 26303, + 60866: 26362, + 60867: 26382, + 60868: 63785, + 60869: 26470, + 60870: 26555, + 60871: 26706, + 60872: 26560, + 60873: 26625, + 60874: 26692, + 60875: 26831, + 60876: 64019, + 60877: 26984, + 60878: 64020, + 60879: 27032, + 60880: 27106, + 60881: 27184, + 60882: 27243, + 60883: 27206, + 60884: 27251, + 60885: 27262, + 60886: 27362, + 60887: 27364, + 60888: 27606, + 60889: 27711, + 60890: 27740, + 60891: 27782, + 60892: 27759, + 60893: 27866, + 60894: 27908, + 60895: 28039, + 60896: 28015, + 60897: 28054, + 60898: 28076, + 60899: 28111, + 60900: 28152, + 60901: 28146, + 60902: 28156, + 60903: 28217, + 60904: 28252, + 60905: 28199, + 60906: 28220, + 60907: 28351, + 60908: 28552, + 60909: 28597, + 60910: 28661, + 60911: 28677, + 60912: 28679, + 60913: 28712, + 60914: 28805, + 60915: 28843, + 60916: 28943, + 60917: 28932, + 60918: 29020, + 60919: 28998, + 60920: 28999, + 60921: 64021, + 60922: 29121, + 60923: 29182, + 60924: 29361, + 60992: 29374, + 60993: 29476, + 60994: 64022, + 60995: 29559, + 60996: 29629, + 60997: 29641, + 60998: 29654, + 60999: 29667, + 61000: 29650, + 61001: 29703, + 61002: 29685, + 61003: 29734, + 61004: 29738, + 61005: 29737, + 61006: 29742, + 61007: 29794, + 61008: 29833, + 61009: 29855, + 61010: 29953, + 61011: 30063, + 61012: 30338, + 61013: 30364, + 61014: 30366, + 61015: 30363, + 61016: 30374, + 61017: 64023, + 61018: 30534, + 61019: 21167, + 61020: 30753, + 61021: 30798, + 61022: 30820, + 61023: 30842, + 61024: 31024, + 61025: 64024, + 61026: 64025, + 61027: 64026, + 61028: 31124, + 61029: 64027, + 61030: 31131, + 61031: 31441, + 61032: 31463, + 61033: 64028, + 61034: 31467, + 61035: 31646, + 61036: 64029, + 61037: 32072, + 61038: 32092, + 61039: 32183, + 61040: 32160, + 61041: 32214, + 61042: 32338, + 61043: 32583, + 61044: 32673, + 61045: 64030, + 61046: 33537, + 61047: 33634, + 61048: 33663, + 61049: 33735, + 61050: 33782, + 61051: 33864, + 61052: 33972, + 61053: 34131, + 61054: 34137, + 61056: 34155, + 61057: 64031, + 61058: 34224, + 61059: 64032, + 61060: 64033, + 61061: 34823, + 61062: 35061, + 61063: 35346, + 61064: 35383, + 61065: 35449, + 61066: 35495, + 61067: 35518, + 61068: 35551, + 61069: 64034, + 61070: 35574, + 61071: 35667, + 61072: 35711, + 61073: 36080, + 61074: 36084, + 61075: 36114, + 61076: 36214, + 61077: 64035, + 61078: 36559, + 61079: 64036, + 61080: 64037, + 61081: 36967, + 61082: 37086, + 61083: 64038, + 61084: 37141, + 61085: 37159, + 61086: 37338, + 61087: 37335, + 61088: 37342, + 61089: 37357, + 61090: 37358, + 61091: 37348, + 61092: 37349, + 61093: 37382, + 61094: 37392, + 61095: 37386, + 61096: 37434, + 61097: 37440, + 61098: 37436, + 61099: 37454, + 61100: 37465, + 61101: 37457, + 61102: 37433, + 61103: 37479, + 61104: 37543, + 61105: 37495, + 61106: 37496, + 61107: 37607, + 61108: 37591, + 61109: 37593, + 61110: 37584, + 61111: 64039, + 61112: 37589, + 61113: 37600, + 61114: 37587, + 61115: 37669, + 61116: 37665, + 61117: 37627, + 61118: 64040, + 61119: 37662, + 61120: 37631, + 61121: 37661, + 61122: 37634, + 61123: 37744, + 61124: 37719, + 61125: 37796, + 61126: 37830, + 61127: 37854, + 61128: 37880, + 61129: 37937, + 61130: 37957, + 61131: 37960, + 61132: 38290, + 61133: 63964, + 61134: 64041, + 61135: 38557, + 61136: 38575, + 61137: 38707, + 61138: 38715, + 61139: 38723, + 61140: 38733, + 61141: 38735, + 61142: 38737, + 61143: 38741, + 61144: 38999, + 61145: 39013, + 61146: 64042, + 61147: 64043, + 61148: 39207, + 61149: 64044, + 61150: 39326, + 61151: 39502, + 61152: 39641, + 61153: 39644, + 61154: 39797, + 61155: 39794, + 61156: 39823, + 61157: 39857, + 61158: 39867, + 61159: 39936, + 61160: 40304, + 61161: 40299, + 61162: 64045, + 61163: 40473, + 61164: 40657, + 61167: 8560, + 61168: 8561, + 61169: 8562, + 61170: 8563, + 61171: 8564, + 61172: 8565, + 61173: 8566, + 61174: 8567, + 61175: 8568, + 61176: 8569, + 61177: 65506, + 61178: 65508, + 61179: 65287, + 61180: 65282, + 61504: 57344, + 61505: 57345, + 61506: 57346, + 61507: 57347, + 61508: 57348, + 61509: 57349, + 61510: 57350, + 61511: 57351, + 61512: 57352, + 61513: 57353, + 61514: 57354, + 61515: 57355, + 61516: 57356, + 61517: 57357, + 61518: 57358, + 61519: 57359, + 61520: 57360, + 61521: 57361, + 61522: 57362, + 61523: 57363, + 61524: 57364, + 61525: 57365, + 61526: 57366, + 61527: 57367, + 61528: 57368, + 61529: 57369, + 61530: 57370, + 61531: 57371, + 61532: 57372, + 61533: 57373, + 61534: 57374, + 61535: 57375, + 61536: 57376, + 61537: 57377, + 61538: 57378, + 61539: 57379, + 61540: 57380, + 61541: 57381, + 61542: 57382, + 61543: 57383, + 61544: 57384, + 61545: 57385, + 61546: 57386, + 61547: 57387, + 61548: 57388, + 61549: 57389, + 61550: 57390, + 61551: 57391, + 61552: 57392, + 61553: 57393, + 61554: 57394, + 61555: 57395, + 61556: 57396, + 61557: 57397, + 61558: 57398, + 61559: 57399, + 61560: 57400, + 61561: 57401, + 61562: 57402, + 61563: 57403, + 61564: 57404, + 61565: 57405, + 61566: 57406, + 61568: 57407, + 61569: 57408, + 61570: 57409, + 61571: 57410, + 61572: 57411, + 61573: 57412, + 61574: 57413, + 61575: 57414, + 61576: 57415, + 61577: 57416, + 61578: 57417, + 61579: 57418, + 61580: 57419, + 61581: 57420, + 61582: 57421, + 61583: 57422, + 61584: 57423, + 61585: 57424, + 61586: 57425, + 61587: 57426, + 61588: 57427, + 61589: 57428, + 61590: 57429, + 61591: 57430, + 61592: 57431, + 61593: 57432, + 61594: 57433, + 61595: 57434, + 61596: 57435, + 61597: 57436, + 61598: 57437, + 61599: 57438, + 61600: 57439, + 61601: 57440, + 61602: 57441, + 61603: 57442, + 61604: 57443, + 61605: 57444, + 61606: 57445, + 61607: 57446, + 61608: 57447, + 61609: 57448, + 61610: 57449, + 61611: 57450, + 61612: 57451, + 61613: 57452, + 61614: 57453, + 61615: 57454, + 61616: 57455, + 61617: 57456, + 61618: 57457, + 61619: 57458, + 61620: 57459, + 61621: 57460, + 61622: 57461, + 61623: 57462, + 61624: 57463, + 61625: 57464, + 61626: 57465, + 61627: 57466, + 61628: 57467, + 61629: 57468, + 61630: 57469, + 61631: 57470, + 61632: 57471, + 61633: 57472, + 61634: 57473, + 61635: 57474, + 61636: 57475, + 61637: 57476, + 61638: 57477, + 61639: 57478, + 61640: 57479, + 61641: 57480, + 61642: 57481, + 61643: 57482, + 61644: 57483, + 61645: 57484, + 61646: 57485, + 61647: 57486, + 61648: 57487, + 61649: 57488, + 61650: 57489, + 61651: 57490, + 61652: 57491, + 61653: 57492, + 61654: 57493, + 61655: 57494, + 61656: 57495, + 61657: 57496, + 61658: 57497, + 61659: 57498, + 61660: 57499, + 61661: 57500, + 61662: 57501, + 61663: 57502, + 61664: 57503, + 61665: 57504, + 61666: 57505, + 61667: 57506, + 61668: 57507, + 61669: 57508, + 61670: 57509, + 61671: 57510, + 61672: 57511, + 61673: 57512, + 61674: 57513, + 61675: 57514, + 61676: 57515, + 61677: 57516, + 61678: 57517, + 61679: 57518, + 61680: 57519, + 61681: 57520, + 61682: 57521, + 61683: 57522, + 61684: 57523, + 61685: 57524, + 61686: 57525, + 61687: 57526, + 61688: 57527, + 61689: 57528, + 61690: 57529, + 61691: 57530, + 61692: 57531, + 61760: 57532, + 61761: 57533, + 61762: 57534, + 61763: 57535, + 61764: 57536, + 61765: 57537, + 61766: 57538, + 61767: 57539, + 61768: 57540, + 61769: 57541, + 61770: 57542, + 61771: 57543, + 61772: 57544, + 61773: 57545, + 61774: 57546, + 61775: 57547, + 61776: 57548, + 61777: 57549, + 61778: 57550, + 61779: 57551, + 61780: 57552, + 61781: 57553, + 61782: 57554, + 61783: 57555, + 61784: 57556, + 61785: 57557, + 61786: 57558, + 61787: 57559, + 61788: 57560, + 61789: 57561, + 61790: 57562, + 61791: 57563, + 61792: 57564, + 61793: 57565, + 61794: 57566, + 61795: 57567, + 61796: 57568, + 61797: 57569, + 61798: 57570, + 61799: 57571, + 61800: 57572, + 61801: 57573, + 61802: 57574, + 61803: 57575, + 61804: 57576, + 61805: 57577, + 61806: 57578, + 61807: 57579, + 61808: 57580, + 61809: 57581, + 61810: 57582, + 61811: 57583, + 61812: 57584, + 61813: 57585, + 61814: 57586, + 61815: 57587, + 61816: 57588, + 61817: 57589, + 61818: 57590, + 61819: 57591, + 61820: 57592, + 61821: 57593, + 61822: 57594, + 61824: 57595, + 61825: 57596, + 61826: 57597, + 61827: 57598, + 61828: 57599, + 61829: 57600, + 61830: 57601, + 61831: 57602, + 61832: 57603, + 61833: 57604, + 61834: 57605, + 61835: 57606, + 61836: 57607, + 61837: 57608, + 61838: 57609, + 61839: 57610, + 61840: 57611, + 61841: 57612, + 61842: 57613, + 61843: 57614, + 61844: 57615, + 61845: 57616, + 61846: 57617, + 61847: 57618, + 61848: 57619, + 61849: 57620, + 61850: 57621, + 61851: 57622, + 61852: 57623, + 61853: 57624, + 61854: 57625, + 61855: 57626, + 61856: 57627, + 61857: 57628, + 61858: 57629, + 61859: 57630, + 61860: 57631, + 61861: 57632, + 61862: 57633, + 61863: 57634, + 61864: 57635, + 61865: 57636, + 61866: 57637, + 61867: 57638, + 61868: 57639, + 61869: 57640, + 61870: 57641, + 61871: 57642, + 61872: 57643, + 61873: 57644, + 61874: 57645, + 61875: 57646, + 61876: 57647, + 61877: 57648, + 61878: 57649, + 61879: 57650, + 61880: 57651, + 61881: 57652, + 61882: 57653, + 61883: 57654, + 61884: 57655, + 61885: 57656, + 61886: 57657, + 61887: 57658, + 61888: 57659, + 61889: 57660, + 61890: 57661, + 61891: 57662, + 61892: 57663, + 61893: 57664, + 61894: 57665, + 61895: 57666, + 61896: 57667, + 61897: 57668, + 61898: 57669, + 61899: 57670, + 61900: 57671, + 61901: 57672, + 61902: 57673, + 61903: 57674, + 61904: 57675, + 61905: 57676, + 61906: 57677, + 61907: 57678, + 61908: 57679, + 61909: 57680, + 61910: 57681, + 61911: 57682, + 61912: 57683, + 61913: 57684, + 61914: 57685, + 61915: 57686, + 61916: 57687, + 61917: 57688, + 61918: 57689, + 61919: 57690, + 61920: 57691, + 61921: 57692, + 61922: 57693, + 61923: 57694, + 61924: 57695, + 61925: 57696, + 61926: 57697, + 61927: 57698, + 61928: 57699, + 61929: 57700, + 61930: 57701, + 61931: 57702, + 61932: 57703, + 61933: 57704, + 61934: 57705, + 61935: 57706, + 61936: 57707, + 61937: 57708, + 61938: 57709, + 61939: 57710, + 61940: 57711, + 61941: 57712, + 61942: 57713, + 61943: 57714, + 61944: 57715, + 61945: 57716, + 61946: 57717, + 61947: 57718, + 61948: 57719, + 62016: 57720, + 62017: 57721, + 62018: 57722, + 62019: 57723, + 62020: 57724, + 62021: 57725, + 62022: 57726, + 62023: 57727, + 62024: 57728, + 62025: 57729, + 62026: 57730, + 62027: 57731, + 62028: 57732, + 62029: 57733, + 62030: 57734, + 62031: 57735, + 62032: 57736, + 62033: 57737, + 62034: 57738, + 62035: 57739, + 62036: 57740, + 62037: 57741, + 62038: 57742, + 62039: 57743, + 62040: 57744, + 62041: 57745, + 62042: 57746, + 62043: 57747, + 62044: 57748, + 62045: 57749, + 62046: 57750, + 62047: 57751, + 62048: 57752, + 62049: 57753, + 62050: 57754, + 62051: 57755, + 62052: 57756, + 62053: 57757, + 62054: 57758, + 62055: 57759, + 62056: 57760, + 62057: 57761, + 62058: 57762, + 62059: 57763, + 62060: 57764, + 62061: 57765, + 62062: 57766, + 62063: 57767, + 62064: 57768, + 62065: 57769, + 62066: 57770, + 62067: 57771, + 62068: 57772, + 62069: 57773, + 62070: 57774, + 62071: 57775, + 62072: 57776, + 62073: 57777, + 62074: 57778, + 62075: 57779, + 62076: 57780, + 62077: 57781, + 62078: 57782, + 62080: 57783, + 62081: 57784, + 62082: 57785, + 62083: 57786, + 62084: 57787, + 62085: 57788, + 62086: 57789, + 62087: 57790, + 62088: 57791, + 62089: 57792, + 62090: 57793, + 62091: 57794, + 62092: 57795, + 62093: 57796, + 62094: 57797, + 62095: 57798, + 62096: 57799, + 62097: 57800, + 62098: 57801, + 62099: 57802, + 62100: 57803, + 62101: 57804, + 62102: 57805, + 62103: 57806, + 62104: 57807, + 62105: 57808, + 62106: 57809, + 62107: 57810, + 62108: 57811, + 62109: 57812, + 62110: 57813, + 62111: 57814, + 62112: 57815, + 62113: 57816, + 62114: 57817, + 62115: 57818, + 62116: 57819, + 62117: 57820, + 62118: 57821, + 62119: 57822, + 62120: 57823, + 62121: 57824, + 62122: 57825, + 62123: 57826, + 62124: 57827, + 62125: 57828, + 62126: 57829, + 62127: 57830, + 62128: 57831, + 62129: 57832, + 62130: 57833, + 62131: 57834, + 62132: 57835, + 62133: 57836, + 62134: 57837, + 62135: 57838, + 62136: 57839, + 62137: 57840, + 62138: 57841, + 62139: 57842, + 62140: 57843, + 62141: 57844, + 62142: 57845, + 62143: 57846, + 62144: 57847, + 62145: 57848, + 62146: 57849, + 62147: 57850, + 62148: 57851, + 62149: 57852, + 62150: 57853, + 62151: 57854, + 62152: 57855, + 62153: 57856, + 62154: 57857, + 62155: 57858, + 62156: 57859, + 62157: 57860, + 62158: 57861, + 62159: 57862, + 62160: 57863, + 62161: 57864, + 62162: 57865, + 62163: 57866, + 62164: 57867, + 62165: 57868, + 62166: 57869, + 62167: 57870, + 62168: 57871, + 62169: 57872, + 62170: 57873, + 62171: 57874, + 62172: 57875, + 62173: 57876, + 62174: 57877, + 62175: 57878, + 62176: 57879, + 62177: 57880, + 62178: 57881, + 62179: 57882, + 62180: 57883, + 62181: 57884, + 62182: 57885, + 62183: 57886, + 62184: 57887, + 62185: 57888, + 62186: 57889, + 62187: 57890, + 62188: 57891, + 62189: 57892, + 62190: 57893, + 62191: 57894, + 62192: 57895, + 62193: 57896, + 62194: 57897, + 62195: 57898, + 62196: 57899, + 62197: 57900, + 62198: 57901, + 62199: 57902, + 62200: 57903, + 62201: 57904, + 62202: 57905, + 62203: 57906, + 62204: 57907, + 62272: 57908, + 62273: 57909, + 62274: 57910, + 62275: 57911, + 62276: 57912, + 62277: 57913, + 62278: 57914, + 62279: 57915, + 62280: 57916, + 62281: 57917, + 62282: 57918, + 62283: 57919, + 62284: 57920, + 62285: 57921, + 62286: 57922, + 62287: 57923, + 62288: 57924, + 62289: 57925, + 62290: 57926, + 62291: 57927, + 62292: 57928, + 62293: 57929, + 62294: 57930, + 62295: 57931, + 62296: 57932, + 62297: 57933, + 62298: 57934, + 62299: 57935, + 62300: 57936, + 62301: 57937, + 62302: 57938, + 62303: 57939, + 62304: 57940, + 62305: 57941, + 62306: 57942, + 62307: 57943, + 62308: 57944, + 62309: 57945, + 62310: 57946, + 62311: 57947, + 62312: 57948, + 62313: 57949, + 62314: 57950, + 62315: 57951, + 62316: 57952, + 62317: 57953, + 62318: 57954, + 62319: 57955, + 62320: 57956, + 62321: 57957, + 62322: 57958, + 62323: 57959, + 62324: 57960, + 62325: 57961, + 62326: 57962, + 62327: 57963, + 62328: 57964, + 62329: 57965, + 62330: 57966, + 62331: 57967, + 62332: 57968, + 62333: 57969, + 62334: 57970, + 62336: 57971, + 62337: 57972, + 62338: 57973, + 62339: 57974, + 62340: 57975, + 62341: 57976, + 62342: 57977, + 62343: 57978, + 62344: 57979, + 62345: 57980, + 62346: 57981, + 62347: 57982, + 62348: 57983, + 62349: 57984, + 62350: 57985, + 62351: 57986, + 62352: 57987, + 62353: 57988, + 62354: 57989, + 62355: 57990, + 62356: 57991, + 62357: 57992, + 62358: 57993, + 62359: 57994, + 62360: 57995, + 62361: 57996, + 62362: 57997, + 62363: 57998, + 62364: 57999, + 62365: 58000, + 62366: 58001, + 62367: 58002, + 62368: 58003, + 62369: 58004, + 62370: 58005, + 62371: 58006, + 62372: 58007, + 62373: 58008, + 62374: 58009, + 62375: 58010, + 62376: 58011, + 62377: 58012, + 62378: 58013, + 62379: 58014, + 62380: 58015, + 62381: 58016, + 62382: 58017, + 62383: 58018, + 62384: 58019, + 62385: 58020, + 62386: 58021, + 62387: 58022, + 62388: 58023, + 62389: 58024, + 62390: 58025, + 62391: 58026, + 62392: 58027, + 62393: 58028, + 62394: 58029, + 62395: 58030, + 62396: 58031, + 62397: 58032, + 62398: 58033, + 62399: 58034, + 62400: 58035, + 62401: 58036, + 62402: 58037, + 62403: 58038, + 62404: 58039, + 62405: 58040, + 62406: 58041, + 62407: 58042, + 62408: 58043, + 62409: 58044, + 62410: 58045, + 62411: 58046, + 62412: 58047, + 62413: 58048, + 62414: 58049, + 62415: 58050, + 62416: 58051, + 62417: 58052, + 62418: 58053, + 62419: 58054, + 62420: 58055, + 62421: 58056, + 62422: 58057, + 62423: 58058, + 62424: 58059, + 62425: 58060, + 62426: 58061, + 62427: 58062, + 62428: 58063, + 62429: 58064, + 62430: 58065, + 62431: 58066, + 62432: 58067, + 62433: 58068, + 62434: 58069, + 62435: 58070, + 62436: 58071, + 62437: 58072, + 62438: 58073, + 62439: 58074, + 62440: 58075, + 62441: 58076, + 62442: 58077, + 62443: 58078, + 62444: 58079, + 62445: 58080, + 62446: 58081, + 62447: 58082, + 62448: 58083, + 62449: 58084, + 62450: 58085, + 62451: 58086, + 62452: 58087, + 62453: 58088, + 62454: 58089, + 62455: 58090, + 62456: 58091, + 62457: 58092, + 62458: 58093, + 62459: 58094, + 62460: 58095, + 62528: 58096, + 62529: 58097, + 62530: 58098, + 62531: 58099, + 62532: 58100, + 62533: 58101, + 62534: 58102, + 62535: 58103, + 62536: 58104, + 62537: 58105, + 62538: 58106, + 62539: 58107, + 62540: 58108, + 62541: 58109, + 62542: 58110, + 62543: 58111, + 62544: 58112, + 62545: 58113, + 62546: 58114, + 62547: 58115, + 62548: 58116, + 62549: 58117, + 62550: 58118, + 62551: 58119, + 62552: 58120, + 62553: 58121, + 62554: 58122, + 62555: 58123, + 62556: 58124, + 62557: 58125, + 62558: 58126, + 62559: 58127, + 62560: 58128, + 62561: 58129, + 62562: 58130, + 62563: 58131, + 62564: 58132, + 62565: 58133, + 62566: 58134, + 62567: 58135, + 62568: 58136, + 62569: 58137, + 62570: 58138, + 62571: 58139, + 62572: 58140, + 62573: 58141, + 62574: 58142, + 62575: 58143, + 62576: 58144, + 62577: 58145, + 62578: 58146, + 62579: 58147, + 62580: 58148, + 62581: 58149, + 62582: 58150, + 62583: 58151, + 62584: 58152, + 62585: 58153, + 62586: 58154, + 62587: 58155, + 62588: 58156, + 62589: 58157, + 62590: 58158, + 62592: 58159, + 62593: 58160, + 62594: 58161, + 62595: 58162, + 62596: 58163, + 62597: 58164, + 62598: 58165, + 62599: 58166, + 62600: 58167, + 62601: 58168, + 62602: 58169, + 62603: 58170, + 62604: 58171, + 62605: 58172, + 62606: 58173, + 62607: 58174, + 62608: 58175, + 62609: 58176, + 62610: 58177, + 62611: 58178, + 62612: 58179, + 62613: 58180, + 62614: 58181, + 62615: 58182, + 62616: 58183, + 62617: 58184, + 62618: 58185, + 62619: 58186, + 62620: 58187, + 62621: 58188, + 62622: 58189, + 62623: 58190, + 62624: 58191, + 62625: 58192, + 62626: 58193, + 62627: 58194, + 62628: 58195, + 62629: 58196, + 62630: 58197, + 62631: 58198, + 62632: 58199, + 62633: 58200, + 62634: 58201, + 62635: 58202, + 62636: 58203, + 62637: 58204, + 62638: 58205, + 62639: 58206, + 62640: 58207, + 62641: 58208, + 62642: 58209, + 62643: 58210, + 62644: 58211, + 62645: 58212, + 62646: 58213, + 62647: 58214, + 62648: 58215, + 62649: 58216, + 62650: 58217, + 62651: 58218, + 62652: 58219, + 62653: 58220, + 62654: 58221, + 62655: 58222, + 62656: 58223, + 62657: 58224, + 62658: 58225, + 62659: 58226, + 62660: 58227, + 62661: 58228, + 62662: 58229, + 62663: 58230, + 62664: 58231, + 62665: 58232, + 62666: 58233, + 62667: 58234, + 62668: 58235, + 62669: 58236, + 62670: 58237, + 62671: 58238, + 62672: 58239, + 62673: 58240, + 62674: 58241, + 62675: 58242, + 62676: 58243, + 62677: 58244, + 62678: 58245, + 62679: 58246, + 62680: 58247, + 62681: 58248, + 62682: 58249, + 62683: 58250, + 62684: 58251, + 62685: 58252, + 62686: 58253, + 62687: 58254, + 62688: 58255, + 62689: 58256, + 62690: 58257, + 62691: 58258, + 62692: 58259, + 62693: 58260, + 62694: 58261, + 62695: 58262, + 62696: 58263, + 62697: 58264, + 62698: 58265, + 62699: 58266, + 62700: 58267, + 62701: 58268, + 62702: 58269, + 62703: 58270, + 62704: 58271, + 62705: 58272, + 62706: 58273, + 62707: 58274, + 62708: 58275, + 62709: 58276, + 62710: 58277, + 62711: 58278, + 62712: 58279, + 62713: 58280, + 62714: 58281, + 62715: 58282, + 62716: 58283, + 62784: 58284, + 62785: 58285, + 62786: 58286, + 62787: 58287, + 62788: 58288, + 62789: 58289, + 62790: 58290, + 62791: 58291, + 62792: 58292, + 62793: 58293, + 62794: 58294, + 62795: 58295, + 62796: 58296, + 62797: 58297, + 62798: 58298, + 62799: 58299, + 62800: 58300, + 62801: 58301, + 62802: 58302, + 62803: 58303, + 62804: 58304, + 62805: 58305, + 62806: 58306, + 62807: 58307, + 62808: 58308, + 62809: 58309, + 62810: 58310, + 62811: 58311, + 62812: 58312, + 62813: 58313, + 62814: 58314, + 62815: 58315, + 62816: 58316, + 62817: 58317, + 62818: 58318, + 62819: 58319, + 62820: 58320, + 62821: 58321, + 62822: 58322, + 62823: 58323, + 62824: 58324, + 62825: 58325, + 62826: 58326, + 62827: 58327, + 62828: 58328, + 62829: 58329, + 62830: 58330, + 62831: 58331, + 62832: 58332, + 62833: 58333, + 62834: 58334, + 62835: 58335, + 62836: 58336, + 62837: 58337, + 62838: 58338, + 62839: 58339, + 62840: 58340, + 62841: 58341, + 62842: 58342, + 62843: 58343, + 62844: 58344, + 62845: 58345, + 62846: 58346, + 62848: 58347, + 62849: 58348, + 62850: 58349, + 62851: 58350, + 62852: 58351, + 62853: 58352, + 62854: 58353, + 62855: 58354, + 62856: 58355, + 62857: 58356, + 62858: 58357, + 62859: 58358, + 62860: 58359, + 62861: 58360, + 62862: 58361, + 62863: 58362, + 62864: 58363, + 62865: 58364, + 62866: 58365, + 62867: 58366, + 62868: 58367, + 62869: 58368, + 62870: 58369, + 62871: 58370, + 62872: 58371, + 62873: 58372, + 62874: 58373, + 62875: 58374, + 62876: 58375, + 62877: 58376, + 62878: 58377, + 62879: 58378, + 62880: 58379, + 62881: 58380, + 62882: 58381, + 62883: 58382, + 62884: 58383, + 62885: 58384, + 62886: 58385, + 62887: 58386, + 62888: 58387, + 62889: 58388, + 62890: 58389, + 62891: 58390, + 62892: 58391, + 62893: 58392, + 62894: 58393, + 62895: 58394, + 62896: 58395, + 62897: 58396, + 62898: 58397, + 62899: 58398, + 62900: 58399, + 62901: 58400, + 62902: 58401, + 62903: 58402, + 62904: 58403, + 62905: 58404, + 62906: 58405, + 62907: 58406, + 62908: 58407, + 62909: 58408, + 62910: 58409, + 62911: 58410, + 62912: 58411, + 62913: 58412, + 62914: 58413, + 62915: 58414, + 62916: 58415, + 62917: 58416, + 62918: 58417, + 62919: 58418, + 62920: 58419, + 62921: 58420, + 62922: 58421, + 62923: 58422, + 62924: 58423, + 62925: 58424, + 62926: 58425, + 62927: 58426, + 62928: 58427, + 62929: 58428, + 62930: 58429, + 62931: 58430, + 62932: 58431, + 62933: 58432, + 62934: 58433, + 62935: 58434, + 62936: 58435, + 62937: 58436, + 62938: 58437, + 62939: 58438, + 62940: 58439, + 62941: 58440, + 62942: 58441, + 62943: 58442, + 62944: 58443, + 62945: 58444, + 62946: 58445, + 62947: 58446, + 62948: 58447, + 62949: 58448, + 62950: 58449, + 62951: 58450, + 62952: 58451, + 62953: 58452, + 62954: 58453, + 62955: 58454, + 62956: 58455, + 62957: 58456, + 62958: 58457, + 62959: 58458, + 62960: 58459, + 62961: 58460, + 62962: 58461, + 62963: 58462, + 62964: 58463, + 62965: 58464, + 62966: 58465, + 62967: 58466, + 62968: 58467, + 62969: 58468, + 62970: 58469, + 62971: 58470, + 62972: 58471, + 63040: 58472, + 63041: 58473, + 63042: 58474, + 63043: 58475, + 63044: 58476, + 63045: 58477, + 63046: 58478, + 63047: 58479, + 63048: 58480, + 63049: 58481, + 63050: 58482, + 63051: 58483, + 63052: 58484, + 63053: 58485, + 63054: 58486, + 63055: 58487, + 63056: 58488, + 63057: 58489, + 63058: 58490, + 63059: 58491, + 63060: 58492, + 63061: 58493, + 63062: 58494, + 63063: 58495, + 63064: 58496, + 63065: 58497, + 63066: 58498, + 63067: 58499, + 63068: 58500, + 63069: 58501, + 63070: 58502, + 63071: 58503, + 63072: 58504, + 63073: 58505, + 63074: 58506, + 63075: 58507, + 63076: 58508, + 63077: 58509, + 63078: 58510, + 63079: 58511, + 63080: 58512, + 63081: 58513, + 63082: 58514, + 63083: 58515, + 63084: 58516, + 63085: 58517, + 63086: 58518, + 63087: 58519, + 63088: 58520, + 63089: 58521, + 63090: 58522, + 63091: 58523, + 63092: 58524, + 63093: 58525, + 63094: 58526, + 63095: 58527, + 63096: 58528, + 63097: 58529, + 63098: 58530, + 63099: 58531, + 63100: 58532, + 63101: 58533, + 63102: 58534, + 63104: 58535, + 63105: 58536, + 63106: 58537, + 63107: 58538, + 63108: 58539, + 63109: 58540, + 63110: 58541, + 63111: 58542, + 63112: 58543, + 63113: 58544, + 63114: 58545, + 63115: 58546, + 63116: 58547, + 63117: 58548, + 63118: 58549, + 63119: 58550, + 63120: 58551, + 63121: 58552, + 63122: 58553, + 63123: 58554, + 63124: 58555, + 63125: 58556, + 63126: 58557, + 63127: 58558, + 63128: 58559, + 63129: 58560, + 63130: 58561, + 63131: 58562, + 63132: 58563, + 63133: 58564, + 63134: 58565, + 63135: 58566, + 63136: 58567, + 63137: 58568, + 63138: 58569, + 63139: 58570, + 63140: 58571, + 63141: 58572, + 63142: 58573, + 63143: 58574, + 63144: 58575, + 63145: 58576, + 63146: 58577, + 63147: 58578, + 63148: 58579, + 63149: 58580, + 63150: 58581, + 63151: 58582, + 63152: 58583, + 63153: 58584, + 63154: 58585, + 63155: 58586, + 63156: 58587, + 63157: 58588, + 63158: 58589, + 63159: 58590, + 63160: 58591, + 63161: 58592, + 63162: 58593, + 63163: 58594, + 63164: 58595, + 63165: 58596, + 63166: 58597, + 63167: 58598, + 63168: 58599, + 63169: 58600, + 63170: 58601, + 63171: 58602, + 63172: 58603, + 63173: 58604, + 63174: 58605, + 63175: 58606, + 63176: 58607, + 63177: 58608, + 63178: 58609, + 63179: 58610, + 63180: 58611, + 63181: 58612, + 63182: 58613, + 63183: 58614, + 63184: 58615, + 63185: 58616, + 63186: 58617, + 63187: 58618, + 63188: 58619, + 63189: 58620, + 63190: 58621, + 63191: 58622, + 63192: 58623, + 63193: 58624, + 63194: 58625, + 63195: 58626, + 63196: 58627, + 63197: 58628, + 63198: 58629, + 63199: 58630, + 63200: 58631, + 63201: 58632, + 63202: 58633, + 63203: 58634, + 63204: 58635, + 63205: 58636, + 63206: 58637, + 63207: 58638, + 63208: 58639, + 63209: 58640, + 63210: 58641, + 63211: 58642, + 63212: 58643, + 63213: 58644, + 63214: 58645, + 63215: 58646, + 63216: 58647, + 63217: 58648, + 63218: 58649, + 63219: 58650, + 63220: 58651, + 63221: 58652, + 63222: 58653, + 63223: 58654, + 63224: 58655, + 63225: 58656, + 63226: 58657, + 63227: 58658, + 63228: 58659, + 63296: 58660, + 63297: 58661, + 63298: 58662, + 63299: 58663, + 63300: 58664, + 63301: 58665, + 63302: 58666, + 63303: 58667, + 63304: 58668, + 63305: 58669, + 63306: 58670, + 63307: 58671, + 63308: 58672, + 63309: 58673, + 63310: 58674, + 63311: 58675, + 63312: 58676, + 63313: 58677, + 63314: 58678, + 63315: 58679, + 63316: 58680, + 63317: 58681, + 63318: 58682, + 63319: 58683, + 63320: 58684, + 63321: 58685, + 63322: 58686, + 63323: 58687, + 63324: 58688, + 63325: 58689, + 63326: 58690, + 63327: 58691, + 63328: 58692, + 63329: 58693, + 63330: 58694, + 63331: 58695, + 63332: 58696, + 63333: 58697, + 63334: 58698, + 63335: 58699, + 63336: 58700, + 63337: 58701, + 63338: 58702, + 63339: 58703, + 63340: 58704, + 63341: 58705, + 63342: 58706, + 63343: 58707, + 63344: 58708, + 63345: 58709, + 63346: 58710, + 63347: 58711, + 63348: 58712, + 63349: 58713, + 63350: 58714, + 63351: 58715, + 63352: 58716, + 63353: 58717, + 63354: 58718, + 63355: 58719, + 63356: 58720, + 63357: 58721, + 63358: 58722, + 63360: 58723, + 63361: 58724, + 63362: 58725, + 63363: 58726, + 63364: 58727, + 63365: 58728, + 63366: 58729, + 63367: 58730, + 63368: 58731, + 63369: 58732, + 63370: 58733, + 63371: 58734, + 63372: 58735, + 63373: 58736, + 63374: 58737, + 63375: 58738, + 63376: 58739, + 63377: 58740, + 63378: 58741, + 63379: 58742, + 63380: 58743, + 63381: 58744, + 63382: 58745, + 63383: 58746, + 63384: 58747, + 63385: 58748, + 63386: 58749, + 63387: 58750, + 63388: 58751, + 63389: 58752, + 63390: 58753, + 63391: 58754, + 63392: 58755, + 63393: 58756, + 63394: 58757, + 63395: 58758, + 63396: 58759, + 63397: 58760, + 63398: 58761, + 63399: 58762, + 63400: 58763, + 63401: 58764, + 63402: 58765, + 63403: 58766, + 63404: 58767, + 63405: 58768, + 63406: 58769, + 63407: 58770, + 63408: 58771, + 63409: 58772, + 63410: 58773, + 63411: 58774, + 63412: 58775, + 63413: 58776, + 63414: 58777, + 63415: 58778, + 63416: 58779, + 63417: 58780, + 63418: 58781, + 63419: 58782, + 63420: 58783, + 63421: 58784, + 63422: 58785, + 63423: 58786, + 63424: 58787, + 63425: 58788, + 63426: 58789, + 63427: 58790, + 63428: 58791, + 63429: 58792, + 63430: 58793, + 63431: 58794, + 63432: 58795, + 63433: 58796, + 63434: 58797, + 63435: 58798, + 63436: 58799, + 63437: 58800, + 63438: 58801, + 63439: 58802, + 63440: 58803, + 63441: 58804, + 63442: 58805, + 63443: 58806, + 63444: 58807, + 63445: 58808, + 63446: 58809, + 63447: 58810, + 63448: 58811, + 63449: 58812, + 63450: 58813, + 63451: 58814, + 63452: 58815, + 63453: 58816, + 63454: 58817, + 63455: 58818, + 63456: 58819, + 63457: 58820, + 63458: 58821, + 63459: 58822, + 63460: 58823, + 63461: 58824, + 63462: 58825, + 63463: 58826, + 63464: 58827, + 63465: 58828, + 63466: 58829, + 63467: 58830, + 63468: 58831, + 63469: 58832, + 63470: 58833, + 63471: 58834, + 63472: 58835, + 63473: 58836, + 63474: 58837, + 63475: 58838, + 63476: 58839, + 63477: 58840, + 63478: 58841, + 63479: 58842, + 63480: 58843, + 63481: 58844, + 63482: 58845, + 63483: 58846, + 63484: 58847, + 63552: 58848, + 63553: 58849, + 63554: 58850, + 63555: 58851, + 63556: 58852, + 63557: 58853, + 63558: 58854, + 63559: 58855, + 63560: 58856, + 63561: 58857, + 63562: 58858, + 63563: 58859, + 63564: 58860, + 63565: 58861, + 63566: 58862, + 63567: 58863, + 63568: 58864, + 63569: 58865, + 63570: 58866, + 63571: 58867, + 63572: 58868, + 63573: 58869, + 63574: 58870, + 63575: 58871, + 63576: 58872, + 63577: 58873, + 63578: 58874, + 63579: 58875, + 63580: 58876, + 63581: 58877, + 63582: 58878, + 63583: 58879, + 63584: 58880, + 63585: 58881, + 63586: 58882, + 63587: 58883, + 63588: 58884, + 63589: 58885, + 63590: 58886, + 63591: 58887, + 63592: 58888, + 63593: 58889, + 63594: 58890, + 63595: 58891, + 63596: 58892, + 63597: 58893, + 63598: 58894, + 63599: 58895, + 63600: 58896, + 63601: 58897, + 63602: 58898, + 63603: 58899, + 63604: 58900, + 63605: 58901, + 63606: 58902, + 63607: 58903, + 63608: 58904, + 63609: 58905, + 63610: 58906, + 63611: 58907, + 63612: 58908, + 63613: 58909, + 63614: 58910, + 63616: 58911, + 63617: 58912, + 63618: 58913, + 63619: 58914, + 63620: 58915, + 63621: 58916, + 63622: 58917, + 63623: 58918, + 63624: 58919, + 63625: 58920, + 63626: 58921, + 63627: 58922, + 63628: 58923, + 63629: 58924, + 63630: 58925, + 63631: 58926, + 63632: 58927, + 63633: 58928, + 63634: 58929, + 63635: 58930, + 63636: 58931, + 63637: 58932, + 63638: 58933, + 63639: 58934, + 63640: 58935, + 63641: 58936, + 63642: 58937, + 63643: 58938, + 63644: 58939, + 63645: 58940, + 63646: 58941, + 63647: 58942, + 63648: 58943, + 63649: 58944, + 63650: 58945, + 63651: 58946, + 63652: 58947, + 63653: 58948, + 63654: 58949, + 63655: 58950, + 63656: 58951, + 63657: 58952, + 63658: 58953, + 63659: 58954, + 63660: 58955, + 63661: 58956, + 63662: 58957, + 63663: 58958, + 63664: 58959, + 63665: 58960, + 63666: 58961, + 63667: 58962, + 63668: 58963, + 63669: 58964, + 63670: 58965, + 63671: 58966, + 63672: 58967, + 63673: 58968, + 63674: 58969, + 63675: 58970, + 63676: 58971, + 63677: 58972, + 63678: 58973, + 63679: 58974, + 63680: 58975, + 63681: 58976, + 63682: 58977, + 63683: 58978, + 63684: 58979, + 63685: 58980, + 63686: 58981, + 63687: 58982, + 63688: 58983, + 63689: 58984, + 63690: 58985, + 63691: 58986, + 63692: 58987, + 63693: 58988, + 63694: 58989, + 63695: 58990, + 63696: 58991, + 63697: 58992, + 63698: 58993, + 63699: 58994, + 63700: 58995, + 63701: 58996, + 63702: 58997, + 63703: 58998, + 63704: 58999, + 63705: 59000, + 63706: 59001, + 63707: 59002, + 63708: 59003, + 63709: 59004, + 63710: 59005, + 63711: 59006, + 63712: 59007, + 63713: 59008, + 63714: 59009, + 63715: 59010, + 63716: 59011, + 63717: 59012, + 63718: 59013, + 63719: 59014, + 63720: 59015, + 63721: 59016, + 63722: 59017, + 63723: 59018, + 63724: 59019, + 63725: 59020, + 63726: 59021, + 63727: 59022, + 63728: 59023, + 63729: 59024, + 63730: 59025, + 63731: 59026, + 63732: 59027, + 63733: 59028, + 63734: 59029, + 63735: 59030, + 63736: 59031, + 63737: 59032, + 63738: 59033, + 63739: 59034, + 63740: 59035, + 64064: 8560, + 64065: 8561, + 64066: 8562, + 64067: 8563, + 64068: 8564, + 64069: 8565, + 64070: 8566, + 64071: 8567, + 64072: 8568, + 64073: 8569, + 64074: 8544, + 64075: 8545, + 64076: 8546, + 64077: 8547, + 64078: 8548, + 64079: 8549, + 64080: 8550, + 64081: 8551, + 64082: 8552, + 64083: 8553, + 64084: 65506, + 64085: 65508, + 64086: 65287, + 64087: 65282, + 64088: 12849, + 64089: 8470, + 64090: 8481, + 64091: 8757, + 64092: 32394, + 64093: 35100, + 64094: 37704, + 64095: 37512, + 64096: 34012, + 64097: 20425, + 64098: 28859, + 64099: 26161, + 64100: 26824, + 64101: 37625, + 64102: 26363, + 64103: 24389, + 64104: 20008, + 64105: 20193, + 64106: 20220, + 64107: 20224, + 64108: 20227, + 64109: 20281, + 64110: 20310, + 64111: 20370, + 64112: 20362, + 64113: 20378, + 64114: 20372, + 64115: 20429, + 64116: 20544, + 64117: 20514, + 64118: 20479, + 64119: 20510, + 64120: 20550, + 64121: 20592, + 64122: 20546, + 64123: 20628, + 64124: 20724, + 64125: 20696, + 64126: 20810, + 64128: 20836, + 64129: 20893, + 64130: 20926, + 64131: 20972, + 64132: 21013, + 64133: 21148, + 64134: 21158, + 64135: 21184, + 64136: 21211, + 64137: 21248, + 64138: 21255, + 64139: 21284, + 64140: 21362, + 64141: 21395, + 64142: 21426, + 64143: 21469, + 64144: 64014, + 64145: 21660, + 64146: 21642, + 64147: 21673, + 64148: 21759, + 64149: 21894, + 64150: 22361, + 64151: 22373, + 64152: 22444, + 64153: 22472, + 64154: 22471, + 64155: 64015, + 64156: 64016, + 64157: 22686, + 64158: 22706, + 64159: 22795, + 64160: 22867, + 64161: 22875, + 64162: 22877, + 64163: 22883, + 64164: 22948, + 64165: 22970, + 64166: 23382, + 64167: 23488, + 64168: 29999, + 64169: 23512, + 64170: 23532, + 64171: 23582, + 64172: 23718, + 64173: 23738, + 64174: 23797, + 64175: 23847, + 64176: 23891, + 64177: 64017, + 64178: 23874, + 64179: 23917, + 64180: 23992, + 64181: 23993, + 64182: 24016, + 64183: 24353, + 64184: 24372, + 64185: 24423, + 64186: 24503, + 64187: 24542, + 64188: 24669, + 64189: 24709, + 64190: 24714, + 64191: 24798, + 64192: 24789, + 64193: 24864, + 64194: 24818, + 64195: 24849, + 64196: 24887, + 64197: 24880, + 64198: 24984, + 64199: 25107, + 64200: 25254, + 64201: 25589, + 64202: 25696, + 64203: 25757, + 64204: 25806, + 64205: 25934, + 64206: 26112, + 64207: 26133, + 64208: 26171, + 64209: 26121, + 64210: 26158, + 64211: 26142, + 64212: 26148, + 64213: 26213, + 64214: 26199, + 64215: 26201, + 64216: 64018, + 64217: 26227, + 64218: 26265, + 64219: 26272, + 64220: 26290, + 64221: 26303, + 64222: 26362, + 64223: 26382, + 64224: 63785, + 64225: 26470, + 64226: 26555, + 64227: 26706, + 64228: 26560, + 64229: 26625, + 64230: 26692, + 64231: 26831, + 64232: 64019, + 64233: 26984, + 64234: 64020, + 64235: 27032, + 64236: 27106, + 64237: 27184, + 64238: 27243, + 64239: 27206, + 64240: 27251, + 64241: 27262, + 64242: 27362, + 64243: 27364, + 64244: 27606, + 64245: 27711, + 64246: 27740, + 64247: 27782, + 64248: 27759, + 64249: 27866, + 64250: 27908, + 64251: 28039, + 64252: 28015, + 64320: 28054, + 64321: 28076, + 64322: 28111, + 64323: 28152, + 64324: 28146, + 64325: 28156, + 64326: 28217, + 64327: 28252, + 64328: 28199, + 64329: 28220, + 64330: 28351, + 64331: 28552, + 64332: 28597, + 64333: 28661, + 64334: 28677, + 64335: 28679, + 64336: 28712, + 64337: 28805, + 64338: 28843, + 64339: 28943, + 64340: 28932, + 64341: 29020, + 64342: 28998, + 64343: 28999, + 64344: 64021, + 64345: 29121, + 64346: 29182, + 64347: 29361, + 64348: 29374, + 64349: 29476, + 64350: 64022, + 64351: 29559, + 64352: 29629, + 64353: 29641, + 64354: 29654, + 64355: 29667, + 64356: 29650, + 64357: 29703, + 64358: 29685, + 64359: 29734, + 64360: 29738, + 64361: 29737, + 64362: 29742, + 64363: 29794, + 64364: 29833, + 64365: 29855, + 64366: 29953, + 64367: 30063, + 64368: 30338, + 64369: 30364, + 64370: 30366, + 64371: 30363, + 64372: 30374, + 64373: 64023, + 64374: 30534, + 64375: 21167, + 64376: 30753, + 64377: 30798, + 64378: 30820, + 64379: 30842, + 64380: 31024, + 64381: 64024, + 64382: 64025, + 64384: 64026, + 64385: 31124, + 64386: 64027, + 64387: 31131, + 64388: 31441, + 64389: 31463, + 64390: 64028, + 64391: 31467, + 64392: 31646, + 64393: 64029, + 64394: 32072, + 64395: 32092, + 64396: 32183, + 64397: 32160, + 64398: 32214, + 64399: 32338, + 64400: 32583, + 64401: 32673, + 64402: 64030, + 64403: 33537, + 64404: 33634, + 64405: 33663, + 64406: 33735, + 64407: 33782, + 64408: 33864, + 64409: 33972, + 64410: 34131, + 64411: 34137, + 64412: 34155, + 64413: 64031, + 64414: 34224, + 64415: 64032, + 64416: 64033, + 64417: 34823, + 64418: 35061, + 64419: 35346, + 64420: 35383, + 64421: 35449, + 64422: 35495, + 64423: 35518, + 64424: 35551, + 64425: 64034, + 64426: 35574, + 64427: 35667, + 64428: 35711, + 64429: 36080, + 64430: 36084, + 64431: 36114, + 64432: 36214, + 64433: 64035, + 64434: 36559, + 64435: 64036, + 64436: 64037, + 64437: 36967, + 64438: 37086, + 64439: 64038, + 64440: 37141, + 64441: 37159, + 64442: 37338, + 64443: 37335, + 64444: 37342, + 64445: 37357, + 64446: 37358, + 64447: 37348, + 64448: 37349, + 64449: 37382, + 64450: 37392, + 64451: 37386, + 64452: 37434, + 64453: 37440, + 64454: 37436, + 64455: 37454, + 64456: 37465, + 64457: 37457, + 64458: 37433, + 64459: 37479, + 64460: 37543, + 64461: 37495, + 64462: 37496, + 64463: 37607, + 64464: 37591, + 64465: 37593, + 64466: 37584, + 64467: 64039, + 64468: 37589, + 64469: 37600, + 64470: 37587, + 64471: 37669, + 64472: 37665, + 64473: 37627, + 64474: 64040, + 64475: 37662, + 64476: 37631, + 64477: 37661, + 64478: 37634, + 64479: 37744, + 64480: 37719, + 64481: 37796, + 64482: 37830, + 64483: 37854, + 64484: 37880, + 64485: 37937, + 64486: 37957, + 64487: 37960, + 64488: 38290, + 64489: 63964, + 64490: 64041, + 64491: 38557, + 64492: 38575, + 64493: 38707, + 64494: 38715, + 64495: 38723, + 64496: 38733, + 64497: 38735, + 64498: 38737, + 64499: 38741, + 64500: 38999, + 64501: 39013, + 64502: 64042, + 64503: 64043, + 64504: 39207, + 64505: 64044, + 64506: 39326, + 64507: 39502, + 64508: 39641, + 64576: 39644, + 64577: 39797, + 64578: 39794, + 64579: 39823, + 64580: 39857, + 64581: 39867, + 64582: 39936, + 64583: 40304, + 64584: 40299, + 64585: 64045, + 64586: 40473, + 64587: 40657 +}; + +/** + * @author takahiro / https://github.com/takahirox + */ + +function DataViewEx( buffer, littleEndian ) { + + this.dv = new DataView( buffer ); + this.offset = 0; + this.littleEndian = ( littleEndian !== undefined ) ? littleEndian : true; + this.encoder = new CharsetEncoder(); + +} + +DataViewEx.prototype = { + + constructor: DataViewEx, + + getInt8: function () { + + var value = this.dv.getInt8( this.offset ); + this.offset += 1; + return value; + + }, + + getInt8Array: function ( size ) { + + var a = []; + + for ( var i = 0; i < size; i ++ ) { + + a.push( this.getInt8() ); + + } + + return a; + + }, + + getUint8: function () { + + var value = this.dv.getUint8( this.offset ); + this.offset += 1; + return value; + + }, + + getUint8Array: function ( size ) { + + var a = []; + + for ( var i = 0; i < size; i ++ ) { + + a.push( this.getUint8() ); + + } + + return a; + + }, + + + getInt16: function () { + + var value = this.dv.getInt16( this.offset, this.littleEndian ); + this.offset += 2; + return value; + + }, + + getInt16Array: function ( size ) { + + var a = []; + + for ( var i = 0; i < size; i ++ ) { + + a.push( this.getInt16() ); + + } + + return a; + + }, + + getUint16: function () { + + var value = this.dv.getUint16( this.offset, this.littleEndian ); + this.offset += 2; + return value; + + }, + + getUint16Array: function ( size ) { + + var a = []; + + for ( var i = 0; i < size; i ++ ) { + + a.push( this.getUint16() ); + + } + + return a; + + }, + + getInt32: function () { + + var value = this.dv.getInt32( this.offset, this.littleEndian ); + this.offset += 4; + return value; + + }, + + getInt32Array: function ( size ) { + + var a = []; + + for ( var i = 0; i < size; i ++ ) { + + a.push( this.getInt32() ); + + } + + return a; + + }, + + getUint32: function () { + + var value = this.dv.getUint32( this.offset, this.littleEndian ); + this.offset += 4; + return value; + + }, + + getUint32Array: function ( size ) { + + var a = []; + + for ( var i = 0; i < size; i ++ ) { + + a.push( this.getUint32() ); + + } + + return a; + + }, + + getFloat32: function () { + + var value = this.dv.getFloat32( this.offset, this.littleEndian ); + this.offset += 4; + return value; + + }, + + getFloat32Array: function ( size ) { + + var a = []; + + for ( var i = 0; i < size; i ++ ) { + + a.push( this.getFloat32() ); + + } + + return a; + + }, + + getFloat64: function () { + + var value = this.dv.getFloat64( this.offset, this.littleEndian ); + this.offset += 8; + return value; + + }, + + getFloat64Array: function ( size ) { + + var a = []; + + for ( var i = 0; i < size; i ++ ) { + + a.push( this.getFloat64() ); + + } + + return a; + + }, + + getIndex: function ( type, isUnsigned ) { + + switch ( type ) { + + case 1: + return ( isUnsigned === true ) ? this.getUint8() : this.getInt8(); + + case 2: + return ( isUnsigned === true ) ? this.getUint16() : this.getInt16(); + + case 4: + return this.getInt32(); // No Uint32 + + default: + throw 'unknown number type ' + type + ' exception.'; + + } + + }, + + getIndexArray: function ( type, size, isUnsigned ) { + + var a = []; + + for ( var i = 0; i < size; i ++ ) { + + a.push( this.getIndex( type, isUnsigned ) ); + + } + + return a; + + }, + + getChars: function ( size ) { + + var str = ''; + + while ( size > 0 ) { + + var value = this.getUint8(); + size --; + + if ( value === 0 ) { + + break; + + } + + str += String.fromCharCode( value ); + + } + + while ( size > 0 ) { + + this.getUint8(); + size --; + + } + + return str; + + }, + + getSjisStringsAsUnicode: function ( size ) { + + var a = []; + + while ( size > 0 ) { + + var value = this.getUint8(); + size --; + + if ( value === 0 ) { + + break; + + } + + a.push( value ); + + } + + while ( size > 0 ) { + + this.getUint8(); + size --; + + } + + return this.encoder.s2u( new Uint8Array( a ) ); + + }, + + getUnicodeStrings: function ( size ) { + + var str = ''; + + while ( size > 0 ) { + + var value = this.getUint16(); + size -= 2; + + if ( value === 0 ) { + + break; + + } + + str += String.fromCharCode( value ); + + } + + while ( size > 0 ) { + + this.getUint8(); + size --; + + } + + return str; + + }, + + getTextBuffer: function () { + + var size = this.getUint32(); + return this.getUnicodeStrings( size ); + + } + +}; + +/** + * @author takahiro / https://github.com/takahirox + */ + +function DataCreationHelper() { +} + +DataCreationHelper.prototype = { + + constructor: DataCreationHelper, + + leftToRightVector3: function ( v ) { + + v[ 2 ] = - v[ 2 ]; + + }, + + leftToRightQuaternion: function ( q ) { + + q[ 0 ] = - q[ 0 ]; + q[ 1 ] = - q[ 1 ]; + + }, + + leftToRightEuler: function ( r ) { + + r[ 0 ] = - r[ 0 ]; + r[ 1 ] = - r[ 1 ]; + + }, + + leftToRightIndexOrder: function ( p ) { + + var tmp = p[ 2 ]; + p[ 2 ] = p[ 0 ]; + p[ 0 ] = tmp; + + }, + + leftToRightVector3Range: function ( v1, v2 ) { + + var tmp = - v2[ 2 ]; + v2[ 2 ] = - v1[ 2 ]; + v1[ 2 ] = tmp; + + }, + + leftToRightEulerRange: function ( r1, r2 ) { + + var tmp1 = - r2[ 0 ]; + var tmp2 = - r2[ 1 ]; + r2[ 0 ] = - r1[ 0 ]; + r2[ 1 ] = - r1[ 1 ]; + r1[ 0 ] = tmp1; + r1[ 1 ] = tmp2; + + } + +}; + +/** + * @author takahiro / https://github.com/takahirox + */ + +function Parser() { +} + +Parser.prototype.parsePmd = function ( buffer, leftToRight ) { + + var pmd = {}; + var dv = new DataViewEx( buffer ); + + pmd.metadata = {}; + pmd.metadata.format = 'pmd'; + pmd.metadata.coordinateSystem = 'left'; + + var parseHeader = function () { + + var metadata = pmd.metadata; + metadata.magic = dv.getChars( 3 ); + + if ( metadata.magic !== 'Pmd' ) { + + throw 'PMD file magic is not Pmd, but ' + metadata.magic; + + } + + metadata.version = dv.getFloat32(); + metadata.modelName = dv.getSjisStringsAsUnicode( 20 ); + metadata.comment = dv.getSjisStringsAsUnicode( 256 ); + + }; + + var parseVertices = function () { + + var parseVertex = function () { + + var p = {}; + p.position = dv.getFloat32Array( 3 ); + p.normal = dv.getFloat32Array( 3 ); + p.uv = dv.getFloat32Array( 2 ); + p.skinIndices = dv.getUint16Array( 2 ); + p.skinWeights = [ dv.getUint8() / 100 ]; + p.skinWeights.push( 1.0 - p.skinWeights[ 0 ] ); + p.edgeFlag = dv.getUint8(); + return p; + + }; + + var metadata = pmd.metadata; + metadata.vertexCount = dv.getUint32(); + + pmd.vertices = []; + + for ( var i = 0; i < metadata.vertexCount; i ++ ) { + + pmd.vertices.push( parseVertex() ); + + } + + }; + + var parseFaces = function () { + + var parseFace = function () { + + var p = {}; + p.indices = dv.getUint16Array( 3 ); + return p; + + }; + + var metadata = pmd.metadata; + metadata.faceCount = dv.getUint32() / 3; + + pmd.faces = []; + + for ( var i = 0; i < metadata.faceCount; i ++ ) { + + pmd.faces.push( parseFace() ); + + } + + }; + + var parseMaterials = function () { + + var parseMaterial = function () { + + var p = {}; + p.diffuse = dv.getFloat32Array( 4 ); + p.shininess = dv.getFloat32(); + p.specular = dv.getFloat32Array( 3 ); + p.ambient = dv.getFloat32Array( 3 ); + p.toonIndex = dv.getInt8(); + p.edgeFlag = dv.getUint8(); + p.faceCount = dv.getUint32() / 3; + p.fileName = dv.getSjisStringsAsUnicode( 20 ); + return p; + + }; + + var metadata = pmd.metadata; + metadata.materialCount = dv.getUint32(); + + pmd.materials = []; + + for ( var i = 0; i < metadata.materialCount; i ++ ) { + + pmd.materials.push( parseMaterial() ); + + } + + }; + + var parseBones = function () { + + var parseBone = function () { + + var p = {}; + p.name = dv.getSjisStringsAsUnicode( 20 ); + p.parentIndex = dv.getInt16(); + p.tailIndex = dv.getInt16(); + p.type = dv.getUint8(); + p.ikIndex = dv.getInt16(); + p.position = dv.getFloat32Array( 3 ); + return p; + + }; + + var metadata = pmd.metadata; + metadata.boneCount = dv.getUint16(); + + pmd.bones = []; + + for ( var i = 0; i < metadata.boneCount; i ++ ) { + + pmd.bones.push( parseBone() ); + + } + + }; + + var parseIks = function () { + + var parseIk = function () { + + var p = {}; + p.target = dv.getUint16(); + p.effector = dv.getUint16(); + p.linkCount = dv.getUint8(); + p.iteration = dv.getUint16(); + p.maxAngle = dv.getFloat32(); + + p.links = []; + for ( var i = 0; i < p.linkCount; i ++ ) { + + var link = {}; + link.index = dv.getUint16(); + p.links.push( link ); + + } + + return p; + + }; + + var metadata = pmd.metadata; + metadata.ikCount = dv.getUint16(); + + pmd.iks = []; + + for ( var i = 0; i < metadata.ikCount; i ++ ) { + + pmd.iks.push( parseIk() ); + + } + + }; + + var parseMorphs = function () { + + var parseMorph = function () { + + var p = {}; + p.name = dv.getSjisStringsAsUnicode( 20 ); + p.elementCount = dv.getUint32(); + p.type = dv.getUint8(); + + p.elements = []; + for ( var i = 0; i < p.elementCount; i ++ ) { + + p.elements.push( { + index: dv.getUint32(), + position: dv.getFloat32Array( 3 ) + } ); + + } + + return p; + + }; + + var metadata = pmd.metadata; + metadata.morphCount = dv.getUint16(); + + pmd.morphs = []; + + for ( var i = 0; i < metadata.morphCount; i ++ ) { + + pmd.morphs.push( parseMorph() ); + + } + + + }; + + var parseMorphFrames = function () { + + var parseMorphFrame = function () { + + var p = {}; + p.index = dv.getUint16(); + return p; + + }; + + var metadata = pmd.metadata; + metadata.morphFrameCount = dv.getUint8(); + + pmd.morphFrames = []; + + for ( var i = 0; i < metadata.morphFrameCount; i ++ ) { + + pmd.morphFrames.push( parseMorphFrame() ); + + } + + }; + + var parseBoneFrameNames = function () { + + var parseBoneFrameName = function () { + + var p = {}; + p.name = dv.getSjisStringsAsUnicode( 50 ); + return p; + + }; + + var metadata = pmd.metadata; + metadata.boneFrameNameCount = dv.getUint8(); + + pmd.boneFrameNames = []; + + for ( var i = 0; i < metadata.boneFrameNameCount; i ++ ) { + + pmd.boneFrameNames.push( parseBoneFrameName() ); + + } + + }; + + var parseBoneFrames = function () { + + var parseBoneFrame = function () { + + var p = {}; + p.boneIndex = dv.getInt16(); + p.frameIndex = dv.getUint8(); + return p; + + }; + + var metadata = pmd.metadata; + metadata.boneFrameCount = dv.getUint32(); + + pmd.boneFrames = []; + + for ( var i = 0; i < metadata.boneFrameCount; i ++ ) { + + pmd.boneFrames.push( parseBoneFrame() ); + + } + + }; + + var parseEnglishHeader = function () { + + var metadata = pmd.metadata; + metadata.englishCompatibility = dv.getUint8(); + + if ( metadata.englishCompatibility > 0 ) { + + metadata.englishModelName = dv.getSjisStringsAsUnicode( 20 ); + metadata.englishComment = dv.getSjisStringsAsUnicode( 256 ); + + } + + }; + + var parseEnglishBoneNames = function () { + + var parseEnglishBoneName = function () { + + var p = {}; + p.name = dv.getSjisStringsAsUnicode( 20 ); + return p; + + }; + + var metadata = pmd.metadata; + + if ( metadata.englishCompatibility === 0 ) { + + return; + + } + + pmd.englishBoneNames = []; + + for ( var i = 0; i < metadata.boneCount; i ++ ) { + + pmd.englishBoneNames.push( parseEnglishBoneName() ); + + } + + }; + + var parseEnglishMorphNames = function () { + + var parseEnglishMorphName = function () { + + var p = {}; + p.name = dv.getSjisStringsAsUnicode( 20 ); + return p; + + }; + + var metadata = pmd.metadata; + + if ( metadata.englishCompatibility === 0 ) { + + return; + + } + + pmd.englishMorphNames = []; + + for ( var i = 0; i < metadata.morphCount - 1; i ++ ) { + + pmd.englishMorphNames.push( parseEnglishMorphName() ); + + } + + }; + + var parseEnglishBoneFrameNames = function () { + + var parseEnglishBoneFrameName = function () { + + var p = {}; + p.name = dv.getSjisStringsAsUnicode( 50 ); + return p; + + }; + + var metadata = pmd.metadata; + + if ( metadata.englishCompatibility === 0 ) { + + return; + + } + + pmd.englishBoneFrameNames = []; + + for ( var i = 0; i < metadata.boneFrameNameCount; i ++ ) { + + pmd.englishBoneFrameNames.push( parseEnglishBoneFrameName() ); + + } + + }; + + var parseToonTextures = function () { + + var parseToonTexture = function () { + + var p = {}; + p.fileName = dv.getSjisStringsAsUnicode( 100 ); + return p; + + }; + + pmd.toonTextures = []; + + for ( var i = 0; i < 10; i ++ ) { + + pmd.toonTextures.push( parseToonTexture() ); + + } + + }; + + var parseRigidBodies = function () { + + var parseRigidBody = function () { + + var p = {}; + p.name = dv.getSjisStringsAsUnicode( 20 ); + p.boneIndex = dv.getInt16(); + p.groupIndex = dv.getUint8(); + p.groupTarget = dv.getUint16(); + p.shapeType = dv.getUint8(); + p.width = dv.getFloat32(); + p.height = dv.getFloat32(); + p.depth = dv.getFloat32(); + p.position = dv.getFloat32Array( 3 ); + p.rotation = dv.getFloat32Array( 3 ); + p.weight = dv.getFloat32(); + p.positionDamping = dv.getFloat32(); + p.rotationDamping = dv.getFloat32(); + p.restitution = dv.getFloat32(); + p.friction = dv.getFloat32(); + p.type = dv.getUint8(); + return p; + + }; + + var metadata = pmd.metadata; + metadata.rigidBodyCount = dv.getUint32(); + + pmd.rigidBodies = []; + + for ( var i = 0; i < metadata.rigidBodyCount; i ++ ) { + + pmd.rigidBodies.push( parseRigidBody() ); + + } + + }; + + var parseConstraints = function () { + + var parseConstraint = function () { + + var p = {}; + p.name = dv.getSjisStringsAsUnicode( 20 ); + p.rigidBodyIndex1 = dv.getUint32(); + p.rigidBodyIndex2 = dv.getUint32(); + p.position = dv.getFloat32Array( 3 ); + p.rotation = dv.getFloat32Array( 3 ); + p.translationLimitation1 = dv.getFloat32Array( 3 ); + p.translationLimitation2 = dv.getFloat32Array( 3 ); + p.rotationLimitation1 = dv.getFloat32Array( 3 ); + p.rotationLimitation2 = dv.getFloat32Array( 3 ); + p.springPosition = dv.getFloat32Array( 3 ); + p.springRotation = dv.getFloat32Array( 3 ); + return p; + + }; + + var metadata = pmd.metadata; + metadata.constraintCount = dv.getUint32(); + + pmd.constraints = []; + + for ( var i = 0; i < metadata.constraintCount; i ++ ) { + + pmd.constraints.push( parseConstraint() ); + + } + + }; + + parseHeader(); + parseVertices(); + parseFaces(); + parseMaterials(); + parseBones(); + parseIks(); + parseMorphs(); + parseMorphFrames(); + parseBoneFrameNames(); + parseBoneFrames(); + parseEnglishHeader(); + parseEnglishBoneNames(); + parseEnglishMorphNames(); + parseEnglishBoneFrameNames(); + parseToonTextures(); + parseRigidBodies(); + parseConstraints(); + + if ( leftToRight === true ) this.leftToRightModel( pmd ); + + // console.log( pmd ); // for console debug + + return pmd; + +}; + +Parser.prototype.parsePmx = function ( buffer, leftToRight ) { + + var pmx = {}; + var dv = new DataViewEx( buffer ); + + pmx.metadata = {}; + pmx.metadata.format = 'pmx'; + pmx.metadata.coordinateSystem = 'left'; + + var parseHeader = function () { + + var metadata = pmx.metadata; + metadata.magic = dv.getChars( 4 ); + + // Note: don't remove the last blank space. + if ( metadata.magic !== 'PMX ' ) { + + throw 'PMX file magic is not PMX , but ' + metadata.magic; + + } + + metadata.version = dv.getFloat32(); + + if ( metadata.version !== 2.0 && metadata.version !== 2.1 ) { + + throw 'PMX version ' + metadata.version + ' is not supported.'; + + } + + metadata.headerSize = dv.getUint8(); + metadata.encoding = dv.getUint8(); + metadata.additionalUvNum = dv.getUint8(); + metadata.vertexIndexSize = dv.getUint8(); + metadata.textureIndexSize = dv.getUint8(); + metadata.materialIndexSize = dv.getUint8(); + metadata.boneIndexSize = dv.getUint8(); + metadata.morphIndexSize = dv.getUint8(); + metadata.rigidBodyIndexSize = dv.getUint8(); + metadata.modelName = dv.getTextBuffer(); + metadata.englishModelName = dv.getTextBuffer(); + metadata.comment = dv.getTextBuffer(); + metadata.englishComment = dv.getTextBuffer(); + + }; + + var parseVertices = function () { + + var parseVertex = function () { + + var p = {}; + p.position = dv.getFloat32Array( 3 ); + p.normal = dv.getFloat32Array( 3 ); + p.uv = dv.getFloat32Array( 2 ); + + p.auvs = []; + + for ( var i = 0; i < pmx.metadata.additionalUvNum; i ++ ) { + + p.auvs.push( dv.getFloat32Array( 4 ) ); + + } + + p.type = dv.getUint8(); + + var indexSize = metadata.boneIndexSize; + + if ( p.type === 0 ) { // BDEF1 + + p.skinIndices = dv.getIndexArray( indexSize, 1 ); + p.skinWeights = [ 1.0 ]; + + } else if ( p.type === 1 ) { // BDEF2 + + p.skinIndices = dv.getIndexArray( indexSize, 2 ); + p.skinWeights = dv.getFloat32Array( 1 ); + p.skinWeights.push( 1.0 - p.skinWeights[ 0 ] ); + + } else if ( p.type === 2 ) { // BDEF4 + + p.skinIndices = dv.getIndexArray( indexSize, 4 ); + p.skinWeights = dv.getFloat32Array( 4 ); + + } else if ( p.type === 3 ) { // SDEF + + p.skinIndices = dv.getIndexArray( indexSize, 2 ); + p.skinWeights = dv.getFloat32Array( 1 ); + p.skinWeights.push( 1.0 - p.skinWeights[ 0 ] ); + + p.skinC = dv.getFloat32Array( 3 ); + p.skinR0 = dv.getFloat32Array( 3 ); + p.skinR1 = dv.getFloat32Array( 3 ); + + // SDEF is not supported yet and is handled as BDEF2 so far. + // TODO: SDEF support + p.type = 1; + + } else { + + throw 'unsupport bone type ' + p.type + ' exception.'; + + } + + p.edgeRatio = dv.getFloat32(); + return p; + + }; + + var metadata = pmx.metadata; + metadata.vertexCount = dv.getUint32(); + + pmx.vertices = []; + + for ( var i = 0; i < metadata.vertexCount; i ++ ) { + + pmx.vertices.push( parseVertex() ); + + } + + }; + + var parseFaces = function () { + + var parseFace = function () { + + var p = {}; + p.indices = dv.getIndexArray( metadata.vertexIndexSize, 3, true ); + return p; + + }; + + var metadata = pmx.metadata; + metadata.faceCount = dv.getUint32() / 3; + + pmx.faces = []; + + for ( var i = 0; i < metadata.faceCount; i ++ ) { + + pmx.faces.push( parseFace() ); + + } + + }; + + var parseTextures = function () { + + var parseTexture = function () { + + return dv.getTextBuffer(); + + }; + + var metadata = pmx.metadata; + metadata.textureCount = dv.getUint32(); + + pmx.textures = []; + + for ( var i = 0; i < metadata.textureCount; i ++ ) { + + pmx.textures.push( parseTexture() ); + + } + + }; + + var parseMaterials = function () { + + var parseMaterial = function () { + + var p = {}; + p.name = dv.getTextBuffer(); + p.englishName = dv.getTextBuffer(); + p.diffuse = dv.getFloat32Array( 4 ); + p.specular = dv.getFloat32Array( 3 ); + p.shininess = dv.getFloat32(); + p.ambient = dv.getFloat32Array( 3 ); + p.flag = dv.getUint8(); + p.edgeColor = dv.getFloat32Array( 4 ); + p.edgeSize = dv.getFloat32(); + p.textureIndex = dv.getIndex( pmx.metadata.textureIndexSize ); + p.envTextureIndex = dv.getIndex( pmx.metadata.textureIndexSize ); + p.envFlag = dv.getUint8(); + p.toonFlag = dv.getUint8(); + + if ( p.toonFlag === 0 ) { + + p.toonIndex = dv.getIndex( pmx.metadata.textureIndexSize ); + + } else if ( p.toonFlag === 1 ) { + + p.toonIndex = dv.getInt8(); + + } else { + + throw 'unknown toon flag ' + p.toonFlag + ' exception.'; + + } + + p.comment = dv.getTextBuffer(); + p.faceCount = dv.getUint32() / 3; + return p; + + }; + + var metadata = pmx.metadata; + metadata.materialCount = dv.getUint32(); + + pmx.materials = []; + + for ( var i = 0; i < metadata.materialCount; i ++ ) { + + pmx.materials.push( parseMaterial() ); + + } + + }; + + var parseBones = function () { + + var parseBone = function () { + + var p = {}; + p.name = dv.getTextBuffer(); + p.englishName = dv.getTextBuffer(); + p.position = dv.getFloat32Array( 3 ); + p.parentIndex = dv.getIndex( pmx.metadata.boneIndexSize ); + p.transformationClass = dv.getUint32(); + p.flag = dv.getUint16(); + + if ( p.flag & 0x1 ) { + + p.connectIndex = dv.getIndex( pmx.metadata.boneIndexSize ); + + } else { + + p.offsetPosition = dv.getFloat32Array( 3 ); + + } + + if ( p.flag & 0x100 || p.flag & 0x200 ) { + + // Note: I don't think Grant is an appropriate name + // but I found that some English translated MMD tools use this term + // so I've named it Grant so far. + // I'd rename to more appropriate name from Grant later. + var grant = {}; + + grant.isLocal = ( p.flag & 0x80 ) !== 0 ? true : false; + grant.affectRotation = ( p.flag & 0x100 ) !== 0 ? true : false; + grant.affectPosition = ( p.flag & 0x200 ) !== 0 ? true : false; + grant.parentIndex = dv.getIndex( pmx.metadata.boneIndexSize ); + grant.ratio = dv.getFloat32(); + + p.grant = grant; + + } + + if ( p.flag & 0x400 ) { + + p.fixAxis = dv.getFloat32Array( 3 ); + + } + + if ( p.flag & 0x800 ) { + + p.localXVector = dv.getFloat32Array( 3 ); + p.localZVector = dv.getFloat32Array( 3 ); + + } + + if ( p.flag & 0x2000 ) { + + p.key = dv.getUint32(); + + } + + if ( p.flag & 0x20 ) { + + var ik = {}; + + ik.effector = dv.getIndex( pmx.metadata.boneIndexSize ); + ik.target = null; + ik.iteration = dv.getUint32(); + ik.maxAngle = dv.getFloat32(); + ik.linkCount = dv.getUint32(); + ik.links = []; + + for ( var i = 0; i < ik.linkCount; i ++ ) { + + var link = {}; + link.index = dv.getIndex( pmx.metadata.boneIndexSize ); + link.angleLimitation = dv.getUint8(); + + if ( link.angleLimitation === 1 ) { + + link.lowerLimitationAngle = dv.getFloat32Array( 3 ); + link.upperLimitationAngle = dv.getFloat32Array( 3 ); + + } + + ik.links.push( link ); + + } + + p.ik = ik; + + } + + return p; + + }; + + var metadata = pmx.metadata; + metadata.boneCount = dv.getUint32(); + + pmx.bones = []; + + for ( var i = 0; i < metadata.boneCount; i ++ ) { + + pmx.bones.push( parseBone() ); + + } + + }; + + var parseMorphs = function () { + + var parseMorph = function () { + + var p = {}; + p.name = dv.getTextBuffer(); + p.englishName = dv.getTextBuffer(); + p.panel = dv.getUint8(); + p.type = dv.getUint8(); + p.elementCount = dv.getUint32(); + p.elements = []; + + for ( var i = 0; i < p.elementCount; i ++ ) { + + if ( p.type === 0 ) { // group morph + + var m = {}; + m.index = dv.getIndex( pmx.metadata.morphIndexSize ); + m.ratio = dv.getFloat32(); + p.elements.push( m ); + + } else if ( p.type === 1 ) { // vertex morph + + var m = {}; + m.index = dv.getIndex( pmx.metadata.vertexIndexSize, true ); + m.position = dv.getFloat32Array( 3 ); + p.elements.push( m ); + + } else if ( p.type === 2 ) { // bone morph + + var m = {}; + m.index = dv.getIndex( pmx.metadata.boneIndexSize ); + m.position = dv.getFloat32Array( 3 ); + m.rotation = dv.getFloat32Array( 4 ); + p.elements.push( m ); + + } else if ( p.type === 3 ) { // uv morph + + var m = {}; + m.index = dv.getIndex( pmx.metadata.vertexIndexSize, true ); + m.uv = dv.getFloat32Array( 4 ); + p.elements.push( m ); + + } else if ( p.type === 4 ) { // additional uv1 + + // TODO: implement + + } else if ( p.type === 5 ) { // additional uv2 + + // TODO: implement + + } else if ( p.type === 6 ) { // additional uv3 + + // TODO: implement + + } else if ( p.type === 7 ) { // additional uv4 + + // TODO: implement + + } else if ( p.type === 8 ) { // material morph + + var m = {}; + m.index = dv.getIndex( pmx.metadata.materialIndexSize ); + m.type = dv.getUint8(); + m.diffuse = dv.getFloat32Array( 4 ); + m.specular = dv.getFloat32Array( 3 ); + m.shininess = dv.getFloat32(); + m.ambient = dv.getFloat32Array( 3 ); + m.edgeColor = dv.getFloat32Array( 4 ); + m.edgeSize = dv.getFloat32(); + m.textureColor = dv.getFloat32Array( 4 ); + m.sphereTextureColor = dv.getFloat32Array( 4 ); + m.toonColor = dv.getFloat32Array( 4 ); + p.elements.push( m ); + + } + + } + + return p; + + }; + + var metadata = pmx.metadata; + metadata.morphCount = dv.getUint32(); + + pmx.morphs = []; + + for ( var i = 0; i < metadata.morphCount; i ++ ) { + + pmx.morphs.push( parseMorph() ); + + } + + }; + + var parseFrames = function () { + + var parseFrame = function () { + + var p = {}; + p.name = dv.getTextBuffer(); + p.englishName = dv.getTextBuffer(); + p.type = dv.getUint8(); + p.elementCount = dv.getUint32(); + p.elements = []; + + for ( var i = 0; i < p.elementCount; i ++ ) { + + var e = {}; + e.target = dv.getUint8(); + e.index = ( e.target === 0 ) ? dv.getIndex( pmx.metadata.boneIndexSize ) : dv.getIndex( pmx.metadata.morphIndexSize ); + p.elements.push( e ); + + } + + return p; + + }; + + var metadata = pmx.metadata; + metadata.frameCount = dv.getUint32(); + + pmx.frames = []; + + for ( var i = 0; i < metadata.frameCount; i ++ ) { + + pmx.frames.push( parseFrame() ); + + } + + }; + + var parseRigidBodies = function () { + + var parseRigidBody = function () { + + var p = {}; + p.name = dv.getTextBuffer(); + p.englishName = dv.getTextBuffer(); + p.boneIndex = dv.getIndex( pmx.metadata.boneIndexSize ); + p.groupIndex = dv.getUint8(); + p.groupTarget = dv.getUint16(); + p.shapeType = dv.getUint8(); + p.width = dv.getFloat32(); + p.height = dv.getFloat32(); + p.depth = dv.getFloat32(); + p.position = dv.getFloat32Array( 3 ); + p.rotation = dv.getFloat32Array( 3 ); + p.weight = dv.getFloat32(); + p.positionDamping = dv.getFloat32(); + p.rotationDamping = dv.getFloat32(); + p.restitution = dv.getFloat32(); + p.friction = dv.getFloat32(); + p.type = dv.getUint8(); + return p; + + }; + + var metadata = pmx.metadata; + metadata.rigidBodyCount = dv.getUint32(); + + pmx.rigidBodies = []; + + for ( var i = 0; i < metadata.rigidBodyCount; i ++ ) { + + pmx.rigidBodies.push( parseRigidBody() ); + + } + + }; + + var parseConstraints = function () { + + var parseConstraint = function () { + + var p = {}; + p.name = dv.getTextBuffer(); + p.englishName = dv.getTextBuffer(); + p.type = dv.getUint8(); + p.rigidBodyIndex1 = dv.getIndex( pmx.metadata.rigidBodyIndexSize ); + p.rigidBodyIndex2 = dv.getIndex( pmx.metadata.rigidBodyIndexSize ); + p.position = dv.getFloat32Array( 3 ); + p.rotation = dv.getFloat32Array( 3 ); + p.translationLimitation1 = dv.getFloat32Array( 3 ); + p.translationLimitation2 = dv.getFloat32Array( 3 ); + p.rotationLimitation1 = dv.getFloat32Array( 3 ); + p.rotationLimitation2 = dv.getFloat32Array( 3 ); + p.springPosition = dv.getFloat32Array( 3 ); + p.springRotation = dv.getFloat32Array( 3 ); + return p; + + }; + + var metadata = pmx.metadata; + metadata.constraintCount = dv.getUint32(); + + pmx.constraints = []; + + for ( var i = 0; i < metadata.constraintCount; i ++ ) { + + pmx.constraints.push( parseConstraint() ); + + } + + }; + + parseHeader(); + parseVertices(); + parseFaces(); + parseTextures(); + parseMaterials(); + parseBones(); + parseMorphs(); + parseFrames(); + parseRigidBodies(); + parseConstraints(); + + if ( leftToRight === true ) this.leftToRightModel( pmx ); + + // console.log( pmx ); // for console debug + + return pmx; + +}; + +Parser.prototype.parseVmd = function ( buffer, leftToRight ) { + + var vmd = {}; + var dv = new DataViewEx( buffer ); + + vmd.metadata = {}; + vmd.metadata.coordinateSystem = 'left'; + + var parseHeader = function () { + + var metadata = vmd.metadata; + metadata.magic = dv.getChars( 30 ); + + if ( metadata.magic !== 'Vocaloid Motion Data 0002' ) { + + throw 'VMD file magic is not Vocaloid Motion Data 0002, but ' + metadata.magic; + + } + + metadata.name = dv.getSjisStringsAsUnicode( 20 ); + + }; + + var parseMotions = function () { + + var parseMotion = function () { + + var p = {}; + p.boneName = dv.getSjisStringsAsUnicode( 15 ); + p.frameNum = dv.getUint32(); + p.position = dv.getFloat32Array( 3 ); + p.rotation = dv.getFloat32Array( 4 ); + p.interpolation = dv.getUint8Array( 64 ); + return p; + + }; + + var metadata = vmd.metadata; + metadata.motionCount = dv.getUint32(); + + vmd.motions = []; + for ( var i = 0; i < metadata.motionCount; i ++ ) { + + vmd.motions.push( parseMotion() ); + + } + + }; + + var parseMorphs = function () { + + var parseMorph = function () { + + var p = {}; + p.morphName = dv.getSjisStringsAsUnicode( 15 ); + p.frameNum = dv.getUint32(); + p.weight = dv.getFloat32(); + return p; + + }; + + var metadata = vmd.metadata; + metadata.morphCount = dv.getUint32(); + + vmd.morphs = []; + for ( var i = 0; i < metadata.morphCount; i ++ ) { + + vmd.morphs.push( parseMorph() ); + + } + + }; + + var parseCameras = function () { + + var parseCamera = function () { + + var p = {}; + p.frameNum = dv.getUint32(); + p.distance = dv.getFloat32(); + p.position = dv.getFloat32Array( 3 ); + p.rotation = dv.getFloat32Array( 3 ); + p.interpolation = dv.getUint8Array( 24 ); + p.fov = dv.getUint32(); + p.perspective = dv.getUint8(); + return p; + + }; + + var metadata = vmd.metadata; + metadata.cameraCount = dv.getUint32(); + + vmd.cameras = []; + for ( var i = 0; i < metadata.cameraCount; i ++ ) { + + vmd.cameras.push( parseCamera() ); + + } + + }; + + parseHeader(); + parseMotions(); + parseMorphs(); + parseCameras(); + + if ( leftToRight === true ) this.leftToRightVmd( vmd ); + + // console.log( vmd ); // for console debug + + return vmd; + +}; + +Parser.prototype.parseVpd = function ( text, leftToRight ) { + + var vpd = {}; + + vpd.metadata = {}; + vpd.metadata.coordinateSystem = 'left'; + + vpd.bones = []; + + var commentPatternG = /\/\/\w*(\r|\n|\r\n)/g; + var newlinePattern = /\r|\n|\r\n/; + + var lines = text.replace( commentPatternG, '' ).split( newlinePattern ); + + function throwError() { + + throw 'the file seems not vpd file.'; + + } + + function checkMagic() { + + if ( lines[ 0 ] !== 'Vocaloid Pose Data file' ) { + + throwError(); + + } + + } + + function parseHeader() { + + if ( lines.length < 4 ) { + + throwError(); + + } + + vpd.metadata.parentFile = lines[ 2 ]; + vpd.metadata.boneCount = parseInt( lines[ 3 ] ); + + } + + function parseBones() { + + var boneHeaderPattern = /^\s*(Bone[0-9]+)\s*\{\s*(.*)$/; + var boneVectorPattern = /^\s*(-?[0-9]+\.[0-9]+)\s*,\s*(-?[0-9]+\.[0-9]+)\s*,\s*(-?[0-9]+\.[0-9]+)\s*;/; + var boneQuaternionPattern = /^\s*(-?[0-9]+\.[0-9]+)\s*,\s*(-?[0-9]+\.[0-9]+)\s*,\s*(-?[0-9]+\.[0-9]+)\s*,\s*(-?[0-9]+\.[0-9]+)\s*;/; + var boneFooterPattern = /^\s*}/; + + var bones = vpd.bones; + var n = null; + var v = null; + var q = null; + + for ( var i = 4; i < lines.length; i ++ ) { + + var line = lines[ i ]; + + var result; + + result = line.match( boneHeaderPattern ); + + if ( result !== null ) { + + if ( n !== null ) { + + throwError(); + + } + + n = result[ 2 ]; + + } + + result = line.match( boneVectorPattern ); + + if ( result !== null ) { + + if ( v !== null ) { + + throwError(); + + } + + v = [ + + parseFloat( result[ 1 ] ), + parseFloat( result[ 2 ] ), + parseFloat( result[ 3 ] ) + + ]; + + } + + result = line.match( boneQuaternionPattern ); + + if ( result !== null ) { + + if ( q !== null ) { + + throwError(); + + } + + q = [ + + parseFloat( result[ 1 ] ), + parseFloat( result[ 2 ] ), + parseFloat( result[ 3 ] ), + parseFloat( result[ 4 ] ) + + ]; + + + } + + result = line.match( boneFooterPattern ); + + if ( result !== null ) { + + if ( n === null || v === null || q === null ) { + + throwError(); + + } + + bones.push( { + + name: n, + translation: v, + quaternion: q + + } ); + + n = null; + v = null; + q = null; + + } + + } + + if ( n !== null || v !== null || q !== null ) { + + throwError(); + + } + + } + + checkMagic(); + parseHeader(); + parseBones(); + + if ( leftToRight === true ) this.leftToRightVpd( vpd ); + + // console.log( vpd ); // for console debug + + return vpd; + +}; + +Parser.prototype.mergeVmds = function ( vmds ) { + + var v = {}; + v.metadata = {}; + v.metadata.name = vmds[ 0 ].metadata.name; + v.metadata.coordinateSystem = vmds[ 0 ].metadata.coordinateSystem; + v.metadata.motionCount = 0; + v.metadata.morphCount = 0; + v.metadata.cameraCount = 0; + v.motions = []; + v.morphs = []; + v.cameras = []; + + for ( var i = 0; i < vmds.length; i ++ ) { + + var v2 = vmds[ i ]; + + v.metadata.motionCount += v2.metadata.motionCount; + v.metadata.morphCount += v2.metadata.morphCount; + v.metadata.cameraCount += v2.metadata.cameraCount; + + for ( var j = 0; j < v2.metadata.motionCount; j ++ ) { + + v.motions.push( v2.motions[ j ] ); + + } + + for ( var j = 0; j < v2.metadata.morphCount; j ++ ) { + + v.morphs.push( v2.morphs[ j ] ); + + } + + for ( var j = 0; j < v2.metadata.cameraCount; j ++ ) { + + v.cameras.push( v2.cameras[ j ] ); + + } + + } + + return v; + +}; + +Parser.prototype.leftToRightModel = function ( model ) { + + if ( model.metadata.coordinateSystem === 'right' ) { + + return; + + } + + model.metadata.coordinateSystem = 'right'; + + var helper = new DataCreationHelper(); + + for ( var i = 0; i < model.metadata.vertexCount; i ++ ) { + + helper.leftToRightVector3( model.vertices[ i ].position ); + helper.leftToRightVector3( model.vertices[ i ].normal ); + + } + + for ( var i = 0; i < model.metadata.faceCount; i ++ ) { + + helper.leftToRightIndexOrder( model.faces[ i ].indices ); + + } + + for ( var i = 0; i < model.metadata.boneCount; i ++ ) { + + helper.leftToRightVector3( model.bones[ i ].position ); + + } + + // TODO: support other morph for PMX + for ( var i = 0; i < model.metadata.morphCount; i ++ ) { + + var m = model.morphs[ i ]; + + if ( model.metadata.format === 'pmx' && m.type !== 1 ) { + + // TODO: implement + continue; + + } + + for ( var j = 0; j < m.elements.length; j ++ ) { + + helper.leftToRightVector3( m.elements[ j ].position ); + + } + + } + + for ( var i = 0; i < model.metadata.rigidBodyCount; i ++ ) { + + helper.leftToRightVector3( model.rigidBodies[ i ].position ); + helper.leftToRightEuler( model.rigidBodies[ i ].rotation ); + + } + + for ( var i = 0; i < model.metadata.constraintCount; i ++ ) { + + helper.leftToRightVector3( model.constraints[ i ].position ); + helper.leftToRightEuler( model.constraints[ i ].rotation ); + helper.leftToRightVector3Range( model.constraints[ i ].translationLimitation1, model.constraints[ i ].translationLimitation2 ); + helper.leftToRightEulerRange( model.constraints[ i ].rotationLimitation1, model.constraints[ i ].rotationLimitation2 ); + + } + +}; + +Parser.prototype.leftToRightVmd = function ( vmd ) { + + if ( vmd.metadata.coordinateSystem === 'right' ) { + + return; + + } + + vmd.metadata.coordinateSystem = 'right'; + + var helper = new DataCreationHelper(); + + for ( var i = 0; i < vmd.metadata.motionCount; i ++ ) { + + helper.leftToRightVector3( vmd.motions[ i ].position ); + helper.leftToRightQuaternion( vmd.motions[ i ].rotation ); + + } + + for ( var i = 0; i < vmd.metadata.cameraCount; i ++ ) { + + helper.leftToRightVector3( vmd.cameras[ i ].position ); + helper.leftToRightEuler( vmd.cameras[ i ].rotation ); + + } + +}; + +Parser.prototype.leftToRightVpd = function ( vpd ) { + + if ( vpd.metadata.coordinateSystem === 'right' ) { + + return; + + } + + vpd.metadata.coordinateSystem = 'right'; + + var helper = new DataCreationHelper(); + + for ( var i = 0; i < vpd.bones.length; i ++ ) { + + helper.leftToRightVector3( vpd.bones[ i ].translation ); + helper.leftToRightQuaternion( vpd.bones[ i ].quaternion ); + + } + +}; + +export { CharsetEncoder, Parser }; diff --git a/examples/jsm/loaders/3MFLoader.js b/examples/jsm/loaders/3MFLoader.js index 8489c33b036e48..853fd9874d6f79 100644 --- a/examples/jsm/loaders/3MFLoader.js +++ b/examples/jsm/loaders/3MFLoader.js @@ -10,6 +10,7 @@ import { Group, LoaderUtils, Matrix4, + Mesh, MeshPhongMaterial, VertexColors, } from "../../../build/three.module.js"; diff --git a/examples/jsm/loaders/BabylonLoader.js b/examples/jsm/loaders/BabylonLoader.js index 07ab0acdda4735..3b448166655449 100644 --- a/examples/jsm/loaders/BabylonLoader.js +++ b/examples/jsm/loaders/BabylonLoader.js @@ -11,6 +11,7 @@ import { Float32BufferAttribute, Group, HemisphereLight, + Mesh, MeshPhongMaterial, PerspectiveCamera, PointLight, diff --git a/examples/jsm/loaders/ColladaLoader.js b/examples/jsm/loaders/ColladaLoader.js index 36f3653c9521a1..371c9afaf6d4e2 100644 --- a/examples/jsm/loaders/ColladaLoader.js +++ b/examples/jsm/loaders/ColladaLoader.js @@ -21,7 +21,7 @@ import { LineBasicMaterial, LineSegments, LoaderUtils, - Math, + Math as _Math, Matrix4, Mesh, MeshBasicMaterial, @@ -2790,7 +2790,7 @@ ColladaLoader.prototype = { case 'rotate': data.obj = new Vector3(); data.obj.fromArray( array ); - data.angle = Math.degToRad( array[ 3 ] ); + data.angle = _Math.degToRad( array[ 3 ] ); break; } @@ -3062,7 +3062,7 @@ ColladaLoader.prototype = { switch ( joint.type ) { case 'revolute': - matrix.multiply( m0.makeRotationAxis( axis, Math.degToRad( value ) ) ); + matrix.multiply( m0.makeRotationAxis( axis, _Math.degToRad( value ) ) ); break; case 'prismatic': @@ -3158,7 +3158,7 @@ ColladaLoader.prototype = { case 'rotate': var array = parseFloats( child.textContent ); var vector = new Vector3().fromArray( array ); - var angle = Math.degToRad( array[ 3 ] ); + var angle = _Math.degToRad( array[ 3 ] ); transforms.push( { sid: child.getAttribute( 'sid' ), type: child.nodeName, @@ -3265,7 +3265,7 @@ ColladaLoader.prototype = { case 'rotate': var array = parseFloats( child.textContent ); - var angle = Math.degToRad( array[ 3 ] ); + var angle = _Math.degToRad( array[ 3 ] ); data.matrix.multiply( matrix.makeRotationAxis( vector.fromArray( array ), angle ) ); data.transforms[ child.getAttribute( 'sid' ) ] = child.nodeName; break; diff --git a/examples/jsm/loaders/FBXLoader.js b/examples/jsm/loaders/FBXLoader.js index eaefcaa7488630..63e0ff7a03cde9 100644 --- a/examples/jsm/loaders/FBXLoader.js +++ b/examples/jsm/loaders/FBXLoader.js @@ -38,6 +38,7 @@ import { Loader, Line, LineBasicMaterial, + Math as _Math, Matrix3, Matrix4, Mesh, @@ -1191,7 +1192,7 @@ FBXTreeParser.prototype = { // TODO: this is not correct - FBX calculates outer and inner angle in degrees // with OuterAngle > InnerAngle && OuterAngle <= Math.PI // while three.js uses a penumbra between (0, 1) to attenuate the inner angle - penumbra = Math.degToRad( lightAttribute.OuterAngle.value ); + penumbra = _Math.degToRad( lightAttribute.OuterAngle.value ); penumbra = Math.max( penumbra, 1 ); } @@ -2737,19 +2738,19 @@ AnimationParser.prototype = { if ( curves.x !== undefined ) { this.interpolateRotations( curves.x ); - curves.x.values = curves.x.values.map( Math.degToRad ); + curves.x.values = curves.x.values.map( _Math.degToRad ); } if ( curves.y !== undefined ) { this.interpolateRotations( curves.y ); - curves.y.values = curves.y.values.map( Math.degToRad ); + curves.y.values = curves.y.values.map( _Math.degToRad ); } if ( curves.z !== undefined ) { this.interpolateRotations( curves.z ); - curves.z.values = curves.z.values.map( Math.degToRad ); + curves.z.values = curves.z.values.map( _Math.degToRad ); } @@ -2768,7 +2769,7 @@ AnimationParser.prototype = { if ( postRotation !== undefined ) { - postRotation = postRotation.map( Math.degToRad ); + postRotation = postRotation.map( _Math.degToRad ); postRotation.push( eulerOrder ); postRotation = new Euler().fromArray( postRotation ); @@ -3999,7 +4000,7 @@ function generateTransform( transformData ) { if ( transformData.preRotation ) { - var array = transformData.preRotation.map( Math.degToRad ); + var array = transformData.preRotation.map( _Math.degToRad ); array.push( transformData.eulerOrder ); lPreRotationM.makeRotationFromEuler( tempEuler.fromArray( array ) ); @@ -4007,7 +4008,7 @@ function generateTransform( transformData ) { if ( transformData.rotation ) { - var array = transformData.rotation.map( Math.degToRad ); + var array = transformData.rotation.map( _Math.degToRad ); array.push( transformData.eulerOrder ); lRotationM.makeRotationFromEuler( tempEuler.fromArray( array ) ); @@ -4015,7 +4016,7 @@ function generateTransform( transformData ) { if ( transformData.postRotation ) { - var array = transformData.postRotation.map( Math.degToRad ); + var array = transformData.postRotation.map( _Math.degToRad ); array.push( transformData.eulerOrder ); lPostRotationM.makeRotationFromEuler( tempEuler.fromArray( array ) ); diff --git a/examples/jsm/loaders/GLTFLoader.js b/examples/jsm/loaders/GLTFLoader.js index bf112370df7726..557539c1179d04 100644 --- a/examples/jsm/loaders/GLTFLoader.js +++ b/examples/jsm/loaders/GLTFLoader.js @@ -46,7 +46,7 @@ import { Loader, LoaderUtils, Material, - Math, + Math as _Math, Matrix3, Matrix4, Mesh, @@ -3003,7 +3003,7 @@ var GLTFLoader = ( function () { if ( cameraDef.type === 'perspective' ) { - camera = new PerspectiveCamera( Math.radToDeg( params.yfov ), params.aspectRatio || 1, params.znear || 1, params.zfar || 2e6 ); + camera = new PerspectiveCamera( _Math.radToDeg( params.yfov ), params.aspectRatio || 1, params.znear || 1, params.zfar || 2e6 ); } else if ( cameraDef.type === 'orthographic' ) { diff --git a/examples/jsm/loaders/LDrawLoader.js b/examples/jsm/loaders/LDrawLoader.js index fa7310c0053a00..b885585c218df4 100644 --- a/examples/jsm/loaders/LDrawLoader.js +++ b/examples/jsm/loaders/LDrawLoader.js @@ -1066,7 +1066,7 @@ LDrawLoader.prototype = { // Parse all line commands for ( lineIndex = 0; lineIndex < numLines; lineIndex ++ ) { - line = lines[ lineIndex ]; + var line = lines[ lineIndex ]; if ( line.length === 0 ) continue; diff --git a/examples/jsm/loaders/MMDLoader.js b/examples/jsm/loaders/MMDLoader.js index 8e8cbd453ae6eb..c8e70db21eea0b 100644 --- a/examples/jsm/loaders/MMDLoader.js +++ b/examples/jsm/loaders/MMDLoader.js @@ -29,7 +29,6 @@ * - shadow support. */ -import { Parser as MMDParser } from 'mmd-parser'; import { AddOperation, AnimationClip, @@ -62,7 +61,7 @@ import { Vector3, VectorKeyframeTrack } from "../../../build/three.module.js"; - +import { Parser as MMDParser } from '../libs/mmdparser.js'; import { TGALoader } from "./TGALoader"; import { MeshToonMaterial } from "../../../build/three"; @@ -361,12 +360,6 @@ MMDLoader.prototype = { if ( this.parser === null ) { - if ( typeof MMDParser === 'undefined' ) { - - throw new Error( 'THREE.MMDLoader: Import MMDParser https://github.com/takahirox/mmd-parser' ); - - } - this.parser = new MMDParser(); } diff --git a/examples/jsm/loaders/MTLLoader.js b/examples/jsm/loaders/MTLLoader.js index 8edeaaef77a335..d6ff6e728bdac7 100644 --- a/examples/jsm/loaders/MTLLoader.js +++ b/examples/jsm/loaders/MTLLoader.js @@ -9,6 +9,7 @@ import { DefaultLoadingManager, FileLoader, FrontSide, + Loader, LoaderUtils, MeshPhongMaterial, RepeatWrapping, diff --git a/examples/jsm/loaders/OBJLoader.js b/examples/jsm/loaders/OBJLoader.js index e6c9b51661ba29..b64ad9a43c332d 100644 --- a/examples/jsm/loaders/OBJLoader.js +++ b/examples/jsm/loaders/OBJLoader.js @@ -702,7 +702,7 @@ var OBJLoader = ( function () { material = this.materials.create( sourceMaterial.name ); // mtl etc. loaders probably can't create line materials correctly, copy properties to a line material. - if ( isLine && material && ! ( material instanceof LineBasicMaterial ) ) { + if ( isLine && material && ! ( material.isLineBasicMaterial ) ) { var materialLine = new LineBasicMaterial(); Material.prototype.copy.call( materialLine, material ); @@ -710,7 +710,7 @@ var OBJLoader = ( function () { materialLine.lights = false; material = materialLine; - } else if ( isPoints && material && ! ( material instanceof PointsMaterial ) ) { + } else if ( isPoints && material && ! ( material.isPointsMaterial ) ) { var materialPoints = new PointsMaterial( { size: 10, sizeAttenuation: false } ); Material.prototype.copy.call( materialPoints, material ); diff --git a/examples/jsm/loaders/XLoader.js b/examples/jsm/loaders/XLoader.js index 44451655de4585..e2693637bdc69a 100644 --- a/examples/jsm/loaders/XLoader.js +++ b/examples/jsm/loaders/XLoader.js @@ -9,6 +9,7 @@ import { Bone, BufferGeometry, DefaultLoadingManager, + FileLoader, Float32BufferAttribute, FrontSide, LoaderUtils, diff --git a/examples/jsm/loaders/deprecated/LegacyGLTFLoader.js b/examples/jsm/loaders/deprecated/LegacyGLTFLoader.js index c1fc0dd12af630..b28c53278a5e99 100644 --- a/examples/jsm/loaders/deprecated/LegacyGLTFLoader.js +++ b/examples/jsm/loaders/deprecated/LegacyGLTFLoader.js @@ -43,7 +43,7 @@ import { LoaderUtils, LuminanceAlphaFormat, LuminanceFormat, - Math, + Math as _Math, Matrix3, Matrix4, Mesh, @@ -896,7 +896,7 @@ DeferredShaderMaterial.prototype.create = function () { var originalUniform = this.params.uniforms[ uniformId ]; - if ( originalUniform.value instanceof Texture ) { + if ( originalUniform.value.isTexture ) { uniforms[ uniformId ].value = originalUniform.value; uniforms[ uniformId ].value.needsUpdate = true; @@ -1870,7 +1870,7 @@ GLTFParser.prototype.loadCameras = function () { // aspectRatio = xfov / yfov var xfov = yfov * aspectRatio; - var _camera = new PerspectiveCamera( Math.radToDeg( xfov ), aspectRatio, camera.perspective.znear || 1, camera.perspective.zfar || 2e6 ); + var _camera = new PerspectiveCamera( _Math.radToDeg( xfov ), aspectRatio, camera.perspective.znear || 1, camera.perspective.zfar || 2e6 ); if ( camera.name !== undefined ) _camera.name = camera.name; if ( camera.extras ) _camera.userData = camera.extras; diff --git a/package-lock.json b/package-lock.json index 7b07cc8a593a06..2659c0d3387de3 100644 --- a/package-lock.json +++ b/package-lock.json @@ -2496,11 +2496,6 @@ } } }, - "mmd-parser": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/mmd-parser/-/mmd-parser-1.0.3.tgz", - "integrity": "sha1-fk5HXQdnKeWSw+a76SmNe4Qe0+0=" - }, "ms": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", diff --git a/package.json b/package.json index 1ec2317cc0277e..f8d18fd2fcfb8c 100644 --- a/package.json +++ b/package.json @@ -74,8 +74,5 @@ "build/three.module.js" ], "directories": {} - }, - "dependencies": { - "mmd-parser": "^1.0.3" } } From ca394bdaf59089e8d829fcd9b85be0c251c2b54c Mon Sep 17 00:00:00 2001 From: Deep Date: Mon, 28 Jan 2019 20:56:44 +0530 Subject: [PATCH 3/3] Fix mmd and obj2meshspray loader examples --- examples/jsm/libs/MMDParser.js | 11524 +++++++++++++++++++ examples/jsm/libs/mmdparser.js | 11533 -------------------- examples/jsm/loaders/MMDLoader.js | 6 +- examples/jsm/loaders/TestParser.js | 5 + examples/webgl_loader_mmd.html | 2 +- examples/webgl_loader_mmd_audio.html | 2 +- examples/webgl_loader_mmd_pose.html | 2 +- examples/webgl_loader_obj2_meshspray.html | 1 + 8 files changed, 11536 insertions(+), 11539 deletions(-) create mode 100644 examples/jsm/libs/MMDParser.js delete mode 100644 examples/jsm/libs/mmdparser.js create mode 100644 examples/jsm/loaders/TestParser.js diff --git a/examples/jsm/libs/MMDParser.js b/examples/jsm/libs/MMDParser.js new file mode 100644 index 00000000000000..8226936b71066f --- /dev/null +++ b/examples/jsm/libs/MMDParser.js @@ -0,0 +1,11524 @@ +/** + * @author Takahiro / https://github.com/takahirox + * + * Simple CharsetEncoder. + */ + +function CharsetEncoder() { +} + +/* + * Converts from Shift_JIS Uint8Array data to Unicode strings. + */ +CharsetEncoder.prototype.s2u = function(uint8Array) { + var t = this.s2uTable; + var str = ''; + var p = 0; + + while(p < uint8Array.length) { + var key = uint8Array[p++]; + + if(! ((key >= 0x00 && key <= 0x7e) || + (key >= 0xa1 && key <= 0xdf)) && + p < uint8Array.length) { + key = (key << 8) | uint8Array[p++]; + } + + if(t[key] === undefined) { + throw 'unknown char code ' + key + '.'; + } + + str += String.fromCharCode(t[key]); + } + + return str; +}; + +CharsetEncoder.prototype.s2uTable = { + 0:0, + 1:1, + 2:2, + 3:3, + 4:4, + 5:5, + 6:6, + 7:7, + 8:8, + 9:9, + 10:10, + 11:11, + 12:12, + 13:13, + 14:14, + 15:15, + 16:16, + 17:17, + 18:18, + 19:19, + 20:20, + 21:21, + 22:22, + 23:23, + 24:24, + 25:25, + 26:26, + 27:27, + 28:28, + 29:29, + 30:30, + 31:31, + 32:32, + 33:33, + 34:34, + 35:35, + 36:36, + 37:37, + 38:38, + 39:39, + 40:40, + 41:41, + 42:42, + 43:43, + 44:44, + 45:45, + 46:46, + 47:47, + 48:48, + 49:49, + 50:50, + 51:51, + 52:52, + 53:53, + 54:54, + 55:55, + 56:56, + 57:57, + 58:58, + 59:59, + 60:60, + 61:61, + 62:62, + 63:63, + 64:64, + 65:65, + 66:66, + 67:67, + 68:68, + 69:69, + 70:70, + 71:71, + 72:72, + 73:73, + 74:74, + 75:75, + 76:76, + 77:77, + 78:78, + 79:79, + 80:80, + 81:81, + 82:82, + 83:83, + 84:84, + 85:85, + 86:86, + 87:87, + 88:88, + 89:89, + 90:90, + 91:91, + 92:92, + 93:93, + 94:94, + 95:95, + 96:96, + 97:97, + 98:98, + 99:99, + 100:100, + 101:101, + 102:102, + 103:103, + 104:104, + 105:105, + 106:106, + 107:107, + 108:108, + 109:109, + 110:110, + 111:111, + 112:112, + 113:113, + 114:114, + 115:115, + 116:116, + 117:117, + 118:118, + 119:119, + 120:120, + 121:121, + 122:122, + 123:123, + 124:124, + 125:125, + 126:126, + 161:65377, + 162:65378, + 163:65379, + 164:65380, + 165:65381, + 166:65382, + 167:65383, + 168:65384, + 169:65385, + 170:65386, + 171:65387, + 172:65388, + 173:65389, + 174:65390, + 175:65391, + 176:65392, + 177:65393, + 178:65394, + 179:65395, + 180:65396, + 181:65397, + 182:65398, + 183:65399, + 184:65400, + 185:65401, + 186:65402, + 187:65403, + 188:65404, + 189:65405, + 190:65406, + 191:65407, + 192:65408, + 193:65409, + 194:65410, + 195:65411, + 196:65412, + 197:65413, + 198:65414, + 199:65415, + 200:65416, + 201:65417, + 202:65418, + 203:65419, + 204:65420, + 205:65421, + 206:65422, + 207:65423, + 208:65424, + 209:65425, + 210:65426, + 211:65427, + 212:65428, + 213:65429, + 214:65430, + 215:65431, + 216:65432, + 217:65433, + 218:65434, + 219:65435, + 220:65436, + 221:65437, + 222:65438, + 223:65439, + 33088:12288, + 33089:12289, + 33090:12290, + 33091:65292, + 33092:65294, + 33093:12539, + 33094:65306, + 33095:65307, + 33096:65311, + 33097:65281, + 33098:12443, + 33099:12444, + 33100:180, + 33101:65344, + 33102:168, + 33103:65342, + 33104:65507, + 33105:65343, + 33106:12541, + 33107:12542, + 33108:12445, + 33109:12446, + 33110:12291, + 33111:20189, + 33112:12293, + 33113:12294, + 33114:12295, + 33115:12540, + 33116:8213, + 33117:8208, + 33118:65295, + 33119:65340, + 33120:65374, + 33121:8741, + 33122:65372, + 33123:8230, + 33124:8229, + 33125:8216, + 33126:8217, + 33127:8220, + 33128:8221, + 33129:65288, + 33130:65289, + 33131:12308, + 33132:12309, + 33133:65339, + 33134:65341, + 33135:65371, + 33136:65373, + 33137:12296, + 33138:12297, + 33139:12298, + 33140:12299, + 33141:12300, + 33142:12301, + 33143:12302, + 33144:12303, + 33145:12304, + 33146:12305, + 33147:65291, + 33148:65293, + 33149:177, + 33150:215, + 33152:247, + 33153:65309, + 33154:8800, + 33155:65308, + 33156:65310, + 33157:8806, + 33158:8807, + 33159:8734, + 33160:8756, + 33161:9794, + 33162:9792, + 33163:176, + 33164:8242, + 33165:8243, + 33166:8451, + 33167:65509, + 33168:65284, + 33169:65504, + 33170:65505, + 33171:65285, + 33172:65283, + 33173:65286, + 33174:65290, + 33175:65312, + 33176:167, + 33177:9734, + 33178:9733, + 33179:9675, + 33180:9679, + 33181:9678, + 33182:9671, + 33183:9670, + 33184:9633, + 33185:9632, + 33186:9651, + 33187:9650, + 33188:9661, + 33189:9660, + 33190:8251, + 33191:12306, + 33192:8594, + 33193:8592, + 33194:8593, + 33195:8595, + 33196:12307, + 33208:8712, + 33209:8715, + 33210:8838, + 33211:8839, + 33212:8834, + 33213:8835, + 33214:8746, + 33215:8745, + 33224:8743, + 33225:8744, + 33226:65506, + 33227:8658, + 33228:8660, + 33229:8704, + 33230:8707, + 33242:8736, + 33243:8869, + 33244:8978, + 33245:8706, + 33246:8711, + 33247:8801, + 33248:8786, + 33249:8810, + 33250:8811, + 33251:8730, + 33252:8765, + 33253:8733, + 33254:8757, + 33255:8747, + 33256:8748, + 33264:8491, + 33265:8240, + 33266:9839, + 33267:9837, + 33268:9834, + 33269:8224, + 33270:8225, + 33271:182, + 33276:9711, + 33359:65296, + 33360:65297, + 33361:65298, + 33362:65299, + 33363:65300, + 33364:65301, + 33365:65302, + 33366:65303, + 33367:65304, + 33368:65305, + 33376:65313, + 33377:65314, + 33378:65315, + 33379:65316, + 33380:65317, + 33381:65318, + 33382:65319, + 33383:65320, + 33384:65321, + 33385:65322, + 33386:65323, + 33387:65324, + 33388:65325, + 33389:65326, + 33390:65327, + 33391:65328, + 33392:65329, + 33393:65330, + 33394:65331, + 33395:65332, + 33396:65333, + 33397:65334, + 33398:65335, + 33399:65336, + 33400:65337, + 33401:65338, + 33409:65345, + 33410:65346, + 33411:65347, + 33412:65348, + 33413:65349, + 33414:65350, + 33415:65351, + 33416:65352, + 33417:65353, + 33418:65354, + 33419:65355, + 33420:65356, + 33421:65357, + 33422:65358, + 33423:65359, + 33424:65360, + 33425:65361, + 33426:65362, + 33427:65363, + 33428:65364, + 33429:65365, + 33430:65366, + 33431:65367, + 33432:65368, + 33433:65369, + 33434:65370, + 33439:12353, + 33440:12354, + 33441:12355, + 33442:12356, + 33443:12357, + 33444:12358, + 33445:12359, + 33446:12360, + 33447:12361, + 33448:12362, + 33449:12363, + 33450:12364, + 33451:12365, + 33452:12366, + 33453:12367, + 33454:12368, + 33455:12369, + 33456:12370, + 33457:12371, + 33458:12372, + 33459:12373, + 33460:12374, + 33461:12375, + 33462:12376, + 33463:12377, + 33464:12378, + 33465:12379, + 33466:12380, + 33467:12381, + 33468:12382, + 33469:12383, + 33470:12384, + 33471:12385, + 33472:12386, + 33473:12387, + 33474:12388, + 33475:12389, + 33476:12390, + 33477:12391, + 33478:12392, + 33479:12393, + 33480:12394, + 33481:12395, + 33482:12396, + 33483:12397, + 33484:12398, + 33485:12399, + 33486:12400, + 33487:12401, + 33488:12402, + 33489:12403, + 33490:12404, + 33491:12405, + 33492:12406, + 33493:12407, + 33494:12408, + 33495:12409, + 33496:12410, + 33497:12411, + 33498:12412, + 33499:12413, + 33500:12414, + 33501:12415, + 33502:12416, + 33503:12417, + 33504:12418, + 33505:12419, + 33506:12420, + 33507:12421, + 33508:12422, + 33509:12423, + 33510:12424, + 33511:12425, + 33512:12426, + 33513:12427, + 33514:12428, + 33515:12429, + 33516:12430, + 33517:12431, + 33518:12432, + 33519:12433, + 33520:12434, + 33521:12435, + 33600:12449, + 33601:12450, + 33602:12451, + 33603:12452, + 33604:12453, + 33605:12454, + 33606:12455, + 33607:12456, + 33608:12457, + 33609:12458, + 33610:12459, + 33611:12460, + 33612:12461, + 33613:12462, + 33614:12463, + 33615:12464, + 33616:12465, + 33617:12466, + 33618:12467, + 33619:12468, + 33620:12469, + 33621:12470, + 33622:12471, + 33623:12472, + 33624:12473, + 33625:12474, + 33626:12475, + 33627:12476, + 33628:12477, + 33629:12478, + 33630:12479, + 33631:12480, + 33632:12481, + 33633:12482, + 33634:12483, + 33635:12484, + 33636:12485, + 33637:12486, + 33638:12487, + 33639:12488, + 33640:12489, + 33641:12490, + 33642:12491, + 33643:12492, + 33644:12493, + 33645:12494, + 33646:12495, + 33647:12496, + 33648:12497, + 33649:12498, + 33650:12499, + 33651:12500, + 33652:12501, + 33653:12502, + 33654:12503, + 33655:12504, + 33656:12505, + 33657:12506, + 33658:12507, + 33659:12508, + 33660:12509, + 33661:12510, + 33662:12511, + 33664:12512, + 33665:12513, + 33666:12514, + 33667:12515, + 33668:12516, + 33669:12517, + 33670:12518, + 33671:12519, + 33672:12520, + 33673:12521, + 33674:12522, + 33675:12523, + 33676:12524, + 33677:12525, + 33678:12526, + 33679:12527, + 33680:12528, + 33681:12529, + 33682:12530, + 33683:12531, + 33684:12532, + 33685:12533, + 33686:12534, + 33695:913, + 33696:914, + 33697:915, + 33698:916, + 33699:917, + 33700:918, + 33701:919, + 33702:920, + 33703:921, + 33704:922, + 33705:923, + 33706:924, + 33707:925, + 33708:926, + 33709:927, + 33710:928, + 33711:929, + 33712:931, + 33713:932, + 33714:933, + 33715:934, + 33716:935, + 33717:936, + 33718:937, + 33727:945, + 33728:946, + 33729:947, + 33730:948, + 33731:949, + 33732:950, + 33733:951, + 33734:952, + 33735:953, + 33736:954, + 33737:955, + 33738:956, + 33739:957, + 33740:958, + 33741:959, + 33742:960, + 33743:961, + 33744:963, + 33745:964, + 33746:965, + 33747:966, + 33748:967, + 33749:968, + 33750:969, + 33856:1040, + 33857:1041, + 33858:1042, + 33859:1043, + 33860:1044, + 33861:1045, + 33862:1025, + 33863:1046, + 33864:1047, + 33865:1048, + 33866:1049, + 33867:1050, + 33868:1051, + 33869:1052, + 33870:1053, + 33871:1054, + 33872:1055, + 33873:1056, + 33874:1057, + 33875:1058, + 33876:1059, + 33877:1060, + 33878:1061, + 33879:1062, + 33880:1063, + 33881:1064, + 33882:1065, + 33883:1066, + 33884:1067, + 33885:1068, + 33886:1069, + 33887:1070, + 33888:1071, + 33904:1072, + 33905:1073, + 33906:1074, + 33907:1075, + 33908:1076, + 33909:1077, + 33910:1105, + 33911:1078, + 33912:1079, + 33913:1080, + 33914:1081, + 33915:1082, + 33916:1083, + 33917:1084, + 33918:1085, + 33920:1086, + 33921:1087, + 33922:1088, + 33923:1089, + 33924:1090, + 33925:1091, + 33926:1092, + 33927:1093, + 33928:1094, + 33929:1095, + 33930:1096, + 33931:1097, + 33932:1098, + 33933:1099, + 33934:1100, + 33935:1101, + 33936:1102, + 33937:1103, + 33951:9472, + 33952:9474, + 33953:9484, + 33954:9488, + 33955:9496, + 33956:9492, + 33957:9500, + 33958:9516, + 33959:9508, + 33960:9524, + 33961:9532, + 33962:9473, + 33963:9475, + 33964:9487, + 33965:9491, + 33966:9499, + 33967:9495, + 33968:9507, + 33969:9523, + 33970:9515, + 33971:9531, + 33972:9547, + 33973:9504, + 33974:9519, + 33975:9512, + 33976:9527, + 33977:9535, + 33978:9501, + 33979:9520, + 33980:9509, + 33981:9528, + 33982:9538, + 34624:9312, + 34625:9313, + 34626:9314, + 34627:9315, + 34628:9316, + 34629:9317, + 34630:9318, + 34631:9319, + 34632:9320, + 34633:9321, + 34634:9322, + 34635:9323, + 34636:9324, + 34637:9325, + 34638:9326, + 34639:9327, + 34640:9328, + 34641:9329, + 34642:9330, + 34643:9331, + 34644:8544, + 34645:8545, + 34646:8546, + 34647:8547, + 34648:8548, + 34649:8549, + 34650:8550, + 34651:8551, + 34652:8552, + 34653:8553, + 34655:13129, + 34656:13076, + 34657:13090, + 34658:13133, + 34659:13080, + 34660:13095, + 34661:13059, + 34662:13110, + 34663:13137, + 34664:13143, + 34665:13069, + 34666:13094, + 34667:13091, + 34668:13099, + 34669:13130, + 34670:13115, + 34671:13212, + 34672:13213, + 34673:13214, + 34674:13198, + 34675:13199, + 34676:13252, + 34677:13217, + 34686:13179, + 34688:12317, + 34689:12319, + 34690:8470, + 34691:13261, + 34692:8481, + 34693:12964, + 34694:12965, + 34695:12966, + 34696:12967, + 34697:12968, + 34698:12849, + 34699:12850, + 34700:12857, + 34701:13182, + 34702:13181, + 34703:13180, + 34704:8786, + 34705:8801, + 34706:8747, + 34707:8750, + 34708:8721, + 34709:8730, + 34710:8869, + 34711:8736, + 34712:8735, + 34713:8895, + 34714:8757, + 34715:8745, + 34716:8746, + 34975:20124, + 34976:21782, + 34977:23043, + 34978:38463, + 34979:21696, + 34980:24859, + 34981:25384, + 34982:23030, + 34983:36898, + 34984:33909, + 34985:33564, + 34986:31312, + 34987:24746, + 34988:25569, + 34989:28197, + 34990:26093, + 34991:33894, + 34992:33446, + 34993:39925, + 34994:26771, + 34995:22311, + 34996:26017, + 34997:25201, + 34998:23451, + 34999:22992, + 35000:34427, + 35001:39156, + 35002:32098, + 35003:32190, + 35004:39822, + 35005:25110, + 35006:31903, + 35007:34999, + 35008:23433, + 35009:24245, + 35010:25353, + 35011:26263, + 35012:26696, + 35013:38343, + 35014:38797, + 35015:26447, + 35016:20197, + 35017:20234, + 35018:20301, + 35019:20381, + 35020:20553, + 35021:22258, + 35022:22839, + 35023:22996, + 35024:23041, + 35025:23561, + 35026:24799, + 35027:24847, + 35028:24944, + 35029:26131, + 35030:26885, + 35031:28858, + 35032:30031, + 35033:30064, + 35034:31227, + 35035:32173, + 35036:32239, + 35037:32963, + 35038:33806, + 35039:34915, + 35040:35586, + 35041:36949, + 35042:36986, + 35043:21307, + 35044:20117, + 35045:20133, + 35046:22495, + 35047:32946, + 35048:37057, + 35049:30959, + 35050:19968, + 35051:22769, + 35052:28322, + 35053:36920, + 35054:31282, + 35055:33576, + 35056:33419, + 35057:39983, + 35058:20801, + 35059:21360, + 35060:21693, + 35061:21729, + 35062:22240, + 35063:23035, + 35064:24341, + 35065:39154, + 35066:28139, + 35067:32996, + 35068:34093, + 35136:38498, + 35137:38512, + 35138:38560, + 35139:38907, + 35140:21515, + 35141:21491, + 35142:23431, + 35143:28879, + 35144:32701, + 35145:36802, + 35146:38632, + 35147:21359, + 35148:40284, + 35149:31418, + 35150:19985, + 35151:30867, + 35152:33276, + 35153:28198, + 35154:22040, + 35155:21764, + 35156:27421, + 35157:34074, + 35158:39995, + 35159:23013, + 35160:21417, + 35161:28006, + 35162:29916, + 35163:38287, + 35164:22082, + 35165:20113, + 35166:36939, + 35167:38642, + 35168:33615, + 35169:39180, + 35170:21473, + 35171:21942, + 35172:23344, + 35173:24433, + 35174:26144, + 35175:26355, + 35176:26628, + 35177:27704, + 35178:27891, + 35179:27945, + 35180:29787, + 35181:30408, + 35182:31310, + 35183:38964, + 35184:33521, + 35185:34907, + 35186:35424, + 35187:37613, + 35188:28082, + 35189:30123, + 35190:30410, + 35191:39365, + 35192:24742, + 35193:35585, + 35194:36234, + 35195:38322, + 35196:27022, + 35197:21421, + 35198:20870, + 35200:22290, + 35201:22576, + 35202:22852, + 35203:23476, + 35204:24310, + 35205:24616, + 35206:25513, + 35207:25588, + 35208:27839, + 35209:28436, + 35210:28814, + 35211:28948, + 35212:29017, + 35213:29141, + 35214:29503, + 35215:32257, + 35216:33398, + 35217:33489, + 35218:34199, + 35219:36960, + 35220:37467, + 35221:40219, + 35222:22633, + 35223:26044, + 35224:27738, + 35225:29989, + 35226:20985, + 35227:22830, + 35228:22885, + 35229:24448, + 35230:24540, + 35231:25276, + 35232:26106, + 35233:27178, + 35234:27431, + 35235:27572, + 35236:29579, + 35237:32705, + 35238:35158, + 35239:40236, + 35240:40206, + 35241:40644, + 35242:23713, + 35243:27798, + 35244:33659, + 35245:20740, + 35246:23627, + 35247:25014, + 35248:33222, + 35249:26742, + 35250:29281, + 35251:20057, + 35252:20474, + 35253:21368, + 35254:24681, + 35255:28201, + 35256:31311, + 35257:38899, + 35258:19979, + 35259:21270, + 35260:20206, + 35261:20309, + 35262:20285, + 35263:20385, + 35264:20339, + 35265:21152, + 35266:21487, + 35267:22025, + 35268:22799, + 35269:23233, + 35270:23478, + 35271:23521, + 35272:31185, + 35273:26247, + 35274:26524, + 35275:26550, + 35276:27468, + 35277:27827, + 35278:28779, + 35279:29634, + 35280:31117, + 35281:31166, + 35282:31292, + 35283:31623, + 35284:33457, + 35285:33499, + 35286:33540, + 35287:33655, + 35288:33775, + 35289:33747, + 35290:34662, + 35291:35506, + 35292:22057, + 35293:36008, + 35294:36838, + 35295:36942, + 35296:38686, + 35297:34442, + 35298:20420, + 35299:23784, + 35300:25105, + 35301:29273, + 35302:30011, + 35303:33253, + 35304:33469, + 35305:34558, + 35306:36032, + 35307:38597, + 35308:39187, + 35309:39381, + 35310:20171, + 35311:20250, + 35312:35299, + 35313:22238, + 35314:22602, + 35315:22730, + 35316:24315, + 35317:24555, + 35318:24618, + 35319:24724, + 35320:24674, + 35321:25040, + 35322:25106, + 35323:25296, + 35324:25913, + 35392:39745, + 35393:26214, + 35394:26800, + 35395:28023, + 35396:28784, + 35397:30028, + 35398:30342, + 35399:32117, + 35400:33445, + 35401:34809, + 35402:38283, + 35403:38542, + 35404:35997, + 35405:20977, + 35406:21182, + 35407:22806, + 35408:21683, + 35409:23475, + 35410:23830, + 35411:24936, + 35412:27010, + 35413:28079, + 35414:30861, + 35415:33995, + 35416:34903, + 35417:35442, + 35418:37799, + 35419:39608, + 35420:28012, + 35421:39336, + 35422:34521, + 35423:22435, + 35424:26623, + 35425:34510, + 35426:37390, + 35427:21123, + 35428:22151, + 35429:21508, + 35430:24275, + 35431:25313, + 35432:25785, + 35433:26684, + 35434:26680, + 35435:27579, + 35436:29554, + 35437:30906, + 35438:31339, + 35439:35226, + 35440:35282, + 35441:36203, + 35442:36611, + 35443:37101, + 35444:38307, + 35445:38548, + 35446:38761, + 35447:23398, + 35448:23731, + 35449:27005, + 35450:38989, + 35451:38990, + 35452:25499, + 35453:31520, + 35454:27179, + 35456:27263, + 35457:26806, + 35458:39949, + 35459:28511, + 35460:21106, + 35461:21917, + 35462:24688, + 35463:25324, + 35464:27963, + 35465:28167, + 35466:28369, + 35467:33883, + 35468:35088, + 35469:36676, + 35470:19988, + 35471:39993, + 35472:21494, + 35473:26907, + 35474:27194, + 35475:38788, + 35476:26666, + 35477:20828, + 35478:31427, + 35479:33970, + 35480:37340, + 35481:37772, + 35482:22107, + 35483:40232, + 35484:26658, + 35485:33541, + 35486:33841, + 35487:31909, + 35488:21000, + 35489:33477, + 35490:29926, + 35491:20094, + 35492:20355, + 35493:20896, + 35494:23506, + 35495:21002, + 35496:21208, + 35497:21223, + 35498:24059, + 35499:21914, + 35500:22570, + 35501:23014, + 35502:23436, + 35503:23448, + 35504:23515, + 35505:24178, + 35506:24185, + 35507:24739, + 35508:24863, + 35509:24931, + 35510:25022, + 35511:25563, + 35512:25954, + 35513:26577, + 35514:26707, + 35515:26874, + 35516:27454, + 35517:27475, + 35518:27735, + 35519:28450, + 35520:28567, + 35521:28485, + 35522:29872, + 35523:29976, + 35524:30435, + 35525:30475, + 35526:31487, + 35527:31649, + 35528:31777, + 35529:32233, + 35530:32566, + 35531:32752, + 35532:32925, + 35533:33382, + 35534:33694, + 35535:35251, + 35536:35532, + 35537:36011, + 35538:36996, + 35539:37969, + 35540:38291, + 35541:38289, + 35542:38306, + 35543:38501, + 35544:38867, + 35545:39208, + 35546:33304, + 35547:20024, + 35548:21547, + 35549:23736, + 35550:24012, + 35551:29609, + 35552:30284, + 35553:30524, + 35554:23721, + 35555:32747, + 35556:36107, + 35557:38593, + 35558:38929, + 35559:38996, + 35560:39000, + 35561:20225, + 35562:20238, + 35563:21361, + 35564:21916, + 35565:22120, + 35566:22522, + 35567:22855, + 35568:23305, + 35569:23492, + 35570:23696, + 35571:24076, + 35572:24190, + 35573:24524, + 35574:25582, + 35575:26426, + 35576:26071, + 35577:26082, + 35578:26399, + 35579:26827, + 35580:26820, + 35648:27231, + 35649:24112, + 35650:27589, + 35651:27671, + 35652:27773, + 35653:30079, + 35654:31048, + 35655:23395, + 35656:31232, + 35657:32000, + 35658:24509, + 35659:35215, + 35660:35352, + 35661:36020, + 35662:36215, + 35663:36556, + 35664:36637, + 35665:39138, + 35666:39438, + 35667:39740, + 35668:20096, + 35669:20605, + 35670:20736, + 35671:22931, + 35672:23452, + 35673:25135, + 35674:25216, + 35675:25836, + 35676:27450, + 35677:29344, + 35678:30097, + 35679:31047, + 35680:32681, + 35681:34811, + 35682:35516, + 35683:35696, + 35684:25516, + 35685:33738, + 35686:38816, + 35687:21513, + 35688:21507, + 35689:21931, + 35690:26708, + 35691:27224, + 35692:35440, + 35693:30759, + 35694:26485, + 35695:40653, + 35696:21364, + 35697:23458, + 35698:33050, + 35699:34384, + 35700:36870, + 35701:19992, + 35702:20037, + 35703:20167, + 35704:20241, + 35705:21450, + 35706:21560, + 35707:23470, + 35708:24339, + 35709:24613, + 35710:25937, + 35712:26429, + 35713:27714, + 35714:27762, + 35715:27875, + 35716:28792, + 35717:29699, + 35718:31350, + 35719:31406, + 35720:31496, + 35721:32026, + 35722:31998, + 35723:32102, + 35724:26087, + 35725:29275, + 35726:21435, + 35727:23621, + 35728:24040, + 35729:25298, + 35730:25312, + 35731:25369, + 35732:28192, + 35733:34394, + 35734:35377, + 35735:36317, + 35736:37624, + 35737:28417, + 35738:31142, + 35739:39770, + 35740:20136, + 35741:20139, + 35742:20140, + 35743:20379, + 35744:20384, + 35745:20689, + 35746:20807, + 35747:31478, + 35748:20849, + 35749:20982, + 35750:21332, + 35751:21281, + 35752:21375, + 35753:21483, + 35754:21932, + 35755:22659, + 35756:23777, + 35757:24375, + 35758:24394, + 35759:24623, + 35760:24656, + 35761:24685, + 35762:25375, + 35763:25945, + 35764:27211, + 35765:27841, + 35766:29378, + 35767:29421, + 35768:30703, + 35769:33016, + 35770:33029, + 35771:33288, + 35772:34126, + 35773:37111, + 35774:37857, + 35775:38911, + 35776:39255, + 35777:39514, + 35778:20208, + 35779:20957, + 35780:23597, + 35781:26241, + 35782:26989, + 35783:23616, + 35784:26354, + 35785:26997, + 35786:29577, + 35787:26704, + 35788:31873, + 35789:20677, + 35790:21220, + 35791:22343, + 35792:24062, + 35793:37670, + 35794:26020, + 35795:27427, + 35796:27453, + 35797:29748, + 35798:31105, + 35799:31165, + 35800:31563, + 35801:32202, + 35802:33465, + 35803:33740, + 35804:34943, + 35805:35167, + 35806:35641, + 35807:36817, + 35808:37329, + 35809:21535, + 35810:37504, + 35811:20061, + 35812:20534, + 35813:21477, + 35814:21306, + 35815:29399, + 35816:29590, + 35817:30697, + 35818:33510, + 35819:36527, + 35820:39366, + 35821:39368, + 35822:39378, + 35823:20855, + 35824:24858, + 35825:34398, + 35826:21936, + 35827:31354, + 35828:20598, + 35829:23507, + 35830:36935, + 35831:38533, + 35832:20018, + 35833:27355, + 35834:37351, + 35835:23633, + 35836:23624, + 35904:25496, + 35905:31391, + 35906:27795, + 35907:38772, + 35908:36705, + 35909:31402, + 35910:29066, + 35911:38536, + 35912:31874, + 35913:26647, + 35914:32368, + 35915:26705, + 35916:37740, + 35917:21234, + 35918:21531, + 35919:34219, + 35920:35347, + 35921:32676, + 35922:36557, + 35923:37089, + 35924:21350, + 35925:34952, + 35926:31041, + 35927:20418, + 35928:20670, + 35929:21009, + 35930:20804, + 35931:21843, + 35932:22317, + 35933:29674, + 35934:22411, + 35935:22865, + 35936:24418, + 35937:24452, + 35938:24693, + 35939:24950, + 35940:24935, + 35941:25001, + 35942:25522, + 35943:25658, + 35944:25964, + 35945:26223, + 35946:26690, + 35947:28179, + 35948:30054, + 35949:31293, + 35950:31995, + 35951:32076, + 35952:32153, + 35953:32331, + 35954:32619, + 35955:33550, + 35956:33610, + 35957:34509, + 35958:35336, + 35959:35427, + 35960:35686, + 35961:36605, + 35962:38938, + 35963:40335, + 35964:33464, + 35965:36814, + 35966:39912, + 35968:21127, + 35969:25119, + 35970:25731, + 35971:28608, + 35972:38553, + 35973:26689, + 35974:20625, + 35975:27424, + 35976:27770, + 35977:28500, + 35978:31348, + 35979:32080, + 35980:34880, + 35981:35363, + 35982:26376, + 35983:20214, + 35984:20537, + 35985:20518, + 35986:20581, + 35987:20860, + 35988:21048, + 35989:21091, + 35990:21927, + 35991:22287, + 35992:22533, + 35993:23244, + 35994:24314, + 35995:25010, + 35996:25080, + 35997:25331, + 35998:25458, + 35999:26908, + 36000:27177, + 36001:29309, + 36002:29356, + 36003:29486, + 36004:30740, + 36005:30831, + 36006:32121, + 36007:30476, + 36008:32937, + 36009:35211, + 36010:35609, + 36011:36066, + 36012:36562, + 36013:36963, + 36014:37749, + 36015:38522, + 36016:38997, + 36017:39443, + 36018:40568, + 36019:20803, + 36020:21407, + 36021:21427, + 36022:24187, + 36023:24358, + 36024:28187, + 36025:28304, + 36026:29572, + 36027:29694, + 36028:32067, + 36029:33335, + 36030:35328, + 36031:35578, + 36032:38480, + 36033:20046, + 36034:20491, + 36035:21476, + 36036:21628, + 36037:22266, + 36038:22993, + 36039:23396, + 36040:24049, + 36041:24235, + 36042:24359, + 36043:25144, + 36044:25925, + 36045:26543, + 36046:28246, + 36047:29392, + 36048:31946, + 36049:34996, + 36050:32929, + 36051:32993, + 36052:33776, + 36053:34382, + 36054:35463, + 36055:36328, + 36056:37431, + 36057:38599, + 36058:39015, + 36059:40723, + 36060:20116, + 36061:20114, + 36062:20237, + 36063:21320, + 36064:21577, + 36065:21566, + 36066:23087, + 36067:24460, + 36068:24481, + 36069:24735, + 36070:26791, + 36071:27278, + 36072:29786, + 36073:30849, + 36074:35486, + 36075:35492, + 36076:35703, + 36077:37264, + 36078:20062, + 36079:39881, + 36080:20132, + 36081:20348, + 36082:20399, + 36083:20505, + 36084:20502, + 36085:20809, + 36086:20844, + 36087:21151, + 36088:21177, + 36089:21246, + 36090:21402, + 36091:21475, + 36092:21521, + 36160:21518, + 36161:21897, + 36162:22353, + 36163:22434, + 36164:22909, + 36165:23380, + 36166:23389, + 36167:23439, + 36168:24037, + 36169:24039, + 36170:24055, + 36171:24184, + 36172:24195, + 36173:24218, + 36174:24247, + 36175:24344, + 36176:24658, + 36177:24908, + 36178:25239, + 36179:25304, + 36180:25511, + 36181:25915, + 36182:26114, + 36183:26179, + 36184:26356, + 36185:26477, + 36186:26657, + 36187:26775, + 36188:27083, + 36189:27743, + 36190:27946, + 36191:28009, + 36192:28207, + 36193:28317, + 36194:30002, + 36195:30343, + 36196:30828, + 36197:31295, + 36198:31968, + 36199:32005, + 36200:32024, + 36201:32094, + 36202:32177, + 36203:32789, + 36204:32771, + 36205:32943, + 36206:32945, + 36207:33108, + 36208:33167, + 36209:33322, + 36210:33618, + 36211:34892, + 36212:34913, + 36213:35611, + 36214:36002, + 36215:36092, + 36216:37066, + 36217:37237, + 36218:37489, + 36219:30783, + 36220:37628, + 36221:38308, + 36222:38477, + 36224:38917, + 36225:39321, + 36226:39640, + 36227:40251, + 36228:21083, + 36229:21163, + 36230:21495, + 36231:21512, + 36232:22741, + 36233:25335, + 36234:28640, + 36235:35946, + 36236:36703, + 36237:40633, + 36238:20811, + 36239:21051, + 36240:21578, + 36241:22269, + 36242:31296, + 36243:37239, + 36244:40288, + 36245:40658, + 36246:29508, + 36247:28425, + 36248:33136, + 36249:29969, + 36250:24573, + 36251:24794, + 36252:39592, + 36253:29403, + 36254:36796, + 36255:27492, + 36256:38915, + 36257:20170, + 36258:22256, + 36259:22372, + 36260:22718, + 36261:23130, + 36262:24680, + 36263:25031, + 36264:26127, + 36265:26118, + 36266:26681, + 36267:26801, + 36268:28151, + 36269:30165, + 36270:32058, + 36271:33390, + 36272:39746, + 36273:20123, + 36274:20304, + 36275:21449, + 36276:21766, + 36277:23919, + 36278:24038, + 36279:24046, + 36280:26619, + 36281:27801, + 36282:29811, + 36283:30722, + 36284:35408, + 36285:37782, + 36286:35039, + 36287:22352, + 36288:24231, + 36289:25387, + 36290:20661, + 36291:20652, + 36292:20877, + 36293:26368, + 36294:21705, + 36295:22622, + 36296:22971, + 36297:23472, + 36298:24425, + 36299:25165, + 36300:25505, + 36301:26685, + 36302:27507, + 36303:28168, + 36304:28797, + 36305:37319, + 36306:29312, + 36307:30741, + 36308:30758, + 36309:31085, + 36310:25998, + 36311:32048, + 36312:33756, + 36313:35009, + 36314:36617, + 36315:38555, + 36316:21092, + 36317:22312, + 36318:26448, + 36319:32618, + 36320:36001, + 36321:20916, + 36322:22338, + 36323:38442, + 36324:22586, + 36325:27018, + 36326:32948, + 36327:21682, + 36328:23822, + 36329:22524, + 36330:30869, + 36331:40442, + 36332:20316, + 36333:21066, + 36334:21643, + 36335:25662, + 36336:26152, + 36337:26388, + 36338:26613, + 36339:31364, + 36340:31574, + 36341:32034, + 36342:37679, + 36343:26716, + 36344:39853, + 36345:31545, + 36346:21273, + 36347:20874, + 36348:21047, + 36416:23519, + 36417:25334, + 36418:25774, + 36419:25830, + 36420:26413, + 36421:27578, + 36422:34217, + 36423:38609, + 36424:30352, + 36425:39894, + 36426:25420, + 36427:37638, + 36428:39851, + 36429:30399, + 36430:26194, + 36431:19977, + 36432:20632, + 36433:21442, + 36434:23665, + 36435:24808, + 36436:25746, + 36437:25955, + 36438:26719, + 36439:29158, + 36440:29642, + 36441:29987, + 36442:31639, + 36443:32386, + 36444:34453, + 36445:35715, + 36446:36059, + 36447:37240, + 36448:39184, + 36449:26028, + 36450:26283, + 36451:27531, + 36452:20181, + 36453:20180, + 36454:20282, + 36455:20351, + 36456:21050, + 36457:21496, + 36458:21490, + 36459:21987, + 36460:22235, + 36461:22763, + 36462:22987, + 36463:22985, + 36464:23039, + 36465:23376, + 36466:23629, + 36467:24066, + 36468:24107, + 36469:24535, + 36470:24605, + 36471:25351, + 36472:25903, + 36473:23388, + 36474:26031, + 36475:26045, + 36476:26088, + 36477:26525, + 36478:27490, + 36480:27515, + 36481:27663, + 36482:29509, + 36483:31049, + 36484:31169, + 36485:31992, + 36486:32025, + 36487:32043, + 36488:32930, + 36489:33026, + 36490:33267, + 36491:35222, + 36492:35422, + 36493:35433, + 36494:35430, + 36495:35468, + 36496:35566, + 36497:36039, + 36498:36060, + 36499:38604, + 36500:39164, + 36501:27503, + 36502:20107, + 36503:20284, + 36504:20365, + 36505:20816, + 36506:23383, + 36507:23546, + 36508:24904, + 36509:25345, + 36510:26178, + 36511:27425, + 36512:28363, + 36513:27835, + 36514:29246, + 36515:29885, + 36516:30164, + 36517:30913, + 36518:31034, + 36519:32780, + 36520:32819, + 36521:33258, + 36522:33940, + 36523:36766, + 36524:27728, + 36525:40575, + 36526:24335, + 36527:35672, + 36528:40235, + 36529:31482, + 36530:36600, + 36531:23437, + 36532:38635, + 36533:19971, + 36534:21489, + 36535:22519, + 36536:22833, + 36537:23241, + 36538:23460, + 36539:24713, + 36540:28287, + 36541:28422, + 36542:30142, + 36543:36074, + 36544:23455, + 36545:34048, + 36546:31712, + 36547:20594, + 36548:26612, + 36549:33437, + 36550:23649, + 36551:34122, + 36552:32286, + 36553:33294, + 36554:20889, + 36555:23556, + 36556:25448, + 36557:36198, + 36558:26012, + 36559:29038, + 36560:31038, + 36561:32023, + 36562:32773, + 36563:35613, + 36564:36554, + 36565:36974, + 36566:34503, + 36567:37034, + 36568:20511, + 36569:21242, + 36570:23610, + 36571:26451, + 36572:28796, + 36573:29237, + 36574:37196, + 36575:37320, + 36576:37675, + 36577:33509, + 36578:23490, + 36579:24369, + 36580:24825, + 36581:20027, + 36582:21462, + 36583:23432, + 36584:25163, + 36585:26417, + 36586:27530, + 36587:29417, + 36588:29664, + 36589:31278, + 36590:33131, + 36591:36259, + 36592:37202, + 36593:39318, + 36594:20754, + 36595:21463, + 36596:21610, + 36597:23551, + 36598:25480, + 36599:27193, + 36600:32172, + 36601:38656, + 36602:22234, + 36603:21454, + 36604:21608, + 36672:23447, + 36673:23601, + 36674:24030, + 36675:20462, + 36676:24833, + 36677:25342, + 36678:27954, + 36679:31168, + 36680:31179, + 36681:32066, + 36682:32333, + 36683:32722, + 36684:33261, + 36685:33311, + 36686:33936, + 36687:34886, + 36688:35186, + 36689:35728, + 36690:36468, + 36691:36655, + 36692:36913, + 36693:37195, + 36694:37228, + 36695:38598, + 36696:37276, + 36697:20160, + 36698:20303, + 36699:20805, + 36700:21313, + 36701:24467, + 36702:25102, + 36703:26580, + 36704:27713, + 36705:28171, + 36706:29539, + 36707:32294, + 36708:37325, + 36709:37507, + 36710:21460, + 36711:22809, + 36712:23487, + 36713:28113, + 36714:31069, + 36715:32302, + 36716:31899, + 36717:22654, + 36718:29087, + 36719:20986, + 36720:34899, + 36721:36848, + 36722:20426, + 36723:23803, + 36724:26149, + 36725:30636, + 36726:31459, + 36727:33308, + 36728:39423, + 36729:20934, + 36730:24490, + 36731:26092, + 36732:26991, + 36733:27529, + 36734:28147, + 36736:28310, + 36737:28516, + 36738:30462, + 36739:32020, + 36740:24033, + 36741:36981, + 36742:37255, + 36743:38918, + 36744:20966, + 36745:21021, + 36746:25152, + 36747:26257, + 36748:26329, + 36749:28186, + 36750:24246, + 36751:32210, + 36752:32626, + 36753:26360, + 36754:34223, + 36755:34295, + 36756:35576, + 36757:21161, + 36758:21465, + 36759:22899, + 36760:24207, + 36761:24464, + 36762:24661, + 36763:37604, + 36764:38500, + 36765:20663, + 36766:20767, + 36767:21213, + 36768:21280, + 36769:21319, + 36770:21484, + 36771:21736, + 36772:21830, + 36773:21809, + 36774:22039, + 36775:22888, + 36776:22974, + 36777:23100, + 36778:23477, + 36779:23558, + 36780:23567, + 36781:23569, + 36782:23578, + 36783:24196, + 36784:24202, + 36785:24288, + 36786:24432, + 36787:25215, + 36788:25220, + 36789:25307, + 36790:25484, + 36791:25463, + 36792:26119, + 36793:26124, + 36794:26157, + 36795:26230, + 36796:26494, + 36797:26786, + 36798:27167, + 36799:27189, + 36800:27836, + 36801:28040, + 36802:28169, + 36803:28248, + 36804:28988, + 36805:28966, + 36806:29031, + 36807:30151, + 36808:30465, + 36809:30813, + 36810:30977, + 36811:31077, + 36812:31216, + 36813:31456, + 36814:31505, + 36815:31911, + 36816:32057, + 36817:32918, + 36818:33750, + 36819:33931, + 36820:34121, + 36821:34909, + 36822:35059, + 36823:35359, + 36824:35388, + 36825:35412, + 36826:35443, + 36827:35937, + 36828:36062, + 36829:37284, + 36830:37478, + 36831:37758, + 36832:37912, + 36833:38556, + 36834:38808, + 36835:19978, + 36836:19976, + 36837:19998, + 36838:20055, + 36839:20887, + 36840:21104, + 36841:22478, + 36842:22580, + 36843:22732, + 36844:23330, + 36845:24120, + 36846:24773, + 36847:25854, + 36848:26465, + 36849:26454, + 36850:27972, + 36851:29366, + 36852:30067, + 36853:31331, + 36854:33976, + 36855:35698, + 36856:37304, + 36857:37664, + 36858:22065, + 36859:22516, + 36860:39166, + 36928:25325, + 36929:26893, + 36930:27542, + 36931:29165, + 36932:32340, + 36933:32887, + 36934:33394, + 36935:35302, + 36936:39135, + 36937:34645, + 36938:36785, + 36939:23611, + 36940:20280, + 36941:20449, + 36942:20405, + 36943:21767, + 36944:23072, + 36945:23517, + 36946:23529, + 36947:24515, + 36948:24910, + 36949:25391, + 36950:26032, + 36951:26187, + 36952:26862, + 36953:27035, + 36954:28024, + 36955:28145, + 36956:30003, + 36957:30137, + 36958:30495, + 36959:31070, + 36960:31206, + 36961:32051, + 36962:33251, + 36963:33455, + 36964:34218, + 36965:35242, + 36966:35386, + 36967:36523, + 36968:36763, + 36969:36914, + 36970:37341, + 36971:38663, + 36972:20154, + 36973:20161, + 36974:20995, + 36975:22645, + 36976:22764, + 36977:23563, + 36978:29978, + 36979:23613, + 36980:33102, + 36981:35338, + 36982:36805, + 36983:38499, + 36984:38765, + 36985:31525, + 36986:35535, + 36987:38920, + 36988:37218, + 36989:22259, + 36990:21416, + 36992:36887, + 36993:21561, + 36994:22402, + 36995:24101, + 36996:25512, + 36997:27700, + 36998:28810, + 36999:30561, + 37000:31883, + 37001:32736, + 37002:34928, + 37003:36930, + 37004:37204, + 37005:37648, + 37006:37656, + 37007:38543, + 37008:29790, + 37009:39620, + 37010:23815, + 37011:23913, + 37012:25968, + 37013:26530, + 37014:36264, + 37015:38619, + 37016:25454, + 37017:26441, + 37018:26905, + 37019:33733, + 37020:38935, + 37021:38592, + 37022:35070, + 37023:28548, + 37024:25722, + 37025:23544, + 37026:19990, + 37027:28716, + 37028:30045, + 37029:26159, + 37030:20932, + 37031:21046, + 37032:21218, + 37033:22995, + 37034:24449, + 37035:24615, + 37036:25104, + 37037:25919, + 37038:25972, + 37039:26143, + 37040:26228, + 37041:26866, + 37042:26646, + 37043:27491, + 37044:28165, + 37045:29298, + 37046:29983, + 37047:30427, + 37048:31934, + 37049:32854, + 37050:22768, + 37051:35069, + 37052:35199, + 37053:35488, + 37054:35475, + 37055:35531, + 37056:36893, + 37057:37266, + 37058:38738, + 37059:38745, + 37060:25993, + 37061:31246, + 37062:33030, + 37063:38587, + 37064:24109, + 37065:24796, + 37066:25114, + 37067:26021, + 37068:26132, + 37069:26512, + 37070:30707, + 37071:31309, + 37072:31821, + 37073:32318, + 37074:33034, + 37075:36012, + 37076:36196, + 37077:36321, + 37078:36447, + 37079:30889, + 37080:20999, + 37081:25305, + 37082:25509, + 37083:25666, + 37084:25240, + 37085:35373, + 37086:31363, + 37087:31680, + 37088:35500, + 37089:38634, + 37090:32118, + 37091:33292, + 37092:34633, + 37093:20185, + 37094:20808, + 37095:21315, + 37096:21344, + 37097:23459, + 37098:23554, + 37099:23574, + 37100:24029, + 37101:25126, + 37102:25159, + 37103:25776, + 37104:26643, + 37105:26676, + 37106:27849, + 37107:27973, + 37108:27927, + 37109:26579, + 37110:28508, + 37111:29006, + 37112:29053, + 37113:26059, + 37114:31359, + 37115:31661, + 37116:32218, + 37184:32330, + 37185:32680, + 37186:33146, + 37187:33307, + 37188:33337, + 37189:34214, + 37190:35438, + 37191:36046, + 37192:36341, + 37193:36984, + 37194:36983, + 37195:37549, + 37196:37521, + 37197:38275, + 37198:39854, + 37199:21069, + 37200:21892, + 37201:28472, + 37202:28982, + 37203:20840, + 37204:31109, + 37205:32341, + 37206:33203, + 37207:31950, + 37208:22092, + 37209:22609, + 37210:23720, + 37211:25514, + 37212:26366, + 37213:26365, + 37214:26970, + 37215:29401, + 37216:30095, + 37217:30094, + 37218:30990, + 37219:31062, + 37220:31199, + 37221:31895, + 37222:32032, + 37223:32068, + 37224:34311, + 37225:35380, + 37226:38459, + 37227:36961, + 37228:40736, + 37229:20711, + 37230:21109, + 37231:21452, + 37232:21474, + 37233:20489, + 37234:21930, + 37235:22766, + 37236:22863, + 37237:29245, + 37238:23435, + 37239:23652, + 37240:21277, + 37241:24803, + 37242:24819, + 37243:25436, + 37244:25475, + 37245:25407, + 37246:25531, + 37248:25805, + 37249:26089, + 37250:26361, + 37251:24035, + 37252:27085, + 37253:27133, + 37254:28437, + 37255:29157, + 37256:20105, + 37257:30185, + 37258:30456, + 37259:31379, + 37260:31967, + 37261:32207, + 37262:32156, + 37263:32865, + 37264:33609, + 37265:33624, + 37266:33900, + 37267:33980, + 37268:34299, + 37269:35013, + 37270:36208, + 37271:36865, + 37272:36973, + 37273:37783, + 37274:38684, + 37275:39442, + 37276:20687, + 37277:22679, + 37278:24974, + 37279:33235, + 37280:34101, + 37281:36104, + 37282:36896, + 37283:20419, + 37284:20596, + 37285:21063, + 37286:21363, + 37287:24687, + 37288:25417, + 37289:26463, + 37290:28204, + 37291:36275, + 37292:36895, + 37293:20439, + 37294:23646, + 37295:36042, + 37296:26063, + 37297:32154, + 37298:21330, + 37299:34966, + 37300:20854, + 37301:25539, + 37302:23384, + 37303:23403, + 37304:23562, + 37305:25613, + 37306:26449, + 37307:36956, + 37308:20182, + 37309:22810, + 37310:22826, + 37311:27760, + 37312:35409, + 37313:21822, + 37314:22549, + 37315:22949, + 37316:24816, + 37317:25171, + 37318:26561, + 37319:33333, + 37320:26965, + 37321:38464, + 37322:39364, + 37323:39464, + 37324:20307, + 37325:22534, + 37326:23550, + 37327:32784, + 37328:23729, + 37329:24111, + 37330:24453, + 37331:24608, + 37332:24907, + 37333:25140, + 37334:26367, + 37335:27888, + 37336:28382, + 37337:32974, + 37338:33151, + 37339:33492, + 37340:34955, + 37341:36024, + 37342:36864, + 37343:36910, + 37344:38538, + 37345:40667, + 37346:39899, + 37347:20195, + 37348:21488, + 37349:22823, + 37350:31532, + 37351:37261, + 37352:38988, + 37353:40441, + 37354:28381, + 37355:28711, + 37356:21331, + 37357:21828, + 37358:23429, + 37359:25176, + 37360:25246, + 37361:25299, + 37362:27810, + 37363:28655, + 37364:29730, + 37365:35351, + 37366:37944, + 37367:28609, + 37368:35582, + 37369:33592, + 37370:20967, + 37371:34552, + 37372:21482, + 37440:21481, + 37441:20294, + 37442:36948, + 37443:36784, + 37444:22890, + 37445:33073, + 37446:24061, + 37447:31466, + 37448:36799, + 37449:26842, + 37450:35895, + 37451:29432, + 37452:40008, + 37453:27197, + 37454:35504, + 37455:20025, + 37456:21336, + 37457:22022, + 37458:22374, + 37459:25285, + 37460:25506, + 37461:26086, + 37462:27470, + 37463:28129, + 37464:28251, + 37465:28845, + 37466:30701, + 37467:31471, + 37468:31658, + 37469:32187, + 37470:32829, + 37471:32966, + 37472:34507, + 37473:35477, + 37474:37723, + 37475:22243, + 37476:22727, + 37477:24382, + 37478:26029, + 37479:26262, + 37480:27264, + 37481:27573, + 37482:30007, + 37483:35527, + 37484:20516, + 37485:30693, + 37486:22320, + 37487:24347, + 37488:24677, + 37489:26234, + 37490:27744, + 37491:30196, + 37492:31258, + 37493:32622, + 37494:33268, + 37495:34584, + 37496:36933, + 37497:39347, + 37498:31689, + 37499:30044, + 37500:31481, + 37501:31569, + 37502:33988, + 37504:36880, + 37505:31209, + 37506:31378, + 37507:33590, + 37508:23265, + 37509:30528, + 37510:20013, + 37511:20210, + 37512:23449, + 37513:24544, + 37514:25277, + 37515:26172, + 37516:26609, + 37517:27880, + 37518:34411, + 37519:34935, + 37520:35387, + 37521:37198, + 37522:37619, + 37523:39376, + 37524:27159, + 37525:28710, + 37526:29482, + 37527:33511, + 37528:33879, + 37529:36015, + 37530:19969, + 37531:20806, + 37532:20939, + 37533:21899, + 37534:23541, + 37535:24086, + 37536:24115, + 37537:24193, + 37538:24340, + 37539:24373, + 37540:24427, + 37541:24500, + 37542:25074, + 37543:25361, + 37544:26274, + 37545:26397, + 37546:28526, + 37547:29266, + 37548:30010, + 37549:30522, + 37550:32884, + 37551:33081, + 37552:33144, + 37553:34678, + 37554:35519, + 37555:35548, + 37556:36229, + 37557:36339, + 37558:37530, + 37559:38263, + 37560:38914, + 37561:40165, + 37562:21189, + 37563:25431, + 37564:30452, + 37565:26389, + 37566:27784, + 37567:29645, + 37568:36035, + 37569:37806, + 37570:38515, + 37571:27941, + 37572:22684, + 37573:26894, + 37574:27084, + 37575:36861, + 37576:37786, + 37577:30171, + 37578:36890, + 37579:22618, + 37580:26626, + 37581:25524, + 37582:27131, + 37583:20291, + 37584:28460, + 37585:26584, + 37586:36795, + 37587:34086, + 37588:32180, + 37589:37716, + 37590:26943, + 37591:28528, + 37592:22378, + 37593:22775, + 37594:23340, + 37595:32044, + 37596:29226, + 37597:21514, + 37598:37347, + 37599:40372, + 37600:20141, + 37601:20302, + 37602:20572, + 37603:20597, + 37604:21059, + 37605:35998, + 37606:21576, + 37607:22564, + 37608:23450, + 37609:24093, + 37610:24213, + 37611:24237, + 37612:24311, + 37613:24351, + 37614:24716, + 37615:25269, + 37616:25402, + 37617:25552, + 37618:26799, + 37619:27712, + 37620:30855, + 37621:31118, + 37622:31243, + 37623:32224, + 37624:33351, + 37625:35330, + 37626:35558, + 37627:36420, + 37628:36883, + 37696:37048, + 37697:37165, + 37698:37336, + 37699:40718, + 37700:27877, + 37701:25688, + 37702:25826, + 37703:25973, + 37704:28404, + 37705:30340, + 37706:31515, + 37707:36969, + 37708:37841, + 37709:28346, + 37710:21746, + 37711:24505, + 37712:25764, + 37713:36685, + 37714:36845, + 37715:37444, + 37716:20856, + 37717:22635, + 37718:22825, + 37719:23637, + 37720:24215, + 37721:28155, + 37722:32399, + 37723:29980, + 37724:36028, + 37725:36578, + 37726:39003, + 37727:28857, + 37728:20253, + 37729:27583, + 37730:28593, + 37731:30000, + 37732:38651, + 37733:20814, + 37734:21520, + 37735:22581, + 37736:22615, + 37737:22956, + 37738:23648, + 37739:24466, + 37740:26007, + 37741:26460, + 37742:28193, + 37743:30331, + 37744:33759, + 37745:36077, + 37746:36884, + 37747:37117, + 37748:37709, + 37749:30757, + 37750:30778, + 37751:21162, + 37752:24230, + 37753:22303, + 37754:22900, + 37755:24594, + 37756:20498, + 37757:20826, + 37758:20908, + 37760:20941, + 37761:20992, + 37762:21776, + 37763:22612, + 37764:22616, + 37765:22871, + 37766:23445, + 37767:23798, + 37768:23947, + 37769:24764, + 37770:25237, + 37771:25645, + 37772:26481, + 37773:26691, + 37774:26812, + 37775:26847, + 37776:30423, + 37777:28120, + 37778:28271, + 37779:28059, + 37780:28783, + 37781:29128, + 37782:24403, + 37783:30168, + 37784:31095, + 37785:31561, + 37786:31572, + 37787:31570, + 37788:31958, + 37789:32113, + 37790:21040, + 37791:33891, + 37792:34153, + 37793:34276, + 37794:35342, + 37795:35588, + 37796:35910, + 37797:36367, + 37798:36867, + 37799:36879, + 37800:37913, + 37801:38518, + 37802:38957, + 37803:39472, + 37804:38360, + 37805:20685, + 37806:21205, + 37807:21516, + 37808:22530, + 37809:23566, + 37810:24999, + 37811:25758, + 37812:27934, + 37813:30643, + 37814:31461, + 37815:33012, + 37816:33796, + 37817:36947, + 37818:37509, + 37819:23776, + 37820:40199, + 37821:21311, + 37822:24471, + 37823:24499, + 37824:28060, + 37825:29305, + 37826:30563, + 37827:31167, + 37828:31716, + 37829:27602, + 37830:29420, + 37831:35501, + 37832:26627, + 37833:27233, + 37834:20984, + 37835:31361, + 37836:26932, + 37837:23626, + 37838:40182, + 37839:33515, + 37840:23493, + 37841:37193, + 37842:28702, + 37843:22136, + 37844:23663, + 37845:24775, + 37846:25958, + 37847:27788, + 37848:35930, + 37849:36929, + 37850:38931, + 37851:21585, + 37852:26311, + 37853:37389, + 37854:22856, + 37855:37027, + 37856:20869, + 37857:20045, + 37858:20970, + 37859:34201, + 37860:35598, + 37861:28760, + 37862:25466, + 37863:37707, + 37864:26978, + 37865:39348, + 37866:32260, + 37867:30071, + 37868:21335, + 37869:26976, + 37870:36575, + 37871:38627, + 37872:27741, + 37873:20108, + 37874:23612, + 37875:24336, + 37876:36841, + 37877:21250, + 37878:36049, + 37879:32905, + 37880:34425, + 37881:24319, + 37882:26085, + 37883:20083, + 37884:20837, + 37952:22914, + 37953:23615, + 37954:38894, + 37955:20219, + 37956:22922, + 37957:24525, + 37958:35469, + 37959:28641, + 37960:31152, + 37961:31074, + 37962:23527, + 37963:33905, + 37964:29483, + 37965:29105, + 37966:24180, + 37967:24565, + 37968:25467, + 37969:25754, + 37970:29123, + 37971:31896, + 37972:20035, + 37973:24316, + 37974:20043, + 37975:22492, + 37976:22178, + 37977:24745, + 37978:28611, + 37979:32013, + 37980:33021, + 37981:33075, + 37982:33215, + 37983:36786, + 37984:35223, + 37985:34468, + 37986:24052, + 37987:25226, + 37988:25773, + 37989:35207, + 37990:26487, + 37991:27874, + 37992:27966, + 37993:29750, + 37994:30772, + 37995:23110, + 37996:32629, + 37997:33453, + 37998:39340, + 37999:20467, + 38000:24259, + 38001:25309, + 38002:25490, + 38003:25943, + 38004:26479, + 38005:30403, + 38006:29260, + 38007:32972, + 38008:32954, + 38009:36649, + 38010:37197, + 38011:20493, + 38012:22521, + 38013:23186, + 38014:26757, + 38016:26995, + 38017:29028, + 38018:29437, + 38019:36023, + 38020:22770, + 38021:36064, + 38022:38506, + 38023:36889, + 38024:34687, + 38025:31204, + 38026:30695, + 38027:33833, + 38028:20271, + 38029:21093, + 38030:21338, + 38031:25293, + 38032:26575, + 38033:27850, + 38034:30333, + 38035:31636, + 38036:31893, + 38037:33334, + 38038:34180, + 38039:36843, + 38040:26333, + 38041:28448, + 38042:29190, + 38043:32283, + 38044:33707, + 38045:39361, + 38046:40614, + 38047:20989, + 38048:31665, + 38049:30834, + 38050:31672, + 38051:32903, + 38052:31560, + 38053:27368, + 38054:24161, + 38055:32908, + 38056:30033, + 38057:30048, + 38058:20843, + 38059:37474, + 38060:28300, + 38061:30330, + 38062:37271, + 38063:39658, + 38064:20240, + 38065:32624, + 38066:25244, + 38067:31567, + 38068:38309, + 38069:40169, + 38070:22138, + 38071:22617, + 38072:34532, + 38073:38588, + 38074:20276, + 38075:21028, + 38076:21322, + 38077:21453, + 38078:21467, + 38079:24070, + 38080:25644, + 38081:26001, + 38082:26495, + 38083:27710, + 38084:27726, + 38085:29256, + 38086:29359, + 38087:29677, + 38088:30036, + 38089:32321, + 38090:33324, + 38091:34281, + 38092:36009, + 38093:31684, + 38094:37318, + 38095:29033, + 38096:38930, + 38097:39151, + 38098:25405, + 38099:26217, + 38100:30058, + 38101:30436, + 38102:30928, + 38103:34115, + 38104:34542, + 38105:21290, + 38106:21329, + 38107:21542, + 38108:22915, + 38109:24199, + 38110:24444, + 38111:24754, + 38112:25161, + 38113:25209, + 38114:25259, + 38115:26000, + 38116:27604, + 38117:27852, + 38118:30130, + 38119:30382, + 38120:30865, + 38121:31192, + 38122:32203, + 38123:32631, + 38124:32933, + 38125:34987, + 38126:35513, + 38127:36027, + 38128:36991, + 38129:38750, + 38130:39131, + 38131:27147, + 38132:31800, + 38133:20633, + 38134:23614, + 38135:24494, + 38136:26503, + 38137:27608, + 38138:29749, + 38139:30473, + 38140:32654, + 38208:40763, + 38209:26570, + 38210:31255, + 38211:21305, + 38212:30091, + 38213:39661, + 38214:24422, + 38215:33181, + 38216:33777, + 38217:32920, + 38218:24380, + 38219:24517, + 38220:30050, + 38221:31558, + 38222:36924, + 38223:26727, + 38224:23019, + 38225:23195, + 38226:32016, + 38227:30334, + 38228:35628, + 38229:20469, + 38230:24426, + 38231:27161, + 38232:27703, + 38233:28418, + 38234:29922, + 38235:31080, + 38236:34920, + 38237:35413, + 38238:35961, + 38239:24287, + 38240:25551, + 38241:30149, + 38242:31186, + 38243:33495, + 38244:37672, + 38245:37618, + 38246:33948, + 38247:34541, + 38248:39981, + 38249:21697, + 38250:24428, + 38251:25996, + 38252:27996, + 38253:28693, + 38254:36007, + 38255:36051, + 38256:38971, + 38257:25935, + 38258:29942, + 38259:19981, + 38260:20184, + 38261:22496, + 38262:22827, + 38263:23142, + 38264:23500, + 38265:20904, + 38266:24067, + 38267:24220, + 38268:24598, + 38269:25206, + 38270:25975, + 38272:26023, + 38273:26222, + 38274:28014, + 38275:29238, + 38276:31526, + 38277:33104, + 38278:33178, + 38279:33433, + 38280:35676, + 38281:36000, + 38282:36070, + 38283:36212, + 38284:38428, + 38285:38468, + 38286:20398, + 38287:25771, + 38288:27494, + 38289:33310, + 38290:33889, + 38291:34154, + 38292:37096, + 38293:23553, + 38294:26963, + 38295:39080, + 38296:33914, + 38297:34135, + 38298:20239, + 38299:21103, + 38300:24489, + 38301:24133, + 38302:26381, + 38303:31119, + 38304:33145, + 38305:35079, + 38306:35206, + 38307:28149, + 38308:24343, + 38309:25173, + 38310:27832, + 38311:20175, + 38312:29289, + 38313:39826, + 38314:20998, + 38315:21563, + 38316:22132, + 38317:22707, + 38318:24996, + 38319:25198, + 38320:28954, + 38321:22894, + 38322:31881, + 38323:31966, + 38324:32027, + 38325:38640, + 38326:25991, + 38327:32862, + 38328:19993, + 38329:20341, + 38330:20853, + 38331:22592, + 38332:24163, + 38333:24179, + 38334:24330, + 38335:26564, + 38336:20006, + 38337:34109, + 38338:38281, + 38339:38491, + 38340:31859, + 38341:38913, + 38342:20731, + 38343:22721, + 38344:30294, + 38345:30887, + 38346:21029, + 38347:30629, + 38348:34065, + 38349:31622, + 38350:20559, + 38351:22793, + 38352:29255, + 38353:31687, + 38354:32232, + 38355:36794, + 38356:36820, + 38357:36941, + 38358:20415, + 38359:21193, + 38360:23081, + 38361:24321, + 38362:38829, + 38363:20445, + 38364:33303, + 38365:37610, + 38366:22275, + 38367:25429, + 38368:27497, + 38369:29995, + 38370:35036, + 38371:36628, + 38372:31298, + 38373:21215, + 38374:22675, + 38375:24917, + 38376:25098, + 38377:26286, + 38378:27597, + 38379:31807, + 38380:33769, + 38381:20515, + 38382:20472, + 38383:21253, + 38384:21574, + 38385:22577, + 38386:22857, + 38387:23453, + 38388:23792, + 38389:23791, + 38390:23849, + 38391:24214, + 38392:25265, + 38393:25447, + 38394:25918, + 38395:26041, + 38396:26379, + 38464:27861, + 38465:27873, + 38466:28921, + 38467:30770, + 38468:32299, + 38469:32990, + 38470:33459, + 38471:33804, + 38472:34028, + 38473:34562, + 38474:35090, + 38475:35370, + 38476:35914, + 38477:37030, + 38478:37586, + 38479:39165, + 38480:40179, + 38481:40300, + 38482:20047, + 38483:20129, + 38484:20621, + 38485:21078, + 38486:22346, + 38487:22952, + 38488:24125, + 38489:24536, + 38490:24537, + 38491:25151, + 38492:26292, + 38493:26395, + 38494:26576, + 38495:26834, + 38496:20882, + 38497:32033, + 38498:32938, + 38499:33192, + 38500:35584, + 38501:35980, + 38502:36031, + 38503:37502, + 38504:38450, + 38505:21536, + 38506:38956, + 38507:21271, + 38508:20693, + 38509:21340, + 38510:22696, + 38511:25778, + 38512:26420, + 38513:29287, + 38514:30566, + 38515:31302, + 38516:37350, + 38517:21187, + 38518:27809, + 38519:27526, + 38520:22528, + 38521:24140, + 38522:22868, + 38523:26412, + 38524:32763, + 38525:20961, + 38526:30406, + 38528:25705, + 38529:30952, + 38530:39764, + 38531:40635, + 38532:22475, + 38533:22969, + 38534:26151, + 38535:26522, + 38536:27598, + 38537:21737, + 38538:27097, + 38539:24149, + 38540:33180, + 38541:26517, + 38542:39850, + 38543:26622, + 38544:40018, + 38545:26717, + 38546:20134, + 38547:20451, + 38548:21448, + 38549:25273, + 38550:26411, + 38551:27819, + 38552:36804, + 38553:20397, + 38554:32365, + 38555:40639, + 38556:19975, + 38557:24930, + 38558:28288, + 38559:28459, + 38560:34067, + 38561:21619, + 38562:26410, + 38563:39749, + 38564:24051, + 38565:31637, + 38566:23724, + 38567:23494, + 38568:34588, + 38569:28234, + 38570:34001, + 38571:31252, + 38572:33032, + 38573:22937, + 38574:31885, + 38575:27665, + 38576:30496, + 38577:21209, + 38578:22818, + 38579:28961, + 38580:29279, + 38581:30683, + 38582:38695, + 38583:40289, + 38584:26891, + 38585:23167, + 38586:23064, + 38587:20901, + 38588:21517, + 38589:21629, + 38590:26126, + 38591:30431, + 38592:36855, + 38593:37528, + 38594:40180, + 38595:23018, + 38596:29277, + 38597:28357, + 38598:20813, + 38599:26825, + 38600:32191, + 38601:32236, + 38602:38754, + 38603:40634, + 38604:25720, + 38605:27169, + 38606:33538, + 38607:22916, + 38608:23391, + 38609:27611, + 38610:29467, + 38611:30450, + 38612:32178, + 38613:32791, + 38614:33945, + 38615:20786, + 38616:26408, + 38617:40665, + 38618:30446, + 38619:26466, + 38620:21247, + 38621:39173, + 38622:23588, + 38623:25147, + 38624:31870, + 38625:36016, + 38626:21839, + 38627:24758, + 38628:32011, + 38629:38272, + 38630:21249, + 38631:20063, + 38632:20918, + 38633:22812, + 38634:29242, + 38635:32822, + 38636:37326, + 38637:24357, + 38638:30690, + 38639:21380, + 38640:24441, + 38641:32004, + 38642:34220, + 38643:35379, + 38644:36493, + 38645:38742, + 38646:26611, + 38647:34222, + 38648:37971, + 38649:24841, + 38650:24840, + 38651:27833, + 38652:30290, + 38720:35565, + 38721:36664, + 38722:21807, + 38723:20305, + 38724:20778, + 38725:21191, + 38726:21451, + 38727:23461, + 38728:24189, + 38729:24736, + 38730:24962, + 38731:25558, + 38732:26377, + 38733:26586, + 38734:28263, + 38735:28044, + 38736:29494, + 38737:29495, + 38738:30001, + 38739:31056, + 38740:35029, + 38741:35480, + 38742:36938, + 38743:37009, + 38744:37109, + 38745:38596, + 38746:34701, + 38747:22805, + 38748:20104, + 38749:20313, + 38750:19982, + 38751:35465, + 38752:36671, + 38753:38928, + 38754:20653, + 38755:24188, + 38756:22934, + 38757:23481, + 38758:24248, + 38759:25562, + 38760:25594, + 38761:25793, + 38762:26332, + 38763:26954, + 38764:27096, + 38765:27915, + 38766:28342, + 38767:29076, + 38768:29992, + 38769:31407, + 38770:32650, + 38771:32768, + 38772:33865, + 38773:33993, + 38774:35201, + 38775:35617, + 38776:36362, + 38777:36965, + 38778:38525, + 38779:39178, + 38780:24958, + 38781:25233, + 38782:27442, + 38784:27779, + 38785:28020, + 38786:32716, + 38787:32764, + 38788:28096, + 38789:32645, + 38790:34746, + 38791:35064, + 38792:26469, + 38793:33713, + 38794:38972, + 38795:38647, + 38796:27931, + 38797:32097, + 38798:33853, + 38799:37226, + 38800:20081, + 38801:21365, + 38802:23888, + 38803:27396, + 38804:28651, + 38805:34253, + 38806:34349, + 38807:35239, + 38808:21033, + 38809:21519, + 38810:23653, + 38811:26446, + 38812:26792, + 38813:29702, + 38814:29827, + 38815:30178, + 38816:35023, + 38817:35041, + 38818:37324, + 38819:38626, + 38820:38520, + 38821:24459, + 38822:29575, + 38823:31435, + 38824:33870, + 38825:25504, + 38826:30053, + 38827:21129, + 38828:27969, + 38829:28316, + 38830:29705, + 38831:30041, + 38832:30827, + 38833:31890, + 38834:38534, + 38835:31452, + 38836:40845, + 38837:20406, + 38838:24942, + 38839:26053, + 38840:34396, + 38841:20102, + 38842:20142, + 38843:20698, + 38844:20001, + 38845:20940, + 38846:23534, + 38847:26009, + 38848:26753, + 38849:28092, + 38850:29471, + 38851:30274, + 38852:30637, + 38853:31260, + 38854:31975, + 38855:33391, + 38856:35538, + 38857:36988, + 38858:37327, + 38859:38517, + 38860:38936, + 38861:21147, + 38862:32209, + 38863:20523, + 38864:21400, + 38865:26519, + 38866:28107, + 38867:29136, + 38868:29747, + 38869:33256, + 38870:36650, + 38871:38563, + 38872:40023, + 38873:40607, + 38874:29792, + 38875:22593, + 38876:28057, + 38877:32047, + 38878:39006, + 38879:20196, + 38880:20278, + 38881:20363, + 38882:20919, + 38883:21169, + 38884:23994, + 38885:24604, + 38886:29618, + 38887:31036, + 38888:33491, + 38889:37428, + 38890:38583, + 38891:38646, + 38892:38666, + 38893:40599, + 38894:40802, + 38895:26278, + 38896:27508, + 38897:21015, + 38898:21155, + 38899:28872, + 38900:35010, + 38901:24265, + 38902:24651, + 38903:24976, + 38904:28451, + 38905:29001, + 38906:31806, + 38907:32244, + 38908:32879, + 38976:34030, + 38977:36899, + 38978:37676, + 38979:21570, + 38980:39791, + 38981:27347, + 38982:28809, + 38983:36034, + 38984:36335, + 38985:38706, + 38986:21172, + 38987:23105, + 38988:24266, + 38989:24324, + 38990:26391, + 38991:27004, + 38992:27028, + 38993:28010, + 38994:28431, + 38995:29282, + 38996:29436, + 38997:31725, + 38998:32769, + 38999:32894, + 39000:34635, + 39001:37070, + 39002:20845, + 39003:40595, + 39004:31108, + 39005:32907, + 39006:37682, + 39007:35542, + 39008:20525, + 39009:21644, + 39010:35441, + 39011:27498, + 39012:36036, + 39013:33031, + 39014:24785, + 39015:26528, + 39016:40434, + 39017:20121, + 39018:20120, + 39019:39952, + 39020:35435, + 39021:34241, + 39022:34152, + 39023:26880, + 39024:28286, + 39025:30871, + 39026:33109, + 39071:24332, + 39072:19984, + 39073:19989, + 39074:20010, + 39075:20017, + 39076:20022, + 39077:20028, + 39078:20031, + 39079:20034, + 39080:20054, + 39081:20056, + 39082:20098, + 39083:20101, + 39084:35947, + 39085:20106, + 39086:33298, + 39087:24333, + 39088:20110, + 39089:20126, + 39090:20127, + 39091:20128, + 39092:20130, + 39093:20144, + 39094:20147, + 39095:20150, + 39096:20174, + 39097:20173, + 39098:20164, + 39099:20166, + 39100:20162, + 39101:20183, + 39102:20190, + 39103:20205, + 39104:20191, + 39105:20215, + 39106:20233, + 39107:20314, + 39108:20272, + 39109:20315, + 39110:20317, + 39111:20311, + 39112:20295, + 39113:20342, + 39114:20360, + 39115:20367, + 39116:20376, + 39117:20347, + 39118:20329, + 39119:20336, + 39120:20369, + 39121:20335, + 39122:20358, + 39123:20374, + 39124:20760, + 39125:20436, + 39126:20447, + 39127:20430, + 39128:20440, + 39129:20443, + 39130:20433, + 39131:20442, + 39132:20432, + 39133:20452, + 39134:20453, + 39135:20506, + 39136:20520, + 39137:20500, + 39138:20522, + 39139:20517, + 39140:20485, + 39141:20252, + 39142:20470, + 39143:20513, + 39144:20521, + 39145:20524, + 39146:20478, + 39147:20463, + 39148:20497, + 39149:20486, + 39150:20547, + 39151:20551, + 39152:26371, + 39153:20565, + 39154:20560, + 39155:20552, + 39156:20570, + 39157:20566, + 39158:20588, + 39159:20600, + 39160:20608, + 39161:20634, + 39162:20613, + 39163:20660, + 39164:20658, + 39232:20681, + 39233:20682, + 39234:20659, + 39235:20674, + 39236:20694, + 39237:20702, + 39238:20709, + 39239:20717, + 39240:20707, + 39241:20718, + 39242:20729, + 39243:20725, + 39244:20745, + 39245:20737, + 39246:20738, + 39247:20758, + 39248:20757, + 39249:20756, + 39250:20762, + 39251:20769, + 39252:20794, + 39253:20791, + 39254:20796, + 39255:20795, + 39256:20799, + 39257:20800, + 39258:20818, + 39259:20812, + 39260:20820, + 39261:20834, + 39262:31480, + 39263:20841, + 39264:20842, + 39265:20846, + 39266:20864, + 39267:20866, + 39268:22232, + 39269:20876, + 39270:20873, + 39271:20879, + 39272:20881, + 39273:20883, + 39274:20885, + 39275:20886, + 39276:20900, + 39277:20902, + 39278:20898, + 39279:20905, + 39280:20906, + 39281:20907, + 39282:20915, + 39283:20913, + 39284:20914, + 39285:20912, + 39286:20917, + 39287:20925, + 39288:20933, + 39289:20937, + 39290:20955, + 39291:20960, + 39292:34389, + 39293:20969, + 39294:20973, + 39296:20976, + 39297:20981, + 39298:20990, + 39299:20996, + 39300:21003, + 39301:21012, + 39302:21006, + 39303:21031, + 39304:21034, + 39305:21038, + 39306:21043, + 39307:21049, + 39308:21071, + 39309:21060, + 39310:21067, + 39311:21068, + 39312:21086, + 39313:21076, + 39314:21098, + 39315:21108, + 39316:21097, + 39317:21107, + 39318:21119, + 39319:21117, + 39320:21133, + 39321:21140, + 39322:21138, + 39323:21105, + 39324:21128, + 39325:21137, + 39326:36776, + 39327:36775, + 39328:21164, + 39329:21165, + 39330:21180, + 39331:21173, + 39332:21185, + 39333:21197, + 39334:21207, + 39335:21214, + 39336:21219, + 39337:21222, + 39338:39149, + 39339:21216, + 39340:21235, + 39341:21237, + 39342:21240, + 39343:21241, + 39344:21254, + 39345:21256, + 39346:30008, + 39347:21261, + 39348:21264, + 39349:21263, + 39350:21269, + 39351:21274, + 39352:21283, + 39353:21295, + 39354:21297, + 39355:21299, + 39356:21304, + 39357:21312, + 39358:21318, + 39359:21317, + 39360:19991, + 39361:21321, + 39362:21325, + 39363:20950, + 39364:21342, + 39365:21353, + 39366:21358, + 39367:22808, + 39368:21371, + 39369:21367, + 39370:21378, + 39371:21398, + 39372:21408, + 39373:21414, + 39374:21413, + 39375:21422, + 39376:21424, + 39377:21430, + 39378:21443, + 39379:31762, + 39380:38617, + 39381:21471, + 39382:26364, + 39383:29166, + 39384:21486, + 39385:21480, + 39386:21485, + 39387:21498, + 39388:21505, + 39389:21565, + 39390:21568, + 39391:21548, + 39392:21549, + 39393:21564, + 39394:21550, + 39395:21558, + 39396:21545, + 39397:21533, + 39398:21582, + 39399:21647, + 39400:21621, + 39401:21646, + 39402:21599, + 39403:21617, + 39404:21623, + 39405:21616, + 39406:21650, + 39407:21627, + 39408:21632, + 39409:21622, + 39410:21636, + 39411:21648, + 39412:21638, + 39413:21703, + 39414:21666, + 39415:21688, + 39416:21669, + 39417:21676, + 39418:21700, + 39419:21704, + 39420:21672, + 39488:21675, + 39489:21698, + 39490:21668, + 39491:21694, + 39492:21692, + 39493:21720, + 39494:21733, + 39495:21734, + 39496:21775, + 39497:21780, + 39498:21757, + 39499:21742, + 39500:21741, + 39501:21754, + 39502:21730, + 39503:21817, + 39504:21824, + 39505:21859, + 39506:21836, + 39507:21806, + 39508:21852, + 39509:21829, + 39510:21846, + 39511:21847, + 39512:21816, + 39513:21811, + 39514:21853, + 39515:21913, + 39516:21888, + 39517:21679, + 39518:21898, + 39519:21919, + 39520:21883, + 39521:21886, + 39522:21912, + 39523:21918, + 39524:21934, + 39525:21884, + 39526:21891, + 39527:21929, + 39528:21895, + 39529:21928, + 39530:21978, + 39531:21957, + 39532:21983, + 39533:21956, + 39534:21980, + 39535:21988, + 39536:21972, + 39537:22036, + 39538:22007, + 39539:22038, + 39540:22014, + 39541:22013, + 39542:22043, + 39543:22009, + 39544:22094, + 39545:22096, + 39546:29151, + 39547:22068, + 39548:22070, + 39549:22066, + 39550:22072, + 39552:22123, + 39553:22116, + 39554:22063, + 39555:22124, + 39556:22122, + 39557:22150, + 39558:22144, + 39559:22154, + 39560:22176, + 39561:22164, + 39562:22159, + 39563:22181, + 39564:22190, + 39565:22198, + 39566:22196, + 39567:22210, + 39568:22204, + 39569:22209, + 39570:22211, + 39571:22208, + 39572:22216, + 39573:22222, + 39574:22225, + 39575:22227, + 39576:22231, + 39577:22254, + 39578:22265, + 39579:22272, + 39580:22271, + 39581:22276, + 39582:22281, + 39583:22280, + 39584:22283, + 39585:22285, + 39586:22291, + 39587:22296, + 39588:22294, + 39589:21959, + 39590:22300, + 39591:22310, + 39592:22327, + 39593:22328, + 39594:22350, + 39595:22331, + 39596:22336, + 39597:22351, + 39598:22377, + 39599:22464, + 39600:22408, + 39601:22369, + 39602:22399, + 39603:22409, + 39604:22419, + 39605:22432, + 39606:22451, + 39607:22436, + 39608:22442, + 39609:22448, + 39610:22467, + 39611:22470, + 39612:22484, + 39613:22482, + 39614:22483, + 39615:22538, + 39616:22486, + 39617:22499, + 39618:22539, + 39619:22553, + 39620:22557, + 39621:22642, + 39622:22561, + 39623:22626, + 39624:22603, + 39625:22640, + 39626:27584, + 39627:22610, + 39628:22589, + 39629:22649, + 39630:22661, + 39631:22713, + 39632:22687, + 39633:22699, + 39634:22714, + 39635:22750, + 39636:22715, + 39637:22712, + 39638:22702, + 39639:22725, + 39640:22739, + 39641:22737, + 39642:22743, + 39643:22745, + 39644:22744, + 39645:22757, + 39646:22748, + 39647:22756, + 39648:22751, + 39649:22767, + 39650:22778, + 39651:22777, + 39652:22779, + 39653:22780, + 39654:22781, + 39655:22786, + 39656:22794, + 39657:22800, + 39658:22811, + 39659:26790, + 39660:22821, + 39661:22828, + 39662:22829, + 39663:22834, + 39664:22840, + 39665:22846, + 39666:31442, + 39667:22869, + 39668:22864, + 39669:22862, + 39670:22874, + 39671:22872, + 39672:22882, + 39673:22880, + 39674:22887, + 39675:22892, + 39676:22889, + 39744:22904, + 39745:22913, + 39746:22941, + 39747:20318, + 39748:20395, + 39749:22947, + 39750:22962, + 39751:22982, + 39752:23016, + 39753:23004, + 39754:22925, + 39755:23001, + 39756:23002, + 39757:23077, + 39758:23071, + 39759:23057, + 39760:23068, + 39761:23049, + 39762:23066, + 39763:23104, + 39764:23148, + 39765:23113, + 39766:23093, + 39767:23094, + 39768:23138, + 39769:23146, + 39770:23194, + 39771:23228, + 39772:23230, + 39773:23243, + 39774:23234, + 39775:23229, + 39776:23267, + 39777:23255, + 39778:23270, + 39779:23273, + 39780:23254, + 39781:23290, + 39782:23291, + 39783:23308, + 39784:23307, + 39785:23318, + 39786:23346, + 39787:23248, + 39788:23338, + 39789:23350, + 39790:23358, + 39791:23363, + 39792:23365, + 39793:23360, + 39794:23377, + 39795:23381, + 39796:23386, + 39797:23387, + 39798:23397, + 39799:23401, + 39800:23408, + 39801:23411, + 39802:23413, + 39803:23416, + 39804:25992, + 39805:23418, + 39806:23424, + 39808:23427, + 39809:23462, + 39810:23480, + 39811:23491, + 39812:23495, + 39813:23497, + 39814:23508, + 39815:23504, + 39816:23524, + 39817:23526, + 39818:23522, + 39819:23518, + 39820:23525, + 39821:23531, + 39822:23536, + 39823:23542, + 39824:23539, + 39825:23557, + 39826:23559, + 39827:23560, + 39828:23565, + 39829:23571, + 39830:23584, + 39831:23586, + 39832:23592, + 39833:23608, + 39834:23609, + 39835:23617, + 39836:23622, + 39837:23630, + 39838:23635, + 39839:23632, + 39840:23631, + 39841:23409, + 39842:23660, + 39843:23662, + 39844:20066, + 39845:23670, + 39846:23673, + 39847:23692, + 39848:23697, + 39849:23700, + 39850:22939, + 39851:23723, + 39852:23739, + 39853:23734, + 39854:23740, + 39855:23735, + 39856:23749, + 39857:23742, + 39858:23751, + 39859:23769, + 39860:23785, + 39861:23805, + 39862:23802, + 39863:23789, + 39864:23948, + 39865:23786, + 39866:23819, + 39867:23829, + 39868:23831, + 39869:23900, + 39870:23839, + 39871:23835, + 39872:23825, + 39873:23828, + 39874:23842, + 39875:23834, + 39876:23833, + 39877:23832, + 39878:23884, + 39879:23890, + 39880:23886, + 39881:23883, + 39882:23916, + 39883:23923, + 39884:23926, + 39885:23943, + 39886:23940, + 39887:23938, + 39888:23970, + 39889:23965, + 39890:23980, + 39891:23982, + 39892:23997, + 39893:23952, + 39894:23991, + 39895:23996, + 39896:24009, + 39897:24013, + 39898:24019, + 39899:24018, + 39900:24022, + 39901:24027, + 39902:24043, + 39903:24050, + 39904:24053, + 39905:24075, + 39906:24090, + 39907:24089, + 39908:24081, + 39909:24091, + 39910:24118, + 39911:24119, + 39912:24132, + 39913:24131, + 39914:24128, + 39915:24142, + 39916:24151, + 39917:24148, + 39918:24159, + 39919:24162, + 39920:24164, + 39921:24135, + 39922:24181, + 39923:24182, + 39924:24186, + 39925:40636, + 39926:24191, + 39927:24224, + 39928:24257, + 39929:24258, + 39930:24264, + 39931:24272, + 39932:24271, + 40000:24278, + 40001:24291, + 40002:24285, + 40003:24282, + 40004:24283, + 40005:24290, + 40006:24289, + 40007:24296, + 40008:24297, + 40009:24300, + 40010:24305, + 40011:24307, + 40012:24304, + 40013:24308, + 40014:24312, + 40015:24318, + 40016:24323, + 40017:24329, + 40018:24413, + 40019:24412, + 40020:24331, + 40021:24337, + 40022:24342, + 40023:24361, + 40024:24365, + 40025:24376, + 40026:24385, + 40027:24392, + 40028:24396, + 40029:24398, + 40030:24367, + 40031:24401, + 40032:24406, + 40033:24407, + 40034:24409, + 40035:24417, + 40036:24429, + 40037:24435, + 40038:24439, + 40039:24451, + 40040:24450, + 40041:24447, + 40042:24458, + 40043:24456, + 40044:24465, + 40045:24455, + 40046:24478, + 40047:24473, + 40048:24472, + 40049:24480, + 40050:24488, + 40051:24493, + 40052:24508, + 40053:24534, + 40054:24571, + 40055:24548, + 40056:24568, + 40057:24561, + 40058:24541, + 40059:24755, + 40060:24575, + 40061:24609, + 40062:24672, + 40064:24601, + 40065:24592, + 40066:24617, + 40067:24590, + 40068:24625, + 40069:24603, + 40070:24597, + 40071:24619, + 40072:24614, + 40073:24591, + 40074:24634, + 40075:24666, + 40076:24641, + 40077:24682, + 40078:24695, + 40079:24671, + 40080:24650, + 40081:24646, + 40082:24653, + 40083:24675, + 40084:24643, + 40085:24676, + 40086:24642, + 40087:24684, + 40088:24683, + 40089:24665, + 40090:24705, + 40091:24717, + 40092:24807, + 40093:24707, + 40094:24730, + 40095:24708, + 40096:24731, + 40097:24726, + 40098:24727, + 40099:24722, + 40100:24743, + 40101:24715, + 40102:24801, + 40103:24760, + 40104:24800, + 40105:24787, + 40106:24756, + 40107:24560, + 40108:24765, + 40109:24774, + 40110:24757, + 40111:24792, + 40112:24909, + 40113:24853, + 40114:24838, + 40115:24822, + 40116:24823, + 40117:24832, + 40118:24820, + 40119:24826, + 40120:24835, + 40121:24865, + 40122:24827, + 40123:24817, + 40124:24845, + 40125:24846, + 40126:24903, + 40127:24894, + 40128:24872, + 40129:24871, + 40130:24906, + 40131:24895, + 40132:24892, + 40133:24876, + 40134:24884, + 40135:24893, + 40136:24898, + 40137:24900, + 40138:24947, + 40139:24951, + 40140:24920, + 40141:24921, + 40142:24922, + 40143:24939, + 40144:24948, + 40145:24943, + 40146:24933, + 40147:24945, + 40148:24927, + 40149:24925, + 40150:24915, + 40151:24949, + 40152:24985, + 40153:24982, + 40154:24967, + 40155:25004, + 40156:24980, + 40157:24986, + 40158:24970, + 40159:24977, + 40160:25003, + 40161:25006, + 40162:25036, + 40163:25034, + 40164:25033, + 40165:25079, + 40166:25032, + 40167:25027, + 40168:25030, + 40169:25018, + 40170:25035, + 40171:32633, + 40172:25037, + 40173:25062, + 40174:25059, + 40175:25078, + 40176:25082, + 40177:25076, + 40178:25087, + 40179:25085, + 40180:25084, + 40181:25086, + 40182:25088, + 40183:25096, + 40184:25097, + 40185:25101, + 40186:25100, + 40187:25108, + 40188:25115, + 40256:25118, + 40257:25121, + 40258:25130, + 40259:25134, + 40260:25136, + 40261:25138, + 40262:25139, + 40263:25153, + 40264:25166, + 40265:25182, + 40266:25187, + 40267:25179, + 40268:25184, + 40269:25192, + 40270:25212, + 40271:25218, + 40272:25225, + 40273:25214, + 40274:25234, + 40275:25235, + 40276:25238, + 40277:25300, + 40278:25219, + 40279:25236, + 40280:25303, + 40281:25297, + 40282:25275, + 40283:25295, + 40284:25343, + 40285:25286, + 40286:25812, + 40287:25288, + 40288:25308, + 40289:25292, + 40290:25290, + 40291:25282, + 40292:25287, + 40293:25243, + 40294:25289, + 40295:25356, + 40296:25326, + 40297:25329, + 40298:25383, + 40299:25346, + 40300:25352, + 40301:25327, + 40302:25333, + 40303:25424, + 40304:25406, + 40305:25421, + 40306:25628, + 40307:25423, + 40308:25494, + 40309:25486, + 40310:25472, + 40311:25515, + 40312:25462, + 40313:25507, + 40314:25487, + 40315:25481, + 40316:25503, + 40317:25525, + 40318:25451, + 40320:25449, + 40321:25534, + 40322:25577, + 40323:25536, + 40324:25542, + 40325:25571, + 40326:25545, + 40327:25554, + 40328:25590, + 40329:25540, + 40330:25622, + 40331:25652, + 40332:25606, + 40333:25619, + 40334:25638, + 40335:25654, + 40336:25885, + 40337:25623, + 40338:25640, + 40339:25615, + 40340:25703, + 40341:25711, + 40342:25718, + 40343:25678, + 40344:25898, + 40345:25749, + 40346:25747, + 40347:25765, + 40348:25769, + 40349:25736, + 40350:25788, + 40351:25818, + 40352:25810, + 40353:25797, + 40354:25799, + 40355:25787, + 40356:25816, + 40357:25794, + 40358:25841, + 40359:25831, + 40360:33289, + 40361:25824, + 40362:25825, + 40363:25260, + 40364:25827, + 40365:25839, + 40366:25900, + 40367:25846, + 40368:25844, + 40369:25842, + 40370:25850, + 40371:25856, + 40372:25853, + 40373:25880, + 40374:25884, + 40375:25861, + 40376:25892, + 40377:25891, + 40378:25899, + 40379:25908, + 40380:25909, + 40381:25911, + 40382:25910, + 40383:25912, + 40384:30027, + 40385:25928, + 40386:25942, + 40387:25941, + 40388:25933, + 40389:25944, + 40390:25950, + 40391:25949, + 40392:25970, + 40393:25976, + 40394:25986, + 40395:25987, + 40396:35722, + 40397:26011, + 40398:26015, + 40399:26027, + 40400:26039, + 40401:26051, + 40402:26054, + 40403:26049, + 40404:26052, + 40405:26060, + 40406:26066, + 40407:26075, + 40408:26073, + 40409:26080, + 40410:26081, + 40411:26097, + 40412:26482, + 40413:26122, + 40414:26115, + 40415:26107, + 40416:26483, + 40417:26165, + 40418:26166, + 40419:26164, + 40420:26140, + 40421:26191, + 40422:26180, + 40423:26185, + 40424:26177, + 40425:26206, + 40426:26205, + 40427:26212, + 40428:26215, + 40429:26216, + 40430:26207, + 40431:26210, + 40432:26224, + 40433:26243, + 40434:26248, + 40435:26254, + 40436:26249, + 40437:26244, + 40438:26264, + 40439:26269, + 40440:26305, + 40441:26297, + 40442:26313, + 40443:26302, + 40444:26300, + 40512:26308, + 40513:26296, + 40514:26326, + 40515:26330, + 40516:26336, + 40517:26175, + 40518:26342, + 40519:26345, + 40520:26352, + 40521:26357, + 40522:26359, + 40523:26383, + 40524:26390, + 40525:26398, + 40526:26406, + 40527:26407, + 40528:38712, + 40529:26414, + 40530:26431, + 40531:26422, + 40532:26433, + 40533:26424, + 40534:26423, + 40535:26438, + 40536:26462, + 40537:26464, + 40538:26457, + 40539:26467, + 40540:26468, + 40541:26505, + 40542:26480, + 40543:26537, + 40544:26492, + 40545:26474, + 40546:26508, + 40547:26507, + 40548:26534, + 40549:26529, + 40550:26501, + 40551:26551, + 40552:26607, + 40553:26548, + 40554:26604, + 40555:26547, + 40556:26601, + 40557:26552, + 40558:26596, + 40559:26590, + 40560:26589, + 40561:26594, + 40562:26606, + 40563:26553, + 40564:26574, + 40565:26566, + 40566:26599, + 40567:27292, + 40568:26654, + 40569:26694, + 40570:26665, + 40571:26688, + 40572:26701, + 40573:26674, + 40574:26702, + 40576:26803, + 40577:26667, + 40578:26713, + 40579:26723, + 40580:26743, + 40581:26751, + 40582:26783, + 40583:26767, + 40584:26797, + 40585:26772, + 40586:26781, + 40587:26779, + 40588:26755, + 40589:27310, + 40590:26809, + 40591:26740, + 40592:26805, + 40593:26784, + 40594:26810, + 40595:26895, + 40596:26765, + 40597:26750, + 40598:26881, + 40599:26826, + 40600:26888, + 40601:26840, + 40602:26914, + 40603:26918, + 40604:26849, + 40605:26892, + 40606:26829, + 40607:26836, + 40608:26855, + 40609:26837, + 40610:26934, + 40611:26898, + 40612:26884, + 40613:26839, + 40614:26851, + 40615:26917, + 40616:26873, + 40617:26848, + 40618:26863, + 40619:26920, + 40620:26922, + 40621:26906, + 40622:26915, + 40623:26913, + 40624:26822, + 40625:27001, + 40626:26999, + 40627:26972, + 40628:27000, + 40629:26987, + 40630:26964, + 40631:27006, + 40632:26990, + 40633:26937, + 40634:26996, + 40635:26941, + 40636:26969, + 40637:26928, + 40638:26977, + 40639:26974, + 40640:26973, + 40641:27009, + 40642:26986, + 40643:27058, + 40644:27054, + 40645:27088, + 40646:27071, + 40647:27073, + 40648:27091, + 40649:27070, + 40650:27086, + 40651:23528, + 40652:27082, + 40653:27101, + 40654:27067, + 40655:27075, + 40656:27047, + 40657:27182, + 40658:27025, + 40659:27040, + 40660:27036, + 40661:27029, + 40662:27060, + 40663:27102, + 40664:27112, + 40665:27138, + 40666:27163, + 40667:27135, + 40668:27402, + 40669:27129, + 40670:27122, + 40671:27111, + 40672:27141, + 40673:27057, + 40674:27166, + 40675:27117, + 40676:27156, + 40677:27115, + 40678:27146, + 40679:27154, + 40680:27329, + 40681:27171, + 40682:27155, + 40683:27204, + 40684:27148, + 40685:27250, + 40686:27190, + 40687:27256, + 40688:27207, + 40689:27234, + 40690:27225, + 40691:27238, + 40692:27208, + 40693:27192, + 40694:27170, + 40695:27280, + 40696:27277, + 40697:27296, + 40698:27268, + 40699:27298, + 40700:27299, + 40768:27287, + 40769:34327, + 40770:27323, + 40771:27331, + 40772:27330, + 40773:27320, + 40774:27315, + 40775:27308, + 40776:27358, + 40777:27345, + 40778:27359, + 40779:27306, + 40780:27354, + 40781:27370, + 40782:27387, + 40783:27397, + 40784:34326, + 40785:27386, + 40786:27410, + 40787:27414, + 40788:39729, + 40789:27423, + 40790:27448, + 40791:27447, + 40792:30428, + 40793:27449, + 40794:39150, + 40795:27463, + 40796:27459, + 40797:27465, + 40798:27472, + 40799:27481, + 40800:27476, + 40801:27483, + 40802:27487, + 40803:27489, + 40804:27512, + 40805:27513, + 40806:27519, + 40807:27520, + 40808:27524, + 40809:27523, + 40810:27533, + 40811:27544, + 40812:27541, + 40813:27550, + 40814:27556, + 40815:27562, + 40816:27563, + 40817:27567, + 40818:27570, + 40819:27569, + 40820:27571, + 40821:27575, + 40822:27580, + 40823:27590, + 40824:27595, + 40825:27603, + 40826:27615, + 40827:27628, + 40828:27627, + 40829:27635, + 40830:27631, + 40832:40638, + 40833:27656, + 40834:27667, + 40835:27668, + 40836:27675, + 40837:27684, + 40838:27683, + 40839:27742, + 40840:27733, + 40841:27746, + 40842:27754, + 40843:27778, + 40844:27789, + 40845:27802, + 40846:27777, + 40847:27803, + 40848:27774, + 40849:27752, + 40850:27763, + 40851:27794, + 40852:27792, + 40853:27844, + 40854:27889, + 40855:27859, + 40856:27837, + 40857:27863, + 40858:27845, + 40859:27869, + 40860:27822, + 40861:27825, + 40862:27838, + 40863:27834, + 40864:27867, + 40865:27887, + 40866:27865, + 40867:27882, + 40868:27935, + 40869:34893, + 40870:27958, + 40871:27947, + 40872:27965, + 40873:27960, + 40874:27929, + 40875:27957, + 40876:27955, + 40877:27922, + 40878:27916, + 40879:28003, + 40880:28051, + 40881:28004, + 40882:27994, + 40883:28025, + 40884:27993, + 40885:28046, + 40886:28053, + 40887:28644, + 40888:28037, + 40889:28153, + 40890:28181, + 40891:28170, + 40892:28085, + 40893:28103, + 40894:28134, + 40895:28088, + 40896:28102, + 40897:28140, + 40898:28126, + 40899:28108, + 40900:28136, + 40901:28114, + 40902:28101, + 40903:28154, + 40904:28121, + 40905:28132, + 40906:28117, + 40907:28138, + 40908:28142, + 40909:28205, + 40910:28270, + 40911:28206, + 40912:28185, + 40913:28274, + 40914:28255, + 40915:28222, + 40916:28195, + 40917:28267, + 40918:28203, + 40919:28278, + 40920:28237, + 40921:28191, + 40922:28227, + 40923:28218, + 40924:28238, + 40925:28196, + 40926:28415, + 40927:28189, + 40928:28216, + 40929:28290, + 40930:28330, + 40931:28312, + 40932:28361, + 40933:28343, + 40934:28371, + 40935:28349, + 40936:28335, + 40937:28356, + 40938:28338, + 40939:28372, + 40940:28373, + 40941:28303, + 40942:28325, + 40943:28354, + 40944:28319, + 40945:28481, + 40946:28433, + 40947:28748, + 40948:28396, + 40949:28408, + 40950:28414, + 40951:28479, + 40952:28402, + 40953:28465, + 40954:28399, + 40955:28466, + 40956:28364, + 57408:28478, + 57409:28435, + 57410:28407, + 57411:28550, + 57412:28538, + 57413:28536, + 57414:28545, + 57415:28544, + 57416:28527, + 57417:28507, + 57418:28659, + 57419:28525, + 57420:28546, + 57421:28540, + 57422:28504, + 57423:28558, + 57424:28561, + 57425:28610, + 57426:28518, + 57427:28595, + 57428:28579, + 57429:28577, + 57430:28580, + 57431:28601, + 57432:28614, + 57433:28586, + 57434:28639, + 57435:28629, + 57436:28652, + 57437:28628, + 57438:28632, + 57439:28657, + 57440:28654, + 57441:28635, + 57442:28681, + 57443:28683, + 57444:28666, + 57445:28689, + 57446:28673, + 57447:28687, + 57448:28670, + 57449:28699, + 57450:28698, + 57451:28532, + 57452:28701, + 57453:28696, + 57454:28703, + 57455:28720, + 57456:28734, + 57457:28722, + 57458:28753, + 57459:28771, + 57460:28825, + 57461:28818, + 57462:28847, + 57463:28913, + 57464:28844, + 57465:28856, + 57466:28851, + 57467:28846, + 57468:28895, + 57469:28875, + 57470:28893, + 57472:28889, + 57473:28937, + 57474:28925, + 57475:28956, + 57476:28953, + 57477:29029, + 57478:29013, + 57479:29064, + 57480:29030, + 57481:29026, + 57482:29004, + 57483:29014, + 57484:29036, + 57485:29071, + 57486:29179, + 57487:29060, + 57488:29077, + 57489:29096, + 57490:29100, + 57491:29143, + 57492:29113, + 57493:29118, + 57494:29138, + 57495:29129, + 57496:29140, + 57497:29134, + 57498:29152, + 57499:29164, + 57500:29159, + 57501:29173, + 57502:29180, + 57503:29177, + 57504:29183, + 57505:29197, + 57506:29200, + 57507:29211, + 57508:29224, + 57509:29229, + 57510:29228, + 57511:29232, + 57512:29234, + 57513:29243, + 57514:29244, + 57515:29247, + 57516:29248, + 57517:29254, + 57518:29259, + 57519:29272, + 57520:29300, + 57521:29310, + 57522:29314, + 57523:29313, + 57524:29319, + 57525:29330, + 57526:29334, + 57527:29346, + 57528:29351, + 57529:29369, + 57530:29362, + 57531:29379, + 57532:29382, + 57533:29380, + 57534:29390, + 57535:29394, + 57536:29410, + 57537:29408, + 57538:29409, + 57539:29433, + 57540:29431, + 57541:20495, + 57542:29463, + 57543:29450, + 57544:29468, + 57545:29462, + 57546:29469, + 57547:29492, + 57548:29487, + 57549:29481, + 57550:29477, + 57551:29502, + 57552:29518, + 57553:29519, + 57554:40664, + 57555:29527, + 57556:29546, + 57557:29544, + 57558:29552, + 57559:29560, + 57560:29557, + 57561:29563, + 57562:29562, + 57563:29640, + 57564:29619, + 57565:29646, + 57566:29627, + 57567:29632, + 57568:29669, + 57569:29678, + 57570:29662, + 57571:29858, + 57572:29701, + 57573:29807, + 57574:29733, + 57575:29688, + 57576:29746, + 57577:29754, + 57578:29781, + 57579:29759, + 57580:29791, + 57581:29785, + 57582:29761, + 57583:29788, + 57584:29801, + 57585:29808, + 57586:29795, + 57587:29802, + 57588:29814, + 57589:29822, + 57590:29835, + 57591:29854, + 57592:29863, + 57593:29898, + 57594:29903, + 57595:29908, + 57596:29681, + 57664:29920, + 57665:29923, + 57666:29927, + 57667:29929, + 57668:29934, + 57669:29938, + 57670:29936, + 57671:29937, + 57672:29944, + 57673:29943, + 57674:29956, + 57675:29955, + 57676:29957, + 57677:29964, + 57678:29966, + 57679:29965, + 57680:29973, + 57681:29971, + 57682:29982, + 57683:29990, + 57684:29996, + 57685:30012, + 57686:30020, + 57687:30029, + 57688:30026, + 57689:30025, + 57690:30043, + 57691:30022, + 57692:30042, + 57693:30057, + 57694:30052, + 57695:30055, + 57696:30059, + 57697:30061, + 57698:30072, + 57699:30070, + 57700:30086, + 57701:30087, + 57702:30068, + 57703:30090, + 57704:30089, + 57705:30082, + 57706:30100, + 57707:30106, + 57708:30109, + 57709:30117, + 57710:30115, + 57711:30146, + 57712:30131, + 57713:30147, + 57714:30133, + 57715:30141, + 57716:30136, + 57717:30140, + 57718:30129, + 57719:30157, + 57720:30154, + 57721:30162, + 57722:30169, + 57723:30179, + 57724:30174, + 57725:30206, + 57726:30207, + 57728:30204, + 57729:30209, + 57730:30192, + 57731:30202, + 57732:30194, + 57733:30195, + 57734:30219, + 57735:30221, + 57736:30217, + 57737:30239, + 57738:30247, + 57739:30240, + 57740:30241, + 57741:30242, + 57742:30244, + 57743:30260, + 57744:30256, + 57745:30267, + 57746:30279, + 57747:30280, + 57748:30278, + 57749:30300, + 57750:30296, + 57751:30305, + 57752:30306, + 57753:30312, + 57754:30313, + 57755:30314, + 57756:30311, + 57757:30316, + 57758:30320, + 57759:30322, + 57760:30326, + 57761:30328, + 57762:30332, + 57763:30336, + 57764:30339, + 57765:30344, + 57766:30347, + 57767:30350, + 57768:30358, + 57769:30355, + 57770:30361, + 57771:30362, + 57772:30384, + 57773:30388, + 57774:30392, + 57775:30393, + 57776:30394, + 57777:30402, + 57778:30413, + 57779:30422, + 57780:30418, + 57781:30430, + 57782:30433, + 57783:30437, + 57784:30439, + 57785:30442, + 57786:34351, + 57787:30459, + 57788:30472, + 57789:30471, + 57790:30468, + 57791:30505, + 57792:30500, + 57793:30494, + 57794:30501, + 57795:30502, + 57796:30491, + 57797:30519, + 57798:30520, + 57799:30535, + 57800:30554, + 57801:30568, + 57802:30571, + 57803:30555, + 57804:30565, + 57805:30591, + 57806:30590, + 57807:30585, + 57808:30606, + 57809:30603, + 57810:30609, + 57811:30624, + 57812:30622, + 57813:30640, + 57814:30646, + 57815:30649, + 57816:30655, + 57817:30652, + 57818:30653, + 57819:30651, + 57820:30663, + 57821:30669, + 57822:30679, + 57823:30682, + 57824:30684, + 57825:30691, + 57826:30702, + 57827:30716, + 57828:30732, + 57829:30738, + 57830:31014, + 57831:30752, + 57832:31018, + 57833:30789, + 57834:30862, + 57835:30836, + 57836:30854, + 57837:30844, + 57838:30874, + 57839:30860, + 57840:30883, + 57841:30901, + 57842:30890, + 57843:30895, + 57844:30929, + 57845:30918, + 57846:30923, + 57847:30932, + 57848:30910, + 57849:30908, + 57850:30917, + 57851:30922, + 57852:30956, + 57920:30951, + 57921:30938, + 57922:30973, + 57923:30964, + 57924:30983, + 57925:30994, + 57926:30993, + 57927:31001, + 57928:31020, + 57929:31019, + 57930:31040, + 57931:31072, + 57932:31063, + 57933:31071, + 57934:31066, + 57935:31061, + 57936:31059, + 57937:31098, + 57938:31103, + 57939:31114, + 57940:31133, + 57941:31143, + 57942:40779, + 57943:31146, + 57944:31150, + 57945:31155, + 57946:31161, + 57947:31162, + 57948:31177, + 57949:31189, + 57950:31207, + 57951:31212, + 57952:31201, + 57953:31203, + 57954:31240, + 57955:31245, + 57956:31256, + 57957:31257, + 57958:31264, + 57959:31263, + 57960:31104, + 57961:31281, + 57962:31291, + 57963:31294, + 57964:31287, + 57965:31299, + 57966:31319, + 57967:31305, + 57968:31329, + 57969:31330, + 57970:31337, + 57971:40861, + 57972:31344, + 57973:31353, + 57974:31357, + 57975:31368, + 57976:31383, + 57977:31381, + 57978:31384, + 57979:31382, + 57980:31401, + 57981:31432, + 57982:31408, + 57984:31414, + 57985:31429, + 57986:31428, + 57987:31423, + 57988:36995, + 57989:31431, + 57990:31434, + 57991:31437, + 57992:31439, + 57993:31445, + 57994:31443, + 57995:31449, + 57996:31450, + 57997:31453, + 57998:31457, + 57999:31458, + 58000:31462, + 58001:31469, + 58002:31472, + 58003:31490, + 58004:31503, + 58005:31498, + 58006:31494, + 58007:31539, + 58008:31512, + 58009:31513, + 58010:31518, + 58011:31541, + 58012:31528, + 58013:31542, + 58014:31568, + 58015:31610, + 58016:31492, + 58017:31565, + 58018:31499, + 58019:31564, + 58020:31557, + 58021:31605, + 58022:31589, + 58023:31604, + 58024:31591, + 58025:31600, + 58026:31601, + 58027:31596, + 58028:31598, + 58029:31645, + 58030:31640, + 58031:31647, + 58032:31629, + 58033:31644, + 58034:31642, + 58035:31627, + 58036:31634, + 58037:31631, + 58038:31581, + 58039:31641, + 58040:31691, + 58041:31681, + 58042:31692, + 58043:31695, + 58044:31668, + 58045:31686, + 58046:31709, + 58047:31721, + 58048:31761, + 58049:31764, + 58050:31718, + 58051:31717, + 58052:31840, + 58053:31744, + 58054:31751, + 58055:31763, + 58056:31731, + 58057:31735, + 58058:31767, + 58059:31757, + 58060:31734, + 58061:31779, + 58062:31783, + 58063:31786, + 58064:31775, + 58065:31799, + 58066:31787, + 58067:31805, + 58068:31820, + 58069:31811, + 58070:31828, + 58071:31823, + 58072:31808, + 58073:31824, + 58074:31832, + 58075:31839, + 58076:31844, + 58077:31830, + 58078:31845, + 58079:31852, + 58080:31861, + 58081:31875, + 58082:31888, + 58083:31908, + 58084:31917, + 58085:31906, + 58086:31915, + 58087:31905, + 58088:31912, + 58089:31923, + 58090:31922, + 58091:31921, + 58092:31918, + 58093:31929, + 58094:31933, + 58095:31936, + 58096:31941, + 58097:31938, + 58098:31960, + 58099:31954, + 58100:31964, + 58101:31970, + 58102:39739, + 58103:31983, + 58104:31986, + 58105:31988, + 58106:31990, + 58107:31994, + 58108:32006, + 58176:32002, + 58177:32028, + 58178:32021, + 58179:32010, + 58180:32069, + 58181:32075, + 58182:32046, + 58183:32050, + 58184:32063, + 58185:32053, + 58186:32070, + 58187:32115, + 58188:32086, + 58189:32078, + 58190:32114, + 58191:32104, + 58192:32110, + 58193:32079, + 58194:32099, + 58195:32147, + 58196:32137, + 58197:32091, + 58198:32143, + 58199:32125, + 58200:32155, + 58201:32186, + 58202:32174, + 58203:32163, + 58204:32181, + 58205:32199, + 58206:32189, + 58207:32171, + 58208:32317, + 58209:32162, + 58210:32175, + 58211:32220, + 58212:32184, + 58213:32159, + 58214:32176, + 58215:32216, + 58216:32221, + 58217:32228, + 58218:32222, + 58219:32251, + 58220:32242, + 58221:32225, + 58222:32261, + 58223:32266, + 58224:32291, + 58225:32289, + 58226:32274, + 58227:32305, + 58228:32287, + 58229:32265, + 58230:32267, + 58231:32290, + 58232:32326, + 58233:32358, + 58234:32315, + 58235:32309, + 58236:32313, + 58237:32323, + 58238:32311, + 58240:32306, + 58241:32314, + 58242:32359, + 58243:32349, + 58244:32342, + 58245:32350, + 58246:32345, + 58247:32346, + 58248:32377, + 58249:32362, + 58250:32361, + 58251:32380, + 58252:32379, + 58253:32387, + 58254:32213, + 58255:32381, + 58256:36782, + 58257:32383, + 58258:32392, + 58259:32393, + 58260:32396, + 58261:32402, + 58262:32400, + 58263:32403, + 58264:32404, + 58265:32406, + 58266:32398, + 58267:32411, + 58268:32412, + 58269:32568, + 58270:32570, + 58271:32581, + 58272:32588, + 58273:32589, + 58274:32590, + 58275:32592, + 58276:32593, + 58277:32597, + 58278:32596, + 58279:32600, + 58280:32607, + 58281:32608, + 58282:32616, + 58283:32617, + 58284:32615, + 58285:32632, + 58286:32642, + 58287:32646, + 58288:32643, + 58289:32648, + 58290:32647, + 58291:32652, + 58292:32660, + 58293:32670, + 58294:32669, + 58295:32666, + 58296:32675, + 58297:32687, + 58298:32690, + 58299:32697, + 58300:32686, + 58301:32694, + 58302:32696, + 58303:35697, + 58304:32709, + 58305:32710, + 58306:32714, + 58307:32725, + 58308:32724, + 58309:32737, + 58310:32742, + 58311:32745, + 58312:32755, + 58313:32761, + 58314:39132, + 58315:32774, + 58316:32772, + 58317:32779, + 58318:32786, + 58319:32792, + 58320:32793, + 58321:32796, + 58322:32801, + 58323:32808, + 58324:32831, + 58325:32827, + 58326:32842, + 58327:32838, + 58328:32850, + 58329:32856, + 58330:32858, + 58331:32863, + 58332:32866, + 58333:32872, + 58334:32883, + 58335:32882, + 58336:32880, + 58337:32886, + 58338:32889, + 58339:32893, + 58340:32895, + 58341:32900, + 58342:32902, + 58343:32901, + 58344:32923, + 58345:32915, + 58346:32922, + 58347:32941, + 58348:20880, + 58349:32940, + 58350:32987, + 58351:32997, + 58352:32985, + 58353:32989, + 58354:32964, + 58355:32986, + 58356:32982, + 58357:33033, + 58358:33007, + 58359:33009, + 58360:33051, + 58361:33065, + 58362:33059, + 58363:33071, + 58364:33099, + 58432:38539, + 58433:33094, + 58434:33086, + 58435:33107, + 58436:33105, + 58437:33020, + 58438:33137, + 58439:33134, + 58440:33125, + 58441:33126, + 58442:33140, + 58443:33155, + 58444:33160, + 58445:33162, + 58446:33152, + 58447:33154, + 58448:33184, + 58449:33173, + 58450:33188, + 58451:33187, + 58452:33119, + 58453:33171, + 58454:33193, + 58455:33200, + 58456:33205, + 58457:33214, + 58458:33208, + 58459:33213, + 58460:33216, + 58461:33218, + 58462:33210, + 58463:33225, + 58464:33229, + 58465:33233, + 58466:33241, + 58467:33240, + 58468:33224, + 58469:33242, + 58470:33247, + 58471:33248, + 58472:33255, + 58473:33274, + 58474:33275, + 58475:33278, + 58476:33281, + 58477:33282, + 58478:33285, + 58479:33287, + 58480:33290, + 58481:33293, + 58482:33296, + 58483:33302, + 58484:33321, + 58485:33323, + 58486:33336, + 58487:33331, + 58488:33344, + 58489:33369, + 58490:33368, + 58491:33373, + 58492:33370, + 58493:33375, + 58494:33380, + 58496:33378, + 58497:33384, + 58498:33386, + 58499:33387, + 58500:33326, + 58501:33393, + 58502:33399, + 58503:33400, + 58504:33406, + 58505:33421, + 58506:33426, + 58507:33451, + 58508:33439, + 58509:33467, + 58510:33452, + 58511:33505, + 58512:33507, + 58513:33503, + 58514:33490, + 58515:33524, + 58516:33523, + 58517:33530, + 58518:33683, + 58519:33539, + 58520:33531, + 58521:33529, + 58522:33502, + 58523:33542, + 58524:33500, + 58525:33545, + 58526:33497, + 58527:33589, + 58528:33588, + 58529:33558, + 58530:33586, + 58531:33585, + 58532:33600, + 58533:33593, + 58534:33616, + 58535:33605, + 58536:33583, + 58537:33579, + 58538:33559, + 58539:33560, + 58540:33669, + 58541:33690, + 58542:33706, + 58543:33695, + 58544:33698, + 58545:33686, + 58546:33571, + 58547:33678, + 58548:33671, + 58549:33674, + 58550:33660, + 58551:33717, + 58552:33651, + 58553:33653, + 58554:33696, + 58555:33673, + 58556:33704, + 58557:33780, + 58558:33811, + 58559:33771, + 58560:33742, + 58561:33789, + 58562:33795, + 58563:33752, + 58564:33803, + 58565:33729, + 58566:33783, + 58567:33799, + 58568:33760, + 58569:33778, + 58570:33805, + 58571:33826, + 58572:33824, + 58573:33725, + 58574:33848, + 58575:34054, + 58576:33787, + 58577:33901, + 58578:33834, + 58579:33852, + 58580:34138, + 58581:33924, + 58582:33911, + 58583:33899, + 58584:33965, + 58585:33902, + 58586:33922, + 58587:33897, + 58588:33862, + 58589:33836, + 58590:33903, + 58591:33913, + 58592:33845, + 58593:33994, + 58594:33890, + 58595:33977, + 58596:33983, + 58597:33951, + 58598:34009, + 58599:33997, + 58600:33979, + 58601:34010, + 58602:34000, + 58603:33985, + 58604:33990, + 58605:34006, + 58606:33953, + 58607:34081, + 58608:34047, + 58609:34036, + 58610:34071, + 58611:34072, + 58612:34092, + 58613:34079, + 58614:34069, + 58615:34068, + 58616:34044, + 58617:34112, + 58618:34147, + 58619:34136, + 58620:34120, + 58688:34113, + 58689:34306, + 58690:34123, + 58691:34133, + 58692:34176, + 58693:34212, + 58694:34184, + 58695:34193, + 58696:34186, + 58697:34216, + 58698:34157, + 58699:34196, + 58700:34203, + 58701:34282, + 58702:34183, + 58703:34204, + 58704:34167, + 58705:34174, + 58706:34192, + 58707:34249, + 58708:34234, + 58709:34255, + 58710:34233, + 58711:34256, + 58712:34261, + 58713:34269, + 58714:34277, + 58715:34268, + 58716:34297, + 58717:34314, + 58718:34323, + 58719:34315, + 58720:34302, + 58721:34298, + 58722:34310, + 58723:34338, + 58724:34330, + 58725:34352, + 58726:34367, + 58727:34381, + 58728:20053, + 58729:34388, + 58730:34399, + 58731:34407, + 58732:34417, + 58733:34451, + 58734:34467, + 58735:34473, + 58736:34474, + 58737:34443, + 58738:34444, + 58739:34486, + 58740:34479, + 58741:34500, + 58742:34502, + 58743:34480, + 58744:34505, + 58745:34851, + 58746:34475, + 58747:34516, + 58748:34526, + 58749:34537, + 58750:34540, + 58752:34527, + 58753:34523, + 58754:34543, + 58755:34578, + 58756:34566, + 58757:34568, + 58758:34560, + 58759:34563, + 58760:34555, + 58761:34577, + 58762:34569, + 58763:34573, + 58764:34553, + 58765:34570, + 58766:34612, + 58767:34623, + 58768:34615, + 58769:34619, + 58770:34597, + 58771:34601, + 58772:34586, + 58773:34656, + 58774:34655, + 58775:34680, + 58776:34636, + 58777:34638, + 58778:34676, + 58779:34647, + 58780:34664, + 58781:34670, + 58782:34649, + 58783:34643, + 58784:34659, + 58785:34666, + 58786:34821, + 58787:34722, + 58788:34719, + 58789:34690, + 58790:34735, + 58791:34763, + 58792:34749, + 58793:34752, + 58794:34768, + 58795:38614, + 58796:34731, + 58797:34756, + 58798:34739, + 58799:34759, + 58800:34758, + 58801:34747, + 58802:34799, + 58803:34802, + 58804:34784, + 58805:34831, + 58806:34829, + 58807:34814, + 58808:34806, + 58809:34807, + 58810:34830, + 58811:34770, + 58812:34833, + 58813:34838, + 58814:34837, + 58815:34850, + 58816:34849, + 58817:34865, + 58818:34870, + 58819:34873, + 58820:34855, + 58821:34875, + 58822:34884, + 58823:34882, + 58824:34898, + 58825:34905, + 58826:34910, + 58827:34914, + 58828:34923, + 58829:34945, + 58830:34942, + 58831:34974, + 58832:34933, + 58833:34941, + 58834:34997, + 58835:34930, + 58836:34946, + 58837:34967, + 58838:34962, + 58839:34990, + 58840:34969, + 58841:34978, + 58842:34957, + 58843:34980, + 58844:34992, + 58845:35007, + 58846:34993, + 58847:35011, + 58848:35012, + 58849:35028, + 58850:35032, + 58851:35033, + 58852:35037, + 58853:35065, + 58854:35074, + 58855:35068, + 58856:35060, + 58857:35048, + 58858:35058, + 58859:35076, + 58860:35084, + 58861:35082, + 58862:35091, + 58863:35139, + 58864:35102, + 58865:35109, + 58866:35114, + 58867:35115, + 58868:35137, + 58869:35140, + 58870:35131, + 58871:35126, + 58872:35128, + 58873:35148, + 58874:35101, + 58875:35168, + 58876:35166, + 58944:35174, + 58945:35172, + 58946:35181, + 58947:35178, + 58948:35183, + 58949:35188, + 58950:35191, + 58951:35198, + 58952:35203, + 58953:35208, + 58954:35210, + 58955:35219, + 58956:35224, + 58957:35233, + 58958:35241, + 58959:35238, + 58960:35244, + 58961:35247, + 58962:35250, + 58963:35258, + 58964:35261, + 58965:35263, + 58966:35264, + 58967:35290, + 58968:35292, + 58969:35293, + 58970:35303, + 58971:35316, + 58972:35320, + 58973:35331, + 58974:35350, + 58975:35344, + 58976:35340, + 58977:35355, + 58978:35357, + 58979:35365, + 58980:35382, + 58981:35393, + 58982:35419, + 58983:35410, + 58984:35398, + 58985:35400, + 58986:35452, + 58987:35437, + 58988:35436, + 58989:35426, + 58990:35461, + 58991:35458, + 58992:35460, + 58993:35496, + 58994:35489, + 58995:35473, + 58996:35493, + 58997:35494, + 58998:35482, + 58999:35491, + 59000:35524, + 59001:35533, + 59002:35522, + 59003:35546, + 59004:35563, + 59005:35571, + 59006:35559, + 59008:35556, + 59009:35569, + 59010:35604, + 59011:35552, + 59012:35554, + 59013:35575, + 59014:35550, + 59015:35547, + 59016:35596, + 59017:35591, + 59018:35610, + 59019:35553, + 59020:35606, + 59021:35600, + 59022:35607, + 59023:35616, + 59024:35635, + 59025:38827, + 59026:35622, + 59027:35627, + 59028:35646, + 59029:35624, + 59030:35649, + 59031:35660, + 59032:35663, + 59033:35662, + 59034:35657, + 59035:35670, + 59036:35675, + 59037:35674, + 59038:35691, + 59039:35679, + 59040:35692, + 59041:35695, + 59042:35700, + 59043:35709, + 59044:35712, + 59045:35724, + 59046:35726, + 59047:35730, + 59048:35731, + 59049:35734, + 59050:35737, + 59051:35738, + 59052:35898, + 59053:35905, + 59054:35903, + 59055:35912, + 59056:35916, + 59057:35918, + 59058:35920, + 59059:35925, + 59060:35938, + 59061:35948, + 59062:35960, + 59063:35962, + 59064:35970, + 59065:35977, + 59066:35973, + 59067:35978, + 59068:35981, + 59069:35982, + 59070:35988, + 59071:35964, + 59072:35992, + 59073:25117, + 59074:36013, + 59075:36010, + 59076:36029, + 59077:36018, + 59078:36019, + 59079:36014, + 59080:36022, + 59081:36040, + 59082:36033, + 59083:36068, + 59084:36067, + 59085:36058, + 59086:36093, + 59087:36090, + 59088:36091, + 59089:36100, + 59090:36101, + 59091:36106, + 59092:36103, + 59093:36111, + 59094:36109, + 59095:36112, + 59096:40782, + 59097:36115, + 59098:36045, + 59099:36116, + 59100:36118, + 59101:36199, + 59102:36205, + 59103:36209, + 59104:36211, + 59105:36225, + 59106:36249, + 59107:36290, + 59108:36286, + 59109:36282, + 59110:36303, + 59111:36314, + 59112:36310, + 59113:36300, + 59114:36315, + 59115:36299, + 59116:36330, + 59117:36331, + 59118:36319, + 59119:36323, + 59120:36348, + 59121:36360, + 59122:36361, + 59123:36351, + 59124:36381, + 59125:36382, + 59126:36368, + 59127:36383, + 59128:36418, + 59129:36405, + 59130:36400, + 59131:36404, + 59132:36426, + 59200:36423, + 59201:36425, + 59202:36428, + 59203:36432, + 59204:36424, + 59205:36441, + 59206:36452, + 59207:36448, + 59208:36394, + 59209:36451, + 59210:36437, + 59211:36470, + 59212:36466, + 59213:36476, + 59214:36481, + 59215:36487, + 59216:36485, + 59217:36484, + 59218:36491, + 59219:36490, + 59220:36499, + 59221:36497, + 59222:36500, + 59223:36505, + 59224:36522, + 59225:36513, + 59226:36524, + 59227:36528, + 59228:36550, + 59229:36529, + 59230:36542, + 59231:36549, + 59232:36552, + 59233:36555, + 59234:36571, + 59235:36579, + 59236:36604, + 59237:36603, + 59238:36587, + 59239:36606, + 59240:36618, + 59241:36613, + 59242:36629, + 59243:36626, + 59244:36633, + 59245:36627, + 59246:36636, + 59247:36639, + 59248:36635, + 59249:36620, + 59250:36646, + 59251:36659, + 59252:36667, + 59253:36665, + 59254:36677, + 59255:36674, + 59256:36670, + 59257:36684, + 59258:36681, + 59259:36678, + 59260:36686, + 59261:36695, + 59262:36700, + 59264:36706, + 59265:36707, + 59266:36708, + 59267:36764, + 59268:36767, + 59269:36771, + 59270:36781, + 59271:36783, + 59272:36791, + 59273:36826, + 59274:36837, + 59275:36834, + 59276:36842, + 59277:36847, + 59278:36999, + 59279:36852, + 59280:36869, + 59281:36857, + 59282:36858, + 59283:36881, + 59284:36885, + 59285:36897, + 59286:36877, + 59287:36894, + 59288:36886, + 59289:36875, + 59290:36903, + 59291:36918, + 59292:36917, + 59293:36921, + 59294:36856, + 59295:36943, + 59296:36944, + 59297:36945, + 59298:36946, + 59299:36878, + 59300:36937, + 59301:36926, + 59302:36950, + 59303:36952, + 59304:36958, + 59305:36968, + 59306:36975, + 59307:36982, + 59308:38568, + 59309:36978, + 59310:36994, + 59311:36989, + 59312:36993, + 59313:36992, + 59314:37002, + 59315:37001, + 59316:37007, + 59317:37032, + 59318:37039, + 59319:37041, + 59320:37045, + 59321:37090, + 59322:37092, + 59323:25160, + 59324:37083, + 59325:37122, + 59326:37138, + 59327:37145, + 59328:37170, + 59329:37168, + 59330:37194, + 59331:37206, + 59332:37208, + 59333:37219, + 59334:37221, + 59335:37225, + 59336:37235, + 59337:37234, + 59338:37259, + 59339:37257, + 59340:37250, + 59341:37282, + 59342:37291, + 59343:37295, + 59344:37290, + 59345:37301, + 59346:37300, + 59347:37306, + 59348:37312, + 59349:37313, + 59350:37321, + 59351:37323, + 59352:37328, + 59353:37334, + 59354:37343, + 59355:37345, + 59356:37339, + 59357:37372, + 59358:37365, + 59359:37366, + 59360:37406, + 59361:37375, + 59362:37396, + 59363:37420, + 59364:37397, + 59365:37393, + 59366:37470, + 59367:37463, + 59368:37445, + 59369:37449, + 59370:37476, + 59371:37448, + 59372:37525, + 59373:37439, + 59374:37451, + 59375:37456, + 59376:37532, + 59377:37526, + 59378:37523, + 59379:37531, + 59380:37466, + 59381:37583, + 59382:37561, + 59383:37559, + 59384:37609, + 59385:37647, + 59386:37626, + 59387:37700, + 59388:37678, + 59456:37657, + 59457:37666, + 59458:37658, + 59459:37667, + 59460:37690, + 59461:37685, + 59462:37691, + 59463:37724, + 59464:37728, + 59465:37756, + 59466:37742, + 59467:37718, + 59468:37808, + 59469:37804, + 59470:37805, + 59471:37780, + 59472:37817, + 59473:37846, + 59474:37847, + 59475:37864, + 59476:37861, + 59477:37848, + 59478:37827, + 59479:37853, + 59480:37840, + 59481:37832, + 59482:37860, + 59483:37914, + 59484:37908, + 59485:37907, + 59486:37891, + 59487:37895, + 59488:37904, + 59489:37942, + 59490:37931, + 59491:37941, + 59492:37921, + 59493:37946, + 59494:37953, + 59495:37970, + 59496:37956, + 59497:37979, + 59498:37984, + 59499:37986, + 59500:37982, + 59501:37994, + 59502:37417, + 59503:38000, + 59504:38005, + 59505:38007, + 59506:38013, + 59507:37978, + 59508:38012, + 59509:38014, + 59510:38017, + 59511:38015, + 59512:38274, + 59513:38279, + 59514:38282, + 59515:38292, + 59516:38294, + 59517:38296, + 59518:38297, + 59520:38304, + 59521:38312, + 59522:38311, + 59523:38317, + 59524:38332, + 59525:38331, + 59526:38329, + 59527:38334, + 59528:38346, + 59529:28662, + 59530:38339, + 59531:38349, + 59532:38348, + 59533:38357, + 59534:38356, + 59535:38358, + 59536:38364, + 59537:38369, + 59538:38373, + 59539:38370, + 59540:38433, + 59541:38440, + 59542:38446, + 59543:38447, + 59544:38466, + 59545:38476, + 59546:38479, + 59547:38475, + 59548:38519, + 59549:38492, + 59550:38494, + 59551:38493, + 59552:38495, + 59553:38502, + 59554:38514, + 59555:38508, + 59556:38541, + 59557:38552, + 59558:38549, + 59559:38551, + 59560:38570, + 59561:38567, + 59562:38577, + 59563:38578, + 59564:38576, + 59565:38580, + 59566:38582, + 59567:38584, + 59568:38585, + 59569:38606, + 59570:38603, + 59571:38601, + 59572:38605, + 59573:35149, + 59574:38620, + 59575:38669, + 59576:38613, + 59577:38649, + 59578:38660, + 59579:38662, + 59580:38664, + 59581:38675, + 59582:38670, + 59583:38673, + 59584:38671, + 59585:38678, + 59586:38681, + 59587:38692, + 59588:38698, + 59589:38704, + 59590:38713, + 59591:38717, + 59592:38718, + 59593:38724, + 59594:38726, + 59595:38728, + 59596:38722, + 59597:38729, + 59598:38748, + 59599:38752, + 59600:38756, + 59601:38758, + 59602:38760, + 59603:21202, + 59604:38763, + 59605:38769, + 59606:38777, + 59607:38789, + 59608:38780, + 59609:38785, + 59610:38778, + 59611:38790, + 59612:38795, + 59613:38799, + 59614:38800, + 59615:38812, + 59616:38824, + 59617:38822, + 59618:38819, + 59619:38835, + 59620:38836, + 59621:38851, + 59622:38854, + 59623:38856, + 59624:38859, + 59625:38876, + 59626:38893, + 59627:40783, + 59628:38898, + 59629:31455, + 59630:38902, + 59631:38901, + 59632:38927, + 59633:38924, + 59634:38968, + 59635:38948, + 59636:38945, + 59637:38967, + 59638:38973, + 59639:38982, + 59640:38991, + 59641:38987, + 59642:39019, + 59643:39023, + 59644:39024, + 59712:39025, + 59713:39028, + 59714:39027, + 59715:39082, + 59716:39087, + 59717:39089, + 59718:39094, + 59719:39108, + 59720:39107, + 59721:39110, + 59722:39145, + 59723:39147, + 59724:39171, + 59725:39177, + 59726:39186, + 59727:39188, + 59728:39192, + 59729:39201, + 59730:39197, + 59731:39198, + 59732:39204, + 59733:39200, + 59734:39212, + 59735:39214, + 59736:39229, + 59737:39230, + 59738:39234, + 59739:39241, + 59740:39237, + 59741:39248, + 59742:39243, + 59743:39249, + 59744:39250, + 59745:39244, + 59746:39253, + 59747:39319, + 59748:39320, + 59749:39333, + 59750:39341, + 59751:39342, + 59752:39356, + 59753:39391, + 59754:39387, + 59755:39389, + 59756:39384, + 59757:39377, + 59758:39405, + 59759:39406, + 59760:39409, + 59761:39410, + 59762:39419, + 59763:39416, + 59764:39425, + 59765:39439, + 59766:39429, + 59767:39394, + 59768:39449, + 59769:39467, + 59770:39479, + 59771:39493, + 59772:39490, + 59773:39488, + 59774:39491, + 59776:39486, + 59777:39509, + 59778:39501, + 59779:39515, + 59780:39511, + 59781:39519, + 59782:39522, + 59783:39525, + 59784:39524, + 59785:39529, + 59786:39531, + 59787:39530, + 59788:39597, + 59789:39600, + 59790:39612, + 59791:39616, + 59792:39631, + 59793:39633, + 59794:39635, + 59795:39636, + 59796:39646, + 59797:39647, + 59798:39650, + 59799:39651, + 59800:39654, + 59801:39663, + 59802:39659, + 59803:39662, + 59804:39668, + 59805:39665, + 59806:39671, + 59807:39675, + 59808:39686, + 59809:39704, + 59810:39706, + 59811:39711, + 59812:39714, + 59813:39715, + 59814:39717, + 59815:39719, + 59816:39720, + 59817:39721, + 59818:39722, + 59819:39726, + 59820:39727, + 59821:39730, + 59822:39748, + 59823:39747, + 59824:39759, + 59825:39757, + 59826:39758, + 59827:39761, + 59828:39768, + 59829:39796, + 59830:39827, + 59831:39811, + 59832:39825, + 59833:39830, + 59834:39831, + 59835:39839, + 59836:39840, + 59837:39848, + 59838:39860, + 59839:39872, + 59840:39882, + 59841:39865, + 59842:39878, + 59843:39887, + 59844:39889, + 59845:39890, + 59846:39907, + 59847:39906, + 59848:39908, + 59849:39892, + 59850:39905, + 59851:39994, + 59852:39922, + 59853:39921, + 59854:39920, + 59855:39957, + 59856:39956, + 59857:39945, + 59858:39955, + 59859:39948, + 59860:39942, + 59861:39944, + 59862:39954, + 59863:39946, + 59864:39940, + 59865:39982, + 59866:39963, + 59867:39973, + 59868:39972, + 59869:39969, + 59870:39984, + 59871:40007, + 59872:39986, + 59873:40006, + 59874:39998, + 59875:40026, + 59876:40032, + 59877:40039, + 59878:40054, + 59879:40056, + 59880:40167, + 59881:40172, + 59882:40176, + 59883:40201, + 59884:40200, + 59885:40171, + 59886:40195, + 59887:40198, + 59888:40234, + 59889:40230, + 59890:40367, + 59891:40227, + 59892:40223, + 59893:40260, + 59894:40213, + 59895:40210, + 59896:40257, + 59897:40255, + 59898:40254, + 59899:40262, + 59900:40264, + 59968:40285, + 59969:40286, + 59970:40292, + 59971:40273, + 59972:40272, + 59973:40281, + 59974:40306, + 59975:40329, + 59976:40327, + 59977:40363, + 59978:40303, + 59979:40314, + 59980:40346, + 59981:40356, + 59982:40361, + 59983:40370, + 59984:40388, + 59985:40385, + 59986:40379, + 59987:40376, + 59988:40378, + 59989:40390, + 59990:40399, + 59991:40386, + 59992:40409, + 59993:40403, + 59994:40440, + 59995:40422, + 59996:40429, + 59997:40431, + 59998:40445, + 59999:40474, + 60000:40475, + 60001:40478, + 60002:40565, + 60003:40569, + 60004:40573, + 60005:40577, + 60006:40584, + 60007:40587, + 60008:40588, + 60009:40594, + 60010:40597, + 60011:40593, + 60012:40605, + 60013:40613, + 60014:40617, + 60015:40632, + 60016:40618, + 60017:40621, + 60018:38753, + 60019:40652, + 60020:40654, + 60021:40655, + 60022:40656, + 60023:40660, + 60024:40668, + 60025:40670, + 60026:40669, + 60027:40672, + 60028:40677, + 60029:40680, + 60030:40687, + 60032:40692, + 60033:40694, + 60034:40695, + 60035:40697, + 60036:40699, + 60037:40700, + 60038:40701, + 60039:40711, + 60040:40712, + 60041:30391, + 60042:40725, + 60043:40737, + 60044:40748, + 60045:40766, + 60046:40778, + 60047:40786, + 60048:40788, + 60049:40803, + 60050:40799, + 60051:40800, + 60052:40801, + 60053:40806, + 60054:40807, + 60055:40812, + 60056:40810, + 60057:40823, + 60058:40818, + 60059:40822, + 60060:40853, + 60061:40860, + 60062:40864, + 60063:22575, + 60064:27079, + 60065:36953, + 60066:29796, + 60067:20956, + 60068:29081, + 60736:32394, + 60737:35100, + 60738:37704, + 60739:37512, + 60740:34012, + 60741:20425, + 60742:28859, + 60743:26161, + 60744:26824, + 60745:37625, + 60746:26363, + 60747:24389, + 60748:20008, + 60749:20193, + 60750:20220, + 60751:20224, + 60752:20227, + 60753:20281, + 60754:20310, + 60755:20370, + 60756:20362, + 60757:20378, + 60758:20372, + 60759:20429, + 60760:20544, + 60761:20514, + 60762:20479, + 60763:20510, + 60764:20550, + 60765:20592, + 60766:20546, + 60767:20628, + 60768:20724, + 60769:20696, + 60770:20810, + 60771:20836, + 60772:20893, + 60773:20926, + 60774:20972, + 60775:21013, + 60776:21148, + 60777:21158, + 60778:21184, + 60779:21211, + 60780:21248, + 60781:21255, + 60782:21284, + 60783:21362, + 60784:21395, + 60785:21426, + 60786:21469, + 60787:64014, + 60788:21660, + 60789:21642, + 60790:21673, + 60791:21759, + 60792:21894, + 60793:22361, + 60794:22373, + 60795:22444, + 60796:22472, + 60797:22471, + 60798:64015, + 60800:64016, + 60801:22686, + 60802:22706, + 60803:22795, + 60804:22867, + 60805:22875, + 60806:22877, + 60807:22883, + 60808:22948, + 60809:22970, + 60810:23382, + 60811:23488, + 60812:29999, + 60813:23512, + 60814:23532, + 60815:23582, + 60816:23718, + 60817:23738, + 60818:23797, + 60819:23847, + 60820:23891, + 60821:64017, + 60822:23874, + 60823:23917, + 60824:23992, + 60825:23993, + 60826:24016, + 60827:24353, + 60828:24372, + 60829:24423, + 60830:24503, + 60831:24542, + 60832:24669, + 60833:24709, + 60834:24714, + 60835:24798, + 60836:24789, + 60837:24864, + 60838:24818, + 60839:24849, + 60840:24887, + 60841:24880, + 60842:24984, + 60843:25107, + 60844:25254, + 60845:25589, + 60846:25696, + 60847:25757, + 60848:25806, + 60849:25934, + 60850:26112, + 60851:26133, + 60852:26171, + 60853:26121, + 60854:26158, + 60855:26142, + 60856:26148, + 60857:26213, + 60858:26199, + 60859:26201, + 60860:64018, + 60861:26227, + 60862:26265, + 60863:26272, + 60864:26290, + 60865:26303, + 60866:26362, + 60867:26382, + 60868:63785, + 60869:26470, + 60870:26555, + 60871:26706, + 60872:26560, + 60873:26625, + 60874:26692, + 60875:26831, + 60876:64019, + 60877:26984, + 60878:64020, + 60879:27032, + 60880:27106, + 60881:27184, + 60882:27243, + 60883:27206, + 60884:27251, + 60885:27262, + 60886:27362, + 60887:27364, + 60888:27606, + 60889:27711, + 60890:27740, + 60891:27782, + 60892:27759, + 60893:27866, + 60894:27908, + 60895:28039, + 60896:28015, + 60897:28054, + 60898:28076, + 60899:28111, + 60900:28152, + 60901:28146, + 60902:28156, + 60903:28217, + 60904:28252, + 60905:28199, + 60906:28220, + 60907:28351, + 60908:28552, + 60909:28597, + 60910:28661, + 60911:28677, + 60912:28679, + 60913:28712, + 60914:28805, + 60915:28843, + 60916:28943, + 60917:28932, + 60918:29020, + 60919:28998, + 60920:28999, + 60921:64021, + 60922:29121, + 60923:29182, + 60924:29361, + 60992:29374, + 60993:29476, + 60994:64022, + 60995:29559, + 60996:29629, + 60997:29641, + 60998:29654, + 60999:29667, + 61000:29650, + 61001:29703, + 61002:29685, + 61003:29734, + 61004:29738, + 61005:29737, + 61006:29742, + 61007:29794, + 61008:29833, + 61009:29855, + 61010:29953, + 61011:30063, + 61012:30338, + 61013:30364, + 61014:30366, + 61015:30363, + 61016:30374, + 61017:64023, + 61018:30534, + 61019:21167, + 61020:30753, + 61021:30798, + 61022:30820, + 61023:30842, + 61024:31024, + 61025:64024, + 61026:64025, + 61027:64026, + 61028:31124, + 61029:64027, + 61030:31131, + 61031:31441, + 61032:31463, + 61033:64028, + 61034:31467, + 61035:31646, + 61036:64029, + 61037:32072, + 61038:32092, + 61039:32183, + 61040:32160, + 61041:32214, + 61042:32338, + 61043:32583, + 61044:32673, + 61045:64030, + 61046:33537, + 61047:33634, + 61048:33663, + 61049:33735, + 61050:33782, + 61051:33864, + 61052:33972, + 61053:34131, + 61054:34137, + 61056:34155, + 61057:64031, + 61058:34224, + 61059:64032, + 61060:64033, + 61061:34823, + 61062:35061, + 61063:35346, + 61064:35383, + 61065:35449, + 61066:35495, + 61067:35518, + 61068:35551, + 61069:64034, + 61070:35574, + 61071:35667, + 61072:35711, + 61073:36080, + 61074:36084, + 61075:36114, + 61076:36214, + 61077:64035, + 61078:36559, + 61079:64036, + 61080:64037, + 61081:36967, + 61082:37086, + 61083:64038, + 61084:37141, + 61085:37159, + 61086:37338, + 61087:37335, + 61088:37342, + 61089:37357, + 61090:37358, + 61091:37348, + 61092:37349, + 61093:37382, + 61094:37392, + 61095:37386, + 61096:37434, + 61097:37440, + 61098:37436, + 61099:37454, + 61100:37465, + 61101:37457, + 61102:37433, + 61103:37479, + 61104:37543, + 61105:37495, + 61106:37496, + 61107:37607, + 61108:37591, + 61109:37593, + 61110:37584, + 61111:64039, + 61112:37589, + 61113:37600, + 61114:37587, + 61115:37669, + 61116:37665, + 61117:37627, + 61118:64040, + 61119:37662, + 61120:37631, + 61121:37661, + 61122:37634, + 61123:37744, + 61124:37719, + 61125:37796, + 61126:37830, + 61127:37854, + 61128:37880, + 61129:37937, + 61130:37957, + 61131:37960, + 61132:38290, + 61133:63964, + 61134:64041, + 61135:38557, + 61136:38575, + 61137:38707, + 61138:38715, + 61139:38723, + 61140:38733, + 61141:38735, + 61142:38737, + 61143:38741, + 61144:38999, + 61145:39013, + 61146:64042, + 61147:64043, + 61148:39207, + 61149:64044, + 61150:39326, + 61151:39502, + 61152:39641, + 61153:39644, + 61154:39797, + 61155:39794, + 61156:39823, + 61157:39857, + 61158:39867, + 61159:39936, + 61160:40304, + 61161:40299, + 61162:64045, + 61163:40473, + 61164:40657, + 61167:8560, + 61168:8561, + 61169:8562, + 61170:8563, + 61171:8564, + 61172:8565, + 61173:8566, + 61174:8567, + 61175:8568, + 61176:8569, + 61177:65506, + 61178:65508, + 61179:65287, + 61180:65282, + 61504:57344, + 61505:57345, + 61506:57346, + 61507:57347, + 61508:57348, + 61509:57349, + 61510:57350, + 61511:57351, + 61512:57352, + 61513:57353, + 61514:57354, + 61515:57355, + 61516:57356, + 61517:57357, + 61518:57358, + 61519:57359, + 61520:57360, + 61521:57361, + 61522:57362, + 61523:57363, + 61524:57364, + 61525:57365, + 61526:57366, + 61527:57367, + 61528:57368, + 61529:57369, + 61530:57370, + 61531:57371, + 61532:57372, + 61533:57373, + 61534:57374, + 61535:57375, + 61536:57376, + 61537:57377, + 61538:57378, + 61539:57379, + 61540:57380, + 61541:57381, + 61542:57382, + 61543:57383, + 61544:57384, + 61545:57385, + 61546:57386, + 61547:57387, + 61548:57388, + 61549:57389, + 61550:57390, + 61551:57391, + 61552:57392, + 61553:57393, + 61554:57394, + 61555:57395, + 61556:57396, + 61557:57397, + 61558:57398, + 61559:57399, + 61560:57400, + 61561:57401, + 61562:57402, + 61563:57403, + 61564:57404, + 61565:57405, + 61566:57406, + 61568:57407, + 61569:57408, + 61570:57409, + 61571:57410, + 61572:57411, + 61573:57412, + 61574:57413, + 61575:57414, + 61576:57415, + 61577:57416, + 61578:57417, + 61579:57418, + 61580:57419, + 61581:57420, + 61582:57421, + 61583:57422, + 61584:57423, + 61585:57424, + 61586:57425, + 61587:57426, + 61588:57427, + 61589:57428, + 61590:57429, + 61591:57430, + 61592:57431, + 61593:57432, + 61594:57433, + 61595:57434, + 61596:57435, + 61597:57436, + 61598:57437, + 61599:57438, + 61600:57439, + 61601:57440, + 61602:57441, + 61603:57442, + 61604:57443, + 61605:57444, + 61606:57445, + 61607:57446, + 61608:57447, + 61609:57448, + 61610:57449, + 61611:57450, + 61612:57451, + 61613:57452, + 61614:57453, + 61615:57454, + 61616:57455, + 61617:57456, + 61618:57457, + 61619:57458, + 61620:57459, + 61621:57460, + 61622:57461, + 61623:57462, + 61624:57463, + 61625:57464, + 61626:57465, + 61627:57466, + 61628:57467, + 61629:57468, + 61630:57469, + 61631:57470, + 61632:57471, + 61633:57472, + 61634:57473, + 61635:57474, + 61636:57475, + 61637:57476, + 61638:57477, + 61639:57478, + 61640:57479, + 61641:57480, + 61642:57481, + 61643:57482, + 61644:57483, + 61645:57484, + 61646:57485, + 61647:57486, + 61648:57487, + 61649:57488, + 61650:57489, + 61651:57490, + 61652:57491, + 61653:57492, + 61654:57493, + 61655:57494, + 61656:57495, + 61657:57496, + 61658:57497, + 61659:57498, + 61660:57499, + 61661:57500, + 61662:57501, + 61663:57502, + 61664:57503, + 61665:57504, + 61666:57505, + 61667:57506, + 61668:57507, + 61669:57508, + 61670:57509, + 61671:57510, + 61672:57511, + 61673:57512, + 61674:57513, + 61675:57514, + 61676:57515, + 61677:57516, + 61678:57517, + 61679:57518, + 61680:57519, + 61681:57520, + 61682:57521, + 61683:57522, + 61684:57523, + 61685:57524, + 61686:57525, + 61687:57526, + 61688:57527, + 61689:57528, + 61690:57529, + 61691:57530, + 61692:57531, + 61760:57532, + 61761:57533, + 61762:57534, + 61763:57535, + 61764:57536, + 61765:57537, + 61766:57538, + 61767:57539, + 61768:57540, + 61769:57541, + 61770:57542, + 61771:57543, + 61772:57544, + 61773:57545, + 61774:57546, + 61775:57547, + 61776:57548, + 61777:57549, + 61778:57550, + 61779:57551, + 61780:57552, + 61781:57553, + 61782:57554, + 61783:57555, + 61784:57556, + 61785:57557, + 61786:57558, + 61787:57559, + 61788:57560, + 61789:57561, + 61790:57562, + 61791:57563, + 61792:57564, + 61793:57565, + 61794:57566, + 61795:57567, + 61796:57568, + 61797:57569, + 61798:57570, + 61799:57571, + 61800:57572, + 61801:57573, + 61802:57574, + 61803:57575, + 61804:57576, + 61805:57577, + 61806:57578, + 61807:57579, + 61808:57580, + 61809:57581, + 61810:57582, + 61811:57583, + 61812:57584, + 61813:57585, + 61814:57586, + 61815:57587, + 61816:57588, + 61817:57589, + 61818:57590, + 61819:57591, + 61820:57592, + 61821:57593, + 61822:57594, + 61824:57595, + 61825:57596, + 61826:57597, + 61827:57598, + 61828:57599, + 61829:57600, + 61830:57601, + 61831:57602, + 61832:57603, + 61833:57604, + 61834:57605, + 61835:57606, + 61836:57607, + 61837:57608, + 61838:57609, + 61839:57610, + 61840:57611, + 61841:57612, + 61842:57613, + 61843:57614, + 61844:57615, + 61845:57616, + 61846:57617, + 61847:57618, + 61848:57619, + 61849:57620, + 61850:57621, + 61851:57622, + 61852:57623, + 61853:57624, + 61854:57625, + 61855:57626, + 61856:57627, + 61857:57628, + 61858:57629, + 61859:57630, + 61860:57631, + 61861:57632, + 61862:57633, + 61863:57634, + 61864:57635, + 61865:57636, + 61866:57637, + 61867:57638, + 61868:57639, + 61869:57640, + 61870:57641, + 61871:57642, + 61872:57643, + 61873:57644, + 61874:57645, + 61875:57646, + 61876:57647, + 61877:57648, + 61878:57649, + 61879:57650, + 61880:57651, + 61881:57652, + 61882:57653, + 61883:57654, + 61884:57655, + 61885:57656, + 61886:57657, + 61887:57658, + 61888:57659, + 61889:57660, + 61890:57661, + 61891:57662, + 61892:57663, + 61893:57664, + 61894:57665, + 61895:57666, + 61896:57667, + 61897:57668, + 61898:57669, + 61899:57670, + 61900:57671, + 61901:57672, + 61902:57673, + 61903:57674, + 61904:57675, + 61905:57676, + 61906:57677, + 61907:57678, + 61908:57679, + 61909:57680, + 61910:57681, + 61911:57682, + 61912:57683, + 61913:57684, + 61914:57685, + 61915:57686, + 61916:57687, + 61917:57688, + 61918:57689, + 61919:57690, + 61920:57691, + 61921:57692, + 61922:57693, + 61923:57694, + 61924:57695, + 61925:57696, + 61926:57697, + 61927:57698, + 61928:57699, + 61929:57700, + 61930:57701, + 61931:57702, + 61932:57703, + 61933:57704, + 61934:57705, + 61935:57706, + 61936:57707, + 61937:57708, + 61938:57709, + 61939:57710, + 61940:57711, + 61941:57712, + 61942:57713, + 61943:57714, + 61944:57715, + 61945:57716, + 61946:57717, + 61947:57718, + 61948:57719, + 62016:57720, + 62017:57721, + 62018:57722, + 62019:57723, + 62020:57724, + 62021:57725, + 62022:57726, + 62023:57727, + 62024:57728, + 62025:57729, + 62026:57730, + 62027:57731, + 62028:57732, + 62029:57733, + 62030:57734, + 62031:57735, + 62032:57736, + 62033:57737, + 62034:57738, + 62035:57739, + 62036:57740, + 62037:57741, + 62038:57742, + 62039:57743, + 62040:57744, + 62041:57745, + 62042:57746, + 62043:57747, + 62044:57748, + 62045:57749, + 62046:57750, + 62047:57751, + 62048:57752, + 62049:57753, + 62050:57754, + 62051:57755, + 62052:57756, + 62053:57757, + 62054:57758, + 62055:57759, + 62056:57760, + 62057:57761, + 62058:57762, + 62059:57763, + 62060:57764, + 62061:57765, + 62062:57766, + 62063:57767, + 62064:57768, + 62065:57769, + 62066:57770, + 62067:57771, + 62068:57772, + 62069:57773, + 62070:57774, + 62071:57775, + 62072:57776, + 62073:57777, + 62074:57778, + 62075:57779, + 62076:57780, + 62077:57781, + 62078:57782, + 62080:57783, + 62081:57784, + 62082:57785, + 62083:57786, + 62084:57787, + 62085:57788, + 62086:57789, + 62087:57790, + 62088:57791, + 62089:57792, + 62090:57793, + 62091:57794, + 62092:57795, + 62093:57796, + 62094:57797, + 62095:57798, + 62096:57799, + 62097:57800, + 62098:57801, + 62099:57802, + 62100:57803, + 62101:57804, + 62102:57805, + 62103:57806, + 62104:57807, + 62105:57808, + 62106:57809, + 62107:57810, + 62108:57811, + 62109:57812, + 62110:57813, + 62111:57814, + 62112:57815, + 62113:57816, + 62114:57817, + 62115:57818, + 62116:57819, + 62117:57820, + 62118:57821, + 62119:57822, + 62120:57823, + 62121:57824, + 62122:57825, + 62123:57826, + 62124:57827, + 62125:57828, + 62126:57829, + 62127:57830, + 62128:57831, + 62129:57832, + 62130:57833, + 62131:57834, + 62132:57835, + 62133:57836, + 62134:57837, + 62135:57838, + 62136:57839, + 62137:57840, + 62138:57841, + 62139:57842, + 62140:57843, + 62141:57844, + 62142:57845, + 62143:57846, + 62144:57847, + 62145:57848, + 62146:57849, + 62147:57850, + 62148:57851, + 62149:57852, + 62150:57853, + 62151:57854, + 62152:57855, + 62153:57856, + 62154:57857, + 62155:57858, + 62156:57859, + 62157:57860, + 62158:57861, + 62159:57862, + 62160:57863, + 62161:57864, + 62162:57865, + 62163:57866, + 62164:57867, + 62165:57868, + 62166:57869, + 62167:57870, + 62168:57871, + 62169:57872, + 62170:57873, + 62171:57874, + 62172:57875, + 62173:57876, + 62174:57877, + 62175:57878, + 62176:57879, + 62177:57880, + 62178:57881, + 62179:57882, + 62180:57883, + 62181:57884, + 62182:57885, + 62183:57886, + 62184:57887, + 62185:57888, + 62186:57889, + 62187:57890, + 62188:57891, + 62189:57892, + 62190:57893, + 62191:57894, + 62192:57895, + 62193:57896, + 62194:57897, + 62195:57898, + 62196:57899, + 62197:57900, + 62198:57901, + 62199:57902, + 62200:57903, + 62201:57904, + 62202:57905, + 62203:57906, + 62204:57907, + 62272:57908, + 62273:57909, + 62274:57910, + 62275:57911, + 62276:57912, + 62277:57913, + 62278:57914, + 62279:57915, + 62280:57916, + 62281:57917, + 62282:57918, + 62283:57919, + 62284:57920, + 62285:57921, + 62286:57922, + 62287:57923, + 62288:57924, + 62289:57925, + 62290:57926, + 62291:57927, + 62292:57928, + 62293:57929, + 62294:57930, + 62295:57931, + 62296:57932, + 62297:57933, + 62298:57934, + 62299:57935, + 62300:57936, + 62301:57937, + 62302:57938, + 62303:57939, + 62304:57940, + 62305:57941, + 62306:57942, + 62307:57943, + 62308:57944, + 62309:57945, + 62310:57946, + 62311:57947, + 62312:57948, + 62313:57949, + 62314:57950, + 62315:57951, + 62316:57952, + 62317:57953, + 62318:57954, + 62319:57955, + 62320:57956, + 62321:57957, + 62322:57958, + 62323:57959, + 62324:57960, + 62325:57961, + 62326:57962, + 62327:57963, + 62328:57964, + 62329:57965, + 62330:57966, + 62331:57967, + 62332:57968, + 62333:57969, + 62334:57970, + 62336:57971, + 62337:57972, + 62338:57973, + 62339:57974, + 62340:57975, + 62341:57976, + 62342:57977, + 62343:57978, + 62344:57979, + 62345:57980, + 62346:57981, + 62347:57982, + 62348:57983, + 62349:57984, + 62350:57985, + 62351:57986, + 62352:57987, + 62353:57988, + 62354:57989, + 62355:57990, + 62356:57991, + 62357:57992, + 62358:57993, + 62359:57994, + 62360:57995, + 62361:57996, + 62362:57997, + 62363:57998, + 62364:57999, + 62365:58000, + 62366:58001, + 62367:58002, + 62368:58003, + 62369:58004, + 62370:58005, + 62371:58006, + 62372:58007, + 62373:58008, + 62374:58009, + 62375:58010, + 62376:58011, + 62377:58012, + 62378:58013, + 62379:58014, + 62380:58015, + 62381:58016, + 62382:58017, + 62383:58018, + 62384:58019, + 62385:58020, + 62386:58021, + 62387:58022, + 62388:58023, + 62389:58024, + 62390:58025, + 62391:58026, + 62392:58027, + 62393:58028, + 62394:58029, + 62395:58030, + 62396:58031, + 62397:58032, + 62398:58033, + 62399:58034, + 62400:58035, + 62401:58036, + 62402:58037, + 62403:58038, + 62404:58039, + 62405:58040, + 62406:58041, + 62407:58042, + 62408:58043, + 62409:58044, + 62410:58045, + 62411:58046, + 62412:58047, + 62413:58048, + 62414:58049, + 62415:58050, + 62416:58051, + 62417:58052, + 62418:58053, + 62419:58054, + 62420:58055, + 62421:58056, + 62422:58057, + 62423:58058, + 62424:58059, + 62425:58060, + 62426:58061, + 62427:58062, + 62428:58063, + 62429:58064, + 62430:58065, + 62431:58066, + 62432:58067, + 62433:58068, + 62434:58069, + 62435:58070, + 62436:58071, + 62437:58072, + 62438:58073, + 62439:58074, + 62440:58075, + 62441:58076, + 62442:58077, + 62443:58078, + 62444:58079, + 62445:58080, + 62446:58081, + 62447:58082, + 62448:58083, + 62449:58084, + 62450:58085, + 62451:58086, + 62452:58087, + 62453:58088, + 62454:58089, + 62455:58090, + 62456:58091, + 62457:58092, + 62458:58093, + 62459:58094, + 62460:58095, + 62528:58096, + 62529:58097, + 62530:58098, + 62531:58099, + 62532:58100, + 62533:58101, + 62534:58102, + 62535:58103, + 62536:58104, + 62537:58105, + 62538:58106, + 62539:58107, + 62540:58108, + 62541:58109, + 62542:58110, + 62543:58111, + 62544:58112, + 62545:58113, + 62546:58114, + 62547:58115, + 62548:58116, + 62549:58117, + 62550:58118, + 62551:58119, + 62552:58120, + 62553:58121, + 62554:58122, + 62555:58123, + 62556:58124, + 62557:58125, + 62558:58126, + 62559:58127, + 62560:58128, + 62561:58129, + 62562:58130, + 62563:58131, + 62564:58132, + 62565:58133, + 62566:58134, + 62567:58135, + 62568:58136, + 62569:58137, + 62570:58138, + 62571:58139, + 62572:58140, + 62573:58141, + 62574:58142, + 62575:58143, + 62576:58144, + 62577:58145, + 62578:58146, + 62579:58147, + 62580:58148, + 62581:58149, + 62582:58150, + 62583:58151, + 62584:58152, + 62585:58153, + 62586:58154, + 62587:58155, + 62588:58156, + 62589:58157, + 62590:58158, + 62592:58159, + 62593:58160, + 62594:58161, + 62595:58162, + 62596:58163, + 62597:58164, + 62598:58165, + 62599:58166, + 62600:58167, + 62601:58168, + 62602:58169, + 62603:58170, + 62604:58171, + 62605:58172, + 62606:58173, + 62607:58174, + 62608:58175, + 62609:58176, + 62610:58177, + 62611:58178, + 62612:58179, + 62613:58180, + 62614:58181, + 62615:58182, + 62616:58183, + 62617:58184, + 62618:58185, + 62619:58186, + 62620:58187, + 62621:58188, + 62622:58189, + 62623:58190, + 62624:58191, + 62625:58192, + 62626:58193, + 62627:58194, + 62628:58195, + 62629:58196, + 62630:58197, + 62631:58198, + 62632:58199, + 62633:58200, + 62634:58201, + 62635:58202, + 62636:58203, + 62637:58204, + 62638:58205, + 62639:58206, + 62640:58207, + 62641:58208, + 62642:58209, + 62643:58210, + 62644:58211, + 62645:58212, + 62646:58213, + 62647:58214, + 62648:58215, + 62649:58216, + 62650:58217, + 62651:58218, + 62652:58219, + 62653:58220, + 62654:58221, + 62655:58222, + 62656:58223, + 62657:58224, + 62658:58225, + 62659:58226, + 62660:58227, + 62661:58228, + 62662:58229, + 62663:58230, + 62664:58231, + 62665:58232, + 62666:58233, + 62667:58234, + 62668:58235, + 62669:58236, + 62670:58237, + 62671:58238, + 62672:58239, + 62673:58240, + 62674:58241, + 62675:58242, + 62676:58243, + 62677:58244, + 62678:58245, + 62679:58246, + 62680:58247, + 62681:58248, + 62682:58249, + 62683:58250, + 62684:58251, + 62685:58252, + 62686:58253, + 62687:58254, + 62688:58255, + 62689:58256, + 62690:58257, + 62691:58258, + 62692:58259, + 62693:58260, + 62694:58261, + 62695:58262, + 62696:58263, + 62697:58264, + 62698:58265, + 62699:58266, + 62700:58267, + 62701:58268, + 62702:58269, + 62703:58270, + 62704:58271, + 62705:58272, + 62706:58273, + 62707:58274, + 62708:58275, + 62709:58276, + 62710:58277, + 62711:58278, + 62712:58279, + 62713:58280, + 62714:58281, + 62715:58282, + 62716:58283, + 62784:58284, + 62785:58285, + 62786:58286, + 62787:58287, + 62788:58288, + 62789:58289, + 62790:58290, + 62791:58291, + 62792:58292, + 62793:58293, + 62794:58294, + 62795:58295, + 62796:58296, + 62797:58297, + 62798:58298, + 62799:58299, + 62800:58300, + 62801:58301, + 62802:58302, + 62803:58303, + 62804:58304, + 62805:58305, + 62806:58306, + 62807:58307, + 62808:58308, + 62809:58309, + 62810:58310, + 62811:58311, + 62812:58312, + 62813:58313, + 62814:58314, + 62815:58315, + 62816:58316, + 62817:58317, + 62818:58318, + 62819:58319, + 62820:58320, + 62821:58321, + 62822:58322, + 62823:58323, + 62824:58324, + 62825:58325, + 62826:58326, + 62827:58327, + 62828:58328, + 62829:58329, + 62830:58330, + 62831:58331, + 62832:58332, + 62833:58333, + 62834:58334, + 62835:58335, + 62836:58336, + 62837:58337, + 62838:58338, + 62839:58339, + 62840:58340, + 62841:58341, + 62842:58342, + 62843:58343, + 62844:58344, + 62845:58345, + 62846:58346, + 62848:58347, + 62849:58348, + 62850:58349, + 62851:58350, + 62852:58351, + 62853:58352, + 62854:58353, + 62855:58354, + 62856:58355, + 62857:58356, + 62858:58357, + 62859:58358, + 62860:58359, + 62861:58360, + 62862:58361, + 62863:58362, + 62864:58363, + 62865:58364, + 62866:58365, + 62867:58366, + 62868:58367, + 62869:58368, + 62870:58369, + 62871:58370, + 62872:58371, + 62873:58372, + 62874:58373, + 62875:58374, + 62876:58375, + 62877:58376, + 62878:58377, + 62879:58378, + 62880:58379, + 62881:58380, + 62882:58381, + 62883:58382, + 62884:58383, + 62885:58384, + 62886:58385, + 62887:58386, + 62888:58387, + 62889:58388, + 62890:58389, + 62891:58390, + 62892:58391, + 62893:58392, + 62894:58393, + 62895:58394, + 62896:58395, + 62897:58396, + 62898:58397, + 62899:58398, + 62900:58399, + 62901:58400, + 62902:58401, + 62903:58402, + 62904:58403, + 62905:58404, + 62906:58405, + 62907:58406, + 62908:58407, + 62909:58408, + 62910:58409, + 62911:58410, + 62912:58411, + 62913:58412, + 62914:58413, + 62915:58414, + 62916:58415, + 62917:58416, + 62918:58417, + 62919:58418, + 62920:58419, + 62921:58420, + 62922:58421, + 62923:58422, + 62924:58423, + 62925:58424, + 62926:58425, + 62927:58426, + 62928:58427, + 62929:58428, + 62930:58429, + 62931:58430, + 62932:58431, + 62933:58432, + 62934:58433, + 62935:58434, + 62936:58435, + 62937:58436, + 62938:58437, + 62939:58438, + 62940:58439, + 62941:58440, + 62942:58441, + 62943:58442, + 62944:58443, + 62945:58444, + 62946:58445, + 62947:58446, + 62948:58447, + 62949:58448, + 62950:58449, + 62951:58450, + 62952:58451, + 62953:58452, + 62954:58453, + 62955:58454, + 62956:58455, + 62957:58456, + 62958:58457, + 62959:58458, + 62960:58459, + 62961:58460, + 62962:58461, + 62963:58462, + 62964:58463, + 62965:58464, + 62966:58465, + 62967:58466, + 62968:58467, + 62969:58468, + 62970:58469, + 62971:58470, + 62972:58471, + 63040:58472, + 63041:58473, + 63042:58474, + 63043:58475, + 63044:58476, + 63045:58477, + 63046:58478, + 63047:58479, + 63048:58480, + 63049:58481, + 63050:58482, + 63051:58483, + 63052:58484, + 63053:58485, + 63054:58486, + 63055:58487, + 63056:58488, + 63057:58489, + 63058:58490, + 63059:58491, + 63060:58492, + 63061:58493, + 63062:58494, + 63063:58495, + 63064:58496, + 63065:58497, + 63066:58498, + 63067:58499, + 63068:58500, + 63069:58501, + 63070:58502, + 63071:58503, + 63072:58504, + 63073:58505, + 63074:58506, + 63075:58507, + 63076:58508, + 63077:58509, + 63078:58510, + 63079:58511, + 63080:58512, + 63081:58513, + 63082:58514, + 63083:58515, + 63084:58516, + 63085:58517, + 63086:58518, + 63087:58519, + 63088:58520, + 63089:58521, + 63090:58522, + 63091:58523, + 63092:58524, + 63093:58525, + 63094:58526, + 63095:58527, + 63096:58528, + 63097:58529, + 63098:58530, + 63099:58531, + 63100:58532, + 63101:58533, + 63102:58534, + 63104:58535, + 63105:58536, + 63106:58537, + 63107:58538, + 63108:58539, + 63109:58540, + 63110:58541, + 63111:58542, + 63112:58543, + 63113:58544, + 63114:58545, + 63115:58546, + 63116:58547, + 63117:58548, + 63118:58549, + 63119:58550, + 63120:58551, + 63121:58552, + 63122:58553, + 63123:58554, + 63124:58555, + 63125:58556, + 63126:58557, + 63127:58558, + 63128:58559, + 63129:58560, + 63130:58561, + 63131:58562, + 63132:58563, + 63133:58564, + 63134:58565, + 63135:58566, + 63136:58567, + 63137:58568, + 63138:58569, + 63139:58570, + 63140:58571, + 63141:58572, + 63142:58573, + 63143:58574, + 63144:58575, + 63145:58576, + 63146:58577, + 63147:58578, + 63148:58579, + 63149:58580, + 63150:58581, + 63151:58582, + 63152:58583, + 63153:58584, + 63154:58585, + 63155:58586, + 63156:58587, + 63157:58588, + 63158:58589, + 63159:58590, + 63160:58591, + 63161:58592, + 63162:58593, + 63163:58594, + 63164:58595, + 63165:58596, + 63166:58597, + 63167:58598, + 63168:58599, + 63169:58600, + 63170:58601, + 63171:58602, + 63172:58603, + 63173:58604, + 63174:58605, + 63175:58606, + 63176:58607, + 63177:58608, + 63178:58609, + 63179:58610, + 63180:58611, + 63181:58612, + 63182:58613, + 63183:58614, + 63184:58615, + 63185:58616, + 63186:58617, + 63187:58618, + 63188:58619, + 63189:58620, + 63190:58621, + 63191:58622, + 63192:58623, + 63193:58624, + 63194:58625, + 63195:58626, + 63196:58627, + 63197:58628, + 63198:58629, + 63199:58630, + 63200:58631, + 63201:58632, + 63202:58633, + 63203:58634, + 63204:58635, + 63205:58636, + 63206:58637, + 63207:58638, + 63208:58639, + 63209:58640, + 63210:58641, + 63211:58642, + 63212:58643, + 63213:58644, + 63214:58645, + 63215:58646, + 63216:58647, + 63217:58648, + 63218:58649, + 63219:58650, + 63220:58651, + 63221:58652, + 63222:58653, + 63223:58654, + 63224:58655, + 63225:58656, + 63226:58657, + 63227:58658, + 63228:58659, + 63296:58660, + 63297:58661, + 63298:58662, + 63299:58663, + 63300:58664, + 63301:58665, + 63302:58666, + 63303:58667, + 63304:58668, + 63305:58669, + 63306:58670, + 63307:58671, + 63308:58672, + 63309:58673, + 63310:58674, + 63311:58675, + 63312:58676, + 63313:58677, + 63314:58678, + 63315:58679, + 63316:58680, + 63317:58681, + 63318:58682, + 63319:58683, + 63320:58684, + 63321:58685, + 63322:58686, + 63323:58687, + 63324:58688, + 63325:58689, + 63326:58690, + 63327:58691, + 63328:58692, + 63329:58693, + 63330:58694, + 63331:58695, + 63332:58696, + 63333:58697, + 63334:58698, + 63335:58699, + 63336:58700, + 63337:58701, + 63338:58702, + 63339:58703, + 63340:58704, + 63341:58705, + 63342:58706, + 63343:58707, + 63344:58708, + 63345:58709, + 63346:58710, + 63347:58711, + 63348:58712, + 63349:58713, + 63350:58714, + 63351:58715, + 63352:58716, + 63353:58717, + 63354:58718, + 63355:58719, + 63356:58720, + 63357:58721, + 63358:58722, + 63360:58723, + 63361:58724, + 63362:58725, + 63363:58726, + 63364:58727, + 63365:58728, + 63366:58729, + 63367:58730, + 63368:58731, + 63369:58732, + 63370:58733, + 63371:58734, + 63372:58735, + 63373:58736, + 63374:58737, + 63375:58738, + 63376:58739, + 63377:58740, + 63378:58741, + 63379:58742, + 63380:58743, + 63381:58744, + 63382:58745, + 63383:58746, + 63384:58747, + 63385:58748, + 63386:58749, + 63387:58750, + 63388:58751, + 63389:58752, + 63390:58753, + 63391:58754, + 63392:58755, + 63393:58756, + 63394:58757, + 63395:58758, + 63396:58759, + 63397:58760, + 63398:58761, + 63399:58762, + 63400:58763, + 63401:58764, + 63402:58765, + 63403:58766, + 63404:58767, + 63405:58768, + 63406:58769, + 63407:58770, + 63408:58771, + 63409:58772, + 63410:58773, + 63411:58774, + 63412:58775, + 63413:58776, + 63414:58777, + 63415:58778, + 63416:58779, + 63417:58780, + 63418:58781, + 63419:58782, + 63420:58783, + 63421:58784, + 63422:58785, + 63423:58786, + 63424:58787, + 63425:58788, + 63426:58789, + 63427:58790, + 63428:58791, + 63429:58792, + 63430:58793, + 63431:58794, + 63432:58795, + 63433:58796, + 63434:58797, + 63435:58798, + 63436:58799, + 63437:58800, + 63438:58801, + 63439:58802, + 63440:58803, + 63441:58804, + 63442:58805, + 63443:58806, + 63444:58807, + 63445:58808, + 63446:58809, + 63447:58810, + 63448:58811, + 63449:58812, + 63450:58813, + 63451:58814, + 63452:58815, + 63453:58816, + 63454:58817, + 63455:58818, + 63456:58819, + 63457:58820, + 63458:58821, + 63459:58822, + 63460:58823, + 63461:58824, + 63462:58825, + 63463:58826, + 63464:58827, + 63465:58828, + 63466:58829, + 63467:58830, + 63468:58831, + 63469:58832, + 63470:58833, + 63471:58834, + 63472:58835, + 63473:58836, + 63474:58837, + 63475:58838, + 63476:58839, + 63477:58840, + 63478:58841, + 63479:58842, + 63480:58843, + 63481:58844, + 63482:58845, + 63483:58846, + 63484:58847, + 63552:58848, + 63553:58849, + 63554:58850, + 63555:58851, + 63556:58852, + 63557:58853, + 63558:58854, + 63559:58855, + 63560:58856, + 63561:58857, + 63562:58858, + 63563:58859, + 63564:58860, + 63565:58861, + 63566:58862, + 63567:58863, + 63568:58864, + 63569:58865, + 63570:58866, + 63571:58867, + 63572:58868, + 63573:58869, + 63574:58870, + 63575:58871, + 63576:58872, + 63577:58873, + 63578:58874, + 63579:58875, + 63580:58876, + 63581:58877, + 63582:58878, + 63583:58879, + 63584:58880, + 63585:58881, + 63586:58882, + 63587:58883, + 63588:58884, + 63589:58885, + 63590:58886, + 63591:58887, + 63592:58888, + 63593:58889, + 63594:58890, + 63595:58891, + 63596:58892, + 63597:58893, + 63598:58894, + 63599:58895, + 63600:58896, + 63601:58897, + 63602:58898, + 63603:58899, + 63604:58900, + 63605:58901, + 63606:58902, + 63607:58903, + 63608:58904, + 63609:58905, + 63610:58906, + 63611:58907, + 63612:58908, + 63613:58909, + 63614:58910, + 63616:58911, + 63617:58912, + 63618:58913, + 63619:58914, + 63620:58915, + 63621:58916, + 63622:58917, + 63623:58918, + 63624:58919, + 63625:58920, + 63626:58921, + 63627:58922, + 63628:58923, + 63629:58924, + 63630:58925, + 63631:58926, + 63632:58927, + 63633:58928, + 63634:58929, + 63635:58930, + 63636:58931, + 63637:58932, + 63638:58933, + 63639:58934, + 63640:58935, + 63641:58936, + 63642:58937, + 63643:58938, + 63644:58939, + 63645:58940, + 63646:58941, + 63647:58942, + 63648:58943, + 63649:58944, + 63650:58945, + 63651:58946, + 63652:58947, + 63653:58948, + 63654:58949, + 63655:58950, + 63656:58951, + 63657:58952, + 63658:58953, + 63659:58954, + 63660:58955, + 63661:58956, + 63662:58957, + 63663:58958, + 63664:58959, + 63665:58960, + 63666:58961, + 63667:58962, + 63668:58963, + 63669:58964, + 63670:58965, + 63671:58966, + 63672:58967, + 63673:58968, + 63674:58969, + 63675:58970, + 63676:58971, + 63677:58972, + 63678:58973, + 63679:58974, + 63680:58975, + 63681:58976, + 63682:58977, + 63683:58978, + 63684:58979, + 63685:58980, + 63686:58981, + 63687:58982, + 63688:58983, + 63689:58984, + 63690:58985, + 63691:58986, + 63692:58987, + 63693:58988, + 63694:58989, + 63695:58990, + 63696:58991, + 63697:58992, + 63698:58993, + 63699:58994, + 63700:58995, + 63701:58996, + 63702:58997, + 63703:58998, + 63704:58999, + 63705:59000, + 63706:59001, + 63707:59002, + 63708:59003, + 63709:59004, + 63710:59005, + 63711:59006, + 63712:59007, + 63713:59008, + 63714:59009, + 63715:59010, + 63716:59011, + 63717:59012, + 63718:59013, + 63719:59014, + 63720:59015, + 63721:59016, + 63722:59017, + 63723:59018, + 63724:59019, + 63725:59020, + 63726:59021, + 63727:59022, + 63728:59023, + 63729:59024, + 63730:59025, + 63731:59026, + 63732:59027, + 63733:59028, + 63734:59029, + 63735:59030, + 63736:59031, + 63737:59032, + 63738:59033, + 63739:59034, + 63740:59035, + 64064:8560, + 64065:8561, + 64066:8562, + 64067:8563, + 64068:8564, + 64069:8565, + 64070:8566, + 64071:8567, + 64072:8568, + 64073:8569, + 64074:8544, + 64075:8545, + 64076:8546, + 64077:8547, + 64078:8548, + 64079:8549, + 64080:8550, + 64081:8551, + 64082:8552, + 64083:8553, + 64084:65506, + 64085:65508, + 64086:65287, + 64087:65282, + 64088:12849, + 64089:8470, + 64090:8481, + 64091:8757, + 64092:32394, + 64093:35100, + 64094:37704, + 64095:37512, + 64096:34012, + 64097:20425, + 64098:28859, + 64099:26161, + 64100:26824, + 64101:37625, + 64102:26363, + 64103:24389, + 64104:20008, + 64105:20193, + 64106:20220, + 64107:20224, + 64108:20227, + 64109:20281, + 64110:20310, + 64111:20370, + 64112:20362, + 64113:20378, + 64114:20372, + 64115:20429, + 64116:20544, + 64117:20514, + 64118:20479, + 64119:20510, + 64120:20550, + 64121:20592, + 64122:20546, + 64123:20628, + 64124:20724, + 64125:20696, + 64126:20810, + 64128:20836, + 64129:20893, + 64130:20926, + 64131:20972, + 64132:21013, + 64133:21148, + 64134:21158, + 64135:21184, + 64136:21211, + 64137:21248, + 64138:21255, + 64139:21284, + 64140:21362, + 64141:21395, + 64142:21426, + 64143:21469, + 64144:64014, + 64145:21660, + 64146:21642, + 64147:21673, + 64148:21759, + 64149:21894, + 64150:22361, + 64151:22373, + 64152:22444, + 64153:22472, + 64154:22471, + 64155:64015, + 64156:64016, + 64157:22686, + 64158:22706, + 64159:22795, + 64160:22867, + 64161:22875, + 64162:22877, + 64163:22883, + 64164:22948, + 64165:22970, + 64166:23382, + 64167:23488, + 64168:29999, + 64169:23512, + 64170:23532, + 64171:23582, + 64172:23718, + 64173:23738, + 64174:23797, + 64175:23847, + 64176:23891, + 64177:64017, + 64178:23874, + 64179:23917, + 64180:23992, + 64181:23993, + 64182:24016, + 64183:24353, + 64184:24372, + 64185:24423, + 64186:24503, + 64187:24542, + 64188:24669, + 64189:24709, + 64190:24714, + 64191:24798, + 64192:24789, + 64193:24864, + 64194:24818, + 64195:24849, + 64196:24887, + 64197:24880, + 64198:24984, + 64199:25107, + 64200:25254, + 64201:25589, + 64202:25696, + 64203:25757, + 64204:25806, + 64205:25934, + 64206:26112, + 64207:26133, + 64208:26171, + 64209:26121, + 64210:26158, + 64211:26142, + 64212:26148, + 64213:26213, + 64214:26199, + 64215:26201, + 64216:64018, + 64217:26227, + 64218:26265, + 64219:26272, + 64220:26290, + 64221:26303, + 64222:26362, + 64223:26382, + 64224:63785, + 64225:26470, + 64226:26555, + 64227:26706, + 64228:26560, + 64229:26625, + 64230:26692, + 64231:26831, + 64232:64019, + 64233:26984, + 64234:64020, + 64235:27032, + 64236:27106, + 64237:27184, + 64238:27243, + 64239:27206, + 64240:27251, + 64241:27262, + 64242:27362, + 64243:27364, + 64244:27606, + 64245:27711, + 64246:27740, + 64247:27782, + 64248:27759, + 64249:27866, + 64250:27908, + 64251:28039, + 64252:28015, + 64320:28054, + 64321:28076, + 64322:28111, + 64323:28152, + 64324:28146, + 64325:28156, + 64326:28217, + 64327:28252, + 64328:28199, + 64329:28220, + 64330:28351, + 64331:28552, + 64332:28597, + 64333:28661, + 64334:28677, + 64335:28679, + 64336:28712, + 64337:28805, + 64338:28843, + 64339:28943, + 64340:28932, + 64341:29020, + 64342:28998, + 64343:28999, + 64344:64021, + 64345:29121, + 64346:29182, + 64347:29361, + 64348:29374, + 64349:29476, + 64350:64022, + 64351:29559, + 64352:29629, + 64353:29641, + 64354:29654, + 64355:29667, + 64356:29650, + 64357:29703, + 64358:29685, + 64359:29734, + 64360:29738, + 64361:29737, + 64362:29742, + 64363:29794, + 64364:29833, + 64365:29855, + 64366:29953, + 64367:30063, + 64368:30338, + 64369:30364, + 64370:30366, + 64371:30363, + 64372:30374, + 64373:64023, + 64374:30534, + 64375:21167, + 64376:30753, + 64377:30798, + 64378:30820, + 64379:30842, + 64380:31024, + 64381:64024, + 64382:64025, + 64384:64026, + 64385:31124, + 64386:64027, + 64387:31131, + 64388:31441, + 64389:31463, + 64390:64028, + 64391:31467, + 64392:31646, + 64393:64029, + 64394:32072, + 64395:32092, + 64396:32183, + 64397:32160, + 64398:32214, + 64399:32338, + 64400:32583, + 64401:32673, + 64402:64030, + 64403:33537, + 64404:33634, + 64405:33663, + 64406:33735, + 64407:33782, + 64408:33864, + 64409:33972, + 64410:34131, + 64411:34137, + 64412:34155, + 64413:64031, + 64414:34224, + 64415:64032, + 64416:64033, + 64417:34823, + 64418:35061, + 64419:35346, + 64420:35383, + 64421:35449, + 64422:35495, + 64423:35518, + 64424:35551, + 64425:64034, + 64426:35574, + 64427:35667, + 64428:35711, + 64429:36080, + 64430:36084, + 64431:36114, + 64432:36214, + 64433:64035, + 64434:36559, + 64435:64036, + 64436:64037, + 64437:36967, + 64438:37086, + 64439:64038, + 64440:37141, + 64441:37159, + 64442:37338, + 64443:37335, + 64444:37342, + 64445:37357, + 64446:37358, + 64447:37348, + 64448:37349, + 64449:37382, + 64450:37392, + 64451:37386, + 64452:37434, + 64453:37440, + 64454:37436, + 64455:37454, + 64456:37465, + 64457:37457, + 64458:37433, + 64459:37479, + 64460:37543, + 64461:37495, + 64462:37496, + 64463:37607, + 64464:37591, + 64465:37593, + 64466:37584, + 64467:64039, + 64468:37589, + 64469:37600, + 64470:37587, + 64471:37669, + 64472:37665, + 64473:37627, + 64474:64040, + 64475:37662, + 64476:37631, + 64477:37661, + 64478:37634, + 64479:37744, + 64480:37719, + 64481:37796, + 64482:37830, + 64483:37854, + 64484:37880, + 64485:37937, + 64486:37957, + 64487:37960, + 64488:38290, + 64489:63964, + 64490:64041, + 64491:38557, + 64492:38575, + 64493:38707, + 64494:38715, + 64495:38723, + 64496:38733, + 64497:38735, + 64498:38737, + 64499:38741, + 64500:38999, + 64501:39013, + 64502:64042, + 64503:64043, + 64504:39207, + 64505:64044, + 64506:39326, + 64507:39502, + 64508:39641, + 64576:39644, + 64577:39797, + 64578:39794, + 64579:39823, + 64580:39857, + 64581:39867, + 64582:39936, + 64583:40304, + 64584:40299, + 64585:64045, + 64586:40473, + 64587:40657 +}; + +/** + * @author takahiro / https://github.com/takahirox + */ + +function DataViewEx ( buffer, littleEndian ) { + + this.dv = new DataView( buffer ); + this.offset = 0; + this.littleEndian = ( littleEndian !== undefined ) ? littleEndian : true; + this.encoder = new CharsetEncoder(); + +} + +DataViewEx.prototype = { + + constructor: DataViewEx, + + getInt8: function () { + + var value = this.dv.getInt8( this.offset ); + this.offset += 1; + return value; + + }, + + getInt8Array: function ( size ) { + + var a = []; + + for ( var i = 0; i < size; i++ ) { + + a.push( this.getInt8() ); + + } + + return a; + + }, + + getUint8: function () { + + var value = this.dv.getUint8( this.offset ); + this.offset += 1; + return value; + + }, + + getUint8Array: function ( size ) { + + var a = []; + + for ( var i = 0; i < size; i++ ) { + + a.push( this.getUint8() ); + + } + + return a; + + }, + + + getInt16: function () { + + var value = this.dv.getInt16( this.offset, this.littleEndian ); + this.offset += 2; + return value; + + }, + + getInt16Array: function ( size ) { + + var a = []; + + for ( var i = 0; i < size; i++ ) { + + a.push( this.getInt16() ); + + } + + return a; + + }, + + getUint16: function () { + + var value = this.dv.getUint16( this.offset, this.littleEndian ); + this.offset += 2; + return value; + + }, + + getUint16Array: function ( size ) { + + var a = []; + + for ( var i = 0; i < size; i++ ) { + + a.push( this.getUint16() ); + + } + + return a; + + }, + + getInt32: function () { + + var value = this.dv.getInt32( this.offset, this.littleEndian ); + this.offset += 4; + return value; + + }, + + getInt32Array: function ( size ) { + + var a = []; + + for ( var i = 0; i < size; i++ ) { + + a.push( this.getInt32() ); + + } + + return a; + + }, + + getUint32: function () { + + var value = this.dv.getUint32( this.offset, this.littleEndian ); + this.offset += 4; + return value; + + }, + + getUint32Array: function ( size ) { + + var a = []; + + for ( var i = 0; i < size; i++ ) { + + a.push( this.getUint32() ); + + } + + return a; + + }, + + getFloat32: function () { + + var value = this.dv.getFloat32( this.offset, this.littleEndian ); + this.offset += 4; + return value; + + }, + + getFloat32Array: function( size ) { + + var a = []; + + for ( var i = 0; i < size; i++ ) { + + a.push( this.getFloat32() ); + + } + + return a; + + }, + + getFloat64: function () { + + var value = this.dv.getFloat64( this.offset, this.littleEndian ); + this.offset += 8; + return value; + + }, + + getFloat64Array: function( size ) { + + var a = []; + + for ( var i = 0; i < size; i++ ) { + + a.push( this.getFloat64() ); + + } + + return a; + + }, + + getIndex: function ( type, isUnsigned ) { + + switch ( type ) { + + case 1: + return ( isUnsigned === true ) ? this.getUint8() : this.getInt8(); + + case 2: + return ( isUnsigned === true ) ? this.getUint16() : this.getInt16(); + + case 4: + return this.getInt32(); // No Uint32 + + default: + throw 'unknown number type ' + type + ' exception.'; + + } + + }, + + getIndexArray: function ( type, size, isUnsigned ) { + + var a = []; + + for ( var i = 0; i < size; i++ ) { + + a.push( this.getIndex( type, isUnsigned ) ); + + } + + return a; + + }, + + getChars: function ( size ) { + + var str = ''; + + while ( size > 0 ) { + + var value = this.getUint8(); + size--; + + if ( value === 0 ) { + + break; + + } + + str += String.fromCharCode( value ); + + } + + while ( size > 0 ) { + + this.getUint8(); + size--; + + } + + return str; + + }, + + getSjisStringsAsUnicode: function ( size ) { + + var a = []; + + while ( size > 0 ) { + + var value = this.getUint8(); + size--; + + if ( value === 0 ) { + + break; + + } + + a.push( value ); + + } + + while ( size > 0 ) { + + this.getUint8(); + size--; + + } + + return this.encoder.s2u( new Uint8Array( a ) ); + + }, + + getUnicodeStrings: function ( size ) { + + var str = ''; + + while ( size > 0 ) { + + var value = this.getUint16(); + size -= 2; + + if ( value === 0 ) { + + break; + + } + + str += String.fromCharCode( value ); + + } + + while ( size > 0 ) { + + this.getUint8(); + size--; + + } + + return str; + + }, + + getTextBuffer: function () { + + var size = this.getUint32(); + return this.getUnicodeStrings( size ); + + } + +}; + +/** + * @author takahiro / https://github.com/takahirox + */ + +function DataCreationHelper () { +} + +DataCreationHelper.prototype = { + + constructor: DataCreationHelper, + + leftToRightVector3: function ( v ) { + + v[ 2 ] = -v[ 2 ]; + + }, + + leftToRightQuaternion: function ( q ) { + + q[ 0 ] = -q[ 0 ]; + q[ 1 ] = -q[ 1 ]; + + }, + + leftToRightEuler: function ( r ) { + + r[ 0 ] = -r[ 0 ]; + r[ 1 ] = -r[ 1 ]; + + }, + + leftToRightIndexOrder: function ( p ) { + + var tmp = p[ 2 ]; + p[ 2 ] = p[ 0 ]; + p[ 0 ] = tmp; + + }, + + leftToRightVector3Range: function ( v1, v2 ) { + + var tmp = -v2[ 2 ]; + v2[ 2 ] = -v1[ 2 ]; + v1[ 2 ] = tmp; + + }, + + leftToRightEulerRange: function ( r1, r2 ) { + + var tmp1 = -r2[ 0 ]; + var tmp2 = -r2[ 1 ]; + r2[ 0 ] = -r1[ 0 ]; + r2[ 1 ] = -r1[ 1 ]; + r1[ 0 ] = tmp1; + r1[ 1 ] = tmp2; + + } + +}; + +/** + * @author takahiro / https://github.com/takahirox + */ + +function Parser() { +} + +Parser.prototype.parsePmd = function ( buffer, leftToRight ) { + + var pmd = {}; + var dv = new DataViewEx( buffer ); + + pmd.metadata = {}; + pmd.metadata.format = 'pmd'; + pmd.metadata.coordinateSystem = 'left'; + + var parseHeader = function () { + + var metadata = pmd.metadata; + metadata.magic = dv.getChars( 3 ); + + if ( metadata.magic !== 'Pmd' ) { + + throw 'PMD file magic is not Pmd, but ' + metadata.magic; + + } + + metadata.version = dv.getFloat32(); + metadata.modelName = dv.getSjisStringsAsUnicode( 20 ); + metadata.comment = dv.getSjisStringsAsUnicode( 256 ); + + }; + + var parseVertices = function () { + + var parseVertex = function () { + + var p = {}; + p.position = dv.getFloat32Array( 3 ); + p.normal = dv.getFloat32Array( 3 ); + p.uv = dv.getFloat32Array( 2 ); + p.skinIndices = dv.getUint16Array( 2 ); + p.skinWeights = [ dv.getUint8() / 100 ]; + p.skinWeights.push( 1.0 - p.skinWeights[ 0 ] ); + p.edgeFlag = dv.getUint8(); + return p; + + }; + + var metadata = pmd.metadata; + metadata.vertexCount = dv.getUint32(); + + pmd.vertices = []; + + for ( var i = 0; i < metadata.vertexCount; i++ ) { + + pmd.vertices.push( parseVertex() ); + + } + + }; + + var parseFaces = function () { + + var parseFace = function () { + + var p = {}; + p.indices = dv.getUint16Array( 3 ); + return p; + + }; + + var metadata = pmd.metadata; + metadata.faceCount = dv.getUint32() / 3; + + pmd.faces = []; + + for ( var i = 0; i < metadata.faceCount; i++ ) { + + pmd.faces.push( parseFace() ); + + } + + }; + + var parseMaterials = function () { + + var parseMaterial = function () { + + var p = {}; + p.diffuse = dv.getFloat32Array( 4 ); + p.shininess = dv.getFloat32(); + p.specular = dv.getFloat32Array( 3 ); + p.ambient = dv.getFloat32Array( 3 ); + p.toonIndex = dv.getInt8(); + p.edgeFlag = dv.getUint8(); + p.faceCount = dv.getUint32() / 3; + p.fileName = dv.getSjisStringsAsUnicode( 20 ); + return p; + + }; + + var metadata = pmd.metadata; + metadata.materialCount = dv.getUint32(); + + pmd.materials = []; + + for ( var i = 0; i < metadata.materialCount; i++ ) { + + pmd.materials.push( parseMaterial() ); + + } + + }; + + var parseBones = function () { + + var parseBone = function () { + + var p = {}; + p.name = dv.getSjisStringsAsUnicode( 20 ); + p.parentIndex = dv.getInt16(); + p.tailIndex = dv.getInt16(); + p.type = dv.getUint8(); + p.ikIndex = dv.getInt16(); + p.position = dv.getFloat32Array( 3 ); + return p; + + }; + + var metadata = pmd.metadata; + metadata.boneCount = dv.getUint16(); + + pmd.bones = []; + + for ( var i = 0; i < metadata.boneCount; i++ ) { + + pmd.bones.push( parseBone() ); + + } + + }; + + var parseIks = function () { + + var parseIk = function () { + + var p = {}; + p.target = dv.getUint16(); + p.effector = dv.getUint16(); + p.linkCount = dv.getUint8(); + p.iteration = dv.getUint16(); + p.maxAngle = dv.getFloat32(); + + p.links = []; + for ( var i = 0; i < p.linkCount; i++ ) { + + var link = {}; + link.index = dv.getUint16(); + p.links.push( link ); + + } + + return p; + + }; + + var metadata = pmd.metadata; + metadata.ikCount = dv.getUint16(); + + pmd.iks = []; + + for ( var i = 0; i < metadata.ikCount; i++ ) { + + pmd.iks.push( parseIk() ); + + } + + }; + + var parseMorphs = function () { + + var parseMorph = function () { + + var p = {}; + p.name = dv.getSjisStringsAsUnicode( 20 ); + p.elementCount = dv.getUint32(); + p.type = dv.getUint8(); + + p.elements = []; + for ( var i = 0; i < p.elementCount; i++ ) { + + p.elements.push( { + index: dv.getUint32(), + position: dv.getFloat32Array( 3 ) + } ) ; + + } + + return p; + + }; + + var metadata = pmd.metadata; + metadata.morphCount = dv.getUint16(); + + pmd.morphs = []; + + for ( var i = 0; i < metadata.morphCount; i++ ) { + + pmd.morphs.push( parseMorph() ); + + } + + + }; + + var parseMorphFrames = function () { + + var parseMorphFrame = function () { + + var p = {}; + p.index = dv.getUint16(); + return p; + + }; + + var metadata = pmd.metadata; + metadata.morphFrameCount = dv.getUint8(); + + pmd.morphFrames = []; + + for ( var i = 0; i < metadata.morphFrameCount; i++ ) { + + pmd.morphFrames.push( parseMorphFrame() ); + + } + + }; + + var parseBoneFrameNames = function () { + + var parseBoneFrameName = function () { + + var p = {}; + p.name = dv.getSjisStringsAsUnicode( 50 ); + return p; + + }; + + var metadata = pmd.metadata; + metadata.boneFrameNameCount = dv.getUint8(); + + pmd.boneFrameNames = []; + + for ( var i = 0; i < metadata.boneFrameNameCount; i++ ) { + + pmd.boneFrameNames.push( parseBoneFrameName() ); + + } + + }; + + var parseBoneFrames = function () { + + var parseBoneFrame = function () { + + var p = {}; + p.boneIndex = dv.getInt16(); + p.frameIndex = dv.getUint8(); + return p; + + }; + + var metadata = pmd.metadata; + metadata.boneFrameCount = dv.getUint32(); + + pmd.boneFrames = []; + + for ( var i = 0; i < metadata.boneFrameCount; i++ ) { + + pmd.boneFrames.push( parseBoneFrame() ); + + } + + }; + + var parseEnglishHeader = function () { + + var metadata = pmd.metadata; + metadata.englishCompatibility = dv.getUint8(); + + if ( metadata.englishCompatibility > 0 ) { + + metadata.englishModelName = dv.getSjisStringsAsUnicode( 20 ); + metadata.englishComment = dv.getSjisStringsAsUnicode( 256 ); + + } + + }; + + var parseEnglishBoneNames = function () { + + var parseEnglishBoneName = function () { + + var p = {}; + p.name = dv.getSjisStringsAsUnicode( 20 ); + return p; + + }; + + var metadata = pmd.metadata; + + if ( metadata.englishCompatibility === 0 ) { + + return; + + } + + pmd.englishBoneNames = []; + + for ( var i = 0; i < metadata.boneCount; i++ ) { + + pmd.englishBoneNames.push( parseEnglishBoneName() ); + + } + + }; + + var parseEnglishMorphNames = function () { + + var parseEnglishMorphName = function () { + + var p = {}; + p.name = dv.getSjisStringsAsUnicode( 20 ); + return p; + + }; + + var metadata = pmd.metadata; + + if ( metadata.englishCompatibility === 0 ) { + + return; + + } + + pmd.englishMorphNames = []; + + for ( var i = 0; i < metadata.morphCount - 1; i++ ) { + + pmd.englishMorphNames.push( parseEnglishMorphName() ); + + } + + }; + + var parseEnglishBoneFrameNames = function () { + + var parseEnglishBoneFrameName = function () { + + var p = {}; + p.name = dv.getSjisStringsAsUnicode( 50 ); + return p; + + }; + + var metadata = pmd.metadata; + + if ( metadata.englishCompatibility === 0 ) { + + return; + + } + + pmd.englishBoneFrameNames = []; + + for ( var i = 0; i < metadata.boneFrameNameCount; i++ ) { + + pmd.englishBoneFrameNames.push( parseEnglishBoneFrameName() ); + + } + + }; + + var parseToonTextures = function () { + + var parseToonTexture = function () { + + var p = {}; + p.fileName = dv.getSjisStringsAsUnicode( 100 ); + return p; + + }; + + pmd.toonTextures = []; + + for ( var i = 0; i < 10; i++ ) { + + pmd.toonTextures.push( parseToonTexture() ); + + } + + }; + + var parseRigidBodies = function () { + + var parseRigidBody = function () { + + var p = {}; + p.name = dv.getSjisStringsAsUnicode( 20 ); + p.boneIndex = dv.getInt16(); + p.groupIndex = dv.getUint8(); + p.groupTarget = dv.getUint16(); + p.shapeType = dv.getUint8(); + p.width = dv.getFloat32(); + p.height = dv.getFloat32(); + p.depth = dv.getFloat32(); + p.position = dv.getFloat32Array( 3 ); + p.rotation = dv.getFloat32Array( 3 ); + p.weight = dv.getFloat32(); + p.positionDamping = dv.getFloat32(); + p.rotationDamping = dv.getFloat32(); + p.restitution = dv.getFloat32(); + p.friction = dv.getFloat32(); + p.type = dv.getUint8(); + return p; + + }; + + var metadata = pmd.metadata; + metadata.rigidBodyCount = dv.getUint32(); + + pmd.rigidBodies = []; + + for ( var i = 0; i < metadata.rigidBodyCount; i++ ) { + + pmd.rigidBodies.push( parseRigidBody() ); + + } + + }; + + var parseConstraints = function () { + + var parseConstraint = function () { + + var p = {}; + p.name = dv.getSjisStringsAsUnicode( 20 ); + p.rigidBodyIndex1 = dv.getUint32(); + p.rigidBodyIndex2 = dv.getUint32(); + p.position = dv.getFloat32Array( 3 ); + p.rotation = dv.getFloat32Array( 3 ); + p.translationLimitation1 = dv.getFloat32Array( 3 ); + p.translationLimitation2 = dv.getFloat32Array( 3 ); + p.rotationLimitation1 = dv.getFloat32Array( 3 ); + p.rotationLimitation2 = dv.getFloat32Array( 3 ); + p.springPosition = dv.getFloat32Array( 3 ); + p.springRotation = dv.getFloat32Array( 3 ); + return p; + + }; + + var metadata = pmd.metadata; + metadata.constraintCount = dv.getUint32(); + + pmd.constraints = []; + + for ( var i = 0; i < metadata.constraintCount; i++ ) { + + pmd.constraints.push( parseConstraint() ); + + } + + }; + + parseHeader(); + parseVertices(); + parseFaces(); + parseMaterials(); + parseBones(); + parseIks(); + parseMorphs(); + parseMorphFrames(); + parseBoneFrameNames(); + parseBoneFrames(); + parseEnglishHeader(); + parseEnglishBoneNames(); + parseEnglishMorphNames(); + parseEnglishBoneFrameNames(); + parseToonTextures(); + parseRigidBodies(); + parseConstraints(); + + if ( leftToRight === true ) this.leftToRightModel( pmd ); + + // console.log( pmd ); // for console debug + + return pmd; + +}; + +Parser.prototype.parsePmx = function ( buffer, leftToRight ) { + + var pmx = {}; + var dv = new DataViewEx( buffer ); + + pmx.metadata = {}; + pmx.metadata.format = 'pmx'; + pmx.metadata.coordinateSystem = 'left'; + + var parseHeader = function () { + + var metadata = pmx.metadata; + metadata.magic = dv.getChars( 4 ); + + // Note: don't remove the last blank space. + if ( metadata.magic !== 'PMX ' ) { + + throw 'PMX file magic is not PMX , but ' + metadata.magic; + + } + + metadata.version = dv.getFloat32(); + + if ( metadata.version !== 2.0 && metadata.version !== 2.1 ) { + + throw 'PMX version ' + metadata.version + ' is not supported.'; + + } + + metadata.headerSize = dv.getUint8(); + metadata.encoding = dv.getUint8(); + metadata.additionalUvNum = dv.getUint8(); + metadata.vertexIndexSize = dv.getUint8(); + metadata.textureIndexSize = dv.getUint8(); + metadata.materialIndexSize = dv.getUint8(); + metadata.boneIndexSize = dv.getUint8(); + metadata.morphIndexSize = dv.getUint8(); + metadata.rigidBodyIndexSize = dv.getUint8(); + metadata.modelName = dv.getTextBuffer(); + metadata.englishModelName = dv.getTextBuffer(); + metadata.comment = dv.getTextBuffer(); + metadata.englishComment = dv.getTextBuffer(); + + }; + + var parseVertices = function () { + + var parseVertex = function () { + + var p = {}; + p.position = dv.getFloat32Array( 3 ); + p.normal = dv.getFloat32Array( 3 ); + p.uv = dv.getFloat32Array( 2 ); + + p.auvs = []; + + for ( var i = 0; i < pmx.metadata.additionalUvNum; i++ ) { + + p.auvs.push( dv.getFloat32Array( 4 ) ); + + } + + p.type = dv.getUint8(); + + var indexSize = metadata.boneIndexSize; + + if ( p.type === 0 ) { // BDEF1 + + p.skinIndices = dv.getIndexArray( indexSize, 1 ); + p.skinWeights = [ 1.0 ]; + + } else if ( p.type === 1 ) { // BDEF2 + + p.skinIndices = dv.getIndexArray( indexSize, 2 ); + p.skinWeights = dv.getFloat32Array( 1 ); + p.skinWeights.push( 1.0 - p.skinWeights[ 0 ] ); + + } else if ( p.type === 2 ) { // BDEF4 + + p.skinIndices = dv.getIndexArray( indexSize, 4 ); + p.skinWeights = dv.getFloat32Array( 4 ); + + } else if ( p.type === 3 ) { // SDEF + + p.skinIndices = dv.getIndexArray( indexSize, 2 ); + p.skinWeights = dv.getFloat32Array( 1 ); + p.skinWeights.push( 1.0 - p.skinWeights[ 0 ] ); + + p.skinC = dv.getFloat32Array( 3 ); + p.skinR0 = dv.getFloat32Array( 3 ); + p.skinR1 = dv.getFloat32Array( 3 ); + + // SDEF is not supported yet and is handled as BDEF2 so far. + // TODO: SDEF support + p.type = 1; + + } else { + + throw 'unsupport bone type ' + p.type + ' exception.'; + + } + + p.edgeRatio = dv.getFloat32(); + return p; + + }; + + var metadata = pmx.metadata; + metadata.vertexCount = dv.getUint32(); + + pmx.vertices = []; + + for ( var i = 0; i < metadata.vertexCount; i++ ) { + + pmx.vertices.push( parseVertex() ); + + } + + }; + + var parseFaces = function () { + + var parseFace = function () { + + var p = {}; + p.indices = dv.getIndexArray( metadata.vertexIndexSize, 3, true ); + return p; + + }; + + var metadata = pmx.metadata; + metadata.faceCount = dv.getUint32() / 3; + + pmx.faces = []; + + for ( var i = 0; i < metadata.faceCount; i++ ) { + + pmx.faces.push( parseFace() ); + + } + + }; + + var parseTextures = function () { + + var parseTexture = function () { + + return dv.getTextBuffer(); + + }; + + var metadata = pmx.metadata; + metadata.textureCount = dv.getUint32(); + + pmx.textures = []; + + for ( var i = 0; i < metadata.textureCount; i++ ) { + + pmx.textures.push( parseTexture() ); + + } + + }; + + var parseMaterials = function () { + + var parseMaterial = function () { + + var p = {}; + p.name = dv.getTextBuffer(); + p.englishName = dv.getTextBuffer(); + p.diffuse = dv.getFloat32Array( 4 ); + p.specular = dv.getFloat32Array( 3 ); + p.shininess = dv.getFloat32(); + p.ambient = dv.getFloat32Array( 3 ); + p.flag = dv.getUint8(); + p.edgeColor = dv.getFloat32Array( 4 ); + p.edgeSize = dv.getFloat32(); + p.textureIndex = dv.getIndex( pmx.metadata.textureIndexSize ); + p.envTextureIndex = dv.getIndex( pmx.metadata.textureIndexSize ); + p.envFlag = dv.getUint8(); + p.toonFlag = dv.getUint8(); + + if ( p.toonFlag === 0 ) { + + p.toonIndex = dv.getIndex( pmx.metadata.textureIndexSize ); + + } else if ( p.toonFlag === 1 ) { + + p.toonIndex = dv.getInt8(); + + } else { + + throw 'unknown toon flag ' + p.toonFlag + ' exception.'; + + } + + p.comment = dv.getTextBuffer(); + p.faceCount = dv.getUint32() / 3; + return p; + + }; + + var metadata = pmx.metadata; + metadata.materialCount = dv.getUint32(); + + pmx.materials = []; + + for ( var i = 0; i < metadata.materialCount; i++ ) { + + pmx.materials.push( parseMaterial() ); + + } + + }; + + var parseBones = function () { + + var parseBone = function () { + + var p = {}; + p.name = dv.getTextBuffer(); + p.englishName = dv.getTextBuffer(); + p.position = dv.getFloat32Array( 3 ); + p.parentIndex = dv.getIndex( pmx.metadata.boneIndexSize ); + p.transformationClass = dv.getUint32(); + p.flag = dv.getUint16(); + + if ( p.flag & 0x1 ) { + + p.connectIndex = dv.getIndex( pmx.metadata.boneIndexSize ); + + } else { + + p.offsetPosition = dv.getFloat32Array( 3 ); + + } + + if ( p.flag & 0x100 || p.flag & 0x200 ) { + + // Note: I don't think Grant is an appropriate name + // but I found that some English translated MMD tools use this term + // so I've named it Grant so far. + // I'd rename to more appropriate name from Grant later. + var grant = {}; + + grant.isLocal = ( p.flag & 0x80 ) !== 0 ? true : false; + grant.affectRotation = ( p.flag & 0x100 ) !== 0 ? true : false; + grant.affectPosition = ( p.flag & 0x200 ) !== 0 ? true : false; + grant.parentIndex = dv.getIndex( pmx.metadata.boneIndexSize ); + grant.ratio = dv.getFloat32(); + + p.grant = grant; + + } + + if ( p.flag & 0x400 ) { + + p.fixAxis = dv.getFloat32Array( 3 ); + + } + + if ( p.flag & 0x800 ) { + + p.localXVector = dv.getFloat32Array( 3 ); + p.localZVector = dv.getFloat32Array( 3 ); + + } + + if ( p.flag & 0x2000 ) { + + p.key = dv.getUint32(); + + } + + if ( p.flag & 0x20 ) { + + var ik = {}; + + ik.effector = dv.getIndex( pmx.metadata.boneIndexSize ); + ik.target = null; + ik.iteration = dv.getUint32(); + ik.maxAngle = dv.getFloat32(); + ik.linkCount = dv.getUint32(); + ik.links = []; + + for ( var i = 0; i < ik.linkCount; i++ ) { + + var link = {}; + link.index = dv.getIndex( pmx.metadata.boneIndexSize ); + link.angleLimitation = dv.getUint8(); + + if ( link.angleLimitation === 1 ) { + + link.lowerLimitationAngle = dv.getFloat32Array( 3 ); + link.upperLimitationAngle = dv.getFloat32Array( 3 ); + + } + + ik.links.push( link ); + + } + + p.ik = ik; + } + + return p; + + }; + + var metadata = pmx.metadata; + metadata.boneCount = dv.getUint32(); + + pmx.bones = []; + + for ( var i = 0; i < metadata.boneCount; i++ ) { + + pmx.bones.push( parseBone() ); + + } + + }; + + var parseMorphs = function () { + + var parseMorph = function () { + + var p = {}; + p.name = dv.getTextBuffer(); + p.englishName = dv.getTextBuffer(); + p.panel = dv.getUint8(); + p.type = dv.getUint8(); + p.elementCount = dv.getUint32(); + p.elements = []; + + for ( var i = 0; i < p.elementCount; i++ ) { + + if ( p.type === 0 ) { // group morph + + var m = {}; + m.index = dv.getIndex( pmx.metadata.morphIndexSize ); + m.ratio = dv.getFloat32(); + p.elements.push( m ); + + } else if ( p.type === 1 ) { // vertex morph + + var m = {}; + m.index = dv.getIndex( pmx.metadata.vertexIndexSize, true ); + m.position = dv.getFloat32Array( 3 ); + p.elements.push( m ); + + } else if ( p.type === 2 ) { // bone morph + + var m = {}; + m.index = dv.getIndex( pmx.metadata.boneIndexSize ); + m.position = dv.getFloat32Array( 3 ); + m.rotation = dv.getFloat32Array( 4 ); + p.elements.push( m ); + + } else if ( p.type === 3 ) { // uv morph + + var m = {}; + m.index = dv.getIndex( pmx.metadata.vertexIndexSize, true ); + m.uv = dv.getFloat32Array( 4 ); + p.elements.push( m ); + + } else if ( p.type === 4 ) { // additional uv1 + + // TODO: implement + + } else if ( p.type === 5 ) { // additional uv2 + + // TODO: implement + + } else if ( p.type === 6 ) { // additional uv3 + + // TODO: implement + + } else if ( p.type === 7 ) { // additional uv4 + + // TODO: implement + + } else if ( p.type === 8 ) { // material morph + + var m = {}; + m.index = dv.getIndex( pmx.metadata.materialIndexSize ); + m.type = dv.getUint8(); + m.diffuse = dv.getFloat32Array( 4 ); + m.specular = dv.getFloat32Array( 3 ); + m.shininess = dv.getFloat32(); + m.ambient = dv.getFloat32Array( 3 ); + m.edgeColor = dv.getFloat32Array( 4 ); + m.edgeSize = dv.getFloat32(); + m.textureColor = dv.getFloat32Array( 4 ); + m.sphereTextureColor = dv.getFloat32Array( 4 ); + m.toonColor = dv.getFloat32Array( 4 ); + p.elements.push( m ); + + } + + } + + return p; + + }; + + var metadata = pmx.metadata; + metadata.morphCount = dv.getUint32(); + + pmx.morphs = []; + + for ( var i = 0; i < metadata.morphCount; i++ ) { + + pmx.morphs.push( parseMorph() ); + + } + + }; + + var parseFrames = function () { + + var parseFrame = function () { + + var p = {}; + p.name = dv.getTextBuffer(); + p.englishName = dv.getTextBuffer(); + p.type = dv.getUint8(); + p.elementCount = dv.getUint32(); + p.elements = []; + + for ( var i = 0; i < p.elementCount; i++ ) { + + var e = {}; + e.target = dv.getUint8(); + e.index = ( e.target === 0 ) ? dv.getIndex( pmx.metadata.boneIndexSize ) : dv.getIndex( pmx.metadata.morphIndexSize ); + p.elements.push( e ); + + } + + return p; + + }; + + var metadata = pmx.metadata; + metadata.frameCount = dv.getUint32(); + + pmx.frames = []; + + for ( var i = 0; i < metadata.frameCount; i++ ) { + + pmx.frames.push( parseFrame() ); + + } + + }; + + var parseRigidBodies = function () { + + var parseRigidBody = function () { + + var p = {}; + p.name = dv.getTextBuffer(); + p.englishName = dv.getTextBuffer(); + p.boneIndex = dv.getIndex( pmx.metadata.boneIndexSize ); + p.groupIndex = dv.getUint8(); + p.groupTarget = dv.getUint16(); + p.shapeType = dv.getUint8(); + p.width = dv.getFloat32(); + p.height = dv.getFloat32(); + p.depth = dv.getFloat32(); + p.position = dv.getFloat32Array( 3 ); + p.rotation = dv.getFloat32Array( 3 ); + p.weight = dv.getFloat32(); + p.positionDamping = dv.getFloat32(); + p.rotationDamping = dv.getFloat32(); + p.restitution = dv.getFloat32(); + p.friction = dv.getFloat32(); + p.type = dv.getUint8(); + return p; + + }; + + var metadata = pmx.metadata; + metadata.rigidBodyCount = dv.getUint32(); + + pmx.rigidBodies = []; + + for ( var i = 0; i < metadata.rigidBodyCount; i++ ) { + + pmx.rigidBodies.push( parseRigidBody() ); + + } + + }; + + var parseConstraints = function () { + + var parseConstraint = function () { + + var p = {}; + p.name = dv.getTextBuffer(); + p.englishName = dv.getTextBuffer(); + p.type = dv.getUint8(); + p.rigidBodyIndex1 = dv.getIndex( pmx.metadata.rigidBodyIndexSize ); + p.rigidBodyIndex2 = dv.getIndex( pmx.metadata.rigidBodyIndexSize ); + p.position = dv.getFloat32Array( 3 ); + p.rotation = dv.getFloat32Array( 3 ); + p.translationLimitation1 = dv.getFloat32Array( 3 ); + p.translationLimitation2 = dv.getFloat32Array( 3 ); + p.rotationLimitation1 = dv.getFloat32Array( 3 ); + p.rotationLimitation2 = dv.getFloat32Array( 3 ); + p.springPosition = dv.getFloat32Array( 3 ); + p.springRotation = dv.getFloat32Array( 3 ); + return p; + + }; + + var metadata = pmx.metadata; + metadata.constraintCount = dv.getUint32(); + + pmx.constraints = []; + + for ( var i = 0; i < metadata.constraintCount; i++ ) { + + pmx.constraints.push( parseConstraint() ); + + } + + }; + + parseHeader(); + parseVertices(); + parseFaces(); + parseTextures(); + parseMaterials(); + parseBones(); + parseMorphs(); + parseFrames(); + parseRigidBodies(); + parseConstraints(); + + if ( leftToRight === true ) this.leftToRightModel( pmx ); + + // console.log( pmx ); // for console debug + + return pmx; + +}; + +Parser.prototype.parseVmd = function ( buffer, leftToRight ) { + + var vmd = {}; + var dv = new DataViewEx( buffer ); + + vmd.metadata = {}; + vmd.metadata.coordinateSystem = 'left'; + + var parseHeader = function () { + + var metadata = vmd.metadata; + metadata.magic = dv.getChars( 30 ); + + if ( metadata.magic !== 'Vocaloid Motion Data 0002' ) { + + throw 'VMD file magic is not Vocaloid Motion Data 0002, but ' + metadata.magic; + + } + + metadata.name = dv.getSjisStringsAsUnicode( 20 ); + + }; + + var parseMotions = function () { + + var parseMotion = function () { + + var p = {}; + p.boneName = dv.getSjisStringsAsUnicode( 15 ); + p.frameNum = dv.getUint32(); + p.position = dv.getFloat32Array( 3 ); + p.rotation = dv.getFloat32Array( 4 ); + p.interpolation = dv.getUint8Array( 64 ); + return p; + + }; + + var metadata = vmd.metadata; + metadata.motionCount = dv.getUint32(); + + vmd.motions = []; + for ( var i = 0; i < metadata.motionCount; i++ ) { + + vmd.motions.push( parseMotion() ); + + } + + }; + + var parseMorphs = function () { + + var parseMorph = function () { + + var p = {}; + p.morphName = dv.getSjisStringsAsUnicode( 15 ); + p.frameNum = dv.getUint32(); + p.weight = dv.getFloat32(); + return p; + + }; + + var metadata = vmd.metadata; + metadata.morphCount = dv.getUint32(); + + vmd.morphs = []; + for ( var i = 0; i < metadata.morphCount; i++ ) { + + vmd.morphs.push( parseMorph() ); + + } + + }; + + var parseCameras = function () { + + var parseCamera = function () { + + var p = {}; + p.frameNum = dv.getUint32(); + p.distance = dv.getFloat32(); + p.position = dv.getFloat32Array( 3 ); + p.rotation = dv.getFloat32Array( 3 ); + p.interpolation = dv.getUint8Array( 24 ); + p.fov = dv.getUint32(); + p.perspective = dv.getUint8(); + return p; + + }; + + var metadata = vmd.metadata; + metadata.cameraCount = dv.getUint32(); + + vmd.cameras = []; + for ( var i = 0; i < metadata.cameraCount; i++ ) { + + vmd.cameras.push( parseCamera() ); + + } + + }; + + parseHeader(); + parseMotions(); + parseMorphs(); + parseCameras(); + + if ( leftToRight === true ) this.leftToRightVmd( vmd ); + + // console.log( vmd ); // for console debug + + return vmd; + +}; + +Parser.prototype.parseVpd = function ( text, leftToRight ) { + + var vpd = {}; + + vpd.metadata = {}; + vpd.metadata.coordinateSystem = 'left'; + + vpd.bones = []; + + var commentPatternG = /\/\/\w*(\r|\n|\r\n)/g; + var newlinePattern = /\r|\n|\r\n/; + + var lines = text.replace( commentPatternG, '' ).split( newlinePattern ); + + function throwError () { + + throw 'the file seems not vpd file.'; + + } + + function checkMagic () { + + if ( lines[ 0 ] !== 'Vocaloid Pose Data file' ) { + + throwError(); + + } + + } + + function parseHeader () { + + if ( lines.length < 4 ) { + + throwError(); + + } + + vpd.metadata.parentFile = lines[ 2 ]; + vpd.metadata.boneCount = parseInt( lines[ 3 ] ); + + } + + function parseBones () { + + var boneHeaderPattern = /^\s*(Bone[0-9]+)\s*\{\s*(.*)$/; + var boneVectorPattern = /^\s*(-?[0-9]+\.[0-9]+)\s*,\s*(-?[0-9]+\.[0-9]+)\s*,\s*(-?[0-9]+\.[0-9]+)\s*;/; + var boneQuaternionPattern = /^\s*(-?[0-9]+\.[0-9]+)\s*,\s*(-?[0-9]+\.[0-9]+)\s*,\s*(-?[0-9]+\.[0-9]+)\s*,\s*(-?[0-9]+\.[0-9]+)\s*;/; + var boneFooterPattern = /^\s*}/; + + var bones = vpd.bones; + var n = null; + var v = null; + var q = null; + + for ( var i = 4; i < lines.length; i++ ) { + + var line = lines[ i ]; + + var result; + + result = line.match( boneHeaderPattern ); + + if ( result !== null ) { + + if ( n !== null ) { + + throwError(); + + } + + n = result[ 2 ]; + + } + + result = line.match( boneVectorPattern ); + + if ( result !== null ) { + + if ( v !== null ) { + + throwError(); + + } + + v = [ + + parseFloat( result[ 1 ] ), + parseFloat( result[ 2 ] ), + parseFloat( result[ 3 ] ) + + ]; + + } + + result = line.match( boneQuaternionPattern ); + + if ( result !== null ) { + + if ( q !== null ) { + + throwError(); + + } + + q = [ + + parseFloat( result[ 1 ] ), + parseFloat( result[ 2 ] ), + parseFloat( result[ 3 ] ), + parseFloat( result[ 4 ] ) + + ]; + + + } + + result = line.match( boneFooterPattern ); + + if ( result !== null ) { + + if ( n === null || v === null || q === null ) { + + throwError(); + + } + + bones.push( { + + name: n, + translation: v, + quaternion: q + + } ); + + n = null; + v = null; + q = null; + + } + + } + + if ( n !== null || v !== null || q !== null ) { + + throwError(); + + } + + } + + checkMagic(); + parseHeader(); + parseBones(); + + if ( leftToRight === true ) this.leftToRightVpd( vpd ); + + // console.log( vpd ); // for console debug + + return vpd; + +}; + +Parser.prototype.mergeVmds = function ( vmds ) { + + var v = {}; + v.metadata = {}; + v.metadata.name = vmds[ 0 ].metadata.name; + v.metadata.coordinateSystem = vmds[ 0 ].metadata.coordinateSystem; + v.metadata.motionCount = 0; + v.metadata.morphCount = 0; + v.metadata.cameraCount = 0; + v.motions = []; + v.morphs = []; + v.cameras = []; + + for ( var i = 0; i < vmds.length; i++ ) { + + var v2 = vmds[ i ]; + + v.metadata.motionCount += v2.metadata.motionCount; + v.metadata.morphCount += v2.metadata.morphCount; + v.metadata.cameraCount += v2.metadata.cameraCount; + + for ( var j = 0; j < v2.metadata.motionCount; j++ ) { + + v.motions.push( v2.motions[ j ] ); + + } + + for ( var j = 0; j < v2.metadata.morphCount; j++ ) { + + v.morphs.push( v2.morphs[ j ] ); + + } + + for ( var j = 0; j < v2.metadata.cameraCount; j++ ) { + + v.cameras.push( v2.cameras[ j ] ); + + } + + } + + return v; + +}; + +Parser.prototype.leftToRightModel = function ( model ) { + + if ( model.metadata.coordinateSystem === 'right' ) { + + return; + + } + + model.metadata.coordinateSystem = 'right'; + + var helper = new DataCreationHelper(); + + for ( var i = 0; i < model.metadata.vertexCount; i++ ) { + + helper.leftToRightVector3( model.vertices[ i ].position ); + helper.leftToRightVector3( model.vertices[ i ].normal ); + + } + + for ( var i = 0; i < model.metadata.faceCount; i++ ) { + + helper.leftToRightIndexOrder( model.faces[ i ].indices ); + + } + + for ( var i = 0; i < model.metadata.boneCount; i++ ) { + + helper.leftToRightVector3( model.bones[ i ].position ); + + } + + // TODO: support other morph for PMX + for ( var i = 0; i < model.metadata.morphCount; i++ ) { + + var m = model.morphs[ i ]; + + if ( model.metadata.format === 'pmx' && m.type !== 1 ) { + + // TODO: implement + continue; + + } + + for ( var j = 0; j < m.elements.length; j++ ) { + + helper.leftToRightVector3( m.elements[ j ].position ); + + } + + } + + for ( var i = 0; i < model.metadata.rigidBodyCount; i++ ) { + + helper.leftToRightVector3( model.rigidBodies[ i ].position ); + helper.leftToRightEuler( model.rigidBodies[ i ].rotation ); + + } + + for ( var i = 0; i < model.metadata.constraintCount; i++ ) { + + helper.leftToRightVector3( model.constraints[ i ].position ); + helper.leftToRightEuler( model.constraints[ i ].rotation ); + helper.leftToRightVector3Range( model.constraints[ i ].translationLimitation1, model.constraints[ i ].translationLimitation2 ); + helper.leftToRightEulerRange( model.constraints[ i ].rotationLimitation1, model.constraints[ i ].rotationLimitation2 ); + + } + +}; + +Parser.prototype.leftToRightVmd = function ( vmd ) { + + if ( vmd.metadata.coordinateSystem === 'right' ) { + + return; + + } + + vmd.metadata.coordinateSystem = 'right'; + + var helper = new DataCreationHelper(); + + for ( var i = 0; i < vmd.metadata.motionCount; i++ ) { + + helper.leftToRightVector3( vmd.motions[ i ].position ); + helper.leftToRightQuaternion( vmd.motions[ i ].rotation ); + + } + + for ( var i = 0; i < vmd.metadata.cameraCount; i++ ) { + + helper.leftToRightVector3( vmd.cameras[ i ].position ); + helper.leftToRightEuler( vmd.cameras[ i ].rotation ); + + } + +}; + +Parser.prototype.leftToRightVpd = function ( vpd ) { + + if ( vpd.metadata.coordinateSystem === 'right' ) { + + return; + + } + + vpd.metadata.coordinateSystem = 'right'; + + var helper = new DataCreationHelper(); + + for ( var i = 0; i < vpd.bones.length; i++ ) { + + helper.leftToRightVector3( vpd.bones[ i ].translation ); + helper.leftToRightQuaternion( vpd.bones[ i ].quaternion ); + + } + +}; + +export { CharsetEncoder, Parser }; diff --git a/examples/jsm/libs/mmdparser.js b/examples/jsm/libs/mmdparser.js deleted file mode 100644 index 8e5322ebbe8fd4..00000000000000 --- a/examples/jsm/libs/mmdparser.js +++ /dev/null @@ -1,11533 +0,0 @@ -/** - * @author Takahiro / https://github.com/takahirox - * - * Simple CharsetEncoder. - */ - -function CharsetEncoder() { -} - -/* - * Converts from Shift_JIS Uint8Array data to Unicode strings. - */ -CharsetEncoder.prototype.s2u = function ( uint8Array ) { - - var t = this.s2uTable; - var str = ''; - var p = 0; - - while ( p < uint8Array.length ) { - - var key = uint8Array[ p ++ ]; - - if ( ! ( ( key >= 0x00 && key <= 0x7e ) || - ( key >= 0xa1 && key <= 0xdf ) ) && - p < uint8Array.length ) { - - key = ( key << 8 ) | uint8Array[ p ++ ]; - - } - - if ( t[ key ] === undefined ) { - - throw 'unknown char code ' + key + '.'; - - } - - str += String.fromCharCode( t[ key ] ); - - } - - return str; - -}; - -CharsetEncoder.prototype.s2uTable = { - 0: 0, - 1: 1, - 2: 2, - 3: 3, - 4: 4, - 5: 5, - 6: 6, - 7: 7, - 8: 8, - 9: 9, - 10: 10, - 11: 11, - 12: 12, - 13: 13, - 14: 14, - 15: 15, - 16: 16, - 17: 17, - 18: 18, - 19: 19, - 20: 20, - 21: 21, - 22: 22, - 23: 23, - 24: 24, - 25: 25, - 26: 26, - 27: 27, - 28: 28, - 29: 29, - 30: 30, - 31: 31, - 32: 32, - 33: 33, - 34: 34, - 35: 35, - 36: 36, - 37: 37, - 38: 38, - 39: 39, - 40: 40, - 41: 41, - 42: 42, - 43: 43, - 44: 44, - 45: 45, - 46: 46, - 47: 47, - 48: 48, - 49: 49, - 50: 50, - 51: 51, - 52: 52, - 53: 53, - 54: 54, - 55: 55, - 56: 56, - 57: 57, - 58: 58, - 59: 59, - 60: 60, - 61: 61, - 62: 62, - 63: 63, - 64: 64, - 65: 65, - 66: 66, - 67: 67, - 68: 68, - 69: 69, - 70: 70, - 71: 71, - 72: 72, - 73: 73, - 74: 74, - 75: 75, - 76: 76, - 77: 77, - 78: 78, - 79: 79, - 80: 80, - 81: 81, - 82: 82, - 83: 83, - 84: 84, - 85: 85, - 86: 86, - 87: 87, - 88: 88, - 89: 89, - 90: 90, - 91: 91, - 92: 92, - 93: 93, - 94: 94, - 95: 95, - 96: 96, - 97: 97, - 98: 98, - 99: 99, - 100: 100, - 101: 101, - 102: 102, - 103: 103, - 104: 104, - 105: 105, - 106: 106, - 107: 107, - 108: 108, - 109: 109, - 110: 110, - 111: 111, - 112: 112, - 113: 113, - 114: 114, - 115: 115, - 116: 116, - 117: 117, - 118: 118, - 119: 119, - 120: 120, - 121: 121, - 122: 122, - 123: 123, - 124: 124, - 125: 125, - 126: 126, - 161: 65377, - 162: 65378, - 163: 65379, - 164: 65380, - 165: 65381, - 166: 65382, - 167: 65383, - 168: 65384, - 169: 65385, - 170: 65386, - 171: 65387, - 172: 65388, - 173: 65389, - 174: 65390, - 175: 65391, - 176: 65392, - 177: 65393, - 178: 65394, - 179: 65395, - 180: 65396, - 181: 65397, - 182: 65398, - 183: 65399, - 184: 65400, - 185: 65401, - 186: 65402, - 187: 65403, - 188: 65404, - 189: 65405, - 190: 65406, - 191: 65407, - 192: 65408, - 193: 65409, - 194: 65410, - 195: 65411, - 196: 65412, - 197: 65413, - 198: 65414, - 199: 65415, - 200: 65416, - 201: 65417, - 202: 65418, - 203: 65419, - 204: 65420, - 205: 65421, - 206: 65422, - 207: 65423, - 208: 65424, - 209: 65425, - 210: 65426, - 211: 65427, - 212: 65428, - 213: 65429, - 214: 65430, - 215: 65431, - 216: 65432, - 217: 65433, - 218: 65434, - 219: 65435, - 220: 65436, - 221: 65437, - 222: 65438, - 223: 65439, - 33088: 12288, - 33089: 12289, - 33090: 12290, - 33091: 65292, - 33092: 65294, - 33093: 12539, - 33094: 65306, - 33095: 65307, - 33096: 65311, - 33097: 65281, - 33098: 12443, - 33099: 12444, - 33100: 180, - 33101: 65344, - 33102: 168, - 33103: 65342, - 33104: 65507, - 33105: 65343, - 33106: 12541, - 33107: 12542, - 33108: 12445, - 33109: 12446, - 33110: 12291, - 33111: 20189, - 33112: 12293, - 33113: 12294, - 33114: 12295, - 33115: 12540, - 33116: 8213, - 33117: 8208, - 33118: 65295, - 33119: 65340, - 33120: 65374, - 33121: 8741, - 33122: 65372, - 33123: 8230, - 33124: 8229, - 33125: 8216, - 33126: 8217, - 33127: 8220, - 33128: 8221, - 33129: 65288, - 33130: 65289, - 33131: 12308, - 33132: 12309, - 33133: 65339, - 33134: 65341, - 33135: 65371, - 33136: 65373, - 33137: 12296, - 33138: 12297, - 33139: 12298, - 33140: 12299, - 33141: 12300, - 33142: 12301, - 33143: 12302, - 33144: 12303, - 33145: 12304, - 33146: 12305, - 33147: 65291, - 33148: 65293, - 33149: 177, - 33150: 215, - 33152: 247, - 33153: 65309, - 33154: 8800, - 33155: 65308, - 33156: 65310, - 33157: 8806, - 33158: 8807, - 33159: 8734, - 33160: 8756, - 33161: 9794, - 33162: 9792, - 33163: 176, - 33164: 8242, - 33165: 8243, - 33166: 8451, - 33167: 65509, - 33168: 65284, - 33169: 65504, - 33170: 65505, - 33171: 65285, - 33172: 65283, - 33173: 65286, - 33174: 65290, - 33175: 65312, - 33176: 167, - 33177: 9734, - 33178: 9733, - 33179: 9675, - 33180: 9679, - 33181: 9678, - 33182: 9671, - 33183: 9670, - 33184: 9633, - 33185: 9632, - 33186: 9651, - 33187: 9650, - 33188: 9661, - 33189: 9660, - 33190: 8251, - 33191: 12306, - 33192: 8594, - 33193: 8592, - 33194: 8593, - 33195: 8595, - 33196: 12307, - 33208: 8712, - 33209: 8715, - 33210: 8838, - 33211: 8839, - 33212: 8834, - 33213: 8835, - 33214: 8746, - 33215: 8745, - 33224: 8743, - 33225: 8744, - 33226: 65506, - 33227: 8658, - 33228: 8660, - 33229: 8704, - 33230: 8707, - 33242: 8736, - 33243: 8869, - 33244: 8978, - 33245: 8706, - 33246: 8711, - 33247: 8801, - 33248: 8786, - 33249: 8810, - 33250: 8811, - 33251: 8730, - 33252: 8765, - 33253: 8733, - 33254: 8757, - 33255: 8747, - 33256: 8748, - 33264: 8491, - 33265: 8240, - 33266: 9839, - 33267: 9837, - 33268: 9834, - 33269: 8224, - 33270: 8225, - 33271: 182, - 33276: 9711, - 33359: 65296, - 33360: 65297, - 33361: 65298, - 33362: 65299, - 33363: 65300, - 33364: 65301, - 33365: 65302, - 33366: 65303, - 33367: 65304, - 33368: 65305, - 33376: 65313, - 33377: 65314, - 33378: 65315, - 33379: 65316, - 33380: 65317, - 33381: 65318, - 33382: 65319, - 33383: 65320, - 33384: 65321, - 33385: 65322, - 33386: 65323, - 33387: 65324, - 33388: 65325, - 33389: 65326, - 33390: 65327, - 33391: 65328, - 33392: 65329, - 33393: 65330, - 33394: 65331, - 33395: 65332, - 33396: 65333, - 33397: 65334, - 33398: 65335, - 33399: 65336, - 33400: 65337, - 33401: 65338, - 33409: 65345, - 33410: 65346, - 33411: 65347, - 33412: 65348, - 33413: 65349, - 33414: 65350, - 33415: 65351, - 33416: 65352, - 33417: 65353, - 33418: 65354, - 33419: 65355, - 33420: 65356, - 33421: 65357, - 33422: 65358, - 33423: 65359, - 33424: 65360, - 33425: 65361, - 33426: 65362, - 33427: 65363, - 33428: 65364, - 33429: 65365, - 33430: 65366, - 33431: 65367, - 33432: 65368, - 33433: 65369, - 33434: 65370, - 33439: 12353, - 33440: 12354, - 33441: 12355, - 33442: 12356, - 33443: 12357, - 33444: 12358, - 33445: 12359, - 33446: 12360, - 33447: 12361, - 33448: 12362, - 33449: 12363, - 33450: 12364, - 33451: 12365, - 33452: 12366, - 33453: 12367, - 33454: 12368, - 33455: 12369, - 33456: 12370, - 33457: 12371, - 33458: 12372, - 33459: 12373, - 33460: 12374, - 33461: 12375, - 33462: 12376, - 33463: 12377, - 33464: 12378, - 33465: 12379, - 33466: 12380, - 33467: 12381, - 33468: 12382, - 33469: 12383, - 33470: 12384, - 33471: 12385, - 33472: 12386, - 33473: 12387, - 33474: 12388, - 33475: 12389, - 33476: 12390, - 33477: 12391, - 33478: 12392, - 33479: 12393, - 33480: 12394, - 33481: 12395, - 33482: 12396, - 33483: 12397, - 33484: 12398, - 33485: 12399, - 33486: 12400, - 33487: 12401, - 33488: 12402, - 33489: 12403, - 33490: 12404, - 33491: 12405, - 33492: 12406, - 33493: 12407, - 33494: 12408, - 33495: 12409, - 33496: 12410, - 33497: 12411, - 33498: 12412, - 33499: 12413, - 33500: 12414, - 33501: 12415, - 33502: 12416, - 33503: 12417, - 33504: 12418, - 33505: 12419, - 33506: 12420, - 33507: 12421, - 33508: 12422, - 33509: 12423, - 33510: 12424, - 33511: 12425, - 33512: 12426, - 33513: 12427, - 33514: 12428, - 33515: 12429, - 33516: 12430, - 33517: 12431, - 33518: 12432, - 33519: 12433, - 33520: 12434, - 33521: 12435, - 33600: 12449, - 33601: 12450, - 33602: 12451, - 33603: 12452, - 33604: 12453, - 33605: 12454, - 33606: 12455, - 33607: 12456, - 33608: 12457, - 33609: 12458, - 33610: 12459, - 33611: 12460, - 33612: 12461, - 33613: 12462, - 33614: 12463, - 33615: 12464, - 33616: 12465, - 33617: 12466, - 33618: 12467, - 33619: 12468, - 33620: 12469, - 33621: 12470, - 33622: 12471, - 33623: 12472, - 33624: 12473, - 33625: 12474, - 33626: 12475, - 33627: 12476, - 33628: 12477, - 33629: 12478, - 33630: 12479, - 33631: 12480, - 33632: 12481, - 33633: 12482, - 33634: 12483, - 33635: 12484, - 33636: 12485, - 33637: 12486, - 33638: 12487, - 33639: 12488, - 33640: 12489, - 33641: 12490, - 33642: 12491, - 33643: 12492, - 33644: 12493, - 33645: 12494, - 33646: 12495, - 33647: 12496, - 33648: 12497, - 33649: 12498, - 33650: 12499, - 33651: 12500, - 33652: 12501, - 33653: 12502, - 33654: 12503, - 33655: 12504, - 33656: 12505, - 33657: 12506, - 33658: 12507, - 33659: 12508, - 33660: 12509, - 33661: 12510, - 33662: 12511, - 33664: 12512, - 33665: 12513, - 33666: 12514, - 33667: 12515, - 33668: 12516, - 33669: 12517, - 33670: 12518, - 33671: 12519, - 33672: 12520, - 33673: 12521, - 33674: 12522, - 33675: 12523, - 33676: 12524, - 33677: 12525, - 33678: 12526, - 33679: 12527, - 33680: 12528, - 33681: 12529, - 33682: 12530, - 33683: 12531, - 33684: 12532, - 33685: 12533, - 33686: 12534, - 33695: 913, - 33696: 914, - 33697: 915, - 33698: 916, - 33699: 917, - 33700: 918, - 33701: 919, - 33702: 920, - 33703: 921, - 33704: 922, - 33705: 923, - 33706: 924, - 33707: 925, - 33708: 926, - 33709: 927, - 33710: 928, - 33711: 929, - 33712: 931, - 33713: 932, - 33714: 933, - 33715: 934, - 33716: 935, - 33717: 936, - 33718: 937, - 33727: 945, - 33728: 946, - 33729: 947, - 33730: 948, - 33731: 949, - 33732: 950, - 33733: 951, - 33734: 952, - 33735: 953, - 33736: 954, - 33737: 955, - 33738: 956, - 33739: 957, - 33740: 958, - 33741: 959, - 33742: 960, - 33743: 961, - 33744: 963, - 33745: 964, - 33746: 965, - 33747: 966, - 33748: 967, - 33749: 968, - 33750: 969, - 33856: 1040, - 33857: 1041, - 33858: 1042, - 33859: 1043, - 33860: 1044, - 33861: 1045, - 33862: 1025, - 33863: 1046, - 33864: 1047, - 33865: 1048, - 33866: 1049, - 33867: 1050, - 33868: 1051, - 33869: 1052, - 33870: 1053, - 33871: 1054, - 33872: 1055, - 33873: 1056, - 33874: 1057, - 33875: 1058, - 33876: 1059, - 33877: 1060, - 33878: 1061, - 33879: 1062, - 33880: 1063, - 33881: 1064, - 33882: 1065, - 33883: 1066, - 33884: 1067, - 33885: 1068, - 33886: 1069, - 33887: 1070, - 33888: 1071, - 33904: 1072, - 33905: 1073, - 33906: 1074, - 33907: 1075, - 33908: 1076, - 33909: 1077, - 33910: 1105, - 33911: 1078, - 33912: 1079, - 33913: 1080, - 33914: 1081, - 33915: 1082, - 33916: 1083, - 33917: 1084, - 33918: 1085, - 33920: 1086, - 33921: 1087, - 33922: 1088, - 33923: 1089, - 33924: 1090, - 33925: 1091, - 33926: 1092, - 33927: 1093, - 33928: 1094, - 33929: 1095, - 33930: 1096, - 33931: 1097, - 33932: 1098, - 33933: 1099, - 33934: 1100, - 33935: 1101, - 33936: 1102, - 33937: 1103, - 33951: 9472, - 33952: 9474, - 33953: 9484, - 33954: 9488, - 33955: 9496, - 33956: 9492, - 33957: 9500, - 33958: 9516, - 33959: 9508, - 33960: 9524, - 33961: 9532, - 33962: 9473, - 33963: 9475, - 33964: 9487, - 33965: 9491, - 33966: 9499, - 33967: 9495, - 33968: 9507, - 33969: 9523, - 33970: 9515, - 33971: 9531, - 33972: 9547, - 33973: 9504, - 33974: 9519, - 33975: 9512, - 33976: 9527, - 33977: 9535, - 33978: 9501, - 33979: 9520, - 33980: 9509, - 33981: 9528, - 33982: 9538, - 34624: 9312, - 34625: 9313, - 34626: 9314, - 34627: 9315, - 34628: 9316, - 34629: 9317, - 34630: 9318, - 34631: 9319, - 34632: 9320, - 34633: 9321, - 34634: 9322, - 34635: 9323, - 34636: 9324, - 34637: 9325, - 34638: 9326, - 34639: 9327, - 34640: 9328, - 34641: 9329, - 34642: 9330, - 34643: 9331, - 34644: 8544, - 34645: 8545, - 34646: 8546, - 34647: 8547, - 34648: 8548, - 34649: 8549, - 34650: 8550, - 34651: 8551, - 34652: 8552, - 34653: 8553, - 34655: 13129, - 34656: 13076, - 34657: 13090, - 34658: 13133, - 34659: 13080, - 34660: 13095, - 34661: 13059, - 34662: 13110, - 34663: 13137, - 34664: 13143, - 34665: 13069, - 34666: 13094, - 34667: 13091, - 34668: 13099, - 34669: 13130, - 34670: 13115, - 34671: 13212, - 34672: 13213, - 34673: 13214, - 34674: 13198, - 34675: 13199, - 34676: 13252, - 34677: 13217, - 34686: 13179, - 34688: 12317, - 34689: 12319, - 34690: 8470, - 34691: 13261, - 34692: 8481, - 34693: 12964, - 34694: 12965, - 34695: 12966, - 34696: 12967, - 34697: 12968, - 34698: 12849, - 34699: 12850, - 34700: 12857, - 34701: 13182, - 34702: 13181, - 34703: 13180, - 34704: 8786, - 34705: 8801, - 34706: 8747, - 34707: 8750, - 34708: 8721, - 34709: 8730, - 34710: 8869, - 34711: 8736, - 34712: 8735, - 34713: 8895, - 34714: 8757, - 34715: 8745, - 34716: 8746, - 34975: 20124, - 34976: 21782, - 34977: 23043, - 34978: 38463, - 34979: 21696, - 34980: 24859, - 34981: 25384, - 34982: 23030, - 34983: 36898, - 34984: 33909, - 34985: 33564, - 34986: 31312, - 34987: 24746, - 34988: 25569, - 34989: 28197, - 34990: 26093, - 34991: 33894, - 34992: 33446, - 34993: 39925, - 34994: 26771, - 34995: 22311, - 34996: 26017, - 34997: 25201, - 34998: 23451, - 34999: 22992, - 35000: 34427, - 35001: 39156, - 35002: 32098, - 35003: 32190, - 35004: 39822, - 35005: 25110, - 35006: 31903, - 35007: 34999, - 35008: 23433, - 35009: 24245, - 35010: 25353, - 35011: 26263, - 35012: 26696, - 35013: 38343, - 35014: 38797, - 35015: 26447, - 35016: 20197, - 35017: 20234, - 35018: 20301, - 35019: 20381, - 35020: 20553, - 35021: 22258, - 35022: 22839, - 35023: 22996, - 35024: 23041, - 35025: 23561, - 35026: 24799, - 35027: 24847, - 35028: 24944, - 35029: 26131, - 35030: 26885, - 35031: 28858, - 35032: 30031, - 35033: 30064, - 35034: 31227, - 35035: 32173, - 35036: 32239, - 35037: 32963, - 35038: 33806, - 35039: 34915, - 35040: 35586, - 35041: 36949, - 35042: 36986, - 35043: 21307, - 35044: 20117, - 35045: 20133, - 35046: 22495, - 35047: 32946, - 35048: 37057, - 35049: 30959, - 35050: 19968, - 35051: 22769, - 35052: 28322, - 35053: 36920, - 35054: 31282, - 35055: 33576, - 35056: 33419, - 35057: 39983, - 35058: 20801, - 35059: 21360, - 35060: 21693, - 35061: 21729, - 35062: 22240, - 35063: 23035, - 35064: 24341, - 35065: 39154, - 35066: 28139, - 35067: 32996, - 35068: 34093, - 35136: 38498, - 35137: 38512, - 35138: 38560, - 35139: 38907, - 35140: 21515, - 35141: 21491, - 35142: 23431, - 35143: 28879, - 35144: 32701, - 35145: 36802, - 35146: 38632, - 35147: 21359, - 35148: 40284, - 35149: 31418, - 35150: 19985, - 35151: 30867, - 35152: 33276, - 35153: 28198, - 35154: 22040, - 35155: 21764, - 35156: 27421, - 35157: 34074, - 35158: 39995, - 35159: 23013, - 35160: 21417, - 35161: 28006, - 35162: 29916, - 35163: 38287, - 35164: 22082, - 35165: 20113, - 35166: 36939, - 35167: 38642, - 35168: 33615, - 35169: 39180, - 35170: 21473, - 35171: 21942, - 35172: 23344, - 35173: 24433, - 35174: 26144, - 35175: 26355, - 35176: 26628, - 35177: 27704, - 35178: 27891, - 35179: 27945, - 35180: 29787, - 35181: 30408, - 35182: 31310, - 35183: 38964, - 35184: 33521, - 35185: 34907, - 35186: 35424, - 35187: 37613, - 35188: 28082, - 35189: 30123, - 35190: 30410, - 35191: 39365, - 35192: 24742, - 35193: 35585, - 35194: 36234, - 35195: 38322, - 35196: 27022, - 35197: 21421, - 35198: 20870, - 35200: 22290, - 35201: 22576, - 35202: 22852, - 35203: 23476, - 35204: 24310, - 35205: 24616, - 35206: 25513, - 35207: 25588, - 35208: 27839, - 35209: 28436, - 35210: 28814, - 35211: 28948, - 35212: 29017, - 35213: 29141, - 35214: 29503, - 35215: 32257, - 35216: 33398, - 35217: 33489, - 35218: 34199, - 35219: 36960, - 35220: 37467, - 35221: 40219, - 35222: 22633, - 35223: 26044, - 35224: 27738, - 35225: 29989, - 35226: 20985, - 35227: 22830, - 35228: 22885, - 35229: 24448, - 35230: 24540, - 35231: 25276, - 35232: 26106, - 35233: 27178, - 35234: 27431, - 35235: 27572, - 35236: 29579, - 35237: 32705, - 35238: 35158, - 35239: 40236, - 35240: 40206, - 35241: 40644, - 35242: 23713, - 35243: 27798, - 35244: 33659, - 35245: 20740, - 35246: 23627, - 35247: 25014, - 35248: 33222, - 35249: 26742, - 35250: 29281, - 35251: 20057, - 35252: 20474, - 35253: 21368, - 35254: 24681, - 35255: 28201, - 35256: 31311, - 35257: 38899, - 35258: 19979, - 35259: 21270, - 35260: 20206, - 35261: 20309, - 35262: 20285, - 35263: 20385, - 35264: 20339, - 35265: 21152, - 35266: 21487, - 35267: 22025, - 35268: 22799, - 35269: 23233, - 35270: 23478, - 35271: 23521, - 35272: 31185, - 35273: 26247, - 35274: 26524, - 35275: 26550, - 35276: 27468, - 35277: 27827, - 35278: 28779, - 35279: 29634, - 35280: 31117, - 35281: 31166, - 35282: 31292, - 35283: 31623, - 35284: 33457, - 35285: 33499, - 35286: 33540, - 35287: 33655, - 35288: 33775, - 35289: 33747, - 35290: 34662, - 35291: 35506, - 35292: 22057, - 35293: 36008, - 35294: 36838, - 35295: 36942, - 35296: 38686, - 35297: 34442, - 35298: 20420, - 35299: 23784, - 35300: 25105, - 35301: 29273, - 35302: 30011, - 35303: 33253, - 35304: 33469, - 35305: 34558, - 35306: 36032, - 35307: 38597, - 35308: 39187, - 35309: 39381, - 35310: 20171, - 35311: 20250, - 35312: 35299, - 35313: 22238, - 35314: 22602, - 35315: 22730, - 35316: 24315, - 35317: 24555, - 35318: 24618, - 35319: 24724, - 35320: 24674, - 35321: 25040, - 35322: 25106, - 35323: 25296, - 35324: 25913, - 35392: 39745, - 35393: 26214, - 35394: 26800, - 35395: 28023, - 35396: 28784, - 35397: 30028, - 35398: 30342, - 35399: 32117, - 35400: 33445, - 35401: 34809, - 35402: 38283, - 35403: 38542, - 35404: 35997, - 35405: 20977, - 35406: 21182, - 35407: 22806, - 35408: 21683, - 35409: 23475, - 35410: 23830, - 35411: 24936, - 35412: 27010, - 35413: 28079, - 35414: 30861, - 35415: 33995, - 35416: 34903, - 35417: 35442, - 35418: 37799, - 35419: 39608, - 35420: 28012, - 35421: 39336, - 35422: 34521, - 35423: 22435, - 35424: 26623, - 35425: 34510, - 35426: 37390, - 35427: 21123, - 35428: 22151, - 35429: 21508, - 35430: 24275, - 35431: 25313, - 35432: 25785, - 35433: 26684, - 35434: 26680, - 35435: 27579, - 35436: 29554, - 35437: 30906, - 35438: 31339, - 35439: 35226, - 35440: 35282, - 35441: 36203, - 35442: 36611, - 35443: 37101, - 35444: 38307, - 35445: 38548, - 35446: 38761, - 35447: 23398, - 35448: 23731, - 35449: 27005, - 35450: 38989, - 35451: 38990, - 35452: 25499, - 35453: 31520, - 35454: 27179, - 35456: 27263, - 35457: 26806, - 35458: 39949, - 35459: 28511, - 35460: 21106, - 35461: 21917, - 35462: 24688, - 35463: 25324, - 35464: 27963, - 35465: 28167, - 35466: 28369, - 35467: 33883, - 35468: 35088, - 35469: 36676, - 35470: 19988, - 35471: 39993, - 35472: 21494, - 35473: 26907, - 35474: 27194, - 35475: 38788, - 35476: 26666, - 35477: 20828, - 35478: 31427, - 35479: 33970, - 35480: 37340, - 35481: 37772, - 35482: 22107, - 35483: 40232, - 35484: 26658, - 35485: 33541, - 35486: 33841, - 35487: 31909, - 35488: 21000, - 35489: 33477, - 35490: 29926, - 35491: 20094, - 35492: 20355, - 35493: 20896, - 35494: 23506, - 35495: 21002, - 35496: 21208, - 35497: 21223, - 35498: 24059, - 35499: 21914, - 35500: 22570, - 35501: 23014, - 35502: 23436, - 35503: 23448, - 35504: 23515, - 35505: 24178, - 35506: 24185, - 35507: 24739, - 35508: 24863, - 35509: 24931, - 35510: 25022, - 35511: 25563, - 35512: 25954, - 35513: 26577, - 35514: 26707, - 35515: 26874, - 35516: 27454, - 35517: 27475, - 35518: 27735, - 35519: 28450, - 35520: 28567, - 35521: 28485, - 35522: 29872, - 35523: 29976, - 35524: 30435, - 35525: 30475, - 35526: 31487, - 35527: 31649, - 35528: 31777, - 35529: 32233, - 35530: 32566, - 35531: 32752, - 35532: 32925, - 35533: 33382, - 35534: 33694, - 35535: 35251, - 35536: 35532, - 35537: 36011, - 35538: 36996, - 35539: 37969, - 35540: 38291, - 35541: 38289, - 35542: 38306, - 35543: 38501, - 35544: 38867, - 35545: 39208, - 35546: 33304, - 35547: 20024, - 35548: 21547, - 35549: 23736, - 35550: 24012, - 35551: 29609, - 35552: 30284, - 35553: 30524, - 35554: 23721, - 35555: 32747, - 35556: 36107, - 35557: 38593, - 35558: 38929, - 35559: 38996, - 35560: 39000, - 35561: 20225, - 35562: 20238, - 35563: 21361, - 35564: 21916, - 35565: 22120, - 35566: 22522, - 35567: 22855, - 35568: 23305, - 35569: 23492, - 35570: 23696, - 35571: 24076, - 35572: 24190, - 35573: 24524, - 35574: 25582, - 35575: 26426, - 35576: 26071, - 35577: 26082, - 35578: 26399, - 35579: 26827, - 35580: 26820, - 35648: 27231, - 35649: 24112, - 35650: 27589, - 35651: 27671, - 35652: 27773, - 35653: 30079, - 35654: 31048, - 35655: 23395, - 35656: 31232, - 35657: 32000, - 35658: 24509, - 35659: 35215, - 35660: 35352, - 35661: 36020, - 35662: 36215, - 35663: 36556, - 35664: 36637, - 35665: 39138, - 35666: 39438, - 35667: 39740, - 35668: 20096, - 35669: 20605, - 35670: 20736, - 35671: 22931, - 35672: 23452, - 35673: 25135, - 35674: 25216, - 35675: 25836, - 35676: 27450, - 35677: 29344, - 35678: 30097, - 35679: 31047, - 35680: 32681, - 35681: 34811, - 35682: 35516, - 35683: 35696, - 35684: 25516, - 35685: 33738, - 35686: 38816, - 35687: 21513, - 35688: 21507, - 35689: 21931, - 35690: 26708, - 35691: 27224, - 35692: 35440, - 35693: 30759, - 35694: 26485, - 35695: 40653, - 35696: 21364, - 35697: 23458, - 35698: 33050, - 35699: 34384, - 35700: 36870, - 35701: 19992, - 35702: 20037, - 35703: 20167, - 35704: 20241, - 35705: 21450, - 35706: 21560, - 35707: 23470, - 35708: 24339, - 35709: 24613, - 35710: 25937, - 35712: 26429, - 35713: 27714, - 35714: 27762, - 35715: 27875, - 35716: 28792, - 35717: 29699, - 35718: 31350, - 35719: 31406, - 35720: 31496, - 35721: 32026, - 35722: 31998, - 35723: 32102, - 35724: 26087, - 35725: 29275, - 35726: 21435, - 35727: 23621, - 35728: 24040, - 35729: 25298, - 35730: 25312, - 35731: 25369, - 35732: 28192, - 35733: 34394, - 35734: 35377, - 35735: 36317, - 35736: 37624, - 35737: 28417, - 35738: 31142, - 35739: 39770, - 35740: 20136, - 35741: 20139, - 35742: 20140, - 35743: 20379, - 35744: 20384, - 35745: 20689, - 35746: 20807, - 35747: 31478, - 35748: 20849, - 35749: 20982, - 35750: 21332, - 35751: 21281, - 35752: 21375, - 35753: 21483, - 35754: 21932, - 35755: 22659, - 35756: 23777, - 35757: 24375, - 35758: 24394, - 35759: 24623, - 35760: 24656, - 35761: 24685, - 35762: 25375, - 35763: 25945, - 35764: 27211, - 35765: 27841, - 35766: 29378, - 35767: 29421, - 35768: 30703, - 35769: 33016, - 35770: 33029, - 35771: 33288, - 35772: 34126, - 35773: 37111, - 35774: 37857, - 35775: 38911, - 35776: 39255, - 35777: 39514, - 35778: 20208, - 35779: 20957, - 35780: 23597, - 35781: 26241, - 35782: 26989, - 35783: 23616, - 35784: 26354, - 35785: 26997, - 35786: 29577, - 35787: 26704, - 35788: 31873, - 35789: 20677, - 35790: 21220, - 35791: 22343, - 35792: 24062, - 35793: 37670, - 35794: 26020, - 35795: 27427, - 35796: 27453, - 35797: 29748, - 35798: 31105, - 35799: 31165, - 35800: 31563, - 35801: 32202, - 35802: 33465, - 35803: 33740, - 35804: 34943, - 35805: 35167, - 35806: 35641, - 35807: 36817, - 35808: 37329, - 35809: 21535, - 35810: 37504, - 35811: 20061, - 35812: 20534, - 35813: 21477, - 35814: 21306, - 35815: 29399, - 35816: 29590, - 35817: 30697, - 35818: 33510, - 35819: 36527, - 35820: 39366, - 35821: 39368, - 35822: 39378, - 35823: 20855, - 35824: 24858, - 35825: 34398, - 35826: 21936, - 35827: 31354, - 35828: 20598, - 35829: 23507, - 35830: 36935, - 35831: 38533, - 35832: 20018, - 35833: 27355, - 35834: 37351, - 35835: 23633, - 35836: 23624, - 35904: 25496, - 35905: 31391, - 35906: 27795, - 35907: 38772, - 35908: 36705, - 35909: 31402, - 35910: 29066, - 35911: 38536, - 35912: 31874, - 35913: 26647, - 35914: 32368, - 35915: 26705, - 35916: 37740, - 35917: 21234, - 35918: 21531, - 35919: 34219, - 35920: 35347, - 35921: 32676, - 35922: 36557, - 35923: 37089, - 35924: 21350, - 35925: 34952, - 35926: 31041, - 35927: 20418, - 35928: 20670, - 35929: 21009, - 35930: 20804, - 35931: 21843, - 35932: 22317, - 35933: 29674, - 35934: 22411, - 35935: 22865, - 35936: 24418, - 35937: 24452, - 35938: 24693, - 35939: 24950, - 35940: 24935, - 35941: 25001, - 35942: 25522, - 35943: 25658, - 35944: 25964, - 35945: 26223, - 35946: 26690, - 35947: 28179, - 35948: 30054, - 35949: 31293, - 35950: 31995, - 35951: 32076, - 35952: 32153, - 35953: 32331, - 35954: 32619, - 35955: 33550, - 35956: 33610, - 35957: 34509, - 35958: 35336, - 35959: 35427, - 35960: 35686, - 35961: 36605, - 35962: 38938, - 35963: 40335, - 35964: 33464, - 35965: 36814, - 35966: 39912, - 35968: 21127, - 35969: 25119, - 35970: 25731, - 35971: 28608, - 35972: 38553, - 35973: 26689, - 35974: 20625, - 35975: 27424, - 35976: 27770, - 35977: 28500, - 35978: 31348, - 35979: 32080, - 35980: 34880, - 35981: 35363, - 35982: 26376, - 35983: 20214, - 35984: 20537, - 35985: 20518, - 35986: 20581, - 35987: 20860, - 35988: 21048, - 35989: 21091, - 35990: 21927, - 35991: 22287, - 35992: 22533, - 35993: 23244, - 35994: 24314, - 35995: 25010, - 35996: 25080, - 35997: 25331, - 35998: 25458, - 35999: 26908, - 36000: 27177, - 36001: 29309, - 36002: 29356, - 36003: 29486, - 36004: 30740, - 36005: 30831, - 36006: 32121, - 36007: 30476, - 36008: 32937, - 36009: 35211, - 36010: 35609, - 36011: 36066, - 36012: 36562, - 36013: 36963, - 36014: 37749, - 36015: 38522, - 36016: 38997, - 36017: 39443, - 36018: 40568, - 36019: 20803, - 36020: 21407, - 36021: 21427, - 36022: 24187, - 36023: 24358, - 36024: 28187, - 36025: 28304, - 36026: 29572, - 36027: 29694, - 36028: 32067, - 36029: 33335, - 36030: 35328, - 36031: 35578, - 36032: 38480, - 36033: 20046, - 36034: 20491, - 36035: 21476, - 36036: 21628, - 36037: 22266, - 36038: 22993, - 36039: 23396, - 36040: 24049, - 36041: 24235, - 36042: 24359, - 36043: 25144, - 36044: 25925, - 36045: 26543, - 36046: 28246, - 36047: 29392, - 36048: 31946, - 36049: 34996, - 36050: 32929, - 36051: 32993, - 36052: 33776, - 36053: 34382, - 36054: 35463, - 36055: 36328, - 36056: 37431, - 36057: 38599, - 36058: 39015, - 36059: 40723, - 36060: 20116, - 36061: 20114, - 36062: 20237, - 36063: 21320, - 36064: 21577, - 36065: 21566, - 36066: 23087, - 36067: 24460, - 36068: 24481, - 36069: 24735, - 36070: 26791, - 36071: 27278, - 36072: 29786, - 36073: 30849, - 36074: 35486, - 36075: 35492, - 36076: 35703, - 36077: 37264, - 36078: 20062, - 36079: 39881, - 36080: 20132, - 36081: 20348, - 36082: 20399, - 36083: 20505, - 36084: 20502, - 36085: 20809, - 36086: 20844, - 36087: 21151, - 36088: 21177, - 36089: 21246, - 36090: 21402, - 36091: 21475, - 36092: 21521, - 36160: 21518, - 36161: 21897, - 36162: 22353, - 36163: 22434, - 36164: 22909, - 36165: 23380, - 36166: 23389, - 36167: 23439, - 36168: 24037, - 36169: 24039, - 36170: 24055, - 36171: 24184, - 36172: 24195, - 36173: 24218, - 36174: 24247, - 36175: 24344, - 36176: 24658, - 36177: 24908, - 36178: 25239, - 36179: 25304, - 36180: 25511, - 36181: 25915, - 36182: 26114, - 36183: 26179, - 36184: 26356, - 36185: 26477, - 36186: 26657, - 36187: 26775, - 36188: 27083, - 36189: 27743, - 36190: 27946, - 36191: 28009, - 36192: 28207, - 36193: 28317, - 36194: 30002, - 36195: 30343, - 36196: 30828, - 36197: 31295, - 36198: 31968, - 36199: 32005, - 36200: 32024, - 36201: 32094, - 36202: 32177, - 36203: 32789, - 36204: 32771, - 36205: 32943, - 36206: 32945, - 36207: 33108, - 36208: 33167, - 36209: 33322, - 36210: 33618, - 36211: 34892, - 36212: 34913, - 36213: 35611, - 36214: 36002, - 36215: 36092, - 36216: 37066, - 36217: 37237, - 36218: 37489, - 36219: 30783, - 36220: 37628, - 36221: 38308, - 36222: 38477, - 36224: 38917, - 36225: 39321, - 36226: 39640, - 36227: 40251, - 36228: 21083, - 36229: 21163, - 36230: 21495, - 36231: 21512, - 36232: 22741, - 36233: 25335, - 36234: 28640, - 36235: 35946, - 36236: 36703, - 36237: 40633, - 36238: 20811, - 36239: 21051, - 36240: 21578, - 36241: 22269, - 36242: 31296, - 36243: 37239, - 36244: 40288, - 36245: 40658, - 36246: 29508, - 36247: 28425, - 36248: 33136, - 36249: 29969, - 36250: 24573, - 36251: 24794, - 36252: 39592, - 36253: 29403, - 36254: 36796, - 36255: 27492, - 36256: 38915, - 36257: 20170, - 36258: 22256, - 36259: 22372, - 36260: 22718, - 36261: 23130, - 36262: 24680, - 36263: 25031, - 36264: 26127, - 36265: 26118, - 36266: 26681, - 36267: 26801, - 36268: 28151, - 36269: 30165, - 36270: 32058, - 36271: 33390, - 36272: 39746, - 36273: 20123, - 36274: 20304, - 36275: 21449, - 36276: 21766, - 36277: 23919, - 36278: 24038, - 36279: 24046, - 36280: 26619, - 36281: 27801, - 36282: 29811, - 36283: 30722, - 36284: 35408, - 36285: 37782, - 36286: 35039, - 36287: 22352, - 36288: 24231, - 36289: 25387, - 36290: 20661, - 36291: 20652, - 36292: 20877, - 36293: 26368, - 36294: 21705, - 36295: 22622, - 36296: 22971, - 36297: 23472, - 36298: 24425, - 36299: 25165, - 36300: 25505, - 36301: 26685, - 36302: 27507, - 36303: 28168, - 36304: 28797, - 36305: 37319, - 36306: 29312, - 36307: 30741, - 36308: 30758, - 36309: 31085, - 36310: 25998, - 36311: 32048, - 36312: 33756, - 36313: 35009, - 36314: 36617, - 36315: 38555, - 36316: 21092, - 36317: 22312, - 36318: 26448, - 36319: 32618, - 36320: 36001, - 36321: 20916, - 36322: 22338, - 36323: 38442, - 36324: 22586, - 36325: 27018, - 36326: 32948, - 36327: 21682, - 36328: 23822, - 36329: 22524, - 36330: 30869, - 36331: 40442, - 36332: 20316, - 36333: 21066, - 36334: 21643, - 36335: 25662, - 36336: 26152, - 36337: 26388, - 36338: 26613, - 36339: 31364, - 36340: 31574, - 36341: 32034, - 36342: 37679, - 36343: 26716, - 36344: 39853, - 36345: 31545, - 36346: 21273, - 36347: 20874, - 36348: 21047, - 36416: 23519, - 36417: 25334, - 36418: 25774, - 36419: 25830, - 36420: 26413, - 36421: 27578, - 36422: 34217, - 36423: 38609, - 36424: 30352, - 36425: 39894, - 36426: 25420, - 36427: 37638, - 36428: 39851, - 36429: 30399, - 36430: 26194, - 36431: 19977, - 36432: 20632, - 36433: 21442, - 36434: 23665, - 36435: 24808, - 36436: 25746, - 36437: 25955, - 36438: 26719, - 36439: 29158, - 36440: 29642, - 36441: 29987, - 36442: 31639, - 36443: 32386, - 36444: 34453, - 36445: 35715, - 36446: 36059, - 36447: 37240, - 36448: 39184, - 36449: 26028, - 36450: 26283, - 36451: 27531, - 36452: 20181, - 36453: 20180, - 36454: 20282, - 36455: 20351, - 36456: 21050, - 36457: 21496, - 36458: 21490, - 36459: 21987, - 36460: 22235, - 36461: 22763, - 36462: 22987, - 36463: 22985, - 36464: 23039, - 36465: 23376, - 36466: 23629, - 36467: 24066, - 36468: 24107, - 36469: 24535, - 36470: 24605, - 36471: 25351, - 36472: 25903, - 36473: 23388, - 36474: 26031, - 36475: 26045, - 36476: 26088, - 36477: 26525, - 36478: 27490, - 36480: 27515, - 36481: 27663, - 36482: 29509, - 36483: 31049, - 36484: 31169, - 36485: 31992, - 36486: 32025, - 36487: 32043, - 36488: 32930, - 36489: 33026, - 36490: 33267, - 36491: 35222, - 36492: 35422, - 36493: 35433, - 36494: 35430, - 36495: 35468, - 36496: 35566, - 36497: 36039, - 36498: 36060, - 36499: 38604, - 36500: 39164, - 36501: 27503, - 36502: 20107, - 36503: 20284, - 36504: 20365, - 36505: 20816, - 36506: 23383, - 36507: 23546, - 36508: 24904, - 36509: 25345, - 36510: 26178, - 36511: 27425, - 36512: 28363, - 36513: 27835, - 36514: 29246, - 36515: 29885, - 36516: 30164, - 36517: 30913, - 36518: 31034, - 36519: 32780, - 36520: 32819, - 36521: 33258, - 36522: 33940, - 36523: 36766, - 36524: 27728, - 36525: 40575, - 36526: 24335, - 36527: 35672, - 36528: 40235, - 36529: 31482, - 36530: 36600, - 36531: 23437, - 36532: 38635, - 36533: 19971, - 36534: 21489, - 36535: 22519, - 36536: 22833, - 36537: 23241, - 36538: 23460, - 36539: 24713, - 36540: 28287, - 36541: 28422, - 36542: 30142, - 36543: 36074, - 36544: 23455, - 36545: 34048, - 36546: 31712, - 36547: 20594, - 36548: 26612, - 36549: 33437, - 36550: 23649, - 36551: 34122, - 36552: 32286, - 36553: 33294, - 36554: 20889, - 36555: 23556, - 36556: 25448, - 36557: 36198, - 36558: 26012, - 36559: 29038, - 36560: 31038, - 36561: 32023, - 36562: 32773, - 36563: 35613, - 36564: 36554, - 36565: 36974, - 36566: 34503, - 36567: 37034, - 36568: 20511, - 36569: 21242, - 36570: 23610, - 36571: 26451, - 36572: 28796, - 36573: 29237, - 36574: 37196, - 36575: 37320, - 36576: 37675, - 36577: 33509, - 36578: 23490, - 36579: 24369, - 36580: 24825, - 36581: 20027, - 36582: 21462, - 36583: 23432, - 36584: 25163, - 36585: 26417, - 36586: 27530, - 36587: 29417, - 36588: 29664, - 36589: 31278, - 36590: 33131, - 36591: 36259, - 36592: 37202, - 36593: 39318, - 36594: 20754, - 36595: 21463, - 36596: 21610, - 36597: 23551, - 36598: 25480, - 36599: 27193, - 36600: 32172, - 36601: 38656, - 36602: 22234, - 36603: 21454, - 36604: 21608, - 36672: 23447, - 36673: 23601, - 36674: 24030, - 36675: 20462, - 36676: 24833, - 36677: 25342, - 36678: 27954, - 36679: 31168, - 36680: 31179, - 36681: 32066, - 36682: 32333, - 36683: 32722, - 36684: 33261, - 36685: 33311, - 36686: 33936, - 36687: 34886, - 36688: 35186, - 36689: 35728, - 36690: 36468, - 36691: 36655, - 36692: 36913, - 36693: 37195, - 36694: 37228, - 36695: 38598, - 36696: 37276, - 36697: 20160, - 36698: 20303, - 36699: 20805, - 36700: 21313, - 36701: 24467, - 36702: 25102, - 36703: 26580, - 36704: 27713, - 36705: 28171, - 36706: 29539, - 36707: 32294, - 36708: 37325, - 36709: 37507, - 36710: 21460, - 36711: 22809, - 36712: 23487, - 36713: 28113, - 36714: 31069, - 36715: 32302, - 36716: 31899, - 36717: 22654, - 36718: 29087, - 36719: 20986, - 36720: 34899, - 36721: 36848, - 36722: 20426, - 36723: 23803, - 36724: 26149, - 36725: 30636, - 36726: 31459, - 36727: 33308, - 36728: 39423, - 36729: 20934, - 36730: 24490, - 36731: 26092, - 36732: 26991, - 36733: 27529, - 36734: 28147, - 36736: 28310, - 36737: 28516, - 36738: 30462, - 36739: 32020, - 36740: 24033, - 36741: 36981, - 36742: 37255, - 36743: 38918, - 36744: 20966, - 36745: 21021, - 36746: 25152, - 36747: 26257, - 36748: 26329, - 36749: 28186, - 36750: 24246, - 36751: 32210, - 36752: 32626, - 36753: 26360, - 36754: 34223, - 36755: 34295, - 36756: 35576, - 36757: 21161, - 36758: 21465, - 36759: 22899, - 36760: 24207, - 36761: 24464, - 36762: 24661, - 36763: 37604, - 36764: 38500, - 36765: 20663, - 36766: 20767, - 36767: 21213, - 36768: 21280, - 36769: 21319, - 36770: 21484, - 36771: 21736, - 36772: 21830, - 36773: 21809, - 36774: 22039, - 36775: 22888, - 36776: 22974, - 36777: 23100, - 36778: 23477, - 36779: 23558, - 36780: 23567, - 36781: 23569, - 36782: 23578, - 36783: 24196, - 36784: 24202, - 36785: 24288, - 36786: 24432, - 36787: 25215, - 36788: 25220, - 36789: 25307, - 36790: 25484, - 36791: 25463, - 36792: 26119, - 36793: 26124, - 36794: 26157, - 36795: 26230, - 36796: 26494, - 36797: 26786, - 36798: 27167, - 36799: 27189, - 36800: 27836, - 36801: 28040, - 36802: 28169, - 36803: 28248, - 36804: 28988, - 36805: 28966, - 36806: 29031, - 36807: 30151, - 36808: 30465, - 36809: 30813, - 36810: 30977, - 36811: 31077, - 36812: 31216, - 36813: 31456, - 36814: 31505, - 36815: 31911, - 36816: 32057, - 36817: 32918, - 36818: 33750, - 36819: 33931, - 36820: 34121, - 36821: 34909, - 36822: 35059, - 36823: 35359, - 36824: 35388, - 36825: 35412, - 36826: 35443, - 36827: 35937, - 36828: 36062, - 36829: 37284, - 36830: 37478, - 36831: 37758, - 36832: 37912, - 36833: 38556, - 36834: 38808, - 36835: 19978, - 36836: 19976, - 36837: 19998, - 36838: 20055, - 36839: 20887, - 36840: 21104, - 36841: 22478, - 36842: 22580, - 36843: 22732, - 36844: 23330, - 36845: 24120, - 36846: 24773, - 36847: 25854, - 36848: 26465, - 36849: 26454, - 36850: 27972, - 36851: 29366, - 36852: 30067, - 36853: 31331, - 36854: 33976, - 36855: 35698, - 36856: 37304, - 36857: 37664, - 36858: 22065, - 36859: 22516, - 36860: 39166, - 36928: 25325, - 36929: 26893, - 36930: 27542, - 36931: 29165, - 36932: 32340, - 36933: 32887, - 36934: 33394, - 36935: 35302, - 36936: 39135, - 36937: 34645, - 36938: 36785, - 36939: 23611, - 36940: 20280, - 36941: 20449, - 36942: 20405, - 36943: 21767, - 36944: 23072, - 36945: 23517, - 36946: 23529, - 36947: 24515, - 36948: 24910, - 36949: 25391, - 36950: 26032, - 36951: 26187, - 36952: 26862, - 36953: 27035, - 36954: 28024, - 36955: 28145, - 36956: 30003, - 36957: 30137, - 36958: 30495, - 36959: 31070, - 36960: 31206, - 36961: 32051, - 36962: 33251, - 36963: 33455, - 36964: 34218, - 36965: 35242, - 36966: 35386, - 36967: 36523, - 36968: 36763, - 36969: 36914, - 36970: 37341, - 36971: 38663, - 36972: 20154, - 36973: 20161, - 36974: 20995, - 36975: 22645, - 36976: 22764, - 36977: 23563, - 36978: 29978, - 36979: 23613, - 36980: 33102, - 36981: 35338, - 36982: 36805, - 36983: 38499, - 36984: 38765, - 36985: 31525, - 36986: 35535, - 36987: 38920, - 36988: 37218, - 36989: 22259, - 36990: 21416, - 36992: 36887, - 36993: 21561, - 36994: 22402, - 36995: 24101, - 36996: 25512, - 36997: 27700, - 36998: 28810, - 36999: 30561, - 37000: 31883, - 37001: 32736, - 37002: 34928, - 37003: 36930, - 37004: 37204, - 37005: 37648, - 37006: 37656, - 37007: 38543, - 37008: 29790, - 37009: 39620, - 37010: 23815, - 37011: 23913, - 37012: 25968, - 37013: 26530, - 37014: 36264, - 37015: 38619, - 37016: 25454, - 37017: 26441, - 37018: 26905, - 37019: 33733, - 37020: 38935, - 37021: 38592, - 37022: 35070, - 37023: 28548, - 37024: 25722, - 37025: 23544, - 37026: 19990, - 37027: 28716, - 37028: 30045, - 37029: 26159, - 37030: 20932, - 37031: 21046, - 37032: 21218, - 37033: 22995, - 37034: 24449, - 37035: 24615, - 37036: 25104, - 37037: 25919, - 37038: 25972, - 37039: 26143, - 37040: 26228, - 37041: 26866, - 37042: 26646, - 37043: 27491, - 37044: 28165, - 37045: 29298, - 37046: 29983, - 37047: 30427, - 37048: 31934, - 37049: 32854, - 37050: 22768, - 37051: 35069, - 37052: 35199, - 37053: 35488, - 37054: 35475, - 37055: 35531, - 37056: 36893, - 37057: 37266, - 37058: 38738, - 37059: 38745, - 37060: 25993, - 37061: 31246, - 37062: 33030, - 37063: 38587, - 37064: 24109, - 37065: 24796, - 37066: 25114, - 37067: 26021, - 37068: 26132, - 37069: 26512, - 37070: 30707, - 37071: 31309, - 37072: 31821, - 37073: 32318, - 37074: 33034, - 37075: 36012, - 37076: 36196, - 37077: 36321, - 37078: 36447, - 37079: 30889, - 37080: 20999, - 37081: 25305, - 37082: 25509, - 37083: 25666, - 37084: 25240, - 37085: 35373, - 37086: 31363, - 37087: 31680, - 37088: 35500, - 37089: 38634, - 37090: 32118, - 37091: 33292, - 37092: 34633, - 37093: 20185, - 37094: 20808, - 37095: 21315, - 37096: 21344, - 37097: 23459, - 37098: 23554, - 37099: 23574, - 37100: 24029, - 37101: 25126, - 37102: 25159, - 37103: 25776, - 37104: 26643, - 37105: 26676, - 37106: 27849, - 37107: 27973, - 37108: 27927, - 37109: 26579, - 37110: 28508, - 37111: 29006, - 37112: 29053, - 37113: 26059, - 37114: 31359, - 37115: 31661, - 37116: 32218, - 37184: 32330, - 37185: 32680, - 37186: 33146, - 37187: 33307, - 37188: 33337, - 37189: 34214, - 37190: 35438, - 37191: 36046, - 37192: 36341, - 37193: 36984, - 37194: 36983, - 37195: 37549, - 37196: 37521, - 37197: 38275, - 37198: 39854, - 37199: 21069, - 37200: 21892, - 37201: 28472, - 37202: 28982, - 37203: 20840, - 37204: 31109, - 37205: 32341, - 37206: 33203, - 37207: 31950, - 37208: 22092, - 37209: 22609, - 37210: 23720, - 37211: 25514, - 37212: 26366, - 37213: 26365, - 37214: 26970, - 37215: 29401, - 37216: 30095, - 37217: 30094, - 37218: 30990, - 37219: 31062, - 37220: 31199, - 37221: 31895, - 37222: 32032, - 37223: 32068, - 37224: 34311, - 37225: 35380, - 37226: 38459, - 37227: 36961, - 37228: 40736, - 37229: 20711, - 37230: 21109, - 37231: 21452, - 37232: 21474, - 37233: 20489, - 37234: 21930, - 37235: 22766, - 37236: 22863, - 37237: 29245, - 37238: 23435, - 37239: 23652, - 37240: 21277, - 37241: 24803, - 37242: 24819, - 37243: 25436, - 37244: 25475, - 37245: 25407, - 37246: 25531, - 37248: 25805, - 37249: 26089, - 37250: 26361, - 37251: 24035, - 37252: 27085, - 37253: 27133, - 37254: 28437, - 37255: 29157, - 37256: 20105, - 37257: 30185, - 37258: 30456, - 37259: 31379, - 37260: 31967, - 37261: 32207, - 37262: 32156, - 37263: 32865, - 37264: 33609, - 37265: 33624, - 37266: 33900, - 37267: 33980, - 37268: 34299, - 37269: 35013, - 37270: 36208, - 37271: 36865, - 37272: 36973, - 37273: 37783, - 37274: 38684, - 37275: 39442, - 37276: 20687, - 37277: 22679, - 37278: 24974, - 37279: 33235, - 37280: 34101, - 37281: 36104, - 37282: 36896, - 37283: 20419, - 37284: 20596, - 37285: 21063, - 37286: 21363, - 37287: 24687, - 37288: 25417, - 37289: 26463, - 37290: 28204, - 37291: 36275, - 37292: 36895, - 37293: 20439, - 37294: 23646, - 37295: 36042, - 37296: 26063, - 37297: 32154, - 37298: 21330, - 37299: 34966, - 37300: 20854, - 37301: 25539, - 37302: 23384, - 37303: 23403, - 37304: 23562, - 37305: 25613, - 37306: 26449, - 37307: 36956, - 37308: 20182, - 37309: 22810, - 37310: 22826, - 37311: 27760, - 37312: 35409, - 37313: 21822, - 37314: 22549, - 37315: 22949, - 37316: 24816, - 37317: 25171, - 37318: 26561, - 37319: 33333, - 37320: 26965, - 37321: 38464, - 37322: 39364, - 37323: 39464, - 37324: 20307, - 37325: 22534, - 37326: 23550, - 37327: 32784, - 37328: 23729, - 37329: 24111, - 37330: 24453, - 37331: 24608, - 37332: 24907, - 37333: 25140, - 37334: 26367, - 37335: 27888, - 37336: 28382, - 37337: 32974, - 37338: 33151, - 37339: 33492, - 37340: 34955, - 37341: 36024, - 37342: 36864, - 37343: 36910, - 37344: 38538, - 37345: 40667, - 37346: 39899, - 37347: 20195, - 37348: 21488, - 37349: 22823, - 37350: 31532, - 37351: 37261, - 37352: 38988, - 37353: 40441, - 37354: 28381, - 37355: 28711, - 37356: 21331, - 37357: 21828, - 37358: 23429, - 37359: 25176, - 37360: 25246, - 37361: 25299, - 37362: 27810, - 37363: 28655, - 37364: 29730, - 37365: 35351, - 37366: 37944, - 37367: 28609, - 37368: 35582, - 37369: 33592, - 37370: 20967, - 37371: 34552, - 37372: 21482, - 37440: 21481, - 37441: 20294, - 37442: 36948, - 37443: 36784, - 37444: 22890, - 37445: 33073, - 37446: 24061, - 37447: 31466, - 37448: 36799, - 37449: 26842, - 37450: 35895, - 37451: 29432, - 37452: 40008, - 37453: 27197, - 37454: 35504, - 37455: 20025, - 37456: 21336, - 37457: 22022, - 37458: 22374, - 37459: 25285, - 37460: 25506, - 37461: 26086, - 37462: 27470, - 37463: 28129, - 37464: 28251, - 37465: 28845, - 37466: 30701, - 37467: 31471, - 37468: 31658, - 37469: 32187, - 37470: 32829, - 37471: 32966, - 37472: 34507, - 37473: 35477, - 37474: 37723, - 37475: 22243, - 37476: 22727, - 37477: 24382, - 37478: 26029, - 37479: 26262, - 37480: 27264, - 37481: 27573, - 37482: 30007, - 37483: 35527, - 37484: 20516, - 37485: 30693, - 37486: 22320, - 37487: 24347, - 37488: 24677, - 37489: 26234, - 37490: 27744, - 37491: 30196, - 37492: 31258, - 37493: 32622, - 37494: 33268, - 37495: 34584, - 37496: 36933, - 37497: 39347, - 37498: 31689, - 37499: 30044, - 37500: 31481, - 37501: 31569, - 37502: 33988, - 37504: 36880, - 37505: 31209, - 37506: 31378, - 37507: 33590, - 37508: 23265, - 37509: 30528, - 37510: 20013, - 37511: 20210, - 37512: 23449, - 37513: 24544, - 37514: 25277, - 37515: 26172, - 37516: 26609, - 37517: 27880, - 37518: 34411, - 37519: 34935, - 37520: 35387, - 37521: 37198, - 37522: 37619, - 37523: 39376, - 37524: 27159, - 37525: 28710, - 37526: 29482, - 37527: 33511, - 37528: 33879, - 37529: 36015, - 37530: 19969, - 37531: 20806, - 37532: 20939, - 37533: 21899, - 37534: 23541, - 37535: 24086, - 37536: 24115, - 37537: 24193, - 37538: 24340, - 37539: 24373, - 37540: 24427, - 37541: 24500, - 37542: 25074, - 37543: 25361, - 37544: 26274, - 37545: 26397, - 37546: 28526, - 37547: 29266, - 37548: 30010, - 37549: 30522, - 37550: 32884, - 37551: 33081, - 37552: 33144, - 37553: 34678, - 37554: 35519, - 37555: 35548, - 37556: 36229, - 37557: 36339, - 37558: 37530, - 37559: 38263, - 37560: 38914, - 37561: 40165, - 37562: 21189, - 37563: 25431, - 37564: 30452, - 37565: 26389, - 37566: 27784, - 37567: 29645, - 37568: 36035, - 37569: 37806, - 37570: 38515, - 37571: 27941, - 37572: 22684, - 37573: 26894, - 37574: 27084, - 37575: 36861, - 37576: 37786, - 37577: 30171, - 37578: 36890, - 37579: 22618, - 37580: 26626, - 37581: 25524, - 37582: 27131, - 37583: 20291, - 37584: 28460, - 37585: 26584, - 37586: 36795, - 37587: 34086, - 37588: 32180, - 37589: 37716, - 37590: 26943, - 37591: 28528, - 37592: 22378, - 37593: 22775, - 37594: 23340, - 37595: 32044, - 37596: 29226, - 37597: 21514, - 37598: 37347, - 37599: 40372, - 37600: 20141, - 37601: 20302, - 37602: 20572, - 37603: 20597, - 37604: 21059, - 37605: 35998, - 37606: 21576, - 37607: 22564, - 37608: 23450, - 37609: 24093, - 37610: 24213, - 37611: 24237, - 37612: 24311, - 37613: 24351, - 37614: 24716, - 37615: 25269, - 37616: 25402, - 37617: 25552, - 37618: 26799, - 37619: 27712, - 37620: 30855, - 37621: 31118, - 37622: 31243, - 37623: 32224, - 37624: 33351, - 37625: 35330, - 37626: 35558, - 37627: 36420, - 37628: 36883, - 37696: 37048, - 37697: 37165, - 37698: 37336, - 37699: 40718, - 37700: 27877, - 37701: 25688, - 37702: 25826, - 37703: 25973, - 37704: 28404, - 37705: 30340, - 37706: 31515, - 37707: 36969, - 37708: 37841, - 37709: 28346, - 37710: 21746, - 37711: 24505, - 37712: 25764, - 37713: 36685, - 37714: 36845, - 37715: 37444, - 37716: 20856, - 37717: 22635, - 37718: 22825, - 37719: 23637, - 37720: 24215, - 37721: 28155, - 37722: 32399, - 37723: 29980, - 37724: 36028, - 37725: 36578, - 37726: 39003, - 37727: 28857, - 37728: 20253, - 37729: 27583, - 37730: 28593, - 37731: 30000, - 37732: 38651, - 37733: 20814, - 37734: 21520, - 37735: 22581, - 37736: 22615, - 37737: 22956, - 37738: 23648, - 37739: 24466, - 37740: 26007, - 37741: 26460, - 37742: 28193, - 37743: 30331, - 37744: 33759, - 37745: 36077, - 37746: 36884, - 37747: 37117, - 37748: 37709, - 37749: 30757, - 37750: 30778, - 37751: 21162, - 37752: 24230, - 37753: 22303, - 37754: 22900, - 37755: 24594, - 37756: 20498, - 37757: 20826, - 37758: 20908, - 37760: 20941, - 37761: 20992, - 37762: 21776, - 37763: 22612, - 37764: 22616, - 37765: 22871, - 37766: 23445, - 37767: 23798, - 37768: 23947, - 37769: 24764, - 37770: 25237, - 37771: 25645, - 37772: 26481, - 37773: 26691, - 37774: 26812, - 37775: 26847, - 37776: 30423, - 37777: 28120, - 37778: 28271, - 37779: 28059, - 37780: 28783, - 37781: 29128, - 37782: 24403, - 37783: 30168, - 37784: 31095, - 37785: 31561, - 37786: 31572, - 37787: 31570, - 37788: 31958, - 37789: 32113, - 37790: 21040, - 37791: 33891, - 37792: 34153, - 37793: 34276, - 37794: 35342, - 37795: 35588, - 37796: 35910, - 37797: 36367, - 37798: 36867, - 37799: 36879, - 37800: 37913, - 37801: 38518, - 37802: 38957, - 37803: 39472, - 37804: 38360, - 37805: 20685, - 37806: 21205, - 37807: 21516, - 37808: 22530, - 37809: 23566, - 37810: 24999, - 37811: 25758, - 37812: 27934, - 37813: 30643, - 37814: 31461, - 37815: 33012, - 37816: 33796, - 37817: 36947, - 37818: 37509, - 37819: 23776, - 37820: 40199, - 37821: 21311, - 37822: 24471, - 37823: 24499, - 37824: 28060, - 37825: 29305, - 37826: 30563, - 37827: 31167, - 37828: 31716, - 37829: 27602, - 37830: 29420, - 37831: 35501, - 37832: 26627, - 37833: 27233, - 37834: 20984, - 37835: 31361, - 37836: 26932, - 37837: 23626, - 37838: 40182, - 37839: 33515, - 37840: 23493, - 37841: 37193, - 37842: 28702, - 37843: 22136, - 37844: 23663, - 37845: 24775, - 37846: 25958, - 37847: 27788, - 37848: 35930, - 37849: 36929, - 37850: 38931, - 37851: 21585, - 37852: 26311, - 37853: 37389, - 37854: 22856, - 37855: 37027, - 37856: 20869, - 37857: 20045, - 37858: 20970, - 37859: 34201, - 37860: 35598, - 37861: 28760, - 37862: 25466, - 37863: 37707, - 37864: 26978, - 37865: 39348, - 37866: 32260, - 37867: 30071, - 37868: 21335, - 37869: 26976, - 37870: 36575, - 37871: 38627, - 37872: 27741, - 37873: 20108, - 37874: 23612, - 37875: 24336, - 37876: 36841, - 37877: 21250, - 37878: 36049, - 37879: 32905, - 37880: 34425, - 37881: 24319, - 37882: 26085, - 37883: 20083, - 37884: 20837, - 37952: 22914, - 37953: 23615, - 37954: 38894, - 37955: 20219, - 37956: 22922, - 37957: 24525, - 37958: 35469, - 37959: 28641, - 37960: 31152, - 37961: 31074, - 37962: 23527, - 37963: 33905, - 37964: 29483, - 37965: 29105, - 37966: 24180, - 37967: 24565, - 37968: 25467, - 37969: 25754, - 37970: 29123, - 37971: 31896, - 37972: 20035, - 37973: 24316, - 37974: 20043, - 37975: 22492, - 37976: 22178, - 37977: 24745, - 37978: 28611, - 37979: 32013, - 37980: 33021, - 37981: 33075, - 37982: 33215, - 37983: 36786, - 37984: 35223, - 37985: 34468, - 37986: 24052, - 37987: 25226, - 37988: 25773, - 37989: 35207, - 37990: 26487, - 37991: 27874, - 37992: 27966, - 37993: 29750, - 37994: 30772, - 37995: 23110, - 37996: 32629, - 37997: 33453, - 37998: 39340, - 37999: 20467, - 38000: 24259, - 38001: 25309, - 38002: 25490, - 38003: 25943, - 38004: 26479, - 38005: 30403, - 38006: 29260, - 38007: 32972, - 38008: 32954, - 38009: 36649, - 38010: 37197, - 38011: 20493, - 38012: 22521, - 38013: 23186, - 38014: 26757, - 38016: 26995, - 38017: 29028, - 38018: 29437, - 38019: 36023, - 38020: 22770, - 38021: 36064, - 38022: 38506, - 38023: 36889, - 38024: 34687, - 38025: 31204, - 38026: 30695, - 38027: 33833, - 38028: 20271, - 38029: 21093, - 38030: 21338, - 38031: 25293, - 38032: 26575, - 38033: 27850, - 38034: 30333, - 38035: 31636, - 38036: 31893, - 38037: 33334, - 38038: 34180, - 38039: 36843, - 38040: 26333, - 38041: 28448, - 38042: 29190, - 38043: 32283, - 38044: 33707, - 38045: 39361, - 38046: 40614, - 38047: 20989, - 38048: 31665, - 38049: 30834, - 38050: 31672, - 38051: 32903, - 38052: 31560, - 38053: 27368, - 38054: 24161, - 38055: 32908, - 38056: 30033, - 38057: 30048, - 38058: 20843, - 38059: 37474, - 38060: 28300, - 38061: 30330, - 38062: 37271, - 38063: 39658, - 38064: 20240, - 38065: 32624, - 38066: 25244, - 38067: 31567, - 38068: 38309, - 38069: 40169, - 38070: 22138, - 38071: 22617, - 38072: 34532, - 38073: 38588, - 38074: 20276, - 38075: 21028, - 38076: 21322, - 38077: 21453, - 38078: 21467, - 38079: 24070, - 38080: 25644, - 38081: 26001, - 38082: 26495, - 38083: 27710, - 38084: 27726, - 38085: 29256, - 38086: 29359, - 38087: 29677, - 38088: 30036, - 38089: 32321, - 38090: 33324, - 38091: 34281, - 38092: 36009, - 38093: 31684, - 38094: 37318, - 38095: 29033, - 38096: 38930, - 38097: 39151, - 38098: 25405, - 38099: 26217, - 38100: 30058, - 38101: 30436, - 38102: 30928, - 38103: 34115, - 38104: 34542, - 38105: 21290, - 38106: 21329, - 38107: 21542, - 38108: 22915, - 38109: 24199, - 38110: 24444, - 38111: 24754, - 38112: 25161, - 38113: 25209, - 38114: 25259, - 38115: 26000, - 38116: 27604, - 38117: 27852, - 38118: 30130, - 38119: 30382, - 38120: 30865, - 38121: 31192, - 38122: 32203, - 38123: 32631, - 38124: 32933, - 38125: 34987, - 38126: 35513, - 38127: 36027, - 38128: 36991, - 38129: 38750, - 38130: 39131, - 38131: 27147, - 38132: 31800, - 38133: 20633, - 38134: 23614, - 38135: 24494, - 38136: 26503, - 38137: 27608, - 38138: 29749, - 38139: 30473, - 38140: 32654, - 38208: 40763, - 38209: 26570, - 38210: 31255, - 38211: 21305, - 38212: 30091, - 38213: 39661, - 38214: 24422, - 38215: 33181, - 38216: 33777, - 38217: 32920, - 38218: 24380, - 38219: 24517, - 38220: 30050, - 38221: 31558, - 38222: 36924, - 38223: 26727, - 38224: 23019, - 38225: 23195, - 38226: 32016, - 38227: 30334, - 38228: 35628, - 38229: 20469, - 38230: 24426, - 38231: 27161, - 38232: 27703, - 38233: 28418, - 38234: 29922, - 38235: 31080, - 38236: 34920, - 38237: 35413, - 38238: 35961, - 38239: 24287, - 38240: 25551, - 38241: 30149, - 38242: 31186, - 38243: 33495, - 38244: 37672, - 38245: 37618, - 38246: 33948, - 38247: 34541, - 38248: 39981, - 38249: 21697, - 38250: 24428, - 38251: 25996, - 38252: 27996, - 38253: 28693, - 38254: 36007, - 38255: 36051, - 38256: 38971, - 38257: 25935, - 38258: 29942, - 38259: 19981, - 38260: 20184, - 38261: 22496, - 38262: 22827, - 38263: 23142, - 38264: 23500, - 38265: 20904, - 38266: 24067, - 38267: 24220, - 38268: 24598, - 38269: 25206, - 38270: 25975, - 38272: 26023, - 38273: 26222, - 38274: 28014, - 38275: 29238, - 38276: 31526, - 38277: 33104, - 38278: 33178, - 38279: 33433, - 38280: 35676, - 38281: 36000, - 38282: 36070, - 38283: 36212, - 38284: 38428, - 38285: 38468, - 38286: 20398, - 38287: 25771, - 38288: 27494, - 38289: 33310, - 38290: 33889, - 38291: 34154, - 38292: 37096, - 38293: 23553, - 38294: 26963, - 38295: 39080, - 38296: 33914, - 38297: 34135, - 38298: 20239, - 38299: 21103, - 38300: 24489, - 38301: 24133, - 38302: 26381, - 38303: 31119, - 38304: 33145, - 38305: 35079, - 38306: 35206, - 38307: 28149, - 38308: 24343, - 38309: 25173, - 38310: 27832, - 38311: 20175, - 38312: 29289, - 38313: 39826, - 38314: 20998, - 38315: 21563, - 38316: 22132, - 38317: 22707, - 38318: 24996, - 38319: 25198, - 38320: 28954, - 38321: 22894, - 38322: 31881, - 38323: 31966, - 38324: 32027, - 38325: 38640, - 38326: 25991, - 38327: 32862, - 38328: 19993, - 38329: 20341, - 38330: 20853, - 38331: 22592, - 38332: 24163, - 38333: 24179, - 38334: 24330, - 38335: 26564, - 38336: 20006, - 38337: 34109, - 38338: 38281, - 38339: 38491, - 38340: 31859, - 38341: 38913, - 38342: 20731, - 38343: 22721, - 38344: 30294, - 38345: 30887, - 38346: 21029, - 38347: 30629, - 38348: 34065, - 38349: 31622, - 38350: 20559, - 38351: 22793, - 38352: 29255, - 38353: 31687, - 38354: 32232, - 38355: 36794, - 38356: 36820, - 38357: 36941, - 38358: 20415, - 38359: 21193, - 38360: 23081, - 38361: 24321, - 38362: 38829, - 38363: 20445, - 38364: 33303, - 38365: 37610, - 38366: 22275, - 38367: 25429, - 38368: 27497, - 38369: 29995, - 38370: 35036, - 38371: 36628, - 38372: 31298, - 38373: 21215, - 38374: 22675, - 38375: 24917, - 38376: 25098, - 38377: 26286, - 38378: 27597, - 38379: 31807, - 38380: 33769, - 38381: 20515, - 38382: 20472, - 38383: 21253, - 38384: 21574, - 38385: 22577, - 38386: 22857, - 38387: 23453, - 38388: 23792, - 38389: 23791, - 38390: 23849, - 38391: 24214, - 38392: 25265, - 38393: 25447, - 38394: 25918, - 38395: 26041, - 38396: 26379, - 38464: 27861, - 38465: 27873, - 38466: 28921, - 38467: 30770, - 38468: 32299, - 38469: 32990, - 38470: 33459, - 38471: 33804, - 38472: 34028, - 38473: 34562, - 38474: 35090, - 38475: 35370, - 38476: 35914, - 38477: 37030, - 38478: 37586, - 38479: 39165, - 38480: 40179, - 38481: 40300, - 38482: 20047, - 38483: 20129, - 38484: 20621, - 38485: 21078, - 38486: 22346, - 38487: 22952, - 38488: 24125, - 38489: 24536, - 38490: 24537, - 38491: 25151, - 38492: 26292, - 38493: 26395, - 38494: 26576, - 38495: 26834, - 38496: 20882, - 38497: 32033, - 38498: 32938, - 38499: 33192, - 38500: 35584, - 38501: 35980, - 38502: 36031, - 38503: 37502, - 38504: 38450, - 38505: 21536, - 38506: 38956, - 38507: 21271, - 38508: 20693, - 38509: 21340, - 38510: 22696, - 38511: 25778, - 38512: 26420, - 38513: 29287, - 38514: 30566, - 38515: 31302, - 38516: 37350, - 38517: 21187, - 38518: 27809, - 38519: 27526, - 38520: 22528, - 38521: 24140, - 38522: 22868, - 38523: 26412, - 38524: 32763, - 38525: 20961, - 38526: 30406, - 38528: 25705, - 38529: 30952, - 38530: 39764, - 38531: 40635, - 38532: 22475, - 38533: 22969, - 38534: 26151, - 38535: 26522, - 38536: 27598, - 38537: 21737, - 38538: 27097, - 38539: 24149, - 38540: 33180, - 38541: 26517, - 38542: 39850, - 38543: 26622, - 38544: 40018, - 38545: 26717, - 38546: 20134, - 38547: 20451, - 38548: 21448, - 38549: 25273, - 38550: 26411, - 38551: 27819, - 38552: 36804, - 38553: 20397, - 38554: 32365, - 38555: 40639, - 38556: 19975, - 38557: 24930, - 38558: 28288, - 38559: 28459, - 38560: 34067, - 38561: 21619, - 38562: 26410, - 38563: 39749, - 38564: 24051, - 38565: 31637, - 38566: 23724, - 38567: 23494, - 38568: 34588, - 38569: 28234, - 38570: 34001, - 38571: 31252, - 38572: 33032, - 38573: 22937, - 38574: 31885, - 38575: 27665, - 38576: 30496, - 38577: 21209, - 38578: 22818, - 38579: 28961, - 38580: 29279, - 38581: 30683, - 38582: 38695, - 38583: 40289, - 38584: 26891, - 38585: 23167, - 38586: 23064, - 38587: 20901, - 38588: 21517, - 38589: 21629, - 38590: 26126, - 38591: 30431, - 38592: 36855, - 38593: 37528, - 38594: 40180, - 38595: 23018, - 38596: 29277, - 38597: 28357, - 38598: 20813, - 38599: 26825, - 38600: 32191, - 38601: 32236, - 38602: 38754, - 38603: 40634, - 38604: 25720, - 38605: 27169, - 38606: 33538, - 38607: 22916, - 38608: 23391, - 38609: 27611, - 38610: 29467, - 38611: 30450, - 38612: 32178, - 38613: 32791, - 38614: 33945, - 38615: 20786, - 38616: 26408, - 38617: 40665, - 38618: 30446, - 38619: 26466, - 38620: 21247, - 38621: 39173, - 38622: 23588, - 38623: 25147, - 38624: 31870, - 38625: 36016, - 38626: 21839, - 38627: 24758, - 38628: 32011, - 38629: 38272, - 38630: 21249, - 38631: 20063, - 38632: 20918, - 38633: 22812, - 38634: 29242, - 38635: 32822, - 38636: 37326, - 38637: 24357, - 38638: 30690, - 38639: 21380, - 38640: 24441, - 38641: 32004, - 38642: 34220, - 38643: 35379, - 38644: 36493, - 38645: 38742, - 38646: 26611, - 38647: 34222, - 38648: 37971, - 38649: 24841, - 38650: 24840, - 38651: 27833, - 38652: 30290, - 38720: 35565, - 38721: 36664, - 38722: 21807, - 38723: 20305, - 38724: 20778, - 38725: 21191, - 38726: 21451, - 38727: 23461, - 38728: 24189, - 38729: 24736, - 38730: 24962, - 38731: 25558, - 38732: 26377, - 38733: 26586, - 38734: 28263, - 38735: 28044, - 38736: 29494, - 38737: 29495, - 38738: 30001, - 38739: 31056, - 38740: 35029, - 38741: 35480, - 38742: 36938, - 38743: 37009, - 38744: 37109, - 38745: 38596, - 38746: 34701, - 38747: 22805, - 38748: 20104, - 38749: 20313, - 38750: 19982, - 38751: 35465, - 38752: 36671, - 38753: 38928, - 38754: 20653, - 38755: 24188, - 38756: 22934, - 38757: 23481, - 38758: 24248, - 38759: 25562, - 38760: 25594, - 38761: 25793, - 38762: 26332, - 38763: 26954, - 38764: 27096, - 38765: 27915, - 38766: 28342, - 38767: 29076, - 38768: 29992, - 38769: 31407, - 38770: 32650, - 38771: 32768, - 38772: 33865, - 38773: 33993, - 38774: 35201, - 38775: 35617, - 38776: 36362, - 38777: 36965, - 38778: 38525, - 38779: 39178, - 38780: 24958, - 38781: 25233, - 38782: 27442, - 38784: 27779, - 38785: 28020, - 38786: 32716, - 38787: 32764, - 38788: 28096, - 38789: 32645, - 38790: 34746, - 38791: 35064, - 38792: 26469, - 38793: 33713, - 38794: 38972, - 38795: 38647, - 38796: 27931, - 38797: 32097, - 38798: 33853, - 38799: 37226, - 38800: 20081, - 38801: 21365, - 38802: 23888, - 38803: 27396, - 38804: 28651, - 38805: 34253, - 38806: 34349, - 38807: 35239, - 38808: 21033, - 38809: 21519, - 38810: 23653, - 38811: 26446, - 38812: 26792, - 38813: 29702, - 38814: 29827, - 38815: 30178, - 38816: 35023, - 38817: 35041, - 38818: 37324, - 38819: 38626, - 38820: 38520, - 38821: 24459, - 38822: 29575, - 38823: 31435, - 38824: 33870, - 38825: 25504, - 38826: 30053, - 38827: 21129, - 38828: 27969, - 38829: 28316, - 38830: 29705, - 38831: 30041, - 38832: 30827, - 38833: 31890, - 38834: 38534, - 38835: 31452, - 38836: 40845, - 38837: 20406, - 38838: 24942, - 38839: 26053, - 38840: 34396, - 38841: 20102, - 38842: 20142, - 38843: 20698, - 38844: 20001, - 38845: 20940, - 38846: 23534, - 38847: 26009, - 38848: 26753, - 38849: 28092, - 38850: 29471, - 38851: 30274, - 38852: 30637, - 38853: 31260, - 38854: 31975, - 38855: 33391, - 38856: 35538, - 38857: 36988, - 38858: 37327, - 38859: 38517, - 38860: 38936, - 38861: 21147, - 38862: 32209, - 38863: 20523, - 38864: 21400, - 38865: 26519, - 38866: 28107, - 38867: 29136, - 38868: 29747, - 38869: 33256, - 38870: 36650, - 38871: 38563, - 38872: 40023, - 38873: 40607, - 38874: 29792, - 38875: 22593, - 38876: 28057, - 38877: 32047, - 38878: 39006, - 38879: 20196, - 38880: 20278, - 38881: 20363, - 38882: 20919, - 38883: 21169, - 38884: 23994, - 38885: 24604, - 38886: 29618, - 38887: 31036, - 38888: 33491, - 38889: 37428, - 38890: 38583, - 38891: 38646, - 38892: 38666, - 38893: 40599, - 38894: 40802, - 38895: 26278, - 38896: 27508, - 38897: 21015, - 38898: 21155, - 38899: 28872, - 38900: 35010, - 38901: 24265, - 38902: 24651, - 38903: 24976, - 38904: 28451, - 38905: 29001, - 38906: 31806, - 38907: 32244, - 38908: 32879, - 38976: 34030, - 38977: 36899, - 38978: 37676, - 38979: 21570, - 38980: 39791, - 38981: 27347, - 38982: 28809, - 38983: 36034, - 38984: 36335, - 38985: 38706, - 38986: 21172, - 38987: 23105, - 38988: 24266, - 38989: 24324, - 38990: 26391, - 38991: 27004, - 38992: 27028, - 38993: 28010, - 38994: 28431, - 38995: 29282, - 38996: 29436, - 38997: 31725, - 38998: 32769, - 38999: 32894, - 39000: 34635, - 39001: 37070, - 39002: 20845, - 39003: 40595, - 39004: 31108, - 39005: 32907, - 39006: 37682, - 39007: 35542, - 39008: 20525, - 39009: 21644, - 39010: 35441, - 39011: 27498, - 39012: 36036, - 39013: 33031, - 39014: 24785, - 39015: 26528, - 39016: 40434, - 39017: 20121, - 39018: 20120, - 39019: 39952, - 39020: 35435, - 39021: 34241, - 39022: 34152, - 39023: 26880, - 39024: 28286, - 39025: 30871, - 39026: 33109, - 39071: 24332, - 39072: 19984, - 39073: 19989, - 39074: 20010, - 39075: 20017, - 39076: 20022, - 39077: 20028, - 39078: 20031, - 39079: 20034, - 39080: 20054, - 39081: 20056, - 39082: 20098, - 39083: 20101, - 39084: 35947, - 39085: 20106, - 39086: 33298, - 39087: 24333, - 39088: 20110, - 39089: 20126, - 39090: 20127, - 39091: 20128, - 39092: 20130, - 39093: 20144, - 39094: 20147, - 39095: 20150, - 39096: 20174, - 39097: 20173, - 39098: 20164, - 39099: 20166, - 39100: 20162, - 39101: 20183, - 39102: 20190, - 39103: 20205, - 39104: 20191, - 39105: 20215, - 39106: 20233, - 39107: 20314, - 39108: 20272, - 39109: 20315, - 39110: 20317, - 39111: 20311, - 39112: 20295, - 39113: 20342, - 39114: 20360, - 39115: 20367, - 39116: 20376, - 39117: 20347, - 39118: 20329, - 39119: 20336, - 39120: 20369, - 39121: 20335, - 39122: 20358, - 39123: 20374, - 39124: 20760, - 39125: 20436, - 39126: 20447, - 39127: 20430, - 39128: 20440, - 39129: 20443, - 39130: 20433, - 39131: 20442, - 39132: 20432, - 39133: 20452, - 39134: 20453, - 39135: 20506, - 39136: 20520, - 39137: 20500, - 39138: 20522, - 39139: 20517, - 39140: 20485, - 39141: 20252, - 39142: 20470, - 39143: 20513, - 39144: 20521, - 39145: 20524, - 39146: 20478, - 39147: 20463, - 39148: 20497, - 39149: 20486, - 39150: 20547, - 39151: 20551, - 39152: 26371, - 39153: 20565, - 39154: 20560, - 39155: 20552, - 39156: 20570, - 39157: 20566, - 39158: 20588, - 39159: 20600, - 39160: 20608, - 39161: 20634, - 39162: 20613, - 39163: 20660, - 39164: 20658, - 39232: 20681, - 39233: 20682, - 39234: 20659, - 39235: 20674, - 39236: 20694, - 39237: 20702, - 39238: 20709, - 39239: 20717, - 39240: 20707, - 39241: 20718, - 39242: 20729, - 39243: 20725, - 39244: 20745, - 39245: 20737, - 39246: 20738, - 39247: 20758, - 39248: 20757, - 39249: 20756, - 39250: 20762, - 39251: 20769, - 39252: 20794, - 39253: 20791, - 39254: 20796, - 39255: 20795, - 39256: 20799, - 39257: 20800, - 39258: 20818, - 39259: 20812, - 39260: 20820, - 39261: 20834, - 39262: 31480, - 39263: 20841, - 39264: 20842, - 39265: 20846, - 39266: 20864, - 39267: 20866, - 39268: 22232, - 39269: 20876, - 39270: 20873, - 39271: 20879, - 39272: 20881, - 39273: 20883, - 39274: 20885, - 39275: 20886, - 39276: 20900, - 39277: 20902, - 39278: 20898, - 39279: 20905, - 39280: 20906, - 39281: 20907, - 39282: 20915, - 39283: 20913, - 39284: 20914, - 39285: 20912, - 39286: 20917, - 39287: 20925, - 39288: 20933, - 39289: 20937, - 39290: 20955, - 39291: 20960, - 39292: 34389, - 39293: 20969, - 39294: 20973, - 39296: 20976, - 39297: 20981, - 39298: 20990, - 39299: 20996, - 39300: 21003, - 39301: 21012, - 39302: 21006, - 39303: 21031, - 39304: 21034, - 39305: 21038, - 39306: 21043, - 39307: 21049, - 39308: 21071, - 39309: 21060, - 39310: 21067, - 39311: 21068, - 39312: 21086, - 39313: 21076, - 39314: 21098, - 39315: 21108, - 39316: 21097, - 39317: 21107, - 39318: 21119, - 39319: 21117, - 39320: 21133, - 39321: 21140, - 39322: 21138, - 39323: 21105, - 39324: 21128, - 39325: 21137, - 39326: 36776, - 39327: 36775, - 39328: 21164, - 39329: 21165, - 39330: 21180, - 39331: 21173, - 39332: 21185, - 39333: 21197, - 39334: 21207, - 39335: 21214, - 39336: 21219, - 39337: 21222, - 39338: 39149, - 39339: 21216, - 39340: 21235, - 39341: 21237, - 39342: 21240, - 39343: 21241, - 39344: 21254, - 39345: 21256, - 39346: 30008, - 39347: 21261, - 39348: 21264, - 39349: 21263, - 39350: 21269, - 39351: 21274, - 39352: 21283, - 39353: 21295, - 39354: 21297, - 39355: 21299, - 39356: 21304, - 39357: 21312, - 39358: 21318, - 39359: 21317, - 39360: 19991, - 39361: 21321, - 39362: 21325, - 39363: 20950, - 39364: 21342, - 39365: 21353, - 39366: 21358, - 39367: 22808, - 39368: 21371, - 39369: 21367, - 39370: 21378, - 39371: 21398, - 39372: 21408, - 39373: 21414, - 39374: 21413, - 39375: 21422, - 39376: 21424, - 39377: 21430, - 39378: 21443, - 39379: 31762, - 39380: 38617, - 39381: 21471, - 39382: 26364, - 39383: 29166, - 39384: 21486, - 39385: 21480, - 39386: 21485, - 39387: 21498, - 39388: 21505, - 39389: 21565, - 39390: 21568, - 39391: 21548, - 39392: 21549, - 39393: 21564, - 39394: 21550, - 39395: 21558, - 39396: 21545, - 39397: 21533, - 39398: 21582, - 39399: 21647, - 39400: 21621, - 39401: 21646, - 39402: 21599, - 39403: 21617, - 39404: 21623, - 39405: 21616, - 39406: 21650, - 39407: 21627, - 39408: 21632, - 39409: 21622, - 39410: 21636, - 39411: 21648, - 39412: 21638, - 39413: 21703, - 39414: 21666, - 39415: 21688, - 39416: 21669, - 39417: 21676, - 39418: 21700, - 39419: 21704, - 39420: 21672, - 39488: 21675, - 39489: 21698, - 39490: 21668, - 39491: 21694, - 39492: 21692, - 39493: 21720, - 39494: 21733, - 39495: 21734, - 39496: 21775, - 39497: 21780, - 39498: 21757, - 39499: 21742, - 39500: 21741, - 39501: 21754, - 39502: 21730, - 39503: 21817, - 39504: 21824, - 39505: 21859, - 39506: 21836, - 39507: 21806, - 39508: 21852, - 39509: 21829, - 39510: 21846, - 39511: 21847, - 39512: 21816, - 39513: 21811, - 39514: 21853, - 39515: 21913, - 39516: 21888, - 39517: 21679, - 39518: 21898, - 39519: 21919, - 39520: 21883, - 39521: 21886, - 39522: 21912, - 39523: 21918, - 39524: 21934, - 39525: 21884, - 39526: 21891, - 39527: 21929, - 39528: 21895, - 39529: 21928, - 39530: 21978, - 39531: 21957, - 39532: 21983, - 39533: 21956, - 39534: 21980, - 39535: 21988, - 39536: 21972, - 39537: 22036, - 39538: 22007, - 39539: 22038, - 39540: 22014, - 39541: 22013, - 39542: 22043, - 39543: 22009, - 39544: 22094, - 39545: 22096, - 39546: 29151, - 39547: 22068, - 39548: 22070, - 39549: 22066, - 39550: 22072, - 39552: 22123, - 39553: 22116, - 39554: 22063, - 39555: 22124, - 39556: 22122, - 39557: 22150, - 39558: 22144, - 39559: 22154, - 39560: 22176, - 39561: 22164, - 39562: 22159, - 39563: 22181, - 39564: 22190, - 39565: 22198, - 39566: 22196, - 39567: 22210, - 39568: 22204, - 39569: 22209, - 39570: 22211, - 39571: 22208, - 39572: 22216, - 39573: 22222, - 39574: 22225, - 39575: 22227, - 39576: 22231, - 39577: 22254, - 39578: 22265, - 39579: 22272, - 39580: 22271, - 39581: 22276, - 39582: 22281, - 39583: 22280, - 39584: 22283, - 39585: 22285, - 39586: 22291, - 39587: 22296, - 39588: 22294, - 39589: 21959, - 39590: 22300, - 39591: 22310, - 39592: 22327, - 39593: 22328, - 39594: 22350, - 39595: 22331, - 39596: 22336, - 39597: 22351, - 39598: 22377, - 39599: 22464, - 39600: 22408, - 39601: 22369, - 39602: 22399, - 39603: 22409, - 39604: 22419, - 39605: 22432, - 39606: 22451, - 39607: 22436, - 39608: 22442, - 39609: 22448, - 39610: 22467, - 39611: 22470, - 39612: 22484, - 39613: 22482, - 39614: 22483, - 39615: 22538, - 39616: 22486, - 39617: 22499, - 39618: 22539, - 39619: 22553, - 39620: 22557, - 39621: 22642, - 39622: 22561, - 39623: 22626, - 39624: 22603, - 39625: 22640, - 39626: 27584, - 39627: 22610, - 39628: 22589, - 39629: 22649, - 39630: 22661, - 39631: 22713, - 39632: 22687, - 39633: 22699, - 39634: 22714, - 39635: 22750, - 39636: 22715, - 39637: 22712, - 39638: 22702, - 39639: 22725, - 39640: 22739, - 39641: 22737, - 39642: 22743, - 39643: 22745, - 39644: 22744, - 39645: 22757, - 39646: 22748, - 39647: 22756, - 39648: 22751, - 39649: 22767, - 39650: 22778, - 39651: 22777, - 39652: 22779, - 39653: 22780, - 39654: 22781, - 39655: 22786, - 39656: 22794, - 39657: 22800, - 39658: 22811, - 39659: 26790, - 39660: 22821, - 39661: 22828, - 39662: 22829, - 39663: 22834, - 39664: 22840, - 39665: 22846, - 39666: 31442, - 39667: 22869, - 39668: 22864, - 39669: 22862, - 39670: 22874, - 39671: 22872, - 39672: 22882, - 39673: 22880, - 39674: 22887, - 39675: 22892, - 39676: 22889, - 39744: 22904, - 39745: 22913, - 39746: 22941, - 39747: 20318, - 39748: 20395, - 39749: 22947, - 39750: 22962, - 39751: 22982, - 39752: 23016, - 39753: 23004, - 39754: 22925, - 39755: 23001, - 39756: 23002, - 39757: 23077, - 39758: 23071, - 39759: 23057, - 39760: 23068, - 39761: 23049, - 39762: 23066, - 39763: 23104, - 39764: 23148, - 39765: 23113, - 39766: 23093, - 39767: 23094, - 39768: 23138, - 39769: 23146, - 39770: 23194, - 39771: 23228, - 39772: 23230, - 39773: 23243, - 39774: 23234, - 39775: 23229, - 39776: 23267, - 39777: 23255, - 39778: 23270, - 39779: 23273, - 39780: 23254, - 39781: 23290, - 39782: 23291, - 39783: 23308, - 39784: 23307, - 39785: 23318, - 39786: 23346, - 39787: 23248, - 39788: 23338, - 39789: 23350, - 39790: 23358, - 39791: 23363, - 39792: 23365, - 39793: 23360, - 39794: 23377, - 39795: 23381, - 39796: 23386, - 39797: 23387, - 39798: 23397, - 39799: 23401, - 39800: 23408, - 39801: 23411, - 39802: 23413, - 39803: 23416, - 39804: 25992, - 39805: 23418, - 39806: 23424, - 39808: 23427, - 39809: 23462, - 39810: 23480, - 39811: 23491, - 39812: 23495, - 39813: 23497, - 39814: 23508, - 39815: 23504, - 39816: 23524, - 39817: 23526, - 39818: 23522, - 39819: 23518, - 39820: 23525, - 39821: 23531, - 39822: 23536, - 39823: 23542, - 39824: 23539, - 39825: 23557, - 39826: 23559, - 39827: 23560, - 39828: 23565, - 39829: 23571, - 39830: 23584, - 39831: 23586, - 39832: 23592, - 39833: 23608, - 39834: 23609, - 39835: 23617, - 39836: 23622, - 39837: 23630, - 39838: 23635, - 39839: 23632, - 39840: 23631, - 39841: 23409, - 39842: 23660, - 39843: 23662, - 39844: 20066, - 39845: 23670, - 39846: 23673, - 39847: 23692, - 39848: 23697, - 39849: 23700, - 39850: 22939, - 39851: 23723, - 39852: 23739, - 39853: 23734, - 39854: 23740, - 39855: 23735, - 39856: 23749, - 39857: 23742, - 39858: 23751, - 39859: 23769, - 39860: 23785, - 39861: 23805, - 39862: 23802, - 39863: 23789, - 39864: 23948, - 39865: 23786, - 39866: 23819, - 39867: 23829, - 39868: 23831, - 39869: 23900, - 39870: 23839, - 39871: 23835, - 39872: 23825, - 39873: 23828, - 39874: 23842, - 39875: 23834, - 39876: 23833, - 39877: 23832, - 39878: 23884, - 39879: 23890, - 39880: 23886, - 39881: 23883, - 39882: 23916, - 39883: 23923, - 39884: 23926, - 39885: 23943, - 39886: 23940, - 39887: 23938, - 39888: 23970, - 39889: 23965, - 39890: 23980, - 39891: 23982, - 39892: 23997, - 39893: 23952, - 39894: 23991, - 39895: 23996, - 39896: 24009, - 39897: 24013, - 39898: 24019, - 39899: 24018, - 39900: 24022, - 39901: 24027, - 39902: 24043, - 39903: 24050, - 39904: 24053, - 39905: 24075, - 39906: 24090, - 39907: 24089, - 39908: 24081, - 39909: 24091, - 39910: 24118, - 39911: 24119, - 39912: 24132, - 39913: 24131, - 39914: 24128, - 39915: 24142, - 39916: 24151, - 39917: 24148, - 39918: 24159, - 39919: 24162, - 39920: 24164, - 39921: 24135, - 39922: 24181, - 39923: 24182, - 39924: 24186, - 39925: 40636, - 39926: 24191, - 39927: 24224, - 39928: 24257, - 39929: 24258, - 39930: 24264, - 39931: 24272, - 39932: 24271, - 40000: 24278, - 40001: 24291, - 40002: 24285, - 40003: 24282, - 40004: 24283, - 40005: 24290, - 40006: 24289, - 40007: 24296, - 40008: 24297, - 40009: 24300, - 40010: 24305, - 40011: 24307, - 40012: 24304, - 40013: 24308, - 40014: 24312, - 40015: 24318, - 40016: 24323, - 40017: 24329, - 40018: 24413, - 40019: 24412, - 40020: 24331, - 40021: 24337, - 40022: 24342, - 40023: 24361, - 40024: 24365, - 40025: 24376, - 40026: 24385, - 40027: 24392, - 40028: 24396, - 40029: 24398, - 40030: 24367, - 40031: 24401, - 40032: 24406, - 40033: 24407, - 40034: 24409, - 40035: 24417, - 40036: 24429, - 40037: 24435, - 40038: 24439, - 40039: 24451, - 40040: 24450, - 40041: 24447, - 40042: 24458, - 40043: 24456, - 40044: 24465, - 40045: 24455, - 40046: 24478, - 40047: 24473, - 40048: 24472, - 40049: 24480, - 40050: 24488, - 40051: 24493, - 40052: 24508, - 40053: 24534, - 40054: 24571, - 40055: 24548, - 40056: 24568, - 40057: 24561, - 40058: 24541, - 40059: 24755, - 40060: 24575, - 40061: 24609, - 40062: 24672, - 40064: 24601, - 40065: 24592, - 40066: 24617, - 40067: 24590, - 40068: 24625, - 40069: 24603, - 40070: 24597, - 40071: 24619, - 40072: 24614, - 40073: 24591, - 40074: 24634, - 40075: 24666, - 40076: 24641, - 40077: 24682, - 40078: 24695, - 40079: 24671, - 40080: 24650, - 40081: 24646, - 40082: 24653, - 40083: 24675, - 40084: 24643, - 40085: 24676, - 40086: 24642, - 40087: 24684, - 40088: 24683, - 40089: 24665, - 40090: 24705, - 40091: 24717, - 40092: 24807, - 40093: 24707, - 40094: 24730, - 40095: 24708, - 40096: 24731, - 40097: 24726, - 40098: 24727, - 40099: 24722, - 40100: 24743, - 40101: 24715, - 40102: 24801, - 40103: 24760, - 40104: 24800, - 40105: 24787, - 40106: 24756, - 40107: 24560, - 40108: 24765, - 40109: 24774, - 40110: 24757, - 40111: 24792, - 40112: 24909, - 40113: 24853, - 40114: 24838, - 40115: 24822, - 40116: 24823, - 40117: 24832, - 40118: 24820, - 40119: 24826, - 40120: 24835, - 40121: 24865, - 40122: 24827, - 40123: 24817, - 40124: 24845, - 40125: 24846, - 40126: 24903, - 40127: 24894, - 40128: 24872, - 40129: 24871, - 40130: 24906, - 40131: 24895, - 40132: 24892, - 40133: 24876, - 40134: 24884, - 40135: 24893, - 40136: 24898, - 40137: 24900, - 40138: 24947, - 40139: 24951, - 40140: 24920, - 40141: 24921, - 40142: 24922, - 40143: 24939, - 40144: 24948, - 40145: 24943, - 40146: 24933, - 40147: 24945, - 40148: 24927, - 40149: 24925, - 40150: 24915, - 40151: 24949, - 40152: 24985, - 40153: 24982, - 40154: 24967, - 40155: 25004, - 40156: 24980, - 40157: 24986, - 40158: 24970, - 40159: 24977, - 40160: 25003, - 40161: 25006, - 40162: 25036, - 40163: 25034, - 40164: 25033, - 40165: 25079, - 40166: 25032, - 40167: 25027, - 40168: 25030, - 40169: 25018, - 40170: 25035, - 40171: 32633, - 40172: 25037, - 40173: 25062, - 40174: 25059, - 40175: 25078, - 40176: 25082, - 40177: 25076, - 40178: 25087, - 40179: 25085, - 40180: 25084, - 40181: 25086, - 40182: 25088, - 40183: 25096, - 40184: 25097, - 40185: 25101, - 40186: 25100, - 40187: 25108, - 40188: 25115, - 40256: 25118, - 40257: 25121, - 40258: 25130, - 40259: 25134, - 40260: 25136, - 40261: 25138, - 40262: 25139, - 40263: 25153, - 40264: 25166, - 40265: 25182, - 40266: 25187, - 40267: 25179, - 40268: 25184, - 40269: 25192, - 40270: 25212, - 40271: 25218, - 40272: 25225, - 40273: 25214, - 40274: 25234, - 40275: 25235, - 40276: 25238, - 40277: 25300, - 40278: 25219, - 40279: 25236, - 40280: 25303, - 40281: 25297, - 40282: 25275, - 40283: 25295, - 40284: 25343, - 40285: 25286, - 40286: 25812, - 40287: 25288, - 40288: 25308, - 40289: 25292, - 40290: 25290, - 40291: 25282, - 40292: 25287, - 40293: 25243, - 40294: 25289, - 40295: 25356, - 40296: 25326, - 40297: 25329, - 40298: 25383, - 40299: 25346, - 40300: 25352, - 40301: 25327, - 40302: 25333, - 40303: 25424, - 40304: 25406, - 40305: 25421, - 40306: 25628, - 40307: 25423, - 40308: 25494, - 40309: 25486, - 40310: 25472, - 40311: 25515, - 40312: 25462, - 40313: 25507, - 40314: 25487, - 40315: 25481, - 40316: 25503, - 40317: 25525, - 40318: 25451, - 40320: 25449, - 40321: 25534, - 40322: 25577, - 40323: 25536, - 40324: 25542, - 40325: 25571, - 40326: 25545, - 40327: 25554, - 40328: 25590, - 40329: 25540, - 40330: 25622, - 40331: 25652, - 40332: 25606, - 40333: 25619, - 40334: 25638, - 40335: 25654, - 40336: 25885, - 40337: 25623, - 40338: 25640, - 40339: 25615, - 40340: 25703, - 40341: 25711, - 40342: 25718, - 40343: 25678, - 40344: 25898, - 40345: 25749, - 40346: 25747, - 40347: 25765, - 40348: 25769, - 40349: 25736, - 40350: 25788, - 40351: 25818, - 40352: 25810, - 40353: 25797, - 40354: 25799, - 40355: 25787, - 40356: 25816, - 40357: 25794, - 40358: 25841, - 40359: 25831, - 40360: 33289, - 40361: 25824, - 40362: 25825, - 40363: 25260, - 40364: 25827, - 40365: 25839, - 40366: 25900, - 40367: 25846, - 40368: 25844, - 40369: 25842, - 40370: 25850, - 40371: 25856, - 40372: 25853, - 40373: 25880, - 40374: 25884, - 40375: 25861, - 40376: 25892, - 40377: 25891, - 40378: 25899, - 40379: 25908, - 40380: 25909, - 40381: 25911, - 40382: 25910, - 40383: 25912, - 40384: 30027, - 40385: 25928, - 40386: 25942, - 40387: 25941, - 40388: 25933, - 40389: 25944, - 40390: 25950, - 40391: 25949, - 40392: 25970, - 40393: 25976, - 40394: 25986, - 40395: 25987, - 40396: 35722, - 40397: 26011, - 40398: 26015, - 40399: 26027, - 40400: 26039, - 40401: 26051, - 40402: 26054, - 40403: 26049, - 40404: 26052, - 40405: 26060, - 40406: 26066, - 40407: 26075, - 40408: 26073, - 40409: 26080, - 40410: 26081, - 40411: 26097, - 40412: 26482, - 40413: 26122, - 40414: 26115, - 40415: 26107, - 40416: 26483, - 40417: 26165, - 40418: 26166, - 40419: 26164, - 40420: 26140, - 40421: 26191, - 40422: 26180, - 40423: 26185, - 40424: 26177, - 40425: 26206, - 40426: 26205, - 40427: 26212, - 40428: 26215, - 40429: 26216, - 40430: 26207, - 40431: 26210, - 40432: 26224, - 40433: 26243, - 40434: 26248, - 40435: 26254, - 40436: 26249, - 40437: 26244, - 40438: 26264, - 40439: 26269, - 40440: 26305, - 40441: 26297, - 40442: 26313, - 40443: 26302, - 40444: 26300, - 40512: 26308, - 40513: 26296, - 40514: 26326, - 40515: 26330, - 40516: 26336, - 40517: 26175, - 40518: 26342, - 40519: 26345, - 40520: 26352, - 40521: 26357, - 40522: 26359, - 40523: 26383, - 40524: 26390, - 40525: 26398, - 40526: 26406, - 40527: 26407, - 40528: 38712, - 40529: 26414, - 40530: 26431, - 40531: 26422, - 40532: 26433, - 40533: 26424, - 40534: 26423, - 40535: 26438, - 40536: 26462, - 40537: 26464, - 40538: 26457, - 40539: 26467, - 40540: 26468, - 40541: 26505, - 40542: 26480, - 40543: 26537, - 40544: 26492, - 40545: 26474, - 40546: 26508, - 40547: 26507, - 40548: 26534, - 40549: 26529, - 40550: 26501, - 40551: 26551, - 40552: 26607, - 40553: 26548, - 40554: 26604, - 40555: 26547, - 40556: 26601, - 40557: 26552, - 40558: 26596, - 40559: 26590, - 40560: 26589, - 40561: 26594, - 40562: 26606, - 40563: 26553, - 40564: 26574, - 40565: 26566, - 40566: 26599, - 40567: 27292, - 40568: 26654, - 40569: 26694, - 40570: 26665, - 40571: 26688, - 40572: 26701, - 40573: 26674, - 40574: 26702, - 40576: 26803, - 40577: 26667, - 40578: 26713, - 40579: 26723, - 40580: 26743, - 40581: 26751, - 40582: 26783, - 40583: 26767, - 40584: 26797, - 40585: 26772, - 40586: 26781, - 40587: 26779, - 40588: 26755, - 40589: 27310, - 40590: 26809, - 40591: 26740, - 40592: 26805, - 40593: 26784, - 40594: 26810, - 40595: 26895, - 40596: 26765, - 40597: 26750, - 40598: 26881, - 40599: 26826, - 40600: 26888, - 40601: 26840, - 40602: 26914, - 40603: 26918, - 40604: 26849, - 40605: 26892, - 40606: 26829, - 40607: 26836, - 40608: 26855, - 40609: 26837, - 40610: 26934, - 40611: 26898, - 40612: 26884, - 40613: 26839, - 40614: 26851, - 40615: 26917, - 40616: 26873, - 40617: 26848, - 40618: 26863, - 40619: 26920, - 40620: 26922, - 40621: 26906, - 40622: 26915, - 40623: 26913, - 40624: 26822, - 40625: 27001, - 40626: 26999, - 40627: 26972, - 40628: 27000, - 40629: 26987, - 40630: 26964, - 40631: 27006, - 40632: 26990, - 40633: 26937, - 40634: 26996, - 40635: 26941, - 40636: 26969, - 40637: 26928, - 40638: 26977, - 40639: 26974, - 40640: 26973, - 40641: 27009, - 40642: 26986, - 40643: 27058, - 40644: 27054, - 40645: 27088, - 40646: 27071, - 40647: 27073, - 40648: 27091, - 40649: 27070, - 40650: 27086, - 40651: 23528, - 40652: 27082, - 40653: 27101, - 40654: 27067, - 40655: 27075, - 40656: 27047, - 40657: 27182, - 40658: 27025, - 40659: 27040, - 40660: 27036, - 40661: 27029, - 40662: 27060, - 40663: 27102, - 40664: 27112, - 40665: 27138, - 40666: 27163, - 40667: 27135, - 40668: 27402, - 40669: 27129, - 40670: 27122, - 40671: 27111, - 40672: 27141, - 40673: 27057, - 40674: 27166, - 40675: 27117, - 40676: 27156, - 40677: 27115, - 40678: 27146, - 40679: 27154, - 40680: 27329, - 40681: 27171, - 40682: 27155, - 40683: 27204, - 40684: 27148, - 40685: 27250, - 40686: 27190, - 40687: 27256, - 40688: 27207, - 40689: 27234, - 40690: 27225, - 40691: 27238, - 40692: 27208, - 40693: 27192, - 40694: 27170, - 40695: 27280, - 40696: 27277, - 40697: 27296, - 40698: 27268, - 40699: 27298, - 40700: 27299, - 40768: 27287, - 40769: 34327, - 40770: 27323, - 40771: 27331, - 40772: 27330, - 40773: 27320, - 40774: 27315, - 40775: 27308, - 40776: 27358, - 40777: 27345, - 40778: 27359, - 40779: 27306, - 40780: 27354, - 40781: 27370, - 40782: 27387, - 40783: 27397, - 40784: 34326, - 40785: 27386, - 40786: 27410, - 40787: 27414, - 40788: 39729, - 40789: 27423, - 40790: 27448, - 40791: 27447, - 40792: 30428, - 40793: 27449, - 40794: 39150, - 40795: 27463, - 40796: 27459, - 40797: 27465, - 40798: 27472, - 40799: 27481, - 40800: 27476, - 40801: 27483, - 40802: 27487, - 40803: 27489, - 40804: 27512, - 40805: 27513, - 40806: 27519, - 40807: 27520, - 40808: 27524, - 40809: 27523, - 40810: 27533, - 40811: 27544, - 40812: 27541, - 40813: 27550, - 40814: 27556, - 40815: 27562, - 40816: 27563, - 40817: 27567, - 40818: 27570, - 40819: 27569, - 40820: 27571, - 40821: 27575, - 40822: 27580, - 40823: 27590, - 40824: 27595, - 40825: 27603, - 40826: 27615, - 40827: 27628, - 40828: 27627, - 40829: 27635, - 40830: 27631, - 40832: 40638, - 40833: 27656, - 40834: 27667, - 40835: 27668, - 40836: 27675, - 40837: 27684, - 40838: 27683, - 40839: 27742, - 40840: 27733, - 40841: 27746, - 40842: 27754, - 40843: 27778, - 40844: 27789, - 40845: 27802, - 40846: 27777, - 40847: 27803, - 40848: 27774, - 40849: 27752, - 40850: 27763, - 40851: 27794, - 40852: 27792, - 40853: 27844, - 40854: 27889, - 40855: 27859, - 40856: 27837, - 40857: 27863, - 40858: 27845, - 40859: 27869, - 40860: 27822, - 40861: 27825, - 40862: 27838, - 40863: 27834, - 40864: 27867, - 40865: 27887, - 40866: 27865, - 40867: 27882, - 40868: 27935, - 40869: 34893, - 40870: 27958, - 40871: 27947, - 40872: 27965, - 40873: 27960, - 40874: 27929, - 40875: 27957, - 40876: 27955, - 40877: 27922, - 40878: 27916, - 40879: 28003, - 40880: 28051, - 40881: 28004, - 40882: 27994, - 40883: 28025, - 40884: 27993, - 40885: 28046, - 40886: 28053, - 40887: 28644, - 40888: 28037, - 40889: 28153, - 40890: 28181, - 40891: 28170, - 40892: 28085, - 40893: 28103, - 40894: 28134, - 40895: 28088, - 40896: 28102, - 40897: 28140, - 40898: 28126, - 40899: 28108, - 40900: 28136, - 40901: 28114, - 40902: 28101, - 40903: 28154, - 40904: 28121, - 40905: 28132, - 40906: 28117, - 40907: 28138, - 40908: 28142, - 40909: 28205, - 40910: 28270, - 40911: 28206, - 40912: 28185, - 40913: 28274, - 40914: 28255, - 40915: 28222, - 40916: 28195, - 40917: 28267, - 40918: 28203, - 40919: 28278, - 40920: 28237, - 40921: 28191, - 40922: 28227, - 40923: 28218, - 40924: 28238, - 40925: 28196, - 40926: 28415, - 40927: 28189, - 40928: 28216, - 40929: 28290, - 40930: 28330, - 40931: 28312, - 40932: 28361, - 40933: 28343, - 40934: 28371, - 40935: 28349, - 40936: 28335, - 40937: 28356, - 40938: 28338, - 40939: 28372, - 40940: 28373, - 40941: 28303, - 40942: 28325, - 40943: 28354, - 40944: 28319, - 40945: 28481, - 40946: 28433, - 40947: 28748, - 40948: 28396, - 40949: 28408, - 40950: 28414, - 40951: 28479, - 40952: 28402, - 40953: 28465, - 40954: 28399, - 40955: 28466, - 40956: 28364, - 57408: 28478, - 57409: 28435, - 57410: 28407, - 57411: 28550, - 57412: 28538, - 57413: 28536, - 57414: 28545, - 57415: 28544, - 57416: 28527, - 57417: 28507, - 57418: 28659, - 57419: 28525, - 57420: 28546, - 57421: 28540, - 57422: 28504, - 57423: 28558, - 57424: 28561, - 57425: 28610, - 57426: 28518, - 57427: 28595, - 57428: 28579, - 57429: 28577, - 57430: 28580, - 57431: 28601, - 57432: 28614, - 57433: 28586, - 57434: 28639, - 57435: 28629, - 57436: 28652, - 57437: 28628, - 57438: 28632, - 57439: 28657, - 57440: 28654, - 57441: 28635, - 57442: 28681, - 57443: 28683, - 57444: 28666, - 57445: 28689, - 57446: 28673, - 57447: 28687, - 57448: 28670, - 57449: 28699, - 57450: 28698, - 57451: 28532, - 57452: 28701, - 57453: 28696, - 57454: 28703, - 57455: 28720, - 57456: 28734, - 57457: 28722, - 57458: 28753, - 57459: 28771, - 57460: 28825, - 57461: 28818, - 57462: 28847, - 57463: 28913, - 57464: 28844, - 57465: 28856, - 57466: 28851, - 57467: 28846, - 57468: 28895, - 57469: 28875, - 57470: 28893, - 57472: 28889, - 57473: 28937, - 57474: 28925, - 57475: 28956, - 57476: 28953, - 57477: 29029, - 57478: 29013, - 57479: 29064, - 57480: 29030, - 57481: 29026, - 57482: 29004, - 57483: 29014, - 57484: 29036, - 57485: 29071, - 57486: 29179, - 57487: 29060, - 57488: 29077, - 57489: 29096, - 57490: 29100, - 57491: 29143, - 57492: 29113, - 57493: 29118, - 57494: 29138, - 57495: 29129, - 57496: 29140, - 57497: 29134, - 57498: 29152, - 57499: 29164, - 57500: 29159, - 57501: 29173, - 57502: 29180, - 57503: 29177, - 57504: 29183, - 57505: 29197, - 57506: 29200, - 57507: 29211, - 57508: 29224, - 57509: 29229, - 57510: 29228, - 57511: 29232, - 57512: 29234, - 57513: 29243, - 57514: 29244, - 57515: 29247, - 57516: 29248, - 57517: 29254, - 57518: 29259, - 57519: 29272, - 57520: 29300, - 57521: 29310, - 57522: 29314, - 57523: 29313, - 57524: 29319, - 57525: 29330, - 57526: 29334, - 57527: 29346, - 57528: 29351, - 57529: 29369, - 57530: 29362, - 57531: 29379, - 57532: 29382, - 57533: 29380, - 57534: 29390, - 57535: 29394, - 57536: 29410, - 57537: 29408, - 57538: 29409, - 57539: 29433, - 57540: 29431, - 57541: 20495, - 57542: 29463, - 57543: 29450, - 57544: 29468, - 57545: 29462, - 57546: 29469, - 57547: 29492, - 57548: 29487, - 57549: 29481, - 57550: 29477, - 57551: 29502, - 57552: 29518, - 57553: 29519, - 57554: 40664, - 57555: 29527, - 57556: 29546, - 57557: 29544, - 57558: 29552, - 57559: 29560, - 57560: 29557, - 57561: 29563, - 57562: 29562, - 57563: 29640, - 57564: 29619, - 57565: 29646, - 57566: 29627, - 57567: 29632, - 57568: 29669, - 57569: 29678, - 57570: 29662, - 57571: 29858, - 57572: 29701, - 57573: 29807, - 57574: 29733, - 57575: 29688, - 57576: 29746, - 57577: 29754, - 57578: 29781, - 57579: 29759, - 57580: 29791, - 57581: 29785, - 57582: 29761, - 57583: 29788, - 57584: 29801, - 57585: 29808, - 57586: 29795, - 57587: 29802, - 57588: 29814, - 57589: 29822, - 57590: 29835, - 57591: 29854, - 57592: 29863, - 57593: 29898, - 57594: 29903, - 57595: 29908, - 57596: 29681, - 57664: 29920, - 57665: 29923, - 57666: 29927, - 57667: 29929, - 57668: 29934, - 57669: 29938, - 57670: 29936, - 57671: 29937, - 57672: 29944, - 57673: 29943, - 57674: 29956, - 57675: 29955, - 57676: 29957, - 57677: 29964, - 57678: 29966, - 57679: 29965, - 57680: 29973, - 57681: 29971, - 57682: 29982, - 57683: 29990, - 57684: 29996, - 57685: 30012, - 57686: 30020, - 57687: 30029, - 57688: 30026, - 57689: 30025, - 57690: 30043, - 57691: 30022, - 57692: 30042, - 57693: 30057, - 57694: 30052, - 57695: 30055, - 57696: 30059, - 57697: 30061, - 57698: 30072, - 57699: 30070, - 57700: 30086, - 57701: 30087, - 57702: 30068, - 57703: 30090, - 57704: 30089, - 57705: 30082, - 57706: 30100, - 57707: 30106, - 57708: 30109, - 57709: 30117, - 57710: 30115, - 57711: 30146, - 57712: 30131, - 57713: 30147, - 57714: 30133, - 57715: 30141, - 57716: 30136, - 57717: 30140, - 57718: 30129, - 57719: 30157, - 57720: 30154, - 57721: 30162, - 57722: 30169, - 57723: 30179, - 57724: 30174, - 57725: 30206, - 57726: 30207, - 57728: 30204, - 57729: 30209, - 57730: 30192, - 57731: 30202, - 57732: 30194, - 57733: 30195, - 57734: 30219, - 57735: 30221, - 57736: 30217, - 57737: 30239, - 57738: 30247, - 57739: 30240, - 57740: 30241, - 57741: 30242, - 57742: 30244, - 57743: 30260, - 57744: 30256, - 57745: 30267, - 57746: 30279, - 57747: 30280, - 57748: 30278, - 57749: 30300, - 57750: 30296, - 57751: 30305, - 57752: 30306, - 57753: 30312, - 57754: 30313, - 57755: 30314, - 57756: 30311, - 57757: 30316, - 57758: 30320, - 57759: 30322, - 57760: 30326, - 57761: 30328, - 57762: 30332, - 57763: 30336, - 57764: 30339, - 57765: 30344, - 57766: 30347, - 57767: 30350, - 57768: 30358, - 57769: 30355, - 57770: 30361, - 57771: 30362, - 57772: 30384, - 57773: 30388, - 57774: 30392, - 57775: 30393, - 57776: 30394, - 57777: 30402, - 57778: 30413, - 57779: 30422, - 57780: 30418, - 57781: 30430, - 57782: 30433, - 57783: 30437, - 57784: 30439, - 57785: 30442, - 57786: 34351, - 57787: 30459, - 57788: 30472, - 57789: 30471, - 57790: 30468, - 57791: 30505, - 57792: 30500, - 57793: 30494, - 57794: 30501, - 57795: 30502, - 57796: 30491, - 57797: 30519, - 57798: 30520, - 57799: 30535, - 57800: 30554, - 57801: 30568, - 57802: 30571, - 57803: 30555, - 57804: 30565, - 57805: 30591, - 57806: 30590, - 57807: 30585, - 57808: 30606, - 57809: 30603, - 57810: 30609, - 57811: 30624, - 57812: 30622, - 57813: 30640, - 57814: 30646, - 57815: 30649, - 57816: 30655, - 57817: 30652, - 57818: 30653, - 57819: 30651, - 57820: 30663, - 57821: 30669, - 57822: 30679, - 57823: 30682, - 57824: 30684, - 57825: 30691, - 57826: 30702, - 57827: 30716, - 57828: 30732, - 57829: 30738, - 57830: 31014, - 57831: 30752, - 57832: 31018, - 57833: 30789, - 57834: 30862, - 57835: 30836, - 57836: 30854, - 57837: 30844, - 57838: 30874, - 57839: 30860, - 57840: 30883, - 57841: 30901, - 57842: 30890, - 57843: 30895, - 57844: 30929, - 57845: 30918, - 57846: 30923, - 57847: 30932, - 57848: 30910, - 57849: 30908, - 57850: 30917, - 57851: 30922, - 57852: 30956, - 57920: 30951, - 57921: 30938, - 57922: 30973, - 57923: 30964, - 57924: 30983, - 57925: 30994, - 57926: 30993, - 57927: 31001, - 57928: 31020, - 57929: 31019, - 57930: 31040, - 57931: 31072, - 57932: 31063, - 57933: 31071, - 57934: 31066, - 57935: 31061, - 57936: 31059, - 57937: 31098, - 57938: 31103, - 57939: 31114, - 57940: 31133, - 57941: 31143, - 57942: 40779, - 57943: 31146, - 57944: 31150, - 57945: 31155, - 57946: 31161, - 57947: 31162, - 57948: 31177, - 57949: 31189, - 57950: 31207, - 57951: 31212, - 57952: 31201, - 57953: 31203, - 57954: 31240, - 57955: 31245, - 57956: 31256, - 57957: 31257, - 57958: 31264, - 57959: 31263, - 57960: 31104, - 57961: 31281, - 57962: 31291, - 57963: 31294, - 57964: 31287, - 57965: 31299, - 57966: 31319, - 57967: 31305, - 57968: 31329, - 57969: 31330, - 57970: 31337, - 57971: 40861, - 57972: 31344, - 57973: 31353, - 57974: 31357, - 57975: 31368, - 57976: 31383, - 57977: 31381, - 57978: 31384, - 57979: 31382, - 57980: 31401, - 57981: 31432, - 57982: 31408, - 57984: 31414, - 57985: 31429, - 57986: 31428, - 57987: 31423, - 57988: 36995, - 57989: 31431, - 57990: 31434, - 57991: 31437, - 57992: 31439, - 57993: 31445, - 57994: 31443, - 57995: 31449, - 57996: 31450, - 57997: 31453, - 57998: 31457, - 57999: 31458, - 58000: 31462, - 58001: 31469, - 58002: 31472, - 58003: 31490, - 58004: 31503, - 58005: 31498, - 58006: 31494, - 58007: 31539, - 58008: 31512, - 58009: 31513, - 58010: 31518, - 58011: 31541, - 58012: 31528, - 58013: 31542, - 58014: 31568, - 58015: 31610, - 58016: 31492, - 58017: 31565, - 58018: 31499, - 58019: 31564, - 58020: 31557, - 58021: 31605, - 58022: 31589, - 58023: 31604, - 58024: 31591, - 58025: 31600, - 58026: 31601, - 58027: 31596, - 58028: 31598, - 58029: 31645, - 58030: 31640, - 58031: 31647, - 58032: 31629, - 58033: 31644, - 58034: 31642, - 58035: 31627, - 58036: 31634, - 58037: 31631, - 58038: 31581, - 58039: 31641, - 58040: 31691, - 58041: 31681, - 58042: 31692, - 58043: 31695, - 58044: 31668, - 58045: 31686, - 58046: 31709, - 58047: 31721, - 58048: 31761, - 58049: 31764, - 58050: 31718, - 58051: 31717, - 58052: 31840, - 58053: 31744, - 58054: 31751, - 58055: 31763, - 58056: 31731, - 58057: 31735, - 58058: 31767, - 58059: 31757, - 58060: 31734, - 58061: 31779, - 58062: 31783, - 58063: 31786, - 58064: 31775, - 58065: 31799, - 58066: 31787, - 58067: 31805, - 58068: 31820, - 58069: 31811, - 58070: 31828, - 58071: 31823, - 58072: 31808, - 58073: 31824, - 58074: 31832, - 58075: 31839, - 58076: 31844, - 58077: 31830, - 58078: 31845, - 58079: 31852, - 58080: 31861, - 58081: 31875, - 58082: 31888, - 58083: 31908, - 58084: 31917, - 58085: 31906, - 58086: 31915, - 58087: 31905, - 58088: 31912, - 58089: 31923, - 58090: 31922, - 58091: 31921, - 58092: 31918, - 58093: 31929, - 58094: 31933, - 58095: 31936, - 58096: 31941, - 58097: 31938, - 58098: 31960, - 58099: 31954, - 58100: 31964, - 58101: 31970, - 58102: 39739, - 58103: 31983, - 58104: 31986, - 58105: 31988, - 58106: 31990, - 58107: 31994, - 58108: 32006, - 58176: 32002, - 58177: 32028, - 58178: 32021, - 58179: 32010, - 58180: 32069, - 58181: 32075, - 58182: 32046, - 58183: 32050, - 58184: 32063, - 58185: 32053, - 58186: 32070, - 58187: 32115, - 58188: 32086, - 58189: 32078, - 58190: 32114, - 58191: 32104, - 58192: 32110, - 58193: 32079, - 58194: 32099, - 58195: 32147, - 58196: 32137, - 58197: 32091, - 58198: 32143, - 58199: 32125, - 58200: 32155, - 58201: 32186, - 58202: 32174, - 58203: 32163, - 58204: 32181, - 58205: 32199, - 58206: 32189, - 58207: 32171, - 58208: 32317, - 58209: 32162, - 58210: 32175, - 58211: 32220, - 58212: 32184, - 58213: 32159, - 58214: 32176, - 58215: 32216, - 58216: 32221, - 58217: 32228, - 58218: 32222, - 58219: 32251, - 58220: 32242, - 58221: 32225, - 58222: 32261, - 58223: 32266, - 58224: 32291, - 58225: 32289, - 58226: 32274, - 58227: 32305, - 58228: 32287, - 58229: 32265, - 58230: 32267, - 58231: 32290, - 58232: 32326, - 58233: 32358, - 58234: 32315, - 58235: 32309, - 58236: 32313, - 58237: 32323, - 58238: 32311, - 58240: 32306, - 58241: 32314, - 58242: 32359, - 58243: 32349, - 58244: 32342, - 58245: 32350, - 58246: 32345, - 58247: 32346, - 58248: 32377, - 58249: 32362, - 58250: 32361, - 58251: 32380, - 58252: 32379, - 58253: 32387, - 58254: 32213, - 58255: 32381, - 58256: 36782, - 58257: 32383, - 58258: 32392, - 58259: 32393, - 58260: 32396, - 58261: 32402, - 58262: 32400, - 58263: 32403, - 58264: 32404, - 58265: 32406, - 58266: 32398, - 58267: 32411, - 58268: 32412, - 58269: 32568, - 58270: 32570, - 58271: 32581, - 58272: 32588, - 58273: 32589, - 58274: 32590, - 58275: 32592, - 58276: 32593, - 58277: 32597, - 58278: 32596, - 58279: 32600, - 58280: 32607, - 58281: 32608, - 58282: 32616, - 58283: 32617, - 58284: 32615, - 58285: 32632, - 58286: 32642, - 58287: 32646, - 58288: 32643, - 58289: 32648, - 58290: 32647, - 58291: 32652, - 58292: 32660, - 58293: 32670, - 58294: 32669, - 58295: 32666, - 58296: 32675, - 58297: 32687, - 58298: 32690, - 58299: 32697, - 58300: 32686, - 58301: 32694, - 58302: 32696, - 58303: 35697, - 58304: 32709, - 58305: 32710, - 58306: 32714, - 58307: 32725, - 58308: 32724, - 58309: 32737, - 58310: 32742, - 58311: 32745, - 58312: 32755, - 58313: 32761, - 58314: 39132, - 58315: 32774, - 58316: 32772, - 58317: 32779, - 58318: 32786, - 58319: 32792, - 58320: 32793, - 58321: 32796, - 58322: 32801, - 58323: 32808, - 58324: 32831, - 58325: 32827, - 58326: 32842, - 58327: 32838, - 58328: 32850, - 58329: 32856, - 58330: 32858, - 58331: 32863, - 58332: 32866, - 58333: 32872, - 58334: 32883, - 58335: 32882, - 58336: 32880, - 58337: 32886, - 58338: 32889, - 58339: 32893, - 58340: 32895, - 58341: 32900, - 58342: 32902, - 58343: 32901, - 58344: 32923, - 58345: 32915, - 58346: 32922, - 58347: 32941, - 58348: 20880, - 58349: 32940, - 58350: 32987, - 58351: 32997, - 58352: 32985, - 58353: 32989, - 58354: 32964, - 58355: 32986, - 58356: 32982, - 58357: 33033, - 58358: 33007, - 58359: 33009, - 58360: 33051, - 58361: 33065, - 58362: 33059, - 58363: 33071, - 58364: 33099, - 58432: 38539, - 58433: 33094, - 58434: 33086, - 58435: 33107, - 58436: 33105, - 58437: 33020, - 58438: 33137, - 58439: 33134, - 58440: 33125, - 58441: 33126, - 58442: 33140, - 58443: 33155, - 58444: 33160, - 58445: 33162, - 58446: 33152, - 58447: 33154, - 58448: 33184, - 58449: 33173, - 58450: 33188, - 58451: 33187, - 58452: 33119, - 58453: 33171, - 58454: 33193, - 58455: 33200, - 58456: 33205, - 58457: 33214, - 58458: 33208, - 58459: 33213, - 58460: 33216, - 58461: 33218, - 58462: 33210, - 58463: 33225, - 58464: 33229, - 58465: 33233, - 58466: 33241, - 58467: 33240, - 58468: 33224, - 58469: 33242, - 58470: 33247, - 58471: 33248, - 58472: 33255, - 58473: 33274, - 58474: 33275, - 58475: 33278, - 58476: 33281, - 58477: 33282, - 58478: 33285, - 58479: 33287, - 58480: 33290, - 58481: 33293, - 58482: 33296, - 58483: 33302, - 58484: 33321, - 58485: 33323, - 58486: 33336, - 58487: 33331, - 58488: 33344, - 58489: 33369, - 58490: 33368, - 58491: 33373, - 58492: 33370, - 58493: 33375, - 58494: 33380, - 58496: 33378, - 58497: 33384, - 58498: 33386, - 58499: 33387, - 58500: 33326, - 58501: 33393, - 58502: 33399, - 58503: 33400, - 58504: 33406, - 58505: 33421, - 58506: 33426, - 58507: 33451, - 58508: 33439, - 58509: 33467, - 58510: 33452, - 58511: 33505, - 58512: 33507, - 58513: 33503, - 58514: 33490, - 58515: 33524, - 58516: 33523, - 58517: 33530, - 58518: 33683, - 58519: 33539, - 58520: 33531, - 58521: 33529, - 58522: 33502, - 58523: 33542, - 58524: 33500, - 58525: 33545, - 58526: 33497, - 58527: 33589, - 58528: 33588, - 58529: 33558, - 58530: 33586, - 58531: 33585, - 58532: 33600, - 58533: 33593, - 58534: 33616, - 58535: 33605, - 58536: 33583, - 58537: 33579, - 58538: 33559, - 58539: 33560, - 58540: 33669, - 58541: 33690, - 58542: 33706, - 58543: 33695, - 58544: 33698, - 58545: 33686, - 58546: 33571, - 58547: 33678, - 58548: 33671, - 58549: 33674, - 58550: 33660, - 58551: 33717, - 58552: 33651, - 58553: 33653, - 58554: 33696, - 58555: 33673, - 58556: 33704, - 58557: 33780, - 58558: 33811, - 58559: 33771, - 58560: 33742, - 58561: 33789, - 58562: 33795, - 58563: 33752, - 58564: 33803, - 58565: 33729, - 58566: 33783, - 58567: 33799, - 58568: 33760, - 58569: 33778, - 58570: 33805, - 58571: 33826, - 58572: 33824, - 58573: 33725, - 58574: 33848, - 58575: 34054, - 58576: 33787, - 58577: 33901, - 58578: 33834, - 58579: 33852, - 58580: 34138, - 58581: 33924, - 58582: 33911, - 58583: 33899, - 58584: 33965, - 58585: 33902, - 58586: 33922, - 58587: 33897, - 58588: 33862, - 58589: 33836, - 58590: 33903, - 58591: 33913, - 58592: 33845, - 58593: 33994, - 58594: 33890, - 58595: 33977, - 58596: 33983, - 58597: 33951, - 58598: 34009, - 58599: 33997, - 58600: 33979, - 58601: 34010, - 58602: 34000, - 58603: 33985, - 58604: 33990, - 58605: 34006, - 58606: 33953, - 58607: 34081, - 58608: 34047, - 58609: 34036, - 58610: 34071, - 58611: 34072, - 58612: 34092, - 58613: 34079, - 58614: 34069, - 58615: 34068, - 58616: 34044, - 58617: 34112, - 58618: 34147, - 58619: 34136, - 58620: 34120, - 58688: 34113, - 58689: 34306, - 58690: 34123, - 58691: 34133, - 58692: 34176, - 58693: 34212, - 58694: 34184, - 58695: 34193, - 58696: 34186, - 58697: 34216, - 58698: 34157, - 58699: 34196, - 58700: 34203, - 58701: 34282, - 58702: 34183, - 58703: 34204, - 58704: 34167, - 58705: 34174, - 58706: 34192, - 58707: 34249, - 58708: 34234, - 58709: 34255, - 58710: 34233, - 58711: 34256, - 58712: 34261, - 58713: 34269, - 58714: 34277, - 58715: 34268, - 58716: 34297, - 58717: 34314, - 58718: 34323, - 58719: 34315, - 58720: 34302, - 58721: 34298, - 58722: 34310, - 58723: 34338, - 58724: 34330, - 58725: 34352, - 58726: 34367, - 58727: 34381, - 58728: 20053, - 58729: 34388, - 58730: 34399, - 58731: 34407, - 58732: 34417, - 58733: 34451, - 58734: 34467, - 58735: 34473, - 58736: 34474, - 58737: 34443, - 58738: 34444, - 58739: 34486, - 58740: 34479, - 58741: 34500, - 58742: 34502, - 58743: 34480, - 58744: 34505, - 58745: 34851, - 58746: 34475, - 58747: 34516, - 58748: 34526, - 58749: 34537, - 58750: 34540, - 58752: 34527, - 58753: 34523, - 58754: 34543, - 58755: 34578, - 58756: 34566, - 58757: 34568, - 58758: 34560, - 58759: 34563, - 58760: 34555, - 58761: 34577, - 58762: 34569, - 58763: 34573, - 58764: 34553, - 58765: 34570, - 58766: 34612, - 58767: 34623, - 58768: 34615, - 58769: 34619, - 58770: 34597, - 58771: 34601, - 58772: 34586, - 58773: 34656, - 58774: 34655, - 58775: 34680, - 58776: 34636, - 58777: 34638, - 58778: 34676, - 58779: 34647, - 58780: 34664, - 58781: 34670, - 58782: 34649, - 58783: 34643, - 58784: 34659, - 58785: 34666, - 58786: 34821, - 58787: 34722, - 58788: 34719, - 58789: 34690, - 58790: 34735, - 58791: 34763, - 58792: 34749, - 58793: 34752, - 58794: 34768, - 58795: 38614, - 58796: 34731, - 58797: 34756, - 58798: 34739, - 58799: 34759, - 58800: 34758, - 58801: 34747, - 58802: 34799, - 58803: 34802, - 58804: 34784, - 58805: 34831, - 58806: 34829, - 58807: 34814, - 58808: 34806, - 58809: 34807, - 58810: 34830, - 58811: 34770, - 58812: 34833, - 58813: 34838, - 58814: 34837, - 58815: 34850, - 58816: 34849, - 58817: 34865, - 58818: 34870, - 58819: 34873, - 58820: 34855, - 58821: 34875, - 58822: 34884, - 58823: 34882, - 58824: 34898, - 58825: 34905, - 58826: 34910, - 58827: 34914, - 58828: 34923, - 58829: 34945, - 58830: 34942, - 58831: 34974, - 58832: 34933, - 58833: 34941, - 58834: 34997, - 58835: 34930, - 58836: 34946, - 58837: 34967, - 58838: 34962, - 58839: 34990, - 58840: 34969, - 58841: 34978, - 58842: 34957, - 58843: 34980, - 58844: 34992, - 58845: 35007, - 58846: 34993, - 58847: 35011, - 58848: 35012, - 58849: 35028, - 58850: 35032, - 58851: 35033, - 58852: 35037, - 58853: 35065, - 58854: 35074, - 58855: 35068, - 58856: 35060, - 58857: 35048, - 58858: 35058, - 58859: 35076, - 58860: 35084, - 58861: 35082, - 58862: 35091, - 58863: 35139, - 58864: 35102, - 58865: 35109, - 58866: 35114, - 58867: 35115, - 58868: 35137, - 58869: 35140, - 58870: 35131, - 58871: 35126, - 58872: 35128, - 58873: 35148, - 58874: 35101, - 58875: 35168, - 58876: 35166, - 58944: 35174, - 58945: 35172, - 58946: 35181, - 58947: 35178, - 58948: 35183, - 58949: 35188, - 58950: 35191, - 58951: 35198, - 58952: 35203, - 58953: 35208, - 58954: 35210, - 58955: 35219, - 58956: 35224, - 58957: 35233, - 58958: 35241, - 58959: 35238, - 58960: 35244, - 58961: 35247, - 58962: 35250, - 58963: 35258, - 58964: 35261, - 58965: 35263, - 58966: 35264, - 58967: 35290, - 58968: 35292, - 58969: 35293, - 58970: 35303, - 58971: 35316, - 58972: 35320, - 58973: 35331, - 58974: 35350, - 58975: 35344, - 58976: 35340, - 58977: 35355, - 58978: 35357, - 58979: 35365, - 58980: 35382, - 58981: 35393, - 58982: 35419, - 58983: 35410, - 58984: 35398, - 58985: 35400, - 58986: 35452, - 58987: 35437, - 58988: 35436, - 58989: 35426, - 58990: 35461, - 58991: 35458, - 58992: 35460, - 58993: 35496, - 58994: 35489, - 58995: 35473, - 58996: 35493, - 58997: 35494, - 58998: 35482, - 58999: 35491, - 59000: 35524, - 59001: 35533, - 59002: 35522, - 59003: 35546, - 59004: 35563, - 59005: 35571, - 59006: 35559, - 59008: 35556, - 59009: 35569, - 59010: 35604, - 59011: 35552, - 59012: 35554, - 59013: 35575, - 59014: 35550, - 59015: 35547, - 59016: 35596, - 59017: 35591, - 59018: 35610, - 59019: 35553, - 59020: 35606, - 59021: 35600, - 59022: 35607, - 59023: 35616, - 59024: 35635, - 59025: 38827, - 59026: 35622, - 59027: 35627, - 59028: 35646, - 59029: 35624, - 59030: 35649, - 59031: 35660, - 59032: 35663, - 59033: 35662, - 59034: 35657, - 59035: 35670, - 59036: 35675, - 59037: 35674, - 59038: 35691, - 59039: 35679, - 59040: 35692, - 59041: 35695, - 59042: 35700, - 59043: 35709, - 59044: 35712, - 59045: 35724, - 59046: 35726, - 59047: 35730, - 59048: 35731, - 59049: 35734, - 59050: 35737, - 59051: 35738, - 59052: 35898, - 59053: 35905, - 59054: 35903, - 59055: 35912, - 59056: 35916, - 59057: 35918, - 59058: 35920, - 59059: 35925, - 59060: 35938, - 59061: 35948, - 59062: 35960, - 59063: 35962, - 59064: 35970, - 59065: 35977, - 59066: 35973, - 59067: 35978, - 59068: 35981, - 59069: 35982, - 59070: 35988, - 59071: 35964, - 59072: 35992, - 59073: 25117, - 59074: 36013, - 59075: 36010, - 59076: 36029, - 59077: 36018, - 59078: 36019, - 59079: 36014, - 59080: 36022, - 59081: 36040, - 59082: 36033, - 59083: 36068, - 59084: 36067, - 59085: 36058, - 59086: 36093, - 59087: 36090, - 59088: 36091, - 59089: 36100, - 59090: 36101, - 59091: 36106, - 59092: 36103, - 59093: 36111, - 59094: 36109, - 59095: 36112, - 59096: 40782, - 59097: 36115, - 59098: 36045, - 59099: 36116, - 59100: 36118, - 59101: 36199, - 59102: 36205, - 59103: 36209, - 59104: 36211, - 59105: 36225, - 59106: 36249, - 59107: 36290, - 59108: 36286, - 59109: 36282, - 59110: 36303, - 59111: 36314, - 59112: 36310, - 59113: 36300, - 59114: 36315, - 59115: 36299, - 59116: 36330, - 59117: 36331, - 59118: 36319, - 59119: 36323, - 59120: 36348, - 59121: 36360, - 59122: 36361, - 59123: 36351, - 59124: 36381, - 59125: 36382, - 59126: 36368, - 59127: 36383, - 59128: 36418, - 59129: 36405, - 59130: 36400, - 59131: 36404, - 59132: 36426, - 59200: 36423, - 59201: 36425, - 59202: 36428, - 59203: 36432, - 59204: 36424, - 59205: 36441, - 59206: 36452, - 59207: 36448, - 59208: 36394, - 59209: 36451, - 59210: 36437, - 59211: 36470, - 59212: 36466, - 59213: 36476, - 59214: 36481, - 59215: 36487, - 59216: 36485, - 59217: 36484, - 59218: 36491, - 59219: 36490, - 59220: 36499, - 59221: 36497, - 59222: 36500, - 59223: 36505, - 59224: 36522, - 59225: 36513, - 59226: 36524, - 59227: 36528, - 59228: 36550, - 59229: 36529, - 59230: 36542, - 59231: 36549, - 59232: 36552, - 59233: 36555, - 59234: 36571, - 59235: 36579, - 59236: 36604, - 59237: 36603, - 59238: 36587, - 59239: 36606, - 59240: 36618, - 59241: 36613, - 59242: 36629, - 59243: 36626, - 59244: 36633, - 59245: 36627, - 59246: 36636, - 59247: 36639, - 59248: 36635, - 59249: 36620, - 59250: 36646, - 59251: 36659, - 59252: 36667, - 59253: 36665, - 59254: 36677, - 59255: 36674, - 59256: 36670, - 59257: 36684, - 59258: 36681, - 59259: 36678, - 59260: 36686, - 59261: 36695, - 59262: 36700, - 59264: 36706, - 59265: 36707, - 59266: 36708, - 59267: 36764, - 59268: 36767, - 59269: 36771, - 59270: 36781, - 59271: 36783, - 59272: 36791, - 59273: 36826, - 59274: 36837, - 59275: 36834, - 59276: 36842, - 59277: 36847, - 59278: 36999, - 59279: 36852, - 59280: 36869, - 59281: 36857, - 59282: 36858, - 59283: 36881, - 59284: 36885, - 59285: 36897, - 59286: 36877, - 59287: 36894, - 59288: 36886, - 59289: 36875, - 59290: 36903, - 59291: 36918, - 59292: 36917, - 59293: 36921, - 59294: 36856, - 59295: 36943, - 59296: 36944, - 59297: 36945, - 59298: 36946, - 59299: 36878, - 59300: 36937, - 59301: 36926, - 59302: 36950, - 59303: 36952, - 59304: 36958, - 59305: 36968, - 59306: 36975, - 59307: 36982, - 59308: 38568, - 59309: 36978, - 59310: 36994, - 59311: 36989, - 59312: 36993, - 59313: 36992, - 59314: 37002, - 59315: 37001, - 59316: 37007, - 59317: 37032, - 59318: 37039, - 59319: 37041, - 59320: 37045, - 59321: 37090, - 59322: 37092, - 59323: 25160, - 59324: 37083, - 59325: 37122, - 59326: 37138, - 59327: 37145, - 59328: 37170, - 59329: 37168, - 59330: 37194, - 59331: 37206, - 59332: 37208, - 59333: 37219, - 59334: 37221, - 59335: 37225, - 59336: 37235, - 59337: 37234, - 59338: 37259, - 59339: 37257, - 59340: 37250, - 59341: 37282, - 59342: 37291, - 59343: 37295, - 59344: 37290, - 59345: 37301, - 59346: 37300, - 59347: 37306, - 59348: 37312, - 59349: 37313, - 59350: 37321, - 59351: 37323, - 59352: 37328, - 59353: 37334, - 59354: 37343, - 59355: 37345, - 59356: 37339, - 59357: 37372, - 59358: 37365, - 59359: 37366, - 59360: 37406, - 59361: 37375, - 59362: 37396, - 59363: 37420, - 59364: 37397, - 59365: 37393, - 59366: 37470, - 59367: 37463, - 59368: 37445, - 59369: 37449, - 59370: 37476, - 59371: 37448, - 59372: 37525, - 59373: 37439, - 59374: 37451, - 59375: 37456, - 59376: 37532, - 59377: 37526, - 59378: 37523, - 59379: 37531, - 59380: 37466, - 59381: 37583, - 59382: 37561, - 59383: 37559, - 59384: 37609, - 59385: 37647, - 59386: 37626, - 59387: 37700, - 59388: 37678, - 59456: 37657, - 59457: 37666, - 59458: 37658, - 59459: 37667, - 59460: 37690, - 59461: 37685, - 59462: 37691, - 59463: 37724, - 59464: 37728, - 59465: 37756, - 59466: 37742, - 59467: 37718, - 59468: 37808, - 59469: 37804, - 59470: 37805, - 59471: 37780, - 59472: 37817, - 59473: 37846, - 59474: 37847, - 59475: 37864, - 59476: 37861, - 59477: 37848, - 59478: 37827, - 59479: 37853, - 59480: 37840, - 59481: 37832, - 59482: 37860, - 59483: 37914, - 59484: 37908, - 59485: 37907, - 59486: 37891, - 59487: 37895, - 59488: 37904, - 59489: 37942, - 59490: 37931, - 59491: 37941, - 59492: 37921, - 59493: 37946, - 59494: 37953, - 59495: 37970, - 59496: 37956, - 59497: 37979, - 59498: 37984, - 59499: 37986, - 59500: 37982, - 59501: 37994, - 59502: 37417, - 59503: 38000, - 59504: 38005, - 59505: 38007, - 59506: 38013, - 59507: 37978, - 59508: 38012, - 59509: 38014, - 59510: 38017, - 59511: 38015, - 59512: 38274, - 59513: 38279, - 59514: 38282, - 59515: 38292, - 59516: 38294, - 59517: 38296, - 59518: 38297, - 59520: 38304, - 59521: 38312, - 59522: 38311, - 59523: 38317, - 59524: 38332, - 59525: 38331, - 59526: 38329, - 59527: 38334, - 59528: 38346, - 59529: 28662, - 59530: 38339, - 59531: 38349, - 59532: 38348, - 59533: 38357, - 59534: 38356, - 59535: 38358, - 59536: 38364, - 59537: 38369, - 59538: 38373, - 59539: 38370, - 59540: 38433, - 59541: 38440, - 59542: 38446, - 59543: 38447, - 59544: 38466, - 59545: 38476, - 59546: 38479, - 59547: 38475, - 59548: 38519, - 59549: 38492, - 59550: 38494, - 59551: 38493, - 59552: 38495, - 59553: 38502, - 59554: 38514, - 59555: 38508, - 59556: 38541, - 59557: 38552, - 59558: 38549, - 59559: 38551, - 59560: 38570, - 59561: 38567, - 59562: 38577, - 59563: 38578, - 59564: 38576, - 59565: 38580, - 59566: 38582, - 59567: 38584, - 59568: 38585, - 59569: 38606, - 59570: 38603, - 59571: 38601, - 59572: 38605, - 59573: 35149, - 59574: 38620, - 59575: 38669, - 59576: 38613, - 59577: 38649, - 59578: 38660, - 59579: 38662, - 59580: 38664, - 59581: 38675, - 59582: 38670, - 59583: 38673, - 59584: 38671, - 59585: 38678, - 59586: 38681, - 59587: 38692, - 59588: 38698, - 59589: 38704, - 59590: 38713, - 59591: 38717, - 59592: 38718, - 59593: 38724, - 59594: 38726, - 59595: 38728, - 59596: 38722, - 59597: 38729, - 59598: 38748, - 59599: 38752, - 59600: 38756, - 59601: 38758, - 59602: 38760, - 59603: 21202, - 59604: 38763, - 59605: 38769, - 59606: 38777, - 59607: 38789, - 59608: 38780, - 59609: 38785, - 59610: 38778, - 59611: 38790, - 59612: 38795, - 59613: 38799, - 59614: 38800, - 59615: 38812, - 59616: 38824, - 59617: 38822, - 59618: 38819, - 59619: 38835, - 59620: 38836, - 59621: 38851, - 59622: 38854, - 59623: 38856, - 59624: 38859, - 59625: 38876, - 59626: 38893, - 59627: 40783, - 59628: 38898, - 59629: 31455, - 59630: 38902, - 59631: 38901, - 59632: 38927, - 59633: 38924, - 59634: 38968, - 59635: 38948, - 59636: 38945, - 59637: 38967, - 59638: 38973, - 59639: 38982, - 59640: 38991, - 59641: 38987, - 59642: 39019, - 59643: 39023, - 59644: 39024, - 59712: 39025, - 59713: 39028, - 59714: 39027, - 59715: 39082, - 59716: 39087, - 59717: 39089, - 59718: 39094, - 59719: 39108, - 59720: 39107, - 59721: 39110, - 59722: 39145, - 59723: 39147, - 59724: 39171, - 59725: 39177, - 59726: 39186, - 59727: 39188, - 59728: 39192, - 59729: 39201, - 59730: 39197, - 59731: 39198, - 59732: 39204, - 59733: 39200, - 59734: 39212, - 59735: 39214, - 59736: 39229, - 59737: 39230, - 59738: 39234, - 59739: 39241, - 59740: 39237, - 59741: 39248, - 59742: 39243, - 59743: 39249, - 59744: 39250, - 59745: 39244, - 59746: 39253, - 59747: 39319, - 59748: 39320, - 59749: 39333, - 59750: 39341, - 59751: 39342, - 59752: 39356, - 59753: 39391, - 59754: 39387, - 59755: 39389, - 59756: 39384, - 59757: 39377, - 59758: 39405, - 59759: 39406, - 59760: 39409, - 59761: 39410, - 59762: 39419, - 59763: 39416, - 59764: 39425, - 59765: 39439, - 59766: 39429, - 59767: 39394, - 59768: 39449, - 59769: 39467, - 59770: 39479, - 59771: 39493, - 59772: 39490, - 59773: 39488, - 59774: 39491, - 59776: 39486, - 59777: 39509, - 59778: 39501, - 59779: 39515, - 59780: 39511, - 59781: 39519, - 59782: 39522, - 59783: 39525, - 59784: 39524, - 59785: 39529, - 59786: 39531, - 59787: 39530, - 59788: 39597, - 59789: 39600, - 59790: 39612, - 59791: 39616, - 59792: 39631, - 59793: 39633, - 59794: 39635, - 59795: 39636, - 59796: 39646, - 59797: 39647, - 59798: 39650, - 59799: 39651, - 59800: 39654, - 59801: 39663, - 59802: 39659, - 59803: 39662, - 59804: 39668, - 59805: 39665, - 59806: 39671, - 59807: 39675, - 59808: 39686, - 59809: 39704, - 59810: 39706, - 59811: 39711, - 59812: 39714, - 59813: 39715, - 59814: 39717, - 59815: 39719, - 59816: 39720, - 59817: 39721, - 59818: 39722, - 59819: 39726, - 59820: 39727, - 59821: 39730, - 59822: 39748, - 59823: 39747, - 59824: 39759, - 59825: 39757, - 59826: 39758, - 59827: 39761, - 59828: 39768, - 59829: 39796, - 59830: 39827, - 59831: 39811, - 59832: 39825, - 59833: 39830, - 59834: 39831, - 59835: 39839, - 59836: 39840, - 59837: 39848, - 59838: 39860, - 59839: 39872, - 59840: 39882, - 59841: 39865, - 59842: 39878, - 59843: 39887, - 59844: 39889, - 59845: 39890, - 59846: 39907, - 59847: 39906, - 59848: 39908, - 59849: 39892, - 59850: 39905, - 59851: 39994, - 59852: 39922, - 59853: 39921, - 59854: 39920, - 59855: 39957, - 59856: 39956, - 59857: 39945, - 59858: 39955, - 59859: 39948, - 59860: 39942, - 59861: 39944, - 59862: 39954, - 59863: 39946, - 59864: 39940, - 59865: 39982, - 59866: 39963, - 59867: 39973, - 59868: 39972, - 59869: 39969, - 59870: 39984, - 59871: 40007, - 59872: 39986, - 59873: 40006, - 59874: 39998, - 59875: 40026, - 59876: 40032, - 59877: 40039, - 59878: 40054, - 59879: 40056, - 59880: 40167, - 59881: 40172, - 59882: 40176, - 59883: 40201, - 59884: 40200, - 59885: 40171, - 59886: 40195, - 59887: 40198, - 59888: 40234, - 59889: 40230, - 59890: 40367, - 59891: 40227, - 59892: 40223, - 59893: 40260, - 59894: 40213, - 59895: 40210, - 59896: 40257, - 59897: 40255, - 59898: 40254, - 59899: 40262, - 59900: 40264, - 59968: 40285, - 59969: 40286, - 59970: 40292, - 59971: 40273, - 59972: 40272, - 59973: 40281, - 59974: 40306, - 59975: 40329, - 59976: 40327, - 59977: 40363, - 59978: 40303, - 59979: 40314, - 59980: 40346, - 59981: 40356, - 59982: 40361, - 59983: 40370, - 59984: 40388, - 59985: 40385, - 59986: 40379, - 59987: 40376, - 59988: 40378, - 59989: 40390, - 59990: 40399, - 59991: 40386, - 59992: 40409, - 59993: 40403, - 59994: 40440, - 59995: 40422, - 59996: 40429, - 59997: 40431, - 59998: 40445, - 59999: 40474, - 60000: 40475, - 60001: 40478, - 60002: 40565, - 60003: 40569, - 60004: 40573, - 60005: 40577, - 60006: 40584, - 60007: 40587, - 60008: 40588, - 60009: 40594, - 60010: 40597, - 60011: 40593, - 60012: 40605, - 60013: 40613, - 60014: 40617, - 60015: 40632, - 60016: 40618, - 60017: 40621, - 60018: 38753, - 60019: 40652, - 60020: 40654, - 60021: 40655, - 60022: 40656, - 60023: 40660, - 60024: 40668, - 60025: 40670, - 60026: 40669, - 60027: 40672, - 60028: 40677, - 60029: 40680, - 60030: 40687, - 60032: 40692, - 60033: 40694, - 60034: 40695, - 60035: 40697, - 60036: 40699, - 60037: 40700, - 60038: 40701, - 60039: 40711, - 60040: 40712, - 60041: 30391, - 60042: 40725, - 60043: 40737, - 60044: 40748, - 60045: 40766, - 60046: 40778, - 60047: 40786, - 60048: 40788, - 60049: 40803, - 60050: 40799, - 60051: 40800, - 60052: 40801, - 60053: 40806, - 60054: 40807, - 60055: 40812, - 60056: 40810, - 60057: 40823, - 60058: 40818, - 60059: 40822, - 60060: 40853, - 60061: 40860, - 60062: 40864, - 60063: 22575, - 60064: 27079, - 60065: 36953, - 60066: 29796, - 60067: 20956, - 60068: 29081, - 60736: 32394, - 60737: 35100, - 60738: 37704, - 60739: 37512, - 60740: 34012, - 60741: 20425, - 60742: 28859, - 60743: 26161, - 60744: 26824, - 60745: 37625, - 60746: 26363, - 60747: 24389, - 60748: 20008, - 60749: 20193, - 60750: 20220, - 60751: 20224, - 60752: 20227, - 60753: 20281, - 60754: 20310, - 60755: 20370, - 60756: 20362, - 60757: 20378, - 60758: 20372, - 60759: 20429, - 60760: 20544, - 60761: 20514, - 60762: 20479, - 60763: 20510, - 60764: 20550, - 60765: 20592, - 60766: 20546, - 60767: 20628, - 60768: 20724, - 60769: 20696, - 60770: 20810, - 60771: 20836, - 60772: 20893, - 60773: 20926, - 60774: 20972, - 60775: 21013, - 60776: 21148, - 60777: 21158, - 60778: 21184, - 60779: 21211, - 60780: 21248, - 60781: 21255, - 60782: 21284, - 60783: 21362, - 60784: 21395, - 60785: 21426, - 60786: 21469, - 60787: 64014, - 60788: 21660, - 60789: 21642, - 60790: 21673, - 60791: 21759, - 60792: 21894, - 60793: 22361, - 60794: 22373, - 60795: 22444, - 60796: 22472, - 60797: 22471, - 60798: 64015, - 60800: 64016, - 60801: 22686, - 60802: 22706, - 60803: 22795, - 60804: 22867, - 60805: 22875, - 60806: 22877, - 60807: 22883, - 60808: 22948, - 60809: 22970, - 60810: 23382, - 60811: 23488, - 60812: 29999, - 60813: 23512, - 60814: 23532, - 60815: 23582, - 60816: 23718, - 60817: 23738, - 60818: 23797, - 60819: 23847, - 60820: 23891, - 60821: 64017, - 60822: 23874, - 60823: 23917, - 60824: 23992, - 60825: 23993, - 60826: 24016, - 60827: 24353, - 60828: 24372, - 60829: 24423, - 60830: 24503, - 60831: 24542, - 60832: 24669, - 60833: 24709, - 60834: 24714, - 60835: 24798, - 60836: 24789, - 60837: 24864, - 60838: 24818, - 60839: 24849, - 60840: 24887, - 60841: 24880, - 60842: 24984, - 60843: 25107, - 60844: 25254, - 60845: 25589, - 60846: 25696, - 60847: 25757, - 60848: 25806, - 60849: 25934, - 60850: 26112, - 60851: 26133, - 60852: 26171, - 60853: 26121, - 60854: 26158, - 60855: 26142, - 60856: 26148, - 60857: 26213, - 60858: 26199, - 60859: 26201, - 60860: 64018, - 60861: 26227, - 60862: 26265, - 60863: 26272, - 60864: 26290, - 60865: 26303, - 60866: 26362, - 60867: 26382, - 60868: 63785, - 60869: 26470, - 60870: 26555, - 60871: 26706, - 60872: 26560, - 60873: 26625, - 60874: 26692, - 60875: 26831, - 60876: 64019, - 60877: 26984, - 60878: 64020, - 60879: 27032, - 60880: 27106, - 60881: 27184, - 60882: 27243, - 60883: 27206, - 60884: 27251, - 60885: 27262, - 60886: 27362, - 60887: 27364, - 60888: 27606, - 60889: 27711, - 60890: 27740, - 60891: 27782, - 60892: 27759, - 60893: 27866, - 60894: 27908, - 60895: 28039, - 60896: 28015, - 60897: 28054, - 60898: 28076, - 60899: 28111, - 60900: 28152, - 60901: 28146, - 60902: 28156, - 60903: 28217, - 60904: 28252, - 60905: 28199, - 60906: 28220, - 60907: 28351, - 60908: 28552, - 60909: 28597, - 60910: 28661, - 60911: 28677, - 60912: 28679, - 60913: 28712, - 60914: 28805, - 60915: 28843, - 60916: 28943, - 60917: 28932, - 60918: 29020, - 60919: 28998, - 60920: 28999, - 60921: 64021, - 60922: 29121, - 60923: 29182, - 60924: 29361, - 60992: 29374, - 60993: 29476, - 60994: 64022, - 60995: 29559, - 60996: 29629, - 60997: 29641, - 60998: 29654, - 60999: 29667, - 61000: 29650, - 61001: 29703, - 61002: 29685, - 61003: 29734, - 61004: 29738, - 61005: 29737, - 61006: 29742, - 61007: 29794, - 61008: 29833, - 61009: 29855, - 61010: 29953, - 61011: 30063, - 61012: 30338, - 61013: 30364, - 61014: 30366, - 61015: 30363, - 61016: 30374, - 61017: 64023, - 61018: 30534, - 61019: 21167, - 61020: 30753, - 61021: 30798, - 61022: 30820, - 61023: 30842, - 61024: 31024, - 61025: 64024, - 61026: 64025, - 61027: 64026, - 61028: 31124, - 61029: 64027, - 61030: 31131, - 61031: 31441, - 61032: 31463, - 61033: 64028, - 61034: 31467, - 61035: 31646, - 61036: 64029, - 61037: 32072, - 61038: 32092, - 61039: 32183, - 61040: 32160, - 61041: 32214, - 61042: 32338, - 61043: 32583, - 61044: 32673, - 61045: 64030, - 61046: 33537, - 61047: 33634, - 61048: 33663, - 61049: 33735, - 61050: 33782, - 61051: 33864, - 61052: 33972, - 61053: 34131, - 61054: 34137, - 61056: 34155, - 61057: 64031, - 61058: 34224, - 61059: 64032, - 61060: 64033, - 61061: 34823, - 61062: 35061, - 61063: 35346, - 61064: 35383, - 61065: 35449, - 61066: 35495, - 61067: 35518, - 61068: 35551, - 61069: 64034, - 61070: 35574, - 61071: 35667, - 61072: 35711, - 61073: 36080, - 61074: 36084, - 61075: 36114, - 61076: 36214, - 61077: 64035, - 61078: 36559, - 61079: 64036, - 61080: 64037, - 61081: 36967, - 61082: 37086, - 61083: 64038, - 61084: 37141, - 61085: 37159, - 61086: 37338, - 61087: 37335, - 61088: 37342, - 61089: 37357, - 61090: 37358, - 61091: 37348, - 61092: 37349, - 61093: 37382, - 61094: 37392, - 61095: 37386, - 61096: 37434, - 61097: 37440, - 61098: 37436, - 61099: 37454, - 61100: 37465, - 61101: 37457, - 61102: 37433, - 61103: 37479, - 61104: 37543, - 61105: 37495, - 61106: 37496, - 61107: 37607, - 61108: 37591, - 61109: 37593, - 61110: 37584, - 61111: 64039, - 61112: 37589, - 61113: 37600, - 61114: 37587, - 61115: 37669, - 61116: 37665, - 61117: 37627, - 61118: 64040, - 61119: 37662, - 61120: 37631, - 61121: 37661, - 61122: 37634, - 61123: 37744, - 61124: 37719, - 61125: 37796, - 61126: 37830, - 61127: 37854, - 61128: 37880, - 61129: 37937, - 61130: 37957, - 61131: 37960, - 61132: 38290, - 61133: 63964, - 61134: 64041, - 61135: 38557, - 61136: 38575, - 61137: 38707, - 61138: 38715, - 61139: 38723, - 61140: 38733, - 61141: 38735, - 61142: 38737, - 61143: 38741, - 61144: 38999, - 61145: 39013, - 61146: 64042, - 61147: 64043, - 61148: 39207, - 61149: 64044, - 61150: 39326, - 61151: 39502, - 61152: 39641, - 61153: 39644, - 61154: 39797, - 61155: 39794, - 61156: 39823, - 61157: 39857, - 61158: 39867, - 61159: 39936, - 61160: 40304, - 61161: 40299, - 61162: 64045, - 61163: 40473, - 61164: 40657, - 61167: 8560, - 61168: 8561, - 61169: 8562, - 61170: 8563, - 61171: 8564, - 61172: 8565, - 61173: 8566, - 61174: 8567, - 61175: 8568, - 61176: 8569, - 61177: 65506, - 61178: 65508, - 61179: 65287, - 61180: 65282, - 61504: 57344, - 61505: 57345, - 61506: 57346, - 61507: 57347, - 61508: 57348, - 61509: 57349, - 61510: 57350, - 61511: 57351, - 61512: 57352, - 61513: 57353, - 61514: 57354, - 61515: 57355, - 61516: 57356, - 61517: 57357, - 61518: 57358, - 61519: 57359, - 61520: 57360, - 61521: 57361, - 61522: 57362, - 61523: 57363, - 61524: 57364, - 61525: 57365, - 61526: 57366, - 61527: 57367, - 61528: 57368, - 61529: 57369, - 61530: 57370, - 61531: 57371, - 61532: 57372, - 61533: 57373, - 61534: 57374, - 61535: 57375, - 61536: 57376, - 61537: 57377, - 61538: 57378, - 61539: 57379, - 61540: 57380, - 61541: 57381, - 61542: 57382, - 61543: 57383, - 61544: 57384, - 61545: 57385, - 61546: 57386, - 61547: 57387, - 61548: 57388, - 61549: 57389, - 61550: 57390, - 61551: 57391, - 61552: 57392, - 61553: 57393, - 61554: 57394, - 61555: 57395, - 61556: 57396, - 61557: 57397, - 61558: 57398, - 61559: 57399, - 61560: 57400, - 61561: 57401, - 61562: 57402, - 61563: 57403, - 61564: 57404, - 61565: 57405, - 61566: 57406, - 61568: 57407, - 61569: 57408, - 61570: 57409, - 61571: 57410, - 61572: 57411, - 61573: 57412, - 61574: 57413, - 61575: 57414, - 61576: 57415, - 61577: 57416, - 61578: 57417, - 61579: 57418, - 61580: 57419, - 61581: 57420, - 61582: 57421, - 61583: 57422, - 61584: 57423, - 61585: 57424, - 61586: 57425, - 61587: 57426, - 61588: 57427, - 61589: 57428, - 61590: 57429, - 61591: 57430, - 61592: 57431, - 61593: 57432, - 61594: 57433, - 61595: 57434, - 61596: 57435, - 61597: 57436, - 61598: 57437, - 61599: 57438, - 61600: 57439, - 61601: 57440, - 61602: 57441, - 61603: 57442, - 61604: 57443, - 61605: 57444, - 61606: 57445, - 61607: 57446, - 61608: 57447, - 61609: 57448, - 61610: 57449, - 61611: 57450, - 61612: 57451, - 61613: 57452, - 61614: 57453, - 61615: 57454, - 61616: 57455, - 61617: 57456, - 61618: 57457, - 61619: 57458, - 61620: 57459, - 61621: 57460, - 61622: 57461, - 61623: 57462, - 61624: 57463, - 61625: 57464, - 61626: 57465, - 61627: 57466, - 61628: 57467, - 61629: 57468, - 61630: 57469, - 61631: 57470, - 61632: 57471, - 61633: 57472, - 61634: 57473, - 61635: 57474, - 61636: 57475, - 61637: 57476, - 61638: 57477, - 61639: 57478, - 61640: 57479, - 61641: 57480, - 61642: 57481, - 61643: 57482, - 61644: 57483, - 61645: 57484, - 61646: 57485, - 61647: 57486, - 61648: 57487, - 61649: 57488, - 61650: 57489, - 61651: 57490, - 61652: 57491, - 61653: 57492, - 61654: 57493, - 61655: 57494, - 61656: 57495, - 61657: 57496, - 61658: 57497, - 61659: 57498, - 61660: 57499, - 61661: 57500, - 61662: 57501, - 61663: 57502, - 61664: 57503, - 61665: 57504, - 61666: 57505, - 61667: 57506, - 61668: 57507, - 61669: 57508, - 61670: 57509, - 61671: 57510, - 61672: 57511, - 61673: 57512, - 61674: 57513, - 61675: 57514, - 61676: 57515, - 61677: 57516, - 61678: 57517, - 61679: 57518, - 61680: 57519, - 61681: 57520, - 61682: 57521, - 61683: 57522, - 61684: 57523, - 61685: 57524, - 61686: 57525, - 61687: 57526, - 61688: 57527, - 61689: 57528, - 61690: 57529, - 61691: 57530, - 61692: 57531, - 61760: 57532, - 61761: 57533, - 61762: 57534, - 61763: 57535, - 61764: 57536, - 61765: 57537, - 61766: 57538, - 61767: 57539, - 61768: 57540, - 61769: 57541, - 61770: 57542, - 61771: 57543, - 61772: 57544, - 61773: 57545, - 61774: 57546, - 61775: 57547, - 61776: 57548, - 61777: 57549, - 61778: 57550, - 61779: 57551, - 61780: 57552, - 61781: 57553, - 61782: 57554, - 61783: 57555, - 61784: 57556, - 61785: 57557, - 61786: 57558, - 61787: 57559, - 61788: 57560, - 61789: 57561, - 61790: 57562, - 61791: 57563, - 61792: 57564, - 61793: 57565, - 61794: 57566, - 61795: 57567, - 61796: 57568, - 61797: 57569, - 61798: 57570, - 61799: 57571, - 61800: 57572, - 61801: 57573, - 61802: 57574, - 61803: 57575, - 61804: 57576, - 61805: 57577, - 61806: 57578, - 61807: 57579, - 61808: 57580, - 61809: 57581, - 61810: 57582, - 61811: 57583, - 61812: 57584, - 61813: 57585, - 61814: 57586, - 61815: 57587, - 61816: 57588, - 61817: 57589, - 61818: 57590, - 61819: 57591, - 61820: 57592, - 61821: 57593, - 61822: 57594, - 61824: 57595, - 61825: 57596, - 61826: 57597, - 61827: 57598, - 61828: 57599, - 61829: 57600, - 61830: 57601, - 61831: 57602, - 61832: 57603, - 61833: 57604, - 61834: 57605, - 61835: 57606, - 61836: 57607, - 61837: 57608, - 61838: 57609, - 61839: 57610, - 61840: 57611, - 61841: 57612, - 61842: 57613, - 61843: 57614, - 61844: 57615, - 61845: 57616, - 61846: 57617, - 61847: 57618, - 61848: 57619, - 61849: 57620, - 61850: 57621, - 61851: 57622, - 61852: 57623, - 61853: 57624, - 61854: 57625, - 61855: 57626, - 61856: 57627, - 61857: 57628, - 61858: 57629, - 61859: 57630, - 61860: 57631, - 61861: 57632, - 61862: 57633, - 61863: 57634, - 61864: 57635, - 61865: 57636, - 61866: 57637, - 61867: 57638, - 61868: 57639, - 61869: 57640, - 61870: 57641, - 61871: 57642, - 61872: 57643, - 61873: 57644, - 61874: 57645, - 61875: 57646, - 61876: 57647, - 61877: 57648, - 61878: 57649, - 61879: 57650, - 61880: 57651, - 61881: 57652, - 61882: 57653, - 61883: 57654, - 61884: 57655, - 61885: 57656, - 61886: 57657, - 61887: 57658, - 61888: 57659, - 61889: 57660, - 61890: 57661, - 61891: 57662, - 61892: 57663, - 61893: 57664, - 61894: 57665, - 61895: 57666, - 61896: 57667, - 61897: 57668, - 61898: 57669, - 61899: 57670, - 61900: 57671, - 61901: 57672, - 61902: 57673, - 61903: 57674, - 61904: 57675, - 61905: 57676, - 61906: 57677, - 61907: 57678, - 61908: 57679, - 61909: 57680, - 61910: 57681, - 61911: 57682, - 61912: 57683, - 61913: 57684, - 61914: 57685, - 61915: 57686, - 61916: 57687, - 61917: 57688, - 61918: 57689, - 61919: 57690, - 61920: 57691, - 61921: 57692, - 61922: 57693, - 61923: 57694, - 61924: 57695, - 61925: 57696, - 61926: 57697, - 61927: 57698, - 61928: 57699, - 61929: 57700, - 61930: 57701, - 61931: 57702, - 61932: 57703, - 61933: 57704, - 61934: 57705, - 61935: 57706, - 61936: 57707, - 61937: 57708, - 61938: 57709, - 61939: 57710, - 61940: 57711, - 61941: 57712, - 61942: 57713, - 61943: 57714, - 61944: 57715, - 61945: 57716, - 61946: 57717, - 61947: 57718, - 61948: 57719, - 62016: 57720, - 62017: 57721, - 62018: 57722, - 62019: 57723, - 62020: 57724, - 62021: 57725, - 62022: 57726, - 62023: 57727, - 62024: 57728, - 62025: 57729, - 62026: 57730, - 62027: 57731, - 62028: 57732, - 62029: 57733, - 62030: 57734, - 62031: 57735, - 62032: 57736, - 62033: 57737, - 62034: 57738, - 62035: 57739, - 62036: 57740, - 62037: 57741, - 62038: 57742, - 62039: 57743, - 62040: 57744, - 62041: 57745, - 62042: 57746, - 62043: 57747, - 62044: 57748, - 62045: 57749, - 62046: 57750, - 62047: 57751, - 62048: 57752, - 62049: 57753, - 62050: 57754, - 62051: 57755, - 62052: 57756, - 62053: 57757, - 62054: 57758, - 62055: 57759, - 62056: 57760, - 62057: 57761, - 62058: 57762, - 62059: 57763, - 62060: 57764, - 62061: 57765, - 62062: 57766, - 62063: 57767, - 62064: 57768, - 62065: 57769, - 62066: 57770, - 62067: 57771, - 62068: 57772, - 62069: 57773, - 62070: 57774, - 62071: 57775, - 62072: 57776, - 62073: 57777, - 62074: 57778, - 62075: 57779, - 62076: 57780, - 62077: 57781, - 62078: 57782, - 62080: 57783, - 62081: 57784, - 62082: 57785, - 62083: 57786, - 62084: 57787, - 62085: 57788, - 62086: 57789, - 62087: 57790, - 62088: 57791, - 62089: 57792, - 62090: 57793, - 62091: 57794, - 62092: 57795, - 62093: 57796, - 62094: 57797, - 62095: 57798, - 62096: 57799, - 62097: 57800, - 62098: 57801, - 62099: 57802, - 62100: 57803, - 62101: 57804, - 62102: 57805, - 62103: 57806, - 62104: 57807, - 62105: 57808, - 62106: 57809, - 62107: 57810, - 62108: 57811, - 62109: 57812, - 62110: 57813, - 62111: 57814, - 62112: 57815, - 62113: 57816, - 62114: 57817, - 62115: 57818, - 62116: 57819, - 62117: 57820, - 62118: 57821, - 62119: 57822, - 62120: 57823, - 62121: 57824, - 62122: 57825, - 62123: 57826, - 62124: 57827, - 62125: 57828, - 62126: 57829, - 62127: 57830, - 62128: 57831, - 62129: 57832, - 62130: 57833, - 62131: 57834, - 62132: 57835, - 62133: 57836, - 62134: 57837, - 62135: 57838, - 62136: 57839, - 62137: 57840, - 62138: 57841, - 62139: 57842, - 62140: 57843, - 62141: 57844, - 62142: 57845, - 62143: 57846, - 62144: 57847, - 62145: 57848, - 62146: 57849, - 62147: 57850, - 62148: 57851, - 62149: 57852, - 62150: 57853, - 62151: 57854, - 62152: 57855, - 62153: 57856, - 62154: 57857, - 62155: 57858, - 62156: 57859, - 62157: 57860, - 62158: 57861, - 62159: 57862, - 62160: 57863, - 62161: 57864, - 62162: 57865, - 62163: 57866, - 62164: 57867, - 62165: 57868, - 62166: 57869, - 62167: 57870, - 62168: 57871, - 62169: 57872, - 62170: 57873, - 62171: 57874, - 62172: 57875, - 62173: 57876, - 62174: 57877, - 62175: 57878, - 62176: 57879, - 62177: 57880, - 62178: 57881, - 62179: 57882, - 62180: 57883, - 62181: 57884, - 62182: 57885, - 62183: 57886, - 62184: 57887, - 62185: 57888, - 62186: 57889, - 62187: 57890, - 62188: 57891, - 62189: 57892, - 62190: 57893, - 62191: 57894, - 62192: 57895, - 62193: 57896, - 62194: 57897, - 62195: 57898, - 62196: 57899, - 62197: 57900, - 62198: 57901, - 62199: 57902, - 62200: 57903, - 62201: 57904, - 62202: 57905, - 62203: 57906, - 62204: 57907, - 62272: 57908, - 62273: 57909, - 62274: 57910, - 62275: 57911, - 62276: 57912, - 62277: 57913, - 62278: 57914, - 62279: 57915, - 62280: 57916, - 62281: 57917, - 62282: 57918, - 62283: 57919, - 62284: 57920, - 62285: 57921, - 62286: 57922, - 62287: 57923, - 62288: 57924, - 62289: 57925, - 62290: 57926, - 62291: 57927, - 62292: 57928, - 62293: 57929, - 62294: 57930, - 62295: 57931, - 62296: 57932, - 62297: 57933, - 62298: 57934, - 62299: 57935, - 62300: 57936, - 62301: 57937, - 62302: 57938, - 62303: 57939, - 62304: 57940, - 62305: 57941, - 62306: 57942, - 62307: 57943, - 62308: 57944, - 62309: 57945, - 62310: 57946, - 62311: 57947, - 62312: 57948, - 62313: 57949, - 62314: 57950, - 62315: 57951, - 62316: 57952, - 62317: 57953, - 62318: 57954, - 62319: 57955, - 62320: 57956, - 62321: 57957, - 62322: 57958, - 62323: 57959, - 62324: 57960, - 62325: 57961, - 62326: 57962, - 62327: 57963, - 62328: 57964, - 62329: 57965, - 62330: 57966, - 62331: 57967, - 62332: 57968, - 62333: 57969, - 62334: 57970, - 62336: 57971, - 62337: 57972, - 62338: 57973, - 62339: 57974, - 62340: 57975, - 62341: 57976, - 62342: 57977, - 62343: 57978, - 62344: 57979, - 62345: 57980, - 62346: 57981, - 62347: 57982, - 62348: 57983, - 62349: 57984, - 62350: 57985, - 62351: 57986, - 62352: 57987, - 62353: 57988, - 62354: 57989, - 62355: 57990, - 62356: 57991, - 62357: 57992, - 62358: 57993, - 62359: 57994, - 62360: 57995, - 62361: 57996, - 62362: 57997, - 62363: 57998, - 62364: 57999, - 62365: 58000, - 62366: 58001, - 62367: 58002, - 62368: 58003, - 62369: 58004, - 62370: 58005, - 62371: 58006, - 62372: 58007, - 62373: 58008, - 62374: 58009, - 62375: 58010, - 62376: 58011, - 62377: 58012, - 62378: 58013, - 62379: 58014, - 62380: 58015, - 62381: 58016, - 62382: 58017, - 62383: 58018, - 62384: 58019, - 62385: 58020, - 62386: 58021, - 62387: 58022, - 62388: 58023, - 62389: 58024, - 62390: 58025, - 62391: 58026, - 62392: 58027, - 62393: 58028, - 62394: 58029, - 62395: 58030, - 62396: 58031, - 62397: 58032, - 62398: 58033, - 62399: 58034, - 62400: 58035, - 62401: 58036, - 62402: 58037, - 62403: 58038, - 62404: 58039, - 62405: 58040, - 62406: 58041, - 62407: 58042, - 62408: 58043, - 62409: 58044, - 62410: 58045, - 62411: 58046, - 62412: 58047, - 62413: 58048, - 62414: 58049, - 62415: 58050, - 62416: 58051, - 62417: 58052, - 62418: 58053, - 62419: 58054, - 62420: 58055, - 62421: 58056, - 62422: 58057, - 62423: 58058, - 62424: 58059, - 62425: 58060, - 62426: 58061, - 62427: 58062, - 62428: 58063, - 62429: 58064, - 62430: 58065, - 62431: 58066, - 62432: 58067, - 62433: 58068, - 62434: 58069, - 62435: 58070, - 62436: 58071, - 62437: 58072, - 62438: 58073, - 62439: 58074, - 62440: 58075, - 62441: 58076, - 62442: 58077, - 62443: 58078, - 62444: 58079, - 62445: 58080, - 62446: 58081, - 62447: 58082, - 62448: 58083, - 62449: 58084, - 62450: 58085, - 62451: 58086, - 62452: 58087, - 62453: 58088, - 62454: 58089, - 62455: 58090, - 62456: 58091, - 62457: 58092, - 62458: 58093, - 62459: 58094, - 62460: 58095, - 62528: 58096, - 62529: 58097, - 62530: 58098, - 62531: 58099, - 62532: 58100, - 62533: 58101, - 62534: 58102, - 62535: 58103, - 62536: 58104, - 62537: 58105, - 62538: 58106, - 62539: 58107, - 62540: 58108, - 62541: 58109, - 62542: 58110, - 62543: 58111, - 62544: 58112, - 62545: 58113, - 62546: 58114, - 62547: 58115, - 62548: 58116, - 62549: 58117, - 62550: 58118, - 62551: 58119, - 62552: 58120, - 62553: 58121, - 62554: 58122, - 62555: 58123, - 62556: 58124, - 62557: 58125, - 62558: 58126, - 62559: 58127, - 62560: 58128, - 62561: 58129, - 62562: 58130, - 62563: 58131, - 62564: 58132, - 62565: 58133, - 62566: 58134, - 62567: 58135, - 62568: 58136, - 62569: 58137, - 62570: 58138, - 62571: 58139, - 62572: 58140, - 62573: 58141, - 62574: 58142, - 62575: 58143, - 62576: 58144, - 62577: 58145, - 62578: 58146, - 62579: 58147, - 62580: 58148, - 62581: 58149, - 62582: 58150, - 62583: 58151, - 62584: 58152, - 62585: 58153, - 62586: 58154, - 62587: 58155, - 62588: 58156, - 62589: 58157, - 62590: 58158, - 62592: 58159, - 62593: 58160, - 62594: 58161, - 62595: 58162, - 62596: 58163, - 62597: 58164, - 62598: 58165, - 62599: 58166, - 62600: 58167, - 62601: 58168, - 62602: 58169, - 62603: 58170, - 62604: 58171, - 62605: 58172, - 62606: 58173, - 62607: 58174, - 62608: 58175, - 62609: 58176, - 62610: 58177, - 62611: 58178, - 62612: 58179, - 62613: 58180, - 62614: 58181, - 62615: 58182, - 62616: 58183, - 62617: 58184, - 62618: 58185, - 62619: 58186, - 62620: 58187, - 62621: 58188, - 62622: 58189, - 62623: 58190, - 62624: 58191, - 62625: 58192, - 62626: 58193, - 62627: 58194, - 62628: 58195, - 62629: 58196, - 62630: 58197, - 62631: 58198, - 62632: 58199, - 62633: 58200, - 62634: 58201, - 62635: 58202, - 62636: 58203, - 62637: 58204, - 62638: 58205, - 62639: 58206, - 62640: 58207, - 62641: 58208, - 62642: 58209, - 62643: 58210, - 62644: 58211, - 62645: 58212, - 62646: 58213, - 62647: 58214, - 62648: 58215, - 62649: 58216, - 62650: 58217, - 62651: 58218, - 62652: 58219, - 62653: 58220, - 62654: 58221, - 62655: 58222, - 62656: 58223, - 62657: 58224, - 62658: 58225, - 62659: 58226, - 62660: 58227, - 62661: 58228, - 62662: 58229, - 62663: 58230, - 62664: 58231, - 62665: 58232, - 62666: 58233, - 62667: 58234, - 62668: 58235, - 62669: 58236, - 62670: 58237, - 62671: 58238, - 62672: 58239, - 62673: 58240, - 62674: 58241, - 62675: 58242, - 62676: 58243, - 62677: 58244, - 62678: 58245, - 62679: 58246, - 62680: 58247, - 62681: 58248, - 62682: 58249, - 62683: 58250, - 62684: 58251, - 62685: 58252, - 62686: 58253, - 62687: 58254, - 62688: 58255, - 62689: 58256, - 62690: 58257, - 62691: 58258, - 62692: 58259, - 62693: 58260, - 62694: 58261, - 62695: 58262, - 62696: 58263, - 62697: 58264, - 62698: 58265, - 62699: 58266, - 62700: 58267, - 62701: 58268, - 62702: 58269, - 62703: 58270, - 62704: 58271, - 62705: 58272, - 62706: 58273, - 62707: 58274, - 62708: 58275, - 62709: 58276, - 62710: 58277, - 62711: 58278, - 62712: 58279, - 62713: 58280, - 62714: 58281, - 62715: 58282, - 62716: 58283, - 62784: 58284, - 62785: 58285, - 62786: 58286, - 62787: 58287, - 62788: 58288, - 62789: 58289, - 62790: 58290, - 62791: 58291, - 62792: 58292, - 62793: 58293, - 62794: 58294, - 62795: 58295, - 62796: 58296, - 62797: 58297, - 62798: 58298, - 62799: 58299, - 62800: 58300, - 62801: 58301, - 62802: 58302, - 62803: 58303, - 62804: 58304, - 62805: 58305, - 62806: 58306, - 62807: 58307, - 62808: 58308, - 62809: 58309, - 62810: 58310, - 62811: 58311, - 62812: 58312, - 62813: 58313, - 62814: 58314, - 62815: 58315, - 62816: 58316, - 62817: 58317, - 62818: 58318, - 62819: 58319, - 62820: 58320, - 62821: 58321, - 62822: 58322, - 62823: 58323, - 62824: 58324, - 62825: 58325, - 62826: 58326, - 62827: 58327, - 62828: 58328, - 62829: 58329, - 62830: 58330, - 62831: 58331, - 62832: 58332, - 62833: 58333, - 62834: 58334, - 62835: 58335, - 62836: 58336, - 62837: 58337, - 62838: 58338, - 62839: 58339, - 62840: 58340, - 62841: 58341, - 62842: 58342, - 62843: 58343, - 62844: 58344, - 62845: 58345, - 62846: 58346, - 62848: 58347, - 62849: 58348, - 62850: 58349, - 62851: 58350, - 62852: 58351, - 62853: 58352, - 62854: 58353, - 62855: 58354, - 62856: 58355, - 62857: 58356, - 62858: 58357, - 62859: 58358, - 62860: 58359, - 62861: 58360, - 62862: 58361, - 62863: 58362, - 62864: 58363, - 62865: 58364, - 62866: 58365, - 62867: 58366, - 62868: 58367, - 62869: 58368, - 62870: 58369, - 62871: 58370, - 62872: 58371, - 62873: 58372, - 62874: 58373, - 62875: 58374, - 62876: 58375, - 62877: 58376, - 62878: 58377, - 62879: 58378, - 62880: 58379, - 62881: 58380, - 62882: 58381, - 62883: 58382, - 62884: 58383, - 62885: 58384, - 62886: 58385, - 62887: 58386, - 62888: 58387, - 62889: 58388, - 62890: 58389, - 62891: 58390, - 62892: 58391, - 62893: 58392, - 62894: 58393, - 62895: 58394, - 62896: 58395, - 62897: 58396, - 62898: 58397, - 62899: 58398, - 62900: 58399, - 62901: 58400, - 62902: 58401, - 62903: 58402, - 62904: 58403, - 62905: 58404, - 62906: 58405, - 62907: 58406, - 62908: 58407, - 62909: 58408, - 62910: 58409, - 62911: 58410, - 62912: 58411, - 62913: 58412, - 62914: 58413, - 62915: 58414, - 62916: 58415, - 62917: 58416, - 62918: 58417, - 62919: 58418, - 62920: 58419, - 62921: 58420, - 62922: 58421, - 62923: 58422, - 62924: 58423, - 62925: 58424, - 62926: 58425, - 62927: 58426, - 62928: 58427, - 62929: 58428, - 62930: 58429, - 62931: 58430, - 62932: 58431, - 62933: 58432, - 62934: 58433, - 62935: 58434, - 62936: 58435, - 62937: 58436, - 62938: 58437, - 62939: 58438, - 62940: 58439, - 62941: 58440, - 62942: 58441, - 62943: 58442, - 62944: 58443, - 62945: 58444, - 62946: 58445, - 62947: 58446, - 62948: 58447, - 62949: 58448, - 62950: 58449, - 62951: 58450, - 62952: 58451, - 62953: 58452, - 62954: 58453, - 62955: 58454, - 62956: 58455, - 62957: 58456, - 62958: 58457, - 62959: 58458, - 62960: 58459, - 62961: 58460, - 62962: 58461, - 62963: 58462, - 62964: 58463, - 62965: 58464, - 62966: 58465, - 62967: 58466, - 62968: 58467, - 62969: 58468, - 62970: 58469, - 62971: 58470, - 62972: 58471, - 63040: 58472, - 63041: 58473, - 63042: 58474, - 63043: 58475, - 63044: 58476, - 63045: 58477, - 63046: 58478, - 63047: 58479, - 63048: 58480, - 63049: 58481, - 63050: 58482, - 63051: 58483, - 63052: 58484, - 63053: 58485, - 63054: 58486, - 63055: 58487, - 63056: 58488, - 63057: 58489, - 63058: 58490, - 63059: 58491, - 63060: 58492, - 63061: 58493, - 63062: 58494, - 63063: 58495, - 63064: 58496, - 63065: 58497, - 63066: 58498, - 63067: 58499, - 63068: 58500, - 63069: 58501, - 63070: 58502, - 63071: 58503, - 63072: 58504, - 63073: 58505, - 63074: 58506, - 63075: 58507, - 63076: 58508, - 63077: 58509, - 63078: 58510, - 63079: 58511, - 63080: 58512, - 63081: 58513, - 63082: 58514, - 63083: 58515, - 63084: 58516, - 63085: 58517, - 63086: 58518, - 63087: 58519, - 63088: 58520, - 63089: 58521, - 63090: 58522, - 63091: 58523, - 63092: 58524, - 63093: 58525, - 63094: 58526, - 63095: 58527, - 63096: 58528, - 63097: 58529, - 63098: 58530, - 63099: 58531, - 63100: 58532, - 63101: 58533, - 63102: 58534, - 63104: 58535, - 63105: 58536, - 63106: 58537, - 63107: 58538, - 63108: 58539, - 63109: 58540, - 63110: 58541, - 63111: 58542, - 63112: 58543, - 63113: 58544, - 63114: 58545, - 63115: 58546, - 63116: 58547, - 63117: 58548, - 63118: 58549, - 63119: 58550, - 63120: 58551, - 63121: 58552, - 63122: 58553, - 63123: 58554, - 63124: 58555, - 63125: 58556, - 63126: 58557, - 63127: 58558, - 63128: 58559, - 63129: 58560, - 63130: 58561, - 63131: 58562, - 63132: 58563, - 63133: 58564, - 63134: 58565, - 63135: 58566, - 63136: 58567, - 63137: 58568, - 63138: 58569, - 63139: 58570, - 63140: 58571, - 63141: 58572, - 63142: 58573, - 63143: 58574, - 63144: 58575, - 63145: 58576, - 63146: 58577, - 63147: 58578, - 63148: 58579, - 63149: 58580, - 63150: 58581, - 63151: 58582, - 63152: 58583, - 63153: 58584, - 63154: 58585, - 63155: 58586, - 63156: 58587, - 63157: 58588, - 63158: 58589, - 63159: 58590, - 63160: 58591, - 63161: 58592, - 63162: 58593, - 63163: 58594, - 63164: 58595, - 63165: 58596, - 63166: 58597, - 63167: 58598, - 63168: 58599, - 63169: 58600, - 63170: 58601, - 63171: 58602, - 63172: 58603, - 63173: 58604, - 63174: 58605, - 63175: 58606, - 63176: 58607, - 63177: 58608, - 63178: 58609, - 63179: 58610, - 63180: 58611, - 63181: 58612, - 63182: 58613, - 63183: 58614, - 63184: 58615, - 63185: 58616, - 63186: 58617, - 63187: 58618, - 63188: 58619, - 63189: 58620, - 63190: 58621, - 63191: 58622, - 63192: 58623, - 63193: 58624, - 63194: 58625, - 63195: 58626, - 63196: 58627, - 63197: 58628, - 63198: 58629, - 63199: 58630, - 63200: 58631, - 63201: 58632, - 63202: 58633, - 63203: 58634, - 63204: 58635, - 63205: 58636, - 63206: 58637, - 63207: 58638, - 63208: 58639, - 63209: 58640, - 63210: 58641, - 63211: 58642, - 63212: 58643, - 63213: 58644, - 63214: 58645, - 63215: 58646, - 63216: 58647, - 63217: 58648, - 63218: 58649, - 63219: 58650, - 63220: 58651, - 63221: 58652, - 63222: 58653, - 63223: 58654, - 63224: 58655, - 63225: 58656, - 63226: 58657, - 63227: 58658, - 63228: 58659, - 63296: 58660, - 63297: 58661, - 63298: 58662, - 63299: 58663, - 63300: 58664, - 63301: 58665, - 63302: 58666, - 63303: 58667, - 63304: 58668, - 63305: 58669, - 63306: 58670, - 63307: 58671, - 63308: 58672, - 63309: 58673, - 63310: 58674, - 63311: 58675, - 63312: 58676, - 63313: 58677, - 63314: 58678, - 63315: 58679, - 63316: 58680, - 63317: 58681, - 63318: 58682, - 63319: 58683, - 63320: 58684, - 63321: 58685, - 63322: 58686, - 63323: 58687, - 63324: 58688, - 63325: 58689, - 63326: 58690, - 63327: 58691, - 63328: 58692, - 63329: 58693, - 63330: 58694, - 63331: 58695, - 63332: 58696, - 63333: 58697, - 63334: 58698, - 63335: 58699, - 63336: 58700, - 63337: 58701, - 63338: 58702, - 63339: 58703, - 63340: 58704, - 63341: 58705, - 63342: 58706, - 63343: 58707, - 63344: 58708, - 63345: 58709, - 63346: 58710, - 63347: 58711, - 63348: 58712, - 63349: 58713, - 63350: 58714, - 63351: 58715, - 63352: 58716, - 63353: 58717, - 63354: 58718, - 63355: 58719, - 63356: 58720, - 63357: 58721, - 63358: 58722, - 63360: 58723, - 63361: 58724, - 63362: 58725, - 63363: 58726, - 63364: 58727, - 63365: 58728, - 63366: 58729, - 63367: 58730, - 63368: 58731, - 63369: 58732, - 63370: 58733, - 63371: 58734, - 63372: 58735, - 63373: 58736, - 63374: 58737, - 63375: 58738, - 63376: 58739, - 63377: 58740, - 63378: 58741, - 63379: 58742, - 63380: 58743, - 63381: 58744, - 63382: 58745, - 63383: 58746, - 63384: 58747, - 63385: 58748, - 63386: 58749, - 63387: 58750, - 63388: 58751, - 63389: 58752, - 63390: 58753, - 63391: 58754, - 63392: 58755, - 63393: 58756, - 63394: 58757, - 63395: 58758, - 63396: 58759, - 63397: 58760, - 63398: 58761, - 63399: 58762, - 63400: 58763, - 63401: 58764, - 63402: 58765, - 63403: 58766, - 63404: 58767, - 63405: 58768, - 63406: 58769, - 63407: 58770, - 63408: 58771, - 63409: 58772, - 63410: 58773, - 63411: 58774, - 63412: 58775, - 63413: 58776, - 63414: 58777, - 63415: 58778, - 63416: 58779, - 63417: 58780, - 63418: 58781, - 63419: 58782, - 63420: 58783, - 63421: 58784, - 63422: 58785, - 63423: 58786, - 63424: 58787, - 63425: 58788, - 63426: 58789, - 63427: 58790, - 63428: 58791, - 63429: 58792, - 63430: 58793, - 63431: 58794, - 63432: 58795, - 63433: 58796, - 63434: 58797, - 63435: 58798, - 63436: 58799, - 63437: 58800, - 63438: 58801, - 63439: 58802, - 63440: 58803, - 63441: 58804, - 63442: 58805, - 63443: 58806, - 63444: 58807, - 63445: 58808, - 63446: 58809, - 63447: 58810, - 63448: 58811, - 63449: 58812, - 63450: 58813, - 63451: 58814, - 63452: 58815, - 63453: 58816, - 63454: 58817, - 63455: 58818, - 63456: 58819, - 63457: 58820, - 63458: 58821, - 63459: 58822, - 63460: 58823, - 63461: 58824, - 63462: 58825, - 63463: 58826, - 63464: 58827, - 63465: 58828, - 63466: 58829, - 63467: 58830, - 63468: 58831, - 63469: 58832, - 63470: 58833, - 63471: 58834, - 63472: 58835, - 63473: 58836, - 63474: 58837, - 63475: 58838, - 63476: 58839, - 63477: 58840, - 63478: 58841, - 63479: 58842, - 63480: 58843, - 63481: 58844, - 63482: 58845, - 63483: 58846, - 63484: 58847, - 63552: 58848, - 63553: 58849, - 63554: 58850, - 63555: 58851, - 63556: 58852, - 63557: 58853, - 63558: 58854, - 63559: 58855, - 63560: 58856, - 63561: 58857, - 63562: 58858, - 63563: 58859, - 63564: 58860, - 63565: 58861, - 63566: 58862, - 63567: 58863, - 63568: 58864, - 63569: 58865, - 63570: 58866, - 63571: 58867, - 63572: 58868, - 63573: 58869, - 63574: 58870, - 63575: 58871, - 63576: 58872, - 63577: 58873, - 63578: 58874, - 63579: 58875, - 63580: 58876, - 63581: 58877, - 63582: 58878, - 63583: 58879, - 63584: 58880, - 63585: 58881, - 63586: 58882, - 63587: 58883, - 63588: 58884, - 63589: 58885, - 63590: 58886, - 63591: 58887, - 63592: 58888, - 63593: 58889, - 63594: 58890, - 63595: 58891, - 63596: 58892, - 63597: 58893, - 63598: 58894, - 63599: 58895, - 63600: 58896, - 63601: 58897, - 63602: 58898, - 63603: 58899, - 63604: 58900, - 63605: 58901, - 63606: 58902, - 63607: 58903, - 63608: 58904, - 63609: 58905, - 63610: 58906, - 63611: 58907, - 63612: 58908, - 63613: 58909, - 63614: 58910, - 63616: 58911, - 63617: 58912, - 63618: 58913, - 63619: 58914, - 63620: 58915, - 63621: 58916, - 63622: 58917, - 63623: 58918, - 63624: 58919, - 63625: 58920, - 63626: 58921, - 63627: 58922, - 63628: 58923, - 63629: 58924, - 63630: 58925, - 63631: 58926, - 63632: 58927, - 63633: 58928, - 63634: 58929, - 63635: 58930, - 63636: 58931, - 63637: 58932, - 63638: 58933, - 63639: 58934, - 63640: 58935, - 63641: 58936, - 63642: 58937, - 63643: 58938, - 63644: 58939, - 63645: 58940, - 63646: 58941, - 63647: 58942, - 63648: 58943, - 63649: 58944, - 63650: 58945, - 63651: 58946, - 63652: 58947, - 63653: 58948, - 63654: 58949, - 63655: 58950, - 63656: 58951, - 63657: 58952, - 63658: 58953, - 63659: 58954, - 63660: 58955, - 63661: 58956, - 63662: 58957, - 63663: 58958, - 63664: 58959, - 63665: 58960, - 63666: 58961, - 63667: 58962, - 63668: 58963, - 63669: 58964, - 63670: 58965, - 63671: 58966, - 63672: 58967, - 63673: 58968, - 63674: 58969, - 63675: 58970, - 63676: 58971, - 63677: 58972, - 63678: 58973, - 63679: 58974, - 63680: 58975, - 63681: 58976, - 63682: 58977, - 63683: 58978, - 63684: 58979, - 63685: 58980, - 63686: 58981, - 63687: 58982, - 63688: 58983, - 63689: 58984, - 63690: 58985, - 63691: 58986, - 63692: 58987, - 63693: 58988, - 63694: 58989, - 63695: 58990, - 63696: 58991, - 63697: 58992, - 63698: 58993, - 63699: 58994, - 63700: 58995, - 63701: 58996, - 63702: 58997, - 63703: 58998, - 63704: 58999, - 63705: 59000, - 63706: 59001, - 63707: 59002, - 63708: 59003, - 63709: 59004, - 63710: 59005, - 63711: 59006, - 63712: 59007, - 63713: 59008, - 63714: 59009, - 63715: 59010, - 63716: 59011, - 63717: 59012, - 63718: 59013, - 63719: 59014, - 63720: 59015, - 63721: 59016, - 63722: 59017, - 63723: 59018, - 63724: 59019, - 63725: 59020, - 63726: 59021, - 63727: 59022, - 63728: 59023, - 63729: 59024, - 63730: 59025, - 63731: 59026, - 63732: 59027, - 63733: 59028, - 63734: 59029, - 63735: 59030, - 63736: 59031, - 63737: 59032, - 63738: 59033, - 63739: 59034, - 63740: 59035, - 64064: 8560, - 64065: 8561, - 64066: 8562, - 64067: 8563, - 64068: 8564, - 64069: 8565, - 64070: 8566, - 64071: 8567, - 64072: 8568, - 64073: 8569, - 64074: 8544, - 64075: 8545, - 64076: 8546, - 64077: 8547, - 64078: 8548, - 64079: 8549, - 64080: 8550, - 64081: 8551, - 64082: 8552, - 64083: 8553, - 64084: 65506, - 64085: 65508, - 64086: 65287, - 64087: 65282, - 64088: 12849, - 64089: 8470, - 64090: 8481, - 64091: 8757, - 64092: 32394, - 64093: 35100, - 64094: 37704, - 64095: 37512, - 64096: 34012, - 64097: 20425, - 64098: 28859, - 64099: 26161, - 64100: 26824, - 64101: 37625, - 64102: 26363, - 64103: 24389, - 64104: 20008, - 64105: 20193, - 64106: 20220, - 64107: 20224, - 64108: 20227, - 64109: 20281, - 64110: 20310, - 64111: 20370, - 64112: 20362, - 64113: 20378, - 64114: 20372, - 64115: 20429, - 64116: 20544, - 64117: 20514, - 64118: 20479, - 64119: 20510, - 64120: 20550, - 64121: 20592, - 64122: 20546, - 64123: 20628, - 64124: 20724, - 64125: 20696, - 64126: 20810, - 64128: 20836, - 64129: 20893, - 64130: 20926, - 64131: 20972, - 64132: 21013, - 64133: 21148, - 64134: 21158, - 64135: 21184, - 64136: 21211, - 64137: 21248, - 64138: 21255, - 64139: 21284, - 64140: 21362, - 64141: 21395, - 64142: 21426, - 64143: 21469, - 64144: 64014, - 64145: 21660, - 64146: 21642, - 64147: 21673, - 64148: 21759, - 64149: 21894, - 64150: 22361, - 64151: 22373, - 64152: 22444, - 64153: 22472, - 64154: 22471, - 64155: 64015, - 64156: 64016, - 64157: 22686, - 64158: 22706, - 64159: 22795, - 64160: 22867, - 64161: 22875, - 64162: 22877, - 64163: 22883, - 64164: 22948, - 64165: 22970, - 64166: 23382, - 64167: 23488, - 64168: 29999, - 64169: 23512, - 64170: 23532, - 64171: 23582, - 64172: 23718, - 64173: 23738, - 64174: 23797, - 64175: 23847, - 64176: 23891, - 64177: 64017, - 64178: 23874, - 64179: 23917, - 64180: 23992, - 64181: 23993, - 64182: 24016, - 64183: 24353, - 64184: 24372, - 64185: 24423, - 64186: 24503, - 64187: 24542, - 64188: 24669, - 64189: 24709, - 64190: 24714, - 64191: 24798, - 64192: 24789, - 64193: 24864, - 64194: 24818, - 64195: 24849, - 64196: 24887, - 64197: 24880, - 64198: 24984, - 64199: 25107, - 64200: 25254, - 64201: 25589, - 64202: 25696, - 64203: 25757, - 64204: 25806, - 64205: 25934, - 64206: 26112, - 64207: 26133, - 64208: 26171, - 64209: 26121, - 64210: 26158, - 64211: 26142, - 64212: 26148, - 64213: 26213, - 64214: 26199, - 64215: 26201, - 64216: 64018, - 64217: 26227, - 64218: 26265, - 64219: 26272, - 64220: 26290, - 64221: 26303, - 64222: 26362, - 64223: 26382, - 64224: 63785, - 64225: 26470, - 64226: 26555, - 64227: 26706, - 64228: 26560, - 64229: 26625, - 64230: 26692, - 64231: 26831, - 64232: 64019, - 64233: 26984, - 64234: 64020, - 64235: 27032, - 64236: 27106, - 64237: 27184, - 64238: 27243, - 64239: 27206, - 64240: 27251, - 64241: 27262, - 64242: 27362, - 64243: 27364, - 64244: 27606, - 64245: 27711, - 64246: 27740, - 64247: 27782, - 64248: 27759, - 64249: 27866, - 64250: 27908, - 64251: 28039, - 64252: 28015, - 64320: 28054, - 64321: 28076, - 64322: 28111, - 64323: 28152, - 64324: 28146, - 64325: 28156, - 64326: 28217, - 64327: 28252, - 64328: 28199, - 64329: 28220, - 64330: 28351, - 64331: 28552, - 64332: 28597, - 64333: 28661, - 64334: 28677, - 64335: 28679, - 64336: 28712, - 64337: 28805, - 64338: 28843, - 64339: 28943, - 64340: 28932, - 64341: 29020, - 64342: 28998, - 64343: 28999, - 64344: 64021, - 64345: 29121, - 64346: 29182, - 64347: 29361, - 64348: 29374, - 64349: 29476, - 64350: 64022, - 64351: 29559, - 64352: 29629, - 64353: 29641, - 64354: 29654, - 64355: 29667, - 64356: 29650, - 64357: 29703, - 64358: 29685, - 64359: 29734, - 64360: 29738, - 64361: 29737, - 64362: 29742, - 64363: 29794, - 64364: 29833, - 64365: 29855, - 64366: 29953, - 64367: 30063, - 64368: 30338, - 64369: 30364, - 64370: 30366, - 64371: 30363, - 64372: 30374, - 64373: 64023, - 64374: 30534, - 64375: 21167, - 64376: 30753, - 64377: 30798, - 64378: 30820, - 64379: 30842, - 64380: 31024, - 64381: 64024, - 64382: 64025, - 64384: 64026, - 64385: 31124, - 64386: 64027, - 64387: 31131, - 64388: 31441, - 64389: 31463, - 64390: 64028, - 64391: 31467, - 64392: 31646, - 64393: 64029, - 64394: 32072, - 64395: 32092, - 64396: 32183, - 64397: 32160, - 64398: 32214, - 64399: 32338, - 64400: 32583, - 64401: 32673, - 64402: 64030, - 64403: 33537, - 64404: 33634, - 64405: 33663, - 64406: 33735, - 64407: 33782, - 64408: 33864, - 64409: 33972, - 64410: 34131, - 64411: 34137, - 64412: 34155, - 64413: 64031, - 64414: 34224, - 64415: 64032, - 64416: 64033, - 64417: 34823, - 64418: 35061, - 64419: 35346, - 64420: 35383, - 64421: 35449, - 64422: 35495, - 64423: 35518, - 64424: 35551, - 64425: 64034, - 64426: 35574, - 64427: 35667, - 64428: 35711, - 64429: 36080, - 64430: 36084, - 64431: 36114, - 64432: 36214, - 64433: 64035, - 64434: 36559, - 64435: 64036, - 64436: 64037, - 64437: 36967, - 64438: 37086, - 64439: 64038, - 64440: 37141, - 64441: 37159, - 64442: 37338, - 64443: 37335, - 64444: 37342, - 64445: 37357, - 64446: 37358, - 64447: 37348, - 64448: 37349, - 64449: 37382, - 64450: 37392, - 64451: 37386, - 64452: 37434, - 64453: 37440, - 64454: 37436, - 64455: 37454, - 64456: 37465, - 64457: 37457, - 64458: 37433, - 64459: 37479, - 64460: 37543, - 64461: 37495, - 64462: 37496, - 64463: 37607, - 64464: 37591, - 64465: 37593, - 64466: 37584, - 64467: 64039, - 64468: 37589, - 64469: 37600, - 64470: 37587, - 64471: 37669, - 64472: 37665, - 64473: 37627, - 64474: 64040, - 64475: 37662, - 64476: 37631, - 64477: 37661, - 64478: 37634, - 64479: 37744, - 64480: 37719, - 64481: 37796, - 64482: 37830, - 64483: 37854, - 64484: 37880, - 64485: 37937, - 64486: 37957, - 64487: 37960, - 64488: 38290, - 64489: 63964, - 64490: 64041, - 64491: 38557, - 64492: 38575, - 64493: 38707, - 64494: 38715, - 64495: 38723, - 64496: 38733, - 64497: 38735, - 64498: 38737, - 64499: 38741, - 64500: 38999, - 64501: 39013, - 64502: 64042, - 64503: 64043, - 64504: 39207, - 64505: 64044, - 64506: 39326, - 64507: 39502, - 64508: 39641, - 64576: 39644, - 64577: 39797, - 64578: 39794, - 64579: 39823, - 64580: 39857, - 64581: 39867, - 64582: 39936, - 64583: 40304, - 64584: 40299, - 64585: 64045, - 64586: 40473, - 64587: 40657 -}; - -/** - * @author takahiro / https://github.com/takahirox - */ - -function DataViewEx( buffer, littleEndian ) { - - this.dv = new DataView( buffer ); - this.offset = 0; - this.littleEndian = ( littleEndian !== undefined ) ? littleEndian : true; - this.encoder = new CharsetEncoder(); - -} - -DataViewEx.prototype = { - - constructor: DataViewEx, - - getInt8: function () { - - var value = this.dv.getInt8( this.offset ); - this.offset += 1; - return value; - - }, - - getInt8Array: function ( size ) { - - var a = []; - - for ( var i = 0; i < size; i ++ ) { - - a.push( this.getInt8() ); - - } - - return a; - - }, - - getUint8: function () { - - var value = this.dv.getUint8( this.offset ); - this.offset += 1; - return value; - - }, - - getUint8Array: function ( size ) { - - var a = []; - - for ( var i = 0; i < size; i ++ ) { - - a.push( this.getUint8() ); - - } - - return a; - - }, - - - getInt16: function () { - - var value = this.dv.getInt16( this.offset, this.littleEndian ); - this.offset += 2; - return value; - - }, - - getInt16Array: function ( size ) { - - var a = []; - - for ( var i = 0; i < size; i ++ ) { - - a.push( this.getInt16() ); - - } - - return a; - - }, - - getUint16: function () { - - var value = this.dv.getUint16( this.offset, this.littleEndian ); - this.offset += 2; - return value; - - }, - - getUint16Array: function ( size ) { - - var a = []; - - for ( var i = 0; i < size; i ++ ) { - - a.push( this.getUint16() ); - - } - - return a; - - }, - - getInt32: function () { - - var value = this.dv.getInt32( this.offset, this.littleEndian ); - this.offset += 4; - return value; - - }, - - getInt32Array: function ( size ) { - - var a = []; - - for ( var i = 0; i < size; i ++ ) { - - a.push( this.getInt32() ); - - } - - return a; - - }, - - getUint32: function () { - - var value = this.dv.getUint32( this.offset, this.littleEndian ); - this.offset += 4; - return value; - - }, - - getUint32Array: function ( size ) { - - var a = []; - - for ( var i = 0; i < size; i ++ ) { - - a.push( this.getUint32() ); - - } - - return a; - - }, - - getFloat32: function () { - - var value = this.dv.getFloat32( this.offset, this.littleEndian ); - this.offset += 4; - return value; - - }, - - getFloat32Array: function ( size ) { - - var a = []; - - for ( var i = 0; i < size; i ++ ) { - - a.push( this.getFloat32() ); - - } - - return a; - - }, - - getFloat64: function () { - - var value = this.dv.getFloat64( this.offset, this.littleEndian ); - this.offset += 8; - return value; - - }, - - getFloat64Array: function ( size ) { - - var a = []; - - for ( var i = 0; i < size; i ++ ) { - - a.push( this.getFloat64() ); - - } - - return a; - - }, - - getIndex: function ( type, isUnsigned ) { - - switch ( type ) { - - case 1: - return ( isUnsigned === true ) ? this.getUint8() : this.getInt8(); - - case 2: - return ( isUnsigned === true ) ? this.getUint16() : this.getInt16(); - - case 4: - return this.getInt32(); // No Uint32 - - default: - throw 'unknown number type ' + type + ' exception.'; - - } - - }, - - getIndexArray: function ( type, size, isUnsigned ) { - - var a = []; - - for ( var i = 0; i < size; i ++ ) { - - a.push( this.getIndex( type, isUnsigned ) ); - - } - - return a; - - }, - - getChars: function ( size ) { - - var str = ''; - - while ( size > 0 ) { - - var value = this.getUint8(); - size --; - - if ( value === 0 ) { - - break; - - } - - str += String.fromCharCode( value ); - - } - - while ( size > 0 ) { - - this.getUint8(); - size --; - - } - - return str; - - }, - - getSjisStringsAsUnicode: function ( size ) { - - var a = []; - - while ( size > 0 ) { - - var value = this.getUint8(); - size --; - - if ( value === 0 ) { - - break; - - } - - a.push( value ); - - } - - while ( size > 0 ) { - - this.getUint8(); - size --; - - } - - return this.encoder.s2u( new Uint8Array( a ) ); - - }, - - getUnicodeStrings: function ( size ) { - - var str = ''; - - while ( size > 0 ) { - - var value = this.getUint16(); - size -= 2; - - if ( value === 0 ) { - - break; - - } - - str += String.fromCharCode( value ); - - } - - while ( size > 0 ) { - - this.getUint8(); - size --; - - } - - return str; - - }, - - getTextBuffer: function () { - - var size = this.getUint32(); - return this.getUnicodeStrings( size ); - - } - -}; - -/** - * @author takahiro / https://github.com/takahirox - */ - -function DataCreationHelper() { -} - -DataCreationHelper.prototype = { - - constructor: DataCreationHelper, - - leftToRightVector3: function ( v ) { - - v[ 2 ] = - v[ 2 ]; - - }, - - leftToRightQuaternion: function ( q ) { - - q[ 0 ] = - q[ 0 ]; - q[ 1 ] = - q[ 1 ]; - - }, - - leftToRightEuler: function ( r ) { - - r[ 0 ] = - r[ 0 ]; - r[ 1 ] = - r[ 1 ]; - - }, - - leftToRightIndexOrder: function ( p ) { - - var tmp = p[ 2 ]; - p[ 2 ] = p[ 0 ]; - p[ 0 ] = tmp; - - }, - - leftToRightVector3Range: function ( v1, v2 ) { - - var tmp = - v2[ 2 ]; - v2[ 2 ] = - v1[ 2 ]; - v1[ 2 ] = tmp; - - }, - - leftToRightEulerRange: function ( r1, r2 ) { - - var tmp1 = - r2[ 0 ]; - var tmp2 = - r2[ 1 ]; - r2[ 0 ] = - r1[ 0 ]; - r2[ 1 ] = - r1[ 1 ]; - r1[ 0 ] = tmp1; - r1[ 1 ] = tmp2; - - } - -}; - -/** - * @author takahiro / https://github.com/takahirox - */ - -function Parser() { -} - -Parser.prototype.parsePmd = function ( buffer, leftToRight ) { - - var pmd = {}; - var dv = new DataViewEx( buffer ); - - pmd.metadata = {}; - pmd.metadata.format = 'pmd'; - pmd.metadata.coordinateSystem = 'left'; - - var parseHeader = function () { - - var metadata = pmd.metadata; - metadata.magic = dv.getChars( 3 ); - - if ( metadata.magic !== 'Pmd' ) { - - throw 'PMD file magic is not Pmd, but ' + metadata.magic; - - } - - metadata.version = dv.getFloat32(); - metadata.modelName = dv.getSjisStringsAsUnicode( 20 ); - metadata.comment = dv.getSjisStringsAsUnicode( 256 ); - - }; - - var parseVertices = function () { - - var parseVertex = function () { - - var p = {}; - p.position = dv.getFloat32Array( 3 ); - p.normal = dv.getFloat32Array( 3 ); - p.uv = dv.getFloat32Array( 2 ); - p.skinIndices = dv.getUint16Array( 2 ); - p.skinWeights = [ dv.getUint8() / 100 ]; - p.skinWeights.push( 1.0 - p.skinWeights[ 0 ] ); - p.edgeFlag = dv.getUint8(); - return p; - - }; - - var metadata = pmd.metadata; - metadata.vertexCount = dv.getUint32(); - - pmd.vertices = []; - - for ( var i = 0; i < metadata.vertexCount; i ++ ) { - - pmd.vertices.push( parseVertex() ); - - } - - }; - - var parseFaces = function () { - - var parseFace = function () { - - var p = {}; - p.indices = dv.getUint16Array( 3 ); - return p; - - }; - - var metadata = pmd.metadata; - metadata.faceCount = dv.getUint32() / 3; - - pmd.faces = []; - - for ( var i = 0; i < metadata.faceCount; i ++ ) { - - pmd.faces.push( parseFace() ); - - } - - }; - - var parseMaterials = function () { - - var parseMaterial = function () { - - var p = {}; - p.diffuse = dv.getFloat32Array( 4 ); - p.shininess = dv.getFloat32(); - p.specular = dv.getFloat32Array( 3 ); - p.ambient = dv.getFloat32Array( 3 ); - p.toonIndex = dv.getInt8(); - p.edgeFlag = dv.getUint8(); - p.faceCount = dv.getUint32() / 3; - p.fileName = dv.getSjisStringsAsUnicode( 20 ); - return p; - - }; - - var metadata = pmd.metadata; - metadata.materialCount = dv.getUint32(); - - pmd.materials = []; - - for ( var i = 0; i < metadata.materialCount; i ++ ) { - - pmd.materials.push( parseMaterial() ); - - } - - }; - - var parseBones = function () { - - var parseBone = function () { - - var p = {}; - p.name = dv.getSjisStringsAsUnicode( 20 ); - p.parentIndex = dv.getInt16(); - p.tailIndex = dv.getInt16(); - p.type = dv.getUint8(); - p.ikIndex = dv.getInt16(); - p.position = dv.getFloat32Array( 3 ); - return p; - - }; - - var metadata = pmd.metadata; - metadata.boneCount = dv.getUint16(); - - pmd.bones = []; - - for ( var i = 0; i < metadata.boneCount; i ++ ) { - - pmd.bones.push( parseBone() ); - - } - - }; - - var parseIks = function () { - - var parseIk = function () { - - var p = {}; - p.target = dv.getUint16(); - p.effector = dv.getUint16(); - p.linkCount = dv.getUint8(); - p.iteration = dv.getUint16(); - p.maxAngle = dv.getFloat32(); - - p.links = []; - for ( var i = 0; i < p.linkCount; i ++ ) { - - var link = {}; - link.index = dv.getUint16(); - p.links.push( link ); - - } - - return p; - - }; - - var metadata = pmd.metadata; - metadata.ikCount = dv.getUint16(); - - pmd.iks = []; - - for ( var i = 0; i < metadata.ikCount; i ++ ) { - - pmd.iks.push( parseIk() ); - - } - - }; - - var parseMorphs = function () { - - var parseMorph = function () { - - var p = {}; - p.name = dv.getSjisStringsAsUnicode( 20 ); - p.elementCount = dv.getUint32(); - p.type = dv.getUint8(); - - p.elements = []; - for ( var i = 0; i < p.elementCount; i ++ ) { - - p.elements.push( { - index: dv.getUint32(), - position: dv.getFloat32Array( 3 ) - } ); - - } - - return p; - - }; - - var metadata = pmd.metadata; - metadata.morphCount = dv.getUint16(); - - pmd.morphs = []; - - for ( var i = 0; i < metadata.morphCount; i ++ ) { - - pmd.morphs.push( parseMorph() ); - - } - - - }; - - var parseMorphFrames = function () { - - var parseMorphFrame = function () { - - var p = {}; - p.index = dv.getUint16(); - return p; - - }; - - var metadata = pmd.metadata; - metadata.morphFrameCount = dv.getUint8(); - - pmd.morphFrames = []; - - for ( var i = 0; i < metadata.morphFrameCount; i ++ ) { - - pmd.morphFrames.push( parseMorphFrame() ); - - } - - }; - - var parseBoneFrameNames = function () { - - var parseBoneFrameName = function () { - - var p = {}; - p.name = dv.getSjisStringsAsUnicode( 50 ); - return p; - - }; - - var metadata = pmd.metadata; - metadata.boneFrameNameCount = dv.getUint8(); - - pmd.boneFrameNames = []; - - for ( var i = 0; i < metadata.boneFrameNameCount; i ++ ) { - - pmd.boneFrameNames.push( parseBoneFrameName() ); - - } - - }; - - var parseBoneFrames = function () { - - var parseBoneFrame = function () { - - var p = {}; - p.boneIndex = dv.getInt16(); - p.frameIndex = dv.getUint8(); - return p; - - }; - - var metadata = pmd.metadata; - metadata.boneFrameCount = dv.getUint32(); - - pmd.boneFrames = []; - - for ( var i = 0; i < metadata.boneFrameCount; i ++ ) { - - pmd.boneFrames.push( parseBoneFrame() ); - - } - - }; - - var parseEnglishHeader = function () { - - var metadata = pmd.metadata; - metadata.englishCompatibility = dv.getUint8(); - - if ( metadata.englishCompatibility > 0 ) { - - metadata.englishModelName = dv.getSjisStringsAsUnicode( 20 ); - metadata.englishComment = dv.getSjisStringsAsUnicode( 256 ); - - } - - }; - - var parseEnglishBoneNames = function () { - - var parseEnglishBoneName = function () { - - var p = {}; - p.name = dv.getSjisStringsAsUnicode( 20 ); - return p; - - }; - - var metadata = pmd.metadata; - - if ( metadata.englishCompatibility === 0 ) { - - return; - - } - - pmd.englishBoneNames = []; - - for ( var i = 0; i < metadata.boneCount; i ++ ) { - - pmd.englishBoneNames.push( parseEnglishBoneName() ); - - } - - }; - - var parseEnglishMorphNames = function () { - - var parseEnglishMorphName = function () { - - var p = {}; - p.name = dv.getSjisStringsAsUnicode( 20 ); - return p; - - }; - - var metadata = pmd.metadata; - - if ( metadata.englishCompatibility === 0 ) { - - return; - - } - - pmd.englishMorphNames = []; - - for ( var i = 0; i < metadata.morphCount - 1; i ++ ) { - - pmd.englishMorphNames.push( parseEnglishMorphName() ); - - } - - }; - - var parseEnglishBoneFrameNames = function () { - - var parseEnglishBoneFrameName = function () { - - var p = {}; - p.name = dv.getSjisStringsAsUnicode( 50 ); - return p; - - }; - - var metadata = pmd.metadata; - - if ( metadata.englishCompatibility === 0 ) { - - return; - - } - - pmd.englishBoneFrameNames = []; - - for ( var i = 0; i < metadata.boneFrameNameCount; i ++ ) { - - pmd.englishBoneFrameNames.push( parseEnglishBoneFrameName() ); - - } - - }; - - var parseToonTextures = function () { - - var parseToonTexture = function () { - - var p = {}; - p.fileName = dv.getSjisStringsAsUnicode( 100 ); - return p; - - }; - - pmd.toonTextures = []; - - for ( var i = 0; i < 10; i ++ ) { - - pmd.toonTextures.push( parseToonTexture() ); - - } - - }; - - var parseRigidBodies = function () { - - var parseRigidBody = function () { - - var p = {}; - p.name = dv.getSjisStringsAsUnicode( 20 ); - p.boneIndex = dv.getInt16(); - p.groupIndex = dv.getUint8(); - p.groupTarget = dv.getUint16(); - p.shapeType = dv.getUint8(); - p.width = dv.getFloat32(); - p.height = dv.getFloat32(); - p.depth = dv.getFloat32(); - p.position = dv.getFloat32Array( 3 ); - p.rotation = dv.getFloat32Array( 3 ); - p.weight = dv.getFloat32(); - p.positionDamping = dv.getFloat32(); - p.rotationDamping = dv.getFloat32(); - p.restitution = dv.getFloat32(); - p.friction = dv.getFloat32(); - p.type = dv.getUint8(); - return p; - - }; - - var metadata = pmd.metadata; - metadata.rigidBodyCount = dv.getUint32(); - - pmd.rigidBodies = []; - - for ( var i = 0; i < metadata.rigidBodyCount; i ++ ) { - - pmd.rigidBodies.push( parseRigidBody() ); - - } - - }; - - var parseConstraints = function () { - - var parseConstraint = function () { - - var p = {}; - p.name = dv.getSjisStringsAsUnicode( 20 ); - p.rigidBodyIndex1 = dv.getUint32(); - p.rigidBodyIndex2 = dv.getUint32(); - p.position = dv.getFloat32Array( 3 ); - p.rotation = dv.getFloat32Array( 3 ); - p.translationLimitation1 = dv.getFloat32Array( 3 ); - p.translationLimitation2 = dv.getFloat32Array( 3 ); - p.rotationLimitation1 = dv.getFloat32Array( 3 ); - p.rotationLimitation2 = dv.getFloat32Array( 3 ); - p.springPosition = dv.getFloat32Array( 3 ); - p.springRotation = dv.getFloat32Array( 3 ); - return p; - - }; - - var metadata = pmd.metadata; - metadata.constraintCount = dv.getUint32(); - - pmd.constraints = []; - - for ( var i = 0; i < metadata.constraintCount; i ++ ) { - - pmd.constraints.push( parseConstraint() ); - - } - - }; - - parseHeader(); - parseVertices(); - parseFaces(); - parseMaterials(); - parseBones(); - parseIks(); - parseMorphs(); - parseMorphFrames(); - parseBoneFrameNames(); - parseBoneFrames(); - parseEnglishHeader(); - parseEnglishBoneNames(); - parseEnglishMorphNames(); - parseEnglishBoneFrameNames(); - parseToonTextures(); - parseRigidBodies(); - parseConstraints(); - - if ( leftToRight === true ) this.leftToRightModel( pmd ); - - // console.log( pmd ); // for console debug - - return pmd; - -}; - -Parser.prototype.parsePmx = function ( buffer, leftToRight ) { - - var pmx = {}; - var dv = new DataViewEx( buffer ); - - pmx.metadata = {}; - pmx.metadata.format = 'pmx'; - pmx.metadata.coordinateSystem = 'left'; - - var parseHeader = function () { - - var metadata = pmx.metadata; - metadata.magic = dv.getChars( 4 ); - - // Note: don't remove the last blank space. - if ( metadata.magic !== 'PMX ' ) { - - throw 'PMX file magic is not PMX , but ' + metadata.magic; - - } - - metadata.version = dv.getFloat32(); - - if ( metadata.version !== 2.0 && metadata.version !== 2.1 ) { - - throw 'PMX version ' + metadata.version + ' is not supported.'; - - } - - metadata.headerSize = dv.getUint8(); - metadata.encoding = dv.getUint8(); - metadata.additionalUvNum = dv.getUint8(); - metadata.vertexIndexSize = dv.getUint8(); - metadata.textureIndexSize = dv.getUint8(); - metadata.materialIndexSize = dv.getUint8(); - metadata.boneIndexSize = dv.getUint8(); - metadata.morphIndexSize = dv.getUint8(); - metadata.rigidBodyIndexSize = dv.getUint8(); - metadata.modelName = dv.getTextBuffer(); - metadata.englishModelName = dv.getTextBuffer(); - metadata.comment = dv.getTextBuffer(); - metadata.englishComment = dv.getTextBuffer(); - - }; - - var parseVertices = function () { - - var parseVertex = function () { - - var p = {}; - p.position = dv.getFloat32Array( 3 ); - p.normal = dv.getFloat32Array( 3 ); - p.uv = dv.getFloat32Array( 2 ); - - p.auvs = []; - - for ( var i = 0; i < pmx.metadata.additionalUvNum; i ++ ) { - - p.auvs.push( dv.getFloat32Array( 4 ) ); - - } - - p.type = dv.getUint8(); - - var indexSize = metadata.boneIndexSize; - - if ( p.type === 0 ) { // BDEF1 - - p.skinIndices = dv.getIndexArray( indexSize, 1 ); - p.skinWeights = [ 1.0 ]; - - } else if ( p.type === 1 ) { // BDEF2 - - p.skinIndices = dv.getIndexArray( indexSize, 2 ); - p.skinWeights = dv.getFloat32Array( 1 ); - p.skinWeights.push( 1.0 - p.skinWeights[ 0 ] ); - - } else if ( p.type === 2 ) { // BDEF4 - - p.skinIndices = dv.getIndexArray( indexSize, 4 ); - p.skinWeights = dv.getFloat32Array( 4 ); - - } else if ( p.type === 3 ) { // SDEF - - p.skinIndices = dv.getIndexArray( indexSize, 2 ); - p.skinWeights = dv.getFloat32Array( 1 ); - p.skinWeights.push( 1.0 - p.skinWeights[ 0 ] ); - - p.skinC = dv.getFloat32Array( 3 ); - p.skinR0 = dv.getFloat32Array( 3 ); - p.skinR1 = dv.getFloat32Array( 3 ); - - // SDEF is not supported yet and is handled as BDEF2 so far. - // TODO: SDEF support - p.type = 1; - - } else { - - throw 'unsupport bone type ' + p.type + ' exception.'; - - } - - p.edgeRatio = dv.getFloat32(); - return p; - - }; - - var metadata = pmx.metadata; - metadata.vertexCount = dv.getUint32(); - - pmx.vertices = []; - - for ( var i = 0; i < metadata.vertexCount; i ++ ) { - - pmx.vertices.push( parseVertex() ); - - } - - }; - - var parseFaces = function () { - - var parseFace = function () { - - var p = {}; - p.indices = dv.getIndexArray( metadata.vertexIndexSize, 3, true ); - return p; - - }; - - var metadata = pmx.metadata; - metadata.faceCount = dv.getUint32() / 3; - - pmx.faces = []; - - for ( var i = 0; i < metadata.faceCount; i ++ ) { - - pmx.faces.push( parseFace() ); - - } - - }; - - var parseTextures = function () { - - var parseTexture = function () { - - return dv.getTextBuffer(); - - }; - - var metadata = pmx.metadata; - metadata.textureCount = dv.getUint32(); - - pmx.textures = []; - - for ( var i = 0; i < metadata.textureCount; i ++ ) { - - pmx.textures.push( parseTexture() ); - - } - - }; - - var parseMaterials = function () { - - var parseMaterial = function () { - - var p = {}; - p.name = dv.getTextBuffer(); - p.englishName = dv.getTextBuffer(); - p.diffuse = dv.getFloat32Array( 4 ); - p.specular = dv.getFloat32Array( 3 ); - p.shininess = dv.getFloat32(); - p.ambient = dv.getFloat32Array( 3 ); - p.flag = dv.getUint8(); - p.edgeColor = dv.getFloat32Array( 4 ); - p.edgeSize = dv.getFloat32(); - p.textureIndex = dv.getIndex( pmx.metadata.textureIndexSize ); - p.envTextureIndex = dv.getIndex( pmx.metadata.textureIndexSize ); - p.envFlag = dv.getUint8(); - p.toonFlag = dv.getUint8(); - - if ( p.toonFlag === 0 ) { - - p.toonIndex = dv.getIndex( pmx.metadata.textureIndexSize ); - - } else if ( p.toonFlag === 1 ) { - - p.toonIndex = dv.getInt8(); - - } else { - - throw 'unknown toon flag ' + p.toonFlag + ' exception.'; - - } - - p.comment = dv.getTextBuffer(); - p.faceCount = dv.getUint32() / 3; - return p; - - }; - - var metadata = pmx.metadata; - metadata.materialCount = dv.getUint32(); - - pmx.materials = []; - - for ( var i = 0; i < metadata.materialCount; i ++ ) { - - pmx.materials.push( parseMaterial() ); - - } - - }; - - var parseBones = function () { - - var parseBone = function () { - - var p = {}; - p.name = dv.getTextBuffer(); - p.englishName = dv.getTextBuffer(); - p.position = dv.getFloat32Array( 3 ); - p.parentIndex = dv.getIndex( pmx.metadata.boneIndexSize ); - p.transformationClass = dv.getUint32(); - p.flag = dv.getUint16(); - - if ( p.flag & 0x1 ) { - - p.connectIndex = dv.getIndex( pmx.metadata.boneIndexSize ); - - } else { - - p.offsetPosition = dv.getFloat32Array( 3 ); - - } - - if ( p.flag & 0x100 || p.flag & 0x200 ) { - - // Note: I don't think Grant is an appropriate name - // but I found that some English translated MMD tools use this term - // so I've named it Grant so far. - // I'd rename to more appropriate name from Grant later. - var grant = {}; - - grant.isLocal = ( p.flag & 0x80 ) !== 0 ? true : false; - grant.affectRotation = ( p.flag & 0x100 ) !== 0 ? true : false; - grant.affectPosition = ( p.flag & 0x200 ) !== 0 ? true : false; - grant.parentIndex = dv.getIndex( pmx.metadata.boneIndexSize ); - grant.ratio = dv.getFloat32(); - - p.grant = grant; - - } - - if ( p.flag & 0x400 ) { - - p.fixAxis = dv.getFloat32Array( 3 ); - - } - - if ( p.flag & 0x800 ) { - - p.localXVector = dv.getFloat32Array( 3 ); - p.localZVector = dv.getFloat32Array( 3 ); - - } - - if ( p.flag & 0x2000 ) { - - p.key = dv.getUint32(); - - } - - if ( p.flag & 0x20 ) { - - var ik = {}; - - ik.effector = dv.getIndex( pmx.metadata.boneIndexSize ); - ik.target = null; - ik.iteration = dv.getUint32(); - ik.maxAngle = dv.getFloat32(); - ik.linkCount = dv.getUint32(); - ik.links = []; - - for ( var i = 0; i < ik.linkCount; i ++ ) { - - var link = {}; - link.index = dv.getIndex( pmx.metadata.boneIndexSize ); - link.angleLimitation = dv.getUint8(); - - if ( link.angleLimitation === 1 ) { - - link.lowerLimitationAngle = dv.getFloat32Array( 3 ); - link.upperLimitationAngle = dv.getFloat32Array( 3 ); - - } - - ik.links.push( link ); - - } - - p.ik = ik; - - } - - return p; - - }; - - var metadata = pmx.metadata; - metadata.boneCount = dv.getUint32(); - - pmx.bones = []; - - for ( var i = 0; i < metadata.boneCount; i ++ ) { - - pmx.bones.push( parseBone() ); - - } - - }; - - var parseMorphs = function () { - - var parseMorph = function () { - - var p = {}; - p.name = dv.getTextBuffer(); - p.englishName = dv.getTextBuffer(); - p.panel = dv.getUint8(); - p.type = dv.getUint8(); - p.elementCount = dv.getUint32(); - p.elements = []; - - for ( var i = 0; i < p.elementCount; i ++ ) { - - if ( p.type === 0 ) { // group morph - - var m = {}; - m.index = dv.getIndex( pmx.metadata.morphIndexSize ); - m.ratio = dv.getFloat32(); - p.elements.push( m ); - - } else if ( p.type === 1 ) { // vertex morph - - var m = {}; - m.index = dv.getIndex( pmx.metadata.vertexIndexSize, true ); - m.position = dv.getFloat32Array( 3 ); - p.elements.push( m ); - - } else if ( p.type === 2 ) { // bone morph - - var m = {}; - m.index = dv.getIndex( pmx.metadata.boneIndexSize ); - m.position = dv.getFloat32Array( 3 ); - m.rotation = dv.getFloat32Array( 4 ); - p.elements.push( m ); - - } else if ( p.type === 3 ) { // uv morph - - var m = {}; - m.index = dv.getIndex( pmx.metadata.vertexIndexSize, true ); - m.uv = dv.getFloat32Array( 4 ); - p.elements.push( m ); - - } else if ( p.type === 4 ) { // additional uv1 - - // TODO: implement - - } else if ( p.type === 5 ) { // additional uv2 - - // TODO: implement - - } else if ( p.type === 6 ) { // additional uv3 - - // TODO: implement - - } else if ( p.type === 7 ) { // additional uv4 - - // TODO: implement - - } else if ( p.type === 8 ) { // material morph - - var m = {}; - m.index = dv.getIndex( pmx.metadata.materialIndexSize ); - m.type = dv.getUint8(); - m.diffuse = dv.getFloat32Array( 4 ); - m.specular = dv.getFloat32Array( 3 ); - m.shininess = dv.getFloat32(); - m.ambient = dv.getFloat32Array( 3 ); - m.edgeColor = dv.getFloat32Array( 4 ); - m.edgeSize = dv.getFloat32(); - m.textureColor = dv.getFloat32Array( 4 ); - m.sphereTextureColor = dv.getFloat32Array( 4 ); - m.toonColor = dv.getFloat32Array( 4 ); - p.elements.push( m ); - - } - - } - - return p; - - }; - - var metadata = pmx.metadata; - metadata.morphCount = dv.getUint32(); - - pmx.morphs = []; - - for ( var i = 0; i < metadata.morphCount; i ++ ) { - - pmx.morphs.push( parseMorph() ); - - } - - }; - - var parseFrames = function () { - - var parseFrame = function () { - - var p = {}; - p.name = dv.getTextBuffer(); - p.englishName = dv.getTextBuffer(); - p.type = dv.getUint8(); - p.elementCount = dv.getUint32(); - p.elements = []; - - for ( var i = 0; i < p.elementCount; i ++ ) { - - var e = {}; - e.target = dv.getUint8(); - e.index = ( e.target === 0 ) ? dv.getIndex( pmx.metadata.boneIndexSize ) : dv.getIndex( pmx.metadata.morphIndexSize ); - p.elements.push( e ); - - } - - return p; - - }; - - var metadata = pmx.metadata; - metadata.frameCount = dv.getUint32(); - - pmx.frames = []; - - for ( var i = 0; i < metadata.frameCount; i ++ ) { - - pmx.frames.push( parseFrame() ); - - } - - }; - - var parseRigidBodies = function () { - - var parseRigidBody = function () { - - var p = {}; - p.name = dv.getTextBuffer(); - p.englishName = dv.getTextBuffer(); - p.boneIndex = dv.getIndex( pmx.metadata.boneIndexSize ); - p.groupIndex = dv.getUint8(); - p.groupTarget = dv.getUint16(); - p.shapeType = dv.getUint8(); - p.width = dv.getFloat32(); - p.height = dv.getFloat32(); - p.depth = dv.getFloat32(); - p.position = dv.getFloat32Array( 3 ); - p.rotation = dv.getFloat32Array( 3 ); - p.weight = dv.getFloat32(); - p.positionDamping = dv.getFloat32(); - p.rotationDamping = dv.getFloat32(); - p.restitution = dv.getFloat32(); - p.friction = dv.getFloat32(); - p.type = dv.getUint8(); - return p; - - }; - - var metadata = pmx.metadata; - metadata.rigidBodyCount = dv.getUint32(); - - pmx.rigidBodies = []; - - for ( var i = 0; i < metadata.rigidBodyCount; i ++ ) { - - pmx.rigidBodies.push( parseRigidBody() ); - - } - - }; - - var parseConstraints = function () { - - var parseConstraint = function () { - - var p = {}; - p.name = dv.getTextBuffer(); - p.englishName = dv.getTextBuffer(); - p.type = dv.getUint8(); - p.rigidBodyIndex1 = dv.getIndex( pmx.metadata.rigidBodyIndexSize ); - p.rigidBodyIndex2 = dv.getIndex( pmx.metadata.rigidBodyIndexSize ); - p.position = dv.getFloat32Array( 3 ); - p.rotation = dv.getFloat32Array( 3 ); - p.translationLimitation1 = dv.getFloat32Array( 3 ); - p.translationLimitation2 = dv.getFloat32Array( 3 ); - p.rotationLimitation1 = dv.getFloat32Array( 3 ); - p.rotationLimitation2 = dv.getFloat32Array( 3 ); - p.springPosition = dv.getFloat32Array( 3 ); - p.springRotation = dv.getFloat32Array( 3 ); - return p; - - }; - - var metadata = pmx.metadata; - metadata.constraintCount = dv.getUint32(); - - pmx.constraints = []; - - for ( var i = 0; i < metadata.constraintCount; i ++ ) { - - pmx.constraints.push( parseConstraint() ); - - } - - }; - - parseHeader(); - parseVertices(); - parseFaces(); - parseTextures(); - parseMaterials(); - parseBones(); - parseMorphs(); - parseFrames(); - parseRigidBodies(); - parseConstraints(); - - if ( leftToRight === true ) this.leftToRightModel( pmx ); - - // console.log( pmx ); // for console debug - - return pmx; - -}; - -Parser.prototype.parseVmd = function ( buffer, leftToRight ) { - - var vmd = {}; - var dv = new DataViewEx( buffer ); - - vmd.metadata = {}; - vmd.metadata.coordinateSystem = 'left'; - - var parseHeader = function () { - - var metadata = vmd.metadata; - metadata.magic = dv.getChars( 30 ); - - if ( metadata.magic !== 'Vocaloid Motion Data 0002' ) { - - throw 'VMD file magic is not Vocaloid Motion Data 0002, but ' + metadata.magic; - - } - - metadata.name = dv.getSjisStringsAsUnicode( 20 ); - - }; - - var parseMotions = function () { - - var parseMotion = function () { - - var p = {}; - p.boneName = dv.getSjisStringsAsUnicode( 15 ); - p.frameNum = dv.getUint32(); - p.position = dv.getFloat32Array( 3 ); - p.rotation = dv.getFloat32Array( 4 ); - p.interpolation = dv.getUint8Array( 64 ); - return p; - - }; - - var metadata = vmd.metadata; - metadata.motionCount = dv.getUint32(); - - vmd.motions = []; - for ( var i = 0; i < metadata.motionCount; i ++ ) { - - vmd.motions.push( parseMotion() ); - - } - - }; - - var parseMorphs = function () { - - var parseMorph = function () { - - var p = {}; - p.morphName = dv.getSjisStringsAsUnicode( 15 ); - p.frameNum = dv.getUint32(); - p.weight = dv.getFloat32(); - return p; - - }; - - var metadata = vmd.metadata; - metadata.morphCount = dv.getUint32(); - - vmd.morphs = []; - for ( var i = 0; i < metadata.morphCount; i ++ ) { - - vmd.morphs.push( parseMorph() ); - - } - - }; - - var parseCameras = function () { - - var parseCamera = function () { - - var p = {}; - p.frameNum = dv.getUint32(); - p.distance = dv.getFloat32(); - p.position = dv.getFloat32Array( 3 ); - p.rotation = dv.getFloat32Array( 3 ); - p.interpolation = dv.getUint8Array( 24 ); - p.fov = dv.getUint32(); - p.perspective = dv.getUint8(); - return p; - - }; - - var metadata = vmd.metadata; - metadata.cameraCount = dv.getUint32(); - - vmd.cameras = []; - for ( var i = 0; i < metadata.cameraCount; i ++ ) { - - vmd.cameras.push( parseCamera() ); - - } - - }; - - parseHeader(); - parseMotions(); - parseMorphs(); - parseCameras(); - - if ( leftToRight === true ) this.leftToRightVmd( vmd ); - - // console.log( vmd ); // for console debug - - return vmd; - -}; - -Parser.prototype.parseVpd = function ( text, leftToRight ) { - - var vpd = {}; - - vpd.metadata = {}; - vpd.metadata.coordinateSystem = 'left'; - - vpd.bones = []; - - var commentPatternG = /\/\/\w*(\r|\n|\r\n)/g; - var newlinePattern = /\r|\n|\r\n/; - - var lines = text.replace( commentPatternG, '' ).split( newlinePattern ); - - function throwError() { - - throw 'the file seems not vpd file.'; - - } - - function checkMagic() { - - if ( lines[ 0 ] !== 'Vocaloid Pose Data file' ) { - - throwError(); - - } - - } - - function parseHeader() { - - if ( lines.length < 4 ) { - - throwError(); - - } - - vpd.metadata.parentFile = lines[ 2 ]; - vpd.metadata.boneCount = parseInt( lines[ 3 ] ); - - } - - function parseBones() { - - var boneHeaderPattern = /^\s*(Bone[0-9]+)\s*\{\s*(.*)$/; - var boneVectorPattern = /^\s*(-?[0-9]+\.[0-9]+)\s*,\s*(-?[0-9]+\.[0-9]+)\s*,\s*(-?[0-9]+\.[0-9]+)\s*;/; - var boneQuaternionPattern = /^\s*(-?[0-9]+\.[0-9]+)\s*,\s*(-?[0-9]+\.[0-9]+)\s*,\s*(-?[0-9]+\.[0-9]+)\s*,\s*(-?[0-9]+\.[0-9]+)\s*;/; - var boneFooterPattern = /^\s*}/; - - var bones = vpd.bones; - var n = null; - var v = null; - var q = null; - - for ( var i = 4; i < lines.length; i ++ ) { - - var line = lines[ i ]; - - var result; - - result = line.match( boneHeaderPattern ); - - if ( result !== null ) { - - if ( n !== null ) { - - throwError(); - - } - - n = result[ 2 ]; - - } - - result = line.match( boneVectorPattern ); - - if ( result !== null ) { - - if ( v !== null ) { - - throwError(); - - } - - v = [ - - parseFloat( result[ 1 ] ), - parseFloat( result[ 2 ] ), - parseFloat( result[ 3 ] ) - - ]; - - } - - result = line.match( boneQuaternionPattern ); - - if ( result !== null ) { - - if ( q !== null ) { - - throwError(); - - } - - q = [ - - parseFloat( result[ 1 ] ), - parseFloat( result[ 2 ] ), - parseFloat( result[ 3 ] ), - parseFloat( result[ 4 ] ) - - ]; - - - } - - result = line.match( boneFooterPattern ); - - if ( result !== null ) { - - if ( n === null || v === null || q === null ) { - - throwError(); - - } - - bones.push( { - - name: n, - translation: v, - quaternion: q - - } ); - - n = null; - v = null; - q = null; - - } - - } - - if ( n !== null || v !== null || q !== null ) { - - throwError(); - - } - - } - - checkMagic(); - parseHeader(); - parseBones(); - - if ( leftToRight === true ) this.leftToRightVpd( vpd ); - - // console.log( vpd ); // for console debug - - return vpd; - -}; - -Parser.prototype.mergeVmds = function ( vmds ) { - - var v = {}; - v.metadata = {}; - v.metadata.name = vmds[ 0 ].metadata.name; - v.metadata.coordinateSystem = vmds[ 0 ].metadata.coordinateSystem; - v.metadata.motionCount = 0; - v.metadata.morphCount = 0; - v.metadata.cameraCount = 0; - v.motions = []; - v.morphs = []; - v.cameras = []; - - for ( var i = 0; i < vmds.length; i ++ ) { - - var v2 = vmds[ i ]; - - v.metadata.motionCount += v2.metadata.motionCount; - v.metadata.morphCount += v2.metadata.morphCount; - v.metadata.cameraCount += v2.metadata.cameraCount; - - for ( var j = 0; j < v2.metadata.motionCount; j ++ ) { - - v.motions.push( v2.motions[ j ] ); - - } - - for ( var j = 0; j < v2.metadata.morphCount; j ++ ) { - - v.morphs.push( v2.morphs[ j ] ); - - } - - for ( var j = 0; j < v2.metadata.cameraCount; j ++ ) { - - v.cameras.push( v2.cameras[ j ] ); - - } - - } - - return v; - -}; - -Parser.prototype.leftToRightModel = function ( model ) { - - if ( model.metadata.coordinateSystem === 'right' ) { - - return; - - } - - model.metadata.coordinateSystem = 'right'; - - var helper = new DataCreationHelper(); - - for ( var i = 0; i < model.metadata.vertexCount; i ++ ) { - - helper.leftToRightVector3( model.vertices[ i ].position ); - helper.leftToRightVector3( model.vertices[ i ].normal ); - - } - - for ( var i = 0; i < model.metadata.faceCount; i ++ ) { - - helper.leftToRightIndexOrder( model.faces[ i ].indices ); - - } - - for ( var i = 0; i < model.metadata.boneCount; i ++ ) { - - helper.leftToRightVector3( model.bones[ i ].position ); - - } - - // TODO: support other morph for PMX - for ( var i = 0; i < model.metadata.morphCount; i ++ ) { - - var m = model.morphs[ i ]; - - if ( model.metadata.format === 'pmx' && m.type !== 1 ) { - - // TODO: implement - continue; - - } - - for ( var j = 0; j < m.elements.length; j ++ ) { - - helper.leftToRightVector3( m.elements[ j ].position ); - - } - - } - - for ( var i = 0; i < model.metadata.rigidBodyCount; i ++ ) { - - helper.leftToRightVector3( model.rigidBodies[ i ].position ); - helper.leftToRightEuler( model.rigidBodies[ i ].rotation ); - - } - - for ( var i = 0; i < model.metadata.constraintCount; i ++ ) { - - helper.leftToRightVector3( model.constraints[ i ].position ); - helper.leftToRightEuler( model.constraints[ i ].rotation ); - helper.leftToRightVector3Range( model.constraints[ i ].translationLimitation1, model.constraints[ i ].translationLimitation2 ); - helper.leftToRightEulerRange( model.constraints[ i ].rotationLimitation1, model.constraints[ i ].rotationLimitation2 ); - - } - -}; - -Parser.prototype.leftToRightVmd = function ( vmd ) { - - if ( vmd.metadata.coordinateSystem === 'right' ) { - - return; - - } - - vmd.metadata.coordinateSystem = 'right'; - - var helper = new DataCreationHelper(); - - for ( var i = 0; i < vmd.metadata.motionCount; i ++ ) { - - helper.leftToRightVector3( vmd.motions[ i ].position ); - helper.leftToRightQuaternion( vmd.motions[ i ].rotation ); - - } - - for ( var i = 0; i < vmd.metadata.cameraCount; i ++ ) { - - helper.leftToRightVector3( vmd.cameras[ i ].position ); - helper.leftToRightEuler( vmd.cameras[ i ].rotation ); - - } - -}; - -Parser.prototype.leftToRightVpd = function ( vpd ) { - - if ( vpd.metadata.coordinateSystem === 'right' ) { - - return; - - } - - vpd.metadata.coordinateSystem = 'right'; - - var helper = new DataCreationHelper(); - - for ( var i = 0; i < vpd.bones.length; i ++ ) { - - helper.leftToRightVector3( vpd.bones[ i ].translation ); - helper.leftToRightQuaternion( vpd.bones[ i ].quaternion ); - - } - -}; - -export { CharsetEncoder, Parser }; diff --git a/examples/jsm/loaders/MMDLoader.js b/examples/jsm/loaders/MMDLoader.js index c8e70db21eea0b..281193224f41fe 100644 --- a/examples/jsm/loaders/MMDLoader.js +++ b/examples/jsm/loaders/MMDLoader.js @@ -46,6 +46,7 @@ import { Interpolant, Loader, LoaderUtils as THRLoaderUtils, + MeshToonMaterial, MultiplyOperation, NumberKeyframeTrack, OneMinusSrcAlphaFactor, @@ -61,9 +62,8 @@ import { Vector3, VectorKeyframeTrack } from "../../../build/three.module.js"; -import { Parser as MMDParser } from '../libs/mmdparser.js'; import { TGALoader } from "./TGALoader"; -import { MeshToonMaterial } from "../../../build/three"; +import { Parser } from '../libs/MMDParser'; /** * @param {LoadingManager} manager @@ -360,7 +360,7 @@ MMDLoader.prototype = { if ( this.parser === null ) { - this.parser = new MMDParser(); + this.parser = new Parser(); } diff --git a/examples/jsm/loaders/TestParser.js b/examples/jsm/loaders/TestParser.js new file mode 100644 index 00000000000000..534a3c05cdfde8 --- /dev/null +++ b/examples/jsm/loaders/TestParser.js @@ -0,0 +1,5 @@ +function TestParser() { + +} + +export { TestParser }; diff --git a/examples/webgl_loader_mmd.html b/examples/webgl_loader_mmd.html index 27825c64cb7806..cd0750d275e670 100644 --- a/examples/webgl_loader_mmd.html +++ b/examples/webgl_loader_mmd.html @@ -35,7 +35,7 @@ - + diff --git a/examples/webgl_loader_mmd_audio.html b/examples/webgl_loader_mmd_audio.html index ca056c33814aab..23296a08e4b514 100644 --- a/examples/webgl_loader_mmd_audio.html +++ b/examples/webgl_loader_mmd_audio.html @@ -37,7 +37,7 @@ - + diff --git a/examples/webgl_loader_mmd_pose.html b/examples/webgl_loader_mmd_pose.html index df7cde2588629c..33234e978ff9a2 100644 --- a/examples/webgl_loader_mmd_pose.html +++ b/examples/webgl_loader_mmd_pose.html @@ -35,7 +35,7 @@ - + diff --git a/examples/webgl_loader_obj2_meshspray.html b/examples/webgl_loader_obj2_meshspray.html index 1f0ac909667060..8a047a5a36915a 100644 --- a/examples/webgl_loader_obj2_meshspray.html +++ b/examples/webgl_loader_obj2_meshspray.html @@ -168,6 +168,7 @@ workerCode += 'MeshSpray = {};\n\n'; workerCode += codeSerializer.serializeObject( 'THREE.LoaderSupport.Validator', THREE.LoaderSupport.Validator ); workerCode += codeSerializer.serializeClass( 'MeshSpray.Parser', MeshSpray.Parser ); + workerCode += 'var LoaderSupport = THREE.LoaderSupport;\n\n'; return workerCode; };