516 lines
16 KiB
Haxe
516 lines
16 KiB
Haxe
package arm;
|
|
|
|
import zui.Nodes;
|
|
import arm.ui.UISidebar;
|
|
import arm.ui.UIView2D;
|
|
import arm.ui.UIFiles;
|
|
import arm.ui.UINodes;
|
|
import arm.ui.UIToolbar;
|
|
import arm.sys.Path;
|
|
import arm.data.LayerSlot;
|
|
import arm.data.MaterialSlot;
|
|
import arm.node.MakeMaterial;
|
|
import arm.Enums;
|
|
|
|
class History {
|
|
|
|
public static var steps: Array<TStep>;
|
|
public static var undoI = 0; // Undo layer
|
|
public static var undos = 0; // Undos available
|
|
public static var redos = 0; // Redos available
|
|
public static var pushUndo = false; // Store undo on next paint
|
|
public static var undoLayers: Array<LayerSlot> = null;
|
|
|
|
public static function undo() {
|
|
if (undos > 0) {
|
|
var active = steps.length - 1 - redos;
|
|
var step = steps[active];
|
|
|
|
if (step.name == tr("New Layer")) {
|
|
Context.layer = Project.layers[step.layer];
|
|
Context.layer.delete();
|
|
}
|
|
else if (step.name == tr("Delete Layer")) {
|
|
var parent = step.layer_parent > 0 ? Project.layers[step.layer_parent - 1] : null;
|
|
var l = new LayerSlot("", step.layer_type, parent);
|
|
Project.layers.insert(step.layer, l);
|
|
Context.setLayer(l);
|
|
undoI = undoI - 1 < 0 ? Config.raw.undo_steps - 1 : undoI - 1;
|
|
var lay = undoLayers[undoI];
|
|
l.swap(lay);
|
|
l.maskOpacity = step.layer_opacity;
|
|
l.blending = step.layer_blending;
|
|
l.objectMask = step.layer_object;
|
|
MakeMaterial.parseMeshMaterial();
|
|
}
|
|
else if (step.name == tr("Clear Layer")) {
|
|
undoI = undoI - 1 < 0 ? Config.raw.undo_steps - 1 : undoI - 1;
|
|
var lay = undoLayers[undoI];
|
|
Context.layer.swap(lay);
|
|
Context.layerPreviewDirty = true;
|
|
}
|
|
else if (step.name == tr("Duplicate Layer")) {
|
|
Context.layer = Project.layers[step.layer + 1];
|
|
Context.layer.delete();
|
|
}
|
|
else if (step.name == tr("Order Layers")) {
|
|
var target = Project.layers[step.prev_order];
|
|
Project.layers[step.prev_order] = Project.layers[step.layer];
|
|
Project.layers[step.layer] = target;
|
|
}
|
|
else if (step.name == tr("Merge Layers")) {
|
|
Context.layer = Project.layers[step.layer];
|
|
Context.layer.delete();
|
|
|
|
var parent = step.layer_parent > 0 ? Project.layers[step.layer_parent - 2] : null;
|
|
var l = new LayerSlot("", step.layer_type, parent);
|
|
Project.layers.insert(step.layer, l);
|
|
Context.setLayer(l);
|
|
|
|
undoI = undoI - 1 < 0 ? Config.raw.undo_steps - 1 : undoI - 1;
|
|
var lay = undoLayers[undoI];
|
|
Context.layer.swap(lay);
|
|
|
|
var l = new LayerSlot("", step.layer_type, parent);
|
|
Project.layers.insert(step.layer + 1, l);
|
|
Context.setLayer(l);
|
|
|
|
undoI = undoI - 1 < 0 ? Config.raw.undo_steps - 1 : undoI - 1;
|
|
var lay = undoLayers[undoI];
|
|
Context.layer.swap(lay);
|
|
|
|
Context.layer.maskOpacity = step.layer_opacity;
|
|
Context.layer.blending = step.layer_blending;
|
|
Context.layer.objectMask = step.layer_object;
|
|
Context.layersPreviewDirty = true;
|
|
MakeMaterial.parseMeshMaterial();
|
|
}
|
|
else if (step.name == tr("Apply Mask")) {
|
|
Context.layer = Project.layers[step.layer];
|
|
Context.layer.delete();
|
|
|
|
Context.layer = Layers.newLayer(false);
|
|
Project.layers.remove(Context.layer);
|
|
Project.layers.insert(step.layer, Context.layer);
|
|
|
|
undoI = undoI - 1 < 0 ? Config.raw.undo_steps - 1 : undoI - 1;
|
|
var lay = undoLayers[undoI];
|
|
Context.layer.swap(lay);
|
|
|
|
Layers.newMask(false, Context.layer);
|
|
Context.layer.swap(lay);
|
|
Context.layersPreviewDirty = true;
|
|
Context.setLayer(Context.layer);
|
|
}
|
|
else if (step.name == "Apply Filter") {
|
|
undoI = undoI - 1 < 0 ? Config.raw.undo_steps - 1 : undoI - 1;
|
|
var lay = undoLayers[undoI];
|
|
Context.setLayer(Project.layers[step.layer]);
|
|
Context.layer.swap(lay);
|
|
Layers.newMask(false, Context.layer);
|
|
Context.layer.swap(lay);
|
|
Context.layerPreviewDirty = true;
|
|
}
|
|
else if (step.name == tr("To Fill Layer")) {
|
|
Context.layer.toPaintLayer();
|
|
undoI = undoI - 1 < 0 ? Config.raw.undo_steps - 1 : undoI - 1;
|
|
var lay = undoLayers[undoI];
|
|
Context.layer.swap(lay);
|
|
}
|
|
else if (step.name == tr("To Paint Layer")) {
|
|
undoI = undoI - 1 < 0 ? Config.raw.undo_steps - 1 : undoI - 1;
|
|
var lay = undoLayers[undoI];
|
|
Context.layer.swap(lay);
|
|
Context.layer.fill_layer = Project.materials[step.material];
|
|
}
|
|
else if (step.name == tr("Layer Opacity")) {
|
|
Context.setLayer(Project.layers[step.layer]);
|
|
var t = Context.layer.maskOpacity;
|
|
Context.layer.maskOpacity = step.layer_opacity;
|
|
step.layer_opacity = t;
|
|
MakeMaterial.parseMeshMaterial();
|
|
}
|
|
else if (step.name == tr("Layer Blending")) {
|
|
Context.setLayer(Project.layers[step.layer]);
|
|
var t = Context.layer.blending;
|
|
Context.layer.blending = step.layer_blending;
|
|
step.layer_blending = t;
|
|
MakeMaterial.parseMeshMaterial();
|
|
}
|
|
else if (step.name == tr("Edit Nodes")) {
|
|
swapCanvas(step);
|
|
}
|
|
else if (step.name == tr("New Material")) {
|
|
Context.material = Project.materials[step.material];
|
|
step.canvas = Context.material.canvas;
|
|
Context.material.delete();
|
|
}
|
|
else if (step.name == tr("Delete Material")) {
|
|
Context.material = new MaterialSlot(Project.materials[0].data);
|
|
Project.materials.insert(step.material, Context.material);
|
|
Context.material.canvas = step.canvas;
|
|
UINodes.inst.canvasChanged();
|
|
@:privateAccess UINodes.inst.getNodes().handle = new zui.Zui.Handle();
|
|
UINodes.inst.hwnd.redraws = 2;
|
|
}
|
|
else if (step.name == tr("Duplicate Material")) {
|
|
Context.material = Project.materials[step.material];
|
|
step.canvas = Context.material.canvas;
|
|
Context.material.delete();
|
|
}
|
|
else { // Paint operation
|
|
undoI = undoI - 1 < 0 ? Config.raw.undo_steps - 1 : undoI - 1;
|
|
var lay = undoLayers[undoI];
|
|
Context.selectPaintObject(Project.paintObjects[step.object]);
|
|
Context.setLayer(Project.layers[step.layer]);
|
|
Context.layer.swap(lay);
|
|
Context.layerPreviewDirty = true;
|
|
}
|
|
undos--;
|
|
redos++;
|
|
UISidebar.inst.hwnd0.redraws = 2;
|
|
UISidebar.inst.hwnd1.redraws = 2;
|
|
Context.ddirty = 2;
|
|
if (UIView2D.inst.show) UIView2D.inst.hwnd.redraws = 2;
|
|
}
|
|
}
|
|
|
|
public static function redo() {
|
|
if (redos > 0) {
|
|
var active = steps.length - redos;
|
|
var step = steps[active];
|
|
|
|
if (step.name == tr("New Layer")) {
|
|
var parent = step.layer_parent > 0 ? Project.layers[step.layer_parent - 1] : null;
|
|
var l = new LayerSlot("", step.layer_type, parent);
|
|
Project.layers.insert(step.layer, l);
|
|
Context.setLayer(l);
|
|
}
|
|
else if (step.name == tr("Delete Layer")) {
|
|
Context.layer = Project.layers[step.layer];
|
|
swapActive();
|
|
Context.layer.delete();
|
|
}
|
|
else if (step.name == tr("Clear Layer")) {
|
|
Context.layer = Project.layers[step.layer];
|
|
swapActive();
|
|
Context.layer.clear();
|
|
Context.layerPreviewDirty = true;
|
|
}
|
|
else if (step.name == tr("Duplicate Layer")) {
|
|
Context.layer = Project.layers[step.layer];
|
|
Context.layer = Context.layer.duplicate();
|
|
}
|
|
else if (step.name == tr("Order Layers")) {
|
|
var target = Project.layers[step.prev_order];
|
|
Project.layers[step.prev_order] = Project.layers[step.layer];
|
|
Project.layers[step.layer] = target;
|
|
}
|
|
else if (step.name == tr("Merge Layers")) {
|
|
Context.layer = Project.layers[step.layer + 1];
|
|
iron.App.notifyOnInit(redoMergeLayers);
|
|
iron.App.notifyOnInit(Layers.mergeDown);
|
|
}
|
|
else if (step.name == tr("Apply Mask")) {
|
|
function _next() {
|
|
Context.layer = Project.layers[step.layer];
|
|
copyToUndo(Context.layer.id, undoI, true);
|
|
Context.layer.applyMask();
|
|
Context.setLayer(Context.layer);
|
|
}
|
|
App.notifyOnNextFrame(_next);
|
|
}
|
|
else if (step.name == tr("Apply Filter")) {
|
|
var lay = undoLayers[undoI];
|
|
Context.setLayer(Project.layers[step.layer]);
|
|
Context.layer.swap(lay);
|
|
Layers.newMask(false, lay);
|
|
Context.layer.swap(lay);
|
|
Context.layerPreviewDirty = true;
|
|
undoI = (undoI + 1) % Config.raw.undo_steps;
|
|
}
|
|
else if (step.name == tr("To Fill Layer")) {
|
|
var lay = undoLayers[undoI];
|
|
Context.layer.swap(lay);
|
|
Context.layer.fill_layer = Project.materials[step.material];
|
|
undoI = (undoI + 1) % Config.raw.undo_steps;
|
|
}
|
|
else if (step.name == tr("To Paint Layer")) {
|
|
Context.layer.toPaintLayer();
|
|
var lay = undoLayers[undoI];
|
|
Context.layer.swap(lay);
|
|
undoI = (undoI + 1) % Config.raw.undo_steps;
|
|
}
|
|
else if (step.name == tr("Layer Opacity")) {
|
|
Context.setLayer(Project.layers[step.layer]);
|
|
var t = Context.layer.maskOpacity;
|
|
Context.layer.maskOpacity = step.layer_opacity;
|
|
step.layer_opacity = t;
|
|
MakeMaterial.parseMeshMaterial();
|
|
}
|
|
else if (step.name == tr("Layer Blending")) {
|
|
Context.setLayer(Project.layers[step.layer]);
|
|
var t = Context.layer.blending;
|
|
Context.layer.blending = step.layer_blending;
|
|
step.layer_blending = t;
|
|
MakeMaterial.parseMeshMaterial();
|
|
}
|
|
else if (step.name == tr("Edit Nodes")) {
|
|
swapCanvas(step);
|
|
}
|
|
else if (step.name == tr("New Material")) {
|
|
Context.material = new MaterialSlot(Project.materials[0].data);
|
|
Project.materials.insert(step.material, Context.material);
|
|
Context.material.canvas = step.canvas;
|
|
UINodes.inst.canvasChanged();
|
|
@:privateAccess UINodes.inst.getNodes().handle = new zui.Zui.Handle();
|
|
UINodes.inst.hwnd.redraws = 2;
|
|
}
|
|
else if (step.name == tr("Delete Material")) {
|
|
Context.material = Project.materials[step.material];
|
|
step.canvas = Context.material.canvas;
|
|
Context.material.delete();
|
|
}
|
|
else if (step.name == tr("Duplicate Material")) {
|
|
Context.material = new MaterialSlot(Project.materials[0].data);
|
|
Project.materials.insert(step.material, Context.material);
|
|
Context.material.canvas = step.canvas;
|
|
UINodes.inst.canvasChanged();
|
|
@:privateAccess UINodes.inst.getNodes().handle = new zui.Zui.Handle();
|
|
UINodes.inst.hwnd.redraws = 2;
|
|
}
|
|
else { // Paint operation
|
|
var lay = undoLayers[undoI];
|
|
Context.selectPaintObject(Project.paintObjects[step.object]);
|
|
Context.setLayer(Project.layers[step.layer]);
|
|
Context.layer.swap(lay);
|
|
Context.layerPreviewDirty = true;
|
|
undoI = (undoI + 1) % Config.raw.undo_steps;
|
|
}
|
|
undos++;
|
|
redos--;
|
|
UISidebar.inst.hwnd0.redraws = 2;
|
|
UISidebar.inst.hwnd1.redraws = 2;
|
|
Context.ddirty = 2;
|
|
if (UIView2D.inst.show) UIView2D.inst.hwnd.redraws = 2;
|
|
}
|
|
}
|
|
|
|
public static function reset() {
|
|
steps = [{name: tr("New"), layer: 0, layer_type: SlotLayer, layer_parent: -1, object: 0, material: 0, brush: 0}];
|
|
undos = 0;
|
|
redos = 0;
|
|
undoI = 0;
|
|
}
|
|
|
|
public static function paint() {
|
|
var isMask = Context.layer.isMask();
|
|
copyToUndo(Context.layer.id, undoI, isMask);
|
|
|
|
pushUndo = false;
|
|
push(tr(UIToolbar.inst.toolNames[Context.tool]));
|
|
}
|
|
|
|
public static function newLayer() {
|
|
push(tr("New Layer"));
|
|
}
|
|
|
|
public static function duplicateLayer() {
|
|
push(tr("Duplicate Layer"));
|
|
}
|
|
|
|
public static function deleteLayer() {
|
|
swapActive();
|
|
push(tr("Delete Layer"));
|
|
}
|
|
|
|
public static function clearLayer() {
|
|
swapActive();
|
|
push(tr("Clear Layer"));
|
|
}
|
|
|
|
public static function orderLayers(prevOrder: Int) {
|
|
var step = push(tr("Order Layers"));
|
|
step.prev_order = prevOrder;
|
|
}
|
|
|
|
public static function mergeLayers() {
|
|
copyMergingLayers();
|
|
|
|
var step = push(tr("Merge Layers"));
|
|
step.layer -= 1; // Merge down
|
|
steps.shift(); // Merge consumes 2 steps
|
|
undos--;
|
|
// TODO: use undo layer in Layers.mergeDown to save memory
|
|
}
|
|
|
|
public static function applyMask() {
|
|
copyToUndo(Context.layer.id, undoI, true);
|
|
push(tr("Apply Mask"));
|
|
}
|
|
|
|
@:keep
|
|
public static function applyFilter() {
|
|
copyToUndo(Context.layer.id, undoI, true);
|
|
push(tr("Apply Filter"));
|
|
}
|
|
|
|
public static function toFillLayer() {
|
|
copyToUndo(Context.layer.id, undoI, Context.layer.isMask());
|
|
push(tr("To Fill Layer"));
|
|
}
|
|
|
|
public static function toPaintLayer() {
|
|
copyToUndo(Context.layer.id, undoI, Context.layer.isMask());
|
|
push(tr("To Paint Layer"));
|
|
}
|
|
|
|
public static function layerOpacity() {
|
|
push(tr("Layer Opacity"));
|
|
}
|
|
|
|
// public static function layerObject() {
|
|
// push("Layer Object");
|
|
// }
|
|
|
|
public static function layerBlending() {
|
|
push(tr("Layer Blending"));
|
|
}
|
|
|
|
public static function newMaterial() {
|
|
var step = push(tr("New Material"));
|
|
step.canvas_type = 0;
|
|
step.canvas = haxe.Json.parse(haxe.Json.stringify(Context.material.canvas));
|
|
}
|
|
|
|
public static function deleteMaterial() {
|
|
var step = push(tr("Delete Material"));
|
|
step.canvas_type = 0;
|
|
step.canvas = haxe.Json.parse(haxe.Json.stringify(Context.material.canvas));
|
|
}
|
|
|
|
public static function duplicateMaterial() {
|
|
var step = push(tr("Duplicate Material"));
|
|
step.canvas_type = 0;
|
|
step.canvas = haxe.Json.parse(haxe.Json.stringify(Context.material.canvas));
|
|
}
|
|
|
|
public static function editNodes(canvas: TNodeCanvas, canvas_type: Int, canvas_group: Null<Int> = null) {
|
|
var step = push(tr("Edit Nodes"));
|
|
step.canvas_type = canvas_type;
|
|
step.canvas_group = canvas_group;
|
|
step.canvas = haxe.Json.parse(haxe.Json.stringify(canvas));
|
|
}
|
|
|
|
static function push(name: String): TStep {
|
|
#if (krom_windows || krom_linux || krom_darwin)
|
|
var filename = Project.filepath == "" ? UIFiles.filename : Project.filepath.substring(Project.filepath.lastIndexOf(Path.sep) + 1, Project.filepath.length - 4);
|
|
kha.Window.get(0).title = filename + "* - " + Main.title;
|
|
#end
|
|
|
|
if (undos < Config.raw.undo_steps) undos++;
|
|
if (redos > 0) {
|
|
for (i in 0...redos) steps.pop();
|
|
redos = 0;
|
|
}
|
|
|
|
var opos = Project.paintObjects.indexOf(Context.paintObject);
|
|
var lpos = Project.layers.indexOf(Context.layer);
|
|
var mpos = Project.materials.indexOf(Context.material);
|
|
var bpos = Project.brushes.indexOf(Context.brush);
|
|
|
|
steps.push({
|
|
name: name,
|
|
layer: lpos,
|
|
layer_type: Context.layer.isMask() ? SlotMask : Context.layer.isGroup() ? SlotGroup : SlotLayer,
|
|
layer_parent: Context.layer.parent == null ? -1 : Project.layers.indexOf(Context.layer.parent),
|
|
object: opos,
|
|
material: mpos,
|
|
brush: bpos,
|
|
layer_opacity: Context.layer.maskOpacity,
|
|
layer_object: Context.layer.objectMask,
|
|
layer_blending: Context.layer.blending
|
|
});
|
|
|
|
while (steps.length > Config.raw.undo_steps + 1) steps.shift();
|
|
return steps[steps.length - 1];
|
|
}
|
|
|
|
static function redoMergeLayers() {
|
|
copyMergingLayers();
|
|
}
|
|
|
|
static function copyMergingLayers() {
|
|
var lay = Context.layer;
|
|
copyToUndo(lay.id, undoI, Context.layer.isMask());
|
|
|
|
var below = Project.layers.indexOf(lay) - 1;
|
|
lay = Project.layers[below];
|
|
copyToUndo(lay.id, undoI, Context.layer.isMask());
|
|
}
|
|
|
|
static function swapActive() {
|
|
var undoLayer = undoLayers[undoI];
|
|
undoLayer.swap(Context.layer);
|
|
undoI = (undoI + 1) % Config.raw.undo_steps;
|
|
}
|
|
|
|
static function copyToUndo(fromId: Int, toId: Int, isMask: Bool) {
|
|
var path = iron.RenderPath.active;
|
|
if (isMask) {
|
|
path.setTarget("texpaint_undo" + toId);
|
|
path.bindTarget("texpaint" + fromId, "tex");
|
|
// path.drawShader("shader_datas/copy_pass/copyR8_pass");
|
|
path.drawShader("shader_datas/copy_pass/copy_pass");
|
|
}
|
|
else {
|
|
path.setTarget("texpaint_undo" + toId, ["texpaint_nor_undo" + toId, "texpaint_pack_undo" + toId]);
|
|
path.bindTarget("texpaint" + fromId, "tex0");
|
|
path.bindTarget("texpaint_nor" + fromId, "tex1");
|
|
path.bindTarget("texpaint_pack" + fromId, "tex2");
|
|
path.drawShader("shader_datas/copy_mrt3_pass/copy_mrt3_pass");
|
|
}
|
|
undoI = (undoI + 1) % Config.raw.undo_steps;
|
|
}
|
|
|
|
static function getCanvasOwner(step: TStep): Dynamic {
|
|
return step.canvas_group == null ? Project.materials[step.material] : Project.materialGroups[step.canvas_group];
|
|
}
|
|
|
|
static function swapCanvas(step: TStep) {
|
|
if (step.canvas_type == 0) {
|
|
var _canvas = getCanvasOwner(step).canvas;
|
|
getCanvasOwner(step).canvas = step.canvas;
|
|
step.canvas = _canvas;
|
|
Context.material = Project.materials[step.material];
|
|
}
|
|
else {
|
|
var _canvas = Project.brushes[step.brush].canvas;
|
|
Project.brushes[step.brush].canvas = step.canvas;
|
|
step.canvas = _canvas;
|
|
Context.brush = Project.brushes[step.brush];
|
|
}
|
|
|
|
UINodes.inst.canvasChanged();
|
|
@:privateAccess UINodes.inst.getNodes().handle = new zui.Zui.Handle();
|
|
UINodes.inst.hwnd.redraws = 2;
|
|
}
|
|
}
|
|
|
|
typedef TStep = {
|
|
public var name: String;
|
|
public var layer: Int;
|
|
public var layer_type: LayerSlotType;
|
|
public var layer_parent: Int;
|
|
public var object: Int;
|
|
public var material: Int;
|
|
public var brush: Int;
|
|
@:optional public var layer_opacity: Float;
|
|
@:optional public var layer_object: Int;
|
|
@:optional public var layer_blending: Int;
|
|
@:optional public var prev_order: Int; // Previous layer position
|
|
@:optional public var canvas: TNodeCanvas; // Node history
|
|
@:optional public var canvas_type: Int;
|
|
@:optional public var canvas_group: Int;
|
|
}
|