From 34ff59e82f996d5612a359be257e7578fa3a8089 Mon Sep 17 00:00:00 2001 From: luboslenco Date: Wed, 20 Nov 2019 16:36:08 +0100 Subject: [PATCH] Texture export presets --- Assets/painter/export_presets/generic.json | 9 + .../export_presets/preset_generic.json | 33 --- Assets/painter/export_presets/unity.json | 7 + Assets/painter/export_presets/unreal.json | 7 + Assets/painter/export_presets/xplane.json | 7 + Sources/arm/App.hx | 8 +- Sources/arm/Context.hx | 1 - Sources/arm/Plugin.hx | 2 - Sources/arm/Project.hx | 6 +- Sources/arm/io/ExportMesh.hx | 11 + .../arm/io/{Exporter.hx => ExportTexture.hx} | 217 +++++++++--------- .../arm/io/{Importer.hx => ImportAsset.hx} | 6 +- Sources/arm/io/ImportFont.hx | 7 +- Sources/arm/io/ImportMesh.hx | 6 +- Sources/arm/io/ImportPlugin.hx | 2 +- Sources/arm/ui/BoxExport.hx | 187 ++++++++++++--- Sources/arm/ui/BoxPreferences.hx | 16 +- Sources/arm/ui/TabMaterials.hx | 4 +- Sources/arm/ui/TabTextures.hx | 4 +- Sources/arm/ui/UIBox.hx | 5 +- Sources/arm/ui/UIMenu.hx | 6 +- Sources/arm/ui/UITrait.hx | 11 +- Sources/arm/util/RenderUtil.hx | 6 +- 23 files changed, 343 insertions(+), 225 deletions(-) create mode 100644 Assets/painter/export_presets/generic.json delete mode 100644 Assets/painter/export_presets/preset_generic.json create mode 100644 Assets/painter/export_presets/unity.json create mode 100644 Assets/painter/export_presets/unreal.json create mode 100644 Assets/painter/export_presets/xplane.json create mode 100644 Sources/arm/io/ExportMesh.hx rename Sources/arm/io/{Exporter.hx => ExportTexture.hx} (59%) rename Sources/arm/io/{Importer.hx => ImportAsset.hx} (89%) diff --git a/Assets/painter/export_presets/generic.json b/Assets/painter/export_presets/generic.json new file mode 100644 index 00000000..65026113 --- /dev/null +++ b/Assets/painter/export_presets/generic.json @@ -0,0 +1,9 @@ +{ + "textures": [ + { "name": "base", "channels": ["base_r", "base_g", "base_b", "1.0"] }, + { "name": "nor", "channels": ["nor_r", "nor_g", "nor_b", "1.0"] }, + { "name": "occ", "channels": ["occ", "occ", "occ", "1.0"] }, + { "name": "rough", "channels": ["rough", "rough", "rough", "1.0"] }, + { "name": "metal", "channels": ["metal", "metal", "metal", "1.0"] } + ] +} diff --git a/Assets/painter/export_presets/preset_generic.json b/Assets/painter/export_presets/preset_generic.json deleted file mode 100644 index 5f37050f..00000000 --- a/Assets/painter/export_presets/preset_generic.json +++ /dev/null @@ -1,33 +0,0 @@ -{ - "name": "Generic", - "textures": [ - { - "name": "base", - "channels": ["base_r", "base_g", "base_b", "1.0"] - }, - { - "name": "opac", - "channels": ["opac", "opac", "opac", "1.0"] - }, - { - "name": "nor", - "channels": ["nor_r", "nor_g", "nor_b", "1.0"] - }, - { - "name": "occ", - "channels": ["occ", "occ", "occ", "1.0"] - }, - { - "name": "rough", - "channels": ["rough", "rough", "rough", "1.0"] - }, - { - "name": "met", - "channels": ["met", "met", "met", "1.0"] - }, - { - "name": "height", - "channels": ["height", "height", "height", "1.0"] - } - ] -} diff --git a/Assets/painter/export_presets/unity.json b/Assets/painter/export_presets/unity.json new file mode 100644 index 00000000..fe1eab55 --- /dev/null +++ b/Assets/painter/export_presets/unity.json @@ -0,0 +1,7 @@ +{ + "textures": [ + { "name": "base", "channels": ["base_r", "base_g", "base_b", "1.0"] }, + { "name": "nor", "channels": ["nor_r", "nor_g", "nor_b", "1.0"] }, + { "name": "mos", "channels": ["metal", "occ", "smooth", "1.0"] } + ] +} diff --git a/Assets/painter/export_presets/unreal.json b/Assets/painter/export_presets/unreal.json new file mode 100644 index 00000000..75404054 --- /dev/null +++ b/Assets/painter/export_presets/unreal.json @@ -0,0 +1,7 @@ +{ + "textures": [ + { "name": "base", "channels": ["base_r", "base_g", "base_b", "1.0"] }, + { "name": "nor", "channels": ["nor_r", "nor_g", "nor_b", "1.0"] }, + { "name": "orm", "channels": ["occ", "rough", "metal", "1.0"] } + ] +} diff --git a/Assets/painter/export_presets/xplane.json b/Assets/painter/export_presets/xplane.json new file mode 100644 index 00000000..8f973278 --- /dev/null +++ b/Assets/painter/export_presets/xplane.json @@ -0,0 +1,7 @@ +{ + "textures": [ + { "name": "base", "channels": ["base_r", "base_g", "base_b", "1.0"] }, + { "name": "nor", "channels": ["nor_r", "nor_g", "metal", "smooth"] }, + { "name": "occ", "channels": ["occ", "occ", "occ", "1.0"] } + ] +} diff --git a/Sources/arm/App.hx b/Sources/arm/App.hx index 9c483589..a3708637 100644 --- a/Sources/arm/App.hx +++ b/Sources/arm/App.hx @@ -17,7 +17,7 @@ import arm.ui.UIView2D; import arm.ui.UIMenu; import arm.ui.UIBox; import arm.ui.UIFiles; -import arm.io.Importer; +import arm.io.ImportAsset; import arm.sys.Path; import arm.util.RenderUtil; import arm.util.ViewportUtil; @@ -158,7 +158,7 @@ class App { Project.projectNew(); // Spawn terrain plane #end if (fileArg != "") { - Importer.run(fileArg); + ImportAsset.run(fileArg); // if (Path.isMesh(fileArg)) { // UITrait.inst.toggleDistractFree(); // } @@ -172,7 +172,7 @@ class App { UITrait.inst.textureExportPath = Krom.getArg(2); if (Krom.getArgCount() > 3) { UIFiles.filename = Krom.getArg(3); - UITrait.inst.outputType = 1; // occ-rough-met + // hpreset.position = presets.indexOf("unreal") } } }); @@ -365,7 +365,7 @@ class App { if (!wait) { dropX = mouse.x; dropY = mouse.y; - Importer.run(dropPath, dropX, dropY); + ImportAsset.run(dropPath, dropX, dropY); dropPath = ""; } } diff --git a/Sources/arm/Context.hx b/Sources/arm/Context.hx index aea42d2f..1735c0a9 100644 --- a/Sources/arm/Context.hx +++ b/Sources/arm/Context.hx @@ -12,7 +12,6 @@ import arm.util.ParticleUtil; import arm.ui.UITrait; import arm.ui.UINodes; import arm.ui.UIFiles; -import arm.io.Importer; import arm.node.MaterialParser; import arm.Tool; import arm.Project; diff --git a/Sources/arm/Plugin.hx b/Sources/arm/Plugin.hx index 673764ac..948dce66 100644 --- a/Sources/arm/Plugin.hx +++ b/Sources/arm/Plugin.hx @@ -78,8 +78,6 @@ class ArmBridge { public static var Path = arm.sys.Path; public static var NodesMaterial = arm.node.NodesMaterial; public static var Material = arm.node.Material; - public static var Exporter = arm.io.Exporter; - public static var Importer = arm.io.Importer; public static var UITrait = arm.ui.UITrait; public static var UINodes = arm.ui.UINodes; public static var UIFiles = arm.ui.UIFiles; diff --git a/Sources/arm/Project.hx b/Sources/arm/Project.hx index 7d99c67f..afa80a61 100644 --- a/Sources/arm/Project.hx +++ b/Sources/arm/Project.hx @@ -20,7 +20,7 @@ import arm.data.LayerSlot; import arm.data.BrushSlot; import arm.data.MaterialSlot; import arm.node.MaterialParser; -import arm.io.Importer; +import arm.io.ImportAsset; import arm.io.ImportArm; import arm.io.ImportBlend; import arm.io.ImportMesh; @@ -258,7 +258,7 @@ class Project { UIBox.show = false; App.redrawUI(); UIFiles.show(Path.meshFormats.join(","), false, function(path:String) { - Importer.run(path); + ImportAsset.run(path); }); } } @@ -276,7 +276,7 @@ class Project { public static function importAsset(filters:String = null) { if (filters == null) filters = Path.textureFormats.join(",") + "," + Path.meshFormats.join(","); UIFiles.show(filters, false, function(path:String) { - Importer.run(path); + ImportAsset.run(path); }); } } diff --git a/Sources/arm/io/ExportMesh.hx b/Sources/arm/io/ExportMesh.hx new file mode 100644 index 00000000..af200e9c --- /dev/null +++ b/Sources/arm/io/ExportMesh.hx @@ -0,0 +1,11 @@ +package arm.io; + +import arm.ui.UITrait; + +class ExportMesh { + + public static function run(path:String) { + if (UITrait.inst.exportMeshFormat == 0) ExportObj.run(path); + else ExportArm.run(path); + } +} diff --git a/Sources/arm/io/Exporter.hx b/Sources/arm/io/ExportTexture.hx similarity index 59% rename from Sources/arm/io/Exporter.hx rename to Sources/arm/io/ExportTexture.hx index 28c7c4d7..d4fc9244 100644 --- a/Sources/arm/io/Exporter.hx +++ b/Sources/arm/io/ExportTexture.hx @@ -9,45 +9,13 @@ import arm.format.JpgWriter; import arm.format.PngWriter; import arm.format.PngTools; import arm.ui.UITrait; -import arm.ui.UIBox; import arm.ui.UIFiles; +import arm.ui.BoxExport; using StringTools; -class Exporter { +class ExportTexture { - static inline var gamma = 1.0 / 2.2; - - static function writeTexture(file:String, pixels:Bytes, type = 1, off = 0) { - var out = new BytesOutput(); - var res = Config.getTextureRes(); - var bitsHandle = UITrait.inst.bitsHandle.position; - var bits = bitsHandle == 0 ? 8 : bitsHandle == 1 ? 16 : 32; - if (bits > 8) { // 16/32bit - var writer = new ExrWriter(out, res, res, pixels, bits, type, off); - } - else if (UITrait.inst.formatType == 0) { - var writer = new PngWriter(out); - var data = - type == 1 ? - PngTools.build32RGB1(res, res, pixels) : - type == 2 ? - PngTools.build32RRR1(res, res, pixels, off) : - PngTools.build32RGBA(res, res, pixels); - writer.write(data); - } - else { - var writer = new JpgWriter(out); - writer.write({ - width: res, - height: res, - quality: UITrait.inst.formatQuality, - pixels: pixels - }, type, off); - } - Krom.fileSaveBytes(file, out.getBytes().getData()); - } - - public static function exportTextures(path:String) { + public static function run(path:String) { #if arm_debug var timer = iron.system.Time.realTime(); #end @@ -161,92 +129,70 @@ class Exporter { } } - var pixels:Bytes = null; + var pixpaint:Bytes = null; + var pixpaint_nor:Bytes = null; + var pixpaint_pack:Bytes = null; + var preset = BoxExport.preset; + var pix:Bytes = null; - if (UITrait.inst.isBase || UITrait.inst.isOpac) { - pixels = texpaint.getPixels(); // bgra - if (UITrait.inst.isBase && UITrait.inst.isBaseSpace == 1) { - for (i in 0...Std.int(pixels.length / 4)) { - pixels.set(i * 4 , Std.int(Math.pow(pixels.get(i * 4 ) / 255, gamma) * 255)); - pixels.set(i * 4 + 1, Std.int(Math.pow(pixels.get(i * 4 + 1) / 255, gamma) * 255)); - pixels.set(i * 4 + 2, Std.int(Math.pow(pixels.get(i * 4 + 2) / 255, gamma) * 255)); - } + for (t in preset.textures) { + for (c in t.channels) { + if ((c == "base_r" || c == "base_g" || c == "base_b" || c == "opac") && pixpaint == null) pixpaint = texpaint.getPixels(); + else if ((c == "nor_r" || c == "nor_g" || c == "nor_b") && pixpaint_nor == null) pixpaint_nor = texpaint_nor.getPixels(); + else if ((c == "occ" || c == "rough" || c == "metal" || c == "height" || c == "smooth") && pixpaint_pack == null) pixpaint_pack = texpaint_pack.getPixels(); } - if (UITrait.inst.isOpac && UITrait.inst.isOpacSpace == 1) { - for (i in 0...Std.int(pixels.length / 4)) { - pixels.set(i * 4 + 3, Std.int(Math.pow(pixels.get(i * 4 + 3) / 255, gamma) * 255)); - } - } - - if (UITrait.inst.isBase) writeTexture(path + "/" + f + "_base" + ext, pixels); - if (UITrait.inst.isOpac) writeTexture(path + "/" + f + "_opac" + ext, pixels, 2, 3); } - if (UITrait.inst.isNor) { - pixels = texpaint_nor.getPixels(); - if (UITrait.inst.isNorSpace == 1) { - for (i in 0...Std.int(pixels.length / 4)) { - pixels.set(i * 4 , Std.int(Math.pow(pixels.get(i * 4 ) / 255, gamma) * 255)); - pixels.set(i * 4 + 1, Std.int(Math.pow(pixels.get(i * 4 + 1) / 255, gamma) * 255)); - pixels.set(i * 4 + 2, Std.int(Math.pow(pixels.get(i * 4 + 2) / 255, gamma) * 255)); - } + for (t in preset.textures) { + var c = t.channels; + var singleChannel = c[0] == c[1] && c[1] == c[2] && c[3] == "1.0"; + if (c[0] == "base_r" && c[1] == "base_g" && c[2] == "base_b" && c[3] == "1.0") { + writeTexture(path + "/" + f + "_" + t.name + ext, pixpaint, 1); } - writeTexture(path + "/" + f + "_nor" + ext, pixels); - } - - if (UITrait.inst.isOcc || UITrait.inst.isRough || UITrait.inst.isMet || UITrait.inst.isHeight) { - pixels = texpaint_pack.getPixels(); // occ, rough, met, height - if (UITrait.inst.isOcc && UITrait.inst.isOccSpace == 1) { - for (i in 0...Std.int(pixels.length / 4)) { - pixels.set(i * 4 + 2, Std.int(Math.pow(pixels.get(i * 4 + 2) / 255, gamma) * 255)); - } + else if (c[0] == "nor_r" && c[1] == "nor_g" && c[2] == "nor_b" && c[3] == "1.0") { + writeTexture(path + "/" + f + "_" + t.name + ext, pixpaint_nor, 1); } - if (UITrait.inst.isRough && UITrait.inst.isRoughSpace == 1) { - for (i in 0...Std.int(pixels.length / 4)) { - pixels.set(i * 4 + 1, Std.int(Math.pow(pixels.get(i * 4 + 1) / 255, gamma) * 255)); - } + else if (c[0] == "occ" && c[1] == "rough" && c[2] == "metal" && c[3] == "1.0") { + writeTexture(path + "/" + f + "_" + t.name + ext, pixpaint_pack, 1); } - if (UITrait.inst.isMet && UITrait.inst.isMetSpace == 1) { - for (i in 0...Std.int(pixels.length / 4)) { - pixels.set(i * 4 , Std.int(Math.pow(pixels.get(i * 4 ) / 255, gamma) * 255)); - } + else if (singleChannel && c[0] == "occ") { + writeTexture(path + "/" + f + "_" + t.name + ext, pixpaint_pack, 2, 0); } - if (UITrait.inst.isHeight && UITrait.inst.isHeightSpace == 1) { - for (i in 0...Std.int(pixels.length / 4)) { - pixels.set(i * 4 + 3, Std.int(Math.pow(pixels.get(i * 4 + 3) / 255, gamma) * 255)); - } + else if (singleChannel && c[0] == "rough") { + writeTexture(path + "/" + f + "_" + t.name + ext, pixpaint_pack, 2, 1); } - - if (UITrait.inst.outputType == 0) { - if (UITrait.inst.isOcc) writeTexture(path + "/" + f + "_occ" + ext, pixels, 2, 0); - if (UITrait.inst.isRough) writeTexture(path + "/" + f + "_rough" + ext, pixels, 2, 1); - if (UITrait.inst.isMet) writeTexture(path + "/" + f + "_met" + ext, pixels, 2, 2); + else if (singleChannel && c[0] == "metal") { + writeTexture(path + "/" + f + "_" + t.name + ext, pixpaint_pack, 2, 2); + } + else if (singleChannel && c[0] == "height") { + writeTexture(path + "/" + f + "_" + t.name + ext, pixpaint_pack, 2, 3); + } + else if (singleChannel && c[0] == "opac") { + writeTexture(path + "/" + f + "_" + t.name + ext, pixpaint, 2, 3); } else { - if (UITrait.inst.isOcc || UITrait.inst.isRough || UITrait.inst.isMet) { - if (UITrait.inst.outputType == 1) { // Unreal 4 - writeTexture(path + "/" + f + "_orm" + ext, pixels); - } - else { // Unity 5 - var pixels2 = Bytes.alloc(pixels.length); - // 8bit only - for (i in 0...Std.int(pixels.length / 4)) { - pixels2.set(i * 4 , pixels.get(i * 4 + 2)); - pixels2.set(i * 4 + 1, pixels.get(i * 4 )); - // pixels2.set(i * 4 + 2, 0); - pixels2.set(i * 4 + 3, 255 - pixels.get(i * 4 + 1)); - } - writeTexture(path + "/" + f + "_mos" + ext, pixels2, 3); - } + if (pix == null) pix = Bytes.alloc(textureSize * textureSize * 4 * Std.int(bits / 8)); + for (i in 0...4) { + var c = t.channels[i]; + if (c == "base_r") copyChannel(pixpaint, 0, pix, i); // copyChannelGamma + else if (c == "base_g") copyChannel(pixpaint, 1, pix, i); // copyChannelGamma + else if (c == "base_b") copyChannel(pixpaint, 2, pix, i); // copyChannelGamma + else if (c == "height") copyChannel(pixpaint_pack, 4, pix, i); + else if (c == "metal") copyChannel(pixpaint_pack, 3, pix, i); + else if (c == "nor_r") copyChannel(pixpaint_nor, 0, pix, i); + else if (c == "nor_g") copyChannel(pixpaint_nor, 1, pix, i); + else if (c == "nor_b") copyChannel(pixpaint_nor, 2, pix, i); + else if (c == "occ") copyChannel(pixpaint_pack, 0, pix, i); + else if (c == "opac") copyChannel(pixpaint, 3, pix, i); + else if (c == "rough") copyChannel(pixpaint_pack, 1, pix, i); + else if (c == "smooth") copyChannelInv(pixpaint_pack, 1, pix, i); + else if (c == "0.0") setChannel(0, pix, i); + else if (c == "1.0") setChannel(255, pix, i); } + writeTexture(path + "/" + f + "_" + t.name + ext, pix); } - - if (UITrait.inst.isHeight) writeTexture(path + "/" + f + "_height" + ext, pixels, 2, 3); } - // if (UITrait.inst.isEmis) Krom.fileSaveBytes(path + "/tex_emis" + ext, bo.getBytes().getData()); - // if (UITrait.inst.isSubs) Krom.fileSaveBytes(path + "/tex_subs" + ext, bo.getBytes().getData()); - #if arm_debug trace("Textures exported in " + (iron.system.Time.realTime() - timer)); #end @@ -254,19 +200,64 @@ class Exporter { Log.showMessage("Textures exported."); } - public static function exportMesh(path:String) { - if (UITrait.inst.exportMeshFormat == 0) ExportObj.run(path); - else ExportArm.run(path); + static function writeTexture(file:String, pixels:Bytes, type = 1, off = 0) { + var out = new BytesOutput(); + var res = Config.getTextureRes(); + var bitsHandle = UITrait.inst.bitsHandle.position; + var bits = bitsHandle == 0 ? 8 : bitsHandle == 1 ? 16 : 32; + if (bits > 8) { // 16/32bit + var writer = new ExrWriter(out, res, res, pixels, bits, type, off); + } + else if (UITrait.inst.formatType == 0) { + var writer = new PngWriter(out); + var data = + type == 1 ? + PngTools.build32RGB1(res, res, pixels) : + type == 2 ? + PngTools.build32RRR1(res, res, pixels, off) : + PngTools.build32RGBA(res, res, pixels); + writer.write(data); + } + else { + var writer = new JpgWriter(out); + writer.write({ + width: res, + height: res, + quality: UITrait.inst.formatQuality, + pixels: pixels + }, type, off); + } + Krom.fileSaveBytes(file, out.getBytes().getData()); } - public static function exportMaterial(path:String) { - ExportArm.runMaterial(path); + static function copyChannel(from:Bytes, fromChannel:Int, to:Bytes, toChannel:Int) { + for (i in 0...Std.int(to.length / 4)) { + to.set(i * 4 + toChannel, from.get(i * 4 + fromChannel)); + } + } + + static inline var gamma = 1.0 / 2.2; + static function copyChannelGamma(from:Bytes, fromChannel:Int, to:Bytes, toChannel:Int) { + for (i in 0...Std.int(to.length / 4)) { + to.set(i * 4 + toChannel, Std.int(Math.pow(from.get(i * 4 + fromChannel) / 255, gamma) * 255)); + } + } + + static function copyChannelInv(from:Bytes, fromChannel:Int, to:Bytes, toChannel:Int) { + for (i in 0...Std.int(to.length / 4)) { + to.set(i * 4 + toChannel, 255 - from.get(i * 4 + fromChannel)); + } + } + + static function setChannel(value:Int, to:Bytes, toChannel:Int) { + for (i in 0...Std.int(to.length / 4)) { + to.set(i * 4 + toChannel, value); + } } } typedef TExportPreset = { - public var name:String; - public var textures:TExportPresetTexture; + public var textures:Array; } typedef TExportPresetTexture = { diff --git a/Sources/arm/io/Importer.hx b/Sources/arm/io/ImportAsset.hx similarity index 89% rename from Sources/arm/io/Importer.hx rename to Sources/arm/io/ImportAsset.hx index 94d648c6..e6d5d927 100644 --- a/Sources/arm/io/Importer.hx +++ b/Sources/arm/io/ImportAsset.hx @@ -18,11 +18,7 @@ import arm.Project; import arm.Tool; using StringTools; -class Importer { - - public static var fontList = ["default.ttf"]; - public static var fontMap = new Map(); - public static var clearLayers = true; +class ImportAsset { public static function run(path:String, dropX = -1.0, dropY = -1.0) { // Mesh diff --git a/Sources/arm/io/ImportFont.hx b/Sources/arm/io/ImportFont.hx index 65f5ac84..66310f5b 100644 --- a/Sources/arm/io/ImportFont.hx +++ b/Sources/arm/io/ImportFont.hx @@ -5,13 +5,16 @@ import iron.data.Data; class ImportFont { + public static var fontList = ["default.ttf"]; + public static var fontMap = new Map(); + public static function run(path:String) { Data.getFont(path, function(font:Font) { var ar = path.split("/"); ar = ar[ar.length - 1].split("\\"); var name = ar[ar.length - 1]; - Importer.fontList.push(name); - Importer.fontMap.set(name, font); + fontList.push(name); + fontMap.set(name, font); }); } } diff --git a/Sources/arm/io/ImportMesh.hx b/Sources/arm/io/ImportMesh.hx index 034a7b87..02a233f3 100644 --- a/Sources/arm/io/ImportMesh.hx +++ b/Sources/arm/io/ImportMesh.hx @@ -20,13 +20,15 @@ using StringTools; class ImportMesh { + public static var clearLayers = true; + public static function run(path:String, _clearLayers = true) { if (!Path.isMesh(path)) { Log.showError(Strings.error1); return; } - Importer.clearLayers = _clearLayers; + clearLayers = _clearLayers; #if arm_debug var timer = iron.system.Time.realTime(); @@ -179,7 +181,7 @@ class ImportMesh { Data.deleteMesh(handle); } - if (Importer.clearLayers) { + if (clearLayers) { while (Project.layers.length > 1) { var l = Project.layers.pop(); l.unload(); } Context.setLayer(Project.layers[0]); iron.App.notifyOnRender(Layers.initLayers); diff --git a/Sources/arm/io/ImportPlugin.hx b/Sources/arm/io/ImportPlugin.hx index 52d67eb5..216aed00 100644 --- a/Sources/arm/io/ImportPlugin.hx +++ b/Sources/arm/io/ImportPlugin.hx @@ -15,6 +15,6 @@ class ImportPlugin { var dstPath = Path.data() + Path.sep + "plugins" + Path.sep + filename; File.copy(path, dstPath); // Copy to plugin folder arm.ui.BoxPreferences.files = null; // Refresh file list - Log.showMessage("Plugin '" + filename + "' installed."); + Log.showMessage("Plugin '" + filename + "' imported."); } } diff --git a/Sources/arm/ui/BoxExport.hx b/Sources/arm/ui/BoxExport.hx index ff527bcf..13872f37 100644 --- a/Sources/arm/ui/BoxExport.hx +++ b/Sources/arm/ui/BoxExport.hx @@ -1,16 +1,32 @@ package arm.ui; +import haxe.io.Bytes; import zui.Zui; import zui.Id; -import zui.Id; import arm.util.UVUtil; -import arm.io.Exporter; +import arm.io.ExportMesh; +import arm.io.ExportTexture; +import arm.sys.Path; +import arm.sys.File; +using StringTools; class BoxExport { + public static var htab = Id.handle(); + public static var hpreset = Id.handle(); + public static var files:Array = null; + public static var preset:TExportPreset = null; + static var channels = ["base_r", "base_g", "base_b", "height", "metal", "nor_r", "nor_g", "nor_b", "occ", "opac", "rough", "smooth", "0.0", "1.0"]; + public static function showTextures() { UIBox.showCustom(function(ui:Zui) { - var htab = Id.handle(); + + if (files == null) fetchPresets(); + if (preset == null) { + parsePreset(); + @:privateAccess hpreset.children = null; + } + if (ui.tab(htab, "Export Textures")) { ui.row([1/2, 1/2]); ui.combo(UITrait.inst.resHandle, ["128", "256", "512", "1K", "2K", "4K", "8K", "16K"], "Res", true); @@ -41,7 +57,7 @@ class BoxExport { ui.enabled = true; ui.row([1/2, 1/2]); UITrait.inst.layersExport = ui.combo(Id.handle({position: UITrait.inst.layersExport}), ["Visible", "Selected"], "Layers", true); - UITrait.inst.outputType = ui.combo(Id.handle({position: UITrait.inst.outputType}), ["Generic", "Unreal 4", "Unity 5"], "Preset", true); + ui.combo(hpreset, files, "Preset", true); @:privateAccess ui.endElement(); @@ -59,36 +75,104 @@ class BoxExport { } if (ui.isHovered) ui.tooltip("Export texture files (" + Config.keymap.file_export_textures + ")"); } - if (ui.tab(htab, "Channels")) { - ui.row([1/2, 1/2]); - UITrait.inst.isBase = ui.check(Id.handle({selected: UITrait.inst.isBase}), "Base Color"); - UITrait.inst.isBaseSpace = ui.combo(Id.handle({position: UITrait.inst.isBaseSpace}), ["linear", "srgb"], "Space"); - ui.row([1/2, 1/2]); - UITrait.inst.isOpac = ui.check(Id.handle({selected: UITrait.inst.isOpac}), "Opacity"); - UITrait.inst.isOpacSpace = ui.combo(Id.handle({position: UITrait.inst.isOpacSpace}), ["linear", "srgb"], "Space"); - ui.row([1/2, 1/2]); - UITrait.inst.isOcc = ui.check(Id.handle({selected: UITrait.inst.isOcc}), "Occlusion"); - UITrait.inst.isOccSpace = ui.combo(Id.handle({position: UITrait.inst.isOccSpace}), ["linear", "srgb"], "Space"); - ui.row([1/2, 1/2]); - UITrait.inst.isRough = ui.check(Id.handle({selected: UITrait.inst.isRough}), "Roughness"); - UITrait.inst.isRoughSpace = ui.combo(Id.handle({position: UITrait.inst.isRoughSpace}), ["linear", "srgb"], "Space"); - ui.row([1/2, 1/2]); - UITrait.inst.isMet = ui.check(Id.handle({selected: UITrait.inst.isMet}), "Metallic"); - UITrait.inst.isMetSpace = ui.combo(Id.handle({position: UITrait.inst.isMetSpace}), ["linear", "srgb"], "Space"); - ui.row([1/2, 1/2]); - UITrait.inst.isNor = ui.check(Id.handle({selected: UITrait.inst.isNor}), "Normal"); - UITrait.inst.isNorSpace = ui.combo(Id.handle({position: UITrait.inst.isNorSpace}), ["linear", "srgb"], "Space"); - ui.row([1/2, 1/2]); - UITrait.inst.isEmis = ui.check(Id.handle({selected: UITrait.inst.isEmis}), "Emission"); - UITrait.inst.isEmisSpace = ui.combo(Id.handle({position: UITrait.inst.isEmisSpace}), ["linear", "srgb"], "Space"); - ui.row([1/2, 1/2]); - UITrait.inst.isHeight = ui.check(Id.handle({selected: UITrait.inst.isHeight}), "Height"); - UITrait.inst.isHeightSpace = ui.combo(Id.handle({position: UITrait.inst.isHeightSpace}), ["linear", "srgb"], "Space"); - ui.row([1/2, 1/2]); - UITrait.inst.isSubs = ui.check(Id.handle({selected: UITrait.inst.isSubs}), "Subsurface"); - UITrait.inst.isSubsSpace = ui.combo(Id.handle({position: UITrait.inst.isSubsSpace}), ["linear", "srgb"], "Space"); + + if (ui.tab(htab, "Presets")) { + ui.row([3/5, 1/5, 1/5]); + + ui.combo(hpreset, files, "Preset"); + if (hpreset.changed) preset = null; + + if (ui.button("New")) { + UIBox.showCustom(function(ui:Zui) { + if (ui.tab(Id.handle(), "New Preset")) { + ui.row([1/2, 1/2]); + var presetName = ui.textInput(Id.handle({text: "new_preset"}), "Name"); + if (ui.button("OK") || ui.isReturnDown) { + newPreset(presetName); + fetchPresets(); + preset = null; + hpreset.position = files.indexOf(presetName); + UIBox.show = false; + BoxExport.htab.position = 1; // Presets + BoxExport.showTextures(); + } + } + }); + } + + if (ui.button("Import")) { + UIFiles.show("json", false, function(path:String) { + path = path.toLowerCase(); + if (path.endsWith(".json")) { + var filename = path.substr(path.lastIndexOf(Path.sep) + 1); + var dstPath = Path.data() + Path.sep + "export_presets" + Path.sep + filename; + File.copy(path, dstPath); // Copy to presets folder + fetchPresets(); + preset = null; + hpreset.position = files.indexOf(filename.substr(0, filename.length - 5)); // Strip .json + Log.showMessage("Preset '" + filename + "' imported."); + } + else Log.showError(Strings.error1); + }); + } + + if (preset == null) { + parsePreset(); + @:privateAccess hpreset.children = null; + } + + // Texture list + ui.separator(10, false); + ui.row([1/5,1/5,1/5,1/5,1/5]); + ui.text("Texture"); + ui.text("R"); + ui.text("G"); + ui.text("B"); + ui.text("A"); + ui.changed = false; + for (i in 0...preset.textures.length) { + var t = preset.textures[i]; + ui.row([1/5,1/5,1/5,1/5,1/5]); + var htex = hpreset.nest(i, {text: t.name}); + t.name = ui.textInput(htex); + + if (ui.isHovered && ui.inputReleasedR) { + UIMenu.draw(function(ui:Zui) { + ui.fill(0, 0, @:privateAccess ui._w / ui.SCALE(), ui.t.ELEMENT_H * 2, ui.t.SEPARATOR_COL); + ui.text(t.name, Right, ui.t.HIGHLIGHT_COL); + if (ui.button("Delete", Left)) { + preset.textures.remove(t); + savePreset(); + } + }); + } + + var hr = htex.nest(0, {position: channels.indexOf(t.channels[0])}); + var hg = htex.nest(1, {position: channels.indexOf(t.channels[1])}); + var hb = htex.nest(2, {position: channels.indexOf(t.channels[2])}); + var ha = htex.nest(3, {position: channels.indexOf(t.channels[3])}); + + ui.combo(hr, channels, "R"); + if (hr.changed) t.channels[0] = channels[hr.position]; + ui.combo(hg, channels, "G"); + if (hg.changed) t.channels[1] = channels[hg.position]; + ui.combo(hb, channels, "B"); + if (hb.changed) t.channels[2] = channels[hb.position]; + ui.combo(ha, channels, "A"); + if (ha.changed) t.channels[3] = channels[ha.position]; + } + + if (ui.changed) { + savePreset(); + } + + ui.row([1/8]); + if (ui.button("Add")) { + preset.textures.push({name: "base", channels: ["base_r", "base_g", "base_b", "1.0"]}); + @:privateAccess hpreset.children = null; + savePreset(); + } } - if (ui.tab(htab, "Presets")) {} }, 500, 310); } @@ -114,10 +198,45 @@ class BoxExport { UIFiles.show(UITrait.inst.exportMeshFormat == 0 ? "obj" : "arm", true, function(path:String) { var f = UIFiles.filename; if (f == "") f = "untitled"; - Exporter.exportMesh(path + "/" + f); + ExportMesh.run(path + "/" + f); }); } } }); } + + static function fetchPresets() { + files = File.readDirectory(Path.data() + Path.sep + "export_presets"); + for (i in 0...files.length) { + files[i] = files[i].substr(0, files[i].length - 5); // Strip .json + } + } + + static function parsePreset() { + var file = "export_presets/" + files[hpreset.position] + ".json"; + iron.data.Data.getBlob(file, function(blob:kha.Blob) { + preset = haxe.Json.parse(blob.toString()); + iron.data.Data.deleteBlob("export_presets/" + file); + }); + } + + static function newPreset(name:String) { + var template = +'{ + "textures": [ + { "name": "base", "channels": ["base_r", "base_g", "base_b", "1.0"] } + ] +} +'; + if (!name.endsWith(".json")) name += ".json"; + var path = Path.data() + Path.sep + "export_presets" + Path.sep + name; + Krom.fileSaveBytes(path, Bytes.ofString(template).getData()); + } + + static function savePreset() { + var name = files[hpreset.position]; + if (name == "generic") return; // generic is const + var path = Path.data() + Path.sep + "export_presets" + Path.sep + name + ".json"; + Krom.fileSaveBytes(path, Bytes.ofString(haxe.Json.stringify(preset)).getData()); + } } diff --git a/Sources/arm/ui/BoxPreferences.hx b/Sources/arm/ui/BoxPreferences.hx index 2d4a21ed..fa69c9e6 100644 --- a/Sources/arm/ui/BoxPreferences.hx +++ b/Sources/arm/ui/BoxPreferences.hx @@ -221,7 +221,12 @@ class BoxPreferences { if (ui.tab(htab, "Plugins")) { ui.row([1/4, 1/4]); if (ui.button("New")) { - var template = + UIBox.showCustom(function(ui:Zui) { + if (ui.tab(Id.handle(), "New Plugin")) { + ui.row([1/2, 1/2]); + var pluginName = ui.textInput(Id.handle({text: "new_plugin"}), "Name"); + if (ui.button("OK") || ui.isReturnDown) { + var template = "let plugin = new arm.Plugin(); let h1 = new zui.Handle(); plugin.drawUI = function(ui) { @@ -232,22 +237,19 @@ plugin.drawUI = function(ui) { } } "; - UIBox.showCustom(function(ui:Zui) { - if (ui.tab(Id.handle(), "New Plugin")) { - ui.row([1/2, 1/2]); - var pluginName = ui.textInput(Id.handle({text: "new_plugin"}), "Name"); - if (ui.button("OK") || ui.isReturnDown) { if (!pluginName.endsWith(".js")) pluginName += ".js"; var path = Path.data() + Path.sep + "plugins" + Path.sep + pluginName; Krom.fileSaveBytes(path, Bytes.ofString(template).getData()); files = null; // Refresh file list UIBox.show = false; App.redrawUI(); + BoxPreferences.htab.position = 5; // Plugins + BoxPreferences.show(); } } }); } - if (ui.button("Install")) { + if (ui.button("Import")) { UIFiles.show("js,wasm,zip", false, function(path:String) { ImportPlugin.run(path); }); diff --git a/Sources/arm/ui/TabMaterials.hx b/Sources/arm/ui/TabMaterials.hx index b9b32ec7..b03b7f26 100644 --- a/Sources/arm/ui/TabMaterials.hx +++ b/Sources/arm/ui/TabMaterials.hx @@ -13,7 +13,7 @@ import arm.util.RenderUtil; import arm.util.MaterialUtil; import arm.io.ImportBlend; import arm.io.ImportArm; -import arm.io.Exporter; +import arm.io.ExportArm; import arm.Tool; using StringTools; @@ -127,7 +127,7 @@ class TabMaterials { UIFiles.show("arm", true, function(path:String) { var f = UIFiles.filename; if (f == "") f = "untitled"; - Exporter.exportMaterial(path + "/" + f); + ExportArm.runMaterial(path + "/" + f); }); } diff --git a/Sources/arm/ui/TabTextures.hx b/Sources/arm/ui/TabTextures.hx index 007004dd..7ef58572 100644 --- a/Sources/arm/ui/TabTextures.hx +++ b/Sources/arm/ui/TabTextures.hx @@ -4,7 +4,7 @@ import zui.Zui; import iron.data.Data; import iron.system.Time; import iron.system.Input; -import arm.io.Importer; +import arm.io.ImportAsset; import arm.sys.Path; using StringTools; @@ -18,7 +18,7 @@ class TabTextures { if (ui.button("Import")) { UIFiles.show(Path.textureFormats.join(","), false, function(path:String) { - Importer.run(path); + ImportAsset.run(path); }); } if (ui.isHovered) ui.tooltip("Import texture file (" + Config.keymap.file_import_assets + ")"); diff --git a/Sources/arm/ui/UIBox.hx b/Sources/arm/ui/UIBox.hx index 9c6c11d8..b05fa209 100644 --- a/Sources/arm/ui/UIBox.hx +++ b/Sources/arm/ui/UIBox.hx @@ -69,7 +69,8 @@ class UIBox { var kb = Input.getKeyboard(); var ui = App.uibox; var inUse = ui.comboSelectedHandle != null; - if (drawn && (ui.inputReleased || kb.started("escape")) && !inUse && !ui.isTyping) { + var isEscape = kb.started("escape"); + if (drawn && (ui.inputReleased || isEscape) && !inUse && !ui.isTyping) { var appw = System.windowWidth(); var apph = System.windowHeight(); var mw = Std.int(modalW * ui.SCALE()); @@ -80,7 +81,7 @@ class UIBox { var bottom = (apph / 2 + mh / 2) + hwnd.dragY; var mx = mouse.x; var my = mouse.y; - if (mx < left || mx > right || my < top || my > bottom) { + if (mx < left || mx > right || my < top || my > bottom || isEscape) { show = false; App.redrawUI(); } diff --git a/Sources/arm/ui/UIMenu.hx b/Sources/arm/ui/UIMenu.hx index 6d8f5977..139fd33f 100644 --- a/Sources/arm/ui/UIMenu.hx +++ b/Sources/arm/ui/UIMenu.hx @@ -17,7 +17,7 @@ import arm.util.BuildMacros; import arm.sys.Path; import arm.sys.File; import arm.node.MaterialParser; -import arm.io.Importer; +import arm.io.ImportAsset; using StringTools; class UIMenu { @@ -27,10 +27,10 @@ class UIMenu { public static var menuX = 0; public static var menuY = 0; public static var keepOpen = false; + public static var menuCommands:Zui->Void = null; static var changeStarted = false; static var showMenuFirst = true; static var hideMenu = false; - static var menuCommands:Zui->Void = null; static var viewportColorHandle = Id.handle({selected: false}); @:access(zui.Zui) @@ -102,7 +102,7 @@ class UIMenu { Log.showError("Error: .hdr file expected"); return; } - Importer.run(path); + ImportAsset.run(path); }); } diff --git a/Sources/arm/ui/UITrait.hx b/Sources/arm/ui/UITrait.hx index 3be97c1e..1d5f0e06 100644 --- a/Sources/arm/ui/UITrait.hx +++ b/Sources/arm/ui/UITrait.hx @@ -23,8 +23,8 @@ import arm.util.UVUtil; import arm.data.LayerSlot; import arm.data.BrushSlot; import arm.data.MaterialSlot; -import arm.io.Importer; -import arm.io.Exporter; +import arm.io.ImportFont; +import arm.io.ExportTexture; import arm.io.ExportArm; import arm.Tool; import arm.Project; @@ -98,7 +98,6 @@ class UITrait { public var formatType = 0; public var formatQuality = 100.0; public var layersExport = 0; - public var outputType = 0; public var isBase = true; public var isBaseSpace = 0; public var isOpac = false; @@ -410,7 +409,7 @@ class UITrait { public function update() { if (textureExport) { textureExport = false; - Exporter.exportTextures(textureExportPath); + ExportTexture.run(textureExportPath); } if (projectExport) { projectExport = false; @@ -1094,7 +1093,7 @@ class UITrait { } } if (Context.tool == ToolText) { - ui.combo(textToolHandle, Importer.fontList, "Font"); + ui.combo(textToolHandle, ImportFont.fontList, "Font"); var h = Id.handle(); h.text = textToolText; textToolText = ui.textInput(h, ""); @@ -1219,7 +1218,7 @@ class UITrait { function menuButton(name:String, category:Int) { ui._w = Std.int(ui.ops.font.width(ui.fontSize, name) + 25); - if (ui.button(name) || (UIMenu.show && ui.isHovered)) { + if (ui.button(name) || (UIMenu.show && UIMenu.menuCommands == null && ui.isHovered)) { UIMenu.show = true; UIMenu.menuCategory = category; UIMenu.menuX = Std.int(ui._x - ui._w); diff --git a/Sources/arm/util/RenderUtil.hx b/Sources/arm/util/RenderUtil.hx index 57f571c2..e9d1abe0 100644 --- a/Sources/arm/util/RenderUtil.hx +++ b/Sources/arm/util/RenderUtil.hx @@ -11,7 +11,7 @@ import arm.ui.UITrait; import arm.render.RenderPathPreview; import arm.render.RenderPathDeferred; import arm.node.MaterialParser; -import arm.io.Importer; +import arm.io.ImportFont; import arm.Tool; class RenderUtil { @@ -174,9 +174,9 @@ class RenderUtil { } static function getTextToolFont():Font { - var fontName = Importer.fontList[UITrait.inst.textToolHandle.position]; + var fontName = ImportFont.fontList[UITrait.inst.textToolHandle.position]; if (fontName == 'default.ttf') return UITrait.inst.ui.ops.font; - return Importer.fontMap.get(fontName); + return ImportFont.fontMap.get(fontName); } public static function makeDecalMaskPreview() {