Add color space option for texture export

This commit is contained in:
Lubos Lenco
2021-03-29 16:27:52 +02:00
parent a3771087c0
commit 0a3cd8777e
8 changed files with 73 additions and 60 deletions
+5 -5
View File
@@ -1,9 +1,9 @@
{ {
"textures": [ "textures": [
{ "name": "base", "channels": ["base_r", "base_g", "base_b", "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"] }, { "name": "nor", "channels": ["nor_r", "nor_g", "nor_b", "1.0"], "color_space": "linear" },
{ "name": "occ", "channels": ["occ", "occ", "occ", "1.0"] }, { "name": "occ", "channels": ["occ", "occ", "occ", "1.0"], "color_space": "linear" },
{ "name": "rough", "channels": ["rough", "rough", "rough", "1.0"] }, { "name": "rough", "channels": ["rough", "rough", "rough", "1.0"], "color_space": "linear" },
{ "name": "metal", "channels": ["metal", "metal", "metal", "1.0"] } { "name": "metal", "channels": ["metal", "metal", "metal", "1.0"], "color_space": "linear" }
] ]
} }
+3 -3
View File
@@ -1,7 +1,7 @@
{ {
"textures": [ "textures": [
{ "name": "", "channels": ["base_r", "base_g", "base_b", "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"] }, { "name": "normal", "channels": ["nor_r", "nor_g", "nor_b", "1.0"], "color_space": "linear" },
{ "name": "mer", "channels": ["metal", "emis", "rough", "1.0"] } { "name": "mer", "channels": ["metal", "emis", "rough", "1.0"], "color_space": "linear" }
] ]
} }
+4 -4
View File
@@ -1,8 +1,8 @@
{ {
"textures": [ "textures": [
{ "name": "base", "channels": ["base_r", "base_g", "base_b", "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"] }, { "name": "nor", "channels": ["nor_r", "nor_g", "0.0", "1.0"], "color_space": "linear" },
{ "name": "sh", "channels": ["metal", "rough", "0.0", "1.0"] }, { "name": "sh", "channels": ["metal", "rough", "0.0", "1.0"], "color_space": "linear" },
{ "name": "occ", "channels": ["occ", "0.0", "0.0", "1.0"] } { "name": "occ", "channels": ["occ", "0.0", "0.0", "1.0"], "color_space": "linear" }
] ]
} }
+4 -4
View File
@@ -1,8 +1,8 @@
{ {
"textures": [ "textures": [
{ "name": "base", "channels": ["base_r", "base_g", "base_b", "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"] }, { "name": "nor", "channels": ["nor_r", "nor_g", "nor_b", "1.0"], "color_space": "linear" },
{ "name": "mos", "channels": ["metal", "occ", "1.0", "smooth"] }, { "name": "mos", "channels": ["metal", "occ", "1.0", "smooth"], "color_space": "linear" },
{ "name": "height", "channels": ["0.0", "height", "0.0", "1.0"] } { "name": "height", "channels": ["0.0", "height", "0.0", "1.0"], "color_space": "linear" }
] ]
} }
+3 -3
View File
@@ -1,7 +1,7 @@
{ {
"textures": [ "textures": [
{ "name": "base", "channels": ["base_r", "base_g", "base_b", "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"] }, { "name": "nor", "channels": ["nor_r", "nor_g", "nor_b", "1.0"], "color_space": "linear" },
{ "name": "orm", "channels": ["occ", "rough", "metal", "1.0"] } { "name": "orm", "channels": ["occ", "rough", "metal", "1.0"], "color_space": "linear" }
] ]
} }
+2 -2
View File
@@ -1,6 +1,6 @@
{ {
"textures": [ "textures": [
{ "name": "base", "channels": ["base_r", "base_g", "base_b", "rough"] }, { "name": "base", "channels": ["base_r", "base_g", "base_b", "rough"], "color_space": "linear" },
{ "name": "nor", "channels": ["nor_r", "nor_g", "smooth", "metal"] } { "name": "nor", "channels": ["nor_r", "nor_g", "smooth", "metal"], "color_space": "linear" }
] ]
} }
+40 -34
View File
@@ -20,6 +20,8 @@ import arm.Enums;
class ExportTexture { class ExportTexture {
static inline var gamma = 1.0 / 2.2;
public static function run(path: String, bakeMaterial = false) { public static function run(path: String, bakeMaterial = false) {
#if arm_debug #if arm_debug
var timer = iron.system.Time.realTime(); var timer = iron.system.Time.realTime();
@@ -267,48 +269,48 @@ class ExportTexture {
var c = t.channels; var c = t.channels;
var tex_name = t.name != "" ? "_" + t.name : ""; var tex_name = t.name != "" ? "_" + t.name : "";
var singleChannel = c[0] == c[1] && c[1] == c[2] && c[3] == "1.0"; 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); 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); 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); 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); 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); 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); 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); 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); writeTexture(path + Path.sep + f + tex_name + ext, pixpaint, 2, 3);
} }
else { else {
if (pix == null) pix = Bytes.alloc(textureSizeX * textureSizeY * 4 * Std.int(bits / 8)); if (pix == null) pix = Bytes.alloc(textureSizeX * textureSizeY * 4 * Std.int(bits / 8));
for (i in 0...4) { for (i in 0...4) {
var c = t.channels[i]; var c = t.channels[i];
if (c == "base_r") copyChannel(pixpaint, 0, pix, i); // copyChannelGamma if (c == "base_r") copyChannel(pixpaint, 0, pix, i, t.color_space == "linear");
else if (c == "base_g") copyChannel(pixpaint, 1, pix, i); // copyChannelGamma else if (c == "base_g") copyChannel(pixpaint, 1, pix, i, t.color_space == "linear");
else if (c == "base_b") copyChannel(pixpaint, 2, pix, i); // copyChannelGamma else if (c == "base_b") copyChannel(pixpaint, 2, pix, i, t.color_space == "linear");
else if (c == "height") copyChannel(pixpaint_pack, 3, pix, i); else if (c == "height") copyChannel(pixpaint_pack, 3, pix, i, t.color_space == "linear");
else if (c == "metal") copyChannel(pixpaint_pack, 2, pix, i); 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); 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); 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); 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); else if (c == "occ") copyChannel(pixpaint_pack, 0, pix, i, t.color_space == "linear");
else if (c == "opac") copyChannel(pixpaint, 3, pix, i); else if (c == "opac") copyChannel(pixpaint, 3, pix, i, t.color_space == "linear");
else if (c == "rough") copyChannel(pixpaint_pack, 1, pix, i); else if (c == "rough") copyChannel(pixpaint_pack, 1, pix, i, t.color_space == "linear");
else if (c == "smooth") copyChannelInv(pixpaint_pack, 1, pix, i); 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); 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); 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 == "0.0") setChannel(0, pix, i);
else if (c == "1.0") setChannel(255, 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); 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)) { for (i in 0...Std.int(to.length / 4)) {
to.set(i * 4 + toChannel, from.get(i * 4 + fromChannel)); 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 copyChannelInv(from: Bytes, fromChannel: Int, to: Bytes, toChannel: Int, linear = true) {
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)) { for (i in 0...Std.int(to.length / 4)) {
to.set(i * 4 + toChannel, 255 - from.get(i * 4 + fromChannel)); 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)) { for (i in 0...Std.int(to.length / 4)) {
to.set(i * 4 + toChannel, value); 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)) { 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 = { typedef TExportPresetTexture = {
public var name: String; public var name: String;
public var channels: Array<String>; public var channels: Array<String>;
public var color_space: String;
} }
+12 -5
View File
@@ -18,6 +18,7 @@ class BoxExport {
public static var files: Array<String> = null; public static var files: Array<String> = null;
public static var preset: TExportPreset = 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 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() { public static function showTextures() {
UIBox.showCustom(function(ui: Zui) { UIBox.showCustom(function(ui: Zui) {
@@ -32,7 +33,7 @@ class BoxExport {
tabPresets(ui); tabPresets(ui);
tabAtlases(ui); tabAtlases(ui);
}, 500, 310); }, 540, 310);
} }
public static function showBakeMaterial() { public static function showBakeMaterial() {
@@ -47,7 +48,7 @@ class BoxExport {
tabExportTextures(ui, tr("Bake to Textures"), true); tabExportTextures(ui, tr("Bake to Textures"), true);
tabPresets(ui); tabPresets(ui);
}, 500, 310); }, 540, 310);
} }
static function tabExportTextures(ui: Zui, title: String, bakeMaterial = false) { static function tabExportTextures(ui: Zui, title: String, bakeMaterial = false) {
@@ -158,16 +159,17 @@ class BoxExport {
// Texture list // Texture list
ui.separator(10, false); 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("Texture"));
ui.text(tr("R")); ui.text(tr("R"));
ui.text(tr("G")); ui.text(tr("G"));
ui.text(tr("B")); ui.text(tr("B"));
ui.text(tr("A")); ui.text(tr("A"));
ui.text(tr("Color Space"));
ui.changed = false; ui.changed = false;
for (i in 0...preset.textures.length) { for (i in 0...preset.textures.length) {
var t = preset.textures[i]; 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); var htex = hpreset.nest(i);
htex.text = t.name; htex.text = t.name;
t.name = ui.textInput(htex); t.name = ui.textInput(htex);
@@ -199,6 +201,11 @@ class BoxExport {
if (hb.changed) t.channels[2] = channels[hb.position]; if (hb.changed) t.channels[2] = channels[hb.position];
ui.combo(ha, channels, tr("A")); ui.combo(ha, channels, tr("A"));
if (ha.changed) t.channels[3] = channels[ha.position]; 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) { if (ui.changed) {
@@ -207,7 +214,7 @@ class BoxExport {
ui.row([1 / 8]); ui.row([1 / 8]);
if (ui.button(tr("Add"))) { 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; @:privateAccess hpreset.children = null;
savePreset(); savePreset();
} }