MakeMaterial.hx 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500
  1. package arm.node;
  2. import zui.Nodes;
  3. import iron.data.SceneFormat;
  4. import iron.data.ShaderData;
  5. import iron.data.MaterialData;
  6. import iron.RenderPath;
  7. import arm.ui.UIHeader;
  8. import arm.ui.UINodes;
  9. import arm.ui.UISidebar;
  10. import arm.shader.NodeShader;
  11. import arm.shader.NodeShaderContext;
  12. import arm.shader.NodeShaderData;
  13. import arm.shader.ShaderFunctions;
  14. import arm.shader.MaterialParser;
  15. import arm.render.RenderPathPaint;
  16. import arm.util.RenderUtil;
  17. import arm.Enums;
  18. class MakeMaterial {
  19. public static var defaultScon: ShaderContext = null;
  20. public static var defaultMcon: MaterialContext = null;
  21. public static var heightUsed = false;
  22. public static var emisUsed = false;
  23. public static var subsUsed = false;
  24. static function getMOut(): Bool {
  25. for (n in UINodes.inst.getCanvasMaterial().nodes) if (n.type == "OUTPUT_MATERIAL_PBR") return true;
  26. return false;
  27. }
  28. public static function parseMeshMaterial() {
  29. var m = Project.materials[0].data;
  30. for (c in m.shader.contexts) {
  31. if (c.raw.name == "mesh") {
  32. m.shader.raw.contexts.remove(c.raw);
  33. m.shader.contexts.remove(c);
  34. c.delete();
  35. break;
  36. }
  37. }
  38. if (MakeMesh.layerPassCount > 1) {
  39. var i = 0;
  40. while (i < m.shader.contexts.length) {
  41. var c = m.shader.contexts[i];
  42. for (j in 1...MakeMesh.layerPassCount) {
  43. if (c.raw.name == "mesh" + j) {
  44. m.shader.raw.contexts.remove(c.raw);
  45. m.shader.contexts.remove(c);
  46. c.delete();
  47. i--;
  48. break;
  49. }
  50. }
  51. i++;
  52. }
  53. i = 0;
  54. while (i < m.contexts.length) {
  55. var c = m.contexts[i];
  56. for (j in 1...MakeMesh.layerPassCount) {
  57. if (c.raw.name == "mesh" + j) {
  58. m.raw.contexts.remove(c.raw);
  59. m.contexts.remove(c);
  60. i--;
  61. break;
  62. }
  63. }
  64. i++;
  65. }
  66. }
  67. var con = MakeMesh.run(new NodeShaderData({name: "Material", canvas: null}));
  68. var scon = new ShaderContext(con.data, function(scon: ShaderContext){});
  69. scon.overrideContext = {};
  70. if (con.frag.sharedSamplers.length > 0) {
  71. var sampler = con.frag.sharedSamplers[0];
  72. scon.overrideContext.shared_sampler = sampler.substr(sampler.lastIndexOf(" ") + 1);
  73. }
  74. if (!Context.textureFilter) {
  75. scon.overrideContext.filter = "point";
  76. }
  77. m.shader.raw.contexts.push(scon.raw);
  78. m.shader.contexts.push(scon);
  79. for (i in 1...MakeMesh.layerPassCount) {
  80. var con = MakeMesh.run(new NodeShaderData({name: "Material", canvas: null}), i);
  81. var scon = new ShaderContext(con.data, function(scon: ShaderContext){});
  82. scon.overrideContext = {};
  83. if (con.frag.sharedSamplers.length > 0) {
  84. var sampler = con.frag.sharedSamplers[0];
  85. scon.overrideContext.shared_sampler = sampler.substr(sampler.lastIndexOf(" ") + 1);
  86. }
  87. if (!Context.textureFilter) {
  88. scon.overrideContext.filter = "point";
  89. }
  90. m.shader.raw.contexts.push(scon.raw);
  91. m.shader.contexts.push(scon);
  92. var mcon = new MaterialContext({ name: "mesh" + i, bind_textures: [] }, function(self: MaterialContext) {});
  93. m.raw.contexts.push(mcon.raw);
  94. m.contexts.push(mcon);
  95. }
  96. Context.ddirty = 2;
  97. #if rp_voxelao
  98. makeVoxel(m);
  99. #end
  100. #if (kha_direct3d12 || kha_vulkan)
  101. arm.render.RenderPathRaytrace.dirty = 1;
  102. #end
  103. }
  104. public static function parseParticleMaterial() {
  105. var m = Context.particleMaterial;
  106. var sc: ShaderContext = null;
  107. for (c in m.shader.contexts) {
  108. if (c.raw.name == "mesh") {
  109. sc = c;
  110. break;
  111. }
  112. }
  113. if (sc != null) {
  114. m.shader.raw.contexts.remove(sc.raw);
  115. m.shader.contexts.remove(sc);
  116. }
  117. var con = MakeParticle.run(new NodeShaderData({name: "MaterialParticle", canvas: null}));
  118. if (sc != null) sc.delete();
  119. sc = new ShaderContext(con.data, function(sc: ShaderContext){});
  120. m.shader.raw.contexts.push(sc.raw);
  121. m.shader.contexts.push(sc);
  122. }
  123. public static function parseMeshPreviewMaterial() {
  124. if (!getMOut()) return;
  125. var m = Project.materials[0].data;
  126. var scon: ShaderContext = null;
  127. for (c in m.shader.contexts) {
  128. if (c.raw.name == "mesh") {
  129. scon = c;
  130. break;
  131. }
  132. }
  133. m.shader.raw.contexts.remove(scon.raw);
  134. m.shader.contexts.remove(scon);
  135. var mcon: TMaterialContext = { name: "mesh", bind_textures: [] };
  136. var sd = new NodeShaderData({ name: "Material", canvas: null });
  137. var con = MakeMeshPreview.run(sd, mcon);
  138. for (i in 0...m.contexts.length) {
  139. if (m.contexts[i].raw.name == "mesh") {
  140. m.contexts[i] = new MaterialContext(mcon, function(self: MaterialContext) {});
  141. break;
  142. }
  143. }
  144. if (scon != null) scon.delete();
  145. var compileError = false;
  146. scon = new ShaderContext(con.data, function(scon: ShaderContext) {
  147. if (scon == null) compileError = true;
  148. });
  149. if (compileError) return;
  150. m.shader.raw.contexts.push(scon.raw);
  151. m.shader.contexts.push(scon);
  152. }
  153. #if rp_voxelao
  154. static function makeVoxel(m: MaterialData) {
  155. var rebuild = heightUsed;
  156. if (Config.raw.rp_gi != false && rebuild) {
  157. var scon: ShaderContext = null;
  158. for (c in m.shader.contexts) {
  159. if (c.raw.name == "voxel") {
  160. scon = c;
  161. break;
  162. }
  163. }
  164. if (scon != null) MakeVoxel.run(scon);
  165. }
  166. }
  167. #end
  168. public static function parsePaintMaterial(bakePreviews = true) {
  169. if (!getMOut()) return;
  170. if (bakePreviews) {
  171. var current = @:privateAccess kha.graphics2.Graphics.current;
  172. if (current != null) current.end();
  173. bakeNodePreviews();
  174. if (current != null) current.begin(false);
  175. }
  176. var m = Project.materials[0].data;
  177. var scon: ShaderContext = null;
  178. var mcon: MaterialContext = null;
  179. for (c in m.shader.contexts) {
  180. if (c.raw.name == "paint") {
  181. m.shader.raw.contexts.remove(c.raw);
  182. m.shader.contexts.remove(c);
  183. if (c != defaultScon) c.delete();
  184. break;
  185. }
  186. }
  187. for (c in m.contexts) {
  188. if (c.raw.name == "paint") {
  189. m.raw.contexts.remove(c.raw);
  190. m.contexts.remove(c);
  191. break;
  192. }
  193. }
  194. var sdata = new NodeShaderData({ name: "Material", canvas: UINodes.inst.getCanvasMaterial() });
  195. var mcon: TMaterialContext = { name: "paint", bind_textures: [] };
  196. var con = MakePaint.run(sdata, mcon);
  197. var compileError = false;
  198. var scon = new ShaderContext(con.data, function(scon: ShaderContext) {
  199. if (scon == null) compileError = true;
  200. });
  201. if (compileError) return;
  202. scon.overrideContext = {};
  203. scon.overrideContext.addressing = "repeat";
  204. var mcon = new MaterialContext(mcon, function(mcon: MaterialContext) {});
  205. m.shader.raw.contexts.push(scon.raw);
  206. m.shader.contexts.push(scon);
  207. m.raw.contexts.push(mcon.raw);
  208. m.contexts.push(mcon);
  209. if (defaultScon == null) defaultScon = scon;
  210. if (defaultMcon == null) defaultMcon = mcon;
  211. }
  212. static function bakeNodePreviews() {
  213. Context.nodePreviewsUsed = [];
  214. if (Context.nodePreviews == null) Context.nodePreviews = [];
  215. traverseNodes(UINodes.inst.getCanvasMaterial().nodes, null, []);
  216. for (key in Context.nodePreviews.keys()) {
  217. if (Context.nodePreviewsUsed.indexOf(key) == -1) {
  218. var image = Context.nodePreviews.get(key);
  219. App.notifyOnNextFrame(image.unload);
  220. Context.nodePreviews.remove(key);
  221. }
  222. }
  223. }
  224. static function traverseNodes(nodes: Array<TNode>, group: TNodeCanvas, parents: Array<TNode>) {
  225. for (node in nodes) {
  226. bakeNodePreview(node, group, parents);
  227. if (node.type == "GROUP") {
  228. for (g in Project.materialGroups) {
  229. if (g.canvas.name == node.name) {
  230. parents.push(node);
  231. traverseNodes(g.canvas.nodes, g.canvas, parents);
  232. parents.pop();
  233. break;
  234. }
  235. }
  236. }
  237. }
  238. }
  239. static function bakeNodePreview(node: TNode, group: TNodeCanvas, parents: Array<TNode>) {
  240. if (node.type == "BLUR") {
  241. var id = MaterialParser.node_name(node, parents);
  242. var image = Context.nodePreviews.get(id);
  243. Context.nodePreviewsUsed.push(id);
  244. var resX = Std.int(Config.getTextureResX() / 4);
  245. var resY = Std.int(Config.getTextureResY() / 4);
  246. if (image == null || image.width != resX || image.height != resY) {
  247. if (image != null) image.unload();
  248. image = kha.Image.createRenderTarget(resX, resY);
  249. Context.nodePreviews.set(id, image);
  250. }
  251. MaterialParser.blur_passthrough = true;
  252. RenderUtil.makeNodePreview(UINodes.inst.getCanvasMaterial(), node, image, group, parents);
  253. MaterialParser.blur_passthrough = false;
  254. }
  255. else if (node.type == "DIRECT_WARP") {
  256. var id = MaterialParser.node_name(node, parents);
  257. var image = Context.nodePreviews.get(id);
  258. Context.nodePreviewsUsed.push(id);
  259. var resX = Std.int(Config.getTextureResX());
  260. var resY = Std.int(Config.getTextureResY());
  261. if (image == null || image.width != resX || image.height != resY) {
  262. if (image != null) image.unload();
  263. image = kha.Image.createRenderTarget(resX, resY);
  264. Context.nodePreviews.set(id, image);
  265. }
  266. MaterialParser.warp_passthrough = true;
  267. RenderUtil.makeNodePreview(UINodes.inst.getCanvasMaterial(), node, image, group, parents);
  268. MaterialParser.warp_passthrough = false;
  269. }
  270. else if (node.type == "BAKE_CURVATURE") {
  271. var id = MaterialParser.node_name(node, parents);
  272. var image = Context.nodePreviews.get(id);
  273. Context.nodePreviewsUsed.push(id);
  274. var resX = Std.int(Config.getTextureResX());
  275. var resY = Std.int(Config.getTextureResY());
  276. if (image == null || image.width != resX || image.height != resY) {
  277. if (image != null) image.unload();
  278. image = kha.Image.createRenderTarget(resX, resY, kha.graphics4.TextureFormat.L8);
  279. Context.nodePreviews.set(id, image);
  280. }
  281. if (RenderPathPaint.liveLayer == null) {
  282. RenderPathPaint.liveLayer = new arm.data.LayerSlot("_live");
  283. }
  284. var _space = UIHeader.inst.worktab.position;
  285. var _tool = Context.tool;
  286. var _bakeType = Context.bakeType;
  287. UIHeader.inst.worktab.position = SpacePaint;
  288. Context.tool = ToolBake;
  289. Context.bakeType = BakeCurvature;
  290. MaterialParser.bake_passthrough = true;
  291. MaterialParser.start_node = node;
  292. MaterialParser.start_group = group;
  293. MaterialParser.start_parents = parents;
  294. parsePaintMaterial(false);
  295. MaterialParser.bake_passthrough = false;
  296. MaterialParser.start_node = null;
  297. MaterialParser.start_group = null;
  298. MaterialParser.start_parents = null;
  299. Context.pdirty = 1;
  300. RenderPathPaint.useLiveLayer(true);
  301. RenderPathPaint.commandsPaint(false);
  302. RenderPathPaint.dilate(true, false);
  303. RenderPathPaint.useLiveLayer(false);
  304. Context.pdirty = 0;
  305. UIHeader.inst.worktab.position = _space;
  306. Context.tool = _tool;
  307. Context.bakeType = _bakeType;
  308. parsePaintMaterial(false);
  309. var rts = RenderPath.active.renderTargets;
  310. var texpaint_live = rts.get("texpaint_live");
  311. image.g2.begin(false);
  312. image.g2.drawImage(texpaint_live.image, 0, 0);
  313. image.g2.end();
  314. }
  315. }
  316. public static function parseNodePreviewMaterial(node: TNode, group: TNodeCanvas = null, parents: Array<TNode> = null): { scon: ShaderContext, mcon: MaterialContext } {
  317. if (node.outputs.length == 0) return null;
  318. var sdata = new NodeShaderData({ name: "Material", canvas: UINodes.inst.getCanvasMaterial() });
  319. var mcon_raw: TMaterialContext = { name: "mesh", bind_textures: [] };
  320. var con = MakeNodePreview.run(sdata, mcon_raw, node, group, parents);
  321. var compileError = false;
  322. var scon = new ShaderContext(con.data, function(scon: ShaderContext) {
  323. if (scon == null) compileError = true;
  324. });
  325. if (compileError) return null;
  326. var mcon = new MaterialContext(mcon_raw, function(mcon: MaterialContext) {});
  327. return { scon: scon, mcon: mcon };
  328. }
  329. public static function parseBrush() {
  330. Brush.parse(Context.brush.canvas, false);
  331. }
  332. public static function blendMode(frag: NodeShader, blending: Int, cola: String, colb: String, opac: String): String {
  333. if (blending == BlendMix) {
  334. return 'mix($cola, $colb, $opac)';
  335. }
  336. else if (blending == BlendDarken) {
  337. return 'mix($cola, min($cola, $colb), $opac)';
  338. }
  339. else if (blending == BlendMultiply) {
  340. return 'mix($cola, $cola * $colb, $opac)';
  341. }
  342. else if (blending == BlendBurn) {
  343. return 'mix($cola, vec3(1.0, 1.0, 1.0) - (vec3(1.0, 1.0, 1.0) - $cola) / $colb, $opac)';
  344. }
  345. else if (blending == BlendLighten) {
  346. return 'max($cola, $colb * $opac)';
  347. }
  348. else if (blending == BlendScreen) {
  349. return '(vec3(1.0, 1.0, 1.0) - (vec3(1.0 - $opac, 1.0 - $opac, 1.0 - $opac) + $opac * (vec3(1.0, 1.0, 1.0) - $colb)) * (vec3(1.0, 1.0, 1.0) - $cola))';
  350. }
  351. else if (blending == BlendDodge) {
  352. return 'mix($cola, $cola / (vec3(1.0, 1.0, 1.0) - $colb), $opac)';
  353. }
  354. else if (blending == BlendAdd) {
  355. return 'mix($cola, $cola + $colb, $opac)';
  356. }
  357. else if (blending == BlendOverlay) {
  358. return 'mix($cola, vec3(
  359. $cola.r < 0.5 ? 2.0 * $cola.r * $colb.r : 1.0 - 2.0 * (1.0 - $cola.r) * (1.0 - $colb.r),
  360. $cola.g < 0.5 ? 2.0 * $cola.g * $colb.g : 1.0 - 2.0 * (1.0 - $cola.g) * (1.0 - $colb.g),
  361. $cola.b < 0.5 ? 2.0 * $cola.b * $colb.b : 1.0 - 2.0 * (1.0 - $cola.b) * (1.0 - $colb.b)
  362. ), $opac)';
  363. }
  364. else if (blending == BlendSoftLight) {
  365. return '((1.0 - $opac) * $cola + $opac * ((vec3(1.0, 1.0, 1.0) - $cola) * $colb * $cola + $cola * (vec3(1.0, 1.0, 1.0) - (vec3(1.0, 1.0, 1.0) - $colb) * (vec3(1.0, 1.0, 1.0) - $cola))))';
  366. }
  367. else if (blending == BlendLinearLight) {
  368. return '($cola + $opac * (vec3(2.0, 2.0, 2.0) * ($colb - vec3(0.5, 0.5, 0.5))))';
  369. }
  370. else if (blending == BlendDifference) {
  371. return 'mix($cola, abs($cola - $colb), $opac)';
  372. }
  373. else if (blending == BlendSubtract) {
  374. return 'mix($cola, $cola - $colb, $opac)';
  375. }
  376. else if (blending == BlendDivide) {
  377. return 'vec3(1.0 - $opac, 1.0 - $opac, 1.0 - $opac) * $cola + vec3($opac, $opac, $opac) * $cola / $colb';
  378. }
  379. else if (blending == BlendHue) {
  380. frag.add_function(ShaderFunctions.str_hue_sat);
  381. return 'mix($cola, hsv_to_rgb(vec3(rgb_to_hsv($colb).r, rgb_to_hsv($cola).g, rgb_to_hsv($cola).b)), $opac)';
  382. }
  383. else if (blending == BlendSaturation) {
  384. frag.add_function(ShaderFunctions.str_hue_sat);
  385. return 'mix($cola, hsv_to_rgb(vec3(rgb_to_hsv($cola).r, rgb_to_hsv($colb).g, rgb_to_hsv($cola).b)), $opac)';
  386. }
  387. else if (blending == BlendColor) {
  388. frag.add_function(ShaderFunctions.str_hue_sat);
  389. return 'mix($cola, hsv_to_rgb(vec3(rgb_to_hsv($colb).r, rgb_to_hsv($colb).g, rgb_to_hsv($cola).b)), $opac)';
  390. }
  391. else { // BlendValue
  392. frag.add_function(ShaderFunctions.str_hue_sat);
  393. return 'mix($cola, hsv_to_rgb(vec3(rgb_to_hsv($cola).r, rgb_to_hsv($cola).g, rgb_to_hsv($colb).b)), $opac)';
  394. }
  395. }
  396. public static function blendModeMask(frag: NodeShader, blending: Int, cola: String, colb: String, opac: String): String {
  397. if (blending == BlendMix) {
  398. return 'mix($cola, $colb, $opac)';
  399. }
  400. else if (blending == BlendDarken) {
  401. return 'mix($cola, min($cola, $colb), $opac)';
  402. }
  403. else if (blending == BlendMultiply) {
  404. return 'mix($cola, $cola * $colb, $opac)';
  405. }
  406. else if (blending == BlendBurn) {
  407. return 'mix($cola, 1.0 - (1.0 - $cola) / $colb, $opac)';
  408. }
  409. else if (blending == BlendLighten) {
  410. return 'max($cola, $colb * $opac)';
  411. }
  412. else if (blending == BlendScreen) {
  413. return '(1.0 - ((1.0 - $opac) + $opac * (1.0 - $colb)) * (1.0 - $cola))';
  414. }
  415. else if (blending == BlendDodge) {
  416. return 'mix($cola, $cola / (1.0 - $colb), $opac)';
  417. }
  418. else if (blending == BlendAdd) {
  419. return 'mix($cola, $cola + $colb, $opac)';
  420. }
  421. else if (blending == BlendOverlay) {
  422. return 'mix($cola, $cola < 0.5 ? 2.0 * $cola * $colb : 1.0 - 2.0 * (1.0 - $cola) * (1.0 - $colb), $opac)';
  423. }
  424. else if (blending == BlendSoftLight) {
  425. return '((1.0 - $opac) * $cola + $opac * ((1.0 - $cola) * $colb * $cola + $cola * (1.0 - (1.0 - $colb) * (1.0 - $cola))))';
  426. }
  427. else if (blending == BlendLinearLight) {
  428. return '($cola + $opac * (2.0 * ($colb - 0.5)))';
  429. }
  430. else if (blending == BlendDifference) {
  431. return 'mix($cola, abs($cola - $colb), $opac)';
  432. }
  433. else if (blending == BlendSubtract) {
  434. return 'mix($cola, $cola - $colb, $opac)';
  435. }
  436. else if (blending == BlendDivide) {
  437. return '(1.0 - $opac) * $cola + $opac * $cola / $colb';
  438. }
  439. else { // BlendHue, BlendSaturation, BlendColor, BlendValue
  440. return 'mix($cola, $colb, $opac)';
  441. }
  442. }
  443. public static inline function getDisplaceStrength():Float {
  444. var sc = Context.mainObject().transform.scale.x;
  445. return Config.raw.displace_strength * 0.02 * sc;
  446. }
  447. public static inline function voxelgiHalfExtents():String {
  448. var ext = Context.vxaoExt;
  449. return 'const vec3 voxelgiHalfExtents = vec3($ext, $ext, $ext);';
  450. }
  451. }