Files
armorpaint/base/Sources/node_shader.ts
T
luboslenco 9b2e2a44a5 Format
2024-03-21 21:06:41 +01:00

721 lines
20 KiB
TypeScript

type node_shader_t = {
context?: node_shader_context_t;
shader_type?: string;
includes?: string[];
ins?: string[];
outs?: string[];
shared_samplers?: string[];
uniforms?: string[];
functions?: map_t<string, string>;
main?: string;
main_init?: string;
main_end?: string;
main_normal?: string;
main_textures?: string;
main_attribs?: string;
header?: string;
write_pre?: bool;
write_normal?: i32;
write_textures?: i32;
vstruct_as_vsin?: bool;
lock?: bool;
// References
bposition?: bool;
wposition?: bool;
mposition?: bool;
vposition?: bool;
wvpposition?: bool;
ndcpos?: bool;
wtangent?: bool;
vvec?: bool;
vvec_cam?: bool;
n?: bool;
nattr?: bool;
dotnv?: bool;
inv_tbn?: bool;
};
function node_shader_create(context: node_shader_context_t, shader_type: string): node_shader_t {
let raw: node_shader_t = {};
raw.context = context;
raw.shader_type = shader_type;
raw.includes = [];
raw.ins = [];
raw.outs = [];
raw.shared_samplers = [];
raw.uniforms = [];
raw.functions = map_create();
raw.main = '';
raw.main_init = '';
raw.main_end = '';
raw.main_normal = '';
raw.main_textures = '';
raw.main_attribs = '';
raw.header = '';
raw.write_pre = false;
raw.write_normal = 0;
raw.write_textures = 0;
raw.vstruct_as_vsin = true;
return raw;
}
function node_shader_add_include(raw: node_shader_t, s: string) {
array_push(raw.includes, s);
}
function node_shader_add_in(raw: node_shader_t, s: string) {
array_push(raw.ins, s);
}
function node_shader_add_out(raw: node_shader_t, s: string) {
array_push(raw.outs, s);
}
function node_shader_add_uniform(raw: node_shader_t, s: string, link: string = null, included: bool = false) {
let ar: string[] = string_split(s, ' ');
// layout(RGBA8) image3D voxels
let utype: string = ar[ar.length - 2];
let uname: string = ar[ar.length - 1];
if (starts_with(utype, 'sampler') || starts_with(utype, 'image') || starts_with(utype, 'uimage')) {
let is_image: bool = (starts_with(utype, 'image') || starts_with(utype, 'uimage')) ? true : false;
node_shader_context_add_texture_unit(raw.context, utype, uname, link, is_image);
}
else {
// Prefer vec4[] for d3d to avoid padding
if (ar[0] == 'float' && string_index_of(ar[1], '[') >= 0) {
ar[0] = 'floats';
ar[1] = string_split(ar[1], '[')[0];
}
else if (ar[0] == 'vec4' && string_index_of(ar[1], '[') >= 0) {
ar[0] = 'floats';
ar[1] = string_split(ar[1], '[')[0];
}
node_shader_context_add_constant(raw.context, ar[0], ar[1], link);
}
if (included == false && array_index_of(raw.uniforms, s) == -1) {
array_push(raw.uniforms, s);
}
}
function node_shader_add_shared_sampler(raw: node_shader_t, s: string) {
if (array_index_of(raw.shared_samplers, s) == -1) {
array_push(raw.shared_samplers, s);
let ar: string[] = string_split(s, ' ');
// layout(RGBA8) sampler2D tex
let utype: string = ar[ar.length - 2];
let uname: string = ar[ar.length - 1];
node_shader_context_add_texture_unit(raw.context, utype, uname, null, false);
}
}
function node_shader_add_function(raw: node_shader_t, s: string) {
let fname: string = string_split(s, '(')[0];
if (raw.functions.has(fname)) {
return;
}
map_set(raw.functions, fname, s);
}
function node_shader_contains(raw: node_shader_t, s: string): bool {
return string_index_of(raw.main, s) >= 0 ||
string_index_of(raw.main_init, s) >= 0 ||
string_index_of(raw.main_normal, s) >= 0 ||
array_index_of(raw.ins, s) >= 0 ||
string_index_of(raw.main_textures, s) >= 0 ||
string_index_of(raw.main_attribs, s) >= 0;
}
function node_shader_write_init(raw: node_shader_t, s: string) {
raw.main_init = s + '\n' + raw.main_init;
}
function node_shader_write(raw: node_shader_t, s: string) {
if (raw.lock) {
return;
}
if (raw.write_textures > 0) {
raw.main_textures += s + '\n';
}
else if (raw.write_normal > 0) {
raw.main_normal += s + '\n';
}
else if (raw.write_pre) {
raw.main_init += s + '\n';
}
else {
raw.main += s + '\n';
}
}
function node_shader_write_header(raw: node_shader_t, s: string) {
raw.header += s + '\n';
}
function node_shader_write_end(raw: node_shader_t, s: string) {
raw.main_end += s + '\n';
}
function node_shader_write_attrib(raw: node_shader_t, s: string) {
raw.main_attribs += s + '\n';
}
function node_shader_data_size(raw: node_shader_t, data: string): string {
if (data == 'float1') {
return '1';
}
else if (data == 'float2') {
return '2';
}
else if (data == 'float3') {
return '3';
}
else if (data == 'float4') {
return '4';
}
else if (data == 'short2norm') {
return '2';
}
else if (data == 'short4norm') {
return '4';
}
else {
return '1';
}
}
function node_shader_vstruct_to_vsin(raw: node_shader_t) {
// if self.shader_type != 'vert' or self.ins != [] or not self.vstruct_as_vsin: # Vertex structure as vertex shader input
// return
let vs: vertex_element_t[] = raw.context.data.vertex_elements;
for (let i: i32 = 0; i < vs.length; ++i) {
let e: vertex_element_t = vs[i];
node_shader_add_in(raw, 'vec' + node_shader_data_size(raw, e.data) + ' ' + e.name);
}
}
///if (krom_direct3d11 || krom_direct3d12)
function node_shader_get_hlsl(raw: node_shader_t, shared_sampler: string): string {
let s: string = '#define HLSL\n';
s += '#define textureArg(tex) Texture2D tex,SamplerState tex ## _sampler\n';
s += '#define texturePass(tex) tex,tex ## _sampler\n';
s += '#define sampler2D Texture2D\n';
s += '#define sampler3D Texture3D\n';
s += '#define texture(tex, coord) tex.Sample(tex ## _sampler, coord)\n';
s += `#define textureShared(tex, coord) tex.Sample(${shared_sampler}, coord)\n`;
s += '#define textureLod(tex, coord, lod) tex.SampleLevel(tex ## _sampler, coord, lod)\n';
s += `#define textureLodShared(tex, coord, lod) tex.SampleLevel(${shared_sampler}, coord, lod)\n`;
s += '#define texelFetch(tex, coord, lod) tex.Load(float3(coord.xy, lod))\n';
s += 'uint2 _GetDimensions(Texture2D tex, uint lod) { uint x, y; tex.GetDimensions(x, y); return uint2(x, y); }\n';
s += '#define textureSize _GetDimensions\n';
s += '#define mod(a, b) (a % b)\n';
s += '#define vec2 float2\n';
s += '#define vec3 float3\n';
s += '#define vec4 float4\n';
s += '#define ivec2 int2\n';
s += '#define ivec3 int3\n';
s += '#define ivec4 int4\n';
s += '#define mat2 float2x2\n';
s += '#define mat3 float3x3\n';
s += '#define mat4 float4x4\n';
s += '#define dFdx ddx\n';
s += '#define dFdy ddy\n';
s += '#define inversesqrt rsqrt\n';
s += '#define fract frac\n';
s += '#define mix lerp\n';
// s += '#define fma mad\n';
s += raw.header;
let in_ext: string = '';
let out_ext: string = '';
for (let i: i32 = 0; i < raw.includes.length; ++i) {
let a: string = raw.includes[i];
s += '#include "' + a + '"\n';
}
// Input structure
let index: i32 = 0;
if (raw.ins.length > 0) {
s += 'struct SPIRV_Cross_Input {\n';
index = 0;
array_sort(raw.ins, function (a, b): i32 {
// Sort inputs by name
return substring(a, 4, a.length) >= substring(b, 4, b.length) ? 1 : -1;
});
for (let i: i32 = 0; i < raw.ins.length; ++i) {
let a: string = raw.ins[i];
s += `${a}${in_ext} : TEXCOORD${index};\n`;
index++;
}
// Built-ins
if (raw.shader_type == 'vert' && string_index_of(raw.main, "gl_VertexID") >= 0) {
s += 'uint gl_VertexID : SV_VertexID;\n';
array_push(raw.ins, 'uint gl_VertexID');
}
if (raw.shader_type == 'vert' && string_index_of(raw.main, "gl_InstanceID") >= 0) {
s += 'uint gl_InstanceID : SV_InstanceID;\n';
array_push(raw.ins, 'uint gl_InstanceID');
}
s += '};\n';
}
// Output structure
let num: i32 = 0;
if (raw.outs.length > 0 || raw.shader_type == 'vert') {
s += 'struct SPIRV_Cross_Output {\n';
array_sort(raw.outs, function (a, b): i32 {
// Sort outputs by name
return substring(a, 4, a.length) >= substring(b, 4, b.length) ? 1 : -1;
});
index = 0;
if (raw.shader_type == 'vert') {
for (let i: i32 = 0; i < raw.outs.length; ++i) {
let a: string = raw.outs[i];
s += `${a}${out_ext} : TEXCOORD${index};\n`;
index++;
}
s += 'float4 svpos : SV_POSITION;\n';
}
else {
let out: string = raw.outs[0];
// Multiple render targets
if (char_at(out, out.length - 1) == ']') {
num = parse_int(char_at(out, out.length - 2));
s += `vec4 fragColor[${num}] : SV_TARGET0;\n`;
}
else {
s += 'vec4 fragColor : SV_TARGET0;\n';
}
}
s += '};\n';
}
for (let i: i32 = 0; i < raw.uniforms.length; ++i) {
let a: string = raw.uniforms[i];
s += 'uniform ' + a + ';\n';
if (starts_with(a, 'sampler')) {
s += 'SamplerState ' + string_split(a, ' ')[1] + '_sampler;\n';
}
}
if (raw.shared_samplers.length > 0) {
for (let i: i32 = 0; o < raw.shared_samplers.length; ++i) {
let a: string = raw.shared_samplers[i];
s += 'uniform ' + a + ';\n';
}
s += `SamplerState ${shared_sampler};\n`;
}
let values: string[] = map_to_array(raw.functions);
for (let i: i32 = 0; i < values.length; ++i) {
let f: string = values[i];
s += f + '\n';
}
// Begin main
if (raw.outs.length > 0 || raw.shader_type == 'vert') {
if (raw.ins.length > 0) {
s += 'SPIRV_Cross_Output main(SPIRV_Cross_Input stage_input) {\n';
}
else {
s += 'SPIRV_Cross_Output main() {\n';
}
}
else {
if (raw.ins.length > 0) {
s += 'void main(SPIRV_Cross_Input stage_input) {\n';
}
else {
s += 'void main() {\n';
}
}
// Declare inputs
for (let i: i32 = 0; i < raw.ins.length; ++i) {
let a: string = raw.ins[i];
let b: string = substring(a, 5, a.length); // Remove type 'vec4 '
s += `${a} = stage_input.${b};\n`;
}
if (raw.shader_type == 'vert') {
s += 'vec4 gl_Position;\n';
for (let i: i32 = 0; i < raw.outs.length; ++i) {
let a: string = raw.outs[i];
s += `${a};\n`;
}
}
else {
if (raw.outs.length > 0) {
if (num > 0) {
s += `vec4 fragColor[${num}];\n`;
}
else {
s += 'vec4 fragColor;\n';
}
}
}
s += raw.main_attribs;
s += raw.main_textures;
s += raw.main_normal;
s += raw.main_init;
s += raw.main;
s += raw.main_end;
// Write output structure
if (raw.outs.length > 0 || raw.shader_type == 'vert') {
s += 'SPIRV_Cross_Output stage_output;\n';
if (raw.shader_type == 'vert') {
s += 'gl_Position.z = (gl_Position.z + gl_Position.w) * 0.5;\n';
s += 'stage_output.svpos = gl_Position;\n';
for (let i: i32 = 0; i < raw.outs.length; ++i) {
let a: string = raw.outs[i];
let b: string = substring(a, 5, a.length); // Remove type 'vec4 '
s += `stage_output.${b} = ${b};\n`;
}
}
else {
if (num > 0) {
for (let i: i32 = 0; i < num; ++i) {
s += `stage_output.fragColor[${i}] = fragColor[${i}];\n`;
}
}
else {
s += 'stage_output.fragColor = fragColor;\n';
}
}
s += 'return stage_output;\n';
}
s += '}\n';
return s;
}
///end
///if krom_metal
function node_shader_get_msl(raw: node_shader_t, shared_sampler: string): string {
let s: string = '#define METAL\n';
s += '#include <metal_stdlib>\n';
s += '#include <simd/simd.h>\n';
s += 'using namespace metal;\n';
s += '#define textureArg(tex) texture2d<float> tex,sampler tex ## _sampler\n';
s += '#define texturePass(tex) tex,tex ## _sampler\n';
s += '#define sampler2D texture2d<float>\n';
s += '#define sampler3D texture3d<float>\n';
s += '#define texture(tex, coord) tex.sample(tex ## _sampler, coord)\n';
s += `#define textureShared(tex, coord) tex.sample(${shared_sampler}, coord)\n`;
s += '#define textureLod(tex, coord, lod) tex.sample(tex ## _sampler, coord, level(lod))\n';
s += `#define textureLodShared(tex, coord, lod) tex.sample(${shared_sampler}, coord, level(lod))\n`;
s += '#define texelFetch(tex, coord, lod) tex.read(uint2(coord), uint(lod))\n';
s += 'float2 _getDimensions(texture2d<float> tex, uint lod) { return float2(tex.get_width(lod), tex.get_height(lod)); }\n';
s += '#define textureSize _getDimensions\n';
s += '#define mod(a, b) fmod(a, b)\n';
s += '#define vec2 float2\n';
s += '#define vec3 float3\n';
s += '#define vec4 float4\n';
s += '#define ivec2 int2\n';
s += '#define ivec3 int3\n';
s += '#define ivec4 int4\n';
s += '#define mat2 float2x2\n';
s += '#define mat3 float3x3\n';
s += '#define mat4 float4x4\n';
s += '#define dFdx dfdx\n';
s += '#define dFdy dfdy\n';
s += '#define inversesqrt rsqrt\n';
s += '#define mul(a, b) b * a\n';
s += '#define discard discard_fragment()\n';
for (let i: i32 = 0; i < raw.includes.length; ++i) {
let a: string = raw.includes[i];
s += '#include "' + a + '"\n';
}
s += raw.header;
// Input structure
let index: i32 = 0;
//if (ins.length > 0) {
s += 'struct main_in {\n';
index = 0;
array_sort(raw.ins, function (a, b): i32 {
// Sort inputs by name
return substring(a, 4, a.length) >= substring(b, 4, b.length) ? 1 : -1;
});
if (raw.shader_type == 'vert') {
for (let i: i32 = 0; i < raw.ins.length; ++i) {
let a: string = raw.ins[i];
s += `${a} [[attribute(${index})]];\n`;
index++;
}
}
else {
for (let i: i32 = 0; i < raw.ins.length; ++i) {
let a: string = raw.ins[i];
s += `${a} [[user(locn${index})]];\n`;
index++;
}
}
s += '};\n';
//}
// Output structure
let num: i32 = 0;
if (raw.outs.length > 0 || raw.shader_type == 'vert') {
s += 'struct main_out {\n';
array_sort(raw.outs, function (a, b): i32 {
// Sort outputs by name
return substring(a, 4, a.length) >= substring(b, 4, b.length) ? 1 : -1;
});
index = 0;
if (raw.shader_type == 'vert') {
for (let i: i32 = 0; i < raw.outs.length; ++i) {
let a: string = raw.outs[i];
s += `${a} [[user(locn${index})]];\n`;
index++;
}
s += 'float4 svpos [[position]];\n';
}
else {
let out: string = raw.outs[0];
// Multiple render targets
if (char_at(out, out.length - 1) == ']') {
num = parse_int(char_at(out, out.length - 2));
for (let i: i32 = 0; i < num; ++i) {
s += `float4 fragColor_${i} [[color(${i})]];\n`;
}
}
else {
s += 'float4 fragColor [[color(0)]];\n';
}
}
s += '};\n';
}
let samplers: string[] = [];
if (raw.uniforms.length > 0) {
s += 'struct main_uniforms {\n';
for (let i: i32 = 0; i < raw.uniforms.length; ++i) {
let a: string = raw.uniforms[i];
if (starts_with(a, 'sampler')) {
array_push(samplers, a);
}
else {
s += a + ';\n';
}
}
s += '};\n';
}
let values: string[] = map_to_array(raw.functions);
for (let i: i32 = 0; i < values.length; ++i) {
let f: string = values[i];
s += f + '\n';
}
// Begin main declaration
s += '#undef texture\n';
s += raw.shader_type == 'vert' ? 'vertex ' : 'fragment ';
s += (raw.outs.length > 0 || raw.shader_type == 'vert') ? 'main_out ' : 'void ';
s += 'my_main(';
//if (ins.length > 0) {
s += 'main_in in [[stage_in]]';
//}
if (raw.uniforms.length > 0) {
let bufi: i32 = raw.shader_type == 'vert' ? 1 : 0;
s += `, constant main_uniforms& uniforms [[buffer(${bufi})]]`;
}
if (samplers.length > 0) {
for (let i: i32 = 0; i < samplers.length; ++i) {
s += `, ${samplers[i]} [[texture(${i})]]`;
s += ', sampler ' + string_split(samplers[i], ' ')[1] + `_sampler [[sampler(${i})]]`;
}
}
if (raw.shared_samplers.length > 0) {
for (let i: i32 = 0; i < raw.shared_samplers.length; ++i) {
let index: i32 = samplers.length + i;
s += `, ${raw.shared_samplers[i]} [[texture(${index})]]`;
}
s += `, sampler ${shared_sampler} [[sampler(${samplers.length})]]`;
}
// Built-ins
if (raw.shader_type == 'vert' && string_index_of(raw.main, "gl_VertexID") >= 0) {
s += ', uint gl_VertexID [[vertex_id]]';
}
if (raw.shader_type == 'vert' && string_index_of(raw.main, "gl_InstanceID") >= 0) {
s += ', uint gl_InstanceID [[instance_id]]';
}
// End main declaration
s += ') {\n';
s += '#define texture(tex, coord) tex.sample(tex ## _sampler, coord)\n';
// Declare inputs
for (let i: i32 = 0; i < raw.ins.length; ++i) {
let a: string = raw.ins[i];
let b: string = substring(a, 5, a.length); // Remove type 'vec4 '
s += `${a} = in.${b};\n`;
}
for (let i: i32 = 0; i < raw.uniforms.length; ++i) {
let a: string = raw.uniforms[i];
if (!starts_with(a, 'sampler')) {
let b: string = string_split(a, " ")[1]; // Remove type 'vec4 '
if (string_index_of(b, "[") >= 0) {
b = substring(b, 0, string_index_of(b, "["));
let type: string = string_split(a, " ")[0];
s += `constant ${type} *${b} = uniforms.${b};\n`;
}
else {
s += `${a} = uniforms.${b};\n`;
}
}
}
if (raw.shader_type == 'vert') {
s += 'vec4 gl_Position;\n';
for (let i: i32 = 0; i < raw.outs.length; ++i) {
let a: string = raw.outs[i];
s += `${a};\n`;
}
}
else {
if (raw.outs.length > 0) {
if (num > 0) {
s += `vec4 fragColor[${num}];\n`;
}
else {
s += 'vec4 fragColor;\n';
}
}
}
s += raw.main_attribs;
s += raw.main_textures;
s += raw.main_normal;
s += raw.main_init;
s += raw.main;
s += raw.main_end;
// Write output structure
if (raw.outs.length > 0 || raw.shader_type == 'vert') {
s += 'main_out out = {};\n';
if (raw.shader_type == 'vert') {
s += 'gl_Position.z = (gl_Position.z + gl_Position.w) * 0.5;\n';
s += 'out.svpos = gl_Position;\n';
for (let i: i32 = 0; i < raw.outs.length; ++i) {
let a: string = raw.outs[i];
let b: string = string_split(a, " ")[1]; // Remove type 'vec4 '
s += `out.${b} = ${b};\n`;
}
}
else {
if (num > 0) {
for (let i: i32 = 0; i < num; ++i) {
s += `out.fragColor_${i} = fragColor[${i}];\n`;
}
}
else {
s += 'out.fragColor = fragColor;\n';
}
}
s += 'return out;\n';
}
s += '}\n';
return s;
}
///end
///if (krom_opengl || krom_vulkan)
function node_shader_get_glsl(raw: node_shader_t, shared_sampler: string, version_header: string): string {
let s: string = version_header;
s += '#define textureArg(tex) sampler2D tex\n';
s += '#define texturePass(tex) tex\n';
s += '#define mul(a, b) b * a\n';
s += '#define textureShared texture\n';
s += '#define textureLodShared textureLod\n';
s += '#define atan2(x, y) atan(y, x)\n';
s += raw.header;
let in_ext: string = '';
let out_ext: string = '';
for (let i: i32 = 0; i < raw.includes.length; ++i) {
let a: string = raw.includes[i];
s += '#include "' + a + '"\n';
}
for (let i: i32 = 0; i < raw.ins.length; ++i) {
let a: string = raw.ins[i];
s += `in ${a}${in_ext};\n`;
}
for (let i: i32 = 0; i < raw.outs.length; ++i) {
let a: string = raw.outs[i];
s += `out ${a}${out_ext};\n`;
}
for (let i: i32 = 0; i < raw.uniforms.length; ++i) {
let a: string = raw.uniforms[i];
s += 'uniform ' + a + ';\n';
}
for (let i: i32 = 0; i < raw.shared_samplers.length; ++i) {
let a: string = raw.shared_samplers[i];
s += 'uniform ' + a + ';\n';
}
let values: string[] = map_to_array(raw.functions);
for (let i: i32 = 0; i < values.length; ++i) {
let f: string = values[i];
s += f + '\n';
}
s += 'void main() {\n';
s += raw.main_attribs;
s += raw.main_textures;
s += raw.main_normal;
s += raw.main_init;
s += raw.main;
s += raw.main_end;
s += '}\n';
return s;
}
///end
function node_shader_get(raw: node_shader_t): string {
if (raw.shader_type == 'vert' && raw.vstruct_as_vsin) {
node_shader_vstruct_to_vsin(raw);
}
let shared_sampler: string = 'shared_sampler';
if (raw.shared_samplers.length > 0) {
shared_sampler = string_split(raw.shared_samplers[0], ' ')[1] + '_sampler';
}
///if (krom_direct3d11 || krom_direct3d12)
let s: string = node_shader_get_hlsl(raw, shared_sampler);
///elseif krom_metal
let s: string = node_shader_get_msl(raw, shared_sampler);
///elseif krom_vulkan
let version_header: string = '#version 450\n';
let s: string = node_shader_get_glsl(raw, shared_sampler, version_header);
///elseif krom_android
let version_header: string = '#version 300 es\n';
if (raw.shader_type == 'frag') {
version_header += 'precision highp float;\n';
version_header += 'precision mediump int;\n';
}
let s: string = node_shader_get_glsl(raw, shared_sampler, version_header);
///elseif krom_opengl
let version_header: string = '#version 330\n';
let s: string = node_shader_get_glsl(raw, shared_sampler, version_header);
///end
return s;
}