From 686cfdf79799a15f088089372f999d007432a0ce Mon Sep 17 00:00:00 2001 From: ousttrue Date: Wed, 26 May 2021 21:12:58 +0900 Subject: [PATCH 1/6] add gltf.openGlbFile --- package.json | 11 ++++++- src/extension.ts | 18 +++++++++++ src/glbProvider.ts | 77 ++++++++++++++++++++++++++++++++++++++++++++++ src/gltfWindow.ts | 10 +++++- 4 files changed, 114 insertions(+), 2 deletions(-) create mode 100644 src/glbProvider.ts diff --git a/package.json b/package.json index 5b4f4fbe..7ba683b6 100644 --- a/package.json +++ b/package.json @@ -37,7 +37,8 @@ "onCommand:gltf.previewModel", "onCommand:gltf.validateFile", "onCommand:gltf.exportGlbFile", - "onCommand:gltf.importGlbFile" + "onCommand:gltf.importGlbFile", + "onCommand:gltf.openGlbFile" ], "main": "./out/src/extension", "contributes": { @@ -177,6 +178,10 @@ "command": "gltf.importGlbFile", "title": "glTF: Import from GLB" }, + { + "command": "gltf.openGlbFile", + "title": "glTF: Open GLB" + }, { "command": "gltf.importAnimation", "title": "glTF: Import animation" @@ -232,6 +237,10 @@ "command": "gltf.importGlbFile", "group": "glTF" }, + { + "command": "gltf.openGlbFile", + "group": "glTF" + }, { "command": "gltf.validateFile", "group": "glTF" diff --git a/src/extension.ts b/src/extension.ts index 52cc5281..8eb7c216 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -9,6 +9,7 @@ import * as fs from 'fs'; import { getFromJsonPointer, guessMimeType, btoa, guessFileExtension, getAccessorData, AccessorTypeToNumComponents, parseJsonMap, truncateJsonPointer } from './utilities'; import { GLTF2 } from './GLTF2'; import { GltfWindow } from './gltfWindow'; +import { GlbProvider } from './glbProvider'; function checkValidEditor(): boolean { if (vscode.window.activeTextEditor === undefined) { @@ -457,6 +458,23 @@ export function activate(context: vscode.ExtensionContext): void { } })); + // + // Virtual TextDocumentContentProvider for glb json chunk. + // + context.subscriptions.push(vscode.workspace.registerTextDocumentContentProvider('glb', new GlbProvider())); + + // + // Open GLB with 'glb:' schema, and send GlbProvider. + // + context.subscriptions.push(vscode.commands.registerCommand('gltf.openGlbFile', async (fileUri) => { + + const uri = vscode.Uri.parse('glb:' + fileUri.fsPath); + const doc = await vscode.workspace.openTextDocument(uri); // calls back into the provider + vscode.languages.setTextDocumentLanguage(doc, "json"); + await vscode.window.showTextDocument(doc, { preview: true }); + + })); + // // Run the validator on an external file. // diff --git a/src/glbProvider.ts b/src/glbProvider.ts new file mode 100644 index 00000000..e00fd383 --- /dev/null +++ b/src/glbProvider.ts @@ -0,0 +1,77 @@ +import * as vscode from 'vscode'; +import * as fs from 'fs'; +import * as path from 'path'; + +// +// Virtual TextDocumentContentProvider for glb json chunk. +// +export class GlbProvider implements vscode.TextDocumentContentProvider { + + // emitter and its event + onDidChangeEmitter = new vscode.EventEmitter(); + onDidChange = this.onDidChangeEmitter.event; + + provideTextDocumentContent(uri: vscode.Uri): string { + + let data = fs.readFileSync(uri.fsPath); + var offset = 0; + if (data.readInt32LE(offset) != 0x46546C67) { + return `not glb`; + } + offset += 4; + + let version = data.readInt32LE(offset) + if (version != 2) { + return `gltf version ${version} not support`; + } + offset += 4; + + let length = data.readInt32LE(offset); + offset += 4; + + var json = null; + var binOffset = null; + while (offset < length) { + + let chunkLength = data.readInt32LE(offset); + offset += 4; + + let chunkType = data.readInt32LE(offset); + offset += 4; + + let chunkData = data.toString('utf8', offset, offset + chunkLength); + + if (chunkType == 0x4E4F534A) { + + var json = JSON.parse(chunkData); + + } + else if (chunkType == 0x004E4942) { + + binOffset = offset; + + } + else { + // unknown + } + + offset += chunkLength; + } + + if (binOffset && json) { + + // buffer access hack + json.buffers[0]['uri'] = path.basename(uri.fsPath); + for (let bufferView of json.bufferViews) { + bufferView.byteOffset += binOffset; + } + + return JSON.stringify(json, null, ' '); + } + else { + + return `invalid glb`; + + } + } +}; diff --git a/src/gltfWindow.ts b/src/gltfWindow.ts index 9a94f3c4..1d3a0b4e 100644 --- a/src/gltfWindow.ts +++ b/src/gltfWindow.ts @@ -4,7 +4,15 @@ import { GltfInspectData } from './gltfInspectData'; import { GltfOutline } from './gltfOutline'; function isGltfFile(editor: vscode.TextEditor | undefined): boolean { - return editor && editor.document.fileName.toLowerCase().endsWith('.gltf'); + if (editor) { + if (editor.document.fileName.toLowerCase().endsWith('.gltf')) { + return true; + } + if (editor.document.uri.scheme == 'glb') { + return true; + } + } + return false; } export class GltfWindow { From cdfc24469d2f0e4447f2a912e9d683d7ed2aaf11 Mon Sep 17 00:00:00 2001 From: ousttrue Date: Sat, 29 May 2021 12:41:01 +0900 Subject: [PATCH 2/6] fix var --- src/glbProvider.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/glbProvider.ts b/src/glbProvider.ts index e00fd383..cf474979 100644 --- a/src/glbProvider.ts +++ b/src/glbProvider.ts @@ -14,7 +14,7 @@ export class GlbProvider implements vscode.TextDocumentContentProvider { provideTextDocumentContent(uri: vscode.Uri): string { let data = fs.readFileSync(uri.fsPath); - var offset = 0; + let offset = 0; if (data.readInt32LE(offset) != 0x46546C67) { return `not glb`; } @@ -29,8 +29,8 @@ export class GlbProvider implements vscode.TextDocumentContentProvider { let length = data.readInt32LE(offset); offset += 4; - var json = null; - var binOffset = null; + let json = null; + let binOffset = null; while (offset < length) { let chunkLength = data.readInt32LE(offset); @@ -43,7 +43,7 @@ export class GlbProvider implements vscode.TextDocumentContentProvider { if (chunkType == 0x4E4F534A) { - var json = JSON.parse(chunkData); + json = JSON.parse(chunkData); } else if (chunkType == 0x004E4942) { From eacc8bd30299b618c8ac45b623753625cf0190da Mon Sep 17 00:00:00 2001 From: ousttrue Date: Sat, 29 May 2021 13:39:11 +0900 Subject: [PATCH 3/6] preview: false --- src/extension.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/extension.ts b/src/extension.ts index 8eb7c216..ff3e4cb6 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -471,7 +471,7 @@ export function activate(context: vscode.ExtensionContext): void { const uri = vscode.Uri.parse('glb:' + fileUri.fsPath); const doc = await vscode.workspace.openTextDocument(uri); // calls back into the provider vscode.languages.setTextDocumentLanguage(doc, "json"); - await vscode.window.showTextDocument(doc, { preview: true }); + await vscode.window.showTextDocument(doc, { preview: false }); })); From d4b236a64fe8d1a9b60de4c4066178d14f476735 Mon Sep 17 00:00:00 2001 From: ousttrue Date: Fri, 4 Jun 2021 18:28:57 +0900 Subject: [PATCH 4/6] support inspect data for image in glb --- src/dataUriTextDocumentContentProvider.ts | 60 +++++++++++++++++------ 1 file changed, 46 insertions(+), 14 deletions(-) diff --git a/src/dataUriTextDocumentContentProvider.ts b/src/dataUriTextDocumentContentProvider.ts index 0746a0bd..b66e3a1e 100644 --- a/src/dataUriTextDocumentContentProvider.ts +++ b/src/dataUriTextDocumentContentProvider.ts @@ -10,7 +10,7 @@ import { getFromJsonPointer, btoa, atob, getAccessorData, AccessorTypeToNumCompo import { GLTF2 } from './GLTF2'; let decoderModule; -draco3dgltf.createDecoderModule({}).then(function(module) { +draco3dgltf.createDecoderModule({}).then(function (module) { decoderModule = module; }); @@ -57,12 +57,54 @@ export class DataUriTextDocumentContentProvider implements vscode.TextDocumentCo return !!jsonPointer.match(/^\/meshes\/\d+\/primitives\//); } + getDocument(fileName: string): vscode.TextDocument { + let document = vscode.workspace.textDocuments.find(e => e.uri.scheme === 'file' && e.fileName === fileName); + if (document) { + return document; + } + + // get json document from glbProvider + return vscode.workspace.textDocuments.find(e => e.uri.scheme === 'glb' && e.fileName === fileName); + } + + getTitleAndData(data: any, gltf: GLTF2.GLTF, jsonPointer: string, fileName: string): [string, string] { + if (data.hasOwnProperty('uri')) { + // from external uri + let dataUri: string = data.uri; + let previewTitle = dataUri; + if (!dataUri.startsWith('data:')) { + // Not a DataURI: Look up external reference. + const name = decodeURI(Url.resolve(fileName, dataUri)); + const contents = fs.readFileSync(name); + dataUri = 'data:image;base64,' + btoa(contents); + previewTitle = decodeURI(previewTitle); + } else { + previewTitle = jsonPointer; + } + + return [previewTitle, dataUri]; + } + + if (data.hasOwnProperty('bufferView')) { + // from internal bufferView + const bufferView = gltf.bufferViews[data.bufferView]; + const buffer = gltf.buffers[bufferView.buffer]; + const name = decodeURI(Url.resolve(fileName, buffer.uri)); + const contents = fs.readFileSync(name).slice(bufferView.byteOffset, bufferView.byteOffset + bufferView.byteLength); + const dataUri = 'data:image;base64,' + btoa(contents); + const previewTitle = `${buffer.uri}:${jsonPointer}`; + return [previewTitle, dataUri]; + } + + return [null, null] + } + public async provideTextDocumentContent(uri: vscode.Uri): Promise { const fileName = decodeURIComponent(uri.fragment); const query = querystring.parse(uri.query); query.viewColumn = query.viewColumn || vscode.ViewColumn.Active.toString(); let glTFContent: string; - const document = vscode.workspace.textDocuments.find(e => e.uri.scheme === 'file' && e.fileName === fileName); + const document = this.getDocument(fileName); if (document) { glTFContent = document.getText(); } else { @@ -76,18 +118,8 @@ export class DataUriTextDocumentContentProvider implements vscode.TextDocumentCo const data = getFromJsonPointer(glTF, jsonPointer); if (data && (typeof data === 'object')) { - if (data.hasOwnProperty('uri')) { - let dataUri: string = data.uri; - let previewTitle = dataUri; - if (!dataUri.startsWith('data:')) { - // Not a DataURI: Look up external reference. - const name = decodeURI(Url.resolve(fileName, dataUri)); - const contents = fs.readFileSync(name); - dataUri = 'data:image;base64,' + btoa(contents); - previewTitle = decodeURI(previewTitle); - } else { - previewTitle = jsonPointer; - } + let [previewTitle, dataUri] = this.getTitleAndData(data, glTF, jsonPointer, fileName); + if (previewTitle && dataUri) { if (this.isImage(jsonPointer)) { // Peek Definition requests have a document that matches the current document From b038c9079e95013729f671f5dee73656697ebb3a Mon Sep 17 00:00:00 2001 From: ousttrue Date: Fri, 18 Jun 2021 01:30:35 +0900 Subject: [PATCH 5/6] Add ".json" suffix to virtualTextPath --- src/extension.ts | 3 ++- src/glbProvider.ts | 9 +++++++-- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/src/extension.ts b/src/extension.ts index ff3e4cb6..a7e5a037 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -468,7 +468,8 @@ export function activate(context: vscode.ExtensionContext): void { // context.subscriptions.push(vscode.commands.registerCommand('gltf.openGlbFile', async (fileUri) => { - const uri = vscode.Uri.parse('glb:' + fileUri.fsPath); + const virtualTextPath = `glb:${fileUri.fsPath}.json`; + const uri = vscode.Uri.parse(virtualTextPath); const doc = await vscode.workspace.openTextDocument(uri); // calls back into the provider vscode.languages.setTextDocumentLanguage(doc, "json"); await vscode.window.showTextDocument(doc, { preview: false }); diff --git a/src/glbProvider.ts b/src/glbProvider.ts index cf474979..49276fc9 100644 --- a/src/glbProvider.ts +++ b/src/glbProvider.ts @@ -13,7 +13,12 @@ export class GlbProvider implements vscode.TextDocumentContentProvider { provideTextDocumentContent(uri: vscode.Uri): string { - let data = fs.readFileSync(uri.fsPath); + if (!uri.fsPath.endsWith(".json")) { + return "fsPath must endsWith .json"; + } + let fsPath = uri.fsPath.substr(0, uri.fsPath.length - 5); + + let data = fs.readFileSync(fsPath); let offset = 0; if (data.readInt32LE(offset) != 0x46546C67) { return `not glb`; @@ -61,7 +66,7 @@ export class GlbProvider implements vscode.TextDocumentContentProvider { if (binOffset && json) { // buffer access hack - json.buffers[0]['uri'] = path.basename(uri.fsPath); + json.buffers[0]['uri'] = path.basename(fsPath); for (let bufferView of json.bufferViews) { bufferView.byteOffset += binOffset; } From cee724277f02e186e6c4e18de84c63b3c66350a5 Mon Sep 17 00:00:00 2001 From: ousttrue Date: Fri, 18 Jun 2021 01:34:00 +0900 Subject: [PATCH 6/6] rename GlbProvider to GlbTextDocumentContentProvider --- src/extension.ts | 4 ++-- src/{glbProvider.ts => glbTextDocumentContentProvider.ts} | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) rename src/{glbProvider.ts => glbTextDocumentContentProvider.ts} (89%) diff --git a/src/extension.ts b/src/extension.ts index a7e5a037..3a69c9d8 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -9,7 +9,7 @@ import * as fs from 'fs'; import { getFromJsonPointer, guessMimeType, btoa, guessFileExtension, getAccessorData, AccessorTypeToNumComponents, parseJsonMap, truncateJsonPointer } from './utilities'; import { GLTF2 } from './GLTF2'; import { GltfWindow } from './gltfWindow'; -import { GlbProvider } from './glbProvider'; +import { GlbTextDocumentContentProvider } from './glbTextDocumentContentProvider'; function checkValidEditor(): boolean { if (vscode.window.activeTextEditor === undefined) { @@ -461,7 +461,7 @@ export function activate(context: vscode.ExtensionContext): void { // // Virtual TextDocumentContentProvider for glb json chunk. // - context.subscriptions.push(vscode.workspace.registerTextDocumentContentProvider('glb', new GlbProvider())); + context.subscriptions.push(vscode.workspace.registerTextDocumentContentProvider('glb', new GlbTextDocumentContentProvider())); // // Open GLB with 'glb:' schema, and send GlbProvider. diff --git a/src/glbProvider.ts b/src/glbTextDocumentContentProvider.ts similarity index 89% rename from src/glbProvider.ts rename to src/glbTextDocumentContentProvider.ts index 49276fc9..03b82526 100644 --- a/src/glbProvider.ts +++ b/src/glbTextDocumentContentProvider.ts @@ -3,9 +3,9 @@ import * as fs from 'fs'; import * as path from 'path'; // -// Virtual TextDocumentContentProvider for glb json chunk. +// GlbTextDocumentContentProvider provide json chunk of glb. // -export class GlbProvider implements vscode.TextDocumentContentProvider { +export class GlbTextDocumentContentProvider implements vscode.TextDocumentContentProvider { // emitter and its event onDidChangeEmitter = new vscode.EventEmitter();