-
Notifications
You must be signed in to change notification settings - Fork 2
/
main.lua
385 lines (351 loc) · 12.6 KB
/
main.lua
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
love.event.quit()
local nfs = require("NFS.nativefs")
local lfs = love.filesystem
local args = require("args")
local getExtension = function(path) return path:match("^.+%.(.+)$") end
local getFileName = function(path) return path:gsub("\\", "/"):match("([^/]+)%..+$") end
local getFullFile = function(path) return path:gsub("\\", "/"):match("([^/]+)$") end
local inputDirs = { args[1] }
if args[1] then
assert(nfs.getInfo(args[1], "directory"), "Given input directory does not exist: "..args[1])
end
if type(args["-input"]) == "table" then
for index, path in ipairs(args["-input"]) do
table.insert(inputDirs, path)
assert(nfs.getInfo(path, "directory"), "Given input directory at "..tostring(index).." does not exist: "..path)
end
end
assert(#inputDirs > 0, "Requires input directory paths either as the first parameter, or using the -input flag")
local outputDirs = {}
if args[2] then
local path, info = args[2], { path=args[2] }
local lastChar, extension = path:sub(-1), getExtension(path)
if not extension then
info.type = "directory"
if lastChar == "/" or lastChar == "\\" then
info.path = path:sub(1, #path-1)
end
assert(nfs.createDirectory(path), "Could not make output directory at 2nd parameter: "..path)
else
info.type = "file"
local fileName = getFileName(path) or getFullFile(path)
local chars = #fileName + (extension and #extension+1 or 0)
info.directory = path:sub(1, #path-chars-1)
end
outputDirs[1] = info
end
if type(args["-output"]) == "table" then
for index, path in ipairs(args["-output"]) do
local info = { path=path }
local lastChar, extension = path:sub(-1), getExtension(path)
if not extension then
info.type = "directory"
if lastChar == "/" or lastChar == "\\" then
info.path = path:sub(1, #path-1)
end
assert(nfs.createDirectory(path), "Could not make output directory at 2nd parameter: "..path)
else
info.type = "file"
local fileName = getFileName(path) or getFullFile(path)
local chars = #fileName + (extension and #extension+1 or 0)
info.directory = path:sub(1, #path-chars-1)
end
table.insert(outputDirs, info)
end
end
assert(#outputDirs > 0, "Requires output directory paths as the second parameter, or using the -output flag")
if #outputDirs == 1 then
outputDirs = outputDirs[1]
if #inputDirs > 1 then
assert(outputDirs.type == "directory", "As there are multiple defined input directories, you cannot have a file path as the single output directory")
end
else
assert(#outputDirs == #inputDirs, "Mismatched number of input and output directories given: "..#inputDirs.." Inputs, "..#outputDirs.." Outputs")
end
local outputDataDirs = {}
if type(args["-dataOutput"]) == "table" then
for index, path in ipairs(args["-dataOutput"]) do
local info = {path = path}
local lastChar, extension = path:sub(-1), getExtension(path)
if not extension then
info.type = "directory"
if lastChar == "/" or lastChar == "\\" then
info.path = path:sub(1, #path-1)
end
assert(nfs.createDirectory(path), "Could not make output directory at 2nd parameter: "..path)
else
info.type = "file"
local fileName = getFileName(path) or getFullFile(path)
local chars = #fileName + (extension and #extension+1 or 0)
info.directory = path:sub(1, #path-chars-1)
end
table.insert(outputDataDirs, info)
end
else
outputDataDirs = nil
end
if outputDataDirs then
if #outputDataDirs == 1 then
outputDataDirs = outputDataDirs[1]
if #outputDirs > 1 then
assert(outputDataDirs.type == "directory", "As there are multuple defined output directories, you cannot have a file path as the single data output directory")
end
else
assert(#outputDataDirs == #outputDirs, "Mismtached number of output and data output directories given: "..#outputDirs.." Outputs, "..#outputDataDirs.." Data Outputs")
end
end
local padding = 1
if type(args["-padding"]) == "table" then
padding = tonumber(args["-padding"][1])
assert(padding ~= nil, "Given value to -padding <num> could not be converted to a number. Gave: \""..tostring(args["-padding"][1]).."\"")
end
local extrude = 0
if type(args["-extrude"]) == "table" then
extrude = tonumber(args["-extrude"][1])
assert(extrude ~= nil, "Given value to -extrude <num> could not be converted to a number. Gave:\""..tostring(args["-extrude"][1]).."\"")
end
local spacing = 0
if type(args["-spacing"]) == "table" then
spacing = tonumber(args["-spacing"][1])
assert(spacing ~= nil, "Given value to -spacing <num> could not be converted to a number. Gave:\""..tostring(args["-spacing"][1]).."\"")
end
local processPaths = function(paths)
for index, path in ipairs(paths) do
local firstchar, lastchar = path:sub(1,1), path:sub(-1)
local info = {
raw = path,
subDirSearch = firstchar ~= "."
}
info.type = (lastchar == "/" or lastchar == "\\") and "directory" or "file"
if info.type == "directory" then
local index = 1
if info.subDirSearch and (firstchar == "/" or firstchar == "\\") then
index = 2
elseif not info.subDirSearch then
index = 3
end
info.path = path:sub(index, #path-1)
-- "./dir/" -> "dir", "/foo/bar/" -> "foo/bar", "foo/bar/" -> "foo/bar"
else -- info.type == "file"
info.extension = getExtension(path)
info.fileName = getFileName(path:sub(info.subDirSearch and ((firstchar == "/" or firstchar == "\\") and 2 or 1) or 3))
info.path = path:sub(info.subDirSearch and 1 or 3, #path):match("^(.+/)"..info.fileName.."."..info.extension.."$")
if info.fileName == "*" and info.extension == "*" then
error("Cannot process two wildcards for both fileName and extension. Have you tried just doing: [.]/"..tostring(info.path))
elseif info.fileName == "*" then
info.wildcard = "filename"
elseif info.extension == "*" then
info.wildcard = "extension"
end
end
paths[index] = info
end
end
if type(args["-ignore"]) == "table" then
processPaths(args["-ignore"])
end
local checkDirectoryIgnore = function(path, directoryName)
if args["-ignore"] then
for _, info in ipairs(args["-ignore"]) do
if info.type == "directory" then
if info.subDirSearch then
if directoryName == info.path then
return false
end
elseif path == info.path then
return false
end
end
end
end
return true
end
local checkFileIgnore = function(path, fileName)
if args["-ignore"] then
for _, info in ipairs(args["-ignore"]) do
if info.type == "file" then
if info.wildcard == "extension" then
if getFileName(fileName) == info.fileName then
return false
end
elseif info.wildcard == "filename" then
if path == info.path and getExtension(fileName) == info.extension then
return false
end
elseif (info.raw):match(info.path .. fileName) then
return false
end
end
end
end
return true
end
local iterateDirectory
iterateDirectory = function(dir, path, callback)
local items = lfs.getDirectoryItems(dir)
for _, item in ipairs(items) do
local loc = dir.."/"..item
local infoType = lfs.getInfo(loc).type
if infoType == "directory" and checkDirectoryIgnore(path..item, item) then
iterateDirectory(loc, (path..item.."/"), callback)
elseif infoType == "file" and checkFileIgnore(path, item) then
callback(loc, path..item)
end
end
end
local supportedExtensions = {
["jpeg"] = true,
["jpg"] = true,
["png"] = true,
["bmp"] = true,
["tga"] = true,
["hdr"] = true,
["pic"] = true,
["exr"] = true,
}
local loadImage = function(location)
local extension = getExtension(location)
if not extension or not supportedExtensions[extension:lower()] then
return
end
local success, result = pcall(love.image.newImageData, location)
assert(success, "Unable to load image: "..tostring(location)..", Reason:"..tostring(result))
return result
end
local lustache = require("lustache.lustache")
local defaultTemplate = [[
return {
quads = {
{{#quads}}
["{{{id}}}"] = {
x = {{x}},
y = {{y}},
w = {{w}},
h = {{h}}
}{{^last}},{{/last}}
{{/quads}}
},
meta = {
padding = {{meta.padding}},
extrude = {{meta.extrude}},
atlasWidth = {{meta.width}},
atlasHeight = {{meta.height}},
quadCount = {{meta.quadCount}}{{#meta.fixedSize}},
fixedSize = {
width = {{width}},
height = {{height}},
}
{{/meta.fixedSize}}
{{^meta.fixedSize}}
{{/meta.fixedSize}}
}
}
]]
local template, extension = defaultTemplate, "lua"
if type(args["-template"]) == "table" then
local contents, errorMessage = nfs.read(args["-template"][1])
assert(contents, "Unable to read "..tostring(args["-template"][1])..", Reason: "..tostring(errorMessage))
template = contents
extension = getExtension(args["-template"][1])
end
for inputIndex, inputDir in ipairs(inputDirs) do
assert(nfs.mount(inputDir, "in", false), "Unable to mount input directory: "..tostring(inputDir))
local atlas
if args["-fixedSize"] then
assert(type(args["-fixedSize"]) == "table", "Arg -fixedSize requires at least one dimension. If height isn't given, it will be the same as width. -fixedSize W <H>")
atlas = require("RTA").newFixedSize(
tonumber(args["-fixedSize"][1]),
tonumber(args["-fixedSize"][2]) or tonumber(args["-fixedSize"][1]),
padding, extrude, spacing)
else
atlas = require("RTA").newDynamicSize(padding, extrude, spacing)
end
if type(args["-maxSize"]) == "table" then
local width = args["-maxSize"][1]
local height = args["-maxSize"][2]
if width then
width = tonumber(width)
assert(width, "Arg -maxSize's width could not be converted to type number")
assert(width > 1, "Arg -maxSize's width is less than 1, invalid size")
end
if height then
height = tonumber(height)
assert(height, "Arg -maxSize's height could not be converted to type number")
assert(height > 1, "Arg -maxSize's height is less than 1, invalid size")
end
atlas:setMaxSize(width, height)
end
if args["-pow2"] then
atlas:setBakeAsPow2(true)
end
iterateDirectory("in", "", function(location, localPath)
if args["-removeFileExtension"] then
local extension = getExtension(location)
localPath = localPath:sub(1, (#localPath)-(#extension+1))
end
local image = loadImage(location)
if image then
atlas:add(image, localPath)
end
end)
assert(nfs.unmount(inputDir), "Unable to unmount input directory: "..tostring(inputDir))
if atlas.imagesSize < 1 then
error("No images have been added to the atlas: "..tostring(atlas.imagesSize))
end
local _, imageData = atlas:hardBake()
assert(imageData, "Fatal error, atlas was baked before reaching this stage")
local output = #outputDirs > 0 and outputDirs[inputIndex] or outputDirs
local dataOutput
if outputDataDirs then
dataOutput = #outputDataDirs > 0 and outputDataDirs[inputIndex] or outputDataDirs
end
local atlasPath
if output.type == "file" then
atlasPath = output.path
else
atlasPath = output.path.."/atlas"..(#inputDirs > 1 and tostring(inputIndex) or "")..".png"
end
local fileData = imageData:encode("png") -- Could not use 2nd arg, probably due to mounting via nfs than lfs
local success, errorMessage = nfs.write(atlasPath, fileData:getString())
assert(success, "Unable to write atlas.png, Reason: "..tostring(errorMessage))
local quads = {}
for id, lovequad in pairs(atlas.quads) do
local quad = {}
quad.id = id
quad.x, quad.y, quad.w, quad.h = lovequad[1], lovequad[2], lovequad[3], lovequad[4]
table.insert(quads, quad)
end
quads[#quads].last = true
local meta = {
padding = atlas.padding,
extrude = atlas.extrude,
width = atlas.image:getWidth(),
height = atlas.image:getHeight(),
quadCount = #quads,
}
if args["-fixedSize"] then
meta.fixedSize = {
width = atlas.width,
height = atlas.height,
}
end
local dataPath
if not dataOutput then
dataPath = output.path
if output.type == "file" then
dataPath = output.directory
end
dataPath = dataPath.."/data"..(#inputDirs > 1 and tostring(inputIndex) or "").."."..extension
else
if dataOutput.type == "file" then
dataPath = dataOutput.path
else
dataPath = dataOutput.path.."/data"..(#inputDirs > 1 and tostring(inputIndex) or "").."."..extension
end
end
local success, errorMessage = nfs.write(dataPath, lustache:render(template, {
quads = quads,
meta = meta,
}))
assert(success, "Could not write data file out: "..tostring(errorMessage))
end