import { AccessorType , IBufferView , IAccessor , INode , IScene , IMesh , IMaterial , ITexture , IImage , ISampler , IAnimation , ImageMimeType , IMeshPrimitive , IBuffer , IGLTF , MeshPrimitiveMode , AccessorComponentType , ITextureInfo , ISkin } from "babylonjs-gltf2interface" ; import { FloatArray , Nullable , IndicesArray } from "babylonjs/types" ; import { Vector2 , Vector3 , Vector4 , Quaternion , Matrix } from "babylonjs/Maths/math.vector" ; import { Color3 , Color4 } from "babylonjs/Maths/math.color" ; import { Tools } from "babylonjs/Misc/tools" ; import { VertexBuffer } from "babylonjs/Buffers/buffer" ; import { Node } from "babylonjs/node" ; import { TransformNode } from "babylonjs/Meshes/transformNode" ; import { AbstractMesh } from "babylonjs/Meshes/abstractMesh" ; import { SubMesh } from "babylonjs/Meshes/subMesh" ; import { Mesh } from "babylonjs/Meshes/mesh" ; import { MorphTarget } from "babylonjs/Morph/morphTarget" ; import { LinesMesh } from "babylonjs/Meshes/linesMesh" ; import { InstancedMesh } from "babylonjs/Meshes/instancedMesh" ; import { Bone } from "babylonjs/Bones/bone" ; import { BaseTexture } from "babylonjs/Materials/Textures/baseTexture" ; import { Texture } from "babylonjs/Materials/Textures/texture" ; import { Material } from "babylonjs/Materials/material" ; import { Engine } from "babylonjs/Engines/engine" ; import { Scene } from "babylonjs/scene" ; import { IGLTFExporterExtensionV2 } from "./glTFExporterExtension" ; import { _GLTFMaterialExporter } from "./glTFMaterialExporter" ; import { IExportOptions } from "./glTFSerializer" ; import { _GLTFUtilities } from "./glTFUtilities" ; import { GLTFData } from "./glTFData" ; import { _GLTFAnimation } from "./glTFAnimation" ; hidden interface _IVertexAttributeData { /**
     * Specifies the Babylon Vertex Buffer Type (Position, Normal, Color, etc.)
    */ kind : string ; /**
     * Specifies the glTF Accessor Type (VEC2, VEC3, etc.)
    */ accessorType : AccessorType ; /**
     * Specifies the glTF Accessor Component Type (BYTE, UNSIGNED_BYTE, FLOAT, SHORT, INT, etc..)
     */ accessorComponentType : AccessorComponentType ; /**
     * Specifies the BufferView index for the vertex attribute data
    */ bufferViewIndex ? : number ; byteStride ? : number ; } hidden export class _Exporter { /**
     * Stores the glTF to export
     */ public _glTF : IGLTF ; /**
     * Stores all generated buffer views, which represents views into the main glTF buffer data
     */ public _bufferViews : IBufferView [ ] ; /**
     * Stores all the generated accessors, which is used for accessing the data within the buffer views in glTF
     */ public _accessors : IAccessor [ ] ; /**
     * Stores all the generated nodes, which contains transform and/or mesh information per node
     */ public _nodes : INode [ ] ; /**
     * Stores all the generated glTF scenes, which stores multiple node hierarchies
     */ private _scenes : IScene [ ] ; /**
     * Stores all the generated mesh information, each containing a set of primitives to render in glTF
     */ private _meshes : IMesh [ ] ; /**
     * Stores all the generated material information, which represents the appearance of each primitive
     */ public _materials : IMaterial [ ] ; public _materialMap : { [ materialID ] : number ; } ; /**
     * Stores all the generated texture information, which is referenced by glTF materials
     */ public _textures : ITexture [ ] ; /**
     * Stores all the generated image information, which is referenced by glTF textures
     */ public _images : IImage [ ] ; /**
     * Stores all the texture samplers
     */ public _samplers : ISampler [ ] ; /**
     * Stores all the generated glTF skins
     */ public _skins : ISkin [ ] ; /**
     * Stores all the generated animation samplers, which is referenced by glTF animations
     */ /**
     * Stores the animations for glTF models
     */ private _animations : IAnimation [ ] ; /**
     * Stores the total amount of bytes stored in the glTF buffer
     */ private _totalByteLength : number ; /**
     * Stores a reference to the Babylon scene containing the source geometry and material information
     */ public _babylonScene : Scene ; /**
     * Stores a map of the image data, where the key is the file name and the value
     * is the image data
     */ public _imageData : { [ fileName ] : { data : Uint8Array ; mimeType : ImageMimeType ; } ; } ; protected _orderedImageData : Array < { data : Uint8Array ; mimeType : ImageMimeType ; } > ; /**
     * Stores a map of the unique id of a node to its index in the node array
     */ public _nodeMap : { [ key ] : number ; } ; /**
     * Specifies if the source Babylon scene was left handed, and needed conversion.
     */ public _convertToRightHandedSystem : boolean ; /**
     * Specifies if a Babylon node should be converted to right-handed on export
     */ public _convertToRightHandedSystemMap : { [ nodeId ] : boolean ; } ; public _includeCoordinateSystemConversionNodes : boolean = false ; /**
     * Baked animation sample rate
     */ private _animationSampleRate : number ; private _options : IExportOptions ; private _localEngine : Engine ; public _glTFMaterialExporter : _GLTFMaterialExporter ; private _extensions : { [ name ] : IGLTFExporterExtensionV2 ; } = { } ; private static _ExtensionNames = new Array < string > ( ) ; private static _ExtensionFactories : { [ name ] : ( exporter : _Exporter ) => IGLTFExporterExtensionV2 ; } = { } ; private _applyExtension < T > ( node , extensions , index , actionAsync ) { if ( index >= extensions . length ) { return Promise . resolve ( node ) ; } let currentPromise = actionAsync ( extensions [ index ] , node ) ; if ( ! currentPromise ) { return this . _applyExtension ( node , extensions , index + 1 , actionAsync ) ; } return currentPromise . then ( ( newNode ) => this . _applyExtension ( newNode , extensions , index + 1 , actionAsync ) ) ; } private _applyExtensions < T > ( node , actionAsync ) { var extensions = [ ] ; for ( const name of _Exporter . _ExtensionNames ) { extensions . push ( this . _extensions [ name ] ) ; } return this . _applyExtension ( node , extensions , 0 , actionAsync ) ; } public _extensionsPreExportTextureAsync ( context , babylonTexture , mimeType ) { return this . _applyExtensions ( babylonTexture , ( extension , node ) => extension . preExportTextureAsync && extension . preExportTextureAsync ( context , node , mimeType ) ) ; } public _extensionsPostExportMeshPrimitiveAsync ( context , meshPrimitive , babylonSubMesh , binaryWriter ) { return this . _applyExtensions ( meshPrimitive , ( extension , node ) => extension . postExportMeshPrimitiveAsync && extension . postExportMeshPrimitiveAsync ( context , node , babylonSubMesh , binaryWriter ) ) ; } public _extensionsPostExportNodeAsync ( context , node , babylonNode , nodeMap ? ) { return this . _applyExtensions ( node , ( extension , node ) => extension . postExportNodeAsync && extension . postExportNodeAsync ( context , node , babylonNode , nodeMap ) ) ; } public _extensionsPostExportMaterialAsync ( context , material , babylonMaterial ) { return this . _applyExtensions ( material , ( extension , node ) => extension . postExportMaterialAsync && extension . postExportMaterialAsync ( context , node , babylonMaterial ) ) ; } public _extensionsPostExportMaterialAdditionalTextures ( context , material , babylonMaterial ) { let output = [ ] ; for ( const name of _Exporter . _ExtensionNames ) { var extension = this . _extensions [ name ] ; if ( extension . postExportMaterialAdditionalTextures ) { output . push ( ... extension . postExportMaterialAdditionalTextures ( context , material , babylonMaterial ) ) ; } } return output ; } public _extensionsPostExportTextures ( context , textureInfo , babylonTexture ) { for ( const name of _Exporter . _ExtensionNames ) { var extension = this . _extensions [ name ] ; if ( extension . postExportTexture ) { extension . postExportTexture ( context , textureInfo , babylonTexture ) ; } } } private _forEachExtensions ( action ) { for ( const name of _Exporter . _ExtensionNames ) { const extension = this . _extensions [ name ] ; if ( extension . enabled ) { action ( extension ) ; } } } private _extensionsOnExporting ( ) { this . _forEachExtensions ( ( extension ) => { if ( extension . wasUsed ) { if ( this . _glTF . extensionsUsed == null ) { this . _glTF . extensionsUsed = [ ] ; } if ( this . _glTF . extensionsUsed . indexOf ( extension . name ) === - 1 ) { this . _glTF . extensionsUsed . push ( extension . name ) ; } if ( extension . required ) { if ( this . _glTF . extensionsRequired == null ) { this . _glTF . extensionsRequired = [ ] ; } if ( this . _glTF . extensionsRequired . indexOf ( extension . name ) === - 1 ) { this . _glTF . extensionsRequired . push ( extension . name ) ; } } if ( this . _glTF . extensions == null ) { this . _glTF . extensions = { } ; } if ( extension . onExporting ) { extension . onExporting ( ) ; } } } ) ; } /**
     * Load glTF serializer extensions
     */ private _loadExtensions ( ) { for ( const name of _Exporter . _ExtensionNames ) { const extension = _Exporter . _ExtensionFactories [ name ] ( this ) ; this . _extensions [ name ] = extension ; } } param babylonScene param options public constructor ( babylonScene , options ? ) { this . _glTF = { asset : { generator : "BabylonJS" , version : "2.0" } } ; this . _babylonScene = babylonScene ; this . _bufferViews = [ ] ; this . _accessors = [ ] ; this . _meshes = [ ] ; this . _scenes = [ ] ; this . _nodes = [ ] ; this . _images = [ ] ; this . _materials = [ ] ; this . _materialMap = [ ] ; this . _textures = [ ] ; this . _samplers = [ ] ; this . _skins = [ ] ; this . _animations = [ ] ; this . _imageData = { } ; this . _orderedImageData = [ ] ; this . _options = options || { } ; this . _animationSampleRate = options && options . animationSampleRate ? options . animationSampleRate : 1 / 60 ; this . _includeCoordinateSystemConversionNodes = options && options . includeCoordinateSystemConversionNodes ? true : false ; this . _glTFMaterialExporter = new _GLTFMaterialExporter ( this ) ; this . _loadExtensions ( ) ; } public dispose ( ) { for ( var extensionKey in this . _extensions ) { const extension = this . _extensions [ extensionKey ] ; extension . dispose ( ) ; } } param name param factory public static RegisterExtension ( name , factory ) { if ( _Exporter . UnregisterExtension ( name ) ) { Tools . Warn ( `Extension with the name ${ name } already exists` ) ; } _Exporter . _ExtensionFactories [ name ] = factory ; _Exporter . _ExtensionNames . push ( name ) ; } param name returns public static UnregisterExtension ( name ) { if ( ! _Exporter . _ExtensionFactories [ name ] ) { return false ; } delete _Exporter . _ExtensionFactories [ name ] ; const index = _Exporter . _ExtensionNames . indexOf ( name ) ; if ( index !== - 1 ) { _Exporter . _ExtensionNames . splice ( index , 1 ) ; } return true ; } private reorderIndicesBasedOnPrimitiveMode ( submesh , primitiveMode , babylonIndices , byteOffset , binaryWriter ) { switch ( primitiveMode ) { case Material . TriangleFillMode : { if ( ! byteOffset ) { byteOffset = 0 ; } for ( let i = submesh . indexStart , length = submesh . indexStart + submesh . indexCount ; i < length ; i = i + 3 ) { const index = byteOffset + i * 4 ; const secondIndex = binaryWriter . getUInt32 ( index + 4 ) ; const thirdIndex = binaryWriter . getUInt32 ( index + 8 ) ; binaryWriter . setUInt32 ( thirdIndex , index + 4 ) ; binaryWriter . setUInt32 ( secondIndex , index + 8 ) ; } break ; } case Material . TriangleFanDrawMode : { for ( let i = submesh . indexStart + submesh . indexCount - 1 , start = submesh . indexStart ; i >= start ; -- i ) { binaryWriter . setUInt32 ( babylonIndices [ i ] , byteOffset ) ; byteOffset += 4 ; } break ; } case Material . TriangleStripDrawMode : { if ( submesh . indexCount >= 3 ) { binaryWriter . setUInt32 ( babylonIndices [ submesh . indexStart + 2 ] , byteOffset + 4 ) ; binaryWriter . setUInt32 ( babylonIndices [ submesh . indexStart + 1 ] , byteOffset + 8 ) ; } break ; } } } param submesh param primitiveMode param sideOrientation param vertexBufferKind param meshAttributeArray param byteOffset param binaryWriter param convertToRightHandedSystem private reorderVertexAttributeDataBasedOnPrimitiveMode ( submesh , primitiveMode , sideOrientation , vertexBufferKind , meshAttributeArray , byteOffset , binaryWriter , convertToRightHandedSystem ) { if ( convertToRightHandedSystem && sideOrientation === Material . ClockWiseSideOrientation ) { switch ( primitiveMode ) { case Material . TriangleFillMode : { this . reorderTriangleFillMode ( submesh , primitiveMode , sideOrientation , vertexBufferKind , meshAttributeArray , byteOffset , binaryWriter , convertToRightHandedSystem ) ; break ; } case Material . TriangleStripDrawMode : { this . reorderTriangleStripDrawMode ( submesh , primitiveMode , sideOrientation , vertexBufferKind , meshAttributeArray , byteOffset , binaryWriter , convertToRightHandedSystem ) ; break ; } case Material . TriangleFanDrawMode : { this . reorderTriangleFanMode ( submesh , primitiveMode , sideOrientation , vertexBufferKind , meshAttributeArray , byteOffset , binaryWriter , convertToRightHandedSystem ) ; break ; } } } } param submesh param primitiveMode param sideOrientation param vertexBufferKind param meshAttributeArray param byteOffset param binaryWriter param convertToRightHandedSystem private reorderTriangleFillMode ( submesh , primitiveMode , sideOrientation , vertexBufferKind , meshAttributeArray , byteOffset , binaryWriter , convertToRightHandedSystem ) { const vertexBuffer = this . getVertexBufferFromMesh ( vertexBufferKind , submesh . getMesh ( ) as Mesh ) ; if ( vertexBuffer ) { let stride = vertexBuffer . byteStride / VertexBuffer . GetTypeByteLength ( vertexBuffer . type ) ; if ( submesh . verticesCount % 3 !== 0 ) { Tools . Error ( 'The submesh vertices for the triangle fill mode is not divisible by 3!' ) ; } else { let vertexData = [ ] ; let index = 0 ; switch ( vertexBufferKind ) { case VertexBuffer . PositionKind : case VertexBuffer . NormalKind : { for ( let x = submesh . verticesStart ; x < submesh . verticesStart + submesh . verticesCount ; x = x + 3 ) { index = x * stride ; ( vertexData as Vector3 [ ] ) . push ( Vector3 . FromArray ( meshAttributeArray , index ) ) ; ( vertexData as Vector3 [ ] ) . push ( Vector3 . FromArray ( meshAttributeArray , index + 2 * stride ) ) ; ( vertexData as Vector3 [ ] ) . push ( Vector3 . FromArray ( meshAttributeArray , index + stride ) ) ; } break ; } case VertexBuffer . TangentKind : { for ( let x = submesh . verticesStart ; x < submesh . verticesStart + submesh . verticesCount ; x = x + 3 ) { index = x * stride ; ( vertexData as Vector4 [ ] ) . push ( Vector4 . FromArray ( meshAttributeArray , index ) ) ; ( vertexData as Vector4 [ ] ) . push ( Vector4 . FromArray ( meshAttributeArray , index + 2 * stride ) ) ; ( vertexData as Vector4 [ ] ) . push ( Vector4 . FromArray ( meshAttributeArray , index + stride ) ) ; } break ; } case VertexBuffer . ColorKind : { const size = vertexBuffer . getSize ( ) ; for ( let x = submesh . verticesStart ; x < submesh . verticesStart + submesh . verticesCount ; x = x + size ) { index = x * stride ; if ( size === 4 ) { ( vertexData as Vector4 [ ] ) . push ( Vector4 . FromArray ( meshAttributeArray , index ) ) ; ( vertexData as Vector4 [ ] ) . push ( Vector4 . FromArray ( meshAttributeArray , index + 2 * stride ) ) ; ( vertexData as Vector4 [ ] ) . push ( Vector4 . FromArray ( meshAttributeArray , index + stride ) ) ; } else { ( vertexData as Vector3 [ ] ) . push ( Vector3 . FromArray ( meshAttributeArray , index ) ) ; ( vertexData as Vector3 [ ] ) . push ( Vector3 . FromArray ( meshAttributeArray , index + 2 * stride ) ) ; ( vertexData as Vector3 [ ] ) . push ( Vector3 . FromArray ( meshAttributeArray , index + stride ) ) ; } } break ; } case VertexBuffer . UVKind : case VertexBuffer . UV2Kind : { for ( let x = submesh . verticesStart ; x < submesh . verticesStart + submesh . verticesCount ; x = x + 3 ) { index = x * stride ; ( vertexData as Vector2 [ ] ) . push ( Vector2 . FromArray ( meshAttributeArray , index ) ) ; ( vertexData as Vector2 [ ] ) . push ( Vector2 . FromArray ( meshAttributeArray , index + 2 * stride ) ) ; ( vertexData as Vector2 [ ] ) . push ( Vector2 . FromArray ( meshAttributeArray , index + stride ) ) ; } break ; } default : { Tools . Error ( `Unsupported Vertex Buffer type: ${ vertexBufferKind }` ) ; } } this . writeVertexAttributeData ( vertexData , byteOffset , vertexBufferKind , meshAttributeArray , binaryWriter , convertToRightHandedSystem ) ; } } else { Tools . Warn ( `reorderTriangleFillMode: Vertex Buffer Kind ${ vertexBufferKind } not present!` ) ; } } param submesh param primitiveMode param sideOrientation param vertexBufferKind param meshAttributeArray param byteOffset param binaryWriter param convertToRightHandedSystem private reorderTriangleStripDrawMode ( submesh , primitiveMode , sideOrientation , vertexBufferKind , meshAttributeArray , byteOffset , binaryWriter , convertToRightHandedSystem ) { const vertexBuffer = this . getVertexBufferFromMesh ( vertexBufferKind , submesh . getMesh ( ) as Mesh ) ; if ( vertexBuffer ) { const stride = vertexBuffer . byteStride / VertexBuffer . GetTypeByteLength ( vertexBuffer . type ) ; let vertexData = [ ] ; let index = 0 ; switch ( vertexBufferKind ) { case VertexBuffer . PositionKind : case VertexBuffer . NormalKind : { index = submesh . verticesStart ; ( vertexData as Vector3 [ ] ) . push ( Vector3 . FromArray ( meshAttributeArray , index + 2 * stride ) ) ; ( vertexData as Vector3 [ ] ) . push ( Vector3 . FromArray ( meshAttributeArray , index + stride ) ) ; break ; } case VertexBuffer . TangentKind : { for ( let x = submesh . verticesStart + submesh . verticesCount - 1 ; x >= submesh . verticesStart ; -- x ) { index = x * stride ; ( vertexData as Vector4 [ ] ) . push ( Vector4 . FromArray ( meshAttributeArray , index ) ) ; } break ; } case VertexBuffer . ColorKind : { for ( let x = submesh . verticesStart + submesh . verticesCount - 1 ; x >= submesh . verticesStart ; -- x ) { index = x * stride ; vertexBuffer . getSize ( ) === 4 ? ( vertexData as Vector4 [ ] ) . push ( Vector4 . FromArray ( meshAttributeArray , index ) ) : ( vertexData as Vector3 [ ] ) . push ( Vector3 . FromArray ( meshAttributeArray , index ) ) ; } break ; } case VertexBuffer . UVKind : case VertexBuffer . UV2Kind : { for ( let x = submesh . verticesStart + submesh . verticesCount - 1 ; x >= submesh . verticesStart ; -- x ) { index = x * stride ; ( vertexData as Vector2 [ ] ) . push ( Vector2 . FromArray ( meshAttributeArray , index ) ) ; } break ; } default : { Tools . Error ( `Unsupported Vertex Buffer type: ${ vertexBufferKind }` ) ; } } this . writeVertexAttributeData ( vertexData , byteOffset + 12 , vertexBufferKind , meshAttributeArray , binaryWriter , convertToRightHandedSystem ) ; } else { Tools . Warn ( `reorderTriangleStripDrawMode: Vertex buffer kind ${ vertexBufferKind } not present!` ) ; } } param submesh param primitiveMode param sideOrientation param vertexBufferKind param meshAttributeArray param byteOffset param binaryWriter param convertToRightHandedSystem private reorderTriangleFanMode ( submesh , primitiveMode , sideOrientation , vertexBufferKind , meshAttributeArray , byteOffset , binaryWriter , convertToRightHandedSystem ) { const vertexBuffer = this . getVertexBufferFromMesh ( vertexBufferKind , submesh . getMesh ( ) as Mesh ) ; if ( vertexBuffer ) { let stride = vertexBuffer . byteStride / VertexBuffer . GetTypeByteLength ( vertexBuffer . type ) ; let vertexData = [ ] ; let index = 0 ; switch ( vertexBufferKind ) { case VertexBuffer . PositionKind : case VertexBuffer . NormalKind : { for ( let x = submesh . verticesStart + submesh . verticesCount - 1 ; x >= submesh . verticesStart ; -- x ) { index = x * stride ; ( vertexData as Vector3 [ ] ) . push ( Vector3 . FromArray ( meshAttributeArray , index ) ) ; } break ; } case VertexBuffer . TangentKind : { for ( let x = submesh . verticesStart + submesh . verticesCount - 1 ; x >= submesh . verticesStart ; -- x ) { index = x * stride ; ( vertexData as Vector4 [ ] ) . push ( Vector4 . FromArray ( meshAttributeArray , index ) ) ; } break ; } case VertexBuffer . ColorKind : { for ( let x = submesh . verticesStart + submesh . verticesCount - 1 ; x >= submesh . verticesStart ; -- x ) { index = x * stride ; ( vertexData as Vector4 [ ] ) . push ( Vector4 . FromArray ( meshAttributeArray , index ) ) ; vertexBuffer . getSize ( ) === 4 ? ( vertexData as Vector4 [ ] ) . push ( Vector4 . FromArray ( meshAttributeArray , index ) ) : ( vertexData as Vector3 [ ] ) . push ( Vector3 . FromArray ( meshAttributeArray , index ) ) ; } break ; } case VertexBuffer . UVKind : case VertexBuffer . UV2Kind : { for ( let x = submesh . verticesStart + submesh . verticesCount - 1 ; x >= submesh . verticesStart ; -- x ) { index = x * stride ; ( vertexData as Vector2 [ ] ) . push ( Vector2 . FromArray ( meshAttributeArray , index ) ) ; } break ; } default : { Tools . Error ( `Unsupported Vertex Buffer type: ${ vertexBufferKind }` ) ; } } this . writeVertexAttributeData ( vertexData , byteOffset , vertexBufferKind , meshAttributeArray , binaryWriter , convertToRightHandedSystem ) ; } else { Tools . Warn ( `reorderTriangleFanMode: Vertex buffer kind ${ vertexBufferKind } not present!` ) ; } } param vertices param byteOffset param vertexAttributeKind param meshAttributeArray param binaryWriter param convertToRightHandedSystem private writeVertexAttributeData ( vertices , byteOffset , vertexAttributeKind , meshAttributeArray , binaryWriter , convertToRightHandedSystem ) { for ( let vertex of vertices ) { if ( convertToRightHandedSystem && ! ( vertexAttributeKind === VertexBuffer . ColorKind ) && ! ( vertex instanceof Vector2 ) ) { if ( vertex instanceof Vector3 ) { if ( vertexAttributeKind === VertexBuffer . NormalKind ) { _GLTFUtilities . _GetRightHandedNormalVector3FromRef ( vertex ) ; } else if ( vertexAttributeKind === VertexBuffer . PositionKind ) { _GLTFUtilities . _GetRightHandedPositionVector3FromRef ( vertex ) ; } else { Tools . Error ( 'Unsupported vertex attribute kind!' ) ; } } else { _GLTFUtilities . _GetRightHandedVector4FromRef ( vertex ) ; } } if ( vertexAttributeKind === VertexBuffer . NormalKind ) { vertex . normalize ( ) ; } else if ( vertexAttributeKind === VertexBuffer . TangentKind && vertex instanceof Vector4 ) { _GLTFUtilities . _NormalizeTangentFromRef ( vertex ) ; } for ( let component of vertex . asArray ( ) ) { binaryWriter . setFloat32 ( component , byteOffset ) ; byteOffset += 4 ; } } } param vertexBufferKind param meshAttributeArray param byteStride param binaryWriter param convertToRightHandedSystem public writeAttributeData ( vertexBufferKind , attributeComponentKind , meshAttributeArray , stride , binaryWriter , convertToRightHandedSystem , babylonTransformNode ) { let vertexAttributes = [ ] ; let index ; switch ( vertexBufferKind ) { case VertexBuffer . PositionKind : { for ( let k = 0 , length = meshAttributeArray . length / stride ; k < length ; ++ k ) { index = k * stride ; const vertexData = Vector3 . FromArray ( meshAttributeArray , index ) ; if ( convertToRightHandedSystem ) { _GLTFUtilities . _GetRightHandedPositionVector3FromRef ( vertexData ) ; } vertexAttributes . push ( vertexData . asArray ( ) ) ; } break ; } case VertexBuffer . NormalKind : { for ( let k = 0 , length = meshAttributeArray . length / stride ; k < length ; ++ k ) { index = k * stride ; const vertexData = Vector3 . FromArray ( meshAttributeArray , index ) ; if ( convertToRightHandedSystem ) { _GLTFUtilities . _GetRightHandedNormalVector3FromRef ( vertexData ) ; } vertexData . normalize ( ) ; vertexAttributes . push ( vertexData . asArray ( ) ) ; } break ; } case VertexBuffer . TangentKind : { for ( let k = 0 , length = meshAttributeArray . length / stride ; k < length ; ++ k ) { index = k * stride ; const vertexData = Vector4 . FromArray ( meshAttributeArray , index ) ; if ( convertToRightHandedSystem ) { _GLTFUtilities . _GetRightHandedVector4FromRef ( vertexData ) ; } _GLTFUtilities . _NormalizeTangentFromRef ( vertexData ) ; vertexAttributes . push ( vertexData . asArray ( ) ) ; } break ; } case VertexBuffer . ColorKind : { const meshMaterial = ( babylonTransformNode as Mesh ) . material ; const convertToLinear = meshMaterial ? ( meshMaterial . getClassName ( ) === "StandardMaterial" ) : true ; const vertexData = stride === 3 ? new Color3 ( ) : new Color4 ( ) ; for ( let k = 0 , length = meshAttributeArray . length / stride ; k < length ; ++ k ) { index = k * stride ; if ( stride === 3 ) { Color3 . FromArrayToRef ( meshAttributeArray , index , ( vertexData as Color3 ) ) ; if ( convertToLinear ) { ( vertexData as Color3 ) . toLinearSpaceToRef ( ( vertexData as Color3 ) ) ; } } else { Color4 . FromArrayToRef ( meshAttributeArray , index , ( vertexData as Color4 ) ) ; if ( convertToLinear ) { ( vertexData as Color4 ) . toLinearSpaceToRef ( ( vertexData as Color4 ) ) ; } } vertexAttributes . push ( vertexData . asArray ( ) ) ; } break ; } case VertexBuffer . UVKind : case VertexBuffer . UV2Kind : { for ( let k = 0 , length = meshAttributeArray . length / stride ; k < length ; ++ k ) { index = k * stride ; vertexAttributes . push ( convertToRightHandedSystem ? [ meshAttributeArray [ index ] , meshAttributeArray [ index + 1 ] ] : [ meshAttributeArray [ index ] , meshAttributeArray [ index + 1 ] ] ) ; } break ; } case VertexBuffer . MatricesIndicesKind : case VertexBuffer . MatricesIndicesExtraKind : { for ( let k = 0 , length = meshAttributeArray . length / stride ; k < length ; ++ k ) { index = k * stride ; const vertexData = Vector4 . FromArray ( meshAttributeArray , index ) ; vertexAttributes . push ( vertexData . asArray ( ) ) ; } break ; } case VertexBuffer . MatricesWeightsKind : case VertexBuffer . MatricesWeightsExtraKind : { for ( let k = 0 , length = meshAttributeArray . length / stride ; k < length ; ++ k ) { index = k * stride ; const vertexData = Vector4 . FromArray ( meshAttributeArray , index ) ; vertexAttributes . push ( vertexData . asArray ( ) ) ; } break ; } default : { Tools . Warn ( "Unsupported Vertex Buffer Type: " + vertexBufferKind ) ; vertexAttributes = [ ] ; } } let writeBinaryFunc ; switch ( attributeComponentKind ) { case AccessorComponentType . UNSIGNED_BYTE : { writeBinaryFunc = binaryWriter . setUInt8 . bind ( binaryWriter ) ; break ; } case AccessorComponentType . UNSIGNED_SHORT : { writeBinaryFunc = binaryWriter . setUInt16 . bind ( binaryWriter ) ; break ; } case AccessorComponentType . UNSIGNED_INT : { writeBinaryFunc = binaryWriter . setUInt32 . bind ( binaryWriter ) ; } case AccessorComponentType . FLOAT : { writeBinaryFunc = binaryWriter . setFloat32 . bind ( binaryWriter ) ; break ; } default : { Tools . Warn ( "Unsupported Attribute Component kind: " + attributeComponentKind ) ; return ; } } for ( let vertexAttribute of vertexAttributes ) { for ( let component of vertexAttribute ) { writeBinaryFunc ( component ) ; } } } param vertexBufferKind param meshAttributeArray param byteStride param binaryWriter param convertToRightHandedSystem public writeMorphTargetAttributeData ( vertexBufferKind , attributeComponentKind , meshPrimitive , morphTarget , meshAttributeArray , morphTargetAttributeArray , stride , binaryWriter , convertToRightHandedSystem , minMax ? ) { let vertexAttributes = [ ] ; let index ; let difference = new Vector3 ( ) ; let difference4 = new Vector4 ( 0 , 0 , 0 , 0 ) ; switch ( vertexBufferKind ) { case VertexBuffer . PositionKind : { for ( let k = meshPrimitive . verticesStart ; k < meshPrimitive . verticesCount ; ++ k ) { index = meshPrimitive . indexStart + k * stride ; const vertexData = Vector3 . FromArray ( meshAttributeArray , index ) ; const morphData = Vector3 . FromArray ( morphTargetAttributeArray , index ) ; difference = morphData . subtractToRef ( vertexData , difference ) ; if ( convertToRightHandedSystem ) { _GLTFUtilities . _GetRightHandedPositionVector3FromRef ( difference ) ; } if ( minMax ) { minMax . min . copyFromFloats ( Math . min ( difference . x , minMax . min . x ) , Math . min ( difference . y , minMax . min . y ) , Math . min ( difference . z , minMax . min . z ) ) ; minMax . max . copyFromFloats ( Math . max ( difference . x , minMax . max . x ) , Math . max ( difference . y , minMax . max . y ) , Math . max ( difference . z , minMax . max . z ) ) ; } vertexAttributes . push ( difference . asArray ( ) ) ; } break ; } case VertexBuffer . NormalKind : { for ( let k = meshPrimitive . verticesStart ; k < meshPrimitive . verticesCount ; ++ k ) { index = meshPrimitive . indexStart + k * stride ; const vertexData = Vector3 . FromArray ( meshAttributeArray , index ) ; vertexData . normalize ( ) ; const morphData = Vector3 . FromArray ( morphTargetAttributeArray , index ) ; morphData . normalize ( ) ; difference = morphData . subtractToRef ( vertexData , difference ) ; if ( convertToRightHandedSystem ) { _GLTFUtilities . _GetRightHandedNormalVector3FromRef ( difference ) ; } vertexAttributes . push ( difference . asArray ( ) ) ; } break ; } case VertexBuffer . TangentKind : { for ( let k = meshPrimitive . verticesStart ; k < meshPrimitive . verticesCount ; ++ k ) { index = meshPrimitive . indexStart + k * ( stride + 1 ) ; const vertexData = Vector4 . FromArray ( meshAttributeArray , index ) ; _GLTFUtilities . _NormalizeTangentFromRef ( vertexData ) ; const morphData = Vector4 . FromArray ( morphTargetAttributeArray , index ) ; _GLTFUtilities . _NormalizeTangentFromRef ( morphData ) ; difference4 = morphData . subtractToRef ( vertexData , difference4 ) ; if ( convertToRightHandedSystem ) { _GLTFUtilities . _GetRightHandedVector4FromRef ( difference4 ) ; } vertexAttributes . push ( [ difference4 . x , difference4 . y , difference4 . z ] ) ; } break ; } default : { Tools . Warn ( "Unsupported Vertex Buffer Type: " + vertexBufferKind ) ; vertexAttributes = [ ] ; } } let writeBinaryFunc ; switch ( attributeComponentKind ) { case AccessorComponentType . UNSIGNED_BYTE : { writeBinaryFunc = binaryWriter . setUInt8 . bind ( binaryWriter ) ; break ; } case AccessorComponentType . UNSIGNED_SHORT : { writeBinaryFunc = binaryWriter . setUInt16 . bind ( binaryWriter ) ; break ; } case AccessorComponentType . UNSIGNED_INT : { writeBinaryFunc = binaryWriter . setUInt32 . bind ( binaryWriter ) ; } case AccessorComponentType . FLOAT : { writeBinaryFunc = binaryWriter . setFloat32 . bind ( binaryWriter ) ; break ; } default : { Tools . Warn ( "Unsupported Attribute Component kind: " + attributeComponentKind ) ; return ; } } for ( let vertexAttribute of vertexAttributes ) { for ( let component of vertexAttribute ) { writeBinaryFunc ( component ) ; } } } param shouldUseGlb param glTFPrefix param prettyPrint returns private generateJSON ( shouldUseGlb , glTFPrefix ? , prettyPrint ? ) { let buffer = { byteLength : this . _totalByteLength } ; let imageName ; let imageData ; let bufferView ; let byteOffset = this . _totalByteLength ; if ( buffer . byteLength ) { this . _glTF . buffers = [ buffer ] ; } if ( this . _nodes && this . _nodes . length ) { this . _glTF . nodes = this . _nodes ; } if ( this . _meshes && this . _meshes . length ) { this . _glTF . meshes = this . _meshes ; } if ( this . _scenes && this . _scenes . length ) { this . _glTF . scenes = this . _scenes ; this . _glTF . scene = 0 ; } if ( this . _bufferViews && this . _bufferViews . length ) { this . _glTF . bufferViews = this . _bufferViews ; } if ( this . _accessors && this . _accessors . length ) { this . _glTF . accessors = this . _accessors ; } if ( this . _animations && this . _animations . length ) { this . _glTF . animations = this . _animations ; } if ( this . _materials && this . _materials . length ) { this . _glTF . materials = this . _materials ; } if ( this . _textures && this . _textures . length ) { this . _glTF . textures = this . _textures ; } if ( this . _samplers && this . _samplers . length ) { this . _glTF . samplers = this . _samplers ; } if ( this . _skins && this . _skins . length ) { this . _glTF . skins = this . _skins ; } if ( this . _images && this . _images . length ) { if ( ! shouldUseGlb ) { this . _glTF . images = this . _images ; } else { this . _glTF . images = [ ] ; this . _images . forEach ( ( image ) => { if ( image . uri ) { imageData = this . _imageData [ image . uri ] ; this . _orderedImageData . push ( imageData ) ; imageName = image . uri . split ( '.' ) [ 0 ] + " image" ; bufferView = _GLTFUtilities . _CreateBufferView ( 0 , byteOffset , imageData . data . length , undefined , imageName ) ; byteOffset += imageData . data . buffer . byteLength ; this . _bufferViews . push ( bufferView ) ; image . bufferView = this . _bufferViews . length - 1 ; image . name = imageName ; image . mimeType = imageData . mimeType ; image . uri = undefined ; if ( ! this . _glTF . images ) { this . _glTF . images = [ ] ; } this . _glTF . images . push ( image ) ; } } ) ; buffer . byteLength = byteOffset ; } } if ( ! shouldUseGlb ) { buffer . uri = glTFPrefix + ".bin" ; } const jsonText = prettyPrint ? JSON . stringify ( this . _glTF , null , 2 ) : JSON . stringify ( this . _glTF ) ; return jsonText ; } param glTFPrefix param dispose returns public _generateGLTFAsync ( glTFPrefix , dispose = true ) { return this . _generateBinaryAsync ( ) . then ( ( binaryBuffer ) => { this . _extensionsOnExporting ( ) ; const jsonText = this . generateJSON ( false , glTFPrefix , true ) ; const bin = new Blob ( [ binaryBuffer ] , { type : 'application/octet-stream' } ) ; const glTFFileName = glTFPrefix + '.gltf' ; const glTFBinFile = glTFPrefix + '.bin' ; const container = new GLTFData ( ) ; container . glTFFiles [ glTFFileName ] = jsonText ; container . glTFFiles [ glTFBinFile ] = bin ; if ( this . _imageData ) { for ( let image in this . _imageData ) { container . glTFFiles [ image ] = new Blob ( [ this . _imageData [ image ] . data ] , { type : this . _imageData [ image ] . mimeType } ) ; } } if ( dispose ) { this . dispose ( ) ; } return container ; } ) ; } returns private _generateBinaryAsync ( ) { let binaryWriter = new _BinaryWriter ( 4 ) ; return this . createSceneAsync ( this . _babylonScene , binaryWriter ) . then ( ( ) => { if ( this . _localEngine ) { this . _localEngine . dispose ( ) ; } return binaryWriter . getArrayBuffer ( ) ; } ) ; } param num returns private _getPadding ( num ) { let remainder = num % 4 ; let padding = remainder === 0 ? remainder : 4 - remainder ; return padding ; } hidden public _generateGLBAsync ( glTFPrefix , dispose = true ) { return this . _generateBinaryAsync ( ) . then ( ( binaryBuffer ) => { this . _extensionsOnExporting ( ) ; const jsonText = this . generateJSON ( true ) ; const glbFileName = glTFPrefix + '.glb' ; const headerLength = 12 ; const chunkLengthPrefix = 8 ; let jsonLength = jsonText . length ; let encodedJsonText ; let imageByteLength = 0 ; if ( typeof TextEncoder !== "undefined" ) { const encoder = new TextEncoder ( ) ; encodedJsonText = encoder . encode ( jsonText ) ; jsonLength = encodedJsonText . length ; } for ( let i = 0 ; i < this . _orderedImageData . length ; ++ i ) { imageByteLength += this . _orderedImageData [ i ] . data . byteLength ; } const jsonPadding = this . _getPadding ( jsonLength ) ; const binPadding = this . _getPadding ( binaryBuffer . byteLength ) ; const imagePadding = this . _getPadding ( imageByteLength ) ; const byteLength = headerLength + ( 2 * chunkLengthPrefix ) + jsonLength + jsonPadding + binaryBuffer . byteLength + binPadding + imageByteLength + imagePadding ; const headerBuffer = new ArrayBuffer ( headerLength ) ; const headerBufferView = new DataView ( headerBuffer ) ; headerBufferView . setUint32 ( 0 , 0x46546C67 , true ) ; headerBufferView . setUint32 ( 4 , 2 , true ) ; headerBufferView . setUint32 ( 8 , byteLength , true ) ; const jsonChunkBuffer = new ArrayBuffer ( chunkLengthPrefix + jsonLength + jsonPadding ) ; const jsonChunkBufferView = new DataView ( jsonChunkBuffer ) ; jsonChunkBufferView . setUint32 ( 0 , jsonLength + jsonPadding , true ) ; jsonChunkBufferView . setUint32 ( 4 , 0x4E4F534A , true ) ; const jsonData = new Uint8Array ( jsonChunkBuffer , chunkLengthPrefix ) ; if ( encodedJsonText ) { jsonData . set ( encodedJsonText ) ; } else { const blankCharCode = "_" . charCodeAt ( 0 ) ; for ( let i = 0 ; i < jsonLength ; ++ i ) { const charCode = jsonText . charCodeAt ( i ) ; if ( charCode != jsonText . codePointAt ( i ) ) { jsonData [ i ] = blankCharCode ; } else { jsonData [ i ] = charCode ; } } } const jsonPaddingView = new Uint8Array ( jsonChunkBuffer , chunkLengthPrefix + jsonLength ) ; for ( let i = 0 ; i < jsonPadding ; ++ i ) { jsonPaddingView [ i ] = 0x20 ; } const binaryChunkBuffer = new ArrayBuffer ( chunkLengthPrefix ) ; const binaryChunkBufferView = new DataView ( binaryChunkBuffer ) ; binaryChunkBufferView . setUint32 ( 0 , binaryBuffer . byteLength + imageByteLength + imagePadding , true ) ; binaryChunkBufferView . setUint32 ( 4 , 0x004E4942 , true ) ; const binPaddingBuffer = new ArrayBuffer ( binPadding ) ; const binPaddingView = new Uint8Array ( binPaddingBuffer ) ; for ( let i = 0 ; i < binPadding ; ++ i ) { binPaddingView [ i ] = 0 ; } const imagePaddingBuffer = new ArrayBuffer ( imagePadding ) ; const imagePaddingView = new Uint8Array ( imagePaddingBuffer ) ; for ( let i = 0 ; i < imagePadding ; ++ i ) { imagePaddingView [ i ] = 0 ; } const glbData = [ headerBuffer , jsonChunkBuffer , binaryChunkBuffer , binaryBuffer ] ; for ( let i = 0 ; i < this . _orderedImageData . length ; ++ i ) { glbData . push ( this . _orderedImageData [ i ] . data . buffer ) ; } glbData . push ( binPaddingBuffer ) ; glbData . push ( imagePaddingBuffer ) ; const glbFile = new Blob ( glbData , { type : 'application/octet-stream' } ) ; const container = new GLTFData ( ) ; container . glTFFiles [ glbFileName ] = glbFile ; if ( this . _localEngine != null ) { this . _localEngine . dispose ( ) ; } if ( dispose ) { this . dispose ( ) ; } return container ; } ) ; } param node param babylonTransformNode param convertToRightHandedSystem private setNodeTransformation ( node , babylonTransformNode , convertToRightHandedSystem ) { if ( ! babylonTransformNode . getPivotPoint ( ) . equalsToFloats ( 0 , 0 , 0 ) ) { Tools . Warn ( "Pivot points are not supported in the glTF serializer" ) ; } if ( ! babylonTransformNode . position . equalsToFloats ( 0 , 0 , 0 ) ) { node . translation = convertToRightHandedSystem ? _GLTFUtilities . _GetRightHandedPositionVector3 ( babylonTransformNode . position ) . asArray ( ) : babylonTransformNode . position . asArray ( ) ; } if ( ! babylonTransformNode . scaling . equalsToFloats ( 1 , 1 , 1 ) ) { node . scale = babylonTransformNode . scaling . asArray ( ) ; } let rotationQuaternion = Quaternion . RotationYawPitchRoll ( babylonTransformNode . rotation . y , babylonTransformNode . rotation . x , babylonTransformNode . rotation . z ) ; if ( babylonTransformNode . rotationQuaternion ) { rotationQuaternion . multiplyInPlace ( babylonTransformNode . rotationQuaternion ) ; } if ( ! ( rotationQuaternion . x === 0 && rotationQuaternion . y === 0 && rotationQuaternion . z === 0 && rotationQuaternion . w === 1 ) ) { if ( convertToRightHandedSystem ) { _GLTFUtilities . _GetRightHandedQuaternionFromRef ( rotationQuaternion ) ; } node . rotation = rotationQuaternion . normalize ( ) . asArray ( ) ; } } private getVertexBufferFromMesh ( attributeKind , bufferMesh ) { if ( bufferMesh . isVerticesDataPresent ( attributeKind ) ) { const vertexBuffer = bufferMesh . getVertexBuffer ( attributeKind ) ; if ( vertexBuffer ) { return vertexBuffer ; } } return null ; } param kind param componentType param babylonTransformNode param binaryWriter param convertToRightHandedSystem private createBufferViewKind ( kind , attributeComponentKind , babylonTransformNode , binaryWriter , byteStride , convertToRightHandedSystem ) { const bufferMesh = babylonTransformNode instanceof Mesh ? babylonTransformNode as Mesh : babylonTransformNode instanceof InstancedMesh ? ( babylonTransformNode as InstancedMesh ) . sourceMesh : null ; if ( bufferMesh ) { const vertexBuffer = bufferMesh . getVertexBuffer ( kind ) ; const vertexData = bufferMesh . getVerticesData ( kind ) ; if ( vertexBuffer && vertexData ) { const typeByteLength = VertexBuffer . GetTypeByteLength ( attributeComponentKind ) ; const byteLength = vertexData . length * typeByteLength ; const bufferView = _GLTFUtilities . _CreateBufferView ( 0 , binaryWriter . getByteOffset ( ) , byteLength , byteStride , kind + " - " + bufferMesh . name ) ; this . _bufferViews . push ( bufferView ) ; this . writeAttributeData ( kind , attributeComponentKind , vertexData , byteStride / typeByteLength , binaryWriter , convertToRightHandedSystem , babylonTransformNode ) ; } } } param babylonSubMesh param babylonMorphTarget param binaryWriter param convertToRightHandedSystem private setMorphTargetAttributes ( babylonSubMesh , meshPrimitive , babylonMorphTarget , binaryWriter , convertToRightHandedSystem ) { if ( babylonMorphTarget ) { if ( ! meshPrimitive . targets ) { meshPrimitive . targets = [ ] ; } let target = { } ; if ( babylonMorphTarget . hasNormals ) { const vertexNormals = babylonSubMesh . getMesh ( ) . getVerticesData ( VertexBuffer . NormalKind ) ! ; const morphNormals = babylonMorphTarget . getNormals ( ) ! ; const count = babylonSubMesh . verticesCount ; const byteStride = 12 ; const byteLength = count * byteStride ; const bufferView = _GLTFUtilities . _CreateBufferView ( 0 , binaryWriter . getByteOffset ( ) , byteLength , byteStride , babylonMorphTarget . name + "_NORMAL" ) ; this . _bufferViews . push ( bufferView ) ; let bufferViewIndex = this . _bufferViews . length - 1 ; const accessor = _GLTFUtilities . _CreateAccessor ( bufferViewIndex , babylonMorphTarget . name + " - " + "NORMAL" , AccessorType . VEC3 , AccessorComponentType . FLOAT , count , 0 , null , null ) ; this . _accessors . push ( accessor ) ; target . NORMAL = this . _accessors . length - 1 ; this . writeMorphTargetAttributeData ( VertexBuffer . NormalKind , AccessorComponentType . FLOAT , babylonSubMesh , babylonMorphTarget , vertexNormals , morphNormals , byteStride / 4 , binaryWriter , convertToRightHandedSystem ) ; } if ( babylonMorphTarget . hasPositions ) { const vertexPositions = babylonSubMesh . getMesh ( ) . getVerticesData ( VertexBuffer . PositionKind ) ! ; const morphPositions = babylonMorphTarget . getPositions ( ) ! ; const count = babylonSubMesh . verticesCount ; const byteStride = 12 ; const byteLength = count * byteStride ; const bufferView = _GLTFUtilities . _CreateBufferView ( 0 , binaryWriter . getByteOffset ( ) , byteLength , byteStride , babylonMorphTarget . name + "_POSITION" ) ; this . _bufferViews . push ( bufferView ) ; let bufferViewIndex = this . _bufferViews . length - 1 ; let minMax = { min : new Vector3 ( Infinity , Infinity , Infinity ) , max : new Vector3 ( - Infinity , - Infinity , - Infinity ) } ; const accessor = _GLTFUtilities . _CreateAccessor ( bufferViewIndex , babylonMorphTarget . name + " - " + "POSITION" , AccessorType . VEC3 , AccessorComponentType . FLOAT , count , 0 , null , null ) ; this . _accessors . push ( accessor ) ; target . POSITION = this . _accessors . length - 1 ; this . writeMorphTargetAttributeData ( VertexBuffer . PositionKind , AccessorComponentType . FLOAT , babylonSubMesh , babylonMorphTarget , vertexPositions , morphPositions , byteStride / 4 , binaryWriter , convertToRightHandedSystem , minMax ) ; accessor . min = minMax . min ! . asArray ( ) ; accessor . max = minMax . max ! . asArray ( ) ; } if ( babylonMorphTarget . hasTangents ) { const vertexTangents = babylonSubMesh . getMesh ( ) . getVerticesData ( VertexBuffer . TangentKind ) ! ; const morphTangents = babylonMorphTarget . getTangents ( ) ! ; const count = babylonSubMesh . verticesCount ; const byteStride = 12 ; const byteLength = count * byteStride ; const bufferView = _GLTFUtilities . _CreateBufferView ( 0 , binaryWriter . getByteOffset ( ) , byteLength , byteStride , babylonMorphTarget . name + "_NORMAL" ) ; this . _bufferViews . push ( bufferView ) ; let bufferViewIndex = this . _bufferViews . length - 1 ; const accessor = _GLTFUtilities . _CreateAccessor ( bufferViewIndex , babylonMorphTarget . name + " - " + "TANGENT" , AccessorType . VEC3 , AccessorComponentType . FLOAT , count , 0 , null , null ) ; this . _accessors . push ( accessor ) ; target . TANGENT = this . _accessors . length - 1 ; this . writeMorphTargetAttributeData ( VertexBuffer . TangentKind , AccessorComponentType . FLOAT , babylonSubMesh , babylonMorphTarget , vertexTangents , morphTangents , byteStride / 4 , binaryWriter , convertToRightHandedSystem ) ; } meshPrimitive . targets . push ( target ) ; } } param babylonMesh private getMeshPrimitiveMode ( babylonMesh ) { if ( babylonMesh instanceof LinesMesh ) { return Material . LineListDrawMode ; } return babylonMesh . material ? babylonMesh . material . fillMode : Material . TriangleFillMode ; } param meshPrimitive param primitiveMode private setPrimitiveMode ( meshPrimitive , primitiveMode ) { switch ( primitiveMode ) { case Material . TriangleFillMode : { break ; } case Material . TriangleStripDrawMode : { meshPrimitive . mode = MeshPrimitiveMode . TRIANGLE_STRIP ; break ; } case Material . TriangleFanDrawMode : { meshPrimitive . mode = MeshPrimitiveMode . TRIANGLE_FAN ; break ; } case Material . PointListDrawMode : { meshPrimitive . mode = MeshPrimitiveMode . POINTS ; } case Material . PointFillMode : { meshPrimitive . mode = MeshPrimitiveMode . POINTS ; break ; } case Material . LineLoopDrawMode : { meshPrimitive . mode = MeshPrimitiveMode . LINE_LOOP ; break ; } case Material . LineListDrawMode : { meshPrimitive . mode = MeshPrimitiveMode . LINES ; break ; } case Material . LineStripDrawMode : { meshPrimitive . mode = MeshPrimitiveMode . LINE_STRIP ; break ; } } } param meshPrimitive param attributeKind returns private setAttributeKind ( meshPrimitive , attributeKind ) { switch ( attributeKind ) { case VertexBuffer . PositionKind : { meshPrimitive . attributes . POSITION = this . _accessors . length - 1 ; break ; } case VertexBuffer . NormalKind : { meshPrimitive . attributes . NORMAL = this . _accessors . length - 1 ; break ; } case VertexBuffer . ColorKind : { meshPrimitive . attributes . COLOR_0 = this . _accessors . length - 1 ; break ; } case VertexBuffer . TangentKind : { meshPrimitive . attributes . TANGENT = this . _accessors . length - 1 ; break ; } case VertexBuffer . UVKind : { meshPrimitive . attributes . TEXCOORD_0 = this . _accessors . length - 1 ; break ; } case VertexBuffer . UV2Kind : { meshPrimitive . attributes . TEXCOORD_1 = this . _accessors . length - 1 ; break ; } case VertexBuffer . MatricesIndicesKind : { meshPrimitive . attributes . JOINTS_0 = this . _accessors . length - 1 ; break ; } case VertexBuffer . MatricesIndicesExtraKind : { meshPrimitive . attributes . JOINTS_1 = this . _accessors . length - 1 ; break ; } case VertexBuffer . MatricesWeightsKind : { meshPrimitive . attributes . WEIGHTS_0 = this . _accessors . length - 1 ; break ; } case VertexBuffer . MatricesWeightsExtraKind : { meshPrimitive . attributes . WEIGHTS_1 = this . _accessors . length - 1 ; break ; } default : { Tools . Warn ( "Unsupported Vertex Buffer Type: " + attributeKind ) ; } } } param mesh param babylonTransformNode param binaryWriter param convertToRightHandedSystem private setPrimitiveAttributesAsync ( mesh , babylonTransformNode , binaryWriter , convertToRightHandedSystem ) { let promises = [ ] ; let bufferMesh = null ; let bufferView ; let minMax ; if ( babylonTransformNode instanceof Mesh ) { bufferMesh = ( babylonTransformNode as Mesh ) ; } else if ( babylonTransformNode instanceof InstancedMesh ) { bufferMesh = ( babylonTransformNode as InstancedMesh ) . sourceMesh ; } const attributeData = [ { kind : VertexBuffer . PositionKind , accessorType : AccessorType . VEC3 , accessorComponentType : AccessorComponentType . FLOAT , byteStride : 12 } , { kind : VertexBuffer . NormalKind , accessorType : AccessorType . VEC3 , accessorComponentType : AccessorComponentType . FLOAT , byteStride : 12 } , { kind : VertexBuffer . ColorKind , accessorType : AccessorType . VEC4 , accessorComponentType : AccessorComponentType . FLOAT , byteStride : 16 } , { kind : VertexBuffer . TangentKind , accessorType : AccessorType . VEC4 , accessorComponentType : AccessorComponentType . FLOAT , byteStride : 16 } , { kind : VertexBuffer . UVKind , accessorType : AccessorType . VEC2 , accessorComponentType : AccessorComponentType . FLOAT , byteStride : 8 } , { kind : VertexBuffer . UV2Kind , accessorType : AccessorType . VEC2 , accessorComponentType : AccessorComponentType . FLOAT , byteStride : 8 } , { kind : VertexBuffer . MatricesIndicesKind , accessorType : AccessorType . VEC4 , accessorComponentType : AccessorComponentType . UNSIGNED_SHORT , byteStride : 8 } , { kind : VertexBuffer . MatricesIndicesExtraKind , accessorType : AccessorType . VEC4 , accessorComponentType : AccessorComponentType . UNSIGNED_SHORT , byteStride : 8 } , { kind : VertexBuffer . MatricesWeightsKind , accessorType : AccessorType . VEC4 , accessorComponentType : AccessorComponentType . FLOAT , byteStride : 16 } , { kind : VertexBuffer . MatricesWeightsExtraKind , accessorType : AccessorType . VEC4 , accessorComponentType : AccessorComponentType . FLOAT , byteStride : 16 } , ] ; if ( bufferMesh ) { let indexBufferViewIndex = null ; const primitiveMode = this . getMeshPrimitiveMode ( bufferMesh ) ; let vertexAttributeBufferViews = { } ; let morphTargetManager = bufferMesh . morphTargetManager ; for ( const attribute of attributeData ) { const attributeKind = attribute . kind ; const attributeComponentKind = attribute . accessorComponentType ; if ( bufferMesh . isVerticesDataPresent ( attributeKind ) ) { const vertexBuffer = this . getVertexBufferFromMesh ( attributeKind , bufferMesh ) ; attribute . byteStride = vertexBuffer ? vertexBuffer . getSize ( ) * VertexBuffer . GetTypeByteLength ( attribute . accessorComponentType ) : VertexBuffer . DeduceStride ( attributeKind ) * 4 ; if ( attribute . byteStride === 12 ) { attribute . accessorType = AccessorType . VEC3 ; } this . createBufferViewKind ( attributeKind , attributeComponentKind , babylonTransformNode , binaryWriter , attribute . byteStride , convertToRightHandedSystem ) ; attribute . bufferViewIndex = this . _bufferViews . length - 1 ; vertexAttributeBufferViews [ attributeKind ] = attribute . bufferViewIndex ; } } if ( bufferMesh . getTotalIndices ( ) ) { const indices = bufferMesh . getIndices ( ) ; if ( indices ) { const byteLength = indices . length * 4 ; bufferView = _GLTFUtilities . _CreateBufferView ( 0 , binaryWriter . getByteOffset ( ) , byteLength , undefined , "Indices - " + bufferMesh . name ) ; this . _bufferViews . push ( bufferView ) ; indexBufferViewIndex = this . _bufferViews . length - 1 ; for ( let k = 0 , length = indices . length ; k < length ; ++ k ) { binaryWriter . setUInt32 ( indices [ k ] ) ; } } } if ( bufferMesh . subMeshes ) { for ( const submesh of bufferMesh . subMeshes ) { let babylonMaterial = submesh . getMaterial ( ) || bufferMesh . getScene ( ) . defaultMaterial ; let materialIndex = null ; if ( babylonMaterial ) { if ( bufferMesh instanceof LinesMesh ) { const material = { name : bufferMesh . name + ' material' } ; if ( ! bufferMesh . color . equals ( Color3 . White ( ) ) || bufferMesh . alpha < 1 ) { material . pbrMetallicRoughness = { baseColorFactor : bufferMesh . color . asArray ( ) . concat ( [ bufferMesh . alpha ] ) } ; } this . _materials . push ( material ) ; materialIndex = this . _materials . length - 1 ; } else if ( babylonMaterial . getClassName ( ) === "MultiMaterial" ) { const subMaterial = ( babylonMaterial as any ) . subMaterials [ submesh . materialIndex ] ; if ( subMaterial ) { babylonMaterial = subMaterial ; materialIndex = this . _materialMap [ babylonMaterial . uniqueId ] ; } } else { materialIndex = this . _materialMap [ babylonMaterial . uniqueId ] ; } } let glTFMaterial = materialIndex != null ? this . _materials [ materialIndex ] : null ; const meshPrimitive = { attributes : { } } ; this . setPrimitiveMode ( meshPrimitive , primitiveMode ) ; for ( const attribute of attributeData ) { const attributeKind = attribute . kind ; if ( ( attributeKind === VertexBuffer . UVKind || attributeKind === VertexBuffer . UV2Kind ) && ! this . _options . exportUnusedUVs ) { if ( ! glTFMaterial || ! this . _glTFMaterialExporter . _hasTexturesPresent ( glTFMaterial ) ) { continue ; } } let vertexData = bufferMesh . getVerticesData ( attributeKind ) ; if ( vertexData ) { const vertexBuffer = this . getVertexBufferFromMesh ( attributeKind , bufferMesh ) ; if ( vertexBuffer ) { const stride = vertexBuffer . getSize ( ) ; const bufferViewIndex = attribute . bufferViewIndex ; if ( bufferViewIndex != undefined ) { minMax = { min : null , max : null } ; if ( attributeKind == VertexBuffer . PositionKind ) { minMax = _GLTFUtilities . _CalculateMinMaxPositions ( vertexData , 0 , vertexData . length / stride , convertToRightHandedSystem ) ; } const accessor = _GLTFUtilities . _CreateAccessor ( bufferViewIndex , attributeKind + " - " + babylonTransformNode . name , attribute . accessorType , attribute . accessorComponentType , vertexData . length / stride , 0 , minMax . min , minMax . max ) ; this . _accessors . push ( accessor ) ; this . setAttributeKind ( meshPrimitive , attributeKind ) ; } } } } if ( indexBufferViewIndex ) { const accessor = _GLTFUtilities . _CreateAccessor ( indexBufferViewIndex , "indices - " + babylonTransformNode . name , AccessorType . SCALAR , AccessorComponentType . UNSIGNED_INT , submesh . indexCount , submesh . indexStart * 4 , null , null ) ; this . _accessors . push ( accessor ) ; meshPrimitive . indices = this . _accessors . length - 1 ; } if ( materialIndex != null && Object . keys ( meshPrimitive . attributes ) . length > 0 ) { let sideOrientation = bufferMesh . overrideMaterialSideOrientation !== null ? bufferMesh . overrideMaterialSideOrientation : babylonMaterial . sideOrientation ; if ( ( sideOrientation == Material . ClockWiseSideOrientation && this . _babylonScene . useRightHandedSystem ) || ( sideOrientation == Material . ClockWiseSideOrientation && convertToRightHandedSystem && bufferMesh . overrideMaterialSideOrientation !== bufferMesh . material ?. sideOrientation ) ) { let byteOffset = indexBufferViewIndex != null ? this . _bufferViews [ indexBufferViewIndex ] . byteOffset : null ; if ( byteOffset == null ) { byteOffset = 0 ; } let babylonIndices = null ; if ( indexBufferViewIndex != null ) { babylonIndices = bufferMesh . getIndices ( ) ; } if ( babylonIndices ) { this . reorderIndicesBasedOnPrimitiveMode ( submesh , primitiveMode , babylonIndices , byteOffset , binaryWriter ) ; } else { for ( let attribute of attributeData ) { let vertexData = bufferMesh . getVerticesData ( attribute . kind ) ; if ( vertexData ) { let byteOffset = this . _bufferViews [ vertexAttributeBufferViews [ attribute . kind ] ] . byteOffset ; if ( ! byteOffset ) { byteOffset = 0 ; } this . reorderVertexAttributeDataBasedOnPrimitiveMode ( submesh , primitiveMode , sideOrientation , attribute . kind , vertexData , byteOffset , binaryWriter , convertToRightHandedSystem ) ; } } } } meshPrimitive . material = materialIndex ; } if ( morphTargetManager ) { let target ; for ( let i = 0 ; i < morphTargetManager . numTargets ; ++ i ) { target = morphTargetManager . getTarget ( i ) ; this . setMorphTargetAttributes ( submesh , meshPrimitive , target , binaryWriter , convertToRightHandedSystem ) ; } } mesh . primitives . push ( meshPrimitive ) ; this . _extensionsPostExportMeshPrimitiveAsync ( "postExport" , meshPrimitive , submesh , binaryWriter ) ; promises . push ( ) ; } } } return Promise . all ( promises ) . then ( ( ) => { } ) ; } param node returns private isBabylonCoordinateSystemConvertingNode ( node ) { if ( node instanceof TransformNode ) { if ( node . name !== "__root__" ) { return false ; } let matrix = node . getWorldMatrix ( ) ; if ( matrix . determinant ( ) === 1 ) { return false ; } if ( ( node instanceof Mesh && node . geometry !== null ) || ( node instanceof InstancedMesh && node . sourceMesh . geometry !== null ) ) { return false ; } if ( this . _includeCoordinateSystemConversionNodes ) { return false ; } return true ; } return false ; } param babylonScene param binaryWriter private createSceneAsync ( babylonScene , binaryWriter ) { const scene = { nodes : [ ] } ; let glTFNodeIndex ; let glTFNode ; let directDescendents ; const nodes = [ ... babylonScene . transformNodes , ... babylonScene . meshes , ... babylonScene . lights ] ; let rootNodesToLeftHanded = [ ] ; this . _convertToRightHandedSystem = ! babylonScene . useRightHandedSystem ; this . _convertToRightHandedSystemMap = { } ; babylonScene . rootNodes . forEach ( ( rootNode ) => { this . _convertToRightHandedSystemMap [ rootNode . uniqueId ] = this . _convertToRightHandedSystem ; rootNode . getDescendants ( false ) . forEach ( ( descendant ) => { this . _convertToRightHandedSystemMap [ descendant . uniqueId ] = this . _convertToRightHandedSystem ; } ) ; } ) ; babylonScene . rootNodes . forEach ( ( rootNode ) => { if ( this . isBabylonCoordinateSystemConvertingNode ( rootNode ) ) { rootNodesToLeftHanded . push ( rootNode ) ; const indexRootNode = nodes . indexOf ( rootNode ) ; if ( indexRootNode !== - 1 ) { nodes . splice ( indexRootNode , 1 ) ; } rootNode . getDescendants ( false ) . forEach ( ( descendant ) => { this . _convertToRightHandedSystemMap [ descendant . uniqueId ] = false ; } ) ; } } ) ; const [ exportNodes , exportMaterials ] = this . getExportNodes ( nodes ) ; return this . _glTFMaterialExporter . _convertMaterialsToGLTFAsync ( exportMaterials , ImageMimeType . PNG , true ) . then ( ( ) => { return this . createNodeMapAndAnimationsAsync ( babylonScene , exportNodes , binaryWriter ) . then ( ( nodeMap ) => { return this . createSkinsAsync ( babylonScene , nodeMap , binaryWriter ) . then ( ( skinMap ) => { this . _nodeMap = nodeMap ; this . _totalByteLength = binaryWriter . getByteOffset ( ) ; if ( this . _totalByteLength == undefined ) { throw new Error ( "undefined byte length!" ) ; } for ( let babylonNode of nodes ) { glTFNodeIndex = this . _nodeMap [ babylonNode . uniqueId ] ; if ( glTFNodeIndex !== undefined ) { glTFNode = this . _nodes [ glTFNodeIndex ] ; if ( babylonNode . metadata ) { if ( this . _options . metadataSelector ) { glTFNode . extras = this . _options . metadataSelector ( babylonNode . metadata ) ; } else if ( babylonNode . metadata . gltf ) { glTFNode . extras = babylonNode . metadata . gltf . extras ; } } if ( ! babylonNode . parent || rootNodesToLeftHanded . indexOf ( babylonNode . parent ) !== - 1 ) { if ( this . _options . shouldExportNode && ! this . _options . shouldExportNode ( babylonNode ) ) { Tools . Log ( "Omitting " + babylonNode . name + " from scene." ) ; } else { let convertToRightHandedSystem = this . _convertToRightHandedSystemMap [ babylonNode . uniqueId ] ; if ( convertToRightHandedSystem ) { if ( glTFNode . translation ) { glTFNode . translation [ 2 ] *= - 1 ; glTFNode . translation [ 0 ] *= - 1 ; } glTFNode . rotation = glTFNode . rotation ? Quaternion . FromArray ( [ 0 , 1 , 0 , 0 ] ) . multiply ( Quaternion . FromArray ( glTFNode . rotation ) ) . asArray ( ) : ( Quaternion . FromArray ( [ 0 , 1 , 0 , 0 ] ) ) . asArray ( ) ; } scene . nodes . push ( glTFNodeIndex ) ; } } if ( babylonNode instanceof Mesh ) { let babylonMesh = babylonNode ; if ( babylonMesh . skeleton ) { glTFNode . skin = skinMap [ babylonMesh . skeleton . uniqueId ] ; } } directDescendents = babylonNode . getDescendants ( true ) ; if ( ! glTFNode . children && directDescendents && directDescendents . length ) { const children = [ ] ; for ( let descendent of directDescendents ) { if ( this . _nodeMap [ descendent . uniqueId ] != null ) { children . push ( this . _nodeMap [ descendent . uniqueId ] ) ; } } if ( children . length ) { glTFNode . children = children ; } } } } if ( scene . nodes . length ) { this . _scenes . push ( scene ) ; } } ) ; } ) ; } ) ; } param nodes returns returns private getExportNodes ( nodes ) { const exportNodes = [ ] ; const exportMaterials = new Set < Material > ( ) ; for ( const babylonNode of nodes ) { if ( ! this . _options . shouldExportNode || this . _options . shouldExportNode ( babylonNode ) ) { exportNodes . push ( babylonNode ) ; if ( babylonNode . getClassName ( ) === "Mesh" ) { const mesh = babylonNode as Mesh ; if ( mesh . material ) { exportMaterials . add ( mesh . material ) ; } } else { const meshes = babylonNode . getChildMeshes ( false ) ; for ( const mesh of meshes ) { if ( mesh . material ) { exportMaterials . add ( mesh . material ) ; } } } } else { `Excluding node ${ babylonNode . name }` ; } } return [ exportNodes , exportMaterials ] ; } param babylonScene param nodes param binaryWriter returns private createNodeMapAndAnimationsAsync ( babylonScene , nodes , binaryWriter ) { let promiseChain = Promise . resolve ( ) ; const nodeMap = { } ; let nodeIndex ; let runtimeGLTFAnimation = { name : 'runtime animations' , channels : [ ] , samplers : [ ] } ; let idleGLTFAnimations = [ ] ; for ( let babylonNode of nodes ) { promiseChain = promiseChain . then ( ( ) => { let convertToRightHandedSystem = this . _convertToRightHandedSystemMap [ babylonNode . uniqueId ] ; return this . createNodeAsync ( babylonNode , binaryWriter , convertToRightHandedSystem , nodeMap ) . then ( ( node ) => { const promise = this . _extensionsPostExportNodeAsync ( "createNodeAsync" , node , babylonNode , nodeMap ) ; if ( promise == null ) { Tools . Warn ( `Not exporting node ${ babylonNode . name }` ) ; return Promise . resolve ( ) ; } else { return promise . then ( ( node ) => { if ( ! node ) { return ; } this . _nodes . push ( node ) ; nodeIndex = this . _nodes . length - 1 ; nodeMap [ babylonNode . uniqueId ] = nodeIndex ; if ( ! babylonScene . animationGroups . length ) { _GLTFAnimation . _CreateMorphTargetAnimationFromMorphTargetAnimations ( babylonNode , runtimeGLTFAnimation , idleGLTFAnimations , nodeMap , this . _nodes , binaryWriter , this . _bufferViews , this . _accessors , convertToRightHandedSystem , this . _animationSampleRate ) ; if ( babylonNode . animations . length ) { _GLTFAnimation . _CreateNodeAnimationFromNodeAnimations ( babylonNode , runtimeGLTFAnimation , idleGLTFAnimations , nodeMap , this . _nodes , binaryWriter , this . _bufferViews , this . _accessors , convertToRightHandedSystem , this . _animationSampleRate ) ; } } } ) ; } } ) ; } ) ; } return promiseChain . then ( ( ) => { if ( runtimeGLTFAnimation . channels . length && runtimeGLTFAnimation . samplers . length ) { this . _animations . push ( runtimeGLTFAnimation ) ; } idleGLTFAnimations . forEach ( ( idleGLTFAnimation ) => { if ( idleGLTFAnimation . channels . length && idleGLTFAnimation . samplers . length ) { this . _animations . push ( idleGLTFAnimation ) ; } } ) ; if ( babylonScene . animationGroups . length ) { _GLTFAnimation . _CreateNodeAndMorphAnimationFromAnimationGroups ( babylonScene , this . _animations , nodeMap , this . _nodes , binaryWriter , this . _bufferViews , this . _accessors , this . _convertToRightHandedSystemMap , this . _animationSampleRate ) ; } return nodeMap ; } ) ; } param babylonMesh param binaryWriter param convertToRightHandedSystem param nodeMap returns private createNodeAsync ( babylonNode , binaryWriter , convertToRightHandedSystem , nodeMap ? ) { return Promise . resolve ( ) . then ( ( ) => { const node = { } ; const mesh = { primitives : [ ] } ; if ( babylonNode . name ) { node . name = babylonNode . name ; } if ( babylonNode instanceof TransformNode ) { this . setNodeTransformation ( node , babylonNode , convertToRightHandedSystem ) ; if ( babylonNode instanceof Mesh ) { let morphTargetManager = babylonNode . morphTargetManager ; if ( morphTargetManager && morphTargetManager . numTargets > 0 ) { mesh . weights = [ ] ; for ( let i = 0 ; i < morphTargetManager . numTargets ; ++ i ) { mesh . weights . push ( morphTargetManager . getTarget ( i ) . influence ) ; } } } return this . setPrimitiveAttributesAsync ( mesh , babylonNode , binaryWriter , convertToRightHandedSystem ) . then ( ( ) => { if ( mesh . primitives . length ) { this . _meshes . push ( mesh ) ; node . mesh = this . _meshes . length - 1 ; } return node ; } ) ; } else { return node ; } } ) ; } param babylonScene param nodes param binaryWriter returns private createSkinsAsync ( babylonScene , nodeMap , binaryWriter ) { const promiseChain = Promise . resolve ( ) ; const skinMap = { } ; for ( let skeleton of babylonScene . skeletons ) { const skin = { joints : [ ] } ; const inverseBindMatrices = [ ] ; const boneIndexMap = { } ; let maxBoneIndex = - 1 ; for ( let i = 0 ; i < skeleton . bones . length ; ++ i ) { const bone = skeleton . bones [ i ] ; const boneIndex = bone . getIndex ( ) ?? i ; if ( boneIndex !== - 1 ) { boneIndexMap [ boneIndex ] = bone ; if ( boneIndex > maxBoneIndex ) { maxBoneIndex = boneIndex ; } } } for ( let boneIndex = 0 ; boneIndex <= maxBoneIndex ; ++ boneIndex ) { const bone = boneIndexMap [ boneIndex ] ; inverseBindMatrices . push ( bone . getInvertedAbsoluteTransform ( ) ) ; const transformNode = bone . getTransformNode ( ) ; if ( transformNode ) { skin . joints . push ( nodeMap [ transformNode . uniqueId ] ) ; } else { Tools . Warn ( "Exporting a bone without a linked transform node is currently unsupported" ) ; } } const byteStride = 64 ; const byteLength = inverseBindMatrices . length * byteStride ; const bufferViewOffset = binaryWriter . getByteOffset ( ) ; const bufferView = _GLTFUtilities . _CreateBufferView ( 0 , bufferViewOffset , byteLength , undefined , "InverseBindMatrices" + " - " + skeleton . name ) ; this . _bufferViews . push ( bufferView ) ; const bufferViewIndex = this . _bufferViews . length - 1 ; const bindMatrixAccessor = _GLTFUtilities . _CreateAccessor ( bufferViewIndex , "InverseBindMatrices" + " - " + skeleton . name , AccessorType . MAT4 , AccessorComponentType . FLOAT , inverseBindMatrices . length , null , null , null ) ; const inverseBindAccessorIndex = this . _accessors . push ( bindMatrixAccessor ) - 1 ; skin . inverseBindMatrices = inverseBindAccessorIndex ; this . _skins . push ( skin ) ; skinMap [ skeleton . uniqueId ] = this . _skins . length - 1 ; inverseBindMatrices . forEach ( ( mat ) => { mat . m . forEach ( ( cell ) => { binaryWriter . setFloat32 ( cell ) ; } ) ; } ) ; } return promiseChain . then ( ( ) => { return skinMap ; } ) ; } } hidden export class _BinaryWriter { /**
     * Array buffer which stores all binary data
     */ private _arrayBuffer : ArrayBuffer ; /**
     * View of the array buffer
     */ private _dataView : DataView ; /**
     * byte offset of data in array buffer
     */ private _byteOffset : number ; param byteLength constructor ( byteLength ) { this . _arrayBuffer = new ArrayBuffer ( byteLength ) ; this . _dataView = new DataView ( this . _arrayBuffer ) ; this . _byteOffset = 0 ; } param byteLength private resizeBuffer ( byteLength ) { let newBuffer = new ArrayBuffer ( byteLength ) ; let oldUint8Array = new Uint8Array ( this . _arrayBuffer ) ; let newUint8Array = new Uint8Array ( newBuffer ) ; for ( let i = 0 , length = newUint8Array . byteLength ; i < length ; ++ i ) { newUint8Array [ i ] = oldUint8Array [ i ] ; } this . _arrayBuffer = newBuffer ; this . _dataView = new DataView ( this . _arrayBuffer ) ; return newBuffer ; } returns public getArrayBuffer ( ) { return this . resizeBuffer ( this . getByteOffset ( ) ) ; } returns public getByteOffset ( ) { if ( this . _byteOffset == undefined ) { throw new Error ( "Byte offset is undefined!" ) ; } return this . _byteOffset ; } param entry param byteOffset public setUInt8 ( entry , byteOffset ? ) { if ( byteOffset != null ) { if ( byteOffset < this . _byteOffset ) { this . _dataView . setUint8 ( byteOffset , entry ) ; } else { Tools . Error ( 'BinaryWriter: byteoffset is greater than the current binary buffer length!' ) ; } } else { if ( this . _byteOffset + 1 > this . _arrayBuffer . byteLength ) { this . resizeBuffer ( this . _arrayBuffer . byteLength * 2 ) ; } this . _dataView . setUint8 ( this . _byteOffset , entry ) ; this . _byteOffset += 1 ; } } param entry param byteOffset public setUInt16 ( entry , byteOffset ? ) { if ( byteOffset != null ) { if ( byteOffset < this . _byteOffset ) { this . _dataView . setUint16 ( byteOffset , entry , true ) ; } else { Tools . Error ( 'BinaryWriter: byteoffset is greater than the current binary buffer length!' ) ; } } else { if ( this . _byteOffset + 2 > this . _arrayBuffer . byteLength ) { this . resizeBuffer ( this . _arrayBuffer . byteLength * 2 ) ; } this . _dataView . setUint16 ( this . _byteOffset , entry , true ) ; this . _byteOffset += 2 ; } } param entry param byteOffset public getUInt32 ( byteOffset ) { if ( byteOffset < this . _byteOffset ) { return this . _dataView . getUint32 ( byteOffset , true ) ; } else { Tools . Error ( 'BinaryWriter: byteoffset is greater than the current binary buffer length!' ) ; throw new Error ( 'BinaryWriter: byteoffset is greater than the current binary buffer length!' ) ; } } public getVector3Float32FromRef ( vector3 , byteOffset ) { if ( byteOffset + 8 > this . _byteOffset ) { Tools . Error ( `BinaryWriter: byteoffset is greater than the current binary buffer length!` ) ; } else { vector3 . x = this . _dataView . getFloat32 ( byteOffset , true ) ; vector3 . y = this . _dataView . getFloat32 ( byteOffset + 4 , true ) ; vector3 . z = this . _dataView . getFloat32 ( byteOffset + 8 , true ) ; } } public setVector3Float32FromRef ( vector3 , byteOffset ) { if ( byteOffset + 8 > this . _byteOffset ) { Tools . Error ( `BinaryWriter: byteoffset is greater than the current binary buffer length!` ) ; } else { this . _dataView . setFloat32 ( byteOffset , vector3 . x , true ) ; this . _dataView . setFloat32 ( byteOffset + 4 , vector3 . y , true ) ; this . _dataView . setFloat32 ( byteOffset + 8 , vector3 . z , true ) ; } } public getVector4Float32FromRef ( vector4 , byteOffset ) { if ( byteOffset + 12 > this . _byteOffset ) { Tools . Error ( `BinaryWriter: byteoffset is greater than the current binary buffer length!` ) ; } else { vector4 . x = this . _dataView . getFloat32 ( byteOffset , true ) ; vector4 . y = this . _dataView . getFloat32 ( byteOffset + 4 , true ) ; vector4 . z = this . _dataView . getFloat32 ( byteOffset + 8 , true ) ; vector4 . w = this . _dataView . getFloat32 ( byteOffset + 12 , true ) ; } } public setVector4Float32FromRef ( vector4 , byteOffset ) { if ( byteOffset + 12 > this . _byteOffset ) { Tools . Error ( `BinaryWriter: byteoffset is greater than the current binary buffer length!` ) ; } else { this . _dataView . setFloat32 ( byteOffset , vector4 . x , true ) ; this . _dataView . setFloat32 ( byteOffset + 4 , vector4 . y , true ) ; this . _dataView . setFloat32 ( byteOffset + 8 , vector4 . z , true ) ; this . _dataView . setFloat32 ( byteOffset + 12 , vector4 . w , true ) ; } } param entry public setFloat32 ( entry , byteOffset ? ) { if ( isNaN ( entry ) ) { Tools . Error ( 'Invalid data being written!' ) ; } if ( byteOffset != null ) { if ( byteOffset < this . _byteOffset ) { this . _dataView . setFloat32 ( byteOffset , entry , true ) ; } else { Tools . Error ( 'BinaryWriter: byteoffset is greater than the current binary length!' ) ; } } if ( this . _byteOffset + 4 > this . _arrayBuffer . byteLength ) { this . resizeBuffer ( this . _arrayBuffer . byteLength * 2 ) ; } this . _dataView . setFloat32 ( this . _byteOffset , entry , true ) ; this . _byteOffset += 4 ; } param entry param byteOffset public setUInt32 ( entry , byteOffset ? ) { if ( byteOffset != null ) { if ( byteOffset < this . _byteOffset ) { this . _dataView . setUint32 ( byteOffset , entry , true ) ; } else { Tools . Error ( 'BinaryWriter: byteoffset is greater than the current binary buffer length!' ) ; } } else { if ( this . _byteOffset + 4 > this . _arrayBuffer . byteLength ) { this . resizeBuffer ( this . _arrayBuffer . byteLength * 2 ) ; } this . _dataView . setUint32 ( this . _byteOffset , entry , true ) ; this . _byteOffset += 4 ; } } }