Skip to main content

siglus_cfx_decompiler/
semantic_wgsl.rs

1use crate::disasm::ShaderKind;
2
3#[derive(Debug, Clone, Copy, PartialEq, Eq)]
4enum VertexMode {
5    Base,
6    D3,
7    D3Fog,
8    D3Light,
9}
10
11#[derive(Debug, Clone, Copy, PartialEq, Eq)]
12enum PixelMode {
13    V0,
14    V1Fog,
15    V2Light,
16}
17
18#[derive(Debug, Clone, Copy, Default)]
19struct PixelFlags {
20    tex: bool,
21    mrbd: bool,
22    rgb: bool,
23    tonecurve: bool,
24    mask: bool,
25    mul: bool,
26    screen: bool,
27}
28
29#[derive(Debug, Clone, Copy)]
30struct RegularTechnique {
31    vertex: VertexMode,
32    pixel: PixelMode,
33    flags: PixelFlags,
34}
35
36pub fn rewrite_wgsl_for_stage(technique_name: &str, kind: ShaderKind) -> Option<String> {
37    if let Some(t) = parse_regular_technique(technique_name) {
38        return match kind {
39            ShaderKind::Vertex => Some(regular_vertex_wgsl(t)),
40            ShaderKind::Pixel => Some(regular_fragment_wgsl(t)),
41        };
42    }
43
44    if kind == ShaderKind::Pixel {
45        return special_fragment_wgsl(technique_name);
46    }
47
48    None
49}
50
51fn parse_regular_technique(name: &str) -> Option<RegularTechnique> {
52    let rest = name.strip_prefix("tec_v")?;
53    let (vpart, ppart) = rest.split_once("_p_")?;
54    let vertex = match vpart {
55        "" => VertexMode::Base,
56        "_d3" => VertexMode::D3,
57        "_d3_fog" => VertexMode::D3Fog,
58        "_d3_light" => VertexMode::D3Light,
59        _ => return None,
60    };
61
62    let pixel = if ppart.starts_with("v0") {
63        PixelMode::V0
64    } else if ppart.starts_with("v1") {
65        PixelMode::V1Fog
66    } else if ppart.starts_with("v2") {
67        PixelMode::V2Light
68    } else {
69        return None;
70    };
71
72    let flags = PixelFlags {
73        tex: has_flag(ppart, "tex"),
74        mrbd: has_flag(ppart, "mrbd"),
75        rgb: has_flag(ppart, "rgb"),
76        tonecurve: has_flag(ppart, "tonecurve"),
77        mask: has_flag(ppart, "mask"),
78        mul: has_flag(ppart, "mul"),
79        screen: has_flag(ppart, "screen"),
80    };
81
82    Some(RegularTechnique { vertex, pixel, flags })
83}
84
85fn has_flag(s: &str, flag: &str) -> bool {
86    s.split('_').any(|part| part == flag)
87}
88
89fn regular_vertex_wgsl(t: RegularTechnique) -> String {
90    let needs_d3 = !matches!(t.vertex, VertexMode::Base);
91    let needs_normal = matches!(t.vertex, VertexMode::D3Light);
92    let needs_mask_uv = t.flags.mask;
93
94    let mut out = String::new();
95    if needs_d3 {
96        push_uniforms(&mut out);
97    }
98
99    out.push_str("struct VSInput {\n");
100    out.push_str("    @location(0) position: vec4<f32>,\n");
101    if needs_normal {
102        out.push_str("    @location(1) normal: vec3<f32>,\n");
103    }
104    out.push_str("    @location(2) diffuse: vec4<f32>,\n");
105    out.push_str("    @location(4) texture_uv: vec2<f32>,\n");
106    if needs_mask_uv {
107        out.push_str("    @location(5) mask_uv: vec2<f32>,\n");
108    }
109    out.push_str("};\n\n");
110
111    out.push_str("struct VSOutput {\n");
112    out.push_str("    @builtin(position) position: vec4<f32>,\n");
113    out.push_str("    @location(2) diffuse: vec4<f32>,\n");
114    out.push_str("    @location(4) texture_uv: vec2<f32>,\n");
115    match t.vertex {
116        VertexMode::Base | VertexMode::D3 => {
117            if needs_mask_uv {
118                out.push_str("    @location(5) mask_uv: vec2<f32>,\n");
119            }
120        }
121        VertexMode::D3Fog => {
122            out.push_str("    @location(5) world_pos: vec4<f32>,\n");
123            out.push_str("    @location(6) proj_pos: vec4<f32>,\n");
124        }
125        VertexMode::D3Light => {
126            out.push_str("    @location(5) normal: vec3<f32>,\n");
127            out.push_str("    @location(6) world_pos: vec4<f32>,\n");
128            out.push_str("    @location(7) proj_pos: vec4<f32>,\n");
129        }
130    }
131    out.push_str("};\n\n");
132
133    if needs_d3 {
134        out.push_str("fn mul_vec4_mat4(v: vec4<f32>, m: mat4x4<f32>) -> vec4<f32> {\n");
135        out.push_str("    return vec4<f32>(dot(v, m[0]), dot(v, m[1]), dot(v, m[2]), dot(v, m[3]));\n");
136        out.push_str("}\n\n");
137        if needs_normal {
138            out.push_str("fn mul_vec3_mat4(v: vec3<f32>, m: mat4x4<f32>) -> vec3<f32> {\n");
139            out.push_str("    let x = dot(vec4<f32>(v, 0.0), m[0]);\n");
140            out.push_str("    let y = dot(vec4<f32>(v, 0.0), m[1]);\n");
141            out.push_str("    let z = dot(vec4<f32>(v, 0.0), m[2]);\n");
142            out.push_str("    return vec3<f32>(x, y, z);\n");
143            out.push_str("}\n\n");
144        }
145    }
146
147    out.push_str("@vertex\n");
148    out.push_str("fn main(input: VSInput) -> VSOutput {\n");
149    out.push_str("    var output: VSOutput;\n");
150    match t.vertex {
151        VertexMode::Base => {
152            out.push_str("    output.position = input.position;\n");
153            out.push_str("    output.diffuse = input.diffuse;\n");
154        }
155        VertexMode::D3 => {
156            out.push_str("    var position = mul_vec4_mat4(input.position, u.g_mat_world);\n");
157            out.push_str("    position = mul_vec4_mat4(position, u.g_mat_view_proj);\n");
158            out.push_str("    output.position = position;\n");
159            out.push_str("    output.diffuse = input.diffuse * u.g_mtrl_diffuse;\n");
160        }
161        VertexMode::D3Fog => {
162            out.push_str("    var position = mul_vec4_mat4(input.position, u.g_mat_world);\n");
163            out.push_str("    output.world_pos = position;\n");
164            out.push_str("    position = mul_vec4_mat4(position, u.g_mat_view_proj);\n");
165            out.push_str("    output.proj_pos = position;\n");
166            out.push_str("    output.position = position;\n");
167            out.push_str("    output.diffuse = input.diffuse * u.g_mtrl_diffuse;\n");
168        }
169        VertexMode::D3Light => {
170            out.push_str("    var position = mul_vec4_mat4(input.position, u.g_mat_world);\n");
171            out.push_str("    output.world_pos = position;\n");
172            out.push_str("    position = mul_vec4_mat4(position, u.g_mat_view_proj);\n");
173            out.push_str("    output.proj_pos = position;\n");
174            out.push_str("    output.position = position;\n");
175            out.push_str("    output.normal = mul_vec3_mat4(input.normal, u.g_mat_world);\n");
176            out.push_str("    output.diffuse = input.diffuse * u.g_mtrl_diffuse;\n");
177        }
178    }
179    out.push_str("    output.texture_uv = input.texture_uv;\n");
180    if needs_mask_uv {
181        out.push_str("    output.mask_uv = input.mask_uv;\n");
182    }
183    out.push_str("    return output;\n");
184    out.push_str("}\n");
185    out
186}
187
188fn regular_fragment_wgsl(t: RegularTechnique) -> String {
189    let mut out = String::new();
190    push_uniforms(&mut out);
191    push_texture_bindings(&mut out, t.flags.tex, t.flags.mask, t.flags.tonecurve, matches!(t.pixel, PixelMode::V1Fog | PixelMode::V2Light), false, false, false);
192    push_regular_helpers(&mut out, t);
193
194    out.push_str("struct PSInput {\n");
195    out.push_str("    @location(2) diffuse: vec4<f32>,\n");
196    if t.flags.tex {
197        out.push_str("    @location(4) texcoord0: vec2<f32>,\n");
198    }
199    if t.flags.mask {
200        out.push_str("    @location(5) mask_uv: vec2<f32>,\n");
201    }
202    match t.pixel {
203        PixelMode::V0 => {}
204        PixelMode::V1Fog => {
205            out.push_str("    @location(5) world_pos: vec4<f32>,\n");
206            out.push_str("    @location(6) proj_pos: vec4<f32>,\n");
207        }
208        PixelMode::V2Light => {
209            out.push_str("    @location(5) normal: vec3<f32>,\n");
210            out.push_str("    @location(6) world_pos: vec4<f32>,\n");
211            out.push_str("    @location(7) proj_pos: vec4<f32>,\n");
212        }
213    }
214    out.push_str("};\n\n");
215
216    out.push_str("@fragment\n");
217    out.push_str("fn main(input: PSInput) -> @location(0) vec4<f32> {\n");
218    out.push_str("    var color = input.diffuse;\n");
219    if t.flags.tex {
220        out.push_str("    let texture_uv = vec2<f32>(input.texcoord0.x, input.texcoord0.y);\n");
221        out.push_str("    color = color * textureSample(tex00, samp_tex00, texture_uv);\n");
222    }
223    if t.flags.mrbd || t.flags.rgb || t.flags.tonecurve || t.flags.mul || t.flags.screen || !matches!(t.pixel, PixelMode::V0) {
224        out.push_str("    let color_org = color;\n");
225        if matches!(t.pixel, PixelMode::V2Light) {
226            out.push_str("    if (u.g_light_pos.w > 0.5) {\n");
227            out.push_str("        color = calc_light(color, input.world_pos, input.normal);\n");
228            out.push_str("    }\n");
229        }
230        if matches!(t.pixel, PixelMode::V1Fog | PixelMode::V2Light) {
231            out.push_str("    if (u.g_fog_range.x > 0.5) {\n");
232            out.push_str("        color = calc_fog(color, input.world_pos, input.proj_pos);\n");
233            out.push_str("    }\n");
234        }
235        if t.flags.tonecurve || t.flags.mrbd {
236            out.push_str("    let mono_y = dot(vec4<f32>(0.2989, 0.5886, 0.1145, 0.0), color);\n");
237        }
238        if t.flags.tonecurve {
239            out.push_str("    color = tonecurve(color, mono_y);\n");
240        }
241        if t.flags.mrbd {
242            out.push_str("    let reverse = vec4<f32>(1.0) - color;\n");
243            out.push_str("    color = mix(color, reverse, u.c[0].y);\n");
244            out.push_str("    color = mix(color, vec4<f32>(mono_y), u.c[0].x);\n");
245            out.push_str("    color = color + vec4<f32>(u.c[0].z);\n");
246            out.push_str("    color = color - vec4<f32>(u.c[0].w);\n");
247        }
248        if t.flags.rgb {
249            out.push_str("    color = mix(color, u.c[1], u.c[1].w);\n");
250            out.push_str("    color = color + u.c[2];\n");
251        }
252        if t.flags.mul {
253            out.push_str("    color = mix(vec4<f32>(1.0), color, color_org.a);\n");
254        }
255        if t.flags.screen {
256            out.push_str("    color = mix(vec4<f32>(0.0), color, color_org.a);\n");
257        }
258        out.push_str("    color.a = color_org.a;\n");
259    }
260    if t.flags.mask {
261        out.push_str("    let mask_color = textureSample(tex01, samp_tex01, input.mask_uv);\n");
262        out.push_str("    color = color * mask_color;\n");
263    }
264    out.push_str("    return color;\n");
265    out.push_str("}\n");
266    out
267}
268
269fn push_regular_helpers(out: &mut String, t: RegularTechnique) {
270    if t.flags.tonecurve {
271        out.push_str("fn tonecurve(color_in: vec4<f32>, mono_y: f32) -> vec4<f32> {\n");
272        out.push_str("    var color = mix(color_in, vec4<f32>(mono_y), u.c[3].g);\n");
273        out.push_str("    var tonecurve_pos = vec2<f32>(color.r, u.c[3].r);\n");
274        out.push_str("    var tonecurve_color = textureSample(tex02, samp_tex02, tonecurve_pos);\n");
275        out.push_str("    color.r = tonecurve_color.r;\n");
276        out.push_str("    tonecurve_pos = vec2<f32>(color.g, u.c[3].r);\n");
277        out.push_str("    tonecurve_color = textureSample(tex02, samp_tex02, tonecurve_pos);\n");
278        out.push_str("    color.g = tonecurve_color.g;\n");
279        out.push_str("    tonecurve_pos = vec2<f32>(color.b, u.c[3].r);\n");
280        out.push_str("    tonecurve_color = textureSample(tex02, samp_tex02, tonecurve_pos);\n");
281        out.push_str("    color.b = tonecurve_color.b;\n");
282        out.push_str("    return color;\n");
283        out.push_str("}\n\n");
284    }
285    if matches!(t.pixel, PixelMode::V2Light) {
286        push_calc_light(out);
287    }
288    if matches!(t.pixel, PixelMode::V1Fog | PixelMode::V2Light) {
289        push_calc_fog(out);
290    }
291}
292
293fn special_fragment_wgsl(name: &str) -> Option<String> {
294    match name {
295        "tec_tex2_mask" => Some(wgsl_tex2_mask()),
296        "tec_tex1_shimi" => Some(wgsl_tex1_shimi(false)),
297        "tec_tex1_shimi_inv" => Some(wgsl_tex1_shimi(true)),
298        "tec_tex2_raster_h" => Some(wgsl_tex2_raster(true)),
299        "tec_tex2_raster_v" => Some(wgsl_tex2_raster(false)),
300        "tec_tex1_raster_h" => Some(wgsl_tex1_raster(true)),
301        "tec_tex1_raster_v" => Some(wgsl_tex1_raster(false)),
302        "tec_tex2_explosion_blur" => Some(wgsl_tex2_explosion_blur()),
303        "tec_tex1_explosion_blur" => Some(wgsl_tex1_explosion_blur()),
304        "tec_tex1_mosaic" => Some(wgsl_tex1_mosaic()),
305        _ => None,
306    }
307}
308
309fn push_uniforms(out: &mut String) {
310    out.push_str("struct SiglusUniforms {\n");
311    out.push_str("    g_mat_world: mat4x4<f32>,\n");
312    out.push_str("    g_mat_view_proj: mat4x4<f32>,\n");
313    out.push_str("    g_mtrl_diffuse: vec4<f32>,\n");
314    out.push_str("    g_camera_pos: vec4<f32>,\n");
315    out.push_str("    g_camera_dir: vec4<f32>,\n");
316    out.push_str("    g_light_pos: vec4<f32>,\n");
317    out.push_str("    g_light_ambient: vec4<f32>,\n");
318    out.push_str("    g_fog_param: vec4<f32>,\n");
319    out.push_str("    g_fog_range: vec4<f32>,\n");
320    out.push_str("    c: array<vec4<f32>, 4>,\n");
321    out.push_str("};\n");
322    out.push_str("@group(0) @binding(0) var<uniform> u: SiglusUniforms;\n\n");
323}
324
325fn push_texture_bindings(out: &mut String, tex00: bool, tex01: bool, tex02: bool, tex03: bool, tex00_point: bool, _tex01_point: bool, _tex02_point: bool) {
326    if tex00 || tex00_point {
327        out.push_str("@group(1) @binding(0) var tex00: texture_2d<f32>;\n");
328    }
329    if tex00 {
330        out.push_str("@group(1) @binding(1) var samp_tex00: sampler;\n");
331    }
332    if tex01 {
333        out.push_str("@group(1) @binding(2) var tex01: texture_2d<f32>;\n");
334        out.push_str("@group(1) @binding(3) var samp_tex01: sampler;\n");
335    }
336    if tex02 {
337        out.push_str("@group(1) @binding(4) var tex02: texture_2d<f32>;\n");
338        out.push_str("@group(1) @binding(5) var samp_tex02: sampler;\n");
339    }
340    if tex03 {
341        out.push_str("@group(1) @binding(6) var tex03: texture_2d<f32>;\n");
342        out.push_str("@group(1) @binding(7) var samp_tex03: sampler;\n");
343    }
344    if tex00_point {
345        out.push_str("@group(1) @binding(8) var samp_tex00_point: sampler;\n");
346    }
347    if tex00 || tex01 || tex02 || tex03 || tex00_point {
348        out.push_str("\n");
349    }
350}
351
352fn push_ps_input_texcoord(out: &mut String) {
353    out.push_str("struct PSInput {\n");
354    out.push_str("    @location(2) diffuse: vec4<f32>,\n");
355    out.push_str("    @location(4) texcoord0: vec2<f32>,\n");
356    out.push_str("};\n\n");
357}
358
359fn push_calc_light(out: &mut String) {
360    out.push_str("fn calc_light(color_in: vec4<f32>, world_pos: vec4<f32>, normal: vec3<f32>) -> vec4<f32> {\n");
361    out.push_str("    var color = color_in;\n");
362    out.push_str("    let dir_point = u.g_light_pos.xyz - world_pos.xyz;\n");
363    out.push_str("    let distance_point = length(dir_point);\n");
364    out.push_str("    var light_power = dot(normalize(normal), normalize(dir_point));\n");
365    out.push_str("    light_power = light_power * (1.0 - distance_point / 2000.0);\n");
366    out.push_str("    light_power = clamp(light_power, 0.0, 1.0);\n");
367    out.push_str("    color = color * vec4<f32>(light_power);\n");
368    out.push_str("    color = color * u.g_light_ambient;\n");
369    out.push_str("    return color;\n");
370    out.push_str("}\n\n");
371}
372
373fn push_calc_fog(out: &mut String) {
374    out.push_str("fn calc_fog(color_in: vec4<f32>, world_pos: vec4<f32>, proj_pos: vec4<f32>) -> vec4<f32> {\n");
375    out.push_str("    var color = color_in;\n");
376    out.push_str("    let camera_dir = u.g_camera_pos.xyz - world_pos.xyz;\n");
377    out.push_str("    let camera_distance = length(camera_dir);\n");
378    out.push_str("    let fog_uv = vec2<f32>((proj_pos.x / proj_pos.w + 1.0) / 2.0 * u.g_fog_param.z + u.g_fog_param.x, 1.0 - (proj_pos.y / proj_pos.w + 1.0) / 2.0) * u.g_fog_param.w + vec2<f32>(u.g_fog_param.y);\n");
379    out.push_str("    let fog_color = textureSample(tex03, samp_tex03, fog_uv);\n");
380    out.push_str("    var fog_rate = (1.0 - 0.0) / (u.g_fog_range.z - u.g_fog_range.y) * (camera_distance - u.g_fog_range.y);\n");
381    out.push_str("    fog_rate = clamp(fog_rate, 0.0, 1.0);\n");
382    out.push_str("    color = vec4<f32>(mix(color.rgb, fog_color.rgb, fog_rate), color.a);\n");
383    out.push_str("    return color;\n");
384    out.push_str("}\n\n");
385}
386
387fn push_brightness(out: &mut String) {
388    out.push_str("fn get_rgb_brightness(rgb: vec4<f32>) -> f32 {\n");
389    out.push_str("    return dot(vec4<f32>(0.2989, 0.5886, 0.1145, 0.0), rgb);\n");
390    out.push_str("}\n\n");
391}
392
393fn push_in_uv_range(out: &mut String) {
394    out.push_str("fn in_uv_range(uv: vec2<f32>) -> bool {\n");
395    out.push_str("    return uv.x >= 0.0 && uv.x <= 1.0 && uv.y >= 0.0 && uv.y <= 1.0;\n");
396    out.push_str("}\n\n");
397}
398
399fn push_log10(out: &mut String) {
400    out.push_str("fn log10_scalar(v: f32) -> f32 {\n");
401    out.push_str("    return log(v) / log(10.0);\n");
402    out.push_str("}\n\n");
403}
404
405fn wgsl_tex2_mask() -> String {
406    let mut out = String::new();
407    push_uniforms(&mut out);
408    push_texture_bindings(&mut out, true, true, false, false, false, false, false);
409    push_ps_input_texcoord2(&mut out);
410    out.push_str("@fragment\n");
411    out.push_str("fn main(input: PSInput) -> @location(0) vec4<f32> {\n");
412    out.push_str("    let tex = textureSample(tex00, samp_tex00, input.texcoord0);\n");
413    out.push_str("    let mask = textureSample(tex01, samp_tex01, input.texcoord1);\n");
414    out.push_str("    let fade = mix(256.0, 2.0, u.c[0].r);\n");
415    out.push_str("    var color = (u.c[0] * fade + mask * (fade - 1.0) - vec4<f32>(fade - 1.0)) * tex;\n");
416    out.push_str("    color = vec4<f32>(tex.r, tex.g, tex.b, color.a);\n");
417    out.push_str("    return color;\n");
418    out.push_str("}\n");
419    out
420}
421
422fn push_ps_input_texcoord2(out: &mut String) {
423    out.push_str("struct PSInput {\n");
424    out.push_str("    @location(2) diffuse: vec4<f32>,\n");
425    out.push_str("    @location(4) texcoord0: vec2<f32>,\n");
426    out.push_str("    @location(5) texcoord1: vec2<f32>,\n");
427    out.push_str("};\n\n");
428}
429
430fn wgsl_tex1_shimi(inv: bool) -> String {
431    let mut out = String::new();
432    push_uniforms(&mut out);
433    push_texture_bindings(&mut out, true, false, false, false, false, false, false);
434    push_brightness(&mut out);
435    push_ps_input_texcoord(&mut out);
436    out.push_str("@fragment\n");
437    out.push_str("fn main(input: PSInput) -> @location(0) vec4<f32> {\n");
438    out.push_str("    let tex = textureSample(tex00, samp_tex00, input.texcoord0);\n");
439    out.push_str("    var color = tex;\n");
440    if inv {
441        out.push_str("    if (get_rgb_brightness(color) < 1.0 - u.c[0].w) {\n");
442    } else {
443        out.push_str("    if (get_rgb_brightness(color) > u.c[0].w) {\n");
444    }
445    out.push_str("        color.a = tex.a * (u.c[0].x - mix(u.c[0].x, 0.0, u.c[0].w));\n");
446    out.push_str("    }\n");
447    out.push_str("    return color;\n");
448    out.push_str("}\n");
449    out
450}
451
452fn wgsl_tex2_raster(horizontal: bool) -> String {
453    let mut out = String::new();
454    push_uniforms(&mut out);
455    push_texture_bindings(&mut out, true, true, false, false, false, false, false);
456    push_in_uv_range(&mut out);
457    push_log10(&mut out);
458    push_ps_input_texcoord(&mut out);
459    out.push_str("@fragment\n");
460    out.push_str("fn main(input: PSInput) -> @location(0) vec4<f32> {\n");
461    out.push_str("    let fraction_num = u.c[0].x;\n");
462    if horizontal {
463        out.push_str("    var tex_coord_for_sin = floor(input.texcoord0.y * fraction_num);\n");
464    } else {
465        out.push_str("    var tex_coord_for_sin = floor(input.texcoord0.x * fraction_num);\n");
466    }
467    out.push_str("    tex_coord_for_sin = tex_coord_for_sin - fraction_num * 0.1;\n");
468    out.push_str("    tex_coord_for_sin = tex_coord_for_sin / fraction_num;\n");
469    out.push_str("    var raster_power = 1.0;\n");
470    out.push_str("    var st_tex_rate = 0.0;\n");
471    out.push_str("    var ed_tex_rate = 0.0;\n");
472    out.push_str("    if (u.c[0].w < 0.5) {\n");
473    out.push_str("        raster_power = 1.0 - mix(1.0, 0.0, u.c[0].w * 2.0);\n");
474    out.push_str("        st_tex_rate = mix(2.0, 0.0, u.c[0].w * 2.0);\n");
475    out.push_str("        if (st_tex_rate > 1.0) { st_tex_rate = 1.0; }\n");
476    out.push_str("    } else {\n");
477    out.push_str("        raster_power = 1.0 - mix(0.0, 2.0, u.c[0].w - 0.5);\n");
478    out.push_str("        ed_tex_rate = mix(0.0, 4.0, u.c[0].w - 0.5);\n");
479    out.push_str("        if (ed_tex_rate > 1.0) { ed_tex_rate = 1.0; }\n");
480    out.push_str("    }\n");
481    out.push_str("    let raster_offset = sin(3.14 * u.c[0].w * u.c[0].z + tex_coord_for_sin * 3.14 * u.c[0].y) * (1.0 - (log10_scalar((1.0 - raster_power) * 100.0) + 1.0) / 3.0);\n");
482    if horizontal {
483        out.push_str("    let raster_uv = vec2<f32>(input.texcoord0.x + raster_offset, input.texcoord0.y);\n");
484    } else {
485        out.push_str("    let raster_uv = vec2<f32>(input.texcoord0.x, input.texcoord0.y + raster_offset);\n");
486    }
487    out.push_str("    var tex0 = textureSample(tex00, samp_tex00, raster_uv);\n");
488    out.push_str("    var tex1 = textureSample(tex01, samp_tex01, raster_uv);\n");
489    out.push_str("    if (!in_uv_range(raster_uv)) {\n");
490    out.push_str("        tex0 = vec4<f32>(0.0);\n");
491    out.push_str("        tex1 = vec4<f32>(0.0);\n");
492    out.push_str("    }\n");
493    out.push_str("    let color = tex1 * st_tex_rate + tex0 * ed_tex_rate;\n");
494    out.push_str("    return color;\n");
495    out.push_str("}\n");
496    out
497}
498
499fn wgsl_tex1_raster(horizontal: bool) -> String {
500    let mut out = String::new();
501    push_uniforms(&mut out);
502    push_texture_bindings(&mut out, true, false, false, false, false, false, false);
503    push_in_uv_range(&mut out);
504    push_log10(&mut out);
505    push_ps_input_texcoord(&mut out);
506    out.push_str("@fragment\n");
507    out.push_str("fn main(input: PSInput) -> @location(0) vec4<f32> {\n");
508    out.push_str("    let fraction_num = u.c[0].x;\n");
509    if horizontal {
510        out.push_str("    var tex_coord_for_sin = floor(input.texcoord0.y * fraction_num);\n");
511    } else {
512        out.push_str("    var tex_coord_for_sin = floor(input.texcoord0.x * fraction_num);\n");
513    }
514    out.push_str("    tex_coord_for_sin = tex_coord_for_sin - fraction_num * 0.1;\n");
515    out.push_str("    tex_coord_for_sin = tex_coord_for_sin / fraction_num;\n");
516    out.push_str("    let raster_power = 1.0 - u.c[0].w;\n");
517    out.push_str("    let tex_rate = u.c[0].w;\n");
518    out.push_str("    let raster_offset = sin(3.14 * u.c[0].w * u.c[0].z + tex_coord_for_sin * 3.14 * u.c[0].y) * (1.0 - (log10_scalar((1.0 - raster_power) * 100.0) + 1.0) / 3.0);\n");
519    if horizontal {
520        out.push_str("    let raster_uv = vec2<f32>(input.texcoord0.x + raster_offset, input.texcoord0.y);\n");
521    } else {
522        out.push_str("    let raster_uv = vec2<f32>(input.texcoord0.x, input.texcoord0.y + raster_offset);\n");
523    }
524    out.push_str("    var tex = textureSample(tex00, samp_tex00, raster_uv);\n");
525    out.push_str("    if (!in_uv_range(raster_uv)) {\n");
526    out.push_str("        tex = vec4<f32>(0.0);\n");
527    out.push_str("    }\n");
528    out.push_str("    var color = tex;\n");
529    out.push_str("    color.a = tex.a * tex_rate;\n");
530    out.push_str("    return color;\n");
531    out.push_str("}\n");
532    out
533}
534
535fn wgsl_tex2_explosion_blur() -> String {
536    let mut out = String::new();
537    push_uniforms(&mut out);
538    push_texture_bindings(&mut out, true, true, false, false, false, false, false);
539    push_ps_input_texcoord(&mut out);
540    out.push_str("@fragment\n");
541    out.push_str("fn main(input: PSInput) -> @location(0) vec4<f32> {\n");
542    out.push_str("    let center_texel = vec2<f32>(u.c[0].z, u.c[0].w);\n");
543    out.push_str("    var dir = center_texel - input.texcoord0;\n");
544    out.push_str("    let len = length(dir);\n");
545    out.push_str("    dir = normalize(dir) * vec2<f32>(u.c[0].x, u.c[0].x);\n");
546    out.push_str("    dir = dir * u.c[1].x * len * u.c[1].y;\n");
547    out.push_str("    let color0 = textureSample(tex01, samp_tex01, input.texcoord0) * 0.40 + textureSample(tex01, samp_tex01, input.texcoord0 + dir * 2.0) * 0.24 + textureSample(tex01, samp_tex01, input.texcoord0 + dir * 4.0) * 0.16 + textureSample(tex01, samp_tex01, input.texcoord0 + dir * 6.0) * 0.14 + textureSample(tex01, samp_tex01, input.texcoord0 + dir * 8.0) * 0.06;\n");
548    out.push_str("    let color1 = textureSample(tex00, samp_tex00, input.texcoord0) * 0.40 + textureSample(tex00, samp_tex00, input.texcoord0 + dir * 2.0) * 0.24 + textureSample(tex00, samp_tex00, input.texcoord0 + dir * 4.0) * 0.16 + textureSample(tex00, samp_tex00, input.texcoord0 + dir * 6.0) * 0.14 + textureSample(tex00, samp_tex00, input.texcoord0 + dir * 8.0) * 0.06;\n");
549    out.push_str("    return input.diffuse.a * color0 + (1.0 - input.diffuse.a) * color1;\n");
550    out.push_str("}\n");
551    out
552}
553
554fn wgsl_tex1_explosion_blur() -> String {
555    let mut out = String::new();
556    push_uniforms(&mut out);
557    push_texture_bindings(&mut out, true, false, false, false, false, false, false);
558    push_ps_input_texcoord(&mut out);
559    out.push_str("@fragment\n");
560    out.push_str("fn main(input: PSInput) -> @location(0) vec4<f32> {\n");
561    out.push_str("    let center_texel = vec2<f32>(u.c[0].z, u.c[0].w);\n");
562    out.push_str("    var dir = center_texel - input.texcoord0;\n");
563    out.push_str("    let len = length(dir);\n");
564    out.push_str("    dir = normalize(dir) * vec2<f32>(u.c[0].x, u.c[0].x);\n");
565    out.push_str("    dir = dir * u.c[1].x * len * u.c[1].y;\n");
566    out.push_str("    var color = textureSample(tex00, samp_tex00, input.texcoord0) * 0.19;\n");
567    out.push_str("    color = color + textureSample(tex00, samp_tex00, input.texcoord0 + dir) * 0.17;\n");
568    out.push_str("    color = color + textureSample(tex00, samp_tex00, input.texcoord0 + dir * 2.0) * 0.15;\n");
569    out.push_str("    color = color + textureSample(tex00, samp_tex00, input.texcoord0 + dir * 3.0) * 0.13;\n");
570    out.push_str("    color = color + textureSample(tex00, samp_tex00, input.texcoord0 + dir * 4.0) * 0.11;\n");
571    out.push_str("    color = color + textureSample(tex00, samp_tex00, input.texcoord0 + dir * 5.0) * 0.09;\n");
572    out.push_str("    color = color + textureSample(tex00, samp_tex00, input.texcoord0 + dir * 6.0) * 0.07;\n");
573    out.push_str("    color = color + textureSample(tex00, samp_tex00, input.texcoord0 + dir * 7.0) * 0.05;\n");
574    out.push_str("    color = color + textureSample(tex00, samp_tex00, input.texcoord0 + dir * 8.0) * 0.03;\n");
575    out.push_str("    color = color + textureSample(tex00, samp_tex00, input.texcoord0 + dir * 9.0) * 0.01;\n");
576    out.push_str("    color.a = input.diffuse.a;\n");
577    out.push_str("    return color;\n");
578    out.push_str("}\n");
579    out
580}
581
582fn wgsl_tex1_mosaic() -> String {
583    let mut out = String::new();
584    push_uniforms(&mut out);
585    push_texture_bindings(&mut out, false, false, false, false, true, false, false);
586    push_ps_input_texcoord(&mut out);
587    out.push_str("@fragment\n");
588    out.push_str("fn main(input: PSInput) -> @location(0) vec4<f32> {\n");
589    out.push_str("    let cut_per_texel_u = u.c[0].x;\n");
590    out.push_str("    let cut_per_texel_u_inv = 1.0 / cut_per_texel_u;\n");
591    out.push_str("    let cut_per_texel_v = u.c[0].x * u.c[0].y;\n");
592    out.push_str("    let cut_per_texel_v_inv = 1.0 / cut_per_texel_v;\n");
593    out.push_str("    let tc = vec2<f32>(floor(cut_per_texel_u_inv * input.texcoord0.x) * cut_per_texel_u, floor(cut_per_texel_v_inv * input.texcoord0.y) * cut_per_texel_v);\n");
594    out.push_str("    var color = textureSample(tex00, samp_tex00_point, tc);\n");
595    out.push_str("    color.a = input.diffuse.a;\n");
596    out.push_str("    return color;\n");
597    out.push_str("}\n");
598    out
599}