You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

272 lines
7.8 KiB

  1. local love11 = love.getVersion() == 11
  2. local getDPI = love11 and love.window.getDPIScale or love.window.getPixelScale
  3. local windowUpdateMode = love11 and love.window.updateMode or function(width, height, settings)
  4. local _, _, flags = love.window.getMode()
  5. for k, v in pairs(settings) do flags[k] = v end
  6. love.window.setMode(width, height, flags)
  7. end
  8. local push = {
  9. defaults = {
  10. fullscreen = false,
  11. resizable = false,
  12. pixelperfect = false,
  13. highdpi = true,
  14. canvas = true,
  15. stencil = true
  16. }
  17. }
  18. setmetatable(push, push)
  19. function push:applySettings(settings)
  20. for k, v in pairs(settings) do
  21. self["_" .. k] = v
  22. end
  23. end
  24. function push:resetSettings() return self:applySettings(self.defaults) end
  25. function push:setupScreen(WWIDTH, WHEIGHT, RWIDTH, RHEIGHT, settings)
  26. settings = settings or {}
  27. self._WWIDTH, self._WHEIGHT = WWIDTH, WHEIGHT
  28. self._RWIDTH, self._RHEIGHT = RWIDTH, RHEIGHT
  29. self:applySettings(self.defaults) --set defaults first
  30. self:applySettings(settings) --then fill with custom settings
  31. windowUpdateMode(self._RWIDTH, self._RHEIGHT, {
  32. fullscreen = self._fullscreen,
  33. resizable = self._resizable,
  34. highdpi = self._highdpi
  35. })
  36. self:initValues()
  37. if self._canvas then
  38. self:setupCanvas({ "default" }) --setup canvas
  39. end
  40. self._borderColor = {0, 0, 0}
  41. self._drawFunctions = {
  42. ["start"] = self.start,
  43. ["end"] = self.finish
  44. }
  45. return self
  46. end
  47. function push:setupCanvas(canvases)
  48. table.insert(canvases, { name = "_render", private = true }) --final render
  49. self._canvas = true
  50. self.canvases = {}
  51. for i = 1, #canvases do
  52. push:addCanvas(canvases[i])
  53. end
  54. return self
  55. end
  56. function push:addCanvas(params)
  57. table.insert(self.canvases, {
  58. name = params.name,
  59. private = params.private,
  60. shader = params.shader,
  61. canvas = love.graphics.newCanvas(self._WWIDTH, self._WHEIGHT),
  62. stencil = params.stencil or self._stencil
  63. })
  64. end
  65. function push:setCanvas(name)
  66. if not self._canvas then return true end
  67. return love.graphics.setCanvas(self:getCanvasTable(name).canvas)
  68. end
  69. function push:getCanvasTable(name)
  70. for i = 1, #self.canvases do
  71. if self.canvases[i].name == name then
  72. return self.canvases[i]
  73. end
  74. end
  75. end
  76. function push:setShader(name, shader)
  77. if not shader then
  78. self:getCanvasTable("_render").shader = name
  79. else
  80. self:getCanvasTable(name).shader = shader
  81. end
  82. end
  83. function push:initValues()
  84. self._PSCALE = (not love11 and self._highdpi) and getDPI() or 1
  85. self._SCALE = {
  86. x = self._RWIDTH/self._WWIDTH * self._PSCALE,
  87. y = self._RHEIGHT/self._WHEIGHT * self._PSCALE
  88. }
  89. if self._stretched then --if stretched, no need to apply offset
  90. self._OFFSET = {x = 0, y = 0}
  91. else
  92. local scale = math.min(self._SCALE.x, self._SCALE.y)
  93. if self._pixelperfect then scale = math.floor(scale) end
  94. self._OFFSET = {x = (self._SCALE.x - scale) * (self._WWIDTH/2), y = (self._SCALE.y - scale) * (self._WHEIGHT/2)}
  95. self._SCALE.x, self._SCALE.y = scale, scale --apply same scale to X and Y
  96. end
  97. self._GWIDTH = self._RWIDTH * self._PSCALE - self._OFFSET.x * 2
  98. self._GHEIGHT = self._RHEIGHT * self._PSCALE - self._OFFSET.y * 2
  99. end
  100. function push:apply(operation, shader)
  101. self._drawFunctions[operation](self, shader)
  102. end
  103. function push:start()
  104. if self._canvas then
  105. love.graphics.push()
  106. love.graphics.setCanvas({ self.canvases[1].canvas, stencil = self.canvases[1].stencil })
  107. else
  108. love.graphics.translate(self._OFFSET.x, self._OFFSET.y)
  109. love.graphics.setScissor(self._OFFSET.x, self._OFFSET.y, self._WWIDTH*self._SCALE.x, self._WHEIGHT*self._SCALE.y)
  110. love.graphics.push()
  111. love.graphics.scale(self._SCALE.x, self._SCALE.y)
  112. end
  113. end
  114. function push:applyShaders(canvas, shaders)
  115. local _shader = love.graphics.getShader()
  116. if #shaders <= 1 then
  117. love.graphics.setShader(shaders[1])
  118. love.graphics.draw(canvas)
  119. else
  120. local _canvas = love.graphics.getCanvas()
  121. local _tmp = self:getCanvasTable("_tmp")
  122. if not _tmp then --create temp canvas only if needed
  123. self:addCanvas({ name = "_tmp", private = true, shader = nil })
  124. _tmp = self:getCanvasTable("_tmp")
  125. end
  126. love.graphics.push()
  127. love.graphics.origin()
  128. local outputCanvas
  129. for i = 1, #shaders do
  130. local inputCanvas = i % 2 == 1 and canvas or _tmp.canvas
  131. outputCanvas = i % 2 == 0 and canvas or _tmp.canvas
  132. love.graphics.setCanvas(outputCanvas)
  133. love.graphics.clear()
  134. love.graphics.setShader(shaders[i])
  135. love.graphics.draw(inputCanvas)
  136. love.graphics.setCanvas(inputCanvas)
  137. end
  138. love.graphics.pop()
  139. love.graphics.setCanvas(_canvas)
  140. love.graphics.draw(outputCanvas)
  141. end
  142. love.graphics.setShader(_shader)
  143. end
  144. function push:finish(shader)
  145. love.graphics.setBackgroundColor(unpack(self._borderColor))
  146. if self._canvas then
  147. local _render = self:getCanvasTable("_render")
  148. love.graphics.pop()
  149. local white = love11 and 1 or 255
  150. love.graphics.setColor(white, white, white)
  151. --draw canvas
  152. love.graphics.setCanvas(_render.canvas)
  153. for i = 1, #self.canvases do --do not draw _render yet
  154. local _table = self.canvases[i]
  155. if not _table.private then
  156. local _canvas = _table.canvas
  157. local _shader = _table.shader
  158. self:applyShaders(_canvas, type(_shader) == "table" and _shader or { _shader })
  159. end
  160. end
  161. love.graphics.setCanvas()
  162. --draw render
  163. love.graphics.translate(self._OFFSET.x, self._OFFSET.y)
  164. local shader = shader or _render.shader
  165. love.graphics.push()
  166. love.graphics.scale(self._SCALE.x, self._SCALE.y)
  167. self:applyShaders(_render.canvas, type(shader) == "table" and shader or { shader })
  168. love.graphics.pop()
  169. --clear canvas
  170. for i = 1, #self.canvases do
  171. love.graphics.setCanvas(self.canvases[i].canvas)
  172. love.graphics.clear()
  173. end
  174. love.graphics.setCanvas()
  175. love.graphics.setShader()
  176. else
  177. love.graphics.pop()
  178. love.graphics.setScissor()
  179. end
  180. end
  181. function push:setBorderColor(color, g, b)
  182. self._borderColor = g and {color, g, b} or color
  183. end
  184. function push:toGame(x, y)
  185. x, y = x - self._OFFSET.x, y - self._OFFSET.y
  186. local normalX, normalY = x / self._GWIDTH, y / self._GHEIGHT
  187. x = (x >= 0 and x <= self._WWIDTH * self._SCALE.x) and normalX * self._WWIDTH or nil
  188. y = (y >= 0 and y <= self._WHEIGHT * self._SCALE.y) and normalY * self._WHEIGHT or nil
  189. return x, y
  190. end
  191. --doesn't work - TODO
  192. function push:toReal(x, y)
  193. return x + self._OFFSET.x, y + self._OFFSET.y
  194. end
  195. function push:switchFullscreen(winw, winh)
  196. self._fullscreen = not self._fullscreen
  197. local windowWidth, windowHeight = love.window.getDesktopDimensions()
  198. if self._fullscreen then --save windowed dimensions for later
  199. self._WINWIDTH, self._WINHEIGHT = self._RWIDTH, self._RHEIGHT
  200. elseif not self._WINWIDTH or not self._WINHEIGHT then
  201. self._WINWIDTH, self._WINHEIGHT = windowWidth * .5, windowHeight * .5
  202. end
  203. self._RWIDTH = self._fullscreen and windowWidth or winw or self._WINWIDTH
  204. self._RHEIGHT = self._fullscreen and windowHeight or winh or self._WINHEIGHT
  205. self:initValues()
  206. love.window.setFullscreen(self._fullscreen, "desktop")
  207. if not self._fullscreen and (winw or winh) then
  208. windowUpdateMode(self._RWIDTH, self._RHEIGHT) --set window dimensions
  209. end
  210. end
  211. function push:resize(w, h)
  212. if self._highdpi then w, h = w / self._PSCALE, h / self._PSCALE end
  213. self._RWIDTH = w
  214. self._RHEIGHT = h
  215. self:initValues()
  216. end
  217. function push:getWidth() return self._WWIDTH end
  218. function push:getHeight() return self._WHEIGHT end
  219. function push:getDimensions() return self._WWIDTH, self._WHEIGHT end
  220. return push