1293 lines
51 KiB
Haxe
1293 lines
51 KiB
Haxe
package arm.nodes;
|
|
|
|
import iron.data.SceneFormat;
|
|
import arm.ui.UITrait;
|
|
import arm.ui.UINodes;
|
|
import arm.nodes.CyclesShader;
|
|
import arm.Tool;
|
|
|
|
class MaterialBuilder {
|
|
|
|
public static var heightUsed = false;
|
|
public static var emisUsed = false;
|
|
public static var subsUsed = false;
|
|
|
|
public static function make_paint(data:CyclesShaderData, matcon:TMaterialContext):CyclesShaderContext {
|
|
var layered = Context.layer != Project.layers[0];
|
|
var eraser = Context.tool == ToolEraser;
|
|
var context_id = 'paint';
|
|
var con_paint:CyclesShaderContext = data.add_context({
|
|
name: context_id,
|
|
depth_write: false,
|
|
compare_mode: 'always', // TODO: align texcoords winding order
|
|
// cull_mode: 'counter_clockwise',
|
|
cull_mode: 'none',
|
|
vertex_elements: [{name: "pos", data: 'short4norm'}, {name: "nor", data: 'short2norm'}, {name: "tex", data: 'short2norm'}]
|
|
});
|
|
|
|
con_paint.data.color_writes_red = [true, true, true, true];
|
|
con_paint.data.color_writes_green = [true, true, true, true];
|
|
con_paint.data.color_writes_blue = [true, true, true, true];
|
|
con_paint.data.color_writes_alpha = [true, true, true, true];
|
|
|
|
if (Context.tool == ToolBake) {
|
|
// Bake into base color, disable other slots
|
|
con_paint.data.color_writes_red[1] = false;
|
|
con_paint.data.color_writes_green[1] = false;
|
|
con_paint.data.color_writes_blue[1] = false;
|
|
con_paint.data.color_writes_alpha[1] = false;
|
|
con_paint.data.color_writes_red[2] = false;
|
|
con_paint.data.color_writes_green[2] = false;
|
|
con_paint.data.color_writes_blue[2] = false;
|
|
con_paint.data.color_writes_alpha[2] = false;
|
|
}
|
|
|
|
var vert = con_paint.make_vert();
|
|
var frag = con_paint.make_frag();
|
|
frag.ins = vert.outs;
|
|
|
|
if (Context.tool == ToolColorId || Context.tool == ToolPicker) {
|
|
// Mangle vertices to form full screen triangle
|
|
vert.write('gl_Position = vec4(-1.0 + float((gl_VertexID & 1) << 2), -1.0 + float((gl_VertexID & 2) << 1), 0.0, 1.0);');
|
|
|
|
frag.add_uniform('sampler2D gbuffer2');
|
|
frag.add_uniform('vec2 gbufferSize', '_gbufferSize');
|
|
frag.add_uniform('vec4 inp', '_inputBrush');
|
|
|
|
#if (kha_opengl || kha_webgl)
|
|
frag.write('vec2 texCoordInp = texelFetch(gbuffer2, ivec2(inp.x * gbufferSize.x, (1.0 - inp.y) * gbufferSize.y), 0).ba;');
|
|
#else
|
|
frag.write('vec2 texCoordInp = texelFetch(gbuffer2, ivec2(inp.x * gbufferSize.x, inp.y * gbufferSize.y), 0).ba;');
|
|
#end
|
|
|
|
if (Context.tool == ToolColorId) {
|
|
frag.add_out('vec4 fragColor');
|
|
frag.add_uniform('sampler2D texcolorid', '_texcolorid');
|
|
frag.write('vec3 idcol = textureLod(texcolorid, texCoordInp, 0.0).rgb;');
|
|
frag.write('fragColor = vec4(idcol, 1.0);');
|
|
}
|
|
else if (Context.tool == ToolPicker) {
|
|
frag.add_out('vec4 fragColor[3]');
|
|
frag.add_uniform('sampler2D texpaint');
|
|
frag.add_uniform('sampler2D texpaint_nor');
|
|
frag.add_uniform('sampler2D texpaint_pack');
|
|
frag.write('fragColor[0] = textureLod(texpaint, texCoordInp, 0.0);');
|
|
frag.write('fragColor[1] = textureLod(texpaint_nor, texCoordInp, 0.0);');
|
|
frag.write('fragColor[2] = textureLod(texpaint_pack, texCoordInp, 0.0);');
|
|
}
|
|
|
|
con_paint.data.shader_from_source = true;
|
|
con_paint.data.vertex_shader = vert.get();
|
|
con_paint.data.fragment_shader = frag.get();
|
|
return con_paint;
|
|
}
|
|
|
|
var faceFill = Context.tool == ToolFill && UITrait.inst.fillTypeHandle.position == 1;
|
|
var decal = Context.tool == ToolDecal || Context.tool == ToolText;
|
|
if (!faceFill && !decal) { // Fix seams at uv borders
|
|
vert.add_uniform('vec2 sub', '_sub');
|
|
vert.write('vec2 subtex = tex + sub;');
|
|
}
|
|
else {
|
|
vert.write('vec2 subtex = tex;');
|
|
}
|
|
|
|
#if (kha_direct3d11 || kha_direct3d12)
|
|
vert.write('vec2 tpos = vec2(subtex.x * 2.0 - 1.0, (1.0 - subtex.y) * 2.0 - 1.0);');
|
|
#else
|
|
vert.write('vec2 tpos = vec2(subtex.xy * 2.0 - 1.0);');
|
|
#end
|
|
|
|
vert.write('gl_Position = vec4(tpos, 0.0, 1.0);');
|
|
|
|
vert.add_uniform('mat4 WVP', '_worldViewProjectionMatrix');
|
|
vert.add_out('vec4 ndc');
|
|
vert.write_attrib('ndc = mul(vec4(pos.xyz, 1.0), WVP);');
|
|
|
|
frag.write_attrib('vec3 sp = vec3((ndc.xyz / ndc.w) * 0.5 + 0.5);');
|
|
frag.write_attrib('sp.y = 1.0 - sp.y;');
|
|
frag.write_attrib('sp.z -= 0.0001;'); // small bias
|
|
|
|
if (UITrait.inst.brushPaint == 1) frag.ndcpos = true; // Project
|
|
|
|
frag.add_uniform('vec4 inp', '_inputBrush');
|
|
frag.add_uniform('vec4 inplast', '_inputBrushLast');
|
|
frag.add_uniform('float aspectRatio', '_aspectRatioWindowF');
|
|
frag.write('vec2 bsp = sp.xy * 2.0 - 1.0;');
|
|
frag.write('bsp.x *= aspectRatio;');
|
|
frag.write('bsp = bsp * 0.5 + 0.5;');
|
|
|
|
frag.add_uniform('sampler2D gbufferD');
|
|
|
|
frag.add_out('vec4 fragColor[4]');
|
|
|
|
frag.add_uniform('float brushRadius', '_brushRadius');
|
|
frag.add_uniform('float brushOpacity', '_brushOpacity');
|
|
frag.add_uniform('float brushHardness', '_brushHardness');
|
|
|
|
if (Context.tool == ToolBrush ||
|
|
Context.tool == ToolEraser ||
|
|
Context.tool == ToolClone ||
|
|
Context.tool == ToolBlur ||
|
|
Context.tool == ToolParticle ||
|
|
decal) {
|
|
|
|
var depthReject = !UITrait.inst.xray;
|
|
if (UITrait.inst.brush3d && !UITrait.inst.brushDepthReject) depthReject = false;
|
|
|
|
// TODO: sp.z needs to take height channel into account
|
|
if (heightUsed) depthReject = false;
|
|
|
|
if (depthReject) {
|
|
#if (kha_opengl || kha_webgl)
|
|
frag.write('if (sp.z > textureLod(gbufferD, vec2(sp.x, 1.0 - sp.y), 0.0).r) discard;');
|
|
#else
|
|
frag.write('if (sp.z > textureLod(gbufferD, sp.xy, 0.0).r) discard;');
|
|
#end
|
|
}
|
|
|
|
if (decal || Context.tool == ToolParticle) {
|
|
frag.write('float dist = 0.0;');
|
|
}
|
|
else {
|
|
if (UITrait.inst.brush3d) {
|
|
|
|
#if (kha_opengl || kha_webgl)
|
|
frag.write('float depth = textureLod(gbufferD, vec2(inp.x, 1.0 - inp.y), 0.0).r;');
|
|
#else
|
|
frag.write('float depth = textureLod(gbufferD, inp.xy, 0.0).r;');
|
|
#end
|
|
|
|
frag.add_uniform('mat4 invVP', '_inverseViewProjectionMatrix');
|
|
frag.write('vec4 winp = vec4(vec2(inp.x, 1.0 - inp.y) * 2.0 - 1.0, depth * 2.0 - 1.0, 1.0);');
|
|
frag.write('winp = mul(winp, invVP);');
|
|
frag.write('winp.xyz /= winp.w;');
|
|
frag.wposition = true;
|
|
|
|
if (UITrait.inst.brushAngleReject || UITrait.inst.xray) {
|
|
frag.add_function(CyclesFunctions.str_octahedronWrap);
|
|
frag.add_uniform('sampler2D gbuffer0');
|
|
#if (kha_opengl || kha_webgl)
|
|
frag.write('vec2 g0 = textureLod(gbuffer0, vec2(inp.x, 1.0 - inp.y), 0.0).rg;');
|
|
#else
|
|
frag.write('vec2 g0 = textureLod(gbuffer0, inp.xy, 0.0).rg;');
|
|
#end
|
|
frag.write('vec3 wn;');
|
|
frag.write('wn.z = 1.0 - abs(g0.x) - abs(g0.y);');
|
|
frag.write('wn.xy = wn.z >= 0.0 ? g0.xy : octahedronWrap(g0.xy);');
|
|
frag.write('wn = normalize(wn);');
|
|
frag.write('float planeDist = dot(wn, winp.xyz - wposition);');
|
|
|
|
if (UITrait.inst.brushAngleReject && !UITrait.inst.xray) {
|
|
frag.write('if (planeDist < -0.01) discard;');
|
|
frag.n = true;
|
|
var angle = UITrait.inst.brushAngleRejectDot;
|
|
frag.write('if (dot(wn, n) < $angle) discard;');
|
|
}
|
|
}
|
|
|
|
#if (kha_opengl || kha_webgl)
|
|
frag.write('float depthlast = textureLod(gbufferD, vec2(inplast.x, 1.0 - inplast.y), 0.0).r;');
|
|
#else
|
|
frag.write('float depthlast = textureLod(gbufferD, inplast.xy, 0.0).r;');
|
|
#end
|
|
|
|
frag.write('vec4 winplast = vec4(vec2(inplast.x, 1.0 - inplast.y) * 2.0 - 1.0, depthlast * 2.0 - 1.0, 1.0);');
|
|
frag.write('winplast = mul(winplast, invVP);');
|
|
frag.write('winplast.xyz /= winplast.w;');
|
|
|
|
frag.write('vec3 pa = wposition - winp.xyz;');
|
|
if (UITrait.inst.xray) {
|
|
frag.write('pa += wn * vec3(planeDist, planeDist, planeDist);');
|
|
}
|
|
frag.write('vec3 ba = winplast.xyz - winp.xyz;');
|
|
frag.write('float h = clamp(dot(pa, ba) / dot(ba, ba), 0.0, 1.0);');
|
|
frag.write('float dist = length(pa - ba * h);');
|
|
frag.write('if (dist > brushRadius) discard;');
|
|
|
|
// Non-continuous
|
|
// frag.write('float dist = distance(wposition, winp.xyz);');
|
|
}
|
|
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;');
|
|
|
|
frag.write('vec2 binplast = inplast.xy * 2.0 - 1.0;');
|
|
frag.write('binplast.x *= aspectRatio;');
|
|
frag.write('binplast = binplast * 0.5 + 0.5;');
|
|
|
|
frag.write('vec2 pa = bsp.xy - binp.xy;');
|
|
frag.write('vec2 ba = binplast.xy - binp.xy;');
|
|
frag.write('float h = clamp(dot(pa, ba) / dot(ba, ba), 0.0, 1.0);');
|
|
frag.write('float dist = length(pa - ba * h);');
|
|
|
|
frag.write('if (dist > brushRadius) discard;');
|
|
|
|
// Non-continuous
|
|
// frag.write('float dist = distance(bsp.xy, binp.xy);');
|
|
}
|
|
}
|
|
}
|
|
else { // Fill, Bake
|
|
frag.write('float dist = 0.0;');
|
|
|
|
var angleFill = Context.tool == ToolFill && UITrait.inst.fillTypeHandle.position == 2;
|
|
if (angleFill) {
|
|
frag.add_function(CyclesFunctions.str_octahedronWrap);
|
|
frag.add_uniform('sampler2D gbuffer0');
|
|
frag.write('vec2 g0 = textureLod(gbuffer0, inp.xy, 0.0).rg;');
|
|
frag.write('vec3 wn;');
|
|
frag.write('wn.z = 1.0 - abs(g0.x) - abs(g0.y);');
|
|
frag.write('wn.xy = wn.z >= 0.0 ? g0.xy : octahedronWrap(g0.xy);');
|
|
frag.write('wn = normalize(wn);');
|
|
frag.n = true;
|
|
var angle = UITrait.inst.brushAngleRejectDot;
|
|
frag.write('if (dot(wn, n) < $angle) discard;');
|
|
}
|
|
}
|
|
|
|
if (UITrait.inst.colorIdPicked) {
|
|
vert.add_out('vec2 texCoordPick');
|
|
vert.write('texCoordPick = fract(subtex);');
|
|
frag.add_uniform('sampler2D texpaint_colorid'); // 1x1 picker
|
|
frag.add_uniform('sampler2D texcolorid', '_texcolorid'); // color map
|
|
frag.add_uniform('vec2 texcoloridSize', '_texcoloridSize'); // color map
|
|
frag.write('vec3 c1 = texelFetch(texpaint_colorid, ivec2(0, 0), 0).rgb;');
|
|
frag.write('vec3 c2 = texelFetch(texcolorid, ivec2(texCoordPick * texcoloridSize), 0).rgb;');
|
|
frag.write('if (any(c1 != c2)) discard;');
|
|
}
|
|
else if (faceFill) { // TODO: allow to combine with colorid mask
|
|
vert.add_out('vec2 texCoordPick');
|
|
vert.write('texCoordPick = fract(subtex);');
|
|
frag.add_uniform('sampler2D gbuffer2');
|
|
frag.add_uniform('sampler2D textrianglemap', '_textrianglemap'); // triangle map
|
|
frag.add_uniform('float textrianglemapSize', '_texpaintSize');
|
|
frag.add_uniform('vec2 gbufferSize', '_gbufferSize');
|
|
#if (kha_opengl || kha_webgl)
|
|
frag.write('vec2 texCoordInp = texelFetch(gbuffer2, ivec2(inp.x * gbufferSize.x, (1.0 - inp.y) * gbufferSize.y), 0).ba;');
|
|
#else
|
|
frag.write('vec2 texCoordInp = texelFetch(gbuffer2, ivec2(inp.x * gbufferSize.x, inp.y * gbufferSize.y), 0).ba;');
|
|
#end
|
|
frag.write('vec4 c1 = texelFetch(textrianglemap, ivec2(texCoordInp * textrianglemapSize), 0);');
|
|
frag.write('vec4 c2 = texelFetch(textrianglemap, ivec2(texCoordPick * textrianglemapSize), 0);');
|
|
frag.write('if (any(c1 != c2)) discard;');
|
|
}
|
|
|
|
if (UITrait.inst.pickerMaskHandle.position == 1) { // material id mask
|
|
frag.wvpposition = true;
|
|
frag.write('vec2 picker_sample_tc = vec2(wvpposition.x / wvpposition.w, wvpposition.y / wvpposition.w) * 0.5 + 0.5;');
|
|
#if (kha_direct3d11 || kha_direct3d12)
|
|
frag.write('picker_sample_tc.y = 1.0 - picker_sample_tc.y;');
|
|
#end
|
|
frag.add_uniform('sampler2D texpaint_nor_undo', '_texpaint_nor_undo');
|
|
var matid = UITrait.inst.materialIdPicked / 255;
|
|
frag.write('if ($matid != textureLod(texpaint_nor_undo, picker_sample_tc, 0.0).a) discard;');
|
|
}
|
|
|
|
// TexCoords - project
|
|
if (UITrait.inst.brushPaint == 1 || decal) {
|
|
frag.add_uniform('float brushScale', '_brushScale');
|
|
frag.write_attrib('vec2 uvsp = sp.xy;');
|
|
|
|
if (decal) {
|
|
frag.write_attrib('uvsp -= inp.xy;');
|
|
frag.write_attrib('uvsp.x *= aspectRatio;');
|
|
|
|
frag.write_attrib('uvsp *= 0.21 / (brushRadius * 0.9);');
|
|
frag.write_attrib('uvsp += vec2(0.5, 0.5);');
|
|
|
|
frag.write_attrib('if (uvsp.x < 0.01 || uvsp.y < 0.01 || uvsp.x > 0.99 || uvsp.y > 0.99) discard;');
|
|
}
|
|
else {
|
|
frag.write_attrib('uvsp.x *= aspectRatio;');
|
|
}
|
|
|
|
frag.write_attrib('vec2 texCoord = fract(uvsp * brushScale);');
|
|
|
|
if (UITrait.inst.brushRot > 0.0) {
|
|
var a = UITrait.inst.brushRot * (Math.PI / 180);
|
|
frag.write('texCoord = vec2(texCoord.x * ${Math.cos(a)} - texCoord.y * ${Math.sin(a)}, texCoord.x * ${Math.sin(a)} + texCoord.y * ${Math.cos(a)});');
|
|
}
|
|
}
|
|
// TexCoords - uvmap
|
|
else if (UITrait.inst.brushPaint == 0) {
|
|
vert.add_uniform('float brushScale', '_brushScale');
|
|
vert.add_out('vec2 texCoord');
|
|
vert.write('texCoord = subtex * brushScale;');
|
|
|
|
if (UITrait.inst.brushRot > 0.0) {
|
|
var a = UITrait.inst.brushRot * (Math.PI / 180);
|
|
vert.write('texCoord = vec2(texCoord.x * ${Math.cos(a)} - texCoord.y * ${Math.sin(a)}, texCoord.x * ${Math.sin(a)} + texCoord.y * ${Math.cos(a)});');
|
|
}
|
|
}
|
|
else { // Triplanar
|
|
frag.wposition = true;
|
|
frag.n = true;
|
|
frag.add_uniform('float brushScale', '_brushScale');
|
|
frag.write_attrib('vec3 triWeight = wnormal * wnormal;'); // n * n
|
|
frag.write_attrib('float triMax = max(triWeight.x, max(triWeight.y, triWeight.z));');
|
|
frag.write_attrib('triWeight = max(triWeight - triMax * 0.75, 0.0);');
|
|
frag.write_attrib('vec3 texCoordBlend = triWeight * (1.0 / (triWeight.x + triWeight.y + triWeight.z));');
|
|
frag.write_attrib('vec2 texCoord = fract(wposition.yz * brushScale);');
|
|
frag.write_attrib('vec2 texCoord1 = fract(wposition.xz * brushScale);');
|
|
frag.write_attrib('vec2 texCoord2 = fract(wposition.xy * brushScale);');
|
|
}
|
|
|
|
if (Context.tool == ToolClone || Context.tool == ToolBlur) {
|
|
|
|
frag.add_uniform('sampler2D gbuffer2');
|
|
frag.add_uniform('vec2 gbufferSize', '_gbufferSize');
|
|
frag.add_uniform('sampler2D texpaint_undo', '_texpaint_undo');
|
|
frag.add_uniform('sampler2D texpaint_nor_undo', '_texpaint_nor_undo');
|
|
frag.add_uniform('sampler2D texpaint_pack_undo', '_texpaint_pack_undo');
|
|
|
|
if (Context.tool == ToolClone) {
|
|
frag.add_uniform('vec2 cloneDelta', '_cloneDelta');
|
|
#if (kha_opengl || kha_webgl)
|
|
frag.write('vec2 texCoordInp = texelFetch(gbuffer2, ivec2((sp.x + cloneDelta.x) * gbufferSize.x, (1.0 - (sp.y + cloneDelta.y)) * gbufferSize.y), 0).ba;');
|
|
#else
|
|
frag.write('vec2 texCoordInp = texelFetch(gbuffer2, ivec2((sp.x + cloneDelta.x) * gbufferSize.x, (sp.y + cloneDelta.y) * gbufferSize.y), 0).ba;');
|
|
#end
|
|
|
|
frag.write('vec3 texpaint_pack_sample = textureLod(texpaint_pack_undo, texCoordInp, 0.0).rgb;');
|
|
var base = 'textureLod(texpaint_undo, texCoordInp, 0.0).rgb';
|
|
var rough = 'texpaint_pack_sample.g';
|
|
var met = 'texpaint_pack_sample.b';
|
|
var occ = 'texpaint_pack_sample.r';
|
|
var nortan = 'textureLod(texpaint_nor_undo, texCoordInp, 0.0).rgb';
|
|
var height = '0.0';
|
|
var opac = '1.0';
|
|
frag.write('vec3 basecol = $base;');
|
|
frag.write('float roughness = $rough;');
|
|
frag.write('float metallic = $met;');
|
|
frag.write('float occlusion = $occ;');
|
|
frag.write('vec3 nortan = $nortan;');
|
|
frag.write('float height = $height;');
|
|
frag.write('float opacity = $opac * brushOpacity;');
|
|
if (Context.material.paintEmis) {
|
|
frag.write('float emis = 0.0;');
|
|
}
|
|
if (Context.material.paintSubs) {
|
|
frag.write('float subs = 0.0;');
|
|
}
|
|
}
|
|
else { // Blur
|
|
#if (kha_opengl || kha_webgl)
|
|
frag.write('vec2 texCoordInp = texelFetch(gbuffer2, ivec2(sp.x * gbufferSize.x, (1.0 - sp.y) * gbufferSize.y), 0).ba;');
|
|
#else
|
|
frag.write('vec2 texCoordInp = texelFetch(gbuffer2, ivec2(sp.x * gbufferSize.x, sp.y * gbufferSize.y), 0).ba;');
|
|
#end
|
|
|
|
frag.write('vec3 basecol = vec3(0.0, 0.0, 0.0);');
|
|
frag.write('float roughness = 0.0;');
|
|
frag.write('float metallic = 0.0;');
|
|
frag.write('float occlusion = 0.0;');
|
|
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 (Context.material.paintEmis) {
|
|
frag.write('float emis = 0.0;');
|
|
}
|
|
if (Context.material.paintSubs) {
|
|
frag.write('float subs = 0.0;');
|
|
}
|
|
#if (kha_direct3d11 || kha_direct3d12)
|
|
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
|
|
frag.write('const float blur_weight[15] = float[](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);');
|
|
#end
|
|
frag.add_uniform('float texpaintSize', '_texpaintSize');
|
|
frag.write('float blur_step = 1.0 / texpaintSize;');
|
|
frag.write('for (int i = -7; i <= 7; i++) {');
|
|
frag.write('basecol += texture(texpaint_undo, texCoordInp + vec2(blur_step * i, 0.0)).rgb * blur_weight[i + 7];');
|
|
frag.write('vec3 texpaint_pack_sample = texture(texpaint_pack_undo, texCoordInp + vec2(blur_step * i, 0.0)).rgb * blur_weight[i + 7];');
|
|
frag.write('roughness += texpaint_pack_sample.g;');
|
|
frag.write('metallic += texpaint_pack_sample.b;');
|
|
frag.write('occlusion += texpaint_pack_sample.r;');
|
|
frag.write('nortan += texture(texpaint_nor_undo, texCoordInp + vec2(blur_step * i, 0.0)).rgb * blur_weight[i + 7];');
|
|
frag.write('}');
|
|
frag.write('for (int i = -7; i <= 7; i++) {');
|
|
frag.write('basecol += texture(texpaint_undo, texCoordInp + vec2(0.0, blur_step * i)).rgb * blur_weight[i + 7];');
|
|
frag.write('vec3 texpaint_pack_sample = texture(texpaint_pack_undo, texCoordInp + vec2(0.0, blur_step * i)).rgb * blur_weight[i + 7];');
|
|
frag.write('roughness += texpaint_pack_sample.g;');
|
|
frag.write('metallic += texpaint_pack_sample.b;');
|
|
frag.write('occlusion += texpaint_pack_sample.r;');
|
|
frag.write('nortan += texture(texpaint_nor_undo, texCoordInp + vec2(0.0, blur_step * i)).rgb * blur_weight[i + 7];');
|
|
frag.write('}');
|
|
}
|
|
}
|
|
else {
|
|
Cycles.parse_emission = Context.material.paintEmis;
|
|
Cycles.parse_subsurface = Context.material.paintSubs;
|
|
Cycles.parse_height = Context.material.paintHeight;
|
|
Cycles.parse_height_as_channel = true;
|
|
Cycles.triplanar = UITrait.inst.brushPaint == 2;
|
|
var sout = Cycles.parse(UINodes.inst.canvas, con_paint, vert, frag, null, null, null, matcon);
|
|
Cycles.parse_emission = false;
|
|
Cycles.parse_subsurface = false;
|
|
Cycles.parse_height_as_channel = false;
|
|
Cycles.parse_height = false;
|
|
var base = sout.out_basecol;
|
|
var rough = sout.out_roughness;
|
|
var met = sout.out_metallic;
|
|
var occ = sout.out_occlusion;
|
|
var nortan = Cycles.out_normaltan;
|
|
var height = sout.out_height;
|
|
var opac = sout.out_opacity;
|
|
var emis = sout.out_emission;
|
|
var subs = sout.out_subsurface;
|
|
frag.write('vec3 basecol = $base;');
|
|
frag.write('float roughness = $rough;');
|
|
frag.write('float metallic = $met;');
|
|
frag.write('float occlusion = $occ;');
|
|
frag.write('vec3 nortan = $nortan;');
|
|
frag.write('float height = $height;');
|
|
frag.write('float opacity = $opac * brushOpacity;');
|
|
if (Context.material.paintEmis) {
|
|
frag.write('float emis = $emis;');
|
|
}
|
|
if (Context.material.paintSubs) {
|
|
frag.write('float subs = $subs;');
|
|
}
|
|
if (height != '0' && !heightUsed) {
|
|
heightUsed = true;
|
|
// Height used for the first time, also rebuild vertex shader
|
|
return make_paint(data, matcon);
|
|
}
|
|
if (emis != '0') emisUsed = true;
|
|
if (subs != '0') subsUsed = true;
|
|
}
|
|
|
|
if (Context.tool == ToolDecal) {
|
|
frag.add_uniform('sampler2D texdecalmask', '_texdecalmask');
|
|
frag.write('opacity *= textureLod(texdecalmask, texCoord, 0.0).r;');
|
|
}
|
|
else if (Context.tool == ToolText) {
|
|
frag.add_uniform('sampler2D textexttool', '_textexttool');
|
|
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)
|
|
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;');
|
|
}
|
|
|
|
// Manual blending to preserve memory
|
|
frag.wvpposition = true;
|
|
frag.write('vec2 sample_tc = vec2(wvpposition.x / wvpposition.w, wvpposition.y / wvpposition.w) * 0.5 + 0.5;');
|
|
#if (kha_direct3d11 || kha_direct3d12)
|
|
frag.write('sample_tc.y = 1.0 - sample_tc.y;');
|
|
#end
|
|
frag.add_uniform('sampler2D paintmask');
|
|
frag.write('float sample_mask = textureLod(paintmask, sample_tc, 0.0).r;');
|
|
frag.write('str = max(str, sample_mask);');
|
|
|
|
frag.add_uniform('sampler2D texpaint_undo', '_texpaint_undo');
|
|
frag.write('vec4 sample_undo = textureLod(texpaint_undo, sample_tc, 0.0);');
|
|
|
|
var matid = Context.material.id / 255;
|
|
if (UITrait.inst.pickerMaskHandle.position == 1) { // material id mask
|
|
matid = UITrait.inst.materialIdPicked / 255; // Keep existing material id in place when mask is set
|
|
}
|
|
frag.write('float matid = $matid;');
|
|
|
|
// TODO: Use emission/subsurface matid
|
|
// matid % 3 == 0 - normal, 1 - emission, 2 - subsurface
|
|
if (Context.material.paintSubs) {
|
|
frag.write('if (subs > 0) { matid = 254 / 255; }');
|
|
}
|
|
if (Context.material.paintEmis) {
|
|
frag.write('if (emis > 0) { matid = 255 / 255; }');
|
|
}
|
|
|
|
if (layered) {
|
|
if (eraser) {
|
|
frag.write('fragColor[0] = vec4(mix(sample_undo.rgb, vec3(0.0, 0.0, 0.0), str), sample_undo.a - str);');
|
|
frag.write('nortan = vec3(0.5, 0.5, 1.0);');
|
|
frag.write('occlusion = 1.0;');
|
|
frag.write('roughness = 0.0;');
|
|
frag.write('metallic = 0.0;');
|
|
frag.write('matid = 0.0;');
|
|
}
|
|
else if (decal) {
|
|
frag.write('fragColor[0] = vec4(mix(sample_undo.rgb, basecol, str), max(str, sample_undo.a));');
|
|
}
|
|
else {
|
|
frag.write('fragColor[0] = vec4(basecol, max(str, sample_undo.a));');
|
|
}
|
|
frag.write('fragColor[1] = vec4(nortan, matid);');
|
|
|
|
var height = '0.0';
|
|
if (Context.material.paintHeight && heightUsed) {
|
|
height = 'height';
|
|
}
|
|
|
|
if (decal) {
|
|
frag.add_uniform('sampler2D texpaint_pack_undo', '_texpaint_pack_undo');
|
|
frag.write('vec4 sample_pack_undo = textureLod(texpaint_pack_undo, sample_tc, 0.0);');
|
|
frag.write('fragColor[2] = mix(sample_pack_undo, vec4(occlusion, roughness, metallic, $height), str);');
|
|
}
|
|
else {
|
|
frag.write('fragColor[2] = vec4(occlusion, roughness, metallic, $height);');
|
|
}
|
|
}
|
|
else {
|
|
if (eraser) {
|
|
frag.write('fragColor[0] = vec4(${Layers.defaultBase}, ${Layers.defaultBase}, ${Layers.defaultBase}, 0.0);');
|
|
frag.write('fragColor[1] = vec4(0.5, 0.5, 1.0, 0.0);');
|
|
frag.write('fragColor[2] = vec4(1.0, ${Layers.defaultRough}, 0.0, 0.0);');
|
|
}
|
|
else {
|
|
frag.add_uniform('sampler2D texpaint_nor_undo', '_texpaint_nor_undo');
|
|
frag.add_uniform('sampler2D texpaint_pack_undo', '_texpaint_pack_undo');
|
|
frag.write('vec4 sample_nor_undo = textureLod(texpaint_nor_undo, sample_tc, 0.0);');
|
|
frag.write('vec4 sample_pack_undo = textureLod(texpaint_pack_undo, sample_tc, 0.0);');
|
|
|
|
frag.write('fragColor[0] = vec4(mix(sample_undo.rgb, basecol, str), 0.0);');
|
|
frag.write('fragColor[1] = vec4(mix(sample_nor_undo.rgb, nortan, str), matid);');
|
|
if (Context.material.paintHeight && heightUsed) {
|
|
frag.write('fragColor[2] = mix(sample_pack_undo, vec4(occlusion, roughness, metallic, height), str);');
|
|
}
|
|
else {
|
|
frag.write('fragColor[2] = vec4(mix(sample_pack_undo.rgb, vec3(occlusion, roughness, metallic), str), 0.0);');
|
|
}
|
|
}
|
|
}
|
|
frag.write('fragColor[3] = vec4(str, 0.0, 0.0, 1.0);');
|
|
|
|
if (!Context.material.paintBase) {
|
|
con_paint.data.color_writes_red[0] = false;
|
|
con_paint.data.color_writes_green[0] = false;
|
|
con_paint.data.color_writes_blue[0] = false;
|
|
}
|
|
if (!Context.material.paintNor) {
|
|
con_paint.data.color_writes_red[1] = false;
|
|
con_paint.data.color_writes_green[1] = false;
|
|
con_paint.data.color_writes_blue[1] = false;
|
|
}
|
|
if (!Context.material.paintOcc) {
|
|
con_paint.data.color_writes_red[2] = false;
|
|
}
|
|
if (!Context.material.paintRough) {
|
|
con_paint.data.color_writes_green[2] = false;
|
|
}
|
|
if (!Context.material.paintMet) {
|
|
con_paint.data.color_writes_blue[2] = false;
|
|
}
|
|
if (!Context.material.paintHeight) {
|
|
con_paint.data.color_writes_alpha[2] = false;
|
|
}
|
|
|
|
// Base color only as mask
|
|
var isMask = Context.layerIsMask;
|
|
if (isMask) {
|
|
// TODO: Apply opacity into base
|
|
// frag.write('fragColor[0].rgb *= fragColor[0].a;');
|
|
con_paint.data.color_writes_red[1] = false;
|
|
con_paint.data.color_writes_green[1] = false;
|
|
con_paint.data.color_writes_blue[1] = false;
|
|
con_paint.data.color_writes_red[2] = false;
|
|
con_paint.data.color_writes_green[2] = false;
|
|
con_paint.data.color_writes_blue[2] = false;
|
|
con_paint.data.color_writes_alpha[2] = false;
|
|
}
|
|
|
|
if (Context.tool == ToolBake) {
|
|
if (UITrait.inst.bakeType == 0) { // AO
|
|
// Apply normal channel
|
|
frag.wposition = true;
|
|
frag.n = true;
|
|
frag.vVec = true;
|
|
frag.add_function(CyclesFunctions.str_cotangentFrame);
|
|
#if (kha_direct3d11 || kha_direct3d12)
|
|
frag.write('mat3 TBN = cotangentFrame(n, vVec, texCoord);');
|
|
#else
|
|
frag.write('mat3 TBN = cotangentFrame(n, -vVec, texCoord);');
|
|
#end
|
|
frag.write('n = nortan * 2.0 - 1.0;');
|
|
frag.write('n.y = -n.y;');
|
|
frag.write('n = normalize(mul(n, TBN));');
|
|
|
|
frag.write('const vec3 voxelgiHalfExtents = vec3(1.0, 1.0, 1.0);');
|
|
frag.write('vec3 voxpos = wposition / voxelgiHalfExtents;');
|
|
frag.add_uniform('sampler3D voxels');
|
|
frag.add_function(CyclesFunctions.str_traceAO);
|
|
frag.n = true;
|
|
var strength = UITrait.inst.bakeAoStrength;
|
|
var radius = UITrait.inst.bakeAoRadius;
|
|
var offset = UITrait.inst.bakeAoOffset;
|
|
frag.write('float ao = traceAO(voxpos, n, $radius, $offset) * $strength;');
|
|
if (UITrait.inst.bakeAxis > 0) {
|
|
var axis = axisString(UITrait.inst.bakeAxis);
|
|
frag.write('ao *= dot(n, $axis);');
|
|
}
|
|
frag.write('ao = 1.0 - ao;');
|
|
frag.write('fragColor[0] = vec4(ao, ao, ao, 1.0);');
|
|
}
|
|
else if (UITrait.inst.bakeType == 1) { // Curvature
|
|
var strength = UITrait.inst.bakeCurvStrength * 2.0;
|
|
var radius = (1.0 / UITrait.inst.bakeCurvRadius) * 0.25;
|
|
var offset = UITrait.inst.bakeCurvOffset / 10;
|
|
frag.n = true;
|
|
frag.write('vec3 dx = dFdx(n);');
|
|
frag.write('vec3 dy = dFdy(n);');
|
|
frag.write('float curvature = max(dot(dx, dx), dot(dy, dy));');
|
|
frag.write('curvature = clamp(pow(curvature, $radius) * $strength + $offset, 0.0, 1.0);');
|
|
if (UITrait.inst.bakeAxis > 0) {
|
|
var axis = axisString(UITrait.inst.bakeAxis);
|
|
frag.write('curvature *= dot(n, $axis);');
|
|
}
|
|
frag.write('fragColor[0] = vec4(curvature, curvature, curvature, 1.0);');
|
|
}
|
|
else if (UITrait.inst.bakeType == 2) { // Normal (Tangent)
|
|
frag.n = true;
|
|
frag.add_uniform('sampler2D texpaint_undo', '_texpaint_undo');
|
|
frag.write('vec3 n0 = textureLod(texpaint_undo, texCoord, 0.0).rgb * vec3(2.0, 2.0, 2.0) - vec3(1.0, 1.0, 1.0);');
|
|
frag.add_function(CyclesFunctions.str_cotangentFrame);
|
|
frag.write('mat3 invTBN = transpose(cotangentFrame(n, n, texCoord));');
|
|
frag.write('vec3 res = normalize(mul(n0, invTBN)) * vec3(0.5, 0.5, 0.5) + vec3(0.5, 0.5, 0.5);');
|
|
frag.write('fragColor[0] = vec4(res, 1.0);');
|
|
}
|
|
else if (UITrait.inst.bakeType == 3) { // Normal (World)
|
|
frag.n = true;
|
|
frag.write('fragColor[0] = vec4(n * vec3(0.5, 0.5, 0.5) + vec3(0.5, 0.5, 0.5), 1.0);');
|
|
}
|
|
else if (UITrait.inst.bakeType == 4) { // Position
|
|
frag.wposition = true;
|
|
frag.write('fragColor[0] = vec4(wposition * vec3(0.5, 0.5, 0.5) + vec3(0.5, 0.5, 0.5), 1.0);');
|
|
}
|
|
else if (UITrait.inst.bakeType == 5) { // TexCoord
|
|
frag.write('fragColor[0] = vec4(texCoord.xy, 0.0, 1.0);');
|
|
}
|
|
else if (UITrait.inst.bakeType == 6) { // Material ID
|
|
frag.add_uniform('sampler2D texpaint_nor_undo', '_texpaint_nor_undo');
|
|
frag.write('float sample_matid = textureLod(texpaint_nor_undo, texCoord, 0.0).a;');
|
|
frag.write('float matid_r = fract(sin(dot(vec2(sample_matid, sample_matid * 20.0), vec2(12.9898, 78.233))) * 43758.5453);');
|
|
frag.write('float matid_g = fract(sin(dot(vec2(sample_matid * 20.0, sample_matid), vec2(12.9898, 78.233))) * 43758.5453);');
|
|
frag.write('float matid_b = fract(sin(dot(vec2(sample_matid, sample_matid * 40.0), vec2(12.9898, 78.233))) * 43758.5453);');
|
|
frag.write('fragColor[0] = vec4(matid_r, matid_g, matid_b, 1.0);');
|
|
}
|
|
else if (UITrait.inst.bakeType == 7) { // Object ID
|
|
frag.add_uniform('float objectId', '_objectId');
|
|
frag.write('float id_r = fract(sin(dot(vec2(objectId, objectId * 20.0), vec2(12.9898, 78.233))) * 43758.5453);');
|
|
frag.write('float id_g = fract(sin(dot(vec2(objectId * 20.0, objectId), vec2(12.9898, 78.233))) * 43758.5453);');
|
|
frag.write('float id_b = fract(sin(dot(vec2(objectId, objectId * 40.0), vec2(12.9898, 78.233))) * 43758.5453);');
|
|
frag.write('fragColor[0] = vec4(id_r, id_g, id_b, 1.0);');
|
|
}
|
|
}
|
|
|
|
Cycles.finalize(con_paint);
|
|
Cycles.triplanar = false;
|
|
con_paint.data.shader_from_source = true;
|
|
con_paint.data.vertex_shader = vert.get();
|
|
con_paint.data.fragment_shader = frag.get();
|
|
|
|
return con_paint;
|
|
}
|
|
|
|
static function axisString(i:Int):String {
|
|
return i == 1 ? "vec3(1,0,0)" :
|
|
i == 2 ? "vec3(0,1,0)" :
|
|
i == 3 ? "vec3(0,0,1)" :
|
|
i == 4 ? "vec3(-1,0,0)" :
|
|
i == 5 ? "vec3(0,-1,0)" :
|
|
"vec3(0,0,-1)";
|
|
}
|
|
|
|
public static function make_mesh_preview(data:CyclesShaderData, matcon:TMaterialContext):CyclesShaderContext {
|
|
var context_id = 'mesh';
|
|
var con_mesh:CyclesShaderContext = data.add_context({
|
|
name: context_id,
|
|
depth_write: true,
|
|
compare_mode: 'less',
|
|
cull_mode: 'clockwise',
|
|
vertex_elements: [{name: "pos", data: 'short4norm'},{name: "nor", data: 'short2norm'},{name: "tex", data: 'short2norm'}] });
|
|
|
|
var vert = con_mesh.make_vert();
|
|
var frag = con_mesh.make_frag();
|
|
|
|
frag.ins = vert.outs;
|
|
|
|
vert.add_uniform('mat4 WVP', '_worldViewProjectionMatrix');
|
|
vert.write_attrib('gl_Position = mul(vec4(pos.xyz, 1.0), WVP);');
|
|
|
|
vert.add_out('vec2 texCoord');
|
|
vert.add_uniform('float brushScale', '_brushScale');
|
|
vert.write_attrib('texCoord = tex * brushScale;');
|
|
|
|
if (heightUsed) {
|
|
frag.bposition = true;
|
|
}
|
|
|
|
Cycles.parse_height = heightUsed;
|
|
var sout = Cycles.parse(UINodes.inst.canvas, con_mesh, vert, frag, null, null, null, matcon);
|
|
Cycles.parse_height = false;
|
|
var base = sout.out_basecol;
|
|
var rough = sout.out_roughness;
|
|
var met = sout.out_metallic;
|
|
var occ = sout.out_occlusion;
|
|
var opac = sout.out_opacity;
|
|
var nortan = Cycles.out_normaltan;
|
|
frag.write('vec3 basecol = pow($base, vec3(2.2, 2.2, 2.2));');
|
|
frag.write('float roughness = $rough;');
|
|
frag.write('float metallic = $met;');
|
|
frag.write('float occlusion = $occ;');
|
|
frag.write('float opacity = $opac;');
|
|
frag.write('vec3 nortan = $nortan;');
|
|
|
|
var decal = UITrait.inst.decalPreview;
|
|
if (decal) {
|
|
if (Context.tool == ToolDecal) {
|
|
frag.add_uniform('sampler2D texdecalmask', '_texdecalmask');
|
|
frag.write('opacity *= textureLod(texdecalmask, texCoord, 0.0).r;');
|
|
}
|
|
else if (Context.tool == ToolText) {
|
|
frag.add_uniform('sampler2D textexttool', '_textexttool');
|
|
frag.write('opacity *= textureLod(textexttool, texCoord, 0.0).r;');
|
|
}
|
|
var opac = 0.05;
|
|
frag.write('if (opacity < $opac) discard;');
|
|
}
|
|
|
|
frag.add_out('vec4 fragColor[3]');
|
|
frag.n = true;
|
|
|
|
frag.add_function(CyclesFunctions.str_packFloatInt16);
|
|
frag.add_function(CyclesFunctions.str_packFloat2);
|
|
frag.add_function(CyclesFunctions.str_cotangentFrame);
|
|
frag.add_function(CyclesFunctions.str_octahedronWrap);
|
|
|
|
// Apply normal channel
|
|
if (decal) {
|
|
// TODO
|
|
}
|
|
else {
|
|
frag.vVec = true;
|
|
#if (kha_direct3d11 || kha_direct3d12)
|
|
frag.write('mat3 TBN = cotangentFrame(n, vVec, texCoord);');
|
|
#else
|
|
frag.write('mat3 TBN = cotangentFrame(n, -vVec, texCoord);');
|
|
#end
|
|
frag.write('n = nortan * 2.0 - 1.0;');
|
|
frag.write('n.y = -n.y;');
|
|
frag.write('n = normalize(mul(n, TBN));');
|
|
}
|
|
|
|
frag.write('n /= (abs(n.x) + abs(n.y) + abs(n.z));');
|
|
frag.write('n.xy = n.z >= 0.0 ? n.xy : octahedronWrap(n.xy);');
|
|
// uint matid = 0;
|
|
frag.write('fragColor[0] = vec4(n.x, n.y, roughness, packFloatInt16(metallic, uint(0)));'); // metallic/matid
|
|
frag.write('fragColor[1] = vec4(basecol.r, basecol.g, basecol.b, packFloat2(occlusion, 1.0));'); // occ/spec
|
|
frag.write('fragColor[2] = vec4(0.0, 0.0, 0.0, 0.0);'); // veloc
|
|
|
|
Cycles.finalize(con_mesh);
|
|
con_mesh.data.shader_from_source = true;
|
|
con_mesh.data.vertex_shader = vert.get();
|
|
con_mesh.data.fragment_shader = frag.get();
|
|
|
|
return con_mesh;
|
|
}
|
|
|
|
public static function make_particle(data:CyclesShaderData):CyclesShaderContext {
|
|
var context_id = 'mesh';
|
|
var con_part:CyclesShaderContext = data.add_context({
|
|
name: context_id,
|
|
depth_write: false,
|
|
compare_mode: 'always',
|
|
cull_mode: 'clockwise',
|
|
vertex_elements: [{name: "pos", data: 'short4norm'}]
|
|
});
|
|
|
|
var vert = con_part.make_vert();
|
|
var frag = con_part.make_frag();
|
|
frag.ins = vert.outs;
|
|
|
|
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";
|
|
vert.add_function(str_tex_hash);
|
|
vert.add_out('float p_age');
|
|
vert.write('p_age = pd[3][3] - gl_InstanceID * pd[0][1];');
|
|
vert.write('p_age -= p_age * fhash(gl_InstanceID) * pd[2][3];');
|
|
|
|
vert.write('if (pd[0][0] > 0 && p_age < 0) p_age += (int(-p_age / pd[0][0]) + 1) * pd[0][0];');
|
|
|
|
vert.add_out('float p_lifetime');
|
|
vert.write('p_lifetime = pd[0][2];');
|
|
vert.write('if (p_age < 0 || p_age > p_lifetime) {');
|
|
// vert.write(' SPIRV_Cross_Output stage_output;');
|
|
// vert.write(' stage_output.svpos /= 0.0;');
|
|
// vert.write(' return stage_output;');
|
|
vert.write(' spos /= 0.0;');
|
|
vert.write('}');
|
|
|
|
vert.add_out('vec3 p_velocity');
|
|
vert.write('p_velocity = vec3(pd[1][0], pd[1][1], pd[1][2]);');
|
|
vert.write('p_velocity.x += fhash(gl_InstanceID) * pd[1][3] - pd[1][3] / 2;');
|
|
vert.write('p_velocity.y += fhash(gl_InstanceID + pd[0][3]) * pd[1][3] - pd[1][3] / 2;');
|
|
vert.write('p_velocity.z += fhash(gl_InstanceID + 2 * pd[0][3]) * pd[1][3] - pd[1][3] / 2;');
|
|
vert.write('p_velocity.x += (pd[2][0] * p_age) / 5;');
|
|
vert.write('p_velocity.y += (pd[2][1] * p_age) / 5;');
|
|
vert.write('p_velocity.z += (pd[2][2] * p_age) / 5;');
|
|
|
|
vert.add_out('vec3 p_location');
|
|
vert.write('p_location = p_velocity * p_age;');
|
|
vert.write('spos.xyz += p_location;');
|
|
|
|
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 gbufferD');
|
|
// frag.write('fragColor *= 1.0 - clamp(distance(textureLod(gbufferD, 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();
|
|
con_part.data.fragment_shader = frag.get();
|
|
|
|
return con_part;
|
|
}
|
|
|
|
public static function make_mesh(data:CyclesShaderData):CyclesShaderContext {
|
|
var context_id = 'mesh';
|
|
var con_mesh:CyclesShaderContext = data.add_context({
|
|
name: context_id,
|
|
depth_write: true,
|
|
compare_mode: 'less',
|
|
cull_mode: UITrait.inst.culling ? 'clockwise' : 'none',
|
|
vertex_elements: [{name: "pos", data: 'short4norm'},{name: "nor", data: 'short2norm'},{name: "tex", data: 'short2norm'}] });
|
|
|
|
var vert = con_mesh.make_vert();
|
|
var frag = con_mesh.make_frag();
|
|
|
|
vert.add_out('vec2 texCoord');
|
|
frag.wvpposition = true;
|
|
vert.add_out('vec4 prevwvpposition');
|
|
vert.add_uniform('mat4 VP', '_viewProjectionMatrix');
|
|
vert.add_uniform('mat4 prevWVP', '_prevWorldViewProjectionMatrix');
|
|
vert.wposition = true;
|
|
|
|
// Height
|
|
// TODO: can cause TAA issues
|
|
if (heightUsed) {
|
|
var displaceStrength = UITrait.inst.displaceStrength * 0.02;
|
|
vert.n = true;
|
|
vert.write('float height = 0.0;');
|
|
var numLayers = 0;
|
|
for (l in Project.layers) {
|
|
if (!l.visible) continue;
|
|
if (numLayers > 16) break;
|
|
numLayers++;
|
|
vert.add_uniform('sampler2D texpaint_pack_vert' + l.id, '_texpaint_pack_vert' + l.id);
|
|
vert.write('height += textureLod(texpaint_pack_vert' + l.id + ', tex, 0.0).a;');
|
|
}
|
|
vert.write('wposition += wnormal * vec3(height, height, height) * vec3($displaceStrength, $displaceStrength, $displaceStrength);');
|
|
}
|
|
//
|
|
|
|
vert.write('gl_Position = mul(vec4(wposition.xyz, 1.0), VP);');
|
|
vert.write('texCoord = tex;');
|
|
if (heightUsed) {
|
|
vert.add_uniform('mat4 invW', '_inverseWorldMatrix');
|
|
vert.write('prevwvpposition = mul(mul(vec4(wposition, 1.0), invW), prevWVP);');
|
|
}
|
|
else {
|
|
vert.write('prevwvpposition = mul(vec4(pos.xyz, 1.0), prevWVP);');
|
|
}
|
|
|
|
frag.ins = vert.outs;
|
|
|
|
frag.add_out('vec4 fragColor[3]');
|
|
frag.n = true;
|
|
frag.add_function(CyclesFunctions.str_packFloatInt16);
|
|
frag.add_function(CyclesFunctions.str_packFloat2);
|
|
|
|
if (Context.tool == ToolColorId) {
|
|
frag.add_uniform('sampler2D texcolorid', '_texcolorid');
|
|
frag.write('fragColor[0] = vec4(n.xy, 1.0, packFloatInt16(0.0, uint(0)));'); // met/rough
|
|
frag.write('vec3 idcol = pow(textureLod(texcolorid, texCoord, 0.0).rgb, vec3(2.2, 2.2, 2.2));');
|
|
frag.write('fragColor[1] = vec4(idcol.rgb, packFloat2(1.0, 1.0));'); // occ/spec
|
|
}
|
|
else {
|
|
frag.add_function(CyclesFunctions.str_octahedronWrap);
|
|
|
|
frag.write('vec3 basecol;');
|
|
frag.write('float roughness;');
|
|
frag.write('float metallic;');
|
|
frag.write('float occlusion;');
|
|
frag.write('float opacity;');
|
|
frag.write('float specular;');
|
|
frag.write('float matid = 0.0;');
|
|
|
|
frag.vVec = true;
|
|
frag.add_function(CyclesFunctions.str_cotangentFrame);
|
|
#if (kha_direct3d11 || kha_direct3d12)
|
|
frag.write('mat3 TBN = cotangentFrame(n, vVec, texCoord);');
|
|
#else
|
|
frag.write('mat3 TBN = cotangentFrame(n, -vVec, texCoord);');
|
|
#end
|
|
|
|
if (Project.layers[0].visible) {
|
|
|
|
if (Context.layer.paintBase) {
|
|
frag.add_shared_sampler('sampler2D texpaint');
|
|
frag.write('basecol = pow(textureLodShared(texpaint, texCoord, 0.0).rgb, vec3(2.2, 2.2, 2.2));');
|
|
}
|
|
else {
|
|
frag.write('basecol = vec3(0.0, 0.0, 0.0);');
|
|
}
|
|
|
|
if (Context.layer.paintNor ||
|
|
emisUsed) {
|
|
frag.add_shared_sampler('sampler2D texpaint_nor');
|
|
frag.write('vec4 texpaint_nor_sample = textureLodShared(texpaint_nor, texCoord, 0.0);');
|
|
|
|
if (emisUsed) {
|
|
frag.write('matid = texpaint_nor_sample.a;');
|
|
}
|
|
|
|
if (Context.layer.paintNor) {
|
|
frag.write('vec3 ntex = texpaint_nor_sample.rgb;');
|
|
frag.write('n = ntex * 2.0 - 1.0;');
|
|
frag.write('n.y = -n.y;');
|
|
frag.write('n = normalize(mul(n, TBN));');
|
|
}
|
|
}
|
|
|
|
if (heightUsed ||
|
|
Context.layer.paintOcc ||
|
|
Context.layer.paintRough ||
|
|
Context.layer.paintMet) {
|
|
frag.add_shared_sampler('sampler2D texpaint_pack');
|
|
frag.write('vec4 pack = textureLodShared(texpaint_pack, texCoord, 0.0);');
|
|
}
|
|
|
|
// Height
|
|
if (heightUsed) {
|
|
frag.write('float bump_res_x = dFdx(pack.a) * 32.0;');
|
|
frag.write('float bump_res_y = dFdy(pack.a) * 32.0;');
|
|
frag.write('vec3 va = normalize(vec3(1.0, 0.0, bump_res_x));');
|
|
frag.write('vec3 vb = normalize(vec3(0.0, 1.0, bump_res_y));');
|
|
frag.write('vec3 vc = normalize(vec3(bump_res_x, bump_res_y, 1.0));');
|
|
frag.write('n = normalize(mul(n, mat3(va, vb, vc)));');
|
|
}
|
|
//
|
|
|
|
if (Context.layer.paintOcc) {
|
|
frag.write('occlusion = pack.r;');
|
|
}
|
|
else {
|
|
frag.write('occlusion = 1.0;');
|
|
}
|
|
if (Context.layer.paintRough) {
|
|
frag.write('roughness = pack.g;');
|
|
}
|
|
else {
|
|
frag.write('roughness = 1.0;');
|
|
}
|
|
if (Context.layer.paintMet) {
|
|
frag.write('metallic = pack.b;');
|
|
}
|
|
else {
|
|
frag.write('metallic = 0.0;');
|
|
}
|
|
|
|
var l = Project.layers[0];
|
|
if (l.maskOpacity < 1) {
|
|
frag.write('basecol *= ${l.maskOpacity};');
|
|
frag.write('occlusion *= ${l.maskOpacity};');
|
|
frag.write('roughness *= ${l.maskOpacity};');
|
|
frag.write('metallic *= ${l.maskOpacity};');
|
|
}
|
|
}
|
|
else {
|
|
frag.write('basecol = vec3(0.0, 0.0, 0.0);');
|
|
frag.write('occlusion = 1.0;');
|
|
frag.write('roughness = 1.0;');
|
|
frag.write('metallic = 0.0;');
|
|
frag.write('specular = 1.0;');
|
|
}
|
|
|
|
if (Project.layers.length > 1) {
|
|
frag.write('float factor0;');
|
|
frag.write('vec4 col_tex0;');
|
|
frag.write('vec4 col_nor0;');
|
|
frag.write('vec4 col_pack0;');
|
|
frag.write('vec3 n0;');
|
|
var len = Project.layers.length;
|
|
var start = len - 1;
|
|
var maxLayers = getMaxVisibleLayers();
|
|
var count = 1;
|
|
for (i in 1...len) {
|
|
if (start == 1) break;
|
|
start--;
|
|
var l = Project.layers[len - i];
|
|
if (l.visible) {
|
|
count++;
|
|
if (count >= maxLayers) break;
|
|
}
|
|
}
|
|
for (i in start...len) {
|
|
var l = Project.layers[i];
|
|
if (!l.visible) continue;
|
|
var id = l.id;
|
|
|
|
if (l.objectMask > 0) {
|
|
var uid = Project.paintObjects[l.objectMask - 1].uid;
|
|
frag.add_uniform('int objectId', '_uid');
|
|
frag.write('if ($uid == objectId) {');
|
|
}
|
|
|
|
frag.add_shared_sampler('sampler2D texpaint' + id);
|
|
frag.write('col_tex0 = textureLodShared(texpaint' + id + ', texCoord, 0.0);');
|
|
frag.write('factor0 = col_tex0.a;');
|
|
|
|
if (l.texpaint_mask != null) {
|
|
frag.add_shared_sampler('sampler2D texpaint_mask' + id);
|
|
frag.write('factor0 *= textureLodShared(texpaint_mask' + id + ', texCoord, 0.0).r;');
|
|
}
|
|
if (l.maskOpacity < 1) {
|
|
frag.write('factor0 *= ${l.maskOpacity};');
|
|
}
|
|
|
|
if (Context.layer.paintBase) {
|
|
frag.write('basecol = mix(basecol, pow(col_tex0.rgb, vec3(2.2, 2.2, 2.2)), factor0);');
|
|
}
|
|
|
|
if (emisUsed ||
|
|
Context.layer.paintNor) {
|
|
|
|
frag.add_shared_sampler('sampler2D texpaint_nor' + id);
|
|
frag.write('col_nor0 = textureLodShared(texpaint_nor' + id + ', texCoord, 0.0);');
|
|
|
|
if (emisUsed) {
|
|
frag.write('matid = col_nor0.a;');
|
|
}
|
|
|
|
if (Context.layer.paintNor) {
|
|
frag.write('n0 = col_nor0.rgb * 2.0 - 1.0;');
|
|
frag.write('n0.y = -n0.y;');
|
|
frag.write('n0 = normalize(mul(n0, TBN));');
|
|
frag.write('n = normalize(mix(n, n0, factor0));');
|
|
}
|
|
}
|
|
|
|
if (Context.layer.paintOcc ||
|
|
Context.layer.paintRough ||
|
|
Context.layer.paintMet) {
|
|
frag.add_shared_sampler('sampler2D texpaint_pack' + id);
|
|
frag.write('col_pack0 = textureLodShared(texpaint_pack' + id + ', texCoord, 0.0);');
|
|
|
|
if (Context.layer.paintOcc) {
|
|
frag.write('occlusion = mix(occlusion, col_pack0.r, factor0);');
|
|
}
|
|
if (Context.layer.paintRough) {
|
|
frag.write('roughness = mix(roughness, col_pack0.g, factor0);');
|
|
}
|
|
if (Context.layer.paintMet) {
|
|
frag.write('metallic = mix(metallic, col_pack0.b, factor0);');
|
|
}
|
|
}
|
|
|
|
if (l.objectMask > 0) {
|
|
frag.write('}');
|
|
}
|
|
}
|
|
}
|
|
|
|
if (UITrait.inst.drawTexels) {
|
|
frag.add_uniform('float texpaintSize', '_texpaintSize');
|
|
frag.write('vec2 texel = texCoord * texpaintSize;');
|
|
frag.write('basecol *= max(float(mod(int(texel.x), 2.0) == mod(int(texel.y), 2.0)), 0.9);');
|
|
}
|
|
|
|
if (UITrait.inst.drawWireframe) {
|
|
// GL_NV_fragment_shader_barycentric
|
|
// VK_AMD_shader_explicit_vertex_parameter
|
|
frag.add_uniform('sampler2D texuvmap', '_texuvmap');
|
|
frag.write('basecol *= 1.0 - textureLod(texuvmap, texCoord, 0.0).r;');
|
|
// frag.write('if (basecol == vec3(0,0,0)) discard;');
|
|
}
|
|
|
|
frag.write('n /= (abs(n.x) + abs(n.y) + abs(n.z));');
|
|
frag.write('n.xy = n.z >= 0.0 ? n.xy : octahedronWrap(n.xy);');
|
|
if (UITrait.inst.viewportMode == 0) { // Render
|
|
if (emisUsed) {
|
|
frag.write('if (matid == 1.0) basecol *= 10.0;'); // Boost for bloom
|
|
}
|
|
frag.write('fragColor[0] = vec4(n.xy, roughness, packFloatInt16(metallic, uint(matid)));');
|
|
frag.write('fragColor[1] = vec4(basecol.rgb, packFloat2(occlusion, 1.0));'); // occ/spec
|
|
}
|
|
else if (UITrait.inst.viewportMode == 1) { // Basecol
|
|
frag.write('fragColor[0] = vec4(n.xy, 1.0, packFloatInt16(0.0, uint(0)));');
|
|
frag.write('fragColor[1] = vec4(basecol.rgb, packFloat2(1.0, 1.0));'); // occ/spec
|
|
}
|
|
else if (UITrait.inst.viewportMode == 2) { // Normal Map
|
|
frag.write('fragColor[0] = vec4(n.xy, 1.0, packFloatInt16(0.0, uint(0)));');
|
|
frag.write('fragColor[1] = vec4(ntex.rgb, packFloat2(1.0, 1.0));'); // occ/spec
|
|
}
|
|
else if (UITrait.inst.viewportMode == 3) { // Occ
|
|
frag.write('fragColor[0] = vec4(n.xy, 1.0, packFloatInt16(0.0, uint(0)));');
|
|
frag.write('fragColor[1] = vec4(vec3(occlusion, occlusion, occlusion), packFloat2(1.0, 1.0));'); // occ/spec
|
|
}
|
|
else if (UITrait.inst.viewportMode == 4) { // Rough
|
|
frag.write('fragColor[0] = vec4(n.xy, 1.0, packFloatInt16(0.0, uint(0)));');
|
|
frag.write('fragColor[1] = vec4(vec3(roughness, roughness, roughness), packFloat2(1.0, 1.0));'); // occ/spec
|
|
}
|
|
else if (UITrait.inst.viewportMode == 5) { // Met
|
|
frag.write('fragColor[0] = vec4(n.xy, 1.0, packFloatInt16(0.0, uint(0)));');
|
|
frag.write('fragColor[1] = vec4(vec3(metallic, metallic, metallic), packFloat2(1.0, 1.0));'); // occ/spec
|
|
}
|
|
else if (UITrait.inst.viewportMode == 6) { // Texcoord
|
|
frag.write('fragColor[0] = vec4(n.xy, 1.0, packFloatInt16(0.0, uint(0)));');
|
|
frag.write('fragColor[1] = vec4(texCoord, 0.0, packFloat2(1.0, 1.0));'); // occ/spec
|
|
}
|
|
else if (UITrait.inst.viewportMode == 7) { // Normal
|
|
frag.nAttr = true;
|
|
frag.write('fragColor[0] = vec4(n.xy, 1.0, packFloatInt16(0.0, uint(0)));');
|
|
frag.write('fragColor[1] = vec4(nAttr, packFloat2(1.0, 1.0));'); // occ/spec
|
|
}
|
|
else if (UITrait.inst.viewportMode == 8) { // MaterialID
|
|
frag.write('float sample_matid = textureLodShared(texpaint_nor, texCoord, 0.0).a;');
|
|
frag.write('float matid_r = fract(sin(dot(vec2(sample_matid, sample_matid * 2.0), vec2(12.9898, 78.233))) * 43758.5453);');
|
|
frag.write('float matid_g = fract(sin(dot(vec2(sample_matid * 2.0, sample_matid), vec2(12.9898, 78.233))) * 43758.5453);');
|
|
frag.write('float matid_b = fract(sin(dot(vec2(sample_matid, sample_matid * 4.0), vec2(12.9898, 78.233))) * 43758.5453);');
|
|
frag.write('fragColor[0] = vec4(n.xy, 1.0, packFloatInt16(0.0, uint(0)));');
|
|
frag.write('fragColor[1] = vec4(matid_r, matid_g, matid_b, packFloat2(1.0, 1.0));'); // occ/spec
|
|
}
|
|
else if (UITrait.inst.viewportMode == 9) { // Mask
|
|
frag.write('fragColor[0] = vec4(n.xy, 1.0, packFloatInt16(0.0, uint(0)));');
|
|
frag.write('float sample_mask = 1.0;');
|
|
if (Context.layer.texpaint_mask != null) {
|
|
frag.add_uniform('sampler2D texpaint_mask_view', '_texpaint_mask');
|
|
frag.write('sample_mask = textureLod(texpaint_mask_view, texCoord, 0.0).r;');
|
|
}
|
|
frag.write('fragColor[1] = vec4(sample_mask, sample_mask, sample_mask, packFloat2(1.0, 1.0));'); // occ/spec
|
|
}
|
|
}
|
|
|
|
frag.write('vec2 posa = (wvpposition.xy / wvpposition.w) * 0.5 + 0.5;');
|
|
frag.write('vec2 posb = (prevwvpposition.xy / prevwvpposition.w) * 0.5 + 0.5;');
|
|
frag.write('fragColor[2] = vec4(posa - posb, texCoord.xy);');
|
|
|
|
Cycles.finalize(con_mesh);
|
|
con_mesh.data.shader_from_source = true;
|
|
con_mesh.data.vertex_shader = vert.get();
|
|
con_mesh.data.fragment_shader = frag.get();
|
|
return con_mesh;
|
|
}
|
|
|
|
static function getMaxVisibleLayers():Int {
|
|
#if (kha_direct3d11 || kha_direct3d12)
|
|
// 128 texture slots available
|
|
// 4 textures per layer (3 + 1 mask)
|
|
// 32 layers - base + 31 on top
|
|
return 31;
|
|
#else
|
|
// 32 texture slots available
|
|
return 4; // base + 4 on top
|
|
#end
|
|
}
|
|
|
|
#if rp_voxelao
|
|
public static function make_voxel(data:iron.data.ShaderData.ShaderContext) {
|
|
var structure = new kha.graphics4.VertexStructure();
|
|
structure.add("pos", kha.graphics4.VertexData.Short4Norm);
|
|
structure.add("nor", kha.graphics4.VertexData.Short2Norm);
|
|
structure.add("tex", kha.graphics4.VertexData.Short2Norm);
|
|
|
|
var pipeState = data.pipeState;
|
|
pipeState.inputLayout = [structure];
|
|
data.raw.vertex_elements = [{name: "pos", data: 'short4norm'}, {name: "nor", data: 'short2norm'}, {name: "tex", data: 'short2norm'}];
|
|
|
|
var ds = UITrait.inst.displaceStrength * 0.02;
|
|
pipeState.vertexShader = kha.graphics4.VertexShader.fromSource(
|
|
#if (kha_direct3d11 || kha_direct3d12)
|
|
"uniform float4x4 W;
|
|
uniform float3x3 N;
|
|
Texture2D<float4> texpaint_pack;
|
|
SamplerState _texpaint_pack_sampler;
|
|
struct SPIRV_Cross_Input { float4 pos : TEXCOORD1; float2 nor : TEXCOORD0; float2 tex : TEXCOORD2; };
|
|
struct SPIRV_Cross_Output { float4 svpos : SV_POSITION; };
|
|
SPIRV_Cross_Output main(SPIRV_Cross_Input stage_input) {
|
|
SPIRV_Cross_Output stage_output;
|
|
stage_output.svpos.xyz = mul(float4(stage_input.pos.xyz, 1.0), W).xyz / float3(1, 1, 1);
|
|
float3 wnormal = normalize(mul(float3(stage_input.nor.xy, stage_input.pos.w), N));
|
|
float height = texpaint_pack.SampleLevel(_texpaint_pack_sampler, stage_input.tex, 0.0).a;
|
|
stage_output.svpos.xyz += wnormal * height.xxx * float3(" + ds + "," + ds + "," + ds + ");
|
|
stage_output.svpos.w = 1.0;
|
|
return stage_output;
|
|
}"
|
|
#else
|
|
"#version 450
|
|
in vec4 pos;
|
|
in vec2 nor;
|
|
in vec2 tex;
|
|
out vec3 voxpositionGeom;
|
|
uniform mat4 W;
|
|
uniform mat3 N;
|
|
uniform sampler2D texpaint_pack;
|
|
void main() {
|
|
const vec3 voxelgiHalfExtents = vec3(1.0, 1.0, 1.0);
|
|
voxpositionGeom = vec3(W * vec4(pos.xyz, 1.0)) / voxelgiHalfExtents;
|
|
vec3 wnormal = normalize(N * vec3(nor.xy, pos.w));
|
|
float height = textureLod(texpaint_pack, tex, 0.0).a;
|
|
voxpositionGeom += wnormal * vec3(height) * vec3(" + ds + ");
|
|
}"
|
|
#end
|
|
);
|
|
|
|
pipeState.compile();
|
|
data.raw.constants = [{ name: "W", type: "mat4", link: "_worldMatrix" }, { name: "N", type: "mat3", link: "_normalMatrix" }];
|
|
data.constants = [pipeState.getConstantLocation("W"), pipeState.getConstantLocation("N")];
|
|
data.raw.texture_units = [{name: "texpaint_pack"}, {name: "voxels", is_image: true}];
|
|
data.textureUnits = [pipeState.getTextureUnit("texpaint_pack"), pipeState.getTextureUnit("voxels")];
|
|
}
|
|
#end
|
|
}
|