Particle brush

This commit is contained in:
luboslenco
2019-03-20 20:42:04 +01:00
parent 23d1963b07
commit 80a84f80e3
5 changed files with 176 additions and 87 deletions
+13 -2
View File
@@ -425,7 +425,7 @@ class App extends iron.Trait {
var menuButtonW = Std.int(ui.ELEMENT_W() * 0.5);
var px = panelx + menuButtonW * menuCategory;
var py = UITrait.inst.headerh;
var menuItems = [5, 2, 8, 2];
var menuItems = [5, 2, 8, 4];
var ph = 24 * menuItems[menuCategory] * ui.SCALE;
g.color = ui.t.SEPARATOR_COL;
@@ -493,7 +493,7 @@ class App extends iron.Trait {
// ui.button("Wireframe", Left);
}
else if (menuCategory == 3) {
if (ui.button("Manual...", Left)) {
if (ui.button("Manual", Left)) {
if (kha.System.systemId == "Windows") {
Krom.sysCommand('explorer "https://armorpaint.org/manual"');
}
@@ -504,6 +504,17 @@ class App extends iron.Trait {
Krom.sysCommand('open "https://armorpaint.org/manual"');
}
}
if (ui.button("Report Bug", Left)) {
if (kha.System.systemId == "Windows") {
Krom.sysCommand('explorer "https://github.com/armory3d/armorpaint/issues"');
}
else if (kha.System.systemId == "Linux") {
Krom.sysCommand('xdg-explorer "https://github.com/armory3d/armorpaint/issues"');
}
else {
Krom.sysCommand('open "https://github.com/armory3d/armorpaint/issues"');
}
}
if (ui.button("Check for Updates...", Left)) {
// Retrieve latest version number
var outFile = Krom.getFilesLocation() + '/' + iron.data.Data.dataPath + "update.txt";
+43 -15
View File
@@ -106,11 +106,6 @@ class MaterialBuilder {
vert.add_out('vec4 ndc');
vert.write_attrib('ndc = mul(vec4(pos.xyz, 1.0), WVP);');
if (UITrait.inst.selectedTool == ToolParticle) {
frag.add_uniform('sampler2D texparticle', '_texparticle');
}
frag.write_attrib('vec3 sp = vec3((ndc.xyz / ndc.w) * 0.5 + 0.5);');
frag.write_attrib('sp.y = 1.0 - sp.y;');
frag.add_uniform('float paintDepthBias', '_paintDepthBias');
@@ -142,15 +137,16 @@ class MaterialBuilder {
UITrait.inst.selectedTool == ToolEraser ||
UITrait.inst.selectedTool == ToolClone ||
UITrait.inst.selectedTool == ToolBlur ||
UITrait.inst.selectedTool == ToolParticle ||
decal) {
#if (kha_opengl || kha_webgl)
frag.write('if (sp.z > textureLod(paintdb, vec2(sp.x, 1.0 - bsp.y), 0.0).r) { discard; }');
frag.write('if (sp.z > textureLod(paintdb, vec2(sp.x, 1.0 - sp.y), 0.0).r) { discard; }');
#else
frag.write('if (sp.z > textureLod(paintdb, vec2(sp.x, bsp.y), 0.0).r) { discard; }');
frag.write('if (sp.z > textureLod(paintdb, vec2(sp.x, sp.y), 0.0).r) { discard; }');
#end
if (decal) {
if (decal || UITrait.inst.selectedTool == ToolParticle) {
frag.write('float dist = 0.0;');
}
else {
@@ -373,11 +369,20 @@ class MaterialBuilder {
frag.write('opacity *= textureLod(textexttool, texCoord, 0.0).r;');
}
frag.write('float str = clamp((brushRadius - dist) * brushHardness * 400.0, 0.0, 1.0) * opacity;');
if (UITrait.inst.mirrorX && UITrait.inst.selectedTool == ToolBrush) {
frag.write('str += clamp((brushRadius - dist2) * brushHardness * 400.0, 0.0, 1.0) * opacity;');
frag.write('str = clamp(str, 0.0, 1.0);');
if (UITrait.inst.selectedTool == ToolParticle) { // particle mask
frag.add_uniform('sampler2D texparticle', '_texparticle');
#if (kha_opengl || kha_webgl)
frag.write('float str = textureLod(texparticle, vec2(sp.x, (1.0 - sp.y)), 0).r;');
#else
frag.write('float str = textureLod(texparticle, sp.xy, 0).r;');
#end
}
else { // brush cursor mask
frag.write('float str = clamp((brushRadius - dist) * brushHardness * 400.0, 0.0, 1.0) * opacity;');
if (UITrait.inst.mirrorX && UITrait.inst.selectedTool == ToolBrush) {
frag.write('str += clamp((brushRadius - dist2) * brushHardness * 400.0, 0.0, 1.0) * opacity;');
frag.write('str = clamp(str, 0.0, 1.0);');
}
}
// Manual blending to preserve memory
@@ -610,11 +615,11 @@ class MaterialBuilder {
}
public static function make_particle(data:CyclesShaderData):CyclesShaderContext {
var context_id = 'particle';
var context_id = 'mesh';
var con_part:CyclesShaderContext = data.add_context({
name: context_id,
depth_write: false,
compare_mode: 'less',
compare_mode: 'always',
cull_mode: 'clockwise',
vertex_elements: [{name: "pos", data: 'short4norm'}]
});
@@ -625,6 +630,11 @@ class MaterialBuilder {
vert.write_attrib('vec4 spos = vec4(pos.xyz, 1.0);');
vert.add_uniform('float brushRadius', '_brushRadius');
vert.write_attrib('vec3 emitFrom = vec3(fhash(gl_InstanceID), fhash(gl_InstanceID * 2), fhash(gl_InstanceID * 3));');
vert.write_attrib('emitFrom = emitFrom * brushRadius - brushRadius / 2;');
vert.write_attrib('spos.xyz += emitFrom * 256;');
vert.add_uniform('mat4 pd', '_particleData');
var str_tex_hash = "float fhash(float n) { return fract(sin(n) * 43758.5453); }\n";
@@ -660,6 +670,24 @@ class MaterialBuilder {
vert.add_uniform('mat4 WVP', '_worldViewProjectionMatrix');
vert.write('gl_Position = mul(spos, WVP);');
vert.add_uniform('vec4 inp', '_inputBrush');
vert.write('vec2 binp = vec2(inp.x, 1.0 - inp.y);');
vert.write('binp = binp * 2.0 - 1.0;');
vert.write('binp *= gl_Position.w;');
vert.write('gl_Position.xy += binp;');
vert.add_out('float p_fade');
vert.write('p_fade = sin(min((p_age / 8) * 3.141592, 3.141592));');
frag.add_out('float fragColor');
frag.write('fragColor = p_fade;');
// vert.add_out('vec4 wvpposition');
// vert.write('wvpposition = gl_Position;');
// frag.write('vec2 texCoord = wvpposition.xy / wvpposition.w;');
// frag.add_uniform('sampler2D paintdb');
// frag.write('fragColor *= 1.0 - clamp(distance(textureLod(paintdb, texCoord, 0).r, wvpposition.z), 0.0, 1.0);');
// Cycles.finalize(con_part);
con_part.data.shader_from_source = true;
con_part.data.vertex_shader = vert.get();
+4
View File
@@ -45,6 +45,10 @@ class BrushOutputNode extends LogicNode {
UITrait.inst.lastPaintX = UITrait.inst.paintVec.x;
UITrait.inst.lastPaintY = UITrait.inst.paintVec.y;
if (UITrait.inst.selectedTool == ToolParticle) {
UITrait.inst.painted = 0; // Always paint particles
}
var decal = UITrait.inst.selectedTool == ToolDecal || UITrait.inst.selectedTool == ToolText;
var paintFrames = decal ? 1 : 8;
+21 -10
View File
@@ -172,10 +172,10 @@ class RenderPathDeferred {
// Only enable vxao in scene mode
var restoreGI = false;
if (armory.data.Config.raw.rp_gi && UITrait.inst.worktab.position == 0) { // paint
armory.data.Config.raw.rp_gi = false;
restoreGI = true;
}
// if (armory.data.Config.raw.rp_gi && UITrait.inst.worktab.position == 0) { // paint
// armory.data.Config.raw.rp_gi = false;
// restoreGI = true;
// }
var ssaa4 = armory.data.Config.raw.rp_supersample == 4 ? true : false;
@@ -216,6 +216,23 @@ class RenderPathDeferred {
}
if (UITrait.inst.paintDirty()) {
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(path.currentG);
}
if (UITrait.inst.selectedTool == ToolColorId) {
path.setTarget("texpaint_colorid");
path.clearTarget(0xff000000);
@@ -262,12 +279,6 @@ class RenderPathDeferred {
}
}
}
else if (UITrait.inst.selectedTool == ToolParticle) {
path.setTarget("texparticle");
path.clearTarget(0xff000000);
path.bindTarget("_paintdb", "paintdb");
path.drawMeshes("particle");
}
else {
if (UITrait.inst.selectedTool == ToolBake) {
if (initVoxels) {
+95 -60
View File
@@ -123,7 +123,7 @@ class UITrait extends iron.Trait {
public var textToolHandle = new Zui.Handle({position: 0});
public var decalMaskImage:kha.Image = null;
public var decalMaskHandle = new Zui.Handle({position: 0});
public var particleMaterial:MaterialData;
public var particleMaterial:MaterialData = null;
var _onBrush:Array<Int->Void> = [];
@@ -534,7 +534,8 @@ class UITrait extends iron.Trait {
selectedTool == ToolDecal ||
selectedTool == ToolText ||
selectedTool == ToolClone ||
selectedTool == ToolBlur) {
selectedTool == ToolBlur ||
selectedTool == ToolParticle) {
if (kb.started("f")) {
brushCanLock = true;
mouse.lock();
@@ -686,6 +687,14 @@ class UITrait extends iron.Trait {
cloneDeltaY = (cloneStartY - mouse.y) / iron.App.h();
cloneStartX = -1;
}
else if (selectedTool == ToolParticle) {
// Reset particles
var emitter:iron.object.MeshObject = cast iron.Scene.active.getChild("ParticleEmitter");
var psys = emitter.particleSystems[0];
@:privateAccess psys.time = 0;
// @:privateAccess psys.time = @:privateAccess psys.seed * @:privateAccess psys.animtime;
// @:privateAccess psys.seed++;
}
}
brushTime += iron.system.Time.delta;
for (f in _onBrush) f(0);
@@ -980,12 +989,6 @@ class UITrait extends iron.Trait {
arm.App.resize();
}
function f32(ar:Array<kha.FastFloat>):kha.arrays.Float32Array {
var res = new kha.arrays.Float32Array(ar.length);
for (i in 0...ar.length) res[i] = ar[i];
return res;
}
function selectTool(i:Int) {
selectedTool = i;
autoFillHandle.selected = false; // Auto-disable
@@ -1014,58 +1017,8 @@ class UITrait extends iron.Trait {
}
if (selectedTool == ToolParticle) {
var raw:TParticleData = {
name: "Particles",
type: 0,
loop: true,
render_emitter: false,
count: 100,
frame_start: 0,
frame_end: 100,
lifetime: 5,
lifetime_random: 0,
emit_from: 1,
object_align_factor: f32([0, 0, -1]),
factor_random: 0,
physics_type: 0,
particle_size: 1,
size_random: 0,
mass: 1,
instance_object: "Particle",
weight_gravity: 1
};
// iron.Scene.active.gravity = f32([0, 0, -9.8]);
var pd = new iron.data.ParticleData(raw, function(pd:iron.data.ParticleData) {});
var particle_refs:Array<TParticleReference> = [
{
name: "Particles",
particle: "Particles",
seed: 0
}
];
// obj.setupParticleSystem("Scene", particle_refs[0]);
// obj.is_particle = true;
// obj.particle_refs = particle_refs;
{
var t = new iron.RenderPath.RenderTargetRaw();
t.name = "texparticle";
t.width = 0;
t.height = 0;
t.format = 'R8';
t.scale = armory.renderpath.Inc.getSuperSampling();
iron.RenderPath.active.createRenderTarget(t);
}
iron.data.Data.cachedMaterials.remove("SceneMaterial2");
iron.data.Data.cachedShaders.remove("Material2_data");
iron.data.Data.cachedSceneRaws.remove("Material2_data");
// iron.data.Data.cachedBlobs.remove("Material2_data.arm");
iron.data.Data.getMaterial("Scene", "Material2", function(md:iron.data.MaterialData) {
md.name = "MaterialParticle";
particleMaterial = md;
UINodes.inst.parseParticleMaterial();
});
initParticle();
UINodes.inst.parseParticleMaterial();
}
}
@@ -2419,6 +2372,88 @@ class UITrait extends iron.Trait {
if (fontName == 'default.ttf') return UITrait.inst.ui.ops.font;
return Importer.fontMap.get(fontName);
}
function f32(ar:Array<kha.FastFloat>):kha.arrays.Float32Array {
var res = new kha.arrays.Float32Array(ar.length);
for (i in 0...ar.length) res[i] = ar[i];
return res;
}
function initParticle() {
if (particleMaterial != null) return;
var raw:TParticleData = {
name: "Particles",
type: 0,
loop: false,
render_emitter: false,
count: 1000,
frame_start: 0,
frame_end: 1000,
lifetime: 400,
lifetime_random: 0.5,
emit_from: 1,
object_align_factor: f32([0, 0, -40]),
factor_random: 2.0,
physics_type: 0,
particle_size: 1.0,
size_random: 0,
mass: 1,
instance_object: "Particle",
weight_gravity: 1
};
// iron.Scene.active.raw.gravity = f32([0, 0, -9.8]);
iron.Scene.active.raw.particle_datas = [raw];
var particle_refs:Array<TParticleReference> = [
{
name: "Particles",
particle: "Particles",
seed: 0
}
];
{
var t = new iron.RenderPath.RenderTargetRaw();
t.name = "texparticle";
t.width = 0;
t.height = 0;
t.format = 'R8';
t.scale = armory.renderpath.Inc.getSuperSampling();
iron.RenderPath.active.createRenderTarget(t);
}
for (mat in iron.Scene.active.raw.material_datas) {
if (mat.name == 'Material2') {
var m = Reflect.copy(mat);
m.name = 'MaterialParticle';
iron.Scene.active.raw.material_datas.push(m);
break;
}
}
iron.data.Data.getMaterial("Scene", "MaterialParticle", function(md:iron.data.MaterialData) {
particleMaterial = md;
for (obj in iron.Scene.active.raw.objects) {
if (obj.name == '.Sphere') {
var particle = Reflect.copy(obj);
particle.name = 'Particle';
particle.is_particle = true;
particle.material_refs = ['MaterialParticle'];
iron.Scene.active.raw.objects.push(particle);
for (i in 0...16) particle.transform.values[i] *= 0.01;
break;
}
}
iron.Scene.active.spawnObject(".Sphere", null, function(o:Object) {
var mo:MeshObject = cast o;
mo.name = "ParticleEmitter";
mo.raw.particle_refs = particle_refs;
mo.setupParticleSystem("Scene", particle_refs[0]);
});
});
}
}
@:enum abstract PaintTool(Int) from Int to Int {