import jimp from "jimp" ; import sharp from "sharp" ; import { Editor } from "./lib/editor" ; import { BitDepth , encode } from "fast-png" ; import fs from "fs" ; import { PNG } from "pngjs" ; import { Float16Array , isFloat16Array , getFloat16 , setFloat16 , hfround } from "@petamoriken/float16" ; import { TextureComponents , TextureDataConverter } from "./lib/designer/texturedataconverter" ; import { TextureDataType } from "./lib/designer/gl" ; import { DesignerNode } from "./lib/designer/designernode" ; import { Designer } from "./lib/designer" ; import AdmZip from "adm-zip" ; import path from "path" ; import electron from "electron" ; export enum ImageFileType { Png = "png" , Jpg = "jpg" , Tga = "tga" , Tif = "tif" } export enum ColorSpace { Linear = "linear" , sRGB = "srgb" } export enum OutputType { Folder , Zip , UnityPackage } export enum TextureTransform { InvertX , InvertY , InvertZ , LinearizeRoughness } export class ExportTextureSettings { channelName : string = "" ; colorSpace : ColorSpace = ColorSpace . Linear ; fileType : ImageFileType = ImageFileType . Png ; transforms : TextureTransform [ ] = [ ] ; } export class ExportSettings { name : string = "" ; filePattern : string = "{project}_{file}" ; outputType : OutputType = OutputType . Zip ; outputPath : string = "" ; } export class Exporter { async export ( editor , settings ) { const designer = editor . designer ; const conv = new TextureDataConverter ( editor . designer . gl ) ; console . log ( editor . textureChannels ) ; const exportNodes = this . gatherExportNodes ( designer , editor ) ; console . log ( exportNodes ) ; let files = new Map < string , ArrayBuffer > ( ) ; for ( const node of exportNodes ) { const channelNode = node . node ; const pixelData = conv . getData ( channelNode . tex , designer . width , designer . height , node . dataType , node . components , true ) ; const fileData = exportNodeAsPng ( node , pixelData , designer . width , designer . height ) ; const projectName = settings . name . replace ( " " , "_" ) . replace ( /[^0-9a-zA-Z_]/g , "" ) ; const fileName = interpolateString ( settings . filePattern , { project : projectName , name : node . name } ) ; files . set ( fileName + ".png" , fileData ) ; } if ( settings . outputType == OutputType . Folder ) { exportFilesToFolder ( files , settings . outputPath ) ; } else { exportFilesToZip ( files , settings . outputPath ) ; electron . remote . shell . showItemInFolder ( settings . outputPath ) ; } } calculateFileName ( fileName , project ) { return null ; } gatherExportNodes ( designer , editor ) { let nodes = designer . nodes . filter ( x => x . typeName === "output" ) ; let exportNodes = [ ] ; for ( const node of nodes ) { let exportNode = new ExportNode ( ) ; exportNode . node = node ; for ( const prop of node . properties ) { if ( prop . name === "name" ) { exportNode . name = prop . getValue ( ) ; } if ( prop . name === "precision" ) { const precision = prop . getValue ( ) as number ; if ( precision === 0 ) exportNode . dataType = TextureDataType . Uint8 ; if ( precision === 1 ) exportNode . dataType = TextureDataType . Uint16 ; } if ( prop . name === "components" ) { const components = prop . getValue ( ) as number ; if ( components === 0 ) exportNode . components = TextureComponents . RGBA ; if ( components === 1 ) exportNode . components = TextureComponents . RGB ; if ( components === 2 ) exportNode . components = TextureComponents . R ; if ( components === 3 ) exportNode . components = TextureComponents . G ; if ( components === 4 ) exportNode . components = TextureComponents . B ; if ( components === 5 ) exportNode . components = TextureComponents . A ; } } if ( ! exportNode . name || exportNode . name === "" ) { exportNode . name = getChannelNameForNode ( exportNode . node , editor ) ; } exportNodes . push ( exportNode ) ; } return exportNodes ; } } function getChannelNameForNode ( node , editor ) { const channels = editor . textureChannels ; for ( const key of channels . keys ( ) ) { if ( channels . get ( key ) === node ) return key ; } return "" ; } function exportNodeAsPng ( node , pixelData , width , height ) { try { let bitDepth = 8 ; if ( node . dataType === TextureDataType . Uint16 ) bitDepth = 16 ; let channels = 4 ; if ( node . components === TextureComponents . RGB ) channels = 3 ; if ( node . components === TextureComponents . R || node . components === TextureComponents . G || node . components === TextureComponents . B || node . components === TextureComponents . A ) channels = 1 ; const bytes = encode ( { data : bitDepth == 8 ? new Uint8Array ( pixelData ) : new Uint16Array ( pixelData ) , width : width , height : height , depth : bitDepth , channels : channels } ) ; return bytes . buffer ; } catch ( error ) { console . log ( "error saving file" ) ; console . log ( error ) ; } } class ExportNode { node : DesignerNode ; components : TextureComponents = TextureComponents . RGBA ; dataType : TextureDataType = TextureDataType . Uint8 ; name : string = "" ; } function exportFilesToFolder ( files , folderPath ) { for ( let fileName of files . keys ( ) ) { console . log ( fileName ) ; const exportPath = path . join ( folderPath , fileName ) ; const bytes = files . get ( fileName ) ; fs . writeFile ( exportPath , new Uint8Array ( bytes ) , function ( err ) { if ( err ) alert ( "Error exporting texture: " + err ) ; } ) ; } } function exportFilesToZip ( files , zipFilePath ) { const zip = new AdmZip ( ) ; for ( let fileName of files . keys ( ) ) { const bytes = files . get ( fileName ) ; console . log ( bytes ) ; zip . addFile ( fileName , Buffer . from ( bytes ) ) ; } zip . writeZip ( zipFilePath ) ; } function convertRange ( data ) { const UINT_MAX = 65535.0 ; const VALUE_MAX = 15360.0 ; const floatData = new Float16Array ( data . buffer ) ; for ( let i = 0 ; i < data . length ; i ++ ) { data [ i ] = floatData [ i ] * UINT_MAX ; } } function interpolateString ( text , exportData ) { if ( ! text ) return "" ; text = text . replace ( /\${([a-z]+)}/g , ( wholeMatch , path ) : string => { const data = exportData [ path ] ; if ( ! data ) return "" ; return data ; } ) ; return text ; }