init.lua 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542
  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 sortWidthThenHeight = function( a, b )
  43. if a.tex:getWidth() == b.tex:getWidth() then
  44. return a.tex:getHeight()>b.tex:getHeight()
  45. else
  46. return a.tex:getWidth() > b.tex:getWidth()
  47. end
  48. end
  49. local split = function(str, sep)
  50. local sep, fields = sep or ":", {}
  51. local pattern = string.format("([^%s]+)", sep)
  52. str:gsub(pattern, function(c) fields[#fields+1] = c end)
  53. return fields
  54. end
  55. local AABB = function(l1, t1, r1, b1, l2, t2, r2, b2)
  56. if (
  57. t2>=b1 or
  58. l2>=r1 or
  59. t1>=b2 or
  60. l1>=r2
  61. ) then
  62. return false
  63. else
  64. return true
  65. end
  66. end
  67. local imAABB = function(i1, ox1, oy1, i2, ox2, oy2)
  68. return AABB(
  69. ox1,
  70. oy1,
  71. ox1+i1:getWidth(),
  72. oy1+i1:getHeight(),
  73. ox2,
  74. oy2,
  75. ox2+i2:getWidth(),
  76. oy2+i2:getHeight()
  77. )
  78. end
  79. local supportedImageFormats = { "png", "bmp", "jpg", "jpeg", "tga" } -- format support by Love2d
  80. function isSupportedImageFormat(ext)
  81. for _, supportedExt in ipairs(supportedImageFormats) do
  82. if ext == supportedExt then
  83. return true
  84. end
  85. end
  86. return false
  87. end
  88. local getAllImages = function(folder)
  89. local images = {}
  90. if love.filesystem.getIdentity():len()<1 then
  91. error("This project does not have an identity set. Please set \"t.identity\" in \"love.conf\" or use \"love.filesystem.setIdentity()\"")
  92. end
  93. for i,v in ipairs(love.filesystem.getDirectoryItems(folder)) do
  94. local split = split(v, ".")
  95. if isSupportedImageFormat(split[2]) then
  96. table.insert(images, {
  97. name = split[1],
  98. tex = love.graphics.newImage(folder.."/"..v)
  99. })
  100. end
  101. end
  102. return images
  103. end
  104. local putrows = function(t, mx)
  105. local rows = {}
  106. local currentRow = 1
  107. local x = 0
  108. local y = 0
  109. for i,v in ipairs(t) do
  110. if x+v.tex:getWidth()>mx then
  111. x=0
  112. y=y+rows[currentRow][1].tex:getHeight()
  113. currentRow = currentRow+1
  114. end
  115. if not rows[currentRow] then
  116. rows[currentRow] = {}
  117. end
  118. table.insert(rows[currentRow],{
  119. tex = v,
  120. x=x,
  121. y=y
  122. })
  123. x = x+v.tex:getWidth()
  124. end
  125. return rows
  126. end
  127. local nsert
  128. nsert = function(node, img)
  129. --print("nsert")
  130. if #node.child>0 then
  131. --print("children")
  132. --print(node.tagged)
  133. local cand = nsert(node.child[1], img)
  134. if cand then
  135. --print("We good")
  136. return cand
  137. end
  138. --print("Could be a problem...")
  139. return nsert(node.child[2], img)
  140. else
  141. if node.tagged then
  142. --print("nope, tagged")
  143. return
  144. end
  145. if img.tex:getWidth()>node.rect.w or img.tex:getHeight()>node.rect.h then
  146. --print("nope, too small",img.tex:getWidth(),img.tex:getHeight(),node.rect.w,node.rect.h)
  147. return
  148. end
  149. if img.tex:getWidth()==node.rect.w and img.tex:getHeight()==node.rect.h then
  150. --print("perfect, tag it 'n' bag it")
  151. node.tagged = true
  152. return node
  153. end
  154. --print("splitty splitty")
  155. node.child[1] = {child = {}}
  156. node.child[2] = {child = {}}
  157. local dw = node.rect.w - img.tex:getWidth()
  158. local dh = node.rect.h - img.tex:getHeight()
  159. if dw>dh then
  160. node.child[1].rect = {
  161. parent = node,
  162. x = node.rect.x,
  163. y = node.rect.y,
  164. w = img.tex:getWidth(),
  165. h = node.rect.h
  166. }
  167. node.child[2].rect = {
  168. parent = node,
  169. x = node.rect.x+img.tex:getWidth(),
  170. y = node.rect.y,
  171. w = node.rect.w-img.tex:getWidth(),
  172. h = node.rect.h
  173. }
  174. else
  175. node.child[1].rect = {
  176. parent = node,
  177. x = node.rect.x,
  178. y = node.rect.y,
  179. w = node.rect.w,
  180. h = img.tex:getHeight()
  181. }
  182. node.child[2].rect = {
  183. parent = node,
  184. x = node.rect.x,
  185. y = node.rect.y+img.tex:getHeight(),
  186. w = node.rect.w,
  187. h = node.rect.h-img.tex:getHeight()
  188. }
  189. end
  190. return nsert(node.child[1], img)
  191. end
  192. end
  193. local npack = function(t, mx, my)
  194. local root = {
  195. child = {},
  196. rect = {x = 0, y = 0, w = mx, h = my}
  197. }
  198. local id = 0
  199. local packed = {}
  200. -- tcop is a copy of t
  201. local tcop = {}
  202. for i,v in ipairs(t) do
  203. tcop[i] = v
  204. end
  205. while #tcop>0 do
  206. local img = tcop[1]
  207. table.remove(tcop, 1)
  208. local pnode = nsert(root, img)
  209. if pnode then
  210. table.insert(packed,
  211. {
  212. tex = img.tex,
  213. name = img.name,
  214. x = pnode.rect.x,
  215. y = pnode.rect.y,
  216. w = pnode.rect.w,
  217. h = pnode.rect.h
  218. })
  219. else
  220. print("Just "..#tcop.." images left!")
  221. local cnv = love.graphics.newCanvas(4096,4096)
  222. love.graphics.setCanvas(cnv)
  223. for i,v in ipairs(packed) do
  224. love.graphics.setColor(0,0,0)
  225. love.graphics.rectangle("line",v.x,v.y,v.w,v.h)
  226. love.graphics.setColor(255,255,255)
  227. love.graphics.draw(v.tex,v.x,v.y)
  228. end
  229. local image = love.graphics.newImage(cnv:getImageData())
  230. image:getData():encode("FUCK.png")
  231. error("PANIC")
  232. end
  233. end
  234. return packed
  235. end
  236. function fudge.import(name)
  237. if love.filesystem.getIdentity():len()<1 then
  238. error("This project does not have an identity set. Please set \"t.identity\" in \"love.conf\" or use \"love.filesystem.setIdentity()\"")
  239. end
  240. -- the 'name' may contain a path
  241. local filepath = ""
  242. local index = string.find(name, "/[^/]*$")
  243. if index then
  244. filepath = string.sub(name, 1, index)
  245. end
  246. local do_load_batch = require(name)
  247. local self = do_load_batch(filepath)
  248. setmetatable(self, {__index=fudge_mt})
  249. for k,v in pairs(self.pieces) do
  250. setmetatable(v, {__index=piece_mt})
  251. end
  252. return self
  253. end
  254. local logfn = function() end
  255. --[[TODO: This code is cancerous]]
  256. function fudge.new(folder, options)
  257. local timeAtStart = love.timer.getTime()
  258. local options = options or {}
  259. local self = setmetatable({},{__index=fudge_mt})
  260. local log = options.log or logfn
  261. self.images = getAllImages(folder)
  262. if #self.images == 0 then
  263. log("[fudge] failed to create sprite atlas: folder '" .. folder .. "'' doesn't contain any images")
  264. return nil
  265. end
  266. if options.preprocess_images then
  267. for _, preprocess in ipairs(options.preprocess_images) do
  268. preprocess(self.images)
  269. end
  270. end
  271. ---[[
  272. local maxWidth = 0
  273. local area = 0
  274. for i,v in ipairs(self.images) do
  275. maxWidth = math.max(maxWidth, v.tex:getWidth())
  276. area = area + v.tex:getWidth()*v.tex:getHeight()
  277. end
  278. --]]
  279. local width = options.npot and maxWidth or 16
  280. while width<maxWidth do
  281. width = width * 2
  282. end
  283. table.sort(self.images, sortMaxLengths)
  284. self.pack, maxHeight = npack(self.images, 4096, 4096)
  285. width = 4096
  286. height = 4096
  287. self.width = width
  288. self.height = height
  289. log("Atlas will be size", width, height)
  290. self.canv = love.graphics.newCanvas(width, height)
  291. local old_cv = love.graphics.getCanvas()
  292. love.graphics.setCanvas(self.canv)
  293. love.graphics.push()
  294. love.graphics.origin()
  295. love.graphics.setColor(255,255,255,255)
  296. for i,v in ipairs(self.pack) do
  297. log("Adding image", i, "/", #self.pack, "to atlas")
  298. love.graphics.draw(v.tex,v.x,v.y)
  299. end
  300. love.graphics.pop()
  301. log("Adding image", i, "/", #self.pack, "to atlas")
  302. love.graphics.setCanvas(old_cv)
  303. self.image = love.graphics.newImage(self.canv:getImageData())
  304. self.pieces = {}
  305. log("Saving piece quads in table")
  306. for i,v in ipairs(self.pack) do
  307. log("Saving quad for", v.name)
  308. self.pieces[v.name] = {
  309. img = self.image,
  310. quad = love.graphics.newQuad(v.x, v.y, v.w, v.h, width, height),
  311. w = v.w,
  312. h = v.h,
  313. x = v.x,
  314. y = v.y
  315. }
  316. setmetatable(self.pieces[v.name], {__index=piece_mt})
  317. end
  318. self.batch = love.graphics.newSpriteBatch(self.image, (options and options.batchSize or nil))
  319. self.canv = nil
  320. self.images = nil
  321. self.pack = nil
  322. self.area = area
  323. self.time = math.floor((love.timer.getTime()-timeAtStart)*100)/100
  324. self.anim = {}
  325. return self
  326. end
  327. function fudge.set(option, value)
  328. if type(option)=="table" then
  329. for k,v in pairs(option) do
  330. fudge.set(k, v)
  331. end
  332. return
  333. end
  334. ;({
  335. current = function(v)
  336. fudge.current = v
  337. end,
  338. monkey = function(v)
  339. if (v) then
  340. love.graphics.draw = monkey_draw
  341. else
  342. love.graphics.draw = old_draw
  343. end
  344. end,
  345. anim_prefix = function(v)
  346. local old_prefix = fudge.anim_prefix
  347. fudge.anim_prefix = v
  348. --[[
  349. Do prefix fixing here
  350. ]]
  351. end
  352. })[option](value)
  353. end
  354. fudge.draw = monkey_draw
  355. function fudge.addb(...)
  356. fudge.current:addb(...)
  357. end
  358. function fudge.setColorb(...)
  359. fudge.current:setColorb(...)
  360. end
  361. function fudge.setWhiteb()
  362. fudge.current:setWhiteb()
  363. end
  364. function fudge_mt:getPiece(name)
  365. if not self.pieces[name] then
  366. error("There is no piece named \""..name.."\"")
  367. return
  368. end
  369. return self.pieces[name]
  370. end
  371. function fudge_mt:getAnimation(name, frame)
  372. if frame then
  373. return self:getAnimation(name):getPiece(frame)
  374. else
  375. if not self.anim[name] then
  376. error("There is no animation named \""..name.."\"")
  377. end
  378. return self.anim[name]
  379. end
  380. end
  381. function fudge_mt:chopToAnimation(piecename, number, options)
  382. local options = options or {}
  383. local numlen = (""..number):len()
  384. local piece = self:getPiece(piecename)
  385. local stepsize = piece:getWidth()/number
  386. local animation = {}
  387. for i=1,number do
  388. self.pieces[piecename.."_"..string.format("%0"..numlen.."d", i)] = {
  389. img = self.image,
  390. quad = love.graphics.newQuad(
  391. piece.x+(i-1)*stepsize,
  392. piece.y,
  393. stepsize,
  394. piece.h,
  395. self.width,
  396. self.height)
  397. }
  398. table.insert(animation, piecename.."_"..string.format("%0"..numlen.."d", i))
  399. end
  400. self:animate((options.name or piecename), animation, options)
  401. end
  402. function fudge_mt:animate(name, frames, options)
  403. local options = options or {}
  404. local prefix = options.prefix or fudge.anim_prefix
  405. self.anim[prefix..name] = setmetatable({
  406. framerate = options.framerate or 10
  407. }, {__index = anim_mt})
  408. for i,v in ipairs(frames) do
  409. table.insert(self.anim[prefix..name], self:getPiece(v))
  410. end
  411. end
  412. function fudge_mt:addb(piece, ...)
  413. piece = type(piece)=="string" and self:getPiece(piece) or piece
  414. self.batch:add(piece.quad, ...)
  415. end
  416. function fudge_mt:addb_centered(piece, x, y, r)
  417. piece = type(piece)=="string" and self:getPiece(piece) or piece
  418. local q = piece.quad
  419. local _, __, w, h = q:getViewport()
  420. self.batch:add(q, x, y, r, 1, 1, w*0.5, h*0.5)
  421. end
  422. function fudge_mt:clearb()
  423. self.batch:clear()
  424. end
  425. function fudge_mt:setColorb(r, g, b, a)
  426. self.batch:setColor(r, g, b, a)
  427. end
  428. function fudge_mt:setWhiteb(a)
  429. self.batch:setColor(255, 255, 255, a or 255)
  430. end
  431. function fudge_mt:setBlackb(a)
  432. self.batch:setColor(0, 0, 0, a or 255)
  433. end
  434. function fudge_mt:setImageFilter(...)
  435. self.image:setFilter(...)
  436. end
  437. function fudge_mt:export(name, options)
  438. if love.filesystem.getIdentity():len()<1 then
  439. error("This project does not have an identity set. Please set \"t.identity\" in \"love.conf\" or use \"love.filesystem.setIdentity()\"")
  440. end
  441. local options = options or {}
  442. local image_extension = options.image_extension or "png"
  443. self.image:getData():encode(name.."."..image_extension)
  444. string = "return function(path)\n"
  445. string = string.. "local file = (path or \"\").. \""..name.."."..image_extension.."\"\n"
  446. string = string.."local f = {"
  447. string = string.."width="..self.width..","
  448. string = string.."height="..self.height..","
  449. string = string.."image=love.graphics.newImage(file)}\n"
  450. string = string.."f.batch=love.graphics.newSpriteBatch(f.image, "..self.batch:getBufferSize()..")\n"
  451. string = string.."f.pieces = {}\n"
  452. for k,v in pairs(self.pieces) do
  453. string = string.."f.pieces['"..k.."']={"
  454. string = string.."img=f.image,"
  455. string = string.."quad=love.graphics.newQuad("..v.x..","..v.y..","..v.w..","..v.h..","..self.width..","..self.height.."),"
  456. string = string.."x="..v.x..","
  457. string = string.."y="..v.y..","
  458. string = string.."w="..v.w..","
  459. string = string.."h="..v.h.."}\n"
  460. end
  461. string = string.."f.anim = {}\n"
  462. string = string.."return f\n"
  463. string = string.."end"
  464. love.filesystem.write(name..".lua", string)
  465. end
  466. function fudge_mt:rename(old, new)
  467. if self.pieces[new] then
  468. error("There is already a piece with name: \""..new.."\".")
  469. return
  470. end
  471. if not self.pieces[old] then
  472. error("There is no piece named \""..old.."\" to rename")
  473. return
  474. end
  475. self.pieces[new], self.pieces[old] = self.pieces[old], nil
  476. end
  477. function piece_mt:getWidth()
  478. return self.w
  479. end
  480. function piece_mt:getHeight()
  481. return self.h
  482. end
  483. function anim_mt:getPiece(frame)
  484. return self[((frame-1)%#self)+1]
  485. end
  486. return fudge