diff --git a/Sources/arm/nodes/Logic.hx b/Sources/arm/nodes/Logic.hx index 43274fcb..b3eca42d 100644 --- a/Sources/arm/nodes/Logic.hx +++ b/Sources/arm/nodes/Logic.hx @@ -98,7 +98,9 @@ class Logic { for (i in 0...5) { for (b in node.buttons) { if (b.name == 'property' + i) { - Reflect.setProperty(v, b.name, b.data[b.default_value]); + var arrayData = Std.is(b.data, Array); + var texts = arrayData ? b.data : Nodes.getEnumTexts(); + Reflect.setProperty(v, b.name, texts[b.default_value]); } } } diff --git a/Sources/arm/nodes/MaterialBuilder.hx b/Sources/arm/nodes/MaterialBuilder.hx index 07583713..2d079c39 100644 --- a/Sources/arm/nodes/MaterialBuilder.hx +++ b/Sources/arm/nodes/MaterialBuilder.hx @@ -208,7 +208,7 @@ class MaterialBuilder { // Non-continuous // frag.write('float dist = distance(wposition, winp.xyz);'); } - else { // !paint3d + else { // !brush3d frag.write('vec2 binp = inp.xy * 2.0 - 1.0;'); frag.write('binp.x *= aspectRatio;'); frag.write('binp = binp * 0.5 + 0.5;'); @@ -468,6 +468,21 @@ class MaterialBuilder { frag.write('opacity *= textureLod(textexttool, texCoord, 0.0).r;'); } + if (UITrait.inst.brushMaskImage != null && Context.tool == ToolBrush) { + frag.add_uniform('sampler2D texbrushmask', '_texbrushmask'); + frag.write('vec2 binp_mask = inp.xy * 2.0 - 1.0;'); + frag.write('binp_mask.x *= aspectRatio;'); + frag.write('binp_mask = binp_mask * 0.5 + 0.5;'); + frag.write('vec2 pa_mask = bsp.xy - binp_mask.xy;'); + frag.write('pa_mask /= brushRadius;'); + if (UITrait.inst.brush3d) { + frag.add_uniform('vec3 eye', '_cameraPosition'); + frag.write('pa_mask *= distance(eye, winp) / 1.5;'); + } + frag.write('pa_mask = pa_mask.xy * 0.5 + 0.5;'); + frag.write('opacity *= textureLod(texbrushmask, pa_mask, 0).r;'); + } + if (Context.tool == ToolParticle) { // particle mask frag.add_uniform('sampler2D texparticle', '_texparticle'); #if (kha_opengl || kha_webgl) diff --git a/Sources/arm/nodes/NodesBrush.hx b/Sources/arm/nodes/NodesBrush.hx index 492cd6fd..18276f18 100644 --- a/Sources/arm/nodes/NodesBrush.hx +++ b/Sources/arm/nodes/NodesBrush.hx @@ -28,58 +28,58 @@ class NodesBrush { ], buttons: [] }, - { - id: 0, - name: "Brush Output", - type: "BrushOutputNode", - x: 0, - y: 0, - color: 0xff4982a0, - inputs: [ - { - id: 0, - node_id: 0, - name: "Position", - type: "VECTOR", - color: 0xff63c763, - default_value: [0.0, 0.0, 0.0] - }, - { - id: 0, - node_id: 0, - name: "Radius", - type: "VALUE", - color: 0xffa1a1a1, - default_value: 1.0 - }, - { - id: 0, - node_id: 0, - name: "Opacity", - type: "VALUE", - color: 0xffa1a1a1, - default_value: 1.0 - }, - { - id: 0, - node_id: 0, - name: "Hardness", - type: "VALUE", - color: 0xffa1a1a1, - default_value: 1.0 - }, - { - id: 0, - node_id: 0, - name: "UV Scale", - type: "VALUE", - color: 0xffa1a1a1, - default_value: 1.0 - } - ], - outputs: [], - buttons: [] - }, + // { + // id: 0, + // name: "Brush Output", + // type: "BrushOutputNode", + // x: 0, + // y: 0, + // color: 0xff4982a0, + // inputs: [ + // { + // id: 0, + // node_id: 0, + // name: "Position", + // type: "VECTOR", + // color: 0xff63c763, + // default_value: [0.0, 0.0, 0.0] + // }, + // { + // id: 0, + // node_id: 0, + // name: "Radius", + // type: "VALUE", + // color: 0xffa1a1a1, + // default_value: 1.0 + // }, + // { + // id: 0, + // node_id: 0, + // name: "Opacity", + // type: "VALUE", + // color: 0xffa1a1a1, + // default_value: 1.0 + // }, + // { + // id: 0, + // node_id: 0, + // name: "Hardness", + // type: "VALUE", + // color: 0xffa1a1a1, + // default_value: 1.0 + // }, + // { + // id: 0, + // node_id: 0, + // name: "UV Scale", + // type: "VALUE", + // color: 0xffa1a1a1, + // default_value: 1.0 + // } + // ], + // outputs: [], + // buttons: [] + // }, { id: 0, name: "Value", @@ -378,7 +378,8 @@ class NodesBrush { ], buttons: [ { - name: "File", + // name: "File", + name: "property0", type: "ENUM", default_value: 0, data: "" diff --git a/Sources/arm/nodes/brush/BrushOutputNode.hx b/Sources/arm/nodes/brush/BrushOutputNode.hx index fa95fdd0..a58373e2 100644 --- a/Sources/arm/nodes/brush/BrushOutputNode.hx +++ b/Sources/arm/nodes/brush/BrushOutputNode.hx @@ -9,13 +9,28 @@ class BrushOutputNode extends LogicNode { public function new(tree:LogicTree) { super(tree); - UITrait.inst.notifyOnBrush(run); + UITrait.inst.onBrush = run; } override function run(from:Int) { UITrait.inst.paintVec = inputs[0].get(); UITrait.inst.brushNodesRadius = inputs[1].get(); - UITrait.inst.brushNodesOpacity = inputs[2].get(); + var opac:Dynamic = inputs[2].get(); // Float or texture name + if (opac == null) opac = 1.0; + var lastMask = UITrait.inst.brushMaskImage; + if (Std.is(opac, String)) { + UITrait.inst.brushNodesOpacity = 1.0; + var index = Project.assetNames.indexOf(opac); + var asset = Project.assets[index]; + UITrait.inst.brushMaskImage = UITrait.inst.getImage(asset); + } + else { + UITrait.inst.brushNodesOpacity = opac; + UITrait.inst.brushMaskImage = null; + } + if (lastMask != UITrait.inst.brushMaskImage) { + MaterialParser.parsePaintMaterial(); + } UITrait.inst.brushNodesHardness = inputs[3].get(); UITrait.inst.brushNodesScale = inputs[4].get(); @@ -67,7 +82,5 @@ class BrushOutputNode extends LogicNode { Context.rdirty = 2; } } - - runOutput(0); } } diff --git a/Sources/arm/nodes/brush/TEX_IMAGE.hx b/Sources/arm/nodes/brush/TEX_IMAGE.hx new file mode 100644 index 00000000..a6106a26 --- /dev/null +++ b/Sources/arm/nodes/brush/TEX_IMAGE.hx @@ -0,0 +1,15 @@ +package arm.nodes.brush; + +@:keep +class TEX_IMAGE extends LogicNode { + + public var property0:String; // file + + public function new(tree:LogicTree) { + super(tree); + } + + override function get(from:Int):Dynamic { + return property0; + } +} diff --git a/Sources/arm/render/Uniforms.hx b/Sources/arm/render/Uniforms.hx index 6013fe39..2e7e07eb 100644 --- a/Sources/arm/render/Uniforms.hx +++ b/Sources/arm/render/Uniforms.hx @@ -121,6 +121,9 @@ class Uniforms { else if (link == "_texdecalmask") { // Opacity map for decal return UITrait.inst.decalMaskImage; } + else if (link == "_texbrushmask") { + return UITrait.inst.brushMaskImage; + } else if (link == "_texpaint_undo") { var i = History.undoI - 1 < 0 ? Config.raw.undo_steps - 1 : History.undoI - 1; return RenderPath.active.renderTargets.get("texpaint_undo" + i).image; diff --git a/Sources/arm/ui/UINodes.hx b/Sources/arm/ui/UINodes.hx index fdc3a6af..6e54e0a2 100644 --- a/Sources/arm/ui/UINodes.hx +++ b/Sources/arm/ui/UINodes.hx @@ -68,6 +68,7 @@ class UINodes { inst = this; Nodes.excludeRemove.push("OUTPUT_MATERIAL_PBR"); + Nodes.excludeRemove.push("BrushOutputNode"); // Cycles.arm_export_tangents = false; Data.getBlob('defaults/default_material.json', function(b1:Blob) { @@ -418,7 +419,7 @@ class UINodes { var canvas = canvasType == 0 ? canvas : canvasBrush; var numNodes = list[menuCategory].length; - var ph = numNodes * 20 * ui.SCALE; + var ph = numNodes * 22 * ui.SCALE; var py = popupY; g.color = ui.t.WINDOW_BG_COL; var menuw = Std.int(ew * 1.6); diff --git a/Sources/arm/ui/UITrait.hx b/Sources/arm/ui/UITrait.hx index 46165b6e..073c89d1 100644 --- a/Sources/arm/ui/UITrait.hx +++ b/Sources/arm/ui/UITrait.hx @@ -124,8 +124,7 @@ class UITrait { public var layerFilter = 0; - var onBrush:ArrayVoid> = []; - + public var onBrush:Int->Void = null; public var paintVec = new Vec4(); public var lastPaintX = -1.0; public var lastPaintY = -1.0; @@ -148,6 +147,7 @@ class UITrait { public var brushNodesRadius = 1.0; public var brushNodesOpacity = 1.0; + public var brushMaskImage:Image = null; public var brushNodesScale = 1.0; public var brushNodesHardness = 1.0; @@ -229,10 +229,6 @@ class UITrait { public var worktab = Id.handle({position: 0}); public var toolNames = ["Brush", "Eraser", "Fill", "Decal", "Text", "Clone", "Blur", "Particle", "Bake", "ColorID", "Picker"]; - public function notifyOnBrush(f:Int->Void) { - onBrush.push(f); - } - public function new() { inst = this; @@ -621,7 +617,7 @@ class UITrait { } } brushTime += Time.delta; - for (f in onBrush) f(0); + if (onBrush != null) onBrush(0); } }