diff --git a/source/import/importer.js b/source/import/importer.js index 3c386e0..1874aab 100644 --- a/source/import/importer.js +++ b/source/import/importer.js @@ -14,6 +14,21 @@ OV.File = class this.name = OV.GetFileName (file.name); this.extension = OV.GetFileExtension (file.name); } + this.url = null; + this.content = null; + } + + HasContent () + { + return this.url !== null && this.content !== null; + } + + Dispose () + { + if (this.url !== null) { + OV.RevokeObjectUrl (this.url); + } + this.url = null; this.content = null; } }; @@ -133,6 +148,7 @@ OV.FileList = class { let callbacks = { success : function (content) { + file.url = OV.CreateObjectUrl (content); file.content = content; }, error : function () { @@ -142,7 +158,7 @@ OV.FileList = class complete (); } }; - if (file.content !== null) { + if (file.HasContent ()) { complete (); return; } @@ -177,6 +193,14 @@ OV.FileList = class } return null; } + + Dispose () + { + for (let i = 0; i < this.files.length; i++) { + let file = this.files[i]; + file.Dispose (); + } + } }; OV.ImporterErrorCode = @@ -236,7 +260,7 @@ OV.Importer = class Import (callbacks) { let mainFile = this.fileList.GetMainFile (); - if (mainFile === null || mainFile.file.content === null) { + if (mainFile === null || mainFile.file === null || !mainFile.file.HasContent ()) { callbacks.error (new OV.ImporterError (OV.ImporterErrorCode.NoImportableFile, null)); return; } @@ -265,10 +289,8 @@ OV.Importer = class fileBuffer = null; } else { result.usedFiles.push (fileName); - let blob = new Blob ([file.content]); - let blobURL = URL.createObjectURL (blob); fileBuffer = { - url : blobURL, + url : file.url, buffer : file.content }; } @@ -318,6 +340,7 @@ OV.Importer = class } } if (reset) { + this.fileList.Dispose (); this.fileList = newFileList; } this.missingFiles = []; @@ -334,5 +357,20 @@ OV.Importer = class IsOnlyFileSource (source) { return this.fileList.IsOnlySource (source); + } + + DisposeModel (model) + { + if (model === null) { + return; + } + for (let i = 0; i < model.MaterialCount (); i++) { + let material = model.GetMaterial (i); + material.EnumerateTextureMaps (function (texture) { + if (texture.url !== null) { + OV.RevokeObjectUrl (texture.url); + } + }); + } } }; diff --git a/source/import/importergltf.js b/source/import/importergltf.js index 6635690..5c6a364 100644 --- a/source/import/importergltf.js +++ b/source/import/importergltf.js @@ -351,12 +351,14 @@ OV.ImporterGltf = class extends OV.ImporterBase this.bufferContents = null; this.gltfExtensions = null; + this.imageIndexToTextureParams = null; } ResetState () { this.bufferContents = []; this.gltfExtensions = new OV.GltfExtensions (); + this.imageIndexToTextureParams = {}; } CanImportExtension (extension) @@ -616,36 +618,48 @@ OV.ImporterGltf = class extends OV.ImporterBase let texture = new OV.TextureMap (); let gltfTexture = gltf.textures[gltfTextureRef.index]; - let gltfImage = gltf.images[gltfTexture.source]; + let gltfImageIndex = gltfTexture.source; + let gltfImage = gltf.images[gltfImageIndex]; - let textureIndexString = gltfTexture.source.toString (); - if (gltfImage.uri !== undefined) { - let base64Buffer = OV.Base64DataURIToArrayBuffer (gltfImage.uri); - if (base64Buffer !== null) { - let blob = new Blob ([base64Buffer.buffer], { type : base64Buffer.mimeType }); - texture.name = 'Embedded_' + textureIndexString + GetTextureFileExtension (base64Buffer.mimeType); - texture.url = URL.createObjectURL (blob); - texture.buffer = base64Buffer.buffer; - } else { - let textureBuffer = this.callbacks.getFileBuffer (gltfImage.uri); - texture.name = gltfImage.uri; - if (textureBuffer !== null) { - texture.url = textureBuffer.url; - texture.buffer = textureBuffer.buffer; + let textureParams = this.imageIndexToTextureParams[gltfImageIndex]; + if (textureParams === undefined) { + textureParams = { + name : null, + url : null, + buffer : null + }; + let textureIndexString = gltfImageIndex.toString (); + if (gltfImage.uri !== undefined) { + let base64Buffer = OV.Base64DataURIToArrayBuffer (gltfImage.uri); + if (base64Buffer !== null) { + textureParams.name = 'Embedded_' + textureIndexString + GetTextureFileExtension (base64Buffer.mimeType); + textureParams.url = OV.CreateObjectUrlWithMimeType (base64Buffer.buffer, base64Buffer.mimeType); + textureParams.buffer = base64Buffer.buffer; + } else { + let textureBuffer = this.callbacks.getFileBuffer (gltfImage.uri); + textureParams.name = gltfImage.uri; + if (textureBuffer !== null) { + textureParams.url = textureBuffer.url; + textureParams.buffer = textureBuffer.buffer; + } + } + } else if (gltfImage.bufferView !== undefined) { + let bufferView = gltf.bufferViews[gltfImage.bufferView]; + let reader = this.GetReaderFromBufferView (bufferView); + if (reader !== null) { + let buffer = reader.ReadArrayBuffer (bufferView.byteLength); + textureParams.name = 'Binary_' + textureIndexString + GetTextureFileExtension (gltfImage.mimeType); + textureParams.url = OV.CreateObjectUrlWithMimeType (buffer, gltfImage.mimeType); + textureParams.buffer = buffer; } } - } else if (gltfImage.bufferView !== undefined) { - let bufferView = gltf.bufferViews[gltfImage.bufferView]; - let reader = this.GetReaderFromBufferView (bufferView); - if (reader !== null) { - let buffer = reader.ReadArrayBuffer (bufferView.byteLength); - let blob = new Blob ([buffer], { type: gltfImage.mimeType }); - texture.name = 'Binary_' + textureIndexString + GetTextureFileExtension (gltfImage.mimeType); - texture.url = URL.createObjectURL (blob); - texture.buffer = buffer; - } + this.imageIndexToTextureParams[gltfImageIndex] = textureParams; } + texture.name = textureParams.name; + texture.url = textureParams.url; + texture.buffer = textureParams.buffer; + this.gltfExtensions.ProcessTexture (gltfTextureRef, texture); return texture; } diff --git a/source/io/bufferutils.js b/source/io/bufferutils.js index 502999e..d8690c9 100644 --- a/source/io/bufferutils.js +++ b/source/io/bufferutils.js @@ -60,3 +60,22 @@ OV.Base64DataURIToArrayBuffer = function (uri) buffer : buffer }; }; + +OV.CreateObjectUrl = function (content) +{ + let blob = new Blob ([content]); + let url = URL.createObjectURL (blob); + return url; +}; + +OV.CreateObjectUrlWithMimeType = function (content, mimeType) +{ + let blob = new Blob ([content], { type : mimeType }); + let url = URL.createObjectURL (blob); + return url; +}; + +OV.RevokeObjectUrl = function (url) +{ + URL.revokeObjectURL (url); +}; diff --git a/source/model/modelentities.js b/source/model/modelentities.js index 7279f3f..18c2076 100644 --- a/source/model/modelentities.js +++ b/source/model/modelentities.js @@ -117,6 +117,25 @@ OV.Material = class this.multiplyDiffuseMap = false; } + EnumerateTextureMaps (enumerator) + { + if (this.diffuseMap !== null) { + enumerator (this.diffuseMap); + } + if (this.specularMap !== null) { + enumerator (this.specularMap); + } + if (this.bumpMap !== null) { + enumerator (this.bumpMap); + } + if (this.normalMap !== null) { + enumerator (this.normalMap); + } + if (this.emissiveMap !== null) { + enumerator (this.emissiveMap); + } + } + Clone () { let cloned = new OV.Material (); diff --git a/website/o3dv/dialogs.js b/website/o3dv/dialogs.js index ca3760a..935d8fd 100644 --- a/website/o3dv/dialogs.js +++ b/website/o3dv/dialogs.js @@ -260,8 +260,8 @@ OV.ShowExportDialog = function (model) let file = files[i]; let url = file.GetUrl (); if (url === null) { - let fileBlob = new Blob ([file.GetContent ()]); - url = URL.createObjectURL (fileBlob); + // TODO: revoke on close + url = OV.CreateObjectUrl (file.GetContent ()); } let fileLink = $('').addClass ('ov_dialog_file_link').appendTo (fileList); fileLink.attr ('href', url); diff --git a/website/o3dv/website.js b/website/o3dv/website.js index c7631ef..b83b059 100644 --- a/website/o3dv/website.js +++ b/website/o3dv/website.js @@ -71,6 +71,10 @@ OV.Website = class this.dialog.Hide (); this.dialog = null; } + if (this.model !== null) { + let importer = this.modelLoader.GetImporter (); + importer.DisposeModel (this.model); + } this.model = null; this.parameters.introDiv.hide (); this.ShowViewer (false);