import * as vscode from 'vscode' ; import { LanguageClient , LanguageClientOptions , ServerOptions , TransportKind } from 'vscode-languageclient' ; import { DataUriTextDocumentContentProvider } from './dataUriTextDocumentContentProvider' ; import { ConvertGLBtoGltfLoadFirst , ConvertToGLB , getBuffer } from 'gltf-import-export' ; import { GltfActionProvider } from './gltfActionProvider' ; import * as GltfValidate from './validationProvider' ; import * as path from 'path' ; import * as Url from 'url' ; import * as fs from 'fs' ; import { getFromJsonPointer , guessMimeType , btoa , guessFileExtension , getAccessorData , AccessorTypeToNumComponents , parseJsonMap , truncateJsonPointer , JsonMap } from './utilities' ; import { GLTF2 } from './GLTF2' ; import { GltfWindow } from './gltfWindow' ; function checkValidEditor ( textEditor ) { if ( textEditor === undefined ) { vscode . window . showErrorMessage ( 'Document too large (or no editor selected). ' + 'Click \'More info\' for details via GitHub.' , 'More info' ) . then ( choice => { if ( choice === 'More info' ) { let uri = vscode . Uri . parse ( 'https://github.com/AnalyticalGraphicsInc/gltf-vscode/blob/master/README.md#compatibiliy-and-known-size-limitations' ) ; vscode . commands . executeCommand ( 'vscode.open' , uri ) ; } } ) ; return false ; } return true ; } function pointerContains ( pointer , selection , textEditor ) { const doc = textEditor . document ; const range = new vscode . Range ( doc . positionAt ( pointer . value . pos ) , doc . positionAt ( pointer . valueEnd . pos ) ) ; return range . contains ( selection ) ; } function tryGetJsonMap ( textEditor ) { try { return parseJsonMap ( textEditor . document . getText ( ) ) ; } catch ( ex ) { vscode . window . showErrorMessage ( 'Error parsing this document.  Please make sure it is valid JSON.' ) ; } return undefined ; } function tryGetCurrentJsonPointer ( map , textEditor ) { const selection = textEditor . selection ; const pointers = map . pointers ; let bestKey , secondBestKey ; for ( let key of Object . keys ( pointers ) ) { let pointer = pointers [ key ] ; if ( pointerContains ( pointer , selection , textEditor ) ) { secondBestKey = bestKey ; bestKey = key ; } } if ( ! bestKey ) { vscode . window . showErrorMessage ( 'Please click on an embedded data item, and try this command again.' ) ; return undefined ; } if ( secondBestKey && bestKey . endsWith ( '/uri' ) ) { bestKey = secondBestKey ; } return bestKey ; } function configurationChanged ( ) { const config = vscode . workspace . getConfiguration ( 'glTF' ) ; const showToolbar3D = config . get ( 'showToolbar3D' ) ; vscode . commands . executeCommand ( 'setContext' , 'gltfShowToolbar3D' , showToolbar3D ) ; } export function activateServer ( context ) { let serverModule = context . asAbsolutePath ( path . join ( 'server' , 'server.js' ) ) ; let debugOptions = { execArgv : [ "--nolazy" , "--inspect=6009" ] } ; let serverOptions = { run : { module : serverModule , transport : TransportKind . ipc } , debug : { module : serverModule , transport : TransportKind . ipc , options : debugOptions } } ; let clientOptions = { documentSelector : [ { scheme : 'file' , language : 'json' } ] , synchronize : { configurationSection : 'glTF' , fileEvents : vscode . workspace . createFileSystemWatcher ( '**/.clientrc' ) } } ; let disposable = new LanguageClient ( 'gltfLanguageServer' , 'glTF Language Server' , serverOptions , clientOptions ) . start ( ) ; context . subscriptions . push ( disposable ) ; } export function activate ( context ) { vscode . workspace . onDidChangeConfiguration ( configurationChanged ) ; configurationChanged ( ) ; activateServer ( context ) ; const gltfWindow = new GltfWindow ( context ) ; const dataPreviewProvider = new DataUriTextDocumentContentProvider ( context ) ; const dataPreviewRegistration = vscode . workspace . registerTextDocumentContentProvider ( 'gltf-dataUri' , dataPreviewProvider ) ; context . subscriptions . push ( dataPreviewRegistration ) ; context . subscriptions . push ( vscode . commands . registerCommand ( 'gltf.inspectData' , async ( ) => { const textEditor = vscode . window . activeTextEditor ; if ( ! checkValidEditor ( textEditor ) ) { return ; } const map = tryGetJsonMap ( textEditor ) ; if ( ! map ) { return ; } let jsonPointer = tryGetCurrentJsonPointer ( map , textEditor ) ; if ( ! jsonPointer ) { return ; } const notDataUri = dataPreviewProvider . uriIfNotDataUri ( map . data , jsonPointer ) ; const isShader = dataPreviewProvider . isShader ( jsonPointer ) ; const isImage = dataPreviewProvider . isImage ( jsonPointer ) ; const isAccessor = dataPreviewProvider . isAccessor ( jsonPointer ) ; const isMeshPrimitive = dataPreviewProvider . isMeshPrimitive ( jsonPointer ) ; if ( ! isImage && ! isShader && ! isAccessor && ! isMeshPrimitive ) { vscode . window . showErrorMessage ( 'This feature currently works only with accessors, images, shaders, and mesh primitives.' ) ; console . log ( 'gltf-vscode: No preview for: ' + jsonPointer ) ; return ; } const fileName = textEditor . document . fileName ; if ( isAccessor ) { jsonPointer = truncateJsonPointer ( jsonPointer , 2 ) ; gltfWindow . inspectData . showAccessor ( fileName , map . data , jsonPointer ) ; return ; } if ( isMeshPrimitive ) { jsonPointer = truncateJsonPointer ( jsonPointer , 4 ) ; gltfWindow . inspectData . showMeshPrimitive ( fileName , map . data , jsonPointer ) ; return ; } if ( notDataUri && ! isImage ) { let finalUri = vscode . Uri . file ( Url . resolve ( fileName , notDataUri ) ) ; await vscode . commands . executeCommand ( 'vscode.open' , finalUri , vscode . ViewColumn . Two ) ; } else { if ( isShader ) { jsonPointer += '.glsl' ; } const previewUri = vscode . Uri . parse ( `${ dataPreviewProvider . UriPrefix }${ jsonPointer }?viewColumn=${ vscode . ViewColumn . Two }#${ encodeURIComponent ( fileName ) }` ) ; await vscode . commands . executeCommand ( 'vscode.open' , previewUri , vscode . ViewColumn . Two ) ; dataPreviewProvider . update ( previewUri ) ; } } ) ) ; context . subscriptions . push ( vscode . commands . registerCommand ( 'gltf.noop' , ( ) => { } ) ) ; context . subscriptions . push ( vscode . commands . registerTextEditorCommand ( 'gltf.importUri' , ( textEditor , edit ) => { if ( ! checkValidEditor ( textEditor ) ) { return ; } const map = tryGetJsonMap ( textEditor ) ; if ( ! map ) { return ; } let bestKey = tryGetCurrentJsonPointer ( map , textEditor ) ; if ( ! bestKey ) { return ; } const data = getFromJsonPointer ( map . data , bestKey ) ; let dataUri = data . uri ; if ( dataUri . startsWith ( 'data:' ) ) { vscode . window . showWarningMessage ( 'This field is already a dataURI.' ) ; } else { const name = Url . resolve ( textEditor . document . fileName , dataUri ) ; let contents ; try { contents = fs . readFileSync ( name ) ; } catch ( ex ) { vscode . window . showErrorMessage ( 'Can\'t read file: ' + name ) ; return ; } dataUri = 'data:' + guessMimeType ( name ) + ';base64,' + btoa ( contents ) ; const pointer = map . pointers [ bestKey + '/uri' ] ; edit . replace ( new vscode . Range ( pointer . value . line , pointer . value . column + 1 , pointer . valueEnd . line , pointer . valueEnd . column - 1 ) , dataUri ) ; } } ) ) ; function exportToFile ( filename , pathFilename , pointer , dataUri , textEditor ) { const pos = dataUri . indexOf ( ',' ) ; const fileContents = Buffer . from ( dataUri . substring ( pos + 1 ) , 'base64' ) ; try { fs . writeFileSync ( pathFilename , fileContents ) ; } catch ( ex ) { vscode . window . showErrorMessage ( 'Can\'t write file: ' + pathFilename ) ; return ; } textEditor . edit ( editBuilder => { editBuilder . replace ( new vscode . Range ( pointer . value . line , pointer . value . column + 1 , pointer . valueEnd . line , pointer . valueEnd . column - 1 ) , filename ) ; } ) ; vscode . window . showInformationMessage ( 'File saved: ' + pathFilename ) ; } context . subscriptions . push ( vscode . commands . registerTextEditorCommand ( 'gltf.exportUri' , async ( textEditor ) => { if ( ! checkValidEditor ( textEditor ) ) { return ; } const map = tryGetJsonMap ( textEditor ) ; if ( ! map ) { return ; } let bestKey = tryGetCurrentJsonPointer ( map , textEditor ) ; if ( ! bestKey ) { return ; } const data = getFromJsonPointer ( map . data , bestKey ) ; let dataUri = data . uri ; if ( ! dataUri . startsWith ( 'data:' ) ) { vscode . window . showWarningMessage ( 'This field is not a dataURI.' ) ; } else { let guessName = 'Texture' ; if ( data . name ) { guessName = data . name ; } const mimeTypePos = dataUri . indexOf ( ';' ) ; let extension ; let mimeType = '' ; if ( mimeTypePos > 0 ) { mimeType = dataUri . substring ( 5 , mimeTypePos ) ; extension = guessFileExtension ( mimeType ) ; guessName += extension ; } let pathGuessName = path . join ( path . dirname ( textEditor . document . fileName ) , guessName ) ; const pointer = map . pointers [ bestKey + '/uri' ] ; if ( ! vscode . workspace . getConfiguration ( 'glTF' ) . get ( 'alwaysOverwriteDefaultFilename' ) ) { let options = { defaultUri : vscode . Uri . file ( pathGuessName ) , filters : { 'All files' : [ '*' ] } } ; options . filters [ mimeType ] = [ extension . replace ( '.' , '' ) ] ; let uri = await vscode . window . showSaveDialog ( options ) ; if ( uri ) { let filename = uri . fsPath ; if ( extension && filename . indexOf ( '.' ) < 0 ) { filename += extension ; } exportToFile ( path . basename ( filename ) , filename , pointer , dataUri , textEditor ) ; } } else { exportToFile ( guessName , pathGuessName , pointer , dataUri , textEditor ) ; } } } ) ) ; context . subscriptions . push ( vscode . commands . registerCommand ( 'gltf.exportGlbFile' , async ( fileUri ) => { const textEditor = vscode . window . activeTextEditor ; let gltfContent ; if ( textEditor && ( ( ! fileUri ) || ! ( fileUri instanceof vscode . Uri ) || fileUri . fsPath === textEditor . document . uri . fsPath ) ) { fileUri = textEditor . document . uri ; gltfContent = textEditor . document . getText ( ) ; } else { if ( ( ! fileUri ) || ! ( fileUri instanceof vscode . Uri ) ) { const options = { canSelectMany : false , openLabel : 'Convert to GLB' , filters : { 'Text glTF' : [ 'gltf' ] , 'All files' : [ '*' ] } } ; let openUri = await vscode . window . showOpenDialog ( options ) ; if ( openUri && openUri [ 0 ] ) { fileUri = openUri [ 0 ] ; } else { return ; } } try { if ( ! fs . existsSync ( fileUri . fsPath ) ) { throw new Error ( 'File not found.' ) ; } gltfContent = fs . readFileSync ( fileUri . fsPath ) . toString ( ) ; } catch ( ex ) { vscode . window . showErrorMessage ( 'Can\'t read file: ' + fileUri . fsPath ) ; return ; } } let gltf ; try { gltf = JSON . parse ( gltfContent ) ; } catch ( ex ) { vscode . window . showErrorMessage ( ex . toString ( ) ) ; return ; } if ( ! gltf || ! gltf . asset || ! gltf . asset . version || gltf . asset . version [ 0 ] !== '2' ) { vscode . window . showErrorMessage ( 'Error: Only glTF 2.0 is supported for GLB export.' ) ; return ; } let glbPath = fileUri . fsPath . replace ( '.gltf' , '.glb' ) ; if ( ! vscode . workspace . getConfiguration ( 'glTF' ) . get ( 'alwaysOverwriteDefaultFilename' ) ) { const options = { defaultUri : vscode . Uri . file ( glbPath ) , filters : { 'Binary glTF' : [ 'glb' ] , 'All files' : [ '*' ] } } ; let uri = await vscode . window . showSaveDialog ( options ) ; if ( uri !== undefined ) { try { ConvertToGLB ( gltf , fileUri . fsPath , uri . fsPath ) ; vscode . window . showInformationMessage ( 'Glb exported as: ' + uri . fsPath ) ; } catch ( ex ) { vscode . window . showErrorMessage ( ex . toString ( ) ) ; } } } else { try { ConvertToGLB ( gltf , fileUri . fsPath , glbPath ) ; vscode . window . showInformationMessage ( 'Glb exported as: ' + glbPath ) ; } catch ( ex ) { vscode . window . showErrorMessage ( ex . toString ( ) ) ; } } } ) ) ; context . subscriptions . push ( vscode . commands . registerCommand ( 'gltf.previewModel' , ( ) => { const textEditor = vscode . window . activeTextEditor ; if ( ! checkValidEditor ( textEditor ) ) { return ; } gltfWindow . preview . openPanel ( textEditor ) ; } ) ) ; context . subscriptions . push ( vscode . commands . registerCommand ( 'gltf.enableDebugMode' , ( ) => { if ( gltfWindow . preview . activePanel ) { gltfWindow . preview . activePanel . webview . postMessage ( { command : 'enableDebugMode' } ) ; } } ) ) ; context . subscriptions . push ( vscode . commands . registerCommand ( 'gltf.disableDebugMode' , ( ) => { if ( gltfWindow . preview . activePanel ) { gltfWindow . preview . activePanel . webview . postMessage ( { command : 'disableDebugMode' } ) ; } } ) ) ; context . subscriptions . push ( vscode . commands . registerCommand ( 'gltf.openGltfSelection' , range => { gltfWindow . outline . select ( range ) ; } ) ) ; context . subscriptions . push ( vscode . commands . registerCommand ( 'gltf.importGlbFile' , async ( fileUri ) => { if ( typeof fileUri === 'undefined' || ! ( fileUri instanceof vscode . Uri ) || ! fileUri . fsPath . endsWith ( '.glb' ) ) { if ( ( vscode . window . activeTextEditor !== undefined ) && ( vscode . window . activeTextEditor . document . uri . fsPath . endsWith ( '.glb' ) ) ) { fileUri = vscode . window . activeTextEditor . document . uri ; } else { const options = { canSelectMany : false , openLabel : 'Import' , filters : { 'Binary glTF' : [ 'glb' ] , 'All files' : [ '*' ] } } ; let openUri = await vscode . window . showOpenDialog ( options ) ; if ( openUri && openUri [ 0 ] ) { fileUri = openUri [ 0 ] ; } else { return ; } } } try { if ( typeof fileUri . fsPath === 'undefined' ) { return ; } if ( ! fs . existsSync ( fileUri . fsPath ) ) { throw new Error ( 'File not found.' ) ; } let getTargetFilename = async ( ) : Promise < string > => { let targetFilename = fileUri . fsPath . replace ( '.glb' , '.gltf' ) ; if ( ! vscode . workspace . getConfiguration ( 'glTF' ) . get ( 'alwaysOverwriteDefaultFilename' ) ) { const options = { defaultUri : vscode . Uri . file ( targetFilename ) , filters : { 'glTF' : [ 'gltf' ] , 'All files' : [ '*' ] } } ; let uri = await vscode . window . showSaveDialog ( options ) ; if ( ! uri ) { return undefined ; } targetFilename = uri . fsPath ; } return targetFilename ; } ; let targetFilename = await ConvertGLBtoGltfLoadFirst ( fileUri . fsPath , getTargetFilename ) ; if ( targetFilename ) { vscode . commands . executeCommand ( 'vscode.open' , vscode . Uri . file ( targetFilename ) ) ; } } catch ( ex ) { vscode . window . showErrorMessage ( ex . toString ( ) ) ; } } ) ) ; context . subscriptions . push ( vscode . commands . registerCommand ( 'gltf.validateFile' , async ( fileUri ) => { if ( typeof fileUri === 'undefined' || ! ( fileUri instanceof vscode . Uri ) || ! ( fileUri . fsPath . endsWith ( '.glb' ) || fileUri . fsPath . endsWith ( '.gltf' ) ) ) { const options = { canSelectMany : false , openLabel : 'Validate' , filters : { 'glTF Files' : [ 'gltf' , 'glb' ] , 'All files' : [ '*' ] } } ; let openUri = await vscode . window . showOpenDialog ( options ) ; if ( openUri && openUri [ 0 ] ) { fileUri = openUri [ 0 ] ; } else { return ; } } try { await GltfValidate . validate ( fileUri . fsPath ) ; } catch ( ex ) { vscode . window . showErrorMessage ( ex . toString ( ) ) ; } } ) ) ; context . subscriptions . push ( vscode . languages . registerCodeActionsProvider ( 'json' , new GltfActionProvider ( ) , { providedCodeActionKinds : GltfActionProvider . providedCodeActionKinds } ) ) ; context . subscriptions . push ( vscode . commands . registerTextEditorCommand ( 'gltf.declareExtension' , ( textEditor , edit , diagnostic ) => { if ( ! textEditor ) { return ; } const map = tryGetJsonMap ( textEditor ) ; if ( ! map ) { return ; } GltfActionProvider . declareExtension ( diagnostic , map , textEditor , edit ) ; } ) ) ; function getAnimationFromJsonPointer ( glTF , jsonPointer ) { let inAnimation = false ; let inSampler = false ; const jsonPointerSplit = jsonPointer . split ( '/' ) ; const numPointerSegments = Math . min ( 5 , jsonPointerSplit . length ) ; let result = glTF ; let path = '' ; const firstValidIndex = 1 ; for ( let i = firstValidIndex ; i < numPointerSegments ; ++ i ) { inAnimation = inAnimation || ( jsonPointerSplit [ i ] === 'animations' ) ; inSampler = inAnimation && ( inSampler || ( jsonPointerSplit [ i ] === 'samplers' ) ) ; result = result [ jsonPointerSplit [ i ] ] ; path += '/' + jsonPointerSplit [ i ] ; } if ( ! inSampler ) { vscode . window . showErrorMessage ( 'Please select an animation sampler to import.' ) ; result = undefined ; } return { json : result , path : path } ; } context . subscriptions . push ( vscode . commands . registerTextEditorCommand ( 'gltf.importAnimation' , ( textEditor , edit ) => { if ( ! textEditor ) { return ; } const map = tryGetJsonMap ( textEditor ) ; if ( ! map ) { return ; } const bestKey = tryGetCurrentJsonPointer ( map , textEditor ) ; if ( ! bestKey ) { return ; } const glTF = map . data ; const animationPointer = getAnimationFromJsonPointer ( glTF , bestKey ) ; if ( ! animationPointer . json ) { return ; } animationPointer . json . extras = animationPointer . json . extras || { } ; for ( const key of [ 'input' , 'output' ] ) { const accessorId = animationPointer . json [ key ] ; const accessor = glTF . accessors [ accessorId ] ; let data = [ ] ; if ( accessor !== undefined ) { data = getAccessorData ( textEditor . document . fileName , glTF , accessor ) ; } animationPointer . json . extras [ `vscode_gltf_${ key }` ] = Array . from ( data ) ; animationPointer . json . extras [ 'vscode_gltf_type' ] = accessor ? accessor . type : 'SCALAR' ; } const pointer = map . pointers [ animationPointer . path ] ; const tabSize = textEditor . options . tabSize as number ; const space = textEditor . options . insertSpaces ? ( new Array ( tabSize + 1 ) . join ( ' ' ) ) : '\t' ; let newJson = JSON . stringify ( animationPointer . json , null , space ) ; const newJsonLines = newJson . split ( /\n/ ) ; const fullTab = new Array ( 5 ) . join ( space ) ; for ( let i = 1 ; i < newJsonLines . length ; i ++ ) { newJsonLines [ i ] = fullTab + newJsonLines [ i ] ; } newJson = newJsonLines . join ( '\n' ) ; const newRange = new vscode . Range ( pointer . value . line , pointer . value . column , pointer . valueEnd . line , pointer . valueEnd . column ) ; edit . replace ( newRange , newJson ) ; } ) ) ; context . subscriptions . push ( vscode . commands . registerTextEditorCommand ( 'gltf.exportAnimation' , ( textEditor , edit ) => { if ( ! textEditor ) { return ; } const map = tryGetJsonMap ( textEditor ) ; if ( ! map ) { return ; } let bestKey = tryGetCurrentJsonPointer ( map , textEditor ) ; if ( ! bestKey ) { return ; } const glTF = map . data ; const animationPointer = getAnimationFromJsonPointer ( map . data , bestKey ) ; if ( ! animationPointer . json || ! animationPointer . json . extras || ! animationPointer . json . extras . vscode_gltf_type || ! animationPointer . json . extras . vscode_gltf_input || ! animationPointer . json . extras . vscode_gltf_output ) { vscode . window . showErrorMessage ( 'Please select an animation sampler with vscode_gltf extras.' ) ; return ; } const samplerType = animationPointer . json . extras . vscode_gltf_type ; const components = AccessorTypeToNumComponents [ samplerType ] ; let entriesPerComponent = animationPointer . json . interpolation === 'CUBICSPLINE' ? 3 : 1 ; const animationIndex = + animationPointer . path . split ( '/' ) [ 2 ] ; const outerAnimation = glTF . animations [ animationIndex ] ; if ( outerAnimation . channels && outerAnimation . channels [ 0 ] ) { const firstChannel = outerAnimation . channels [ 0 ] ; if ( firstChannel . target && firstChannel . target . path === 'weights' ) { const animatedNode = glTF . nodes [ firstChannel . target . node ] ; const animatedMesh = glTF . meshes [ animatedNode . mesh ] ; const numTargets = animatedMesh . primitives [ 0 ] . targets . length ; entriesPerComponent *= numTargets ; } } const newData = { input : animationPointer . json . extras . vscode_gltf_input , output : animationPointer . json . extras . vscode_gltf_output } ; if ( ( newData . input . length * components * entriesPerComponent ) !== newData . output . length ) { vscode . window . showErrorMessage ( `Number of input values (${ newData . input . length }) does not equal output values (${ newData . output . length / components / entriesPerComponent }).` ) ; return ; } delete animationPointer . json . extras . vscode_gltf_type ; delete animationPointer . json . extras . vscode_gltf_input ; delete animationPointer . json . extras . vscode_gltf_output ; if ( Object . keys ( animationPointer . json . extras ) . length === 0 ) { delete animationPointer . json . extras ; } const inputAccessor = glTF . accessors [ animationPointer . json . input ] ; let bufferIndex = 0 ; if ( inputAccessor !== undefined ) { const bufferView = glTF . bufferViews [ inputAccessor . bufferView ] ; bufferIndex = bufferView . buffer ; } const bufferJson = glTF . buffers [ bufferIndex ] ; const bufferData = getBuffer ( glTF , bufferIndex , textEditor . document . fileName ) ; const alignedLength = ( value ) => { const alignValue = 4 ; if ( value === 0 ) { return value ; } const multiple = value % alignValue ; if ( multiple === 0 ) { return value ; } return value + ( alignValue - multiple ) ; } ; let bufferOffset = alignedLength ( bufferData . length ) ; const outputBuffers = [ bufferData ] ; if ( bufferOffset !== bufferData . length ) { outputBuffers . push ( new Buffer ( bufferOffset - bufferData . length ) ) ; } for ( const accessorType of [ 'input' , 'output' ] ) { const values = newData [ accessorType ] ; const accessorComponents = accessorType === 'input' ? 1 : components ; const max = new Array ( accessorComponents ) . fill ( Number . NEGATIVE_INFINITY ) ; for ( let i = 0 ; i < values . length ; i ++ ) { const j = i % accessorComponents ; max [ j ] = Math . max ( max [ j ] , values [ i ] ) ; } const min = new Array ( accessorComponents ) . fill ( Number . POSITIVE_INFINITY ) ; for ( let i = 0 ; i < values . length ; i ++ ) { const j = i % accessorComponents ; min [ j ] = Math . min ( min [ j ] , values [ i ] ) ; } const float32Values = Float32Array . from ( values ) ; const accessor = { "bufferView" : glTF . bufferViews . length , "componentType" : 5126 , "count" : values . length / accessorComponents , "type" : accessorType === 'input' ? 'SCALAR' : samplerType , "max" : max , "min" : min } ; const accessorId = animationPointer . json [ accessorType ] ; if ( glTF . accessors [ accessorId ] === undefined ) { animationPointer . json [ accessorType ] = glTF . accessors . length ; glTF . accessors . push ( accessor ) ; } else { glTF . accessors [ accessorId ] = accessor ; } const newBufferView = { "buffer" : bufferIndex , "byteOffset" : bufferOffset , "byteLength" : float32Values . byteLength , } ; glTF . bufferViews . push ( newBufferView ) ; if ( alignedLength ( float32Values . byteLength ) !== float32Values . byteLength ) { throw new Error ( 'Float32Array not 4 byte length' ) ; } bufferOffset += float32Values . byteLength ; outputBuffers . push ( Buffer . from ( float32Values . buffer as ArrayBuffer ) ) ; } const finalBuffer = Buffer . concat ( outputBuffers ) ; bufferJson . uri = 'data:application/octet-stream;base64,' + finalBuffer . toString ( 'base64' ) ; bufferJson . byteLength = finalBuffer . length ; const tabSize = textEditor . options . tabSize as number ; const space = textEditor . options . insertSpaces ? ( new Array ( tabSize + 1 ) . join ( ' ' ) ) : '\t' ; const newJson = JSON . stringify ( glTF , null , space ) ; const newRange = new vscode . Range ( 0 , 0 , textEditor . document . lineCount + 1 , 0 ) ; edit . replace ( newRange , newJson ) ; const newMap = tryGetJsonMap ( textEditor ) ; const newPointer = newMap . pointers [ animationPointer . path ] ; textEditor . selection = new vscode . Selection ( newPointer . value . line , space . length * 5 , newPointer . value . line , space . length * 5 ) ; } ) ) ; } export function deactivate ( ) { }