add aseprite support
This commit is contained in:
parent
12bb5e084a
commit
27d7c5ab02
|
@ -88,10 +88,10 @@ game.engine_start = function (s) {
|
||||||
1, 1, 0], 0),
|
1, 1, 0], 0),
|
||||||
verts: 4,
|
verts: 4,
|
||||||
uv: os.make_buffer([
|
uv: os.make_buffer([
|
||||||
0, 0,
|
|
||||||
0, 1,
|
0, 1,
|
||||||
1, 0,
|
0, 0,
|
||||||
1, 1], 2),
|
1, 1,
|
||||||
|
1, 0], 2),
|
||||||
index: os.make_buffer([0, 1, 2, 2, 1, 3], 1),
|
index: os.make_buffer([0, 1, 2, 2, 1, 3], 1),
|
||||||
count: 6,
|
count: 6,
|
||||||
};
|
};
|
||||||
|
@ -112,10 +112,10 @@ game.engine_start = function (s) {
|
||||||
0.5, 0.5, -0.5], 0),
|
0.5, 0.5, -0.5], 0),
|
||||||
verts: 4,
|
verts: 4,
|
||||||
uv: os.make_buffer([
|
uv: os.make_buffer([
|
||||||
0, 0,
|
|
||||||
0, 1,
|
0, 1,
|
||||||
1, 0,
|
0, 0,
|
||||||
1, 1], 2),
|
1, 1,
|
||||||
|
1, 0], 2),
|
||||||
index: os.make_buffer([0, 1, 2, 2, 1, 3], 1),
|
index: os.make_buffer([0, 1, 2, 2, 1, 3], 1),
|
||||||
count: 6,
|
count: 6,
|
||||||
};
|
};
|
||||||
|
@ -255,7 +255,9 @@ function pack_into_sheet(images)
|
||||||
|
|
||||||
game.texture = function (path) {
|
game.texture = function (path) {
|
||||||
if (!path) return game.texture("icons/no_tex.gif");
|
if (!path) return game.texture("icons/no_tex.gif");
|
||||||
path = Resources.find_image(path);
|
|
||||||
|
var parts = path.split(':');
|
||||||
|
path = Resources.find_image(parts[0]);
|
||||||
|
|
||||||
if (!io.exists(path)) {
|
if (!io.exists(path)) {
|
||||||
console.error(`Missing texture: ${path}`);
|
console.error(`Missing texture: ${path}`);
|
||||||
|
@ -263,12 +265,38 @@ game.texture = function (path) {
|
||||||
game.texture.time_cache[path] = io.mod(path);
|
game.texture.time_cache[path] = io.mod(path);
|
||||||
return game.texture.cache[path];
|
return game.texture.cache[path];
|
||||||
}
|
}
|
||||||
if (game.texture.cache[path]) return game.texture.cache[path];
|
|
||||||
var tex = os.make_texture(path);
|
var cachestr = path;
|
||||||
|
if (parts[1])
|
||||||
|
cachestr += parts[1];
|
||||||
|
|
||||||
|
var frame;
|
||||||
|
var anim_str;
|
||||||
|
if (parts.length > 1) {
|
||||||
|
// it's an animation
|
||||||
|
path = parts[0];
|
||||||
|
parts = parts[1].split('_'); // For a gif, it might be 'water.gif:3', but for an ase it might be 'water.ase:run_3', meaning the third frame of the 'run' animation
|
||||||
|
if (parts.length === 1)
|
||||||
|
frame = Number(parts[0]);
|
||||||
|
else {
|
||||||
|
anim_str = parts[0];
|
||||||
|
frame = Number(parts[1]);
|
||||||
|
}
|
||||||
|
} else
|
||||||
|
parts = undefined;
|
||||||
|
|
||||||
|
if (game.texture.cache[cachestr]) return game.texture.cache[path];
|
||||||
|
|
||||||
|
var tex = os.make_texture(path.split(':')[0]);
|
||||||
|
if (!tex) return;
|
||||||
|
|
||||||
var image;
|
var image;
|
||||||
|
|
||||||
var anim = SpriteAnim.make(path, tex);
|
var ext = path.ext();
|
||||||
|
var anim;
|
||||||
|
if (ext === 'ase' || ext === 'aseprite') {
|
||||||
|
anim = os.make_aseprite(path);
|
||||||
|
}
|
||||||
if (!anim) {
|
if (!anim) {
|
||||||
image = {
|
image = {
|
||||||
texture: tex,
|
texture: tex,
|
||||||
|
@ -293,12 +321,12 @@ game.texture = function (path) {
|
||||||
tex = spritesheet;
|
tex = spritesheet;
|
||||||
}
|
}
|
||||||
|
|
||||||
game.texture.cache[path] = image;
|
game.texture.cache[cachestr] = image;
|
||||||
game.texture.time_cache[path] = io.mod(path);
|
game.texture.time_cache[path] = io.mod(path);
|
||||||
|
|
||||||
tex.load_gpu();
|
tex.load_gpu();
|
||||||
|
|
||||||
return game.texture.cache[path];
|
return game.texture.cache[cachestr];
|
||||||
};
|
};
|
||||||
game.texture.cache = {};
|
game.texture.cache = {};
|
||||||
game.texture.time_cache = {};
|
game.texture.time_cache = {};
|
||||||
|
|
|
@ -1012,7 +1012,9 @@ render.image = function image(image, rect = [0,0], rotation = 0, color = Color.w
|
||||||
var tex = image.texture;
|
var tex = image.texture;
|
||||||
if (!tex) return;
|
if (!tex) return;
|
||||||
|
|
||||||
var size = [rect.width ? rect.width : tex.width, rect.height ? rect.height : tex.height];
|
var image_size = [image.rect.width*tex.width, image.rect.height*tex.height];
|
||||||
|
|
||||||
|
var size = [rect.width ? rect.width : image_size.x, rect.height ? rect.height : image_size.y];
|
||||||
|
|
||||||
if (!lasttex) {
|
if (!lasttex) {
|
||||||
check_flush(flush_img);
|
check_flush(flush_img);
|
||||||
|
@ -1026,7 +1028,7 @@ render.image = function image(image, rect = [0,0], rotation = 0, color = Color.w
|
||||||
|
|
||||||
var e = img_e();
|
var e = img_e();
|
||||||
var pos = [rect.x,rect.y].sub(size.scale([rect.anchor_x, rect.anchor_y]));
|
var pos = [rect.x,rect.y].sub(size.scale([rect.anchor_x, rect.anchor_y]));
|
||||||
e.transform.trs(pos, undefined, size.div([tex.width,tex.height]));
|
e.transform.trs(pos, undefined, size.div(image_size));
|
||||||
e.image = image;
|
e.image = image;
|
||||||
e.shade = color;
|
e.shade = color;
|
||||||
|
|
||||||
|
|
|
@ -7,8 +7,12 @@
|
||||||
#include "sokol/sokol_args.h"
|
#include "sokol/sokol_args.h"
|
||||||
#include "sokol/sokol_gfx.h"
|
#include "sokol/sokol_gfx.h"
|
||||||
#include "sokol/sokol_app.h"
|
#include "sokol/sokol_app.h"
|
||||||
|
#include "sokol/sokol_fetch.h"
|
||||||
#include "sokol/util/sokol_gl.h"
|
#include "sokol/util/sokol_gl.h"
|
||||||
|
|
||||||
|
#define CUTE_ASEPRITE_IMPLEMENTATION
|
||||||
|
#include "cute_aseprite.h"
|
||||||
|
|
||||||
#define LAY_FLOAT 1
|
#define LAY_FLOAT 1
|
||||||
#define LAY_IMPLEMENTATION
|
#define LAY_IMPLEMENTATION
|
||||||
#include "layout.h"
|
#include "layout.h"
|
||||||
|
|
|
@ -11,6 +11,7 @@
|
||||||
#include "datastream.h"
|
#include "datastream.h"
|
||||||
#include "sound.h"
|
#include "sound.h"
|
||||||
#include "stb_ds.h"
|
#include "stb_ds.h"
|
||||||
|
#include "stb_image.h"
|
||||||
#include "stb_rect_pack.h"
|
#include "stb_rect_pack.h"
|
||||||
#include "string.h"
|
#include "string.h"
|
||||||
#include "window.h"
|
#include "window.h"
|
||||||
|
@ -40,6 +41,8 @@
|
||||||
#include "gui.h"
|
#include "gui.h"
|
||||||
#include "timer.h"
|
#include "timer.h"
|
||||||
|
|
||||||
|
#include "cute_aseprite.h"
|
||||||
|
|
||||||
#define LAY_FLOAT 1
|
#define LAY_FLOAT 1
|
||||||
#include "layout.h"
|
#include "layout.h"
|
||||||
|
|
||||||
|
@ -627,8 +630,8 @@ JSValue rect2js(struct rect rect) {
|
||||||
JSValue obj = JS_NewObject(js);
|
JSValue obj = JS_NewObject(js);
|
||||||
js_setprop_str(obj, "x", number2js(rect.x));
|
js_setprop_str(obj, "x", number2js(rect.x));
|
||||||
js_setprop_str(obj, "y", number2js(rect.y));
|
js_setprop_str(obj, "y", number2js(rect.y));
|
||||||
js_setprop_str(obj, "w", number2js(rect.w));
|
js_setprop_str(obj, "width", number2js(rect.w));
|
||||||
js_setprop_str(obj, "h", number2js(rect.h));
|
js_setprop_str(obj, "height", number2js(rect.h));
|
||||||
return obj;
|
return obj;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2758,8 +2761,6 @@ static const JSCFunctionListEntry js_datastream_funcs[] = {
|
||||||
|
|
||||||
JSC_GET(texture, width, number)
|
JSC_GET(texture, width, number)
|
||||||
JSC_GET(texture, height, number)
|
JSC_GET(texture, height, number)
|
||||||
JSC_GET(texture, frames, number)
|
|
||||||
JSC_GET(texture, delays, ints)
|
|
||||||
JSC_GET(texture, vram, number)
|
JSC_GET(texture, vram, number)
|
||||||
|
|
||||||
JSC_SCALL(texture_save, texture_save(js2texture(self), str));
|
JSC_SCALL(texture_save, texture_save(js2texture(self), str));
|
||||||
|
@ -2811,8 +2812,6 @@ JSC_CCALL(texture_offload,
|
||||||
static const JSCFunctionListEntry js_texture_funcs[] = {
|
static const JSCFunctionListEntry js_texture_funcs[] = {
|
||||||
MIST_GET(texture, width),
|
MIST_GET(texture, width),
|
||||||
MIST_GET(texture, height),
|
MIST_GET(texture, height),
|
||||||
MIST_GET(texture, frames),
|
|
||||||
MIST_GET(texture, delays),
|
|
||||||
MIST_GET(texture, vram),
|
MIST_GET(texture, vram),
|
||||||
MIST_FUNC_DEF(texture, save, 1),
|
MIST_FUNC_DEF(texture, save, 1),
|
||||||
MIST_FUNC_DEF(texture, write_pixel, 2),
|
MIST_FUNC_DEF(texture, write_pixel, 2),
|
||||||
|
@ -3377,12 +3376,101 @@ JSC_SCALL(os_make_texture,
|
||||||
JS_SetPropertyStr(js, ret, "path", JS_DupValue(js,argv[0]));
|
JS_SetPropertyStr(js, ret, "path", JS_DupValue(js,argv[0]));
|
||||||
)
|
)
|
||||||
|
|
||||||
JSC_SCALL(os_make_pcm,
|
JSC_SCALL(os_make_gif,
|
||||||
ret = pcm2js(make_pcm(str));
|
size_t rawlen;
|
||||||
|
unsigned char *raw = slurp_file(str, &rawlen);
|
||||||
|
if (!raw) goto ENDEND;
|
||||||
|
int n;
|
||||||
|
texture *tex = calloc(1,sizeof(*tex));
|
||||||
|
int frames;
|
||||||
|
int *delays;
|
||||||
|
tex->data = stbi_load_gif_from_memory(raw, rawlen, &delays, &tex->width, &tex->height, &frames, &n, 4);
|
||||||
|
|
||||||
|
JSValue gif = JS_NewObject(js);
|
||||||
|
js_setpropstr(gif, "texture", texture2js(tex));
|
||||||
|
|
||||||
|
JSValue delay_arr = JS_NewArray(js);
|
||||||
|
float yslice = 1.0/frames;
|
||||||
|
for (int i = 0; i < frames; i++) {
|
||||||
|
JSValue frame = JS_NewObject(js);
|
||||||
|
js_setpropstr(frame, "time", number2js((float)delays[i]/1000.0));
|
||||||
|
js_setpropstr(frame, "rect", rect2js((rect){
|
||||||
|
.x = 0,
|
||||||
|
.y = yslice*i,
|
||||||
|
.w = 1,
|
||||||
|
.h = yslice
|
||||||
|
}));
|
||||||
|
js_setprop_num(delay_arr, i, frame);
|
||||||
|
}
|
||||||
|
|
||||||
|
js_setpropstr(gif, "delays", delay_arr);
|
||||||
|
|
||||||
|
free(delays);
|
||||||
|
|
||||||
|
ret = gif;
|
||||||
|
|
||||||
|
END:
|
||||||
|
free(raw);
|
||||||
|
|
||||||
|
ENDEND:
|
||||||
)
|
)
|
||||||
|
|
||||||
JSC_SCALL(os_make_aseprite,
|
JSC_SCALL(os_make_aseprite,
|
||||||
|
size_t rawlen;
|
||||||
|
unsigned char *raw = slurp_file(str, &rawlen);
|
||||||
|
ase_t *ase = cute_aseprite_load_from_memory(raw, rawlen, NULL);
|
||||||
|
|
||||||
|
JSValue obj = JS_NewObject(js);
|
||||||
|
|
||||||
|
int w = ase->w;
|
||||||
|
int h = ase->h;
|
||||||
|
|
||||||
|
int pixels = w*h;
|
||||||
|
|
||||||
|
for (int t = 0; t < ase->tag_count; t++) {
|
||||||
|
ase_tag_t tag = ase->tags[t];
|
||||||
|
JSValue anim = JS_NewObject(js);
|
||||||
|
js_setpropstr(anim, "repeat", number2js(tag.repeat));
|
||||||
|
switch(tag.loop_animation_direction) {
|
||||||
|
case ASE_ANIMATION_DIRECTION_FORWARDS:
|
||||||
|
js_setpropstr(anim, "loop", str2js("forward"));
|
||||||
|
break;
|
||||||
|
case ASE_ANIMATION_DIRECTION_BACKWORDS:
|
||||||
|
js_setpropstr(anim, "loop", str2js("backward"));
|
||||||
|
break;
|
||||||
|
case ASE_ANIMATION_DIRECTION_PINGPONG:
|
||||||
|
js_setpropstr(anim, "loop", str2js("pingpong"));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
int _frame = 0;
|
||||||
|
JSValue frames = JS_NewArray(js);
|
||||||
|
for (int f = tag.from_frame; f <= tag.to_frame; f++) {
|
||||||
|
ase_frame_t aframe = ase->frames[f];
|
||||||
|
JSValue frame = JS_NewObject(js);
|
||||||
|
|
||||||
|
texture *tex = calloc(1,sizeof(*tex));
|
||||||
|
tex->width = w;
|
||||||
|
tex->height = h;
|
||||||
|
tex->data = malloc(w*h*4);
|
||||||
|
memcpy(tex->data, aframe.pixels, w*h*4);
|
||||||
|
js_setpropstr(frame, "texture", texture2js(tex));
|
||||||
|
js_setpropstr(frame, "rect", rect2js((rect){.x=0,.y=0,.w=1,.h=1}));
|
||||||
|
js_setpropstr(frame, "time", number2js((float)aframe.duration_milliseconds/1000.0));
|
||||||
|
js_setprop_num(frames, _frame, frame);
|
||||||
|
_frame++;
|
||||||
|
}
|
||||||
|
js_setpropstr(anim, "frames", frames);
|
||||||
|
js_setpropstr(obj, tag.name, anim);
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = obj;
|
||||||
|
|
||||||
|
cute_aseprite_free(ase);
|
||||||
|
)
|
||||||
|
|
||||||
|
JSC_SCALL(os_make_pcm,
|
||||||
|
ret = pcm2js(make_pcm(str));
|
||||||
)
|
)
|
||||||
|
|
||||||
JSC_SCALL(os_texture_swap,
|
JSC_SCALL(os_texture_swap,
|
||||||
|
@ -3678,6 +3766,8 @@ static const JSCFunctionListEntry js_os_funcs[] = {
|
||||||
MIST_FUNC_DEF(os, make_poly2d, 2),
|
MIST_FUNC_DEF(os, make_poly2d, 2),
|
||||||
MIST_FUNC_DEF(os, make_seg2d, 1),
|
MIST_FUNC_DEF(os, make_seg2d, 1),
|
||||||
MIST_FUNC_DEF(os, make_texture, 1),
|
MIST_FUNC_DEF(os, make_texture, 1),
|
||||||
|
MIST_FUNC_DEF(os, make_gif, 1),
|
||||||
|
MIST_FUNC_DEF(os, make_aseprite, 1),
|
||||||
MIST_FUNC_DEF(os, make_pcm, 1),
|
MIST_FUNC_DEF(os, make_pcm, 1),
|
||||||
MIST_FUNC_DEF(os, texture_swap, 2),
|
MIST_FUNC_DEF(os, texture_swap, 2),
|
||||||
MIST_FUNC_DEF(os, make_tex_data, 3),
|
MIST_FUNC_DEF(os, make_tex_data, 3),
|
||||||
|
|
|
@ -21,9 +21,7 @@
|
||||||
#include <ftw.h>
|
#include <ftw.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#define SOKOL_FETCH_IMPL
|
|
||||||
#include "sokol/sokol_fetch.h"
|
#include "sokol/sokol_fetch.h"
|
||||||
|
|
||||||
#include "stb_ds.h"
|
#include "stb_ds.h"
|
||||||
|
|
||||||
#include "core.cdb.h"
|
#include "core.cdb.h"
|
||||||
|
|
|
@ -15,9 +15,6 @@
|
||||||
|
|
||||||
#include "qoi.h"
|
#include "qoi.h"
|
||||||
|
|
||||||
#define CUTE_ASEPRITE_IMPLEMENTATION
|
|
||||||
#include "cute_aseprite.h"
|
|
||||||
|
|
||||||
#ifndef NSVG
|
#ifndef NSVG
|
||||||
#include "nanosvgrast.h"
|
#include "nanosvgrast.h"
|
||||||
#endif
|
#endif
|
||||||
|
@ -175,50 +172,16 @@ struct texture *texture_from_file(const char *path) {
|
||||||
|
|
||||||
struct texture *tex = calloc(1, sizeof(*tex));
|
struct texture *tex = calloc(1, sizeof(*tex));
|
||||||
|
|
||||||
stbi_set_flip_vertically_on_load(1);
|
|
||||||
|
|
||||||
int n;
|
int n;
|
||||||
|
|
||||||
char *ext = strrchr(path, '.');
|
char *ext = strrchr(path, '.');
|
||||||
|
|
||||||
if (!strcmp(ext, ".ase")) {
|
if (!strcmp(ext, ".qoi")) {
|
||||||
ase_t *ase = cute_aseprite_load_from_memory(raw, rawlen, NULL);
|
|
||||||
// frame is ase->w, ase->h
|
|
||||||
/* sprite anim is
|
|
||||||
anim = {
|
|
||||||
frames: [
|
|
||||||
rect: { <---- gathered after rect packing
|
|
||||||
x:
|
|
||||||
y:
|
|
||||||
w:
|
|
||||||
h:
|
|
||||||
},
|
|
||||||
time: ase->duration_milliseconds / 1000 (so it's in seconds)
|
|
||||||
],
|
|
||||||
path: path,
|
|
||||||
};
|
|
||||||
|
|
||||||
*/
|
|
||||||
for (int i = 0; i < ase->frame_count; i++) {
|
|
||||||
ase_frame_t *frame = ase->frames+i;
|
|
||||||
// add to thing with frame->pixels
|
|
||||||
}
|
|
||||||
|
|
||||||
cute_aseprite_free(ase);
|
|
||||||
} else if (!strcmp(ext, ".qoi")) {
|
|
||||||
qoi_desc qoi;
|
qoi_desc qoi;
|
||||||
data = qoi_decode(raw, rawlen, &qoi, 4);
|
data = qoi_decode(raw, rawlen, &qoi, 4);
|
||||||
tex->width = qoi.width;
|
tex->width = qoi.width;
|
||||||
tex->height = qoi.height;
|
tex->height = qoi.height;
|
||||||
n = qoi.channels;
|
n = qoi.channels;
|
||||||
} else if (!strcmp(ext, ".gif")) {
|
|
||||||
data = stbi_load_gif_from_memory(raw, rawlen, &tex->delays, &tex->width, &tex->height, &tex->frames, &n, 4);
|
|
||||||
int *dd = tex->delays;
|
|
||||||
tex->delays = NULL;
|
|
||||||
arrsetlen(tex->delays, tex->frames);
|
|
||||||
for (int i = 0; i < tex->frames; i++) tex->delays[i] = dd[i];
|
|
||||||
free(dd);
|
|
||||||
tex->height *= tex->frames;
|
|
||||||
} else if (!strcmp(ext, ".svg")) {
|
} else if (!strcmp(ext, ".svg")) {
|
||||||
#ifndef NSVG
|
#ifndef NSVG
|
||||||
NSVGimage *svg = nsvgParse(raw, "px", 96);
|
NSVGimage *svg = nsvgParse(raw, "px", 96);
|
||||||
|
@ -256,7 +219,6 @@ void texture_free(texture *tex)
|
||||||
if (!tex) return;
|
if (!tex) return;
|
||||||
if (tex->data)
|
if (tex->data)
|
||||||
free(tex->data);
|
free(tex->data);
|
||||||
if (tex->delays) arrfree(tex->delays);
|
|
||||||
if (tex->simgui.id)
|
if (tex->simgui.id)
|
||||||
simgui_destroy_image(tex->simgui);
|
simgui_destroy_image(tex->simgui);
|
||||||
|
|
||||||
|
|
|
@ -29,8 +29,6 @@ struct texture {
|
||||||
int height;
|
int height;
|
||||||
HMM_Vec3 dimensions;
|
HMM_Vec3 dimensions;
|
||||||
unsigned char *data;
|
unsigned char *data;
|
||||||
int frames;
|
|
||||||
int *delays;
|
|
||||||
int vram;
|
int vram;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue