Add swatches tab
This commit is contained in:
+11
-11
@@ -61,17 +61,8 @@ class Context {
|
||||
public static var viewIndexLast = -1;
|
||||
public static var materialPreview = false; // Drawing material previews
|
||||
public static var savedCamera = Mat4.identity();
|
||||
public static var baseRPicked = 0.0;
|
||||
public static var baseGPicked = 0.0;
|
||||
public static var baseBPicked = 0.0;
|
||||
public static var normalRPicked = 0.0;
|
||||
public static var normalGPicked = 0.0;
|
||||
public static var normalBPicked = 0.0;
|
||||
public static var opacityPicked = 0.0;
|
||||
public static var roughnessPicked = 0.0;
|
||||
public static var metallicPicked = 0.0;
|
||||
public static var occlusionPicked = 0.0;
|
||||
public static var heightPicked = 0.0;
|
||||
|
||||
public static var swatch: TSwatch;
|
||||
public static var materialIdPicked = 0;
|
||||
public static var uvxPicked = 0.0;
|
||||
public static var uvyPicked = 0.0;
|
||||
@@ -318,6 +309,15 @@ class Context {
|
||||
UIView2D.inst.hwnd.redraws = 2;
|
||||
}
|
||||
|
||||
public static function setSwatch(s: TSwatch) {
|
||||
swatch = s;
|
||||
App.notifyOnNextFrame(function() {
|
||||
MakeMaterial.parsePaintMaterial();
|
||||
RenderUtil.makeMaterialPreview();
|
||||
UISidebar.inst.hwnd1.redraws = 2;
|
||||
});
|
||||
}
|
||||
|
||||
public static function setLayer(l: LayerSlot, isMask = false) {
|
||||
if (l == layer && layerIsMask == isMask) return;
|
||||
layer = l;
|
||||
|
||||
@@ -253,6 +253,8 @@ class Project {
|
||||
var fontNames = App.font.getFontNames();
|
||||
fonts = [new FontSlot(fontNames.length > 0 ? fontNames[0] : "default.ttf", App.font)];
|
||||
Context.font = fonts[0];
|
||||
Project.raw.swatches = [makeSwatch()];
|
||||
Context.swatch = Project.raw.swatches[0];
|
||||
|
||||
History.reset();
|
||||
|
||||
@@ -463,6 +465,16 @@ class Project {
|
||||
for (pa in packed_assets) if (pa.name == name) return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
public static function exportSwatches() {
|
||||
UIFiles.show("arm", true, function(path: String) {
|
||||
ExportArm.runSwatches(path);
|
||||
});
|
||||
}
|
||||
|
||||
public static function makeSwatch(): TSwatch {
|
||||
return { base: 0xffffffff, opacity: 1.0, occlusion: 1.0, roughness: 0.0, metallic: 0.0, normal: 0xff8080ff, emission: 0.0, height: 0.0, subsurface: 0.0 };
|
||||
}
|
||||
}
|
||||
|
||||
typedef TNodeGroup = {
|
||||
|
||||
@@ -17,6 +17,7 @@ typedef TProjectFormat = {
|
||||
@:optional public var mesh_assets: Array<String>;
|
||||
@:optional public var atlas_objects: Array<Int>;
|
||||
@:optional public var atlas_names: Array<String>;
|
||||
@:optional public var swatches: Array<TSwatch>;
|
||||
@:optional public var is_bgra: Null<Bool>; // Swapped red and blue channels for layer textures
|
||||
@:optional public var packed_assets: Array<TPackedAsset>;
|
||||
}
|
||||
@@ -62,3 +63,15 @@ typedef TPackedAsset = {
|
||||
public var name: String;
|
||||
public var bytes: haxe.io.Bytes;
|
||||
}
|
||||
|
||||
typedef TSwatch = {
|
||||
public var base: kha.Color;
|
||||
public var opacity: Float;
|
||||
public var occlusion: Float;
|
||||
public var roughness: Float;
|
||||
public var metallic: Float;
|
||||
public var normal: kha.Color;
|
||||
public var emission: Float;
|
||||
public var height: Float;
|
||||
public var subsurface: Float;
|
||||
}
|
||||
|
||||
@@ -102,6 +102,7 @@ class ExportArm {
|
||||
mesh_assets: mesh_files,
|
||||
atlas_objects: Project.atlasObjects,
|
||||
atlas_names: Project.atlasNames,
|
||||
swatches: Project.raw.swatches,
|
||||
packed_assets: packed_assets,
|
||||
#if (kha_metal || kha_vulkan)
|
||||
is_bgra: true
|
||||
@@ -347,4 +348,14 @@ class ExportArm {
|
||||
for (image in tempImages) image.unload();
|
||||
});
|
||||
}
|
||||
|
||||
public static function runSwatches(path: String) {
|
||||
if (!path.endsWith(".arm")) path += ".arm";
|
||||
var raw = {
|
||||
version: Main.version,
|
||||
swatches: Project.raw.swatches
|
||||
};
|
||||
var bytes = ArmPack.encode(raw);
|
||||
Krom.fileSaveBytes(path, bytes.getData(), bytes.length + 1);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -75,6 +75,10 @@ class ImportArm {
|
||||
else if (project.brush_nodes != null) {
|
||||
runBrushFromProject(project, path);
|
||||
}
|
||||
// Import as swatches
|
||||
else if (project.swatches != null) {
|
||||
runSwatchesFromProject(project, path);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -465,6 +469,14 @@ class ImportArm {
|
||||
Data.deleteBlob(path);
|
||||
}
|
||||
|
||||
public static function runSwatchesFromProject(project: TProjectFormat, path: String) {
|
||||
for (s in project.swatches) {
|
||||
Project.raw.swatches.push(s);
|
||||
}
|
||||
UISidebar.inst.hwnd2.redraws = 2;
|
||||
Data.deleteBlob(path);
|
||||
}
|
||||
|
||||
static function makePink(abs: String) {
|
||||
Log.error(Strings.error2() + " " + abs);
|
||||
var b = Bytes.alloc(4);
|
||||
|
||||
@@ -9,6 +9,7 @@ import iron.Scene;
|
||||
import arm.util.ViewportUtil;
|
||||
import arm.ui.UIView2D;
|
||||
import arm.ui.UIHeader;
|
||||
import arm.ui.UISidebar;
|
||||
import arm.node.MakeMaterial;
|
||||
import arm.Enums;
|
||||
|
||||
@@ -177,6 +178,7 @@ class RenderPathPaint {
|
||||
path.bindTarget("texpaint_pack" + tid, "texpaint_pack");
|
||||
path.drawMeshes("paint");
|
||||
UIHeader.inst.headerHandle.redraws = 2;
|
||||
UISidebar.inst.hwnd2.redraws = 2;
|
||||
|
||||
var texpaint_picker = path.renderTargets.get("texpaint_picker").image;
|
||||
var texpaint_nor_picker = path.renderTargets.get("texpaint_nor_picker").image;
|
||||
@@ -186,17 +188,17 @@ class RenderPathPaint {
|
||||
var c = texpaint_pack_picker.getPixels();
|
||||
|
||||
// Picked surface values
|
||||
Context.baseRPicked = a.get(0) / 255;
|
||||
Context.baseGPicked = a.get(1) / 255;
|
||||
Context.baseBPicked = a.get(2) / 255;
|
||||
Context.swatch.base.Rb = a.get(0);
|
||||
Context.swatch.base.Gb = a.get(1);
|
||||
Context.swatch.base.Bb = a.get(2);
|
||||
Context.uvxPicked = a.get(3) / 255;
|
||||
Context.normalRPicked = b.get(0) / 255;
|
||||
Context.normalGPicked = b.get(1) / 255;
|
||||
Context.normalBPicked = b.get(2) / 255;
|
||||
Context.swatch.normal.Rb = b.get(0);
|
||||
Context.swatch.normal.Gb = b.get(1);
|
||||
Context.swatch.normal.Bb = b.get(2);
|
||||
Context.uvyPicked = c.get(3) / 255;
|
||||
Context.occlusionPicked = c.get(0) / 255;
|
||||
Context.roughnessPicked = c.get(1) / 255;
|
||||
Context.metallicPicked = c.get(2) / 255;
|
||||
Context.swatch.occlusion = c.get(0) / 255;
|
||||
Context.swatch.roughness = c.get(1) / 255;
|
||||
Context.swatch.metallic = c.get(2) / 255;
|
||||
// Pick material
|
||||
if (Context.pickerSelectMaterial) {
|
||||
var matid = b.get(3);
|
||||
|
||||
@@ -101,19 +101,19 @@ class Uniforms {
|
||||
return Context.layer.decalMat.getScale().z * 0.5;
|
||||
}
|
||||
case "_pickerOpacity": {
|
||||
return Context.opacityPicked;
|
||||
return Context.swatch.opacity;
|
||||
}
|
||||
case "_pickerOcclusion": {
|
||||
return Context.occlusionPicked;
|
||||
return Context.swatch.occlusion;
|
||||
}
|
||||
case "_pickerRoughness": {
|
||||
return Context.roughnessPicked;
|
||||
return Context.swatch.roughness;
|
||||
}
|
||||
case "_pickerMetallic": {
|
||||
return Context.metallicPicked;
|
||||
return Context.swatch.metallic;
|
||||
}
|
||||
case "_pickerHeight": {
|
||||
return Context.heightPicked;
|
||||
return Context.swatch.height;
|
||||
}
|
||||
}
|
||||
if (MaterialParser.script_links != null) {
|
||||
@@ -198,12 +198,12 @@ class Uniforms {
|
||||
}
|
||||
case "_pickerBase": {
|
||||
v = iron.object.Uniforms.helpVec;
|
||||
v.set(Context.baseRPicked, Context.baseGPicked, Context.baseBPicked);
|
||||
v.set(Context.swatch.base.R, Context.swatch.base.G, Context.swatch.base.B);
|
||||
return v;
|
||||
}
|
||||
case "_pickerNormal": {
|
||||
v = iron.object.Uniforms.helpVec;
|
||||
v.set(Context.normalRPicked, Context.normalGPicked, Context.normalBPicked);
|
||||
v.set(Context.swatch.normal.R, Context.swatch.normal.G, Context.swatch.normal.B);
|
||||
return v;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -60,6 +60,14 @@ class TabMeshes {
|
||||
var h = Id.handle();
|
||||
h.selected = o.visible;
|
||||
o.visible = ui.check(h, o.name);
|
||||
if (ui.isHovered && ui.inputReleasedR) {
|
||||
UIMenu.draw(function(ui: Zui) {
|
||||
ui.text(o.name, Right, ui.t.HIGHLIGHT_COL);
|
||||
if (ui.button(tr("Export"), Left)) {
|
||||
BoxExport.showMesh();
|
||||
}
|
||||
}, 2);
|
||||
}
|
||||
if (h.changed) {
|
||||
var visibles: Array<MeshObject> = [];
|
||||
for (p in Project.paintObjects) if (p.visible) visibles.push(p);
|
||||
|
||||
@@ -0,0 +1,110 @@
|
||||
package arm.ui;
|
||||
|
||||
import zui.Zui;
|
||||
import zui.Id;
|
||||
import iron.system.Time;
|
||||
import iron.system.Input;
|
||||
import arm.io.ImportFont;
|
||||
import arm.util.RenderUtil;
|
||||
import arm.Enums;
|
||||
|
||||
class TabSwatches {
|
||||
|
||||
static var empty: kha.Image = null;
|
||||
|
||||
@:access(zui.Zui)
|
||||
public static function draw() {
|
||||
var ui = UISidebar.inst.ui;
|
||||
if (ui.tab(UISidebar.inst.htab2, tr("Swatches"))) {
|
||||
|
||||
ui.beginSticky();
|
||||
ui.row([1 / 4, 1 / 4, 1 / 4]);
|
||||
|
||||
if (ui.button(tr("New"))) {
|
||||
Context.setSwatch(Project.makeSwatch());
|
||||
Project.raw.swatches.push(Context.swatch);
|
||||
}
|
||||
if (ui.button(tr("Import"))) Project.importAsset("arm");
|
||||
if (ui.isHovered) ui.tooltip(tr("Import swatches") + ' (${Config.keymap.file_import_assets})');
|
||||
if (ui.button(tr("Tools..."))) {
|
||||
UIMenu.draw(function(ui: Zui) {
|
||||
ui.text(tr("Tools"), Right, ui.t.HIGHLIGHT_COL);
|
||||
if (ui.button(tr("Clear"), Left)) {
|
||||
Project.raw.swatches = [Project.makeSwatch()];
|
||||
}
|
||||
}, 3);
|
||||
}
|
||||
|
||||
ui.endSticky();
|
||||
ui.separator(3, false);
|
||||
|
||||
var slotw = Std.int(26 * ui.SCALE());
|
||||
var num = Std.int(Config.raw.layout[LayoutSidebarW] / (slotw + 3));
|
||||
|
||||
for (row in 0...Std.int(Math.ceil(Project.raw.swatches.length / num))) {
|
||||
ui.row([for (i in 0...num) 1 / num]);
|
||||
|
||||
ui._x += 2;
|
||||
if (row > 0) ui._y += 6;
|
||||
|
||||
for (j in 0...num) {
|
||||
var imgw = Std.int(20 * ui.SCALE());
|
||||
var i = j + row * num;
|
||||
if (i >= Project.raw.swatches.length) {
|
||||
@:privateAccess ui.endElement(imgw);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (Context.swatch == Project.raw.swatches[i]) {
|
||||
var off = row % 2 == 1 ? 1 : 0;
|
||||
var w = 32;
|
||||
if (Config.raw.window_scale > 1) w += Std.int(Config.raw.window_scale * 2);
|
||||
ui.fill(-2, -2, w, w, ui.t.HIGHLIGHT_COL);
|
||||
}
|
||||
|
||||
if (empty == null) {
|
||||
var b = haxe.io.Bytes.alloc(4);
|
||||
b.set(0, 255);
|
||||
b.set(1, 255);
|
||||
b.set(2, 255);
|
||||
b.set(3, 255);
|
||||
empty = kha.Image.fromBytes(b, 1, 1);
|
||||
}
|
||||
var state = ui.image(empty, Project.raw.swatches[i].base, slotw);
|
||||
|
||||
if (state == State.Started) {
|
||||
Context.setSwatch(Project.raw.swatches[i]);
|
||||
}
|
||||
else if (state == State.Released) {
|
||||
if (Time.time() - Context.selectTime < 0.25) {
|
||||
|
||||
UIMenu.draw(function(ui) {
|
||||
ui.fill(0, 0, ui._w / ui.ops.scaleFactor, ui.t.ELEMENT_H * 9, ui.t.SEPARATOR_COL);
|
||||
ui.changed = false;
|
||||
var h = Id.handle();
|
||||
h.color = Context.swatch.base;
|
||||
Context.swatch.base = zui.Ext.colorWheel(ui, h, false, null, false);
|
||||
if (ui.changed) UIMenu.keepOpen = true;
|
||||
if (ui.inputReleased) Context.setSwatch(Context.swatch); // Trigger material preview update
|
||||
}, 3, Std.int(Input.getMouse().x) - 200, Std.int(Input.getMouse().y) - 250);
|
||||
}
|
||||
|
||||
Context.selectTime = Time.time();
|
||||
}
|
||||
if (ui.isHovered && ui.inputReleasedR) {
|
||||
var add = Project.raw.swatches.length > 1 ? 1 : 0;
|
||||
UIMenu.draw(function(ui: Zui) {
|
||||
ui.text(tr("Swatch"), Right, ui.t.HIGHLIGHT_COL);
|
||||
if (Project.raw.swatches.length > 1 && ui.button(tr("Delete"), Left)) {
|
||||
Context.setSwatch(Project.raw.swatches[i == 0 ? 1 : 0]);
|
||||
Project.raw.swatches.splice(i, 1);
|
||||
UISidebar.inst.hwnd2.redraws = 2;
|
||||
}
|
||||
}, 1 + add);
|
||||
}
|
||||
if (ui.isHovered) ui.tooltip(tr("Swatch"));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -44,24 +44,15 @@ class UIHeader {
|
||||
if (Project.assets.length > 0) ui.image(Project.getImage(Project.assets[cid]));
|
||||
}
|
||||
else if (Context.tool == ToolPicker) {
|
||||
Context.baseRPicked = Math.round(Context.baseRPicked * 10) / 10;
|
||||
Context.baseGPicked = Math.round(Context.baseGPicked * 10) / 10;
|
||||
Context.baseBPicked = Math.round(Context.baseBPicked * 10) / 10;
|
||||
Context.normalRPicked = Math.round(Context.normalRPicked * 10) / 10;
|
||||
Context.normalGPicked = Math.round(Context.normalGPicked * 10) / 10;
|
||||
Context.normalBPicked = Math.round(Context.normalBPicked * 10) / 10;
|
||||
Context.occlusionPicked = Math.round(Context.occlusionPicked * 100) / 100;
|
||||
Context.roughnessPicked = Math.round(Context.roughnessPicked * 100) / 100;
|
||||
Context.metallicPicked = Math.round(Context.metallicPicked * 100) / 100;
|
||||
var baseRPicked = Context.baseRPicked;
|
||||
var baseGPicked = Context.baseGPicked;
|
||||
var baseBPicked = Context.baseBPicked;
|
||||
var normalRPicked = Context.normalRPicked;
|
||||
var normalGPicked = Context.normalGPicked;
|
||||
var normalBPicked = Context.normalBPicked;
|
||||
var occlusionPicked = Context.occlusionPicked;
|
||||
var roughnessPicked = Context.roughnessPicked;
|
||||
var metallicPicked = Context.metallicPicked;
|
||||
var baseRPicked = Math.round(Context.swatch.base.R * 10) / 10;
|
||||
var baseGPicked = Math.round(Context.swatch.base.G * 10) / 10;
|
||||
var baseBPicked = Math.round(Context.swatch.base.B * 10) / 10;
|
||||
var normalRPicked = Math.round(Context.swatch.normal.R * 10) / 10;
|
||||
var normalGPicked = Math.round(Context.swatch.normal.G * 10) / 10;
|
||||
var normalBPicked = Math.round(Context.swatch.normal.B * 10) / 10;
|
||||
var occlusionPicked = Math.round(Context.swatch.occlusion * 100) / 100;
|
||||
var roughnessPicked = Math.round(Context.swatch.roughness * 100) / 100;
|
||||
var metallicPicked = Math.round(Context.swatch.metallic * 100) / 100;
|
||||
#if kha_metal
|
||||
ui.text('TODO'); // Skips first draw
|
||||
#end
|
||||
|
||||
@@ -54,7 +54,7 @@ class UIMenu {
|
||||
}
|
||||
else {
|
||||
var menuItems = [
|
||||
16, // MenuFile
|
||||
18, // MenuFile
|
||||
4, // MenuEdit
|
||||
#if (krom_windows || krom_linux) 14 #else 13 #end, // MenuViewport
|
||||
#if (kha_direct3d12 || kha_vulkan) 13 #else 12 #end, // MenuMode
|
||||
@@ -76,6 +76,7 @@ class UIMenu {
|
||||
if (ui.button(" " + tr("Import Font..."), Left)) Project.importAsset("ttf,ttc,otf");
|
||||
if (ui.button(" " + tr("Import Material..."), Left)) Project.importMaterial();
|
||||
if (ui.button(" " + tr("Import Brush..."), Left)) Project.importBrush();
|
||||
if (ui.button(" " + tr("Import Swatches..."), Left)) Project.importAsset("arm");
|
||||
if (ui.button(" " + tr("Import Mesh..."), Left)) Project.importMesh();
|
||||
if (ui.button(" " + tr("Reimport Mesh"), Left, Config.keymap.file_reimport_mesh)) Project.reimportMesh();
|
||||
if (ui.button(" " + tr("Reimport Textures"), Left, Config.keymap.file_reimport_textures)) Project.reimportTextures();
|
||||
@@ -84,6 +85,7 @@ class UIMenu {
|
||||
Context.layersExport = ExportVisible;
|
||||
BoxExport.showTextures();
|
||||
}
|
||||
if (ui.button(" " + tr("Export Swatches..."), Left)) Project.exportSwatches();
|
||||
if (ui.button(" " + tr("Export Mesh..."), Left)) BoxExport.showMesh();
|
||||
if (ui.button(" " + tr("Bake Material..."), Left)) BoxExport.showBakeMaterial();
|
||||
ui.fill(0, 0, sepw, 1, ui.t.ACCENT_SELECT_COL);
|
||||
|
||||
@@ -89,6 +89,12 @@ class UISidebar {
|
||||
Context.layer = Project.layers[0];
|
||||
}
|
||||
|
||||
if (Project.raw.swatches == null) {
|
||||
Project.raw.swatches = [];
|
||||
Project.raw.swatches.push(Project.makeSwatch());
|
||||
Context.swatch = Project.raw.swatches[0];
|
||||
}
|
||||
|
||||
if (Context.emptyEnvmap == null) {
|
||||
var b = Bytes.alloc(4);
|
||||
b.set(0, 3);
|
||||
@@ -682,6 +688,7 @@ class UISidebar {
|
||||
TabTextures.draw();
|
||||
TabMeshes.draw();
|
||||
TabFonts.draw();
|
||||
TabSwatches.draw();
|
||||
}
|
||||
|
||||
ui.end();
|
||||
|
||||
Reference in New Issue
Block a user