package arm; import kha.Image; import kha.graphics4.TextureFormat; import kha.graphics4.TextureUnit; import kha.graphics4.ConstantLocation; import kha.graphics4.PipelineState; import kha.graphics4.VertexStructure; import kha.graphics4.VertexData; import kha.graphics4.BlendingFactor; import kha.graphics4.CompareMode; import iron.RenderPath; import arm.ui.UITrait; import arm.data.LayerSlot; import arm.node.MaterialParser; import arm.render.RenderPathPaint; import arm.util.MeshUtil; import arm.Tool; import arm.Project; class Layers { public static var pipe: PipelineState = null; public static var pipeCopy: PipelineState; public static var pipeMask: PipelineState; public static var tex0: TextureUnit; public static var tex1: TextureUnit; public static var tex2: TextureUnit; public static var texa: TextureUnit; public static var texb: TextureUnit; public static var texc: TextureUnit; public static var opac: ConstantLocation; public static var blending: ConstantLocation; public static var tex0Mask: TextureUnit; public static var texaMask: TextureUnit; public static var imga: Image = null; public static var imgb: Image = null; public static var imgc: Image = null; public static var expa: Image = null; public static var expb: Image = null; public static var expc: Image = null; public static var expd: Image = null; public static var pipeCursor: PipelineState; public static var cursorVP: ConstantLocation; public static var cursorInvVP: ConstantLocation; public static var cursorMouse: ConstantLocation; public static var cursorStep: ConstantLocation; public static var cursorRadius: ConstantLocation; public static var cursorTex: TextureUnit; public static var cursorGbufferD: TextureUnit; public static var cursorGbuffer0: TextureUnit; public static inline var defaultBase = 0.5; public static inline var defaultRough = 0.4; public static function initLayers(g: kha.graphics4.Graphics) { g.end(); var layers = Project.layers; layers[0].texpaint.g4.begin(); layers[0].texpaint.g4.clear(kha.Color.fromFloats(defaultBase, defaultBase, defaultBase, 0.0)); // Base layers[0].texpaint.g4.end(); layers[0].texpaint_nor.g4.begin(); layers[0].texpaint_nor.g4.clear(kha.Color.fromFloats(0.5, 0.5, 1.0, 0.0)); // Nor layers[0].texpaint_nor.g4.end(); layers[0].texpaint_pack.g4.begin(); layers[0].texpaint_pack.g4.clear(kha.Color.fromFloats(1.0, defaultRough, 0.0, 0.0)); // Occ, rough, met layers[0].texpaint_pack.g4.end(); g.begin(); iron.App.removeRender(initLayers); Context.layerPreviewDirty = true; Context.ddirty = 3; } public static function resizeLayers(g: kha.graphics4.Graphics) { var C = Config.raw; if (App.resHandle.position >= 4) { // Save memory for >=16k C.undo_steps = 2; if (UITrait.inst.undoHandle != null) UITrait.inst.undoHandle.value = C.undo_steps; while (History.undoLayers.length > C.undo_steps) { var l = History.undoLayers.pop(); l.unload(); } } g.end(); for (l in Project.layers) l.resizeAndSetBits(); for (l in History.undoLayers) l.resizeAndSetBits(); var rts = RenderPath.active.renderTargets; rts.get("texpaint_blend0").image.unload(); rts.get("texpaint_blend0").raw.width = Config.getTextureRes(); rts.get("texpaint_blend0").raw.height = Config.getTextureRes(); rts.get("texpaint_blend0").image = Image.createRenderTarget(Config.getTextureRes(), Config.getTextureRes(), TextureFormat.L8); rts.get("texpaint_blend1").image.unload(); rts.get("texpaint_blend1").raw.width = Config.getTextureRes(); rts.get("texpaint_blend1").raw.height = Config.getTextureRes(); rts.get("texpaint_blend1").image = Image.createRenderTarget(Config.getTextureRes(), Config.getTextureRes(), TextureFormat.L8); Context.brushBlendDirty = true; if (rts.get("texpaint_blur") != null) { rts.get("texpaint_blur").image.unload(); var size = Std.int(Config.getTextureRes() * 0.95); rts.get("texpaint_blur").raw.width = size; rts.get("texpaint_blur").raw.height = size; rts.get("texpaint_blur").image = Image.createRenderTarget(size, size); } g.begin(); Context.ddirty = 2; iron.App.removeRender(resizeLayers); } public static function setLayerBits(g: kha.graphics4.Graphics) { g.end(); for (l in Project.layers) l.resizeAndSetBits(); for (l in History.undoLayers) l.resizeAndSetBits(); g.begin(); iron.App.removeRender(setLayerBits); } public static function makePipe() { pipe = new PipelineState(); pipe.vertexShader = Reflect.field(kha.Shaders, "layer_merge_vert"); pipe.fragmentShader = Reflect.field(kha.Shaders, "layer_merge_frag"); var vs = new VertexStructure(); vs.add("pos", VertexData.Float2); pipe.inputLayout = [vs]; pipe.compile(); tex0 = pipe.getTextureUnit("tex0"); tex1 = pipe.getTextureUnit("tex1"); tex2 = pipe.getTextureUnit("tex2"); texa = pipe.getTextureUnit("texa"); texb = pipe.getTextureUnit("texb"); texc = pipe.getTextureUnit("texc"); opac = pipe.getConstantLocation("opac"); blending = pipe.getConstantLocation("blending"); pipeCopy = new PipelineState(); pipeCopy.vertexShader = Reflect.field(kha.Shaders, "layer_view_vert"); pipeCopy.fragmentShader = Reflect.field(kha.Shaders, "layer_view_frag"); var vs = new VertexStructure(); vs.add("pos", VertexData.Float3); vs.add("tex", VertexData.Float2); vs.add("col", VertexData.Float4); pipeCopy.inputLayout = [vs]; pipeCopy.compile(); pipeMask = new PipelineState(); pipeMask.vertexShader = Reflect.field(kha.Shaders, "layer_merge_vert"); pipeMask.fragmentShader = Reflect.field(kha.Shaders, "mask_merge_frag"); var vs = new VertexStructure(); vs.add("pos", VertexData.Float2); pipeMask.inputLayout = [vs]; pipeMask.compile(); tex0Mask = pipeMask.getTextureUnit("tex0"); texaMask = pipeMask.getTextureUnit("texa"); } public static function makeCursorPipe() { pipeCursor = new PipelineState(); pipeCursor.vertexShader = Reflect.field(kha.Shaders, "cursor_vert"); pipeCursor.fragmentShader = Reflect.field(kha.Shaders, "cursor_frag"); var vs = new VertexStructure(); vs.add("pos", VertexData.Short4Norm); vs.add("nor", VertexData.Short2Norm); vs.add("tex", VertexData.Short2Norm); pipeCursor.inputLayout = [vs]; pipeCursor.blendSource = BlendingFactor.SourceAlpha; pipeCursor.blendDestination = BlendingFactor.InverseSourceAlpha; pipeCursor.depthWrite = false; pipeCursor.depthMode = CompareMode.Always; pipeCursor.compile(); cursorVP = pipeCursor.getConstantLocation("VP"); cursorInvVP = pipeCursor.getConstantLocation("invVP"); cursorMouse = pipeCursor.getConstantLocation("mouse"); cursorStep = pipeCursor.getConstantLocation("step"); cursorRadius = pipeCursor.getConstantLocation("radius"); cursorGbufferD = pipeCursor.getTextureUnit("gbufferD"); cursorGbuffer0 = pipeCursor.getTextureUnit("gbuffer0"); cursorTex = pipeCursor.getTextureUnit("tex"); } public static function makeTempImg() { var l = Project.layers[0]; if (imga != null && imga.width != l.texpaint.width) { RenderPath.active.renderTargets.get("temptex0").unload(); RenderPath.active.renderTargets.get("temptex1").unload(); RenderPath.active.renderTargets.get("temptex2").unload(); RenderPath.active.renderTargets.remove("temptex0"); RenderPath.active.renderTargets.remove("temptex1"); RenderPath.active.renderTargets.remove("temptex2"); imga = null; imgb = null; imgc = null; } if (imga == null) { { var t = new RenderTargetRaw(); t.name = "temptex0"; t.width = l.texpaint.width; t.height = l.texpaint.height; t.format = "RGBA32"; var rt = RenderPath.active.createRenderTarget(t); imga = rt.image; } { var t = new RenderTargetRaw(); t.name = "temptex1"; t.width = l.texpaint.width; t.height = l.texpaint.height; t.format = "RGBA32"; var rt = RenderPath.active.createRenderTarget(t); imgb = rt.image; } { var t = new RenderTargetRaw(); t.name = "temptex2"; t.width = l.texpaint.width; t.height = l.texpaint.height; t.format = "RGBA32"; var rt = RenderPath.active.createRenderTarget(t); imgc = rt.image; } } } public static function makeExportImg() { var l = Project.layers[0]; if (expa != null && expa.width != l.texpaint.width) { expa.unload(); expb.unload(); expc.unload(); expd.unload(); expa = null; expb = null; expc = null; expd = null; } if (expa == null) { expa = Image.createRenderTarget(l.texpaint.width, l.texpaint.height); expb = Image.createRenderTarget(l.texpaint.width, l.texpaint.height); expc = Image.createRenderTarget(l.texpaint.width, l.texpaint.height); expd = Image.createRenderTarget(l.texpaint.width, l.texpaint.height); } } public static function mergeSelectedLayer(g: kha.graphics4.Graphics) { if (pipe == null) makePipe(); var l0 = Project.layers[0]; var l1 = Context.layer; for (i in 1...Project.layers.length) { // Merge down if (Project.layers[i] == l1) { l0 = Project.layers[i - 1]; break; } } g.end(); makeTempImg(); if (l1.texpaint_mask != null) { l1.applyMask(); } // Copy layer0 to temp imga.g2.begin(false); imga.g2.pipeline = pipeCopy; imga.g2.drawImage(l0.texpaint, 0, 0); imga.g2.end(); imgb.g2.begin(false); imgb.g2.pipeline = pipeCopy; imgb.g2.drawImage(l0.texpaint_nor, 0, 0); imgb.g2.end(); imgc.g2.begin(false); imgc.g2.pipeline = pipeCopy; imgc.g2.drawImage(l0.texpaint_pack, 0, 0); imgc.g2.end(); // Merge into layer0 if (iron.data.ConstData.screenAlignedVB == null) iron.data.ConstData.createScreenAlignedData(); l0.texpaint.g4.begin([l0.texpaint_nor, l0.texpaint_pack]); l0.texpaint.g4.setPipeline(pipe); l0.texpaint.g4.setTexture(tex0, l1.texpaint); l0.texpaint.g4.setTexture(tex1, l1.texpaint_nor); l0.texpaint.g4.setTexture(tex2, l1.texpaint_pack); l0.texpaint.g4.setTexture(texa, imga); l0.texpaint.g4.setTexture(texb, imgb); l0.texpaint.g4.setTexture(texc, imgc); l0.texpaint.g4.setFloat(opac, l1.maskOpacity); l0.texpaint.g4.setInt(blending, l1.blending); l0.texpaint.g4.setVertexBuffer(iron.data.ConstData.screenAlignedVB); l0.texpaint.g4.setIndexBuffer(iron.data.ConstData.screenAlignedIB); l0.texpaint.g4.drawIndexedVertices(); l0.texpaint.g4.end(); g.begin(); Context.layer.delete(); iron.App.removeRender(mergeSelectedLayer); Context.setLayer(l0); Context.layerPreviewDirty = true; } public static function isFillMaterial(): Bool { var m = Context.material; for (l in Project.layers) if (l.material_mask == m) return true; return false; } public static function updateFillLayers(fills = 1) { var layers = Project.layers; var selectedLayer = Context.layer; var isMask = Context.layerIsMask; var selectedTool = Context.tool; var current: kha.graphics4.Graphics2 = null; var first = true; for (l in layers) { if (l.material_mask == Context.material) { if (first) { current = @:privateAccess kha.graphics4.Graphics2.current; if (current != null) current.end(); Context.pdirty = fills; Context.layerIsMask = false; Context.tool = ToolFill; } Context.layer = l; setObjectMask(); if (first) { first = false; MaterialParser.parsePaintMaterial(); } for (i in 0...fills) { RenderPathPaint.commandsPaint(); } } } if (!first) { Context.pdirty = 0; Context.ddirty = 2; Context.rdirty = 2; if (current != null) current.begin(false); Context.layer = selectedLayer; Context.layerIsMask = isMask; setObjectMask(); Context.tool = selectedTool; } } public static function setObjectMask() { var ar = ["None"]; for (p in Project.paintObjects) ar.push(p.name); var mask = Context.layer.objectMask; if (UITrait.inst.layerFilter > 0) mask = UITrait.inst.layerFilter; if (mask > 0) { if (Context.mergedObject != null) Context.mergedObject.visible = false; var o = Project.paintObjects[0]; for (p in Project.paintObjects) if (p.name == ar[mask]) { o = p; break; } Context.selectPaintObject(o); } else { if (Context.mergedObject == null) { MeshUtil.mergeMesh(); } Context.selectPaintObject(Context.mainObject()); Context.paintObject.skip_context = "paint"; Context.mergedObject.visible = true; } } public static function newLayer(clear = true): LayerSlot { if (Project.layers.length > 255) return null; var l = new LayerSlot(); Project.layers.push(l); Context.setLayer(l); if (clear) iron.App.notifyOnRender(l.clear); Context.layerPreviewDirty = true; return l; } public static function createFillLayer() { function makeFill(g: kha.graphics4.Graphics) { g.end(); var l = newLayer(false); History.newLayer(); l.objectMask = UITrait.inst.layerFilter; History.toFillLayer(); l.toFillLayer(); g.begin(); iron.App.removeRender(makeFill); } iron.App.notifyOnRender(makeFill); } public static function createImageMask(asset: TAsset) { var l = Context.layer; if (l != Project.layers[0]) { History.newMask(); l.createMask(0x00000000, true, UITrait.inst.getImage(asset)); Context.setLayer(l, true); Context.layerPreviewDirty = true; } } }