Skip to content

Commit 162f8ae

Browse files
authored
add SMODS.ScreenShader (#1174)
1 parent 3b3277f commit 162f8ae

File tree

5 files changed

+167
-2
lines changed

5 files changed

+167
-2
lines changed

lovely/screen_shader_stack.toml

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
[manifest]
2+
version = "1.0.0"
3+
dump_lua = true
4+
priority = -5
5+
6+
# i think prio 0 is correct. idk if this patch builds off of any other patches
7+
8+
[[patches]]
9+
[patches.pattern]
10+
target = "game.lua"
11+
pattern = '''
12+
if G.AA_CANVAS then
13+
love.graphics.push()
14+
love.graphics.scale(1/G.CANV_SCALE)
15+
love.graphics.draw(G.AA_CANVAS, 0, 0)
16+
love.graphics.pop()
17+
end
18+
'''
19+
position = "before"
20+
payload = '''
21+
local last_canvas = self.CANVAS
22+
G.SHADER_CANVAS_A = G.SHADER_CANVAS_A or SMODS.create_canvas()
23+
G.SHADER_CANVAS_B = G.SHADER_CANVAS_B or SMODS.create_canvas()
24+
for index, key in ipairs(SMODS.ScreenShader.obj_buffer) do
25+
local shader = SMODS.ScreenShaders[key]
26+
if (shader.should_apply and shader:should_apply()) or (not shader.should_apply) then
27+
--hypothetically less table accesses
28+
local shader_object = G.SHADERS[shader.shader]
29+
assert(shader_object, "Shader " .. shader.key .. " not found in G.SHADERS")
30+
31+
-- effectively swaps between A and B
32+
local current_canvas = (last_canvas == G.SHADER_CANVAS_A) and G.SHADER_CANVAS_B or G.SHADER_CANVAS_A
33+
34+
if shader.send_vars then
35+
local vars = shader:send_vars()
36+
for k,v in pairs(vars) do
37+
shader_object:send(k, v)
38+
end
39+
40+
end
41+
42+
love.graphics.setCanvas(current_canvas)
43+
love.graphics.setShader(shader_object)
44+
love.graphics.draw(last_canvas, 0, 0)
45+
46+
last_canvas = current_canvas
47+
end
48+
end
49+
-- I don't like having this out here but id rather not run it when i don't have to
50+
-- also less setcanvas and setshader means it should run a smidge faster
51+
love.graphics.setCanvas()
52+
love.graphics.setShader()
53+
if last_canvas then
54+
love.graphics.draw(last_canvas, 0, 0)
55+
-- Draw everything we just did to the main canvas (what the player actually sees)
56+
end
57+
'''
58+
match_indent = true
59+
60+
61+
62+
[[patches]]
63+
[patches.pattern]
64+
target = "main.lua"
65+
pattern = '''
66+
G.CANVAS = love.graphics.newCanvas(w*G.CANV_SCALE, h*G.CANV_SCALE, {type = '2d', readable = true})
67+
G.CANVAS:setFilter('linear', 'linear')
68+
'''
69+
position = "after"
70+
payload = '''
71+
G.SHADER_CANVAS_A = SMODS.create_canvas()
72+
G.SHADER_CANVAS_B = SMODS.create_canvas()
73+
'''
74+
match_indent = true

lsp_def/classes/screenshader.lua

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
---@meta
2+
3+
---@class SMODS.ScreenShader: SMODS.GameObject
4+
---@field obj_table? table<string, SMODS.ScreenShader|table> Table of objects registered to this class.
5+
---@field super? SMODS.GameObject|table Parent class.
6+
---@field key string Unique string to reference this object.
7+
---@field shader? string Key of the shader to apply to the screen, shader must already exist to use this. Either this or `path` must be used
8+
---@field order? number Sets the order. `ScreenShader` objects are rendered in order from lowest to highest. Defaults to 0 if not provided.
9+
---@field path? string Name of the shader file to use if `shader` is not provided.
10+
---@field __call? fun(self: SMODS.ScreenShader|table, o: SMODS.ScreenShader|table): nil|table|SMODS.ScreenShader
11+
---@field extend? fun(self: SMODS.ScreenShader|table, o: SMODS.ScreenShader|table): table Primary method of creating a class.
12+
---@field check_duplicate_register? fun(self: SMODS.ScreenShader|table): boolean? Ensures objects already registered will not register.
13+
---@field check_duplicate_key? fun(self: SMODS.ScreenShader|table): boolean? Ensures objects with duplicate keys will not register. Checked on `__call` but not `take_ownership`. For take_ownership, the key must exist.
14+
---@field register? fun(self: SMODS.ScreenShader|table) Registers the object.
15+
---@field check_dependencies? fun(self: SMODS.ScreenShader|table): boolean? Returns `true` if there's no failed dependencies.
16+
---@field send_to_subclasses? fun(self: SMODS.ScreenShader|table, func: string, ...: any) Starting from this class, recusively searches for functions with the given key on all subordinate classes and run all found functions with the given arguments.
17+
---@field pre_inject_class? fun(self: SMODS.ScreenShader|table) Called before `inject_class`. Injects and manages class information before object injection.
18+
---@field post_inject_class? fun(self: SMODS.ScreenShader|table) Called after `inject_class`. Injects and manages class information after object injection.
19+
---@field inject_class? fun(self: SMODS.ScreenShader|table) Injects all direct instances of class objects by calling `obj:inject` and `obj:process_loc_text`. Also injects anything necessary for the class itself. Only called if class has defined both `obj_table` and `obj_buffer`.
20+
---@field inject? fun(self: SMODS.ScreenShader|table, i?: number) Called during `inject_class`. Injects the object into the game.
21+
---@field take_ownership? fun(self: SMODS.ScreenShader|table, key: string, obj: SMODS.ScreenShader|table, silent?: boolean): nil|table|SMODS.ScreenShader Takes control of vanilla objects. Child class must have get_obj for this to function
22+
---@field get_obj? fun(self: SMODS.ScreenShader|table, key: string): SMODS.ScreenShader|table? Returns an object if one matches the `key`.
23+
---@field send_vars? fun(self: SMODS.ScreenShader|table): table? Used to send extra args to the shader via `Shader:send(key, value)`.
24+
---@field should_apply? fun(self: SMODS.ScreenShader|table): boolean? Used to control if the ScreenShader should apply on the screen per-frame
25+
---@overload fun(self: SMODS.ScreenShader): SMODS.ScreenShader
26+
SMODS.ScreenShader = setmetatable({}, {
27+
__call = function(self)
28+
return self
29+
end
30+
})
31+
32+
---@type table<string, SMODS.ScreenShader|table>
33+
SMODS.ScreenShaders = {}

lsp_def/classes/shader.lua

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,8 @@
33
---@class SMODS.Shader: SMODS.GameObject
44
---@field obj_table? table<string, SMODS.Shader|table> Table of objects registered to this class.
55
---@field super? SMODS.GameObject|table Parent class.
6-
---@field key? string Unique string to reference this object. This, `path`, and shader name in the GLSL must be the same.
7-
---@field path? string Name of the shader file. This, `key`, and shader name in the GLSL must be the same.
6+
---@field key string Unique string to reference this object. This, `path`, and shader name in the GLSL must be the same.
7+
---@field path string Name of the shader file. This, `key`, and shader name in the GLSL must be the same.
88
---@field __call? fun(self: SMODS.Shader|table, o: SMODS.Shader|table): nil|table|SMODS.Shader
99
---@field extend? fun(self: SMODS.Shader|table, o: SMODS.Shader|table): table Primary method of creating a class.
1010
---@field check_duplicate_register? fun(self: SMODS.Shader|table): boolean? Ensures objects already registered will not register.

src/game_object.lua

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3294,6 +3294,56 @@ Set `prefix_config.key = false` on your object instead.]]):format(obj.key), obj.
32943294
process_loc_text = function() end
32953295
}
32963296

3297+
-------------------------------------------------------------------------------------------------
3298+
----- API CODE GameObject.ScreenShader
3299+
-------------------------------------------------------------------------------------------------
3300+
3301+
SMODS.ScreenShaders = {}
3302+
SMODS.ScreenShader = SMODS.GameObject:extend {
3303+
obj_table = SMODS.ScreenShaders,
3304+
obj_buffer = {},
3305+
required_params = {
3306+
"key",
3307+
},
3308+
set = "ScreenShader",
3309+
order = 0,
3310+
send_vars = nil, -- same as Shader.send_vars
3311+
should_apply = nil, -- function to determine if the shader should be drawn. defaults to true if not specified
3312+
inject = function(self)
3313+
assert(self.shader or self.path, "ScreenShader " .. self.key .. " not given shader key or path")
3314+
if self.path and (not self.shader) then
3315+
SMODS.Shader.inject(self)
3316+
self.shader = self.key
3317+
end
3318+
end,
3319+
post_inject_class = function(self)
3320+
table.sort(self.obj_buffer, function(_self, _other) return self.obj_table[_self].order < self.obj_table[_other].order end)
3321+
end,
3322+
}
3323+
3324+
SMODS.ScreenShader {
3325+
key = "CRT",
3326+
shader = "CRT",
3327+
send_vars = function(self)
3328+
local crt = G.SETTINGS.GRAPHICS.crt * 0.3
3329+
return {
3330+
['distortion_fac'] = {1.0 + 0.07*crt/100, 1.0 + 0.1*crt/100},
3331+
['scale_fac'] = {1.0 - 0.008*crt/100, 1.0 - 0.008*crt/100},
3332+
['feather_fac'] = 0.01,
3333+
['bloom_fac'] = G.SETTINGS.GRAPHICS.bloom - 1,
3334+
['time'] = 400 + G.TIMERS.REAL,
3335+
['noise_fac'] = 0.001*crt/100,
3336+
['crt_intensity'] = 0.16*crt/100,
3337+
['glitch_intensity'] = 0,
3338+
['scanlines'] = G.CANVAS:getPixelHeight()*0.75/G.CANV_SCALE,
3339+
['mouse_screen_pos'] = G.video_control and {love.graphics.getWidth( )/2, love.graphics.getHeight( )/2} or {G.ARGS.eased_cursor_pos.sx, G.ARGS.eased_cursor_pos.sy},
3340+
['screen_scale'] = G.TILESCALE*G.TILESIZE,
3341+
['hovering'] = 1,
3342+
}
3343+
end,
3344+
order = 0, -- not necessary, but explicitly set in this example for clarity
3345+
}
3346+
32973347
-------------------------------------------------------------------------------------------------
32983348
----- API CODE GameObject.Edition
32993349
-------------------------------------------------------------------------------------------------

src/utils.lua

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3377,6 +3377,14 @@ function SMODS.log_crash_info(info, defined)
33773377
end
33783378
end
33793379

3380+
3381+
-- Used for SMODS.ScreenShader, just to save lines re-creating canvases when relevant
3382+
function SMODS.create_canvas()
3383+
local canvas = love.graphics.newCanvas(love.window.fromPixels(love.graphics.getWidth()) * G.CANV_SCALE, love.window.fromPixels(love.graphics.getHeight()) * G.CANV_SCALE, { type = '2d', readable = true })
3384+
canvas:setFilter('linear', 'linear')
3385+
return canvas
3386+
end
3387+
33803388
function SMODS.get_clean_pool(_type, _rarity, _legendary, _append)
33813389
local pool = get_current_pool(_type, _rarity, _legendary, _append)
33823390
local clean_pool = {}

0 commit comments

Comments
 (0)