diff --git a/Assets/common/shader_datas.arm b/Assets/common/shader_datas.arm index 4985ae7d..5e00b1a9 100644 Binary files a/Assets/common/shader_datas.arm and b/Assets/common/shader_datas.arm differ diff --git a/Shaders/common/dilate_pass.frag.glsl b/Shaders/common/dilate_pass.frag.glsl new file mode 100644 index 00000000..2db5b084 --- /dev/null +++ b/Shaders/common/dilate_pass.frag.glsl @@ -0,0 +1,56 @@ +#version 450 + +uniform sampler2D tex; +uniform float dilateRadius; +in vec2 texCoord; +out vec4 fragColor; + +void main() { + // Based on https://shaderbits.com/blog/uv-dilation by Ryan Brucks + float texelSize = 1.0 / textureSize(tex, 0).x; + float minDist = 10000000; + vec2 offsets[8] = { + vec2(-1, 0), + vec2( 1, 0), + vec2( 0, 1), + vec2( 0,-1), + vec2(-1, 1), + vec2( 1, 1), + vec2( 1,-1), + vec2(-1,-1) + }; + + vec3 col = textureLod(tex, texCoord, 0.0).rgb; + vec3 minCol = col; + if (col.x == 0 && col.y == 0 && col.z == 0) { + int i = 0; + while (i < dilateRadius) { + i++; + int j = 0; + while (j < 8) { + vec2 curUV = texCoord + offsets[j] * texelSize * i; + vec3 offsetCol = textureLod(tex, curUV, 0.0).rgb; + + if (offsetCol.x != 0 || offsetCol.y != 0 || offsetCol.z != 0) { + float curDist = length(texCoord - curUV); + + if (curDist < minDist) { + vec2 projectUV = curUV + offsets[j] * texelSize * i * 0.25; + vec3 direction = textureLod(tex, projectUV, 0.0).rgb; + minDist = curDist; + + if (direction.x != 0 || direction.y != 0 || direction.z != 0) { + vec3 delta = offsetCol - direction; + minCol = offsetCol + delta * 4; + } + else { + minCol = offsetCol; + } + } + } + j++; + } + } + } + fragColor = vec4(minCol, 1.0); +} diff --git a/Sources/arm/Layers.hx b/Sources/arm/Layers.hx index 0fa475a2..d4180b88 100644 --- a/Sources/arm/Layers.hx +++ b/Sources/arm/Layers.hx @@ -184,17 +184,44 @@ class Layers { public static function makeTempImg() { var l = Project.layers[0]; if (imga != null && imga.width != l.texpaint.width) { - imga.unload(); - imgb.unload(); - imgc.unload(); + 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) { - imga = Image.createRenderTarget(l.texpaint.width, l.texpaint.height); - imgb = Image.createRenderTarget(l.texpaint.width, l.texpaint.height); - imgc = Image.createRenderTarget(l.texpaint.width, l.texpaint.height); + { + 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; + } } } diff --git a/Sources/arm/render/RenderPathDeferred.hx b/Sources/arm/render/RenderPathDeferred.hx index 6c15ba4b..56f92576 100644 --- a/Sources/arm/render/RenderPathDeferred.hx +++ b/Sources/arm/render/RenderPathDeferred.hx @@ -312,6 +312,7 @@ class RenderPathDeferred { if (Context.ddirty <= 0) Context.ddirty--; } Inc.endSplit(); + RenderPathPaint.finishPaint(); return; } #end diff --git a/Sources/arm/render/RenderPathForward.hx b/Sources/arm/render/RenderPathForward.hx index 36ffaf42..d47fc099 100644 --- a/Sources/arm/render/RenderPathForward.hx +++ b/Sources/arm/render/RenderPathForward.hx @@ -123,6 +123,7 @@ class RenderPathForward { if (Context.ddirty <= 0) Context.ddirty--; } Inc.endSplit(); + RenderPathPaint.finishPaint(); return; } #end diff --git a/Sources/arm/render/RenderPathPaint.hx b/Sources/arm/render/RenderPathPaint.hx index ffa40256..61991bbc 100644 --- a/Sources/arm/render/RenderPathPaint.hx +++ b/Sources/arm/render/RenderPathPaint.hx @@ -25,6 +25,7 @@ class RenderPathPaint { static var visibles:Array = null; static var mergedObjectVisible = false; static var savedFov = 0.0; + static var dilated = true; public static function init(_path:RenderPath) { path = _path; @@ -64,6 +65,7 @@ class RenderPathPaint { } path.loadShader("shader_datas/copy_mrt3_pass/copy_mrt3_pass"); + path.loadShader("shader_datas/dilate_pass/dilate_pass"); } @:access(iron.RenderPath) @@ -161,11 +163,18 @@ class RenderPathPaint { } #end + if (Context.tool == ToolBake && UITrait.inst.brushTime == iron.system.Time.delta) { + // Clear to black on bake start + path.setTarget("texpaint" + tid); + path.clearTarget(0xff000000); + } + 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 = Context.layerIsMask; var texpaint = isMask ? "texpaint_mask" + tid : "texpaint" + tid; path.setTarget(texpaint, ["texpaint_nor" + tid, "texpaint_pack" + tid, blendA]); @@ -379,6 +388,7 @@ class RenderPathPaint { } if (Context.tool == ToolBake) { + if (Context.pdirty > 0) dilated = false; if (UITrait.inst.bakeType == 2) { // Normal (Tangent) UITrait.inst.bakeType = 3; // Bake high poly world normals MaterialParser.parsePaintMaterial(); @@ -475,6 +485,19 @@ class RenderPathPaint { } } } + + public static function finishPaint() { + if (Context.tool == ToolBake && !dilated && UITrait.inst.dilateRadius > 0) { + if (Layers.imga == null) Layers.makeTempImg(); + dilated = true; + path.setTarget("temptex0"); + path.bindTarget("texpaint0", "tex"); + path.drawShader("shader_datas/copy_pass/copy_pass"); + path.setTarget("texpaint0"); + path.bindTarget("temptex0", "tex"); + path.drawShader("shader_datas/dilate_pass/dilate_pass"); + } + } } #end diff --git a/Sources/arm/render/Uniforms.hx b/Sources/arm/render/Uniforms.hx index b73d8de5..dae3bff0 100644 --- a/Sources/arm/render/Uniforms.hx +++ b/Sources/arm/render/Uniforms.hx @@ -95,6 +95,13 @@ class Uniforms { return 1.2; #end } + if (link == "_dilateRadius") { + #if arm_painter + return UITrait.inst.dilateRadius; + #else + return 8.0; + #end + } return null; } diff --git a/Sources/arm/ui/BoxPreferences.hx b/Sources/arm/ui/BoxPreferences.hx index 04e6f77c..f2663ef0 100644 --- a/Sources/arm/ui/BoxPreferences.hx +++ b/Sources/arm/ui/BoxPreferences.hx @@ -107,6 +107,9 @@ class BoxPreferences { UITrait.inst.brushBias = ui.slider(Id.handle({value: UITrait.inst.brushBias}), "Paint Bleed", 0.0, 2.0, true); if (ui.isHovered) ui.tooltip("Stretch brush strokes on the uv map to prevent seams"); + UITrait.inst.dilateRadius = ui.slider(Id.handle({value: UITrait.inst.dilateRadius}), "Dilate Radius", 0.0, 64.0, true, 1); + if (ui.isHovered) ui.tooltip("Dilate baked textures to prevent seams"); + var brush3dHandle = Id.handle({selected: UITrait.inst.brush3d}); UITrait.inst.brush3d = ui.check(brush3dHandle, "3D Cursor"); if (brush3dHandle.changed) MaterialParser.parsePaintMaterial(); diff --git a/Sources/arm/ui/UITrait.hx b/Sources/arm/ui/UITrait.hx index 1aa1f3f2..f2172a4e 100644 --- a/Sources/arm/ui/UITrait.hx +++ b/Sources/arm/ui/UITrait.hx @@ -200,6 +200,7 @@ class UITrait { public var bakeCurvOffset = 0.0; public var bakeCurvSmooth = 1; public var bakeHighPoly = 0; + public var dilateRadius = 8.0; public var xray = false; public var symX = false;