MaterialShader.hx 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532
  1. package arm.node;
  2. import zui.Nodes;
  3. import iron.data.SceneFormat;
  4. class MaterialShaderData {
  5. var material:TMaterial;
  6. public function new(material:TMaterial) {
  7. this.material = material;
  8. }
  9. public function add_context(props:Dynamic):MaterialShaderContext {
  10. return new MaterialShaderContext(material, props);
  11. }
  12. }
  13. class MaterialShaderContext {
  14. public var vert:MaterialShader;
  15. public var frag:MaterialShader;
  16. public var geom:MaterialShader;
  17. public var tesc:MaterialShader;
  18. public var tese:MaterialShader;
  19. public var data:TShaderContext;
  20. var material:TMaterial;
  21. var constants:Array<TShaderConstant>;
  22. var tunits:Array<TTextureUnit>;
  23. public function new(material:TMaterial, props:Dynamic) {
  24. this.material = material;
  25. data = {
  26. name: props.name,
  27. depth_write: props.depth_write,
  28. compare_mode: props.compare_mode,
  29. cull_mode: props.cull_mode,
  30. blend_source: props.blend_source,
  31. blend_destination: props.blend_destination,
  32. blend_operation: props.blend_operation,
  33. alpha_blend_source: props.alpha_blend_source,
  34. alpha_blend_destination: props.alpha_blend_destination,
  35. alpha_blend_operation: props.alpha_blend_operation,
  36. fragment_shader: '',
  37. vertex_shader: '',
  38. vertex_elements: Reflect.hasField(props, 'vertex_elements') ? props.vertex_elements : [ {name: "pos", data: 'short4norm'}, {name: "nor", data: 'short2norm'}]
  39. };
  40. if (props.color_writes_red != null)
  41. data.color_writes_red = props.color_writes_red;
  42. if (props.color_writes_green != null)
  43. data.color_writes_green = props.color_writes_green;
  44. if (props.color_writes_blue != null)
  45. data.color_writes_blue = props.color_writes_blue;
  46. if (props.color_writes_alpha != null)
  47. data.color_writes_alpha = props.color_writes_alpha;
  48. tunits = data.texture_units = [];
  49. constants = data.constants = [];
  50. }
  51. public function add_elem(name:String, data_type:String) {
  52. for (e in data.vertex_elements) {
  53. if (e.name == name) return;
  54. }
  55. var elem:TVertexElement = { name: name, data: data_type };
  56. data.vertex_elements.push(elem);
  57. }
  58. public function is_elem(name:String):Bool {
  59. for (elem in data.vertex_elements)
  60. if (elem.name == name)
  61. return true;
  62. return false;
  63. }
  64. public function get_elem(name:String):TVertexElement {
  65. for (elem in data.vertex_elements) {
  66. #if cpp
  67. if (Reflect.field(elem, "name") == name)
  68. #else
  69. if (elem.name == name)
  70. #end {
  71. return elem;
  72. }
  73. }
  74. return null;
  75. }
  76. public function add_constant(ctype:String, name:String, link:String = null) {
  77. for (c in constants)
  78. if (c.name == name)
  79. return;
  80. var c:TShaderConstant = { name: name, type: ctype };
  81. if (link != null)
  82. c.link = link;
  83. constants.push(c);
  84. }
  85. public function add_texture_unit(ctype:String, name:String, link:String = null, is_image = false) {
  86. for (c in tunits)
  87. if (c.name == name)
  88. return;
  89. var c:TTextureUnit = { name: name };
  90. if (link != null)
  91. c.link = link;
  92. if (is_image)
  93. c.is_image = is_image;
  94. tunits.push(c);
  95. }
  96. public function make_vert():MaterialShader {
  97. data.vertex_shader = material.name + '_' + data.name + '.vert';
  98. vert = new MaterialShader(this, 'vert');
  99. return vert;
  100. }
  101. public function make_frag():MaterialShader {
  102. data.fragment_shader = material.name + '_' + data.name + '.frag';
  103. frag = new MaterialShader(this, 'frag');
  104. return frag;
  105. }
  106. }
  107. class MaterialShader {
  108. public var context:MaterialShaderContext;
  109. var shader_type = '';
  110. var includes:Array<String> = [];
  111. public var ins:Array<String> = [];
  112. public var outs:Array<String> = [];
  113. public var sharedSamplers:Array<String> = [];
  114. var uniforms:Array<String> = [];
  115. var functions = new Map<String, String>();
  116. public var main = '';
  117. public var main_init = '';
  118. public var main_end = '';
  119. public var main_normal = '';
  120. public var main_textures = '';
  121. public var main_attribs = '';
  122. var header = '';
  123. public var write_pre = false;
  124. public var write_normal = 0;
  125. public var write_textures = 0;
  126. var vstruct_as_vsin = true;
  127. var lock = false;
  128. // References
  129. public var bposition = false;
  130. public var wposition = false;
  131. public var mposition = false;
  132. public var vposition = false;
  133. public var wvpposition = false;
  134. public var ndcpos = false;
  135. public var wtangent = false;
  136. public var vVec = false;
  137. public var vVecCam = false;
  138. public var n = false;
  139. public var nAttr = false;
  140. public var dotNV = false;
  141. public var invTBN = false;
  142. public function new(context:MaterialShaderContext, shader_type:String) {
  143. this.context = context;
  144. this.shader_type = shader_type;
  145. }
  146. public function add_include(s:String) {
  147. includes.push(s);
  148. }
  149. public function add_in(s:String) {
  150. ins.push(s);
  151. }
  152. public function add_out(s:String) {
  153. outs.push(s);
  154. }
  155. public function add_uniform(s:String, link:String = null, included = false) {
  156. var ar = s.split(' ');
  157. // layout(RGBA8) image3D voxels
  158. var utype = ar[ar.length - 2];
  159. var uname = ar[ar.length - 1];
  160. if (StringTools.startsWith(utype, 'sampler') || StringTools.startsWith(utype, 'image') || StringTools.startsWith(utype, 'uimage')) {
  161. var is_image = (StringTools.startsWith(utype, 'image') || StringTools.startsWith(utype, 'uimage')) ? true : false;
  162. context.add_texture_unit(utype, uname, link, is_image);
  163. }
  164. else {
  165. // Prefer vec4[] for d3d to avoid padding
  166. if (ar[0] == 'float' && ar[1].indexOf('[') >= 0) {
  167. ar[0] = 'floats';
  168. ar[1] = ar[1].split('[')[0];
  169. }
  170. else if (ar[0] == 'vec4' && ar[1].indexOf('[') >= 0) {
  171. ar[0] = 'floats';
  172. ar[1] = ar[1].split('[')[0];
  173. }
  174. context.add_constant(ar[0], ar[1], link);
  175. }
  176. if (included == false && uniforms.indexOf(s) == -1) {
  177. uniforms.push(s);
  178. }
  179. }
  180. public function add_shared_sampler(s:String) {
  181. if (sharedSamplers.indexOf(s) == -1) {
  182. sharedSamplers.push(s);
  183. var ar = s.split(' ');
  184. // layout(RGBA8) sampler2D tex
  185. var utype = ar[ar.length - 2];
  186. var uname = ar[ar.length - 1];
  187. context.add_texture_unit(utype, uname, null, false);
  188. }
  189. }
  190. public function add_function(s:String) {
  191. var fname = s.split('(')[0];
  192. if (functions.exists(fname)) return;
  193. functions.set(fname, s);
  194. }
  195. public function contains(s:String):Bool {
  196. return main.indexOf(s) >= 0 ||
  197. main_init.indexOf(s) >= 0 ||
  198. main_normal.indexOf(s) >= 0 ||
  199. ins.indexOf(s) >= 0 ||
  200. main_textures.indexOf(s) >= 0 ||
  201. main_attribs.indexOf(s) >= 0;
  202. }
  203. public function write_init(s:String) {
  204. main_init = s + '\n' + main_init;
  205. }
  206. public function write(s:String) {
  207. if (lock) return;
  208. if (write_textures > 0) {
  209. main_textures += s + '\n';
  210. }
  211. else if (write_normal > 0) {
  212. main_normal += s + '\n';
  213. }
  214. else if (write_pre) {
  215. main_init += s + '\n';
  216. }
  217. else {
  218. main += s + '\n';
  219. }
  220. }
  221. public function write_header(s:String) {
  222. header += s + '\n';
  223. }
  224. public function write_end(s:String) {
  225. main_end += s + '\n';
  226. }
  227. public function write_attrib(s:String) {
  228. main_attribs += s + '\n';
  229. }
  230. function dataSize(data:String):String {
  231. if (data == 'float1') return '1';
  232. else if (data == 'float2') return '2';
  233. else if (data == 'float3') return '3';
  234. else if (data == 'float4') return '4';
  235. else if (data == 'short2norm') return '2';
  236. else if (data == 'short4norm') return '4';
  237. else return '1';
  238. }
  239. function vstruct_to_vsin() {
  240. // if self.shader_type != 'vert' or self.ins != [] or not self.vstruct_as_vsin: # Vertex structure as vertex shader input
  241. // return
  242. var vs = context.data.vertex_elements;
  243. for (e in vs) {
  244. add_in('vec' + dataSize(e.data) + ' ' + e.name);
  245. }
  246. }
  247. public function get():String {
  248. if (shader_type == 'vert' && vstruct_as_vsin) {
  249. vstruct_to_vsin();
  250. }
  251. var sharedSampler = 'shared_sampler';
  252. if (sharedSamplers.length > 0) {
  253. sharedSampler = sharedSamplers[0].split(' ')[1] + '_sampler';
  254. }
  255. #if (kha_direct3d11 || kha_direct3d12)
  256. var s = '#define HLSL\n';
  257. s += '#define sampler2D Texture2D\n';
  258. s += '#define sampler3D Texture3D\n';
  259. s += '#define texture(tex, coord) tex.Sample(tex ## _sampler, coord)\n';
  260. s += '#define textureShared(tex, coord) tex.Sample($sharedSampler, coord)\n';
  261. s += '#define textureLod(tex, coord, lod) tex.SampleLevel(tex ## _sampler, coord, lod)\n';
  262. s += '#define textureLodShared(tex, coord, lod) tex.SampleLevel($sharedSampler, coord, lod)\n';
  263. s += '#define texelFetch(tex, coord, lod) tex.Load(float3(coord.xy, lod))\n';
  264. s += 'uint2 _GetDimensions(Texture2D tex, uint lod) { uint x, y; tex.GetDimensions(x, y); return uint2(x, y); }\n';
  265. s += '#define textureSize _GetDimensions\n';
  266. s += '#define mod(a, b) (a % b)\n';
  267. s += '#define vec2 float2\n';
  268. s += '#define vec3 float3\n';
  269. s += '#define vec4 float4\n';
  270. s += '#define ivec2 int2\n';
  271. s += '#define ivec3 int3\n';
  272. s += '#define ivec4 int4\n';
  273. s += '#define mat2 float2x2\n';
  274. s += '#define mat3 float3x3\n';
  275. s += '#define mat4 float4x4\n';
  276. s += '#define dFdx ddx\n';
  277. s += '#define dFdy ddy\n';
  278. s += '#define inversesqrt rsqrt\n';
  279. s += '#define fract frac\n';
  280. s += '#define mix lerp\n';
  281. // s += '#define fma mad\n';
  282. // s += '#define atan(x, y) atan2(y, x)\n';
  283. // s += '#define clamp(x, 0.0, 1.0) saturate(x)\n';
  284. s += header;
  285. var in_ext = '';
  286. var out_ext = '';
  287. for (a in includes)
  288. s += '#include "' + a + '"\n';
  289. // Input structure
  290. var index = 0;
  291. if (ins.length > 0) {
  292. s += 'struct SPIRV_Cross_Input {\n';
  293. index = 0;
  294. ins.sort(function(a, b):Int {
  295. // Sort inputs by name
  296. return a.substring(4) >= b.substring(4) ? 1 : -1;
  297. });
  298. for (a in ins) {
  299. s += '$a$in_ext : TEXCOORD$index;\n';
  300. index++;
  301. }
  302. // Built-ins
  303. if (shader_type == 'vert' && main.indexOf("gl_VertexID") >= 0) {
  304. s += 'uint gl_VertexID : SV_VertexID;\n';
  305. ins.push('uint gl_VertexID');
  306. }
  307. if (shader_type == 'vert' && main.indexOf("gl_InstanceID") >= 0) {
  308. s += 'uint gl_InstanceID : SV_InstanceID;\n';
  309. ins.push('uint gl_InstanceID');
  310. }
  311. s += '};\n';
  312. }
  313. // Output structure
  314. var num = 0;
  315. if (outs.length > 0 || shader_type == 'vert') {
  316. s += 'struct SPIRV_Cross_Output {\n';
  317. outs.sort(function(a, b):Int {
  318. // Sort outputs by name
  319. return a.substring(4) >= b.substring(4) ? 1 : -1;
  320. });
  321. index = 0;
  322. if (shader_type == 'vert') {
  323. for (a in outs) {
  324. s += '$a$out_ext : TEXCOORD$index;\n';
  325. index++;
  326. }
  327. s += 'float4 svpos : SV_POSITION;\n';
  328. }
  329. else {
  330. var out = outs[0];
  331. // Multiple render targets
  332. if (out.charAt(out.length - 1) == ']') {
  333. num = Std.parseInt(out.charAt(out.length - 2));
  334. s += 'vec4 fragColor[$num] : SV_TARGET0;\n';
  335. }
  336. else {
  337. s += 'vec4 fragColor : SV_TARGET0;\n';
  338. }
  339. }
  340. s += '};\n';
  341. }
  342. for (a in uniforms) {
  343. s += 'uniform ' + a + ';\n';
  344. #if (kha_direct3d11 || kha_direct3d12)
  345. if (StringTools.startsWith(a, 'sampler')) {
  346. s += 'SamplerState ' + a.split(' ')[1] + '_sampler;\n';
  347. }
  348. #end
  349. }
  350. if (sharedSamplers.length > 0) {
  351. for (a in sharedSamplers) {
  352. s += 'uniform ' + a + ';\n';
  353. }
  354. #if (kha_direct3d11 || kha_direct3d12)
  355. s += 'SamplerState $sharedSampler;\n';
  356. #end
  357. }
  358. for (f in functions) {
  359. s += f + '\n';
  360. }
  361. // Begin main
  362. if (outs.length > 0 || shader_type == 'vert') {
  363. if (ins.length > 0) {
  364. s += 'SPIRV_Cross_Output main(SPIRV_Cross_Input stage_input) {\n';
  365. }
  366. else {
  367. s += 'SPIRV_Cross_Output main() {\n';
  368. }
  369. }
  370. else {
  371. if (ins.length > 0) {
  372. s += 'void main(SPIRV_Cross_Input stage_input) {\n';
  373. }
  374. else {
  375. s += 'void main() {\n';
  376. }
  377. }
  378. // Declare inputs
  379. for (a in ins) {
  380. var b = a.substring(5); // Remove type 'vec4 '
  381. s += '$a = stage_input.$b;\n';
  382. }
  383. if (shader_type == 'vert') {
  384. s += 'vec4 gl_Position;\n';
  385. for (a in outs) {
  386. s += '$a;\n';
  387. }
  388. }
  389. else {
  390. if (outs.length > 0) {
  391. if (num > 0) s += 'vec4 fragColor[$num];\n';
  392. else s += 'vec4 fragColor;\n';
  393. }
  394. }
  395. s += main_attribs;
  396. s += main_textures;
  397. s += main_normal;
  398. s += main_init;
  399. s += main;
  400. s += main_end;
  401. // Write output structure
  402. if (shader_type == 'vert') {
  403. s += 'SPIRV_Cross_Output stage_output;\n';
  404. s += 'gl_Position.z = (gl_Position.z + gl_Position.w) * 0.5;\n';
  405. s += 'stage_output.svpos = gl_Position;\n';
  406. for (a in outs) {
  407. var b = a.substring(5); // Remove type 'vec4 '
  408. s += 'stage_output.$b = $b;\n';
  409. }
  410. s += 'return stage_output;\n';
  411. }
  412. else {
  413. if (outs.length > 0) {
  414. s += 'SPIRV_Cross_Output stage_output;\n';
  415. if (num > 0) {
  416. for (i in 0...num) {
  417. s += 'stage_output.fragColor[$i] = fragColor[$i];\n';
  418. }
  419. }
  420. else {
  421. s += 'stage_output.fragColor = fragColor;\n';
  422. }
  423. s += 'return stage_output;\n';
  424. }
  425. }
  426. s += '}\n';
  427. #else // kha_opengl
  428. #if kha_webgl
  429. var s = '#version 300 es\n';
  430. if (shader_type == 'frag') {
  431. s += 'precision mediump float;\n';
  432. s += 'precision mediump int;\n';
  433. }
  434. #else
  435. var s = '#version 330\n';
  436. #end
  437. s += '#define mul(a, b) b * a\n';
  438. s += '#define textureShared texture\n';
  439. s += '#define textureLodShared textureLod\n';
  440. s += header;
  441. var in_ext = '';
  442. var out_ext = '';
  443. for (a in includes)
  444. s += '#include "' + a + '"\n';
  445. for (a in ins)
  446. s += 'in $a$in_ext;\n';
  447. for (a in outs)
  448. s += 'out $a$out_ext;\n';
  449. for (a in uniforms)
  450. s += 'uniform ' + a + ';\n';
  451. for (a in sharedSamplers)
  452. s += 'uniform ' + a + ';\n';
  453. for (f in functions)
  454. s += f + '\n';
  455. s += 'void main() {\n';
  456. s += main_attribs;
  457. s += main_textures;
  458. s += main_normal;
  459. s += main_init;
  460. s += main;
  461. s += main_end;
  462. s += '}\n';
  463. #end
  464. return s;
  465. }
  466. }
  467. typedef TMaterial = {
  468. var name:String;
  469. var canvas:TNodeCanvas;
  470. }