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