init.lua 13 KB

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