Paint shader fixes
This commit is contained in:
@@ -300,7 +300,7 @@ class Layers {
|
||||
}
|
||||
|
||||
for (i in 0...fills) {
|
||||
arm.renderpath.RenderPathDeferred.commandsPaint();
|
||||
arm.renderpath.RenderPathPaint.commandsPaint();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -302,6 +302,12 @@ class MaterialBuilder {
|
||||
frag.write('vec3 nortan = $nortan;');
|
||||
frag.write('float height = $height;');
|
||||
frag.write('float opacity = $opac * brushOpacity;');
|
||||
if (UITrait.inst.selectedMaterial.paintEmis) {
|
||||
frag.write('float emis = 0.0;');
|
||||
}
|
||||
if (UITrait.inst.selectedMaterial.paintSubs) {
|
||||
frag.write('float subs = 0.0;');
|
||||
}
|
||||
}
|
||||
else { // Blur
|
||||
#if (kha_opengl || kha_webgl)
|
||||
@@ -317,6 +323,12 @@ class MaterialBuilder {
|
||||
frag.write('vec3 nortan = vec3(0.0, 0.0, 0.0);');
|
||||
frag.write('float height = 0.0;');
|
||||
frag.write('float opacity = 1.0 * brushOpacity;');
|
||||
if (UITrait.inst.selectedMaterial.paintEmis) {
|
||||
frag.write('float emis = 0.0;');
|
||||
}
|
||||
if (UITrait.inst.selectedMaterial.paintSubs) {
|
||||
frag.write('float subs = 0.0;');
|
||||
}
|
||||
#if kha_direct3d11
|
||||
frag.write('const float blur_weight[15] = {0.034619 / 2.0, 0.044859 / 2.0, 0.055857 / 2.0, 0.066833 / 2.0, 0.076841 / 2.0, 0.084894 / 2.0, 0.090126 / 2.0, 0.09194 / 2.0, 0.090126 / 2.0, 0.084894 / 2.0, 0.076841 / 2.0, 0.066833 / 2.0, 0.055857 / 2.0, 0.044859 / 2.0, 0.034619 / 2.0};');
|
||||
#else
|
||||
|
||||
@@ -15,7 +15,6 @@ class RenderPathDeferred {
|
||||
static var voxels = "voxels";
|
||||
static var voxelsLast = "voxels";
|
||||
#end
|
||||
static var initVoxels = true; // Bake AO
|
||||
static var taaFrame = 0;
|
||||
|
||||
public static function init(_path:RenderPath) {
|
||||
@@ -269,7 +268,7 @@ class RenderPathDeferred {
|
||||
path.drawMeshes("depth");
|
||||
}
|
||||
|
||||
commandsPaint();
|
||||
RenderPathPaint.commandsPaint();
|
||||
//
|
||||
|
||||
if (UITrait.inst.brushBlendDirty) {
|
||||
@@ -691,142 +690,5 @@ class RenderPathDeferred {
|
||||
UITrait.inst.rdirty--;
|
||||
}
|
||||
|
||||
@:access(iron.RenderPath)
|
||||
public static function commandsPaint() {
|
||||
|
||||
if (UITrait.inst.paintDirty()) {
|
||||
|
||||
var tid = UITrait.inst.selectedLayer.id;
|
||||
|
||||
if (UITrait.inst.selectedTool == ToolParticle) {
|
||||
path.setTarget("texparticle");
|
||||
path.clearTarget(0x00000000);
|
||||
path.bindTarget("_paintdb", "paintdb");
|
||||
|
||||
var mo:iron.object.MeshObject = cast iron.Scene.active.getChild(".ParticleEmitter");
|
||||
mo.visible = true;
|
||||
mo.render(path.currentG, "mesh", @:privateAccess path.bindParams);
|
||||
mo.visible = false;
|
||||
|
||||
mo = cast iron.Scene.active.getChild(".Particle");
|
||||
mo.visible = true;
|
||||
mo.render(path.currentG, "mesh", @:privateAccess path.bindParams);
|
||||
mo.visible = false;
|
||||
@:privateAccess path.end();
|
||||
}
|
||||
|
||||
if (UITrait.inst.selectedTool == ToolColorId) {
|
||||
path.setTarget("texpaint_colorid");
|
||||
path.clearTarget(0xff000000);
|
||||
path.bindTarget("gbuffer2", "gbuffer2");
|
||||
path.drawMeshes("paint");
|
||||
UITrait.inst.headerHandle.redraws = 2;
|
||||
}
|
||||
else if (UITrait.inst.selectedTool == ToolPicker) {
|
||||
path.setTarget("texpaint_picker", ["texpaint_nor_picker", "texpaint_pack_picker"]);
|
||||
path.clearTarget(0xff000000);
|
||||
path.bindTarget("gbuffer2", "gbuffer2");
|
||||
tid = UITrait.inst.layers[0].id;
|
||||
path.bindTarget("texpaint" + tid, "texpaint");
|
||||
path.bindTarget("texpaint_nor" + tid, "texpaint_nor");
|
||||
path.bindTarget("texpaint_pack" + tid, "texpaint_pack");
|
||||
path.drawMeshes("paint");
|
||||
UITrait.inst.headerHandle.redraws = 2;
|
||||
|
||||
var texpaint_picker = path.renderTargets.get("texpaint_picker").image;
|
||||
var texpaint_nor_picker = path.renderTargets.get("texpaint_nor_picker").image;
|
||||
var texpaint_pack_picker = path.renderTargets.get("texpaint_pack_picker").image;
|
||||
var a = texpaint_picker.getPixels();
|
||||
var b = texpaint_nor_picker.getPixels();
|
||||
var c = texpaint_pack_picker.getPixels();
|
||||
// Picked surface values
|
||||
UITrait.inst.baseRPicked = a.get(0) / 255;
|
||||
UITrait.inst.baseGPicked = a.get(1) / 255;
|
||||
UITrait.inst.baseBPicked = a.get(2) / 255;
|
||||
UITrait.inst.normalRPicked = b.get(0) / 255;
|
||||
UITrait.inst.normalGPicked = b.get(1) / 255;
|
||||
UITrait.inst.normalBPicked = b.get(2) / 255;
|
||||
UITrait.inst.occlusionPicked = c.get(0) / 255;
|
||||
UITrait.inst.roughnessPicked = c.get(1) / 255;
|
||||
UITrait.inst.metallicPicked = c.get(2) / 255;
|
||||
// Pick material
|
||||
if (UITrait.inst.pickerSelectMaterial) {
|
||||
var matid = b.get(3);
|
||||
for (m in UITrait.inst.materials) {
|
||||
if (m.id == matid) {
|
||||
UITrait.inst.setMaterial(m);
|
||||
UITrait.inst.materialIdPicked = matid;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (UITrait.inst.selectedTool == ToolBake) {
|
||||
if (initVoxels) {
|
||||
initVoxels = false;
|
||||
// Init voxel texture
|
||||
var rp_gi = UITrait.inst.C.rp_gi;
|
||||
UITrait.inst.C.rp_gi = true;
|
||||
#if rp_voxelao
|
||||
Inc.initGI();
|
||||
#end
|
||||
UITrait.inst.C.rp_gi = rp_gi;
|
||||
}
|
||||
path.clearImage("voxels", 0x00000000);
|
||||
path.setTarget("");
|
||||
path.setViewport(256, 256);
|
||||
path.bindTarget("voxels", "voxels");
|
||||
path.drawMeshes("voxel");
|
||||
path.generateMipmaps("voxels");
|
||||
}
|
||||
|
||||
//
|
||||
#if (!kha_opengl)
|
||||
path.setDepthFrom("bufa", "texpaint" + tid); // Unbind depth so we can read it
|
||||
path.setDepthFrom("texpaint" + tid, "texpaint_nor" + tid);
|
||||
path.depthToRenderTarget.set("paintdb", path.renderTargets.get("bufa"));
|
||||
#end
|
||||
//
|
||||
|
||||
var blendA = "texpaint_blend0";
|
||||
var blendB = "texpaint_blend1";
|
||||
path.setTarget(blendB);
|
||||
path.bindTarget(blendA, "tex");
|
||||
path.drawShader("shader_datas/copy_pass/copy_pass");
|
||||
var isMask = UITrait.inst.selectedLayerIsMask;
|
||||
var texpaint = isMask ? "texpaint_mask" + tid : "texpaint" + tid;
|
||||
path.setTarget(texpaint, ["texpaint_nor" + tid, "texpaint_pack" + tid, blendA]);
|
||||
path.bindTarget("_paintdb", "paintdb");
|
||||
path.bindTarget(blendB, "paintmask");
|
||||
if (UITrait.inst.selectedTool == ToolBake) {
|
||||
path.bindTarget("voxels", "voxels");
|
||||
}
|
||||
if (UITrait.inst.colorIdPicked) {
|
||||
path.bindTarget("texpaint_colorid", "texpaint_colorid");
|
||||
}
|
||||
|
||||
// Read texcoords from gbuffer
|
||||
var readTC = (UITrait.inst.selectedTool == ToolFill && UITrait.inst.fillTypeHandle.position == 1) || // Face fill
|
||||
UITrait.inst.selectedTool == ToolClone ||
|
||||
UITrait.inst.selectedTool == ToolBlur;
|
||||
if (readTC) {
|
||||
path.bindTarget("gbuffer2", "gbuffer2");
|
||||
}
|
||||
|
||||
path.drawMeshes("paint");
|
||||
|
||||
//
|
||||
#if (!kha_opengl)
|
||||
path.setDepthFrom("texpaint" + tid, "bufa"); // Re-bind depth
|
||||
path.setDepthFrom("bufa", "texpaint_nor" + tid);
|
||||
var tid0 = UITrait.inst.layers[0].id;
|
||||
path.depthToRenderTarget.set("paintdb", path.renderTargets.get("texpaint" + tid0));
|
||||
#end
|
||||
//
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#end
|
||||
}
|
||||
|
||||
@@ -0,0 +1,150 @@
|
||||
package arm.renderpath;
|
||||
|
||||
import iron.RenderPath;
|
||||
import armory.renderpath.Inc;
|
||||
import arm.ui.UITrait;
|
||||
import arm.ui.*;
|
||||
|
||||
class RenderPathPaint {
|
||||
|
||||
static var initVoxels = true; // Bake AO
|
||||
|
||||
@:access(iron.RenderPath)
|
||||
public static function commandsPaint() {
|
||||
|
||||
var path = RenderPathDeferred.path;
|
||||
|
||||
if (UITrait.inst.paintDirty()) {
|
||||
|
||||
var tid = UITrait.inst.selectedLayer.id;
|
||||
|
||||
if (UITrait.inst.selectedTool == ToolParticle) {
|
||||
path.setTarget("texparticle");
|
||||
path.clearTarget(0x00000000);
|
||||
path.bindTarget("_paintdb", "paintdb");
|
||||
|
||||
var mo:iron.object.MeshObject = cast iron.Scene.active.getChild(".ParticleEmitter");
|
||||
mo.visible = true;
|
||||
mo.render(path.currentG, "mesh", @:privateAccess path.bindParams);
|
||||
mo.visible = false;
|
||||
|
||||
mo = cast iron.Scene.active.getChild(".Particle");
|
||||
mo.visible = true;
|
||||
mo.render(path.currentG, "mesh", @:privateAccess path.bindParams);
|
||||
mo.visible = false;
|
||||
@:privateAccess path.end();
|
||||
}
|
||||
|
||||
if (UITrait.inst.selectedTool == ToolColorId) {
|
||||
path.setTarget("texpaint_colorid");
|
||||
path.clearTarget(0xff000000);
|
||||
path.bindTarget("gbuffer2", "gbuffer2");
|
||||
path.drawMeshes("paint");
|
||||
UITrait.inst.headerHandle.redraws = 2;
|
||||
}
|
||||
else if (UITrait.inst.selectedTool == ToolPicker) {
|
||||
path.setTarget("texpaint_picker", ["texpaint_nor_picker", "texpaint_pack_picker"]);
|
||||
path.clearTarget(0xff000000);
|
||||
path.bindTarget("gbuffer2", "gbuffer2");
|
||||
tid = UITrait.inst.layers[0].id;
|
||||
path.bindTarget("texpaint" + tid, "texpaint");
|
||||
path.bindTarget("texpaint_nor" + tid, "texpaint_nor");
|
||||
path.bindTarget("texpaint_pack" + tid, "texpaint_pack");
|
||||
path.drawMeshes("paint");
|
||||
UITrait.inst.headerHandle.redraws = 2;
|
||||
|
||||
var texpaint_picker = path.renderTargets.get("texpaint_picker").image;
|
||||
var texpaint_nor_picker = path.renderTargets.get("texpaint_nor_picker").image;
|
||||
var texpaint_pack_picker = path.renderTargets.get("texpaint_pack_picker").image;
|
||||
var a = texpaint_picker.getPixels();
|
||||
var b = texpaint_nor_picker.getPixels();
|
||||
var c = texpaint_pack_picker.getPixels();
|
||||
// Picked surface values
|
||||
UITrait.inst.baseRPicked = a.get(0) / 255;
|
||||
UITrait.inst.baseGPicked = a.get(1) / 255;
|
||||
UITrait.inst.baseBPicked = a.get(2) / 255;
|
||||
UITrait.inst.normalRPicked = b.get(0) / 255;
|
||||
UITrait.inst.normalGPicked = b.get(1) / 255;
|
||||
UITrait.inst.normalBPicked = b.get(2) / 255;
|
||||
UITrait.inst.occlusionPicked = c.get(0) / 255;
|
||||
UITrait.inst.roughnessPicked = c.get(1) / 255;
|
||||
UITrait.inst.metallicPicked = c.get(2) / 255;
|
||||
// Pick material
|
||||
if (UITrait.inst.pickerSelectMaterial) {
|
||||
var matid = b.get(3);
|
||||
for (m in UITrait.inst.materials) {
|
||||
if (m.id == matid) {
|
||||
UITrait.inst.setMaterial(m);
|
||||
UITrait.inst.materialIdPicked = matid;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (UITrait.inst.selectedTool == ToolBake) {
|
||||
if (initVoxels) {
|
||||
initVoxels = false;
|
||||
// Init voxel texture
|
||||
var rp_gi = UITrait.inst.C.rp_gi;
|
||||
UITrait.inst.C.rp_gi = true;
|
||||
#if rp_voxelao
|
||||
Inc.initGI();
|
||||
#end
|
||||
UITrait.inst.C.rp_gi = rp_gi;
|
||||
}
|
||||
path.clearImage("voxels", 0x00000000);
|
||||
path.setTarget("");
|
||||
path.setViewport(256, 256);
|
||||
path.bindTarget("voxels", "voxels");
|
||||
path.drawMeshes("voxel");
|
||||
path.generateMipmaps("voxels");
|
||||
}
|
||||
|
||||
//
|
||||
#if (!kha_opengl)
|
||||
path.setDepthFrom("bufa", "texpaint" + tid); // Unbind depth so we can read it
|
||||
path.setDepthFrom("texpaint" + tid, "texpaint_nor" + tid);
|
||||
path.depthToRenderTarget.set("paintdb", path.renderTargets.get("bufa"));
|
||||
#end
|
||||
//
|
||||
|
||||
var blendA = "texpaint_blend0";
|
||||
var blendB = "texpaint_blend1";
|
||||
path.setTarget(blendB);
|
||||
path.bindTarget(blendA, "tex");
|
||||
path.drawShader("shader_datas/copy_pass/copy_pass");
|
||||
var isMask = UITrait.inst.selectedLayerIsMask;
|
||||
var texpaint = isMask ? "texpaint_mask" + tid : "texpaint" + tid;
|
||||
path.setTarget(texpaint, ["texpaint_nor" + tid, "texpaint_pack" + tid, blendA]);
|
||||
path.bindTarget("_paintdb", "paintdb");
|
||||
path.bindTarget(blendB, "paintmask");
|
||||
if (UITrait.inst.selectedTool == ToolBake) {
|
||||
path.bindTarget("voxels", "voxels");
|
||||
}
|
||||
if (UITrait.inst.colorIdPicked) {
|
||||
path.bindTarget("texpaint_colorid", "texpaint_colorid");
|
||||
}
|
||||
|
||||
// Read texcoords from gbuffer
|
||||
var readTC = (UITrait.inst.selectedTool == ToolFill && UITrait.inst.fillTypeHandle.position == 1) || // Face fill
|
||||
UITrait.inst.selectedTool == ToolClone ||
|
||||
UITrait.inst.selectedTool == ToolBlur;
|
||||
if (readTC) {
|
||||
path.bindTarget("gbuffer2", "gbuffer2");
|
||||
}
|
||||
|
||||
path.drawMeshes("paint");
|
||||
|
||||
//
|
||||
#if (!kha_opengl)
|
||||
path.setDepthFrom("texpaint" + tid, "bufa"); // Re-bind depth
|
||||
path.setDepthFrom("bufa", "texpaint_nor" + tid);
|
||||
var tid0 = UITrait.inst.layers[0].id;
|
||||
path.depthToRenderTarget.set("paintdb", path.renderTargets.get("texpaint" + tid0));
|
||||
#end
|
||||
//
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -137,8 +137,8 @@ class RenderPathPreview {
|
||||
path.setDepthFrom("tex", "gbuffer0"); // Re-bind depth
|
||||
#end
|
||||
|
||||
// path.setTarget("tex"); // Re-binds depth
|
||||
// path.drawSkydome("shader_datas/world_pass/world_pass");
|
||||
path.setTarget("tex"); // Re-binds depth
|
||||
path.drawSkydome("shader_datas/world_pass/world_pass");
|
||||
|
||||
var framebuffer = "texpreview";
|
||||
iron.RenderPath.active.renderTargets.get("texpreview").image = UITrait.inst.decalImage;
|
||||
|
||||
+37
-32
@@ -456,16 +456,13 @@ class UITrait extends iron.Trait {
|
||||
|
||||
if (!arm.App.uienabled) return;
|
||||
|
||||
if (kb.started("tab")) {
|
||||
if (kb.started("tab") && !UINodes.inst.ui.isTyping && !UITrait.inst.ui.isTyping) {
|
||||
if (ctrl) { // Cycle layers
|
||||
var i = (layers.indexOf(selectedLayer) + 1) % layers.length;
|
||||
setLayer(layers[i]);
|
||||
}
|
||||
else { // Toggle node editor
|
||||
if (!UINodes.inst.ui.isTyping && !UITrait.inst.ui.isTyping) {
|
||||
showMaterialNodes();
|
||||
}
|
||||
}
|
||||
else if (shift) show2DView();
|
||||
else showMaterialNodes();
|
||||
}
|
||||
|
||||
if (ctrl && !shift && kb.started("s")) Project.projectSave();
|
||||
@@ -1781,6 +1778,7 @@ class UITrait extends iron.Trait {
|
||||
}
|
||||
|
||||
if (ui.button("2D View")) show2DView();
|
||||
else if (ui.isHovered) ui.tooltip("Show 2D View (SHIFT+TAB)");
|
||||
|
||||
var ar = ["All"];
|
||||
for (p in paintObjects) ar.push(p.name);
|
||||
@@ -1912,12 +1910,39 @@ class UITrait extends iron.Trait {
|
||||
if (contextMenu) {
|
||||
App.showContextMenu(function(ui:Zui) {
|
||||
if (l == layers[0]) {
|
||||
ui.fill(0, 0, ui._w, ui.t.ELEMENT_H * 9, ui.t.SEPARATOR_COL);
|
||||
ui.fill(0, 0, ui._w, ui.t.ELEMENT_H * 10, ui.t.SEPARATOR_COL);
|
||||
ui.text(l.name, Right);
|
||||
}
|
||||
else {
|
||||
ui.fill(0, 0, ui._w, ui.t.ELEMENT_H * 17, ui.t.SEPARATOR_COL);
|
||||
ui.text(l.name, Right);
|
||||
}
|
||||
|
||||
if (l.material_mask == null && ui.button("To Fill Layer", Left)) {
|
||||
setLayer(l);
|
||||
l.material_mask = UITrait.inst.selectedMaterial;
|
||||
function makeFill(g:kha.graphics4.Graphics) {
|
||||
g.end();
|
||||
Layers.updateFillLayers(8);
|
||||
UINodes.inst.parsePaintMaterial();
|
||||
UITrait.inst.layerPreviewDirty = true;
|
||||
hwnd.redraws = 2;
|
||||
g.begin();
|
||||
iron.App.removeRender(makeFill);
|
||||
}
|
||||
iron.App.notifyOnRender(makeFill);
|
||||
}
|
||||
if (l.material_mask != null && ui.button("To Paint Layer", Left)) {
|
||||
setLayer(l);
|
||||
l.material_mask = null;
|
||||
UINodes.inst.parsePaintMaterial();
|
||||
UITrait.inst.layerPreviewDirty = true;
|
||||
hwnd.redraws = 2;
|
||||
}
|
||||
|
||||
if (l == layers[0]) {
|
||||
}
|
||||
else {
|
||||
if (ui.button("Delete", Left)) {
|
||||
selectedLayer = l;
|
||||
Layers.deleteSelectedLayer();
|
||||
@@ -1960,28 +1985,6 @@ class UITrait extends iron.Trait {
|
||||
setLayer(l, true);
|
||||
layerPreviewDirty = true;
|
||||
}
|
||||
|
||||
if (l.material_mask == null && ui.button("To Fill Layer", Left)) {
|
||||
setLayer(l);
|
||||
l.material_mask = UITrait.inst.selectedMaterial;
|
||||
function makeFill(g:kha.graphics4.Graphics) {
|
||||
g.end();
|
||||
Layers.updateFillLayers(8);
|
||||
UINodes.inst.parsePaintMaterial();
|
||||
UITrait.inst.layerPreviewDirty = true;
|
||||
hwnd.redraws = 2;
|
||||
g.begin();
|
||||
iron.App.removeRender(makeFill);
|
||||
}
|
||||
iron.App.notifyOnRender(makeFill);
|
||||
}
|
||||
if (l.material_mask != null && ui.button("To Paint Layer", Left)) {
|
||||
setLayer(l);
|
||||
l.material_mask = null;
|
||||
UINodes.inst.parsePaintMaterial();
|
||||
UITrait.inst.layerPreviewDirty = true;
|
||||
hwnd.redraws = 2;
|
||||
}
|
||||
}
|
||||
|
||||
var baseHandle = Id.handle().nest(l.id, {selected: l.paintBase});
|
||||
@@ -2212,7 +2215,7 @@ class UITrait extends iron.Trait {
|
||||
if (ui.button("New")) {
|
||||
ui.g.end();
|
||||
headerHandle.redraws = 2;
|
||||
selectedMaterial = new MaterialSlot();
|
||||
selectedMaterial = new MaterialSlot(materials[0].data);
|
||||
materials.push(selectedMaterial);
|
||||
UINodes.inst.updateCanvasMap();
|
||||
UINodes.inst.parsePaintMaterial();
|
||||
@@ -2225,7 +2228,7 @@ class UITrait extends iron.Trait {
|
||||
if (ui.button("Nodes")) {
|
||||
showMaterialNodes();
|
||||
}
|
||||
else if (ui.isHovered) ui.tooltip("Show Editor (TAB)");
|
||||
else if (ui.isHovered) ui.tooltip("Show Node Editor (TAB)");
|
||||
|
||||
for (row in 0...Std.int(Math.ceil(materials.length / 5))) {
|
||||
ui.row([1/5,1/5,1/5,1/5,1/5]);
|
||||
@@ -2268,6 +2271,7 @@ class UITrait extends iron.Trait {
|
||||
ui.text(UINodes.inst.canvasMap.get(materials[i]).name, Right);
|
||||
|
||||
if (ui.button("Delete", Left) && materials.length > 1) {
|
||||
selectMaterial(i == 0 ? 1 : 0);
|
||||
materials.splice(i, 1);
|
||||
hwnd1.redraws = 2;
|
||||
}
|
||||
@@ -2550,7 +2554,8 @@ class UITrait extends iron.Trait {
|
||||
}
|
||||
|
||||
ui.row([1/2, 1/2]);
|
||||
ui.combo(Id.handle(), ["Selected", "All"], "Layer", true);
|
||||
// ui.combo(Id.handle(), ["Selected", "All"], "Layer", true);
|
||||
ui.combo(Id.handle(), ["Selected"], "Layer", true);
|
||||
outputType = ui.combo(Id.handle(), ["Generic", "UE4 (ORM)"], "Output", true);
|
||||
|
||||
if (ui.panel(Id.handle({selected: false}), "Channels")) {
|
||||
|
||||
Reference in New Issue
Block a user