From dbb0cdb166ab7b0a2afea9723013534ee8b67a27 Mon Sep 17 00:00:00 2001 From: HTV04 <53527582+HTV04@users.noreply.github.com> Date: Tue, 31 Aug 2021 00:10:23 -0400 Subject: [PATCH 1/2] push v1.0 --- .gitignore | 2 + README.md | 178 ++++-------- examples/low-res/background.png | Bin 18166 -> 0 bytes examples/low-res/image.png | Bin 0 -> 19721 bytes examples/low-res/init.lua | 117 ++++---- examples/low-res/mario.png | Bin 15163 -> 0 bytes examples/mouse-input/init.lua | 71 ++--- examples/stencil/background.png | Bin 18166 -> 0 bytes images/screenshot.png | Bin 0 -> 11383 bytes main.lua | 52 ++-- push.lua | 483 ++++++++++++++++---------------- 11 files changed, 401 insertions(+), 502 deletions(-) create mode 100644 .gitignore delete mode 100644 examples/low-res/background.png create mode 100644 examples/low-res/image.png delete mode 100644 examples/low-res/mario.png delete mode 100644 examples/stencil/background.png create mode 100644 images/screenshot.png diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..d398c33 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +# Visual Studio Code +.vscode/* diff --git a/README.md b/README.md index 8d0007c..5fb8497 100644 --- a/README.md +++ b/README.md @@ -1,180 +1,116 @@ -**⚠️ Looking for maintainer:** as I am not working with LÖVE anymore, this repository is not actively maintained, except for critical fixes. Please do reach out if you're interested in maintaining this repository. +# push +**push** is a simple resolution-handling library for LÖVE that allows you to focus on making your game with a fixed resolution. -push -============== +![Screenshot](images/screenshot.png) -push is a simple resolution-handling library that allows you to focus on making your game with a fixed resolution. - -![image](https://media.giphy.com/media/xTb1RycLHeAOPDownu/giphy.gif) - -Setup ----------------- -Fullscreen +## Demo +This demo creates a 1280x720 resizable window and sets push to an upscaled 800x600 resolution. Under the "Draw stuff here!" comment, add some drawing functions to see push in action! ```lua -local push = require "push" +push = require "push" -local gameWidth, gameHeight = 1080, 720 --fixed game resolution -local windowWidth, windowHeight = love.window.getDesktopDimensions() +love.window.setMode(1280, 720, {resizable = true}) -- Resizable 1280x720 window +push.setupScreen(800, 600, {upscale = "normal"}) -- 800x600 game resolution, upscaled -push:setupScreen(gameWidth, gameHeight, windowWidth, windowHeight, {fullscreen = true}) - -function love.draw() - push:start() - - --draw here - - push:finish() +-- Make sure push follows LÖVE's resizes +function love.resize(width, height) + push.resize(width, height) end -``` - -Windowed -```lua -local push = require "push" - -local gameWidth, gameHeight = 1080, 720 --fixed game resolution -local windowWidth, windowHeight = love.window.getDesktopDimensions() -windowWidth, windowHeight = windowWidth*.7, windowHeight*.7 --make the window a bit smaller than the screen itself - -push:setupScreen(gameWidth, gameHeight, windowWidth, windowHeight, {fullscreen = false}) function love.draw() - push:start() - - --draw here - - push:finish() + push.start() + -- Draw stuff here! + push.finish() end ``` -Usage ----------------- - -Init push -```lua -push:setupScreen(gameWidth, gameHeight, windowWidth, windowHeight, {fullscreen, resizable, canvas, pixelperfect}) -``` -**gameWidth**, **gameHeight** represent the game's fixed resolution. **windowWidth** and **windowHeight** are the dimensions of the window you need to adapt the game to. - -The last argument is a table containing: -- **fullscreen** (bool): turns fullscreen mode on or off -- **resizable** (bool): allows resizing the window -- **canvas** (bool): uses canvas -- **pixelperfect** (bool): enables pixel-perfect mode (integer scaling 1x, 2x, 3x, ...) -- **highdpi** (bool): enables high-dpi mode on supported screens (e.g. Retina) -- **stretched** (bool): stretches the game to window dimensions - -Apply **push** transforms +## Usage +After applying changes to LÖVE's window using `love.window.setMode()`, init **push**: ```lua -push:start() ---draw here -push:finish() - ---alias -push:apply(operation) +push.setupScreen(pushWidth, pushHeight, {upscale = ..., canvas = ...}) ``` -**operation** should be equal to "start" or "end", meaning "before" or "after" your main drawing logic - -Mobile support ----------------- +`pushWidth` and `pushHeight` represent **push's** fixed resolution. -**push** does *not* have built-in support for mobile platforms, but it is trivial to handle mobile screens correctly. +The last argument is a table containing settings for **push**: +* `upscale` (string): upscale **push's** resolution to the current window size + * `"normal"`: fit to the current window size, preserving aspect ratio + * `"pixel-perfect"`: pixel-perfect scaling using integer scaling (for values ≥1, otherwise uses normal scaling) + * `"stretched"`: stretch to the current window size +* `canvas` (bool): use and upscale canvas set to **push's** resolution -A possible solution is to initialize **push** in fullscreen mode: +Hook **push** into the `love.resize()` function so that it follows LÖVE's resizes: ```lua -local screenWidth, screenHeight = love.window.getDesktopDimensions() -push:setupScreen(gameWidth, gameHeight, screenWidth, screenHeight, { fullscreen = true, resizable = false, ... }) +function love.resize(width, height) + push.resize(width, height) +end ``` -And listen to screen orientation changes: +Finally, apply **push** transforms: ```lua -function love.resize(w, h) - return push:resize(w, h) +function love.draw() + push.start() + -- Draw stuff here! + push.finish() end ``` -Multiple shaders ----------------- - +## Multiple shaders Any method that takes a shader as an argument can also take a *table* of shaders instead. The shaders will be applied in the order they're provided. Set multiple global shaders ```lua -push:setShader({ shader1, shader2 }) +push.setShader({ shader1, shader2 }) ``` Set multiple canvas-specific shaders ```lua -push:setupCanvas({ { name = "multiple_shaders", shader = { shader1, shader2 } } }) +push.setupCanvas({{name = "multiple_shaders", shader = {shader1, shader2}}}) ``` -Advanced canvases/shaders ----------------- - -**push** provides basic canvas and shader functionality through the *canvas* flag in push:setupScreen() and push:setShader(), but you can also create additional canvases, name them for later use and apply multiple shaders to them. +## Advanced canvases/shaders +**push** provides basic canvas and shader functionality through the `canvas` flag in push:setupScreen() and push:setShader(), but you can also create additional canvases, name them for later use and apply multiple shaders to them. -Set up custom canvases +Set up custom canvases: ```lua -push:setupCanvas(canvasList) +push.setupCanvas(canvasList) ---e.g. push:setupCanvas({ { name = "foreground", shader = foregroundShader }, { name = "background" } }) +-- e.g. push.setupCanvas({{name = "foreground", shader = foregroundShader}, {name = "background"}}) ``` Shaders can be passed to canvases directly through push:setupCanvas(), or you can choose to set them later. ```lua -push:setShader(canvasName, shader) +push.setShader(canvasName, shader) ``` Then, you just need to draw your game on different canvases like you'd do with love.graphics.setCanvas(): ```lua -push:setCanvas(canvasName) +push.setCanvas(canvasName) ``` -Resizing the window ----------------- - -In order for push to take in account window resizing (if you have set {resizable = true} in push:setupScreen()), you need to call push:resize() like so: - +## Misc +Update settings: ```lua -function love.resize(w, h) - push:resize(w, h) -end +push.updateSettings({settings}) ``` -Misc ----------------- - -Switch fullscreen +Set a post-processing shader (will apply to the whole screen): ```lua -push:switchFullscreen(w, h) +push.setShader([canvasName], shader) ``` -**w** and **h** are optional parameters that are used in case the game switches to windowed mode +You don't need to call this every frame. Simply call it once, and it will be stored into **push** until you change it back to something else. If no `canvasName` is passed, shader will apply to the final render. Use it at your advantage to combine shader effects. -Set a post-processing shader (will apply to the whole screen) +Convert coordinates: ```lua -push:setShader([canvasName], shader) -``` -You don't need to call this every frame. Simply call it once, and it will be stored into **push** until you change it back to something else. -If no canvasName is passed, shader will apply to the final render. Use it at your advantage to combine shader effects. - -Convert coordinates -```lua -push:toGame(x, y) --convert coordinates from screen to game (useful for mouse position) ---push:toGame will return nil for the values that are outside the game - be sure to check that before using them +push.toGame(x, y) -- Convert coordinates from screen to game (useful for mouse position) +-- push.toGame will return false for values that are outside the game, be sure to check that before using them! -push:toReal(x, y) --convert coordinates from game to screen +push.toReal(x, y) -- Convert coordinates from game to screen ``` -Get game dimensions +Get game dimensions: ```lua -push:getDimensions() --returns push:getWidth(), push:getHeight() +push.getWidth() -- Returns game width -push:getWidth() --returns game width +push.getHeight() -- Returns game height -push:getHeight() --returns game height +push.getDimensions() -- Returns push.getWidth(), push.getHeight() ``` - -Set border color -```lua -push:setBorderColor(r, g, b, a) --also accepts a table -``` - diff --git a/examples/low-res/background.png b/examples/low-res/background.png deleted file mode 100644 index 90773c7165f5d65a861f780a7abf7e3708a5dad3..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 18166 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 OVQce^W_hMN5B@hvbt~Ke diff --git a/examples/low-res/image.png b/examples/low-res/image.png new file mode 100644 index 0000000000000000000000000000000000000000..5cf844313913d2de23a382c3bdcccf417b8034f8 GIT binary patch literal 19721 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 literal 0 HcmV?d00001 diff --git a/examples/low-res/init.lua b/examples/low-res/init.lua index 477f7ec..84a16ae 100644 --- a/examples/low-res/init.lua +++ b/examples/low-res/init.lua @@ -1,77 +1,56 @@ --[[ Low resolution ]]-- return function() - - love.graphics.setDefaultFilter("nearest", "nearest") --disable blurry scaling - - local gameWidth, gameHeight = 64, 64 - local windowWidth, windowHeight = love.window.getDesktopDimensions() - windowWidth, windowHeight = windowWidth*.5, windowHeight*.5 + love.graphics.setDefaultFilter("nearest", "nearest") -- Disable blurry scaling - push:setupScreen(gameWidth, gameHeight, windowWidth, windowHeight, { - fullscreen = false, - resizable = true, - pixelperfect = true - }) - push:setBorderColor{0, 0, 0} --default value - - -- - - time = 0 + love.window.setMode(640, 480, {resizable = true}) -- LÖVE resolution 640x480, resizable + push.setupScreen(64, 64, {upscale = "pixel-perfect", canvas = true}) -- push resolution 64x64, pixel perfect scaling, drawn to a canvas - function love.load() - mario = love.graphics.newImage("examples/low-res/mario.png") - background = love.graphics.newImage("examples/low-res/background.png") - - love.graphics.setNewFont(16) - end - - function love.update(dt) - - time = (time + dt) % 1 - - end + -- - function love.draw() - push:apply("start") - - local mouseX, mouseY = love.mouse.getPosition() - mouseX, mouseY = push:toGame(mouseX, mouseY) - --if nil is returned, that means the mouse is outside the game screen - - local abs = math.abs(time-.5) - local pi = math.cos(math.pi*2*time) - local pi2 = math.cos(math.pi*8*time) - local w = push:getWidth() - --for animating basic stuff - - love.graphics.draw(background, 0, 0) - - love.graphics.setScissor(0, 0, push:getWidth(), push:getHeight()-16) - love.graphics.setColor(0, 0, 0, 100) - love.graphics.draw(mario, 27, 33) - love.graphics.setScissor() - love.graphics.setColor(255, 255, 255) - love.graphics.draw(mario, 26, 32) - - love.graphics.setColor(0, 0, 0, 100) - love.graphics.printf("Hi!", 34+1, 22-pi*2+1, w, "center", -.15+.5*abs, abs*.25+1, abs*.25+1, w*.5, 12) - love.graphics.setColor(255, 255, 255) - love.graphics.printf("Hi!", 34, 22-pi*2, w, "center", -.15+.5*abs, abs*.25+1, abs*.25+1, w*.5, 12) + time = 0 - love.graphics.setColor(255, 255, 255) - if mouseX and mouseY then --cursor - love.graphics.points( - mouseX, mouseY-1, - mouseX-1, mouseY, - mouseX, mouseY, - mouseX+1, mouseY, - mouseX, mouseY+1 - ) - end - - push:apply("end") - end - -end \ No newline at end of file + function love.load() + image = love.graphics.newImage("examples/low-res/image.png") + + love.graphics.setNewFont(16) + end + + function love.update(dt) + + time = (time + dt) % 1 + + end + + function love.draw() + push.start() + local mouseX, mouseY = love.mouse.getPosition() + mouseX, mouseY = push.toGame(mouseX, mouseY) + -- If false is returned, that means the mouse is outside the game screen + + local abs = math.abs(time-.5) + local pi = math.cos(math.pi*2*time) + local w = push:getWidth() + --for animating basic stuff + + love.graphics.draw(image, 0, 0) + + love.graphics.setColor(0, 0, 0, 0.5) + love.graphics.printf("Hi!", 31, 23-pi*2, w, "center", -.15+.5*abs, abs*.25+1, abs*.25+1, w*.5, 12) + love.graphics.setColor(1, 1, 1) + love.graphics.printf("Hi!", 30, 22-pi*2, w, "center", -.15+.5*abs, abs*.25+1, abs*.25+1, w*.5, 12) + + love.graphics.setColor(1, 1, 1) + if mouseX and mouseY then --cursor + love.graphics.points( + mouseX, mouseY-1, + mouseX-1, mouseY, + mouseX, mouseY, + mouseX+1, mouseY, + mouseX, mouseY+1 + ) + end + push.finish() + end +end diff --git a/examples/low-res/mario.png b/examples/low-res/mario.png deleted file mode 100644 index 6962b59625cb1eea7a7a8579a59629baef01f02e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 15163 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 OVQce^W_hMN5B@hvbt~Ke diff --git a/images/screenshot.png b/images/screenshot.png new file mode 100644 index 0000000000000000000000000000000000000000..18c31fcdec739a6518ec57431d7e5591f5d4b31f GIT binary patch literal 11383 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 literal 0 HcmV?d00001 diff --git a/main.lua b/main.lua index 42a0a88..a959830 100644 --- a/main.lua +++ b/main.lua @@ -1,40 +1,40 @@ -io.stdout:setvbuf('no') - push = require "push" --require the library -love.window.setTitle("Press space to switch examples") +love.window.setTitle("Press space to switch examples!") local examples = { - "low-res", - "single-shader", - "multiple-shaders", - "mouse-input", - "canvases-shaders", - "stencil" + "low-res", + --[[ + "single-shader", + "multiple-shaders", + --]] + "mouse-input" + --[[ + "canvases-shaders", + "stencil" + --]] } local example = 1 for i = 1, #examples do - examples[i] = require("examples." .. examples[i]) + examples[i] = require("examples." .. examples[i]) end function love.resize(w, h) - push:resize(w, h) + push.resize(w, h) end -function love.keypressed(key, scancode, isrepeat) - - if key == "space" then - example = (example < #examples) and example + 1 or 1 - - --be sure to reset push settings - push:resetSettings() - - examples[example]() - love.load() - elseif key == "f" then --activate fullscreen mode - push:switchFullscreen() --optional width and height parameters for window mode - end - +function love.keypressed(key) + if key == "space" then + example = (example < #examples) and example + 1 or 1 + + examples[example]() + love.load() + elseif key == "f" then -- Activate fullscreen mode + love.window.setMode(0, 0, {fullscreen = true, fullscreentype = "desktop"}) + push.resize(love.graphics.getDimensions()) + elseif key == "escape" then + love.event.quit() + end end -examples[example]() \ No newline at end of file +examples[example]() diff --git a/push.lua b/push.lua index b22f698..d753744 100644 --- a/push.lua +++ b/push.lua @@ -1,281 +1,272 @@ --- push.lua v0.4 - --- Copyright (c) 2020 Ulysse Ramage --- Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: --- The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. --- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - -local love11 = love.getVersion() == 11 -local getDPI = love11 and love.window.getDPIScale or love.window.getPixelScale -local windowUpdateMode = love11 and love.window.updateMode or function(width, height, settings) - local _, _, flags = love.window.getMode() - for k, v in pairs(settings) do flags[k] = v end - love.window.setMode(width, height, flags) -end +--[[ +push.lua v1.0 -local push = { - - defaults = { - fullscreen = false, - resizable = false, - pixelperfect = false, - highdpi = true, - canvas = true, - stencil = true - } - -} -setmetatable(push, push) +The MIT License (MIT) -function push:applySettings(settings) - for k, v in pairs(settings) do - self["_" .. k] = v - end -end +Copyright (c) 2018 Ulysse Ramage -function push:resetSettings() return self:applySettings(self.defaults) end +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: -function push:setupScreen(WWIDTH, WHEIGHT, RWIDTH, RHEIGHT, settings) +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. - settings = settings or {} +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +--]] - self._WWIDTH, self._WHEIGHT = WWIDTH, WHEIGHT - self._RWIDTH, self._RHEIGHT = RWIDTH, RHEIGHT +local settings - self:applySettings(self.defaults) --set defaults first - self:applySettings(settings) --then fill with custom settings - - windowUpdateMode(self._RWIDTH, self._RHEIGHT, { - fullscreen = self._fullscreen, - resizable = self._resizable, - highdpi = self._highdpi - }) +local pushWidth, pushHeight +local windowWidth, windowHeight - self:initValues() +local scale = {x = 0, y = 0} +local offset = {x = 0, y = 0} - if self._canvas then - self:setupCanvas({ "default" }) --setup canvas - end +local drawWidth, drawHeight - self._borderColor = {0, 0, 0} +local canvases - self._drawFunctions = { - ["start"] = self.start, - ["end"] = self.finish - } +local canvasOptions - return self -end +local function initValues() + if settings.upscale then + scale.x = windowWidth / pushWidth + scale.y = windowHeight / pushHeight -function push:setupCanvas(canvases) - table.insert(canvases, { name = "_render", private = true }) --final render + if settings.upscale == "normal" or settings.upscale == "pixel-perfect" then + local scaleVal - self._canvas = true - self.canvases = {} + scaleVal = math.min(scale.x, scale.y) + if scaleVal >= 1 and settings.upscale == "pixel-perfect" then scaleVal = math.floor(scaleVal) end - for i = 1, #canvases do - push:addCanvas(canvases[i]) - end + offset.x = math.floor((scale.x - scaleVal) * (pushWidth / 2)) + offset.y = math.floor((scale.y - scaleVal) * (pushHeight / 2)) - return self -end -function push:addCanvas(params) - table.insert(self.canvases, { - name = params.name, - private = params.private, - shader = params.shader, - canvas = love.graphics.newCanvas(self._WWIDTH, self._WHEIGHT), - stencil = params.stencil or self._stencil - }) -end + scale.x, scale.y = scaleVal, scaleVal -- Apply same scale to width and height + elseif settings.upscale == "stretched" then -- If stretched, no need to apply offset + offset.x, offset.y = 0, 0 + else + error("Invalid upscale setting") + end + else + scale.x, scale.y = 1, 1 -function push:setCanvas(name) - if not self._canvas then return true end - local canvasTable = self:getCanvasTable(name) - return love.graphics.setCanvas({ canvasTable.canvas, stencil = canvasTable.stencil }) -end -function push:getCanvasTable(name) - for i = 1, #self.canvases do - if self.canvases[i].name == name then - return self.canvases[i] - end - end -end -function push:setShader(name, shader) - if not shader then - self:getCanvasTable("_render").shader = name - else - self:getCanvasTable(name).shader = shader - end -end + offset.x = math.floor((windowWidth / pushWidth - 1) * (pushWidth / 2)) + offset.y = math.floor((windowHeight / pushHeight - 1) * (pushHeight / 2)) + end -function push:initValues() - self._PSCALE = (not love11 and self._highdpi) and getDPI() or 1 - - self._SCALE = { - x = self._RWIDTH/self._WWIDTH * self._PSCALE, - y = self._RHEIGHT/self._WHEIGHT * self._PSCALE - } - - if self._stretched then --if stretched, no need to apply offset - self._OFFSET = {x = 0, y = 0} - else - local scale = math.min(self._SCALE.x, self._SCALE.y) - if self._pixelperfect then scale = math.floor(scale) end - - self._OFFSET = {x = (self._SCALE.x - scale) * (self._WWIDTH/2), y = (self._SCALE.y - scale) * (self._WHEIGHT/2)} - self._SCALE.x, self._SCALE.y = scale, scale --apply same scale to X and Y - end - - self._GWIDTH = self._RWIDTH * self._PSCALE - self._OFFSET.x * 2 - self._GHEIGHT = self._RHEIGHT * self._PSCALE - self._OFFSET.y * 2 + drawWidth = windowWidth - offset.x * 2 + drawHeight = windowHeight - offset.y * 2 end -function push:apply(operation, shader) - self._drawFunctions[operation](self, shader) -end +local function setupCanvas(canvasTable) + table.insert(canvasTable, {name = "_render", private = true}) -- Final render -function push:start() - if self._canvas then - love.graphics.push() - love.graphics.setCanvas({ self.canvases[1].canvas, stencil = self.canvases[1].stencil }) - - else - love.graphics.translate(self._OFFSET.x, self._OFFSET.y) - love.graphics.setScissor(self._OFFSET.x, self._OFFSET.y, self._WWIDTH*self._SCALE.x, self._WHEIGHT*self._SCALE.y) - love.graphics.push() - love.graphics.scale(self._SCALE.x, self._SCALE.y) - end -end + canvases = {} -function push:applyShaders(canvas, shaders) - local _shader = love.graphics.getShader() - if #shaders <= 1 then - love.graphics.setShader(shaders[1]) - love.graphics.draw(canvas) - else - local _canvas = love.graphics.getCanvas() - - local _tmp = self:getCanvasTable("_tmp") - if not _tmp then --create temp canvas only if needed - self:addCanvas({ name = "_tmp", private = true, shader = nil }) - _tmp = self:getCanvasTable("_tmp") - end - - love.graphics.push() - love.graphics.origin() - local outputCanvas - for i = 1, #shaders do - local inputCanvas = i % 2 == 1 and canvas or _tmp.canvas - outputCanvas = i % 2 == 0 and canvas or _tmp.canvas - love.graphics.setCanvas(outputCanvas) - love.graphics.clear() - love.graphics.setShader(shaders[i]) - love.graphics.draw(inputCanvas) - love.graphics.setCanvas(inputCanvas) - end - love.graphics.pop() - - love.graphics.setCanvas(_canvas) - love.graphics.draw(outputCanvas) - end - love.graphics.setShader(_shader) -end + for i = 1, #canvasTable do + local params = canvasTable[i] -function push:finish(shader) - love.graphics.setBackgroundColor(unpack(self._borderColor)) - if self._canvas then - local _render = self:getCanvasTable("_render") - - love.graphics.pop() - - local white = love11 and 1 or 255 - love.graphics.setColor(white, white, white) - - --draw canvas - love.graphics.setCanvas(_render.canvas) - for i = 1, #self.canvases do --do not draw _render yet - local _table = self.canvases[i] - if not _table.private then - local _canvas = _table.canvas - local _shader = _table.shader - self:applyShaders(_canvas, type(_shader) == "table" and _shader or { _shader }) - end - end - love.graphics.setCanvas() - - --draw render - love.graphics.translate(self._OFFSET.x, self._OFFSET.y) - local shader = shader or _render.shader - love.graphics.push() - love.graphics.scale(self._SCALE.x, self._SCALE.y) - self:applyShaders(_render.canvas, type(shader) == "table" and shader or { shader }) - love.graphics.pop() - - --clear canvas - for i = 1, #self.canvases do - love.graphics.setCanvas(self.canvases[i].canvas) - love.graphics.clear() - end - - love.graphics.setCanvas() - love.graphics.setShader() - else - love.graphics.pop() - love.graphics.setScissor() - end -end + table.insert( + canvases, + { + name = params.name, + private = params.private, + shader = params.shader, + canvas = love.graphics.newCanvas(pushWidth, pushHeight), + stencil = params.stencil + } + ) + end -function push:setBorderColor(color, g, b) - self._borderColor = g and {color, g, b} or color + canvasOptions = {canvases[1].canvas, stencil = canvases[1].stencil} end -function push:toGame(x, y) - x, y = x - self._OFFSET.x, y - self._OFFSET.y - local normalX, normalY = x / self._GWIDTH, y / self._GHEIGHT - - x = (x >= 0 and x <= self._WWIDTH * self._SCALE.x) and normalX * self._WWIDTH or nil - y = (y >= 0 and y <= self._WHEIGHT * self._SCALE.y) and normalY * self._WHEIGHT or nil - - return x, y +local function getCanvasTable(name) + for i = 1, #canvases do + if canvases[i].name == name then + return canvases[i] + end + end end -function push:toReal(x, y) - local realX = self._OFFSET.x + (self._GWIDTH * x)/self._WWIDTH - local realY = self._OFFSET.y + (self._GHEIGHT * y)/self._WHEIGHT - return realX, realY +local function start() + if settings.canvas then + love.graphics.push() + love.graphics.setCanvas(canvasOptions) + else + love.graphics.translate(offset.x, offset.y) + love.graphics.setScissor(offset.x, offset.y, pushWidth * scale.x, pushHeight * scale.y) + love.graphics.push() + love.graphics.scale(scale.x, scale.y) + end end -function push:switchFullscreen(winw, winh) - self._fullscreen = not self._fullscreen - local windowWidth, windowHeight = love.window.getDesktopDimensions() - - if self._fullscreen then --save windowed dimensions for later - self._WINWIDTH, self._WINHEIGHT = self._RWIDTH, self._RHEIGHT - elseif not self._WINWIDTH or not self._WINHEIGHT then - self._WINWIDTH, self._WINHEIGHT = windowWidth * .5, windowHeight * .5 - end - - self._RWIDTH = self._fullscreen and windowWidth or winw or self._WINWIDTH - self._RHEIGHT = self._fullscreen and windowHeight or winh or self._WINHEIGHT - - self:initValues() - - love.window.setFullscreen(self._fullscreen, "desktop") - if not self._fullscreen and (winw or winh) then - windowUpdateMode(self._RWIDTH, self._RHEIGHT) --set window dimensions - end +local function applyShaders(canvas, shaders) + local shader = love.graphics.getShader() + + if #shaders <= 1 then + love.graphics.setShader(shaders[1]) + love.graphics.draw(canvas) + else + local canvas = love.graphics.getCanvas() + local tmp = getCanvasTable("_tmp") + local outputCanvas + local inputCanvas + + -- Only create "_tmp" canvas if needed + if not tmp then + table.insert( + canvases, + { + name = "_tmp", + private = true, + canvas = love.graphics.newCanvas(pushWidth, pushHeight) + } + ) + + tmp = getCanvasTable("_tmp") + end + + love.graphics.push() + love.graphics.origin() + for i = 1, #shaders do + inputCanvas = i % 2 == 1 and canvas or tmp.canvas + outputCanvas = i % 2 == 0 and canvas or tmp.canvas + love.graphics.setCanvas(outputCanvas) + love.graphics.clear() + love.graphics.setShader(shaders[i]) + love.graphics.draw(inputCanvas) + love.graphics.setCanvas(inputCanvas) + end + love.graphics.pop() + + love.graphics.setCanvas(canvas) + love.graphics.draw(outputCanvas) + end + + love.graphics.setShader(shader) end -function push:resize(w, h) - if self._highdpi then w, h = w / self._PSCALE, h / self._PSCALE end - self._RWIDTH = w - self._RHEIGHT = h - self:initValues() +local function finish(shader) + if settings.canvas then + local render = getCanvasTable("_render") + + love.graphics.pop() + + -- Draw canvas + love.graphics.setCanvas(render.canvas) + -- Do not draw render yet + for i = 1, #canvases do + local canvasTable = canvases[i] + + if not canvasTable.private then + local shader = canvasTable.shader + + applyShaders(canvasTable.canvas, type(shader) == "table" and shader or {shader}) + end + end + love.graphics.setCanvas() + + -- Now draw render + love.graphics.translate(offset.x, offset.y) + love.graphics.push() + love.graphics.scale(scale.x, scale.y) + do + local shader = shader or render.shader + + applyShaders(render.canvas, type(shader) == "table" and shader or {shader}) + end + love.graphics.pop() + + -- Clear canvas + for i = 1, #canvases do + love.graphics.setCanvas(canvases[i].canvas) + love.graphics.clear() + end + + love.graphics.setCanvas() + love.graphics.setShader() + else + love.graphics.pop() + love.graphics.setScissor() + end end -function push:getWidth() return self._WWIDTH end -function push:getHeight() return self._WHEIGHT end -function push:getDimensions() return self._WWIDTH, self._WHEIGHT end +return { + setupScreen = function(width, height, settingsTable) + pushWidth, pushHeight = width, height + windowWidth, windowHeight = love.graphics.getDimensions() + + settings = settingsTable + + initValues() -return push + if settings.canvas then + setupCanvas({"default"}) + end + end, + + setupCanvas = setupCanvas, + setCanvas = function(name) + local canvasTable + + if not settings.canvas then return true end + + canvasTable = getCanvasTable(name) + return love.graphics.setCanvas({canvasTable.canvas, stencil = canvasTable.stencil}) + end, + setShader = function(name, shader) + if not shader then + getCanvasTable("_render").shader = name + else + getCanvasTable(name).shader = shader + end + end, + + updateSettings = function(settingsTable) + settings.upscale = settingsTable.upscale or settings.upscale + settings.canvas = settingsTable.canvas or settings.canvas + end, + + toGame = function(x, y) + local normalX, normalY + + x, y = x - offset.x, y - offset.y + normalX, normalY = x / drawWidth, y / drawHeight + + x = (x >= 0 and x <= pushWidth * scale.x) and math.floor(normalX * pushWidth) or false + y = (y >= 0 and y <= pushHeight * scale.y) and math.floor(normalY * pushHeight) or false + + return x, y + end, + toReal = function(x, y) + local realX = offset.x + (drawWidth * x) / pushWidth + local realY = offset.y + (drawHeight * y)/ pushHeight + + return realX, realY + end, + + start = start, + finish = finish, + + resize = function(width, height) + windowWidth, windowHeight = width, height + + initValues() + end, + + getWidth = function() return pushWidth end, + getHeight = function() return pushHeight end, + getDimensions = function() return pushWidth, pushHeight end +} From f38feeac5df1dc7f4536111b8b3e9a03b2b32848 Mon Sep 17 00:00:00 2001 From: HTV04 <53527582+HTV04@users.noreply.github.com> Date: Sun, 24 Oct 2021 21:29:13 -0400 Subject: [PATCH 2/2] Fix indentation --- push.lua | 84 ++++++++++++++++++++++++++++---------------------------- 1 file changed, 42 insertions(+), 42 deletions(-) diff --git a/push.lua b/push.lua index d753744..1c101a8 100644 --- a/push.lua +++ b/push.lua @@ -50,9 +50,9 @@ local function initValues() if scaleVal >= 1 and settings.upscale == "pixel-perfect" then scaleVal = math.floor(scaleVal) end offset.x = math.floor((scale.x - scaleVal) * (pushWidth / 2)) - offset.y = math.floor((scale.y - scaleVal) * (pushHeight / 2)) + offset.y = math.floor((scale.y - scaleVal) * (pushHeight / 2)) - scale.x, scale.y = scaleVal, scaleVal -- Apply same scale to width and height + scale.x, scale.y = scaleVal, scaleVal -- Apply same scale to width and height elseif settings.upscale == "stretched" then -- If stretched, no need to apply offset offset.x, offset.y = 0, 0 else @@ -62,7 +62,7 @@ local function initValues() scale.x, scale.y = 1, 1 offset.x = math.floor((windowWidth / pushWidth - 1) * (pushWidth / 2)) - offset.y = math.floor((windowHeight / pushHeight - 1) * (pushHeight / 2)) + offset.y = math.floor((windowHeight / pushHeight - 1) * (pushHeight / 2)) end drawWidth = windowWidth - offset.x * 2 @@ -77,23 +77,23 @@ local function setupCanvas(canvasTable) for i = 1, #canvasTable do local params = canvasTable[i] - table.insert( - canvases, - { - name = params.name, - private = params.private, - shader = params.shader, - canvas = love.graphics.newCanvas(pushWidth, pushHeight), - stencil = params.stencil - } - ) + table.insert( + canvases, + { + name = params.name, + private = params.private, + shader = params.shader, + canvas = love.graphics.newCanvas(pushWidth, pushHeight), + stencil = params.stencil + } + ) end canvasOptions = {canvases[1].canvas, stencil = canvases[1].stencil} end local function getCanvasTable(name) - for i = 1, #canvases do + for i = 1, #canvases do if canvases[i].name == name then return canvases[i] end @@ -101,7 +101,7 @@ local function getCanvasTable(name) end local function start() - if settings.canvas then + if settings.canvas then love.graphics.push() love.graphics.setCanvas(canvasOptions) else @@ -127,13 +127,13 @@ local function applyShaders(canvas, shaders) -- Only create "_tmp" canvas if needed if not tmp then table.insert( - canvases, - { - name = "_tmp", - private = true, - canvas = love.graphics.newCanvas(pushWidth, pushHeight) - } - ) + canvases, + { + name = "_tmp", + private = true, + canvas = love.graphics.newCanvas(pushWidth, pushHeight) + } + ) tmp = getCanvasTable("_tmp") end @@ -205,27 +205,27 @@ end return { setupScreen = function(width, height, settingsTable) - pushWidth, pushHeight = width, height + pushWidth, pushHeight = width, height windowWidth, windowHeight = love.graphics.getDimensions() - settings = settingsTable + settings = settingsTable - initValues() + initValues() - if settings.canvas then - setupCanvas({"default"}) - end - end, + if settings.canvas then + setupCanvas({"default"}) + end + end, - setupCanvas = setupCanvas, + setupCanvas = setupCanvas, setCanvas = function(name) - local canvasTable + local canvasTable - if not settings.canvas then return true end + if not settings.canvas then return true end - canvasTable = getCanvasTable(name) - return love.graphics.setCanvas({canvasTable.canvas, stencil = canvasTable.stencil}) - end, + canvasTable = getCanvasTable(name) + return love.graphics.setCanvas({canvasTable.canvas, stencil = canvasTable.stencil}) + end, setShader = function(name, shader) if not shader then getCanvasTable("_render").shader = name @@ -260,13 +260,13 @@ return { start = start, finish = finish, - resize = function(width, height) - windowWidth, windowHeight = width, height + resize = function(width, height) + windowWidth, windowHeight = width, height - initValues() - end, + initValues() + end, - getWidth = function() return pushWidth end, - getHeight = function() return pushHeight end, - getDimensions = function() return pushWidth, pushHeight end + getWidth = function() return pushWidth end, + getHeight = function() return pushHeight end, + getDimensions = function() return pushWidth, pushHeight end }