229 lines
6.2 KiB
JavaScript
229 lines
6.2 KiB
JavaScript
var graphics = {}
|
|
var os = use('os')
|
|
var io = use('io')
|
|
var res = use('resources')
|
|
|
|
function calc_image_size(img)
|
|
{
|
|
if (!img.texture || !img.rect) return;
|
|
return [img.texture.width*img.rect.width, img.texture.height*img.rect.height];
|
|
}
|
|
|
|
function create_image(path)
|
|
{
|
|
var data = io.slurpbytes(path);
|
|
var newimg;
|
|
switch(path.ext()) {
|
|
case 'gif':
|
|
newimg = os.make_gif(data);
|
|
if (newimg.surface)
|
|
newimg.texture = prosperon.gpu.load_texture(newimg.surface);
|
|
else
|
|
for (var frame of newimg.frames)
|
|
frame.texture = prosperon.gpu.load_texture(frame.surface);
|
|
break;
|
|
case 'ase':
|
|
case 'aseprite':
|
|
newimg = os.make_aseprite(data);
|
|
if (newimg.surface)
|
|
newimg.texture = prosperon.gpu.load_texture(newimg.surface);
|
|
else {
|
|
for (var anim in newimg) {
|
|
var a = newimg[anim];
|
|
for (var frame of a.frames)
|
|
frame.texture = prosperon.gpu.load_texture(frame.surface);
|
|
}
|
|
}
|
|
break;
|
|
default:
|
|
newimg = {
|
|
surface: os.make_texture(data)
|
|
};
|
|
newimg.texture = prosperon.gpu.load_texture(newimg.surface);
|
|
break;
|
|
}
|
|
return newimg;
|
|
}
|
|
|
|
var image = {};
|
|
image.dimensions = function()
|
|
{
|
|
return [this.texture.width, this.texture.height].scale([this.rect[2], this.rect[3]]);
|
|
}
|
|
|
|
var spritesheet;
|
|
var sheet_frames = [];
|
|
var sheetsize = 1024;
|
|
|
|
function pack_into_sheet(images)
|
|
{
|
|
return;
|
|
if (!Array.isArray(images)) images = [images];
|
|
if (images[0].texture.width > 300 && images[0].texture.height > 300) return;
|
|
sheet_frames = sheet_frames.concat(images);
|
|
var sizes = sheet_frames.map(x => [x.rect.width*x.texture.width, x.rect.height*x.texture.height]);
|
|
var pos = os.rectpack(sheetsize, sheetsize, sizes);
|
|
if (!pos) {
|
|
console.error(`did not make spritesheet properly from images ${images}`);
|
|
console.info(sizes);
|
|
return;
|
|
}
|
|
|
|
var newsheet = os.make_tex_data(sheetsize,sheetsize);
|
|
|
|
for (var i = 0; i < pos.length; i++) {
|
|
// Copy the texture to the new sheet
|
|
newsheet.copy(sheet_frames[i].texture, pos[i], sheet_frames[i].rect);
|
|
|
|
// Update the frame's rect to the new position in normalized coordinates
|
|
sheet_frames[i].rect.x = pos[i][0] / newsheet.width;
|
|
sheet_frames[i].rect.y = pos[i][1] / newsheet.height;
|
|
sheet_frames[i].rect.width = sizes[i][0] / newsheet.width;
|
|
sheet_frames[i].rect.height = sizes[i][1] / newsheet.height;
|
|
sheet_frames[i].texture = newsheet;
|
|
}
|
|
|
|
newsheet.load_gpu();
|
|
spritesheet = newsheet;
|
|
return spritesheet;
|
|
}
|
|
|
|
graphics.is_image = function(obj)
|
|
{
|
|
if (obj.texture && obj.rect) return true;
|
|
}
|
|
|
|
// Any request to it returns an image, which is a texture and rect.
|
|
graphics.texture = function texture(path) {
|
|
if (typeof path !== 'string') {
|
|
return path;
|
|
throw new Error('need a string for graphics.texture')
|
|
}
|
|
var parts = path.split(':');
|
|
var ipath = res.find_image(parts[0]);
|
|
|
|
graphics.texture.cache[ipath] ??= create_image(ipath);
|
|
return graphics.texture.cache[ipath];
|
|
}
|
|
|
|
graphics.texture.cache = {};
|
|
graphics.texture.time_cache = {};
|
|
|
|
graphics.texture.total_size = function()
|
|
{
|
|
var size = 0;
|
|
// Object.values(graphics.texture.cache).forEach(x => size += x.texture.inram() ? x..texture.width*x.texture.height*4 : 0);
|
|
return size;
|
|
}
|
|
|
|
graphics.texture.total_vram = function()
|
|
{
|
|
var vram = 0;
|
|
// Object.values(graphics.texture.cache).forEach(x => vram += x.vram);
|
|
return vram;
|
|
}
|
|
|
|
function merge_objects(ov,nv,arr)
|
|
{
|
|
arr.forEach(x => ov[x] = nv[x])
|
|
}
|
|
|
|
graphics.tex_hotreload = function tex_hotreload(file) {
|
|
console.log(`hot reloading ${file}`)
|
|
if (!(file in graphics.texture.cache)) return;
|
|
console.log('really doing it')
|
|
|
|
var img = create_image(file);
|
|
var oldimg = graphics.texture.cache[file];
|
|
console.log(`new image:${json.encode(img)}`)
|
|
console.log(`old image: ${json.encode(oldimg)}`)
|
|
|
|
merge_objects(oldimg,img, ['surface', 'texture', 'loop', 'time']);
|
|
// graphics.texture.cache[file] = img;
|
|
};
|
|
|
|
function make_spritesheet(paths, width, height)
|
|
{
|
|
return
|
|
var textures = paths.map(path => graphics.texture(path));
|
|
var sizes = textures.map(tex => [tex.width, tex.height]);
|
|
var pos = os.rectpack(width, height, sizes);
|
|
if (!pos) return;
|
|
|
|
var sheet = os.make_tex_data(width,height);
|
|
|
|
var st = profile.now();
|
|
for (var i = 0; i < pos.length; i++)
|
|
sheet.copy(textures[i], pos[i].x, pos[i].y);
|
|
|
|
sheet.save("spritesheet.qoi");
|
|
|
|
sheet.load_gpu();
|
|
}
|
|
|
|
var fontcache = {};
|
|
var datas= [];
|
|
graphics.get_font = function get_font(path,size)
|
|
{
|
|
var parts = path.split('.');
|
|
if (!isNaN(parts[1])) {
|
|
path = parts[0];
|
|
size = Number(parts[1]);
|
|
}
|
|
var fullpath = res.find_font(path);
|
|
if (!fullpath) throw new Error(`Cannot load font ${path}`)
|
|
|
|
var fontstr = `${fullpath}.${size}`;
|
|
if (fontcache[fontstr]) return fontcache[fontstr];
|
|
|
|
var data = io.slurpbytes(fullpath);
|
|
fontcache[fontstr] = os.make_font(data,size);
|
|
fontcache[fontstr].texture = prosperon.gpu.load_texture(fontcache[fontstr].surface);
|
|
return fontcache[fontstr];
|
|
}
|
|
|
|
graphics.semver = {};
|
|
graphics.semver.valid = function (v, range) {
|
|
v = v.split(".");
|
|
range = range.split(".");
|
|
if (v.length !== 3) return undefined;
|
|
if (range.length !== 3) return undefined;
|
|
|
|
if (range[0][0] === "^") {
|
|
range[0] = range[0].slice(1);
|
|
if (parseInt(v[0]) >= parseInt(range[0])) return true;
|
|
|
|
return false;
|
|
}
|
|
|
|
if (range[0] === "~") {
|
|
range[0] = range[0].slice(1);
|
|
for (var i = 0; i < 2; i++) if (parseInt(v[i]) < parseInt(range[i])) return false;
|
|
return true;
|
|
}
|
|
|
|
return graphics.semver.cmp(v.join("."), range.join(".")) === 0;
|
|
};
|
|
|
|
graphics.semver.cmp = function (v1, v2) {
|
|
var ver1 = v1.split(".");
|
|
var ver2 = v2.split(".");
|
|
|
|
for (var i = 0; i < 3; i++) {
|
|
var n1 = parseInt(ver1[i]);
|
|
var n2 = parseInt(ver2[i]);
|
|
if (n1 > n2) return 1;
|
|
else if (n1 < n2) return -1;
|
|
}
|
|
|
|
return 0;
|
|
};
|
|
|
|
graphics.semver.cmp.doc = "Compare two semantic version numbers, given like X.X.X.";
|
|
graphics.semver.valid.doc = `Test if semantic version v is valid, given a range.
|
|
Range is given by a semantic versioning number, prefixed with nothing, a ~, or a ^.
|
|
~ means that MAJOR and MINOR must match exactly, but any PATCH greater or equal is valid.
|
|
^ means that MAJOR must match exactly, but any MINOR and PATCH greater or equal is valid.`;
|
|
|
|
return graphics
|