MakePaint.hx 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386
  1. package arm.node;
  2. import iron.data.SceneFormat;
  3. import arm.ui.UITrait;
  4. import arm.ui.UINodes;
  5. import arm.node.MaterialShader;
  6. import arm.Tool;
  7. class MakePaint {
  8. public static function run(data: MaterialShaderData, matcon: TMaterialContext): MaterialShaderContext {
  9. var layered = Context.layer != Project.layers[0];
  10. var eraser = Context.tool == ToolEraser;
  11. var context_id = "paint";
  12. var con_paint:MaterialShaderContext = data.add_context({
  13. name: context_id,
  14. depth_write: false,
  15. compare_mode: "always", // TODO: align texcoords winding order
  16. // cull_mode: "counter_clockwise",
  17. cull_mode: "none",
  18. vertex_elements: [{name: "pos", data: "short4norm"}, {name: "nor", data: "short2norm"}, {name: "tex", data: "short2norm"}]
  19. });
  20. con_paint.data.color_writes_red = [true, true, true, true];
  21. con_paint.data.color_writes_green = [true, true, true, true];
  22. con_paint.data.color_writes_blue = [true, true, true, true];
  23. con_paint.data.color_writes_alpha = [true, true, true, true];
  24. var vert = con_paint.make_vert();
  25. var frag = con_paint.make_frag();
  26. frag.ins = vert.outs;
  27. #if kha_direct3d12
  28. if (Context.tool == ToolBake && UITrait.inst.bakeType == BakeInit) {
  29. // Init raytraced bake
  30. MakeBake.positionAndNormal(vert, frag);
  31. con_paint.data.shader_from_source = true;
  32. con_paint.data.vertex_shader = vert.get();
  33. con_paint.data.fragment_shader = frag.get();
  34. return con_paint;
  35. }
  36. #end
  37. if (Context.tool == ToolBake) {
  38. MakeBake.setColorWrites(con_paint);
  39. }
  40. if (Context.tool == ToolColorId || Context.tool == ToolPicker) {
  41. MakeColorIdPicker.run(vert, frag);
  42. con_paint.data.shader_from_source = true;
  43. con_paint.data.vertex_shader = vert.get();
  44. con_paint.data.fragment_shader = frag.get();
  45. return con_paint;
  46. }
  47. var faceFill = Context.tool == ToolFill && UITrait.inst.fillTypeHandle.position == FillFace;
  48. var decal = Context.tool == ToolDecal || Context.tool == ToolText;
  49. if (!faceFill && !decal) { // Fix seams at uv borders
  50. vert.add_uniform('vec2 sub', '_sub');
  51. vert.write('vec2 subtex = tex + sub;');
  52. }
  53. else {
  54. vert.write('vec2 subtex = tex;');
  55. }
  56. #if (kha_direct3d11 || kha_direct3d12)
  57. vert.write('vec2 tpos = vec2(subtex.x * 2.0 - 1.0, (1.0 - subtex.y) * 2.0 - 1.0);');
  58. #else
  59. vert.write('vec2 tpos = vec2(subtex.xy * 2.0 - 1.0);');
  60. #end
  61. vert.write('gl_Position = vec4(tpos, 0.0, 1.0);');
  62. vert.add_uniform('mat4 WVP', '_worldViewProjectionMatrix');
  63. vert.add_out('vec4 ndc');
  64. vert.write_attrib('ndc = mul(vec4(pos.xyz, 1.0), WVP);');
  65. frag.write_attrib('vec3 sp = vec3((ndc.xyz / ndc.w) * 0.5 + 0.5);');
  66. frag.write_attrib('sp.y = 1.0 - sp.y;');
  67. frag.write_attrib('sp.z -= 0.0001;'); // small bias
  68. var uvType = Context.layer.material_mask != null ? Context.layer.uvType : UITrait.inst.brushPaint;
  69. if (uvType == UVProject) frag.ndcpos = true;
  70. frag.add_uniform('vec4 inp', '_inputBrush');
  71. frag.add_uniform('vec4 inplast', '_inputBrushLast');
  72. frag.add_uniform('float aspectRatio', '_aspectRatioWindowF');
  73. frag.write('vec2 bsp = sp.xy * 2.0 - 1.0;');
  74. frag.write('bsp.x *= aspectRatio;');
  75. frag.write('bsp = bsp * 0.5 + 0.5;');
  76. frag.add_uniform('sampler2D gbufferD');
  77. frag.add_out('vec4 fragColor[4]');
  78. frag.add_uniform('float brushRadius', '_brushRadius');
  79. frag.add_uniform('float brushOpacity', '_brushOpacity');
  80. frag.add_uniform('float brushHardness', '_brushHardness');
  81. if (Context.tool == ToolBrush ||
  82. Context.tool == ToolEraser ||
  83. Context.tool == ToolClone ||
  84. Context.tool == ToolBlur ||
  85. Context.tool == ToolParticle ||
  86. decal) {
  87. var depthReject = !UITrait.inst.xray;
  88. if (UITrait.inst.brush3d && !UITrait.inst.brushDepthReject) depthReject = false;
  89. // TODO: sp.z needs to take height channel into account
  90. if (MaterialBuilder.heightUsed) depthReject = false;
  91. if (depthReject) {
  92. #if (kha_opengl || kha_webgl)
  93. frag.write('if (sp.z > textureLod(gbufferD, vec2(sp.x, 1.0 - sp.y), 0.0).r) discard;');
  94. #else
  95. frag.write('if (sp.z > textureLod(gbufferD, sp.xy, 0.0).r) discard;');
  96. #end
  97. }
  98. if (decal || Context.tool == ToolParticle) {
  99. frag.write('float dist = 0.0;');
  100. }
  101. else {
  102. MakeBrush.run(vert, frag);
  103. }
  104. }
  105. else { // Fill, Bake
  106. frag.write('float dist = 0.0;');
  107. var angleFill = Context.tool == ToolFill && UITrait.inst.fillTypeHandle.position == FillAngle;
  108. if (angleFill) {
  109. frag.add_function(MaterialFunctions.str_octahedronWrap);
  110. frag.add_uniform('sampler2D gbuffer0');
  111. frag.write('vec2 g0 = textureLod(gbuffer0, inp.xy, 0.0).rg;');
  112. frag.write('vec3 wn;');
  113. frag.write('wn.z = 1.0 - abs(g0.x) - abs(g0.y);');
  114. frag.write('wn.xy = wn.z >= 0.0 ? g0.xy : octahedronWrap(g0.xy);');
  115. frag.write('wn = normalize(wn);');
  116. frag.n = true;
  117. var angle = UITrait.inst.brushAngleRejectDot;
  118. frag.write('if (dot(wn, n) < $angle) discard;');
  119. }
  120. }
  121. if (UITrait.inst.colorIdPicked) {
  122. MakeDiscard.colorId(vert, frag);
  123. }
  124. else if (faceFill) { // TODO: allow to combine with colorid mask
  125. MakeDiscard.face(vert, frag);
  126. }
  127. if (UITrait.inst.pickerMaskHandle.position == MaskMaterial) {
  128. MakeDiscard.materialId(vert, frag);
  129. }
  130. MakeTexcoord.run(vert, frag);
  131. if (Context.tool == ToolClone || Context.tool == ToolBlur) {
  132. frag.add_uniform('sampler2D gbuffer2');
  133. frag.add_uniform('vec2 gbufferSize', '_gbufferSize');
  134. frag.add_uniform('sampler2D texpaint_undo', '_texpaint_undo');
  135. frag.add_uniform('sampler2D texpaint_nor_undo', '_texpaint_nor_undo');
  136. frag.add_uniform('sampler2D texpaint_pack_undo', '_texpaint_pack_undo');
  137. if (Context.tool == ToolClone) {
  138. MakeClone.run(vert, frag);
  139. }
  140. else { // Blur
  141. MakeBlur.run(vert, frag);
  142. }
  143. }
  144. else {
  145. Material.parse_emission = Context.material.paintEmis;
  146. Material.parse_subsurface = Context.material.paintSubs;
  147. Material.parse_height = Context.material.paintHeight;
  148. Material.parse_height_as_channel = true;
  149. var uvType = Context.layer.material_mask != null ? Context.layer.uvType : UITrait.inst.brushPaint;
  150. Material.triplanar = uvType == UVTriplanar && !decal;
  151. var sout = Material.parse(UINodes.inst.getCanvasMaterial(), con_paint, vert, frag, null, null, null, matcon);
  152. Material.parse_emission = false;
  153. Material.parse_subsurface = false;
  154. Material.parse_height_as_channel = false;
  155. Material.parse_height = false;
  156. var base = sout.out_basecol;
  157. var rough = sout.out_roughness;
  158. var met = sout.out_metallic;
  159. var occ = sout.out_occlusion;
  160. var nortan = Material.out_normaltan;
  161. var height = sout.out_height;
  162. var opac = sout.out_opacity;
  163. var emis = sout.out_emission;
  164. var subs = sout.out_subsurface;
  165. frag.write('vec3 basecol = $base;');
  166. frag.write('float roughness = $rough;');
  167. frag.write('float metallic = $met;');
  168. frag.write('float occlusion = $occ;');
  169. frag.write('vec3 nortan = $nortan;');
  170. frag.write('float height = $height;');
  171. frag.write('float opacity = $opac;');
  172. if (Context.layer.material_mask == null) {
  173. frag.write('opacity *= brushOpacity;');
  174. }
  175. if (Context.material.paintEmis) {
  176. frag.write('float emis = $emis;');
  177. }
  178. if (Context.material.paintSubs) {
  179. frag.write('float subs = $subs;');
  180. }
  181. if (height != "0" && !MaterialBuilder.heightUsed) {
  182. MaterialBuilder.heightUsed = true;
  183. // Height used for the first time, also rebuild vertex shader
  184. return run(data, matcon);
  185. }
  186. if (emis != "0") MaterialBuilder.emisUsed = true;
  187. if (subs != "0") MaterialBuilder.subsUsed = true;
  188. }
  189. if (Context.tool == ToolDecal) {
  190. frag.add_uniform('sampler2D texdecalmask', '_texdecalmask');
  191. frag.write('opacity *= textureLod(texdecalmask, texCoord, 0.0).r;');
  192. }
  193. else if (Context.tool == ToolText) {
  194. frag.add_uniform('sampler2D textexttool', '_textexttool');
  195. frag.write('opacity *= textureLod(textexttool, texCoord, 0.0).r;');
  196. }
  197. if (UITrait.inst.brushMaskImage != null && Context.tool == ToolBrush) {
  198. frag.add_uniform('sampler2D texbrushmask', '_texbrushmask');
  199. frag.write('vec2 binp_mask = inp.xy * 2.0 - 1.0;');
  200. frag.write('binp_mask.x *= aspectRatio;');
  201. frag.write('binp_mask = binp_mask * 0.5 + 0.5;');
  202. frag.write('vec2 pa_mask = bsp.xy - binp_mask.xy;');
  203. frag.write('pa_mask /= brushRadius;');
  204. if (UITrait.inst.brush3d) {
  205. frag.add_uniform('vec3 eye', '_cameraPosition');
  206. frag.write('pa_mask *= distance(eye, winp.xyz) / 1.5;');
  207. }
  208. frag.write('pa_mask = pa_mask.xy * 0.5 + 0.5;');
  209. frag.write('opacity *= textureLod(texbrushmask, pa_mask, 0).r;');
  210. }
  211. if (Context.tool == ToolParticle) { // particle mask
  212. frag.add_uniform('sampler2D texparticle', '_texparticle');
  213. #if (kha_opengl || kha_webgl)
  214. frag.write('float str = textureLod(texparticle, vec2(sp.x, (1.0 - sp.y)), 0).r;');
  215. #else
  216. frag.write('float str = textureLod(texparticle, sp.xy, 0).r;');
  217. #end
  218. }
  219. else { // brush cursor mask
  220. frag.write('float str = clamp((brushRadius - dist) * brushHardness * 400.0, 0.0, 1.0) * opacity;');
  221. }
  222. // Manual blending to preserve memory
  223. frag.wvpposition = true;
  224. frag.write('vec2 sample_tc = vec2(wvpposition.x / wvpposition.w, wvpposition.y / wvpposition.w) * 0.5 + 0.5;');
  225. #if (kha_direct3d11 || kha_direct3d12)
  226. frag.write('sample_tc.y = 1.0 - sample_tc.y;');
  227. #end
  228. frag.add_uniform('sampler2D paintmask');
  229. frag.write('float sample_mask = textureLod(paintmask, sample_tc, 0.0).r;');
  230. frag.write('str = max(str, sample_mask);');
  231. frag.add_uniform('sampler2D texpaint_undo', '_texpaint_undo');
  232. frag.write('vec4 sample_undo = textureLod(texpaint_undo, sample_tc, 0.0);');
  233. var matid = Context.material.id / 255;
  234. if (UITrait.inst.pickerMaskHandle.position == MaskMaterial) {
  235. matid = UITrait.inst.materialIdPicked / 255; // Keep existing material id in place when mask is set
  236. }
  237. frag.write('float matid = $matid;');
  238. // TODO: Use emission/subsurface matid
  239. // matid % 3 == 0 - normal, 1 - emission, 2 - subsurface
  240. if (Context.material.paintSubs) {
  241. frag.write('if (subs > 0) { matid = 254 / 255; }');
  242. }
  243. if (Context.material.paintEmis) {
  244. frag.write('if (emis > 0) { matid = 255 / 255; }');
  245. }
  246. if (layered) {
  247. if (eraser) {
  248. frag.write('fragColor[0] = vec4(mix(sample_undo.rgb, vec3(0.0, 0.0, 0.0), str), sample_undo.a - str);');
  249. frag.write('nortan = vec3(0.5, 0.5, 1.0);');
  250. frag.write('occlusion = 1.0;');
  251. frag.write('roughness = 0.0;');
  252. frag.write('metallic = 0.0;');
  253. frag.write('matid = 0.0;');
  254. }
  255. else if (decal) {
  256. frag.write('fragColor[0] = vec4(mix(sample_undo.rgb, basecol, str), max(str, sample_undo.a));');
  257. }
  258. else {
  259. frag.write('fragColor[0] = vec4(' + MaterialBuilder.blendMode(frag, UITrait.inst.brushBlending, 'sample_undo.rgb', 'basecol', 'opacity') + ', max(str, sample_undo.a));');
  260. }
  261. frag.write('fragColor[1] = vec4(nortan, matid);');
  262. var height = "0.0";
  263. if (Context.material.paintHeight && MaterialBuilder.heightUsed) {
  264. height = "height";
  265. }
  266. if (decal) {
  267. frag.add_uniform('sampler2D texpaint_pack_undo', '_texpaint_pack_undo');
  268. frag.write('vec4 sample_pack_undo = textureLod(texpaint_pack_undo, sample_tc, 0.0);');
  269. frag.write('fragColor[2] = mix(sample_pack_undo, vec4(occlusion, roughness, metallic, $height), str);');
  270. }
  271. else {
  272. frag.write('fragColor[2] = vec4(occlusion, roughness, metallic, $height);');
  273. }
  274. }
  275. else {
  276. if (eraser) {
  277. frag.write('fragColor[0] = vec4(mix(sample_undo.rgb, vec3(${Layers.defaultBase}, ${Layers.defaultBase}, ${Layers.defaultBase}), str), sample_undo.a - str);');
  278. frag.write('fragColor[1] = vec4(0.5, 0.5, 1.0, 0.0);');
  279. frag.write('fragColor[2] = vec4(1.0, ${Layers.defaultRough}, 0.0, 0.0);');
  280. }
  281. else {
  282. frag.add_uniform('sampler2D texpaint_nor_undo', '_texpaint_nor_undo');
  283. frag.add_uniform('sampler2D texpaint_pack_undo', '_texpaint_pack_undo');
  284. frag.write('vec4 sample_nor_undo = textureLod(texpaint_nor_undo, sample_tc, 0.0);');
  285. frag.write('vec4 sample_pack_undo = textureLod(texpaint_pack_undo, sample_tc, 0.0);');
  286. frag.write('fragColor[0] = vec4(' + MaterialBuilder.blendMode(frag, UITrait.inst.brushBlending, 'sample_undo.rgb', 'basecol', 'str') + ', 0.0);');
  287. frag.write('fragColor[1] = vec4(mix(sample_nor_undo.rgb, nortan, str), matid);');
  288. if (Context.material.paintHeight && MaterialBuilder.heightUsed) {
  289. frag.write('fragColor[2] = mix(sample_pack_undo, vec4(occlusion, roughness, metallic, height), str);');
  290. }
  291. else {
  292. frag.write('fragColor[2] = vec4(mix(sample_pack_undo.rgb, vec3(occlusion, roughness, metallic), str), 0.0);');
  293. }
  294. }
  295. }
  296. frag.write('fragColor[3] = vec4(str, 0.0, 0.0, 1.0);');
  297. if (!Context.material.paintBase) {
  298. con_paint.data.color_writes_red[0] = false;
  299. con_paint.data.color_writes_green[0] = false;
  300. con_paint.data.color_writes_blue[0] = false;
  301. }
  302. if (!Context.material.paintNor) {
  303. con_paint.data.color_writes_red[1] = false;
  304. con_paint.data.color_writes_green[1] = false;
  305. con_paint.data.color_writes_blue[1] = false;
  306. }
  307. if (!Context.material.paintOcc) {
  308. con_paint.data.color_writes_red[2] = false;
  309. }
  310. if (!Context.material.paintRough) {
  311. con_paint.data.color_writes_green[2] = false;
  312. }
  313. if (!Context.material.paintMet) {
  314. con_paint.data.color_writes_blue[2] = false;
  315. }
  316. if (!Context.material.paintHeight) {
  317. con_paint.data.color_writes_alpha[2] = false;
  318. }
  319. // Base color only as mask
  320. var isMask = Context.layerIsMask;
  321. if (isMask) {
  322. // TODO: Apply opacity into base
  323. // frag.write('fragColor[0].rgb *= fragColor[0].a;');
  324. con_paint.data.color_writes_red[1] = false;
  325. con_paint.data.color_writes_green[1] = false;
  326. con_paint.data.color_writes_blue[1] = false;
  327. con_paint.data.color_writes_red[2] = false;
  328. con_paint.data.color_writes_green[2] = false;
  329. con_paint.data.color_writes_blue[2] = false;
  330. con_paint.data.color_writes_alpha[2] = false;
  331. }
  332. if (Context.tool == ToolBake) {
  333. MakeBake.run(vert, frag);
  334. }
  335. Material.finalize(con_paint);
  336. Material.triplanar = false;
  337. con_paint.data.shader_from_source = true;
  338. con_paint.data.vertex_shader = vert.get();
  339. con_paint.data.fragment_shader = frag.get();
  340. return con_paint;
  341. }
  342. }