diff --git a/Assets/export_presets/generic.json b/Assets/export_presets/generic.json index 65026113..a337c4a8 100644 --- a/Assets/export_presets/generic.json +++ b/Assets/export_presets/generic.json @@ -1,9 +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"] } + { "name": "base", "channels": ["base_r", "base_g", "base_b", "1.0"], "color_space": "linear" }, + { "name": "nor", "channels": ["nor_r", "nor_g", "nor_b", "1.0"], "color_space": "linear" }, + { "name": "occ", "channels": ["occ", "occ", "occ", "1.0"], "color_space": "linear" }, + { "name": "rough", "channels": ["rough", "rough", "rough", "1.0"], "color_space": "linear" }, + { "name": "metal", "channels": ["metal", "metal", "metal", "1.0"], "color_space": "linear" } ] } diff --git a/Assets/export_presets/minecraft_mer.json b/Assets/export_presets/minecraft_mer.json index a56ea609..2bb70138 100644 --- a/Assets/export_presets/minecraft_mer.json +++ b/Assets/export_presets/minecraft_mer.json @@ -1,7 +1,7 @@ { "textures": [ - { "name": "", "channels": ["base_r", "base_g", "base_b", "1.0"] }, - { "name": "normal", "channels": ["nor_r", "nor_g", "nor_b", "1.0"] }, - { "name": "mer", "channels": ["metal", "emis", "rough", "1.0"] } + { "name": "", "channels": ["base_r", "base_g", "base_b", "1.0"], "color_space": "linear" }, + { "name": "normal", "channels": ["nor_r", "nor_g", "nor_b", "1.0"], "color_space": "linear" }, + { "name": "mer", "channels": ["metal", "emis", "rough", "1.0"], "color_space": "linear" } ] } diff --git a/Assets/export_presets/unigine.json b/Assets/export_presets/unigine.json index f36355ac..eb49db0e 100644 --- a/Assets/export_presets/unigine.json +++ b/Assets/export_presets/unigine.json @@ -1,8 +1,8 @@ { "textures": [ - { "name": "base", "channels": ["base_r", "base_g", "base_b", "1.0"] }, - { "name": "nor", "channels": ["nor_r", "nor_g", "0.0", "1.0"] }, - { "name": "sh", "channels": ["metal", "rough", "0.0", "1.0"] }, - { "name": "occ", "channels": ["occ", "0.0", "0.0", "1.0"] } + { "name": "base", "channels": ["base_r", "base_g", "base_b", "1.0"], "color_space": "linear" }, + { "name": "nor", "channels": ["nor_r", "nor_g", "0.0", "1.0"], "color_space": "linear" }, + { "name": "sh", "channels": ["metal", "rough", "0.0", "1.0"], "color_space": "linear" }, + { "name": "occ", "channels": ["occ", "0.0", "0.0", "1.0"], "color_space": "linear" } ] } diff --git a/Assets/export_presets/unity.json b/Assets/export_presets/unity.json index f4fda707..af156731 100644 --- a/Assets/export_presets/unity.json +++ b/Assets/export_presets/unity.json @@ -1,8 +1,8 @@ { "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", "1.0", "smooth"] }, - { "name": "height", "channels": ["0.0", "height", "0.0", "1.0"] } + { "name": "base", "channels": ["base_r", "base_g", "base_b", "1.0"], "color_space": "linear" }, + { "name": "nor", "channels": ["nor_r", "nor_g", "nor_b", "1.0"], "color_space": "linear" }, + { "name": "mos", "channels": ["metal", "occ", "1.0", "smooth"], "color_space": "linear" }, + { "name": "height", "channels": ["0.0", "height", "0.0", "1.0"], "color_space": "linear" } ] } diff --git a/Assets/export_presets/unreal.json b/Assets/export_presets/unreal.json index 75404054..7f7f162b 100644 --- a/Assets/export_presets/unreal.json +++ b/Assets/export_presets/unreal.json @@ -1,7 +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"] } + { "name": "base", "channels": ["base_r", "base_g", "base_b", "1.0"], "color_space": "linear" }, + { "name": "nor", "channels": ["nor_r", "nor_g", "nor_b", "1.0"], "color_space": "linear" }, + { "name": "orm", "channels": ["occ", "rough", "metal", "1.0"], "color_space": "linear" } ] } diff --git a/Assets/export_presets/xplane.json b/Assets/export_presets/xplane.json index 727e16a8..16ebe552 100644 --- a/Assets/export_presets/xplane.json +++ b/Assets/export_presets/xplane.json @@ -1,6 +1,6 @@ { "textures": [ - { "name": "base", "channels": ["base_r", "base_g", "base_b", "rough"] }, - { "name": "nor", "channels": ["nor_r", "nor_g", "smooth", "metal"] } + { "name": "base", "channels": ["base_r", "base_g", "base_b", "rough"], "color_space": "linear" }, + { "name": "nor", "channels": ["nor_r", "nor_g", "smooth", "metal"], "color_space": "linear" } ] } diff --git a/Sources/arm/io/ExportTexture.hx b/Sources/arm/io/ExportTexture.hx index 0e266cc3..2620de8e 100644 --- a/Sources/arm/io/ExportTexture.hx +++ b/Sources/arm/io/ExportTexture.hx @@ -20,6 +20,8 @@ import arm.Enums; class ExportTexture { + static inline var gamma = 1.0 / 2.2; + public static function run(path: String, bakeMaterial = false) { #if arm_debug var timer = iron.system.Time.realTime(); @@ -267,48 +269,48 @@ class ExportTexture { var c = t.channels; var tex_name = t.name != "" ? "_" + t.name : ""; 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") { + if (c[0] == "base_r" && c[1] == "base_g" && c[2] == "base_b" && c[3] == "1.0" && t.color_space == "linear") { writeTexture(path + Path.sep + f + tex_name + ext, pixpaint, 1); } - else if (c[0] == "nor_r" && c[1] == "nor_g" && c[2] == "nor_b" && c[3] == "1.0") { + else if (c[0] == "nor_r" && c[1] == "nor_g" && c[2] == "nor_b" && c[3] == "1.0" && t.color_space == "linear") { writeTexture(path + Path.sep + f + tex_name + ext, pixpaint_nor, 1); } - else if (c[0] == "occ" && c[1] == "rough" && c[2] == "metal" && c[3] == "1.0") { + else if (c[0] == "occ" && c[1] == "rough" && c[2] == "metal" && c[3] == "1.0" && t.color_space == "linear") { writeTexture(path + Path.sep + f + tex_name + ext, pixpaint_pack, 1); } - else if (singleChannel && c[0] == "occ") { + else if (singleChannel && c[0] == "occ" && t.color_space == "linear") { writeTexture(path + Path.sep + f + tex_name + ext, pixpaint_pack, 2, 0); } - else if (singleChannel && c[0] == "rough") { + else if (singleChannel && c[0] == "rough" && t.color_space == "linear") { writeTexture(path + Path.sep + f + tex_name + ext, pixpaint_pack, 2, 1); } - else if (singleChannel && c[0] == "metal") { + else if (singleChannel && c[0] == "metal" && t.color_space == "linear") { writeTexture(path + Path.sep + f + tex_name + ext, pixpaint_pack, 2, 2); } - else if (singleChannel && c[0] == "height") { + else if (singleChannel && c[0] == "height" && t.color_space == "linear") { writeTexture(path + Path.sep + f + tex_name + ext, pixpaint_pack, 2, 3); } - else if (singleChannel && c[0] == "opac") { + else if (singleChannel && c[0] == "opac" && t.color_space == "linear") { writeTexture(path + Path.sep + f + tex_name + ext, pixpaint, 2, 3); } else { if (pix == null) pix = Bytes.alloc(textureSizeX * textureSizeY * 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, 3, pix, i); - else if (c == "metal") copyChannel(pixpaint_pack, 2, 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 == "emis") extractChannel(pixpaint_nor, 3, pix, i, 255); - else if (c == "subs") extractChannel(pixpaint_nor, 3, pix, i, 254); + if (c == "base_r") copyChannel(pixpaint, 0, pix, i, t.color_space == "linear"); + else if (c == "base_g") copyChannel(pixpaint, 1, pix, i, t.color_space == "linear"); + else if (c == "base_b") copyChannel(pixpaint, 2, pix, i, t.color_space == "linear"); + else if (c == "height") copyChannel(pixpaint_pack, 3, pix, i, t.color_space == "linear"); + else if (c == "metal") copyChannel(pixpaint_pack, 2, pix, i, t.color_space == "linear"); + else if (c == "nor_r") copyChannel(pixpaint_nor, 0, pix, i, t.color_space == "linear"); + else if (c == "nor_g") copyChannel(pixpaint_nor, 1, pix, i, t.color_space == "linear"); + else if (c == "nor_b") copyChannel(pixpaint_nor, 2, pix, i, t.color_space == "linear"); + else if (c == "occ") copyChannel(pixpaint_pack, 0, pix, i, t.color_space == "linear"); + else if (c == "opac") copyChannel(pixpaint, 3, pix, i, t.color_space == "linear"); + else if (c == "rough") copyChannel(pixpaint_pack, 1, pix, i, t.color_space == "linear"); + else if (c == "smooth") copyChannelInv(pixpaint_pack, 1, pix, i, t.color_space == "linear"); + else if (c == "emis") extractChannel(pixpaint_nor, 3, pix, i, 255, t.color_space == "linear"); + else if (c == "subs") extractChannel(pixpaint_nor, 3, pix, i, 254, t.color_space == "linear"); else if (c == "0.0") setChannel(0, pix, i); else if (c == "1.0") setChannel(255, pix, i); } @@ -369,34 +371,37 @@ class ExportTexture { Krom.fileSaveBytes(file, out.getBytes().getData(), out.getBytes().length); } - static function copyChannel(from: Bytes, fromChannel: Int, to: Bytes, toChannel: Int) { + static function copyChannel(from: Bytes, fromChannel: Int, to: Bytes, toChannel: Int, linear = true) { for (i in 0...Std.int(to.length / 4)) { to.set(i * 4 + toChannel, from.get(i * 4 + fromChannel)); } + if (!linear) toSrgb(to, toChannel); } - 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) { + static function copyChannelInv(from: Bytes, fromChannel: Int, to: Bytes, toChannel: Int, linear = true) { for (i in 0...Std.int(to.length / 4)) { to.set(i * 4 + toChannel, 255 - from.get(i * 4 + fromChannel)); } + if (!linear) toSrgb(to, toChannel); } - static function setChannel(value: Int, to: Bytes, toChannel: Int) { + static function extractChannel(from: Bytes, fromChannel: Int, to: Bytes, toChannel: Int, mask: Int, linear = true) { + for (i in 0...Std.int(to.length / 4)) { + to.set(i * 4 + toChannel, from.get(i * 4 + fromChannel) == mask ? 255 : 0); + } + if (!linear) toSrgb(to, toChannel); + } + + static function setChannel(value: Int, to: Bytes, toChannel: Int, linear = true) { for (i in 0...Std.int(to.length / 4)) { to.set(i * 4 + toChannel, value); } + if (!linear) toSrgb(to, toChannel); } - static function extractChannel(from: Bytes, fromChannel: Int, to: Bytes, toChannel: Int, mask: Int) { + static function toSrgb(to: Bytes, toChannel: Int) { for (i in 0...Std.int(to.length / 4)) { - to.set(i * 4 + toChannel, from.get(i * 4 + fromChannel) == mask ? 255 : 0); + to.set(i * 4 + toChannel, Std.int(Math.pow(to.get(i * 4 + toChannel) / 255, gamma) * 255)); } } } @@ -408,4 +413,5 @@ typedef TExportPreset = { typedef TExportPresetTexture = { public var name: String; public var channels: Array; + public var color_space: String; } diff --git a/Sources/arm/ui/BoxExport.hx b/Sources/arm/ui/BoxExport.hx index f26dd141..f8a4977d 100644 --- a/Sources/arm/ui/BoxExport.hx +++ b/Sources/arm/ui/BoxExport.hx @@ -18,6 +18,7 @@ class BoxExport { 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", "emis", "subs", "0.0", "1.0"]; + static var colorSpaces = ["linear", "srgb"]; public static function showTextures() { UIBox.showCustom(function(ui: Zui) { @@ -32,7 +33,7 @@ class BoxExport { tabPresets(ui); tabAtlases(ui); - }, 500, 310); + }, 540, 310); } public static function showBakeMaterial() { @@ -47,7 +48,7 @@ class BoxExport { tabExportTextures(ui, tr("Bake to Textures"), true); tabPresets(ui); - }, 500, 310); + }, 540, 310); } static function tabExportTextures(ui: Zui, title: String, bakeMaterial = false) { @@ -158,16 +159,17 @@ class BoxExport { // Texture list ui.separator(10, false); - ui.row([0.2, 0.2, 0.2, 0.2, 0.2]); + ui.row([1 / 6, 1 / 6, 1 / 6, 1 / 6, 1 / 6, 1 / 6]); ui.text(tr("Texture")); ui.text(tr("R")); ui.text(tr("G")); ui.text(tr("B")); ui.text(tr("A")); + ui.text(tr("Color Space")); ui.changed = false; for (i in 0...preset.textures.length) { var t = preset.textures[i]; - ui.row([0.2, 0.2, 0.2, 0.2, 0.2]); + ui.row([1 / 6, 1 / 6, 1 / 6, 1 / 6, 1 / 6, 1 / 6]); var htex = hpreset.nest(i); htex.text = t.name; t.name = ui.textInput(htex); @@ -199,6 +201,11 @@ class BoxExport { if (hb.changed) t.channels[2] = channels[hb.position]; ui.combo(ha, channels, tr("A")); if (ha.changed) t.channels[3] = channels[ha.position]; + + var hspace = htex.nest(4); + hspace.position = colorSpaces.indexOf(t.color_space); + ui.combo(hspace, colorSpaces, tr("Color Space")); + if (hspace.changed) t.color_space = colorSpaces[hspace.position]; } if (ui.changed) { @@ -207,7 +214,7 @@ class BoxExport { ui.row([1 / 8]); if (ui.button(tr("Add"))) { - preset.textures.push({name: "base", channels: ["base_r", "base_g", "base_b", "1.0"]}); + preset.textures.push({name: "base", channels: ["base_r", "base_g", "base_b", "1.0"], color_space: "linear"}); @:privateAccess hpreset.children = null; savePreset(); }