Files
souls/main.odin
Slendi b6850cb0fe use odin's ease
Signed-off-by: Slendi <slendi@socopon.com>
2025-09-28 19:19:18 +03:00

467 lines
10 KiB
Odin

package main
import "core:fmt"
import "core:math"
import "core:math/ease"
import rl "vendor:raylib"
SFX: []u8 : #load("sfx.mp3")
OTF: []u8 : #load("a.otf")
color_mul_rgb :: proc(a, b: rl.Color) -> rl.Color {
return {
u8((f32(a.r) * f32(b.r)) / 255),
u8((f32(a.g) * f32(b.g)) / 255),
u8((f32(a.b) * f32(b.b)) / 255),
255,
}
}
with_alpha :: proc(c: rl.Color, alpha: f32) -> rl.Color {
out := c
out.a = u8(clamp(alpha, 0, 1) * 255)
return out
}
ease_out_back :: proc(t: f32, s: f32 = 1.70158) -> f32 {
t1 := clamp(t, 0, 1)
return 1 + s * math.pow(t1 - 1, 3) + s * (t1 - 1) * math.pow(t1 - 1, 2)
}
adsr :: proc(t, attack, decay, sustain: f32, r_start: f32, r_dur: f32) -> f32 {
if t <= 0 {return 0}
if t < attack {
return ease.cubic_out(t / attack)
}
if t < attack + decay {
return f32(math.lerp(f32(1), sustain, ease.cubic_in_out((t - attack) / decay)))
}
if t < r_start {
return sustain
}
return sustain * (1 - ease.cubic_out((t - r_start) / math.max(r_dur, 0.0001)))
}
draw_noun_verbed_sheen :: proc(
font: rl.Font,
text: string,
center: rl.Vector2,
base_font_size: f32,
spacing: f32,
text_color: rl.Color,
text_opacity: f32,
sheen_tint: rl.Color,
blur_size_: f32,
blur_opacity: f32,
) {
blur_size := blur_size_
screen_h := rl.GetScreenHeight()
s := f32(screen_h) / 1080.0
if blur_size <= 1.0001 {
blur_size = 1.0001
}
steps_f := 20.0 * blur_size * math.pow(s, 0.25)
zoomSteps := i32(math.floor(steps_f))
if zoomSteps < 6 {
zoomSteps = 6
}
voff := s / (blur_size - 1.0)
base_size := rl.MeasureTextEx(font, fmt.ctprintf("{}", text), base_font_size, spacing)
base_origin := rl.Vector2{base_size.x * 0.5, base_size.y * 0.5}
glow_base := color_mul_rgb(text_color, sheen_tint)
rl.BeginBlendMode(.ADDITIVE)
for i := zoomSteps; i >= 0; i -= 1 {
t := f32(i) / f32(zoomSteps)
scale_factor := f32(math.pow(blur_size, t))
denom := f32(math.log2_f32(blur_size) / math.log2_f32(2))
if denom == 0 {
denom = 0.000001
}
fat_product := f32(math.pow(scale_factor, 1.0 / denom))
alpha := blur_opacity / fat_product
if alpha <= 0.001 {
continue
}
fs := base_font_size * scale_factor
sp := spacing * scale_factor
size := rl.MeasureTextEx(font, fmt.ctprintf("{}", text), fs, sp)
origin := rl.Vector2{size.x * 0.5, size.y * 0.5}
y_offset := voff * (scale_factor - 1.0)
rl.DrawTextPro(
font,
fmt.ctprintf("{}", text),
rl.Vector2{center.x, center.y + y_offset},
origin,
0.0,
fs,
sp,
with_alpha(glow_base, alpha),
)
}
rl.EndBlendMode()
rl.DrawTextPro(
font,
fmt.ctprintf("{}", text),
center,
base_origin,
0.0,
base_font_size,
spacing,
with_alpha(text_color, text_opacity),
)
}
draw_shadow_bar :: proc(
center_y: f32,
rel_size: f32,
opacity: f32,
offset: f32,
softness: f32,
tint: rl.Color,
) {
if rel_size <= 0.0 || opacity <= 0.0 {
return
}
w := rl.GetScreenWidth()
hf := f32(rl.GetScreenHeight())
shadow_h := rel_size * 0.25 * hf
center := center_y + offset * hf
top := center - shadow_h * 0.5
c_tint := rl.Color{tint.r, tint.g, tint.b, u8(clamp(opacity, 0, 1) * 255)}
c_clear := rl.Color{tint.r, tint.g, tint.b, 0}
y0 := i32(math.round(top))
total_h := i32(math.round(shadow_h))
if total_h <= 0 {
return
}
y3 := y0 + total_h
fade_h := shadow_h * clamp(softness, 0, 1) * 0.5
fade_i := i32(math.round(fade_h))
if fade_i * 2 > total_h {
fade_i = total_h / 2
}
y1 := y0 + fade_i
y2 := y3 - fade_i
mid_h := y2 - y1
rl.BeginBlendMode(.ALPHA)
if fade_i > 0 {
rl.DrawRectangleGradientV(0, y0, w, y1 - y0, c_clear, c_tint)
}
if mid_h > 0 {
rl.DrawRectangle(0, y1, w, mid_h, c_tint)
}
if fade_i > 0 {
rl.DrawRectangleGradientV(0, y2, w, y3 - y2, c_tint, c_clear)
}
rl.EndBlendMode()
}
draw_sheen_sweep :: proc(
font: rl.Font,
text: string,
center: rl.Vector2,
font_size: f32,
spacing: f32,
tint: rl.Color,
progress: f32,
core_frac: f32,
soft_frac: f32,
core_alpha: f32,
soft_alpha: f32,
) {
size := rl.MeasureTextEx(font, fmt.ctprintf("{}", text), font_size, spacing)
origin := rl.Vector2{size.x * 0.5, size.y * 0.5}
left := center.x - origin.x
top := center.y - origin.y
width := size.x
height := size.y
p := clamp(progress, 0, 1)
x_center := left + width * p
core_w := width * core_frac
soft_w := width * soft_frac
bright := rl.Color{255, 235, 210, 255}
rl.BeginBlendMode(.ADDITIVE)
rl.BeginScissorMode(i32(x_center - soft_w * 0.5), i32(top), i32(soft_w), i32(height))
rl.DrawTextPro(
font,
fmt.ctprintf("{}", text),
center,
origin,
0,
font_size,
spacing,
with_alpha(bright, soft_alpha),
)
rl.EndScissorMode()
rl.BeginScissorMode(i32(x_center - core_w * 0.5), i32(top), i32(core_w), i32(height))
rl.DrawTextPro(
font,
fmt.ctprintf("{}", text),
center,
origin,
0,
font_size,
spacing,
with_alpha(bright, core_alpha),
)
rl.EndScissorMode()
rl.EndBlendMode()
}
main :: proc() {
mon := rl.GetCurrentMonitor()
w, h := rl.GetMonitorWidth(mon), rl.GetMonitorHeight(mon)
rl.SetConfigFlags(
{
.WINDOW_TRANSPARENT,
.WINDOW_RESIZABLE,
.WINDOW_MOUSE_PASSTHROUGH,
.WINDOW_HIGHDPI,
.WINDOW_TOPMOST,
.WINDOW_UNFOCUSED,
.WINDOW_UNDECORATED,
.FULLSCREEN_MODE,
},
)
rl.InitWindow(w, h, "amongus")
rl.InitAudioDevice()
music := rl.LoadMusicStreamFromMemory(".mp3", raw_data(SFX), i32(len(SFX)))
rl.PlayMusicStream(music)
rt := rl.LoadRenderTexture(rl.GetScreenWidth(), rl.GetScreenHeight())
rl.SetTextureFilter(rt.texture, .BILINEAR)
font := rl.LoadFontFromMemory(".otf", raw_data(OTF), i32(len(OTF)), 256, nil, 0)
text := "NIXOS REBUILT"
//base_font_size0 := f32(128)
spacing := f32(2)
text_color := rl.Color{235, 200, 120, 255}
sheen_tint := rl.Color{255, 178, 153, 255}
// base durations
fade_in_dur := f32(0.55)
scale_dur := f32(1.10)
sweep_delay := f32(0.20)
sweep_dur := f32(1.20)
glow_rise_dur := f32(0.70)
post_fade_in_dur := f32(0.25)
post_hold_after_sweep := f32(0.35)
post_fade_out_dur := f32(0.60)
DUR_SCALE :: f32(2.0)
blur_base := f32(1.0)
blur_opacity_base := f32(0.08)
// overlay appears 7s into the mp3
OVERLAY_DELAY :: f32(5.0)
SHEEN_GAIN :: 0.3 // overall intensity (0..1)
SHEEN_W_SCALE :: 0.5 // width scaler (<1 = thinner)
SHEEN_SOFT_ALPHA_CAP :: 0.10 // upper cap for soft halo
SHEEN_CORE_ALPHA_CAP :: 0.28 // upper cap for core
prev_mt := f32(0)
ended := false
len_s := f32(rl.GetMusicTimeLength(music))
running := true
for !rl.WindowShouldClose() && running {
free_all(context.temp_allocator)
if rl.IsWindowResized() {
rl.UnloadRenderTexture(rt)
rt = rl.LoadRenderTexture(rl.GetScreenWidth(), rl.GetScreenHeight())
rl.SetTextureFilter(rt.texture, .BILINEAR)
}
rl.UpdateMusicStream(music)
mt_raw := f32(rl.GetMusicTimePlayed(music))
if !ended {
if mt_raw < prev_mt - 0.02 ||
mt_raw >= len_s - 0.01 ||
(!rl.IsMusicStreamPlaying(music) && mt_raw > 0) {
ended = true
rl.StopMusicStream(music)
}
}
mt := mt_raw
if ended {
mt = len_s
running = false
}
prev_mt = mt_raw
visible := mt >= OVERLAY_DELAY
t := mt - OVERLAY_DELAY
if t < 0 {t = 0}
fade_in := fade_in_dur * DUR_SCALE
scale_len := scale_dur * DUR_SCALE
sweep_dly := sweep_delay * DUR_SCALE
sweep_len := sweep_dur * DUR_SCALE
glow_len := glow_rise_dur * DUR_SCALE
post_in := post_fade_in_dur * DUR_SCALE
post_hold := post_hold_after_sweep * DUR_SCALE
post_out := post_fade_out_dur * DUR_SCALE
alpha := ease.cubic_out(t / fade_in)
scale_bump := 0.06 * ease_out_back(t / scale_len, 1.3)
base_font_size0 := 0.6 * 0.25 * f32(rl.GetScreenHeight())
font_size := base_font_size0 * (1.0 + scale_bump)
glow_in := ease.cubic_out(t / glow_len)
glow_pulse := 0.5 + 0.5 * f32(math.sin(t * 2.0))
sweep_end_time := sweep_dly + sweep_len
fade_out_start := sweep_end_time + post_hold
blur_env := adsr(
t,
0.18 * DUR_SCALE, // attack
0.35 * DUR_SCALE, // decay
0.70, // sustain level
fade_out_start, // release start
post_out, // release dur
)
blur_size := blur_base * f32(math.lerp(f32(1.00), 1.30, ease.cubic_in_out(blur_env)))
blur_opacity := blur_opacity_base
blur_opacity *= (0.60 + 0.40 * blur_env) // grow with env
blur_opacity *= clamp(glow_in * (0.8 + 0.2 * glow_pulse), 0.0, 1.0)
sheen_t := t - sweep_dly
raw_env := adsr(
sheen_t,
0.10 * DUR_SCALE,
0.22 * DUR_SCALE,
0.65,
sweep_len,
0.28 * DUR_SCALE,
)
sheen_env := f32(math.pow(clamp(raw_env, 0, 1), 0.75) * SHEEN_GAIN)
core_frac := f32(math.lerp(f32(0.12), 0.18, sheen_env) * SHEEN_W_SCALE)
soft_frac := f32(math.lerp(f32(0.24), 0.34, sheen_env) * SHEEN_W_SCALE)
core_alpha_dyn := f32(
math.min(math.lerp(f32(0.10), 0.26, sheen_env), SHEEN_CORE_ALPHA_CAP),
)
soft_alpha_dyn := f32(
math.min(math.lerp(f32(0.03), 0.09, sheen_env), SHEEN_SOFT_ALPHA_CAP),
)
center := rl.Vector2{f32(rl.GetScreenWidth()) * 0.5, f32(rl.GetScreenHeight()) * 0.5}
rl.BeginTextureMode(rt)
rl.ClearBackground(rl.BLANK)
if visible {
draw_shadow_bar(center.y, 1.4, 0.95, -0.002, 0.5, rl.BLACK)
draw_noun_verbed_sheen(
font,
text,
center,
font_size,
spacing,
text_color,
alpha,
sheen_tint,
blur_size,
blur_opacity,
)
sweep_p := clamp((t - sweep_dly) / sweep_len, 0, 1)
if sweep_p > 0 && sweep_p <= 1 {
draw_sheen_sweep(
font,
text,
center,
font_size,
spacing,
sheen_tint,
sweep_p,
core_frac,
soft_frac,
core_alpha_dyn,
soft_alpha_dyn,
)
}
}
rl.EndTextureMode()
post_alpha := f32(1.0)
if !visible {
post_alpha = 0
} else {
if t < post_in {
post_alpha = ease.cubic_out(t / post_in)
} else if t >= fade_out_start {
post_alpha = 1.0 - ease.cubic_out((t - fade_out_start) / post_out)
}
post_alpha = clamp(post_alpha, 0, 1)
}
rl.BeginDrawing()
rl.ClearBackground(rl.BLANK)
src := rl.Rectangle{0, 0, f32(rt.texture.width), -f32(rt.texture.height)}
dst := rl.Rectangle{0, 0, f32(rl.GetScreenWidth()), f32(rl.GetScreenHeight())}
rl.DrawTexturePro(
rt.texture,
src,
dst,
rl.Vector2{0, 0},
0,
with_alpha(rl.WHITE, post_alpha),
)
rl.EndDrawing()
}
rl.UnloadRenderTexture(rt)
rl.UnloadFont(font)
rl.UnloadMusicStream(music)
rl.CloseAudioDevice()
rl.CloseWindow()
}