init.lua 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502
  1. local fudge_mt = {}
  2. local piece_mt = {}
  3. local anim_mt = {}
  4. local fudge = {}
  5. fudge.anim_prefix = "ani_"
  6. local old_draw = love.graphics.draw
  7. local monkey_draw
  8. local anim_draw = function(anim, frame, ...)
  9. monkey_draw(anim:getPiece(frame), ...)
  10. end
  11. monkey_draw = function(im, ...)
  12. if fudge.current and type(im)=="string" then
  13. --Get associate and draw it
  14. if im:sub(1,fudge.anim_prefix:len())==fudge.anim_prefix then
  15. monkey_draw(fudge.current:getAnimation(im), ...)
  16. else
  17. local fud = fudge.current:getPiece(im)
  18. old_draw(fud.img, fud.quad, ...)
  19. end
  20. elseif type(im)=="table" and im.img and im.quad then
  21. old_draw(im.img, im.quad, ...)
  22. elseif type(im)=="table" and im.batch then
  23. old_draw(im.batch)
  24. elseif type(im)=="table" and im.framerate then
  25. anim_draw(im, math.floor(love.timer.getTime()*im.framerate), ...)
  26. else
  27. old_draw(im, ...)
  28. end
  29. end
  30. local sortAreas = function(a, b)
  31. return (a.tex:getHeight()*a.tex:getWidth()) > (b.tex:getHeight()*b.tex:getWidth())
  32. end
  33. local sortMaxLengths = function(a, b)
  34. return math.max(a.tex:getHeight(),a.tex:getWidth()) > math.max(b.tex:getHeight(),b.tex:getWidth())
  35. end
  36. local sortWidths = function(a, b)
  37. return a.tex:getWidth() > b.tex:getWidth()
  38. end
  39. local sortHeights = function(a, b)
  40. return a.tex:getHeight() > b.tex:getHeight()
  41. end
  42. local split = function(str, sep)
  43. local sep, fields = sep or ":", {}
  44. local pattern = string.format("([^%s]+)", sep)
  45. str:gsub(pattern, function(c) fields[#fields+1] = c end)
  46. return fields
  47. end
  48. local AABB = function(l1, t1, r1, b1, l2, t2, r2, b2)
  49. if (
  50. t2>=b1 or
  51. l2>=r1 or
  52. t1>=b2 or
  53. l1>=r2
  54. ) then
  55. return false
  56. else
  57. return true
  58. end
  59. end
  60. local imAABB = function(i1, ox1, oy1, i2, ox2, oy2)
  61. return AABB(
  62. ox1,
  63. oy1,
  64. ox1+i1:getWidth(),
  65. oy1+i1:getHeight(),
  66. ox2,
  67. oy2,
  68. ox2+i2:getWidth(),
  69. oy2+i2:getHeight()
  70. )
  71. end
  72. local getAllImages = function(folder, options)
  73. local images = {}
  74. if love.filesystem.getIdentity():len()<1 then
  75. error("This project does not have an identity set. Please set \"t.identity\" in \"love.conf\" or use \"love.filesystem.setIdentity()\"")
  76. end
  77. local ignore = options.ignore or {}
  78. for i,v in ipairs(love.filesystem.getDirectoryItems(folder)) do
  79. local skip = false
  80. for _,ig in ipairs(ignore) do
  81. if string.find(v, ig) then
  82. skip = true
  83. break
  84. end
  85. end
  86. if not skip then
  87. table.insert(images, {
  88. name = split(v, ".")[1],
  89. tex = love.graphics.newImage(folder.."/"..v)
  90. })
  91. end
  92. --table.insert(images, love.graphics.newImage(folder.."/"..v))
  93. --table.insert(images, love.graphics.newImage(folder.."/"..v))
  94. end
  95. return images
  96. end
  97. local putrows = function(t, mx)
  98. local rows = {}
  99. local currentRow = 1
  100. local x = 0
  101. local y = 0
  102. for i,v in ipairs(t) do
  103. if x+v.tex:getWidth()>mx then
  104. x=0
  105. y=y+rows[currentRow][1].tex:getHeight()
  106. currentRow = currentRow+1
  107. end
  108. if not rows[currentRow] then
  109. rows[currentRow] = {}
  110. end
  111. table.insert(rows[currentRow],{
  112. tex = v,
  113. x=x,
  114. y=y
  115. })
  116. x = x+v.tex:getWidth()
  117. end
  118. return rows
  119. end
  120. local pack = function(t, mx)
  121. local tcop = {}
  122. for i,v in ipairs(t) do
  123. tcop[i] = v
  124. end
  125. local packed = {}
  126. local points = {
  127. {x = 0, y = 0}
  128. }
  129. local maxHeight = 0
  130. while #tcop>=1 do
  131. table.sort(points, function( a, b )
  132. if a.y==b.y then
  133. return a.x<b.x
  134. else
  135. return a.y<b.y
  136. end
  137. end)
  138. local im = tcop[1]
  139. local p = 1
  140. local placed = false
  141. while not placed do
  142. local collides = false
  143. local outofbounds = false
  144. if points[p].x + im.tex:getWidth() > mx then
  145. outofbounds = true
  146. end
  147. if not outofbounds then
  148. for i,v in ipairs(packed) do
  149. if imAABB(im.tex,points[p].x,points[p].y, v.tex, v.x, v.y) then
  150. collides = true
  151. end
  152. end
  153. end
  154. if outofbounds or collides then
  155. p=p+1
  156. else
  157. maxHeight = math.max(maxHeight, points[p].y+im.tex:getHeight())
  158. table.insert(packed,{
  159. tex = im.tex,
  160. name = im.name,
  161. x = points[p].x,
  162. y = points[p].y,
  163. w = im.tex:getWidth(),
  164. h = im.tex:getHeight()
  165. })
  166. ---[[
  167. table.insert(points,{
  168. x = points[p].x,
  169. y = points[p].y+im.tex:getHeight()
  170. })
  171. --]]
  172. ---[[
  173. table.insert(points, {
  174. x = points[p].x+im.tex:getWidth(),
  175. y = points[p].y
  176. })
  177. --]]
  178. local maxy = points[p].y+im.tex:getHeight()
  179. for i,v in ipairs(packed) do
  180. maxy = math.max(maxy, v.y+v.tex:getHeight())
  181. end
  182. ---[[
  183. table.insert(points, {
  184. x = 0,
  185. y = maxy
  186. })
  187. --]]
  188. table.remove(points, p)
  189. table.remove(tcop, 1)
  190. placed = true
  191. end
  192. end
  193. end
  194. return packed, maxHeight
  195. end
  196. function fudge.import(name)
  197. if love.filesystem.getIdentity():len()<1 then
  198. error("This project does not have an identity set. Please set \"t.identity\" in \"love.conf\" or use \"love.filesystem.setIdentity()\"")
  199. end
  200. local self = require(name)
  201. setmetatable(self, {__index=fudge_mt})
  202. for k,v in pairs(self.pieces) do
  203. setmetatable(v, {__index=piece_mt})
  204. end
  205. return self
  206. end
  207. function fudge.new(folder, options)
  208. local timeAtStart = love.timer.getTime()
  209. local options = options or {}
  210. local self = setmetatable({},{__index=fudge_mt})
  211. self.images = getAllImages(folder, options)
  212. if #self.images == 0 then
  213. print("[fudge] failed to create sprite atlas: folder '" .. folder .. "'' doesn't contain any images")
  214. return nil
  215. end
  216. ---[[
  217. local maxWidth = 0
  218. local area = 0
  219. for i,v in ipairs(self.images) do
  220. maxWidth = math.max(maxWidth, v.tex:getWidth())
  221. area = area + v.tex:getWidth()*v.tex:getHeight()
  222. end
  223. --]]
  224. local width = options.npot and maxWidth or 16
  225. while width<maxWidth do
  226. width = width * 2
  227. end
  228. local testWidths = options.npot and
  229. {
  230. width * 1,
  231. width * 2,
  232. width * 3,
  233. width * 4,
  234. width * 5,
  235. width * 6
  236. }
  237. or
  238. {
  239. width * 1,
  240. width * 2,
  241. width * 4,
  242. width * 8,
  243. width * 16,
  244. width * 32
  245. }
  246. local testSorts = {
  247. sortAreas,
  248. sortMaxLengths
  249. }
  250. local bestArea = math.huge
  251. local bestWidth = -1
  252. local bestSort = function() end
  253. local height = 0
  254. local maxHeight = 0
  255. for j,sortAlgo in ipairs(testSorts) do
  256. for i,w in ipairs(testWidths) do
  257. local h
  258. table.sort(self.images, sortAlgo)
  259. self.pack, h = pack(self.images, w)
  260. local h2 = 2
  261. while h2<h do
  262. h2 = h2 * 2
  263. end
  264. local comph = options.npot and h or h2
  265. if w*comph < bestArea then
  266. bestArea = w*comph
  267. bestWidth = w
  268. bestSort = sortAlgo
  269. end
  270. end
  271. end
  272. table.sort(self.images, bestSort)
  273. self.pack, maxHeight = pack(self.images, bestWidth)
  274. width = bestWidth
  275. if maxHeight>0 then
  276. height = options.npot and maxHeight or 16
  277. while height<maxHeight do
  278. height = height * 2
  279. end
  280. end
  281. self.width = width
  282. self.height = height
  283. self.canv = love.graphics.newCanvas(width, height)
  284. local old_cv = love.graphics.getCanvas()
  285. love.graphics.setCanvas(self.canv)
  286. love.graphics.push()
  287. love.graphics.origin()
  288. love.graphics.setColor(255,255,255,255)
  289. for i,v in ipairs(self.pack) do
  290. love.graphics.draw(v.tex,v.x,v.y)
  291. end
  292. love.graphics.pop()
  293. love.graphics.setCanvas(old_cv)
  294. self.image = love.graphics.newImage(self.canv:getImageData())
  295. self.pieces = {}
  296. for i,v in ipairs(self.pack) do
  297. self.pieces[v.name] = {
  298. img = self.image,
  299. quad = love.graphics.newQuad(v.x, v.y, v.w, v.h, width, height),
  300. w = v.w,
  301. h = v.h,
  302. x = v.x,
  303. y = v.y
  304. }
  305. setmetatable(self.pieces[v.name], {__index=piece_mt})
  306. end
  307. self.batch = love.graphics.newSpriteBatch(self.image, (options and options.batchSize or nil))
  308. self.canv = nil
  309. self.images = nil
  310. self.pack = nil
  311. self.area = area
  312. self.time = math.floor((love.timer.getTime()-timeAtStart)*100)/100
  313. self.anim = {}
  314. return self
  315. end
  316. function fudge.set(option, value)
  317. if type(option)=="table" then
  318. for k,v in pairs(option) do
  319. fudge.set(k, v)
  320. end
  321. return
  322. end
  323. ;({
  324. current = function(v)
  325. fudge.current = v
  326. end,
  327. monkey = function(v)
  328. if (v) then
  329. love.graphics.draw = monkey_draw
  330. else
  331. love.graphics.draw = old_draw
  332. end
  333. end,
  334. anim_prefix = function(v)
  335. local old_prefix = fudge.anim_prefix
  336. fudge.anim_prefix = v
  337. --[[
  338. Do prefix fixing here
  339. ]]
  340. end
  341. })[option](value)
  342. end
  343. fudge.draw = monkey_draw
  344. function fudge.addb(...)
  345. fudge.current:addb(...)
  346. end
  347. function fudge.setColorb(...)
  348. fudge.current:setColorb(...)
  349. end
  350. function fudge.setWhiteb()
  351. fudge.current:setWhiteb()
  352. end
  353. function fudge_mt:getPiece(name)
  354. if not self.pieces[name] then
  355. error("There is no piece named \""..name.."\"")
  356. return
  357. end
  358. return self.pieces[name]
  359. end
  360. function fudge_mt:getAnimation(name, frame)
  361. if frame then
  362. return self:getAnimation(name):getPiece(frame)
  363. else
  364. if not self.anim[name] then
  365. error("There is no animation named \""..name.."\"")
  366. end
  367. return self.anim[name]
  368. end
  369. end
  370. function fudge_mt:chopToAnimation(piecename, number, options)
  371. local options = options or {}
  372. local numlen = (""..number):len()
  373. local piece = self:getPiece(piecename)
  374. local stepsize = piece:getWidth()/number
  375. local animation = {}
  376. for i=1,number do
  377. self.pieces[piecename.."_"..string.format("%0"..numlen.."d", i)] = {
  378. img = self.image,
  379. quad = love.graphics.newQuad(
  380. piece.x+(i-1)*stepsize,
  381. piece.y,
  382. stepsize,
  383. piece.h,
  384. self.width,
  385. self.height)
  386. }
  387. table.insert(animation, piecename.."_"..string.format("%0"..numlen.."d", i))
  388. end
  389. self:animate((options.name or piecename), animation, options)
  390. end
  391. function fudge_mt:animate(name, frames, options)
  392. local options = options or {}
  393. local prefix = options.prefix or fudge.anim_prefix
  394. self.anim[prefix..name] = setmetatable({
  395. framerate = options.framerate or 10
  396. }, {__index = anim_mt})
  397. for i,v in ipairs(frames) do
  398. table.insert(self.anim[prefix..name], self:getPiece(v))
  399. end
  400. end
  401. function fudge_mt:addb(piece, ...)
  402. piece = type(piece)=="string" and self:getPiece(piece) or piece
  403. self.batch:add(piece.quad, ...)
  404. end
  405. function fudge_mt:clearb()
  406. self.batch:clear()
  407. end
  408. function fudge_mt:setColorb(r, g, b, a)
  409. self.batch:setColor(r, g, b, a)
  410. end
  411. function fudge_mt:setWhiteb(a)
  412. self.batch:setColor(255, 255, 255, a or 255)
  413. end
  414. function fudge_mt:setBlackb(a)
  415. self.batch:setColor(0, 0, 0, a or 255)
  416. end
  417. function fudge_mt:setImageFilter(...)
  418. self.image:setFilter(...)
  419. end
  420. function fudge_mt:export(name, options)
  421. if love.filesystem.getIdentity():len()<1 then
  422. error("This project does not have an identity set. Please set \"t.identity\" in \"love.conf\" or use \"love.filesystem.setIdentity()\"")
  423. end
  424. local options = options or {}
  425. local image_extension = options.image_extension or "png"
  426. self.image:getData():encode(name.."."..image_extension)
  427. local string = "local f = {"
  428. string = string.."width="..self.width..","
  429. string = string.."height="..self.height..","
  430. string = string.."image=love.graphics.newImage('"..name.."."..image_extension.."')}\n"
  431. string = string.."f.batch=love.graphics.newSpriteBatch(f.image, "..self.batch:getBufferSize()..")\n"
  432. string = string.."f.pieces = {}\n"
  433. for k,v in pairs(self.pieces) do
  434. string = string.."f.pieces['"..k.."']={"
  435. string = string.."img=f.image,"
  436. string = string.."quad=love.graphics.newQuad("..v.x..","..v.y..","..v.w..","..v.h..","..self.width..","..self.height.."),"
  437. string = string.."x="..v.x..","
  438. string = string.."y="..v.y..","
  439. string = string.."w="..v.w..","
  440. string = string.."h="..v.h.."}\n"
  441. end
  442. string = string.."f.anim = {}\n"
  443. string = string.."return f"
  444. love.filesystem.write(name..".lua", string)
  445. end
  446. function fudge_mt:rename(old, new)
  447. if self.pieces[new] then
  448. error("There is already a piece with name: \""..new.."\".")
  449. return
  450. end
  451. if not self.pieces[old] then
  452. error("There is no piece named \""..old.."\" to rename")
  453. return
  454. end
  455. self.pieces[new], self.pieces[old] = self.pieces[old], nil
  456. end
  457. function piece_mt:getWidth()
  458. return self.w
  459. end
  460. function piece_mt:getHeight()
  461. return self.h
  462. end
  463. function anim_mt:getPiece(frame)
  464. return self[((frame-1)%#self)+1]
  465. end
  466. return fudge