diff --git a/index.html b/index.html index a7dd919..cdf7ec2 100644 --- a/index.html +++ b/index.html @@ -857,6 +857,7 @@

NormalMaps from photos

+ diff --git a/javascripts/ambientOccMap.js b/javascripts/ambientOccMap.js index 90e7a50..5dc03b4 100644 --- a/javascripts/ambientOccMap.js +++ b/javascripts/ambientOccMap.js @@ -56,6 +56,7 @@ NMO_AmbientOccMap = new function(){ this.uniforms; this.height_map_tex; this.gaussian_shader_y, this.gaussian_shader_x; + this.renderTarget; this.createAmbientOcclusionTexture = function(){ this.createGPUbasedAOTexture(); @@ -154,6 +155,7 @@ NMO_AmbientOccMap = new function(){ this.renderer.setSize( w, h ); this.composer.setSize( w, h ); var renderTargetParameters = { minFilter: THREE.NearestFilter, magFilter: THREE.NearestFilter, format: THREE.RGBAFormat, stencilBufer: false }; + NMO_RenderTargetUtils.disposeRenderTarget(this); this.renderTarget = new THREE.WebGLRenderTarget( w, h, renderTargetParameters ); this.composer.reset(this.renderTarget); this.composer.render( 1 / 60 ); @@ -222,6 +224,7 @@ NMO_AmbientOccMap = new function(){ var renderTargetParameters = { minFilter: THREE.NearestFilter, magFilter: THREE.NearestFilter, format: THREE.RGBAFormat, stencilBufer: false }; + NMO_RenderTargetUtils.disposeRenderTarget(this); this.renderTarget = new THREE.WebGLRenderTarget( w, h, renderTargetParameters ); this.composer = new THREE.EffectComposer( this.renderer, this.renderTarget ); //renderer_aomap.render( scene_aomap, camera_aomap, renderTarget ); @@ -279,4 +282,4 @@ NMO_AmbientOccMap = new function(){ // this.timer = 0; //} }; -} \ No newline at end of file +} diff --git a/javascripts/displaceMap.js b/javascripts/displaceMap.js index aa5af40..87f254b 100644 --- a/javascripts/displaceMap.js +++ b/javascripts/displaceMap.js @@ -48,6 +48,7 @@ NMO_DisplacementMap = new function(){ this.uniforms; this.height_map_tex; this.gaussian_shader_y, this.gaussian_shader_x; + this.renderTarget; this.createDisplacementMap = function(){ this.createGPUbasedDisplacementTexture(); @@ -158,6 +159,7 @@ NMO_DisplacementMap = new function(){ this.renderer.setSize( w, h ); this.composer.setSize( w, h ); var renderTargetParameters = { minFilter: THREE.NearestFilter, magFilter: THREE.NearestFilter, format: THREE.RGBAFormat, stencilBufer: false }; + NMO_RenderTargetUtils.disposeRenderTarget(this); this.renderTarget = new THREE.WebGLRenderTarget( w, h, renderTargetParameters ); this.composer.reset(this.renderTarget); this.composer.render( 1 / 60 ); @@ -222,8 +224,9 @@ NMO_DisplacementMap = new function(){ var renderTargetParameters = { minFilter: THREE.NearestFilter, magFilter: THREE.NearestFilter, format: THREE.RGBAFormat, stencilBufer: false }; + NMO_RenderTargetUtils.disposeRenderTarget(this); this.renderTarget = new THREE.WebGLRenderTarget( w, h, renderTargetParameters ); - this.composer = new THREE.EffectComposer( this.renderer, renderTarget ); + this.composer = new THREE.EffectComposer( this.renderer, this.renderTarget ); //renderer_aomap.render( scene_aomap, camera_aomap, renderTarget ); //renderer_aomap.render( scene_aomap, camera_aomap ); //this.composer_aomap.setSize( w, h ); @@ -317,4 +320,4 @@ NMO_DisplacementMap = new function(){ } return imageData; }; -} \ No newline at end of file +} diff --git a/javascripts/filedrop.js b/javascripts/filedrop.js index f44867a..502b4a6 100644 --- a/javascripts/filedrop.js +++ b/javascripts/filedrop.js @@ -63,31 +63,39 @@ NMO_FileDrop = new function(){ const includeDisplacement = document.getElementById('displacement_tick').checked; const includeAmbient = document.getElementById('ambient_tick').checked; const includeSpecular = document.getElementById('specular_tick').checked; + const fileNameInput = document.getElementById('file_name'); + const batchInput = document.getElementById('select_multiple_height_files'); + const originalFileName = fileNameInput.value; + const selectedMaps = []; + + if (includeNormal) + selectedMaps.push({ type: "NormalMap", suffix: "normal" }); + if (includeDisplacement) + selectedMaps.push({ type: "DisplacementMap", suffix: "displacement" }); + if (includeAmbient) + selectedMaps.push({ type: "AmbientOcclusionMap", suffix: "ambient" }); + if (includeSpecular) + selectedMaps.push({ type: "SpecularMap", suffix: "specular" }); + + if (selectedMaps.length === 0) + return; - for (let i = 0; i < files.length; i++) { - await this.readImage(files[i], "height", "", async (name) => { - const baseName = name.replace(/\.[^/.]+$/, ""); - - if (includeNormal) { - document.getElementById('file_name').value = `${baseName}_normal`; - await NMO_Main.downloadImage("NormalMap"); - } - - if (includeDisplacement) { - document.getElementById('file_name').value = `${baseName}_displacement`; - await NMO_Main.downloadImage("DisplacementMap"); - } - - if (includeAmbient) { - document.getElementById('file_name').value = `${baseName}_ambient`; - await NMO_Main.downloadImage("AmbientOcclusionMap"); - } - - if (includeSpecular) { - document.getElementById('file_name').value = `${baseName}_specular`; - await NMO_Main.downloadImage("SpecularMap"); - } - }, files[i].name); + try { + for (let i = 0; i < files.length; i++) { + await this.readImage(files[i], "height", "", async (name) => { + const baseName = name.replace(/\.[^/.]+$/, ""); + + for (let mapIndex = 0; mapIndex < selectedMaps.length; mapIndex++) { + const map = selectedMaps[mapIndex]; + fileNameInput.value = `${baseName}_${map.suffix}`; + await NMO_Main.downloadImage(map.type); + } + }, files[i].name); + } + } + finally { + fileNameInput.value = originalFileName; + batchInput.value = ""; } }; diff --git a/javascripts/main.js b/javascripts/main.js index 3049bcf..e57bb46 100644 --- a/javascripts/main.js +++ b/javascripts/main.js @@ -304,14 +304,26 @@ var NMO_Main = new function(){ NMO_SpecularMap.createSpecularTexture(); }; - this.download_all_btn.addEventListener('click', function (e) { - NMO_Main.downloadImage("NormalMap"); - NMO_Main.downloadImage("DisplacementMap"); - NMO_Main.downloadImage("AmbientOcclusionMap"); - NMO_Main.downloadImage("SpecularMap"); + this.wait = function(ms){ + return new Promise(function(resolve){ + setTimeout(resolve, ms); + }); + }; + + this.finishDownload = async function(blob, file_name){ + saveAs(blob, file_name); + // Give the browser a moment to register each programmatic save before the next one. + await this.wait(150); + }; + + this.download_all_btn.addEventListener('click', async function (e) { + await NMO_Main.downloadImage("NormalMap"); + await NMO_Main.downloadImage("DisplacementMap"); + await NMO_Main.downloadImage("AmbientOcclusionMap"); + await NMO_Main.downloadImage("SpecularMap"); }); - this.download_btn.addEventListener('click', function (e) { + this.download_btn.addEventListener('click', function (e) { if (document.getElementById('normal_map').style.cssText != "display: none;"){ NMO_Main.downloadImage("NormalMap"); } @@ -327,12 +339,11 @@ var NMO_Main = new function(){ }); - this.downloadImage = function(type){ + this.downloadImage = async function(type){ console.log("Downloading image"); - var qual = 0.9; var file_name = "download"; var canvas = document.createElement("canvas"); - + var file_type = NMO_Main.getImageType(); var image_type = "image/png"; if (file_type == "jpg") @@ -342,7 +353,7 @@ var NMO_Main = new function(){ canvas.width = NMO_NormalMap.normal_canvas.width; canvas.height = NMO_NormalMap.normal_canvas.height; var context = canvas.getContext('2d'); - if (file_type == "png") + if (file_type == "png") context.globalAlpha = $('#transparency_nmb').val() / 100; context.drawImage(NMO_NormalMap.normal_canvas,0,0); file_name="NormalMap"; @@ -351,7 +362,7 @@ var NMO_Main = new function(){ canvas.width = NMO_DisplacementMap.displacement_canvas.width; canvas.height = NMO_DisplacementMap.displacement_canvas.height; var context = canvas.getContext('2d'); - if (file_type == "png") + if (file_type == "png") context.globalAlpha = $('#transparency_nmb').val() / 100; context.drawImage(NMO_DisplacementMap.displacement_canvas,0,0); file_name="DisplacementMap"; @@ -360,7 +371,7 @@ var NMO_Main = new function(){ canvas.width = NMO_AmbientOccMap.ao_canvas.width; canvas.height = NMO_AmbientOccMap.ao_canvas.height; var context = canvas.getContext('2d'); - if (file_type == "png") + if (file_type == "png") context.globalAlpha = $('#transparency_nmb').val() / 100; context.drawImage(NMO_AmbientOccMap.ao_canvas,0,0); file_name="AmbientOcclusionMap"; @@ -369,28 +380,41 @@ var NMO_Main = new function(){ canvas.width = NMO_SpecularMap.specular_canvas.width; canvas.height = NMO_SpecularMap.specular_canvas.height; var context = canvas.getContext('2d'); - if (file_type == "png") + if (file_type == "png") context.globalAlpha = $('#transparency_nmb').val() / 100; context.drawImage(NMO_SpecularMap.specular_canvas,0,0); file_name="SpecularMap"; } - + if (document.getElementById('file_name').value != "") file_name = document.getElementById('file_name').value; - - - + var qual = $('#file_jpg_qual_nmb').val() / 100; - if (file_type == "tiff"){ - CanvasToTIFF.toBlob(canvas, function(blob) { - saveAs(blob, file_name + ".tif"); - }); - } - else{ - canvas.toBlob(function(blob) { - saveAs(blob, file_name + "." + file_type); - }, image_type, qual); - } + var blob = await new Promise(function(resolve, reject) { + if (file_type == "tiff"){ + CanvasToTIFF.toBlob(canvas, function(generatedBlob) { + if (!generatedBlob){ + reject(new Error("Could not create TIFF blob.")); + return; + } + resolve(generatedBlob); + }); + } + else{ + canvas.toBlob(function(generatedBlob) { + if (!generatedBlob){ + reject(new Error("Could not create image blob.")); + return; + } + resolve(generatedBlob); + }, image_type, qual); + } + }); + + if (file_type == "tiff") + await NMO_Main.finishDownload(blob, file_name + ".tif"); + else + await NMO_Main.finishDownload(blob, file_name + "." + file_type); } this.resetNormalMapSettings = function() { @@ -490,4 +514,4 @@ var NMO_Main = new function(){ NMO_SpecularMap.setSpecularSetting('spec_range', initialRange); NMO_SpecularMap.setSpecularSetting('spec_falloff', initialFallOffType); }; -} \ No newline at end of file +} diff --git a/javascripts/renderNormalview.js b/javascripts/renderNormalview.js index d100938..a45e947 100644 --- a/javascripts/renderNormalview.js +++ b/javascripts/renderNormalview.js @@ -26,6 +26,7 @@ var NMO_RenderNormalview = new function(){ this.renderer_Normalview; this.composer_Normalview; + this.renderTarget; this.scene_Normalview; this.camera_Normalview; this.gaussian_shader_y, this.gaussian_shader_x, this.gaussian_shader; @@ -149,8 +150,9 @@ var NMO_RenderNormalview = new function(){ //composer_Normalview.addPass( copyPass ); var renderTargetParameters = { minFilter: THREE.NearestFilter, magFilter: THREE.NearestFilter, format: THREE.RGBAFormat, stencilBufer: false }; - renderTarget = new THREE.WebGLRenderTarget( NMO_FileDrop.height_image.width, NMO_FileDrop.height_image.height, renderTargetParameters ); - this.composer_Normalview = new THREE.EffectComposer( this.renderer_Normalview, renderTarget ); + NMO_RenderTargetUtils.disposeRenderTarget(this); + this.renderTarget = new THREE.WebGLRenderTarget( NMO_FileDrop.height_image.width, NMO_FileDrop.height_image.height, renderTargetParameters ); + this.composer_Normalview = new THREE.EffectComposer( this.renderer_Normalview, this.renderTarget ); this.composer_Normalview.setSize( NMO_FileDrop.height_image.width, NMO_FileDrop.height_image.height ); this.composer_Normalview.addPass( this.NormalRenderScene ); this.composer_Normalview.addPass( this.gaussian_shader_y ); @@ -216,8 +218,9 @@ var NMO_RenderNormalview = new function(){ this.renderer_Normalview.setSize( img.naturalWidth, img.naturalHeight ); this.composer_Normalview.setSize( img.naturalWidth, img.naturalHeight ); var renderTargetParameters = { minFilter: THREE.NearestFilter, magFilter: THREE.NearestFilter, format: THREE.RGBAFormat, stencilBufer: false }; - renderTarget = new THREE.WebGLRenderTarget( img.width, img.height, renderTargetParameters ); - this.composer_Normalview.reset(renderTarget); + NMO_RenderTargetUtils.disposeRenderTarget(this); + this.renderTarget = new THREE.WebGLRenderTarget( img.width, img.height, renderTargetParameters ); + this.composer_Normalview.reset(this.renderTarget); } else if (map === "pictures"){ @@ -240,8 +243,9 @@ var NMO_RenderNormalview = new function(){ this.renderer_Normalview.setSize( img.naturalWidth, img.naturalHeight ); this.composer_Normalview.setSize( img.naturalWidth, img.naturalHeight ); var renderTargetParameters = { minFilter: THREE.NearestFilter, magFilter: THREE.NearestFilter, format: THREE.RGBAFormat, stencilBufer: false }; - renderTarget = new THREE.WebGLRenderTarget( img.width, img.height, renderTargetParameters ); - this.composer_Normalview.reset(renderTarget); + NMO_RenderTargetUtils.disposeRenderTarget(this); + this.renderTarget = new THREE.WebGLRenderTarget( img.width, img.height, renderTargetParameters ); + this.composer_Normalview.reset(this.renderTarget); this.picture_above_map.needsUpdate = true; this.picture_left_map.needsUpdate = true; @@ -324,4 +328,4 @@ var NMO_RenderNormalview = new function(){ NMO_AmbientOccMap.createAmbientOcclusionTexture(); }; -} \ No newline at end of file +} diff --git a/javascripts/renderTargetUtils.js b/javascripts/renderTargetUtils.js new file mode 100644 index 0000000..e48bc30 --- /dev/null +++ b/javascripts/renderTargetUtils.js @@ -0,0 +1,31 @@ +/* + * Author: Christian Petry + * Homepage: www.petry-christian.de + * + * License: MIT + * Copyright (c) 2014 Christian Petry + * Permission is hereby granted, free of charge, to any person obtaining a copy of this software + * and associated documentation files (the "Software"), to deal in the Software without restriction, + * including without limitation the rights to use, copy, modify, merge, publish, distribute, + * sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or + * substantial portions of the Software. + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, + * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, + * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +var NMO_RenderTargetUtils = new function(){ + this.disposeRenderTarget = function(instance){ + if (instance.renderTarget){ + instance.renderTarget.dispose(); + instance.renderTarget = null; + } + }; +} diff --git a/javascripts/specularMap.js b/javascripts/specularMap.js index cdef86c..c2e14d5 100644 --- a/javascripts/specularMap.js +++ b/javascripts/specularMap.js @@ -58,6 +58,7 @@ NMO_SpecularMap = new function(){ this.uniforms; this.height_map_tex; this.gaussian_shader_y, this.gaussian_shader_x; + this.renderTarget; this.setSpecularSetting = function(element, v){ if (element == "spec_strength") @@ -177,6 +178,7 @@ NMO_SpecularMap = new function(){ this.renderer.setSize( w, h ); this.composer.setSize( w, h ); var renderTargetParameters = { minFilter: THREE.NearestFilter, magFilter: THREE.NearestFilter, format: THREE.RGBAFormat, stencilBufer: false }; + NMO_RenderTargetUtils.disposeRenderTarget(this); this.renderTarget = new THREE.WebGLRenderTarget( w, h, renderTargetParameters ); this.composer.reset(this.renderTarget); this.composer.render( 1 / 60 ); @@ -246,6 +248,7 @@ NMO_SpecularMap = new function(){ var renderTargetParameters = { minFilter: THREE.NearestFilter, magFilter: THREE.NearestFilter, format: THREE.RGBAFormat, stencilBufer: false }; + NMO_RenderTargetUtils.disposeRenderTarget(this); this.renderTarget = new THREE.WebGLRenderTarget( w, h, renderTargetParameters ); this.composer = new THREE.EffectComposer( this.renderer, this.renderTarget ); //this.renderer.render( scene, camera, this.renderTarget ); @@ -269,4 +272,4 @@ NMO_SpecularMap = new function(){ //console.log("Specular: " + (Date.now() - start)); }; -} \ No newline at end of file +}