Move texture caching/handling to javascript

This commit is contained in:
John Alanbrook 2024-03-21 21:07:24 -05:00
parent f5d610cbed
commit 1540da8392
11 changed files with 112 additions and 143 deletions

View file

@ -90,38 +90,46 @@ Object.mixin(os.sprite(true), {
playing: 0,
play(str) {
var sp = this;
this.del_anim = function() {
sp = undefined;
advance = undefined;
}
str ??= 0;
var playing = this.anim[str];
if (!playing) return; //TODO: ERROR
if (!playing) return;
var f = 0;
function advance() {
if (!sp) this.del_anim();
if (!sp.gameobject) return;
sp.path = playing.path;
sp.frame = playing.frames[f].rect;
f = (f+1)%playing.frames.length;
if (f === 0)
sp.anim_done?.();
sp.ddd = sp.gameobject?.delay(advance, playing.frames[f].time);
if (f === 0) sp.anim_done?.();
sp.gameobject.delay(advance, playing.frames[f].time);
}
advance();
},
set path(p) {
p = Resources.find_image(p);
if (!p) return;
if (p === this.path) return;
this.tex = texture.find(p);
this._p = p;
this.tex(game.texture(p));
var anim = SpriteAnim.make(p);
if (!anim) return;
this.anim = anim;
this.play();
},
get path() {
return this.tex.path();
return this._p;
},
kill() {
this.del_anim?.();
this.anim = undefined;
this.ddd?.();
delete this.ddd;
this.gameobject = undefined;
},
toString() { return "sprite"; },
move(d) { this.pos = this.pos.add(d); },
@ -197,10 +205,14 @@ var SpriteAnim = {
return undefined;
},
gif(path) {
console.info(`making an anim from ${path}`);
var anim = {};
anim.frames = [];
anim.path = path;
var frames = Resources.gif.frames(path);
var tex = game.texture(path);
var frames = tex.frames;
console.info(`frames are ${frames}`);
if (frames === 1) return undefined;
var yslice = 1/frames;
for (var f = 0; f < frames; f++) {
var frame = {};
@ -213,11 +225,13 @@ var SpriteAnim = {
frame.time = 0.05;
anim.frames.push(frame);
}
var times = render.gif_times(path);
var times = tex.delays;
console.info(`times are ${times}, num ${times.length}`);
for (var i = 0; i < frames; i++)
anim.frames[i].time = times[i]/1000;
anim.loop = true;
var dim = Resources.texture.dimensions(path);
var dim = [tex.width,tex.height];
console.info(`dimensions are ${dim}`);
dim.y /= frames;
anim.dim = dim;
return {0:anim};

View file

@ -176,6 +176,7 @@ game.engine_start = function(s) {
gggstart(function() {
world_start();
go_init();
window.set_icon(os.make_texture("icons/moon.gif"))
s();
}, process);
}
@ -235,12 +236,18 @@ game.all_objects = function(fn) { eachobj(world,fn); };
game.doc = {};
game.doc.object = "Returns the entity belonging to a given id.";
game.doc.pause = "Pause game simulation.";
game.doc.stop = "Stop game simulation. This does the same thing as 'pause', and if the game is a debug build, starts its editor.";
game.doc.play = "Resume or start game simulation.";
game.doc.editor_mode = "Set to true for the game to only update on input; otherwise the game updates every frame.";
game.doc.dt = "Current frame dt.";
game.doc.camera = "Current camera.";
game.texture = function(path)
{
game.texture.cache[path] ??= os.make_texture(path);
return game.texture.cache[path];
}
game.texture.cache = {};
prosperon.semver = {};
prosperon.semver.valid = function(v, range)
{
@ -337,9 +344,7 @@ var timer = {
},
};
global.mixin("scripts/tween");
global.mixin("scripts/physics");
global.mixin("scripts/ai");
global.mixin("scripts/geometry");
/*
@ -359,7 +364,6 @@ var Register = {
fn = fn.bind(obj);
fns.push(fn);
return function() {
console.spam(`removed from ${name}.`);
fns.remove(fn);
};
}

View file

@ -178,23 +178,33 @@ var gameobject = {
},
delay(fn, seconds) {
var timescale = this.timescale;
var timers = this.timers;
var thisfn = fn.bind(this);
var rm_register;
var that = this;
var ud = function(dt) {
seconds -= dt*timescale;
if (seconds <= 0) {
thisfn();
rm_register();
rm_register = undefined;
thisfn = undefined;
}
var stop = function() {
that.timers.remove(stop);
execute = undefined;
stop = undefined;
rm();
rm = undefined;
update = undefined;
}
rm_register = Register.update.register(ud);
return rm_register;
function execute() {
fn.call(that);
stop?.();
}
stop.remain = seconds;
stop.seconds = seconds;
function update(dt) {
stop.remain -= dt;
if (stop.remain <= 0) execute();
}
var rm = Register.update.register(update);
this.timers.push(stop);
return stop;
},
tween(prop, values, def) {
@ -535,7 +545,10 @@ var gameobject = {
this.__kill = true;
console.info(`Killing entity of type ${this.ur}`);
console.info(`killing ${this.timers.length} timers.`);
this.timers.forEach(t => t());
console.info(`now there are ${this.timers.length}`);
for (var i of this.timers) console.info(i);
this.timers = [];
Event.rm_obj(this);
Player.do_uncontrol(this);
@ -679,7 +692,7 @@ gameobject.doc = {
gear: 'Keeps the angular velocity ratio of this body and to constant. Ratio is the gear ratio.',
motor: 'Keeps the relative angular velocity of this body to to at a constant rate. The most simple idea is for one of the bodies to be static, to the other is kept at rate.',
layer: 'Bitmask for collision layers.',
draw_layer: 'Layer for drawing. Higher numbers draw above lower ones.',
drawlayer: 'Layer for drawing. Higher numbers draw above lower ones.',
warp_layer: 'Bitmask for selecting what warps should affect this entity.',
};

View file

@ -76,6 +76,8 @@ const char *js2str(JSValue v) {
#define MIST_CGETSET_DEF(name, fgetter, fsetter) { name, JS_PROP_CONFIGURABLE | JS_PROP_ENUMERABLE, JS_DEF_CGETSET, 0, .u = { .getset = { .get = { .getter = fgetter }, .set = { .setter = fsetter } } } }
#define MIST_GET(name, fgetter) { #fgetter , JS_PROP_CONFIGURABLE | JS_PROP_ENUMERABLE, JS_DEF_CGETSET, 0, .u = { .getset = { .get = { .getter = js_##name##_get_##fgetter } } } }
#define CGETSET_ADD(ID, ENTRY) MIST_CGETSET_DEF(#ENTRY, js_##ID##_get_##ENTRY, js_##ID##_set_##ENTRY)
#define JSC_CCALL(NAME, FN) JSValue js_##NAME (JSContext *js, JSValue this, int argc, JSValue *argv) { \
@ -125,6 +127,10 @@ JSValue js_gameobject_get_##ENTRY (JSContext *js, JSValue this) { \
return TYPE##2js (cpBodyGet##CPENTRY (b)); \
} \
#define JSC_GET(ID, ENTRY, TYPE) \
JSValue js_##ID##_get_##ENTRY (JSContext *js, JSValue this) { \
return TYPE##2js(js2##ID (this)->ENTRY); } \
#define QJSCLASS(TYPE)\
static JSClassID js_##TYPE##_id;\
static void js_##TYPE##_finalizer(JSRuntime *rt, JSValue val){\
@ -797,6 +803,11 @@ JSC_CCALL(os_make_edge2d,
return edgeval;
)
JSC_SCALL(os_make_texture,
ret = texture2js(texture_from_file(str));
JS_SetPropertyStr(js, ret, "path", JS_DupValue(js,argv[0]));
)
static const JSCFunctionListEntry js_os_funcs[] = {
MIST_FUNC_DEF(os,sprite,1),
MIST_FUNC_DEF(os, cwd, 0),
@ -812,6 +823,7 @@ static const JSCFunctionListEntry js_os_funcs[] = {
MIST_FUNC_DEF(os, make_poly2d, 1),
MIST_FUNC_DEF(os, make_edge2d, 1),
MIST_FUNC_DEF(os, make_model, 2),
MIST_FUNC_DEF(os, make_texture, 1),
};
JSC_CCALL(render_normal, opengl_rendermode(LIT))
@ -841,16 +853,12 @@ JSC_CCALL(render_pass, debug_nextpass())
JSC_CCALL(render_end_pass, sg_end_pass())
JSC_CCALL(render_commit, sg_commit(); debug_newframe();)
JSC_SCALL(render_text_size, ret = bb2js(text_bb(str, js2number(argv[1]), js2number(argv[2]), 1)))
JSC_SCALL(render_gif_times, ret = ints2js(gif_delays(str)))
JSC_SCALL(render_gif_frames, ret = number2js(gif_nframes(str)))
JSC_CCALL(render_world2screen, return vec22js(world2screen(js2vec2(argv[0]))))
JSC_CCALL(render_screen2world, return vec22js(screen2world(js2vec2(argv[0]))))
static const JSCFunctionListEntry js_render_funcs[] = {
MIST_FUNC_DEF(render,world2screen,1),
MIST_FUNC_DEF(render,screen2world,1),
MIST_FUNC_DEF(render,gif_frames,1),
MIST_FUNC_DEF(render, gif_times, 1),
MIST_FUNC_DEF(render, normal, 0),
MIST_FUNC_DEF(render, wireframe, 0),
MIST_FUNC_DEF(render, grid, 3),
@ -1271,8 +1279,7 @@ static JSValue js_window_set_title(JSContext *js, JSValue this, JSValue v)
return JS_UNDEFINED;
}
JSC_CCALL(window_get_title, return str2js(js2window(this)->title))
JSC_SCALL(window_set_icon, set_icon(str))
JSC_SCALL(window_icon, set_icon(str))
JSC_CCALL(window_set_icon, window_seticon(&mainwin, js2texture(argv[0])))
static const JSCFunctionListEntry js_window_funcs[] = {
CGETSET_ADD(window, size),
@ -1435,12 +1442,13 @@ JSC_GETSET(sprite, scale, vec2)
JSC_GETSET(sprite, angle, number)
JSC_GETSET(sprite, frame, rect)
JSC_GETSET(sprite,go,gameobject)
JSC_CCALL(sprite_tex, js2sprite(this)->tex = js2texture(argv[0]))
static const JSCFunctionListEntry js_sprite_funcs[] = {
CGETSET_ADD(sprite,pos),
CGETSET_ADD(sprite,scale),
CGETSET_ADD(sprite,angle),
CGETSET_ADD(sprite,tex),
MIST_FUNC_DEF(sprite, tex, 1),
CGETSET_ADD(sprite,color),
CGETSET_ADD(sprite,emissive),
CGETSET_ADD(sprite,enabled),
@ -1449,17 +1457,16 @@ static const JSCFunctionListEntry js_sprite_funcs[] = {
CGETSET_ADD(sprite,go)
};
GETFN(texture,width,number)
GETFN(texture,height,number)
JSC_CCALL(texture_path, return str2js(tex_get_path(js2texture(this))))
JSC_SCALL(texture_find, ret = texture2js(texture_from_file(str)))
JSC_GET(texture, width, number)
JSC_GET(texture, height, number)
JSC_GET(texture, frames, number)
JSC_GET(texture, delays, ints)
static const JSCFunctionListEntry js_texture_funcs[] = {
MIST_FUNC_DEF(texture, width, 0),
MIST_FUNC_DEF(texture, height, 0),
MIST_FUNC_DEF(texture, path, 0),
MIST_FUNC_DEF(texture, find, 1),
// MIST_FUNC_DEF(texture, dimensions, 1),
MIST_GET(texture, width),
MIST_GET(texture, height),
MIST_GET(texture, frames),
MIST_GET(texture, delays),
};
JSValue js_constraint_set_max_force (JSContext *js, JSValue this, JSValue val) {

View file

@ -99,7 +99,7 @@ emitter *make_emitter() {
sampler_add(&e->color, 0, (HMM_Vec4){1,1,1,1});
e->scale = 1;
e->speed = 20;
e->texture = texture_from_file("glass_chunk2.gif");
e->texture = NULL;
arrpush(emitters,e);
return e;
}
@ -166,7 +166,7 @@ void parallel_pv(emitter *e, struct scheduler *sched, struct sched_task_partitio
s = lerp(p->time/e->grow_for, 0, p->scale);
else if (p->time > (p->life - e->shrink_for))
s = lerp((p->time-(p->life-e->shrink_for))/e->shrink_for, p->scale, 0);
pv[i].scale = HMM_ScaleV2(tex_get_dimensions(e->texture), s);
pv[i].scale = HMM_ScaleV2((HMM_Vec2){e->texture->width,e->texture->height}, s);
pv[i].color = vec2rgba(p->color);
}
}

View file

@ -174,12 +174,6 @@ static struct {
sg_shader shader;
} sg_shadow;
void trace_make_image(const sg_image_desc *d, sg_image id, void *data)
{
YughSpam("Made image %s.", d->label);
}
void trace_init_image(sg_image id, const sg_image_desc *d, void *data)
{
YughSpam("Init image %s", d->label);
@ -238,7 +232,6 @@ static sg_trace_hooks hooks = {
.make_shader = trace_make_shader,
.destroy_shader = trace_destroy_shader,
.fail_image = trace_fail_image,
.make_image = trace_make_image,
.init_image = trace_init_image,
.make_pipeline = trace_make_pipeline,
.fail_pipeline = trace_fail_pipeline,

View file

@ -51,7 +51,7 @@ sprite *sprite_make()
sp->color = color_white;
sp->emissive = color_clear;
sp->go = NULL;
sp->tex = texture_from_file(NULL);
sp->tex = NULL;
sp->frame = ST_UNIT;
sp->drawmode = DRAW_SIMPLE;
sp->enabled = 1;
@ -64,8 +64,6 @@ sprite *sprite_make()
void sprite_free(sprite *sprite)
{
YughWarn("Freeing sprite %p.", sprite);
free(sprite);
for (int i = arrlen(sprites)-1; i >= 0; i--)
if (sprites[i] == sprite) {
@ -215,6 +213,7 @@ void sprite_draw(struct sprite *sprite) {
}
void gui_draw_img(const char *img, transform2d t, int wrap, HMM_Vec2 wrapoffset, float wrapscale, struct rgba color) {
return;
sg_apply_pipeline(pip_sprite);
sg_apply_uniforms(SG_SHADERSTAGE_VS, 0, SG_RANGE_REF(hudproj));
struct texture *tex = texture_from_file(img);

View file

@ -4,7 +4,6 @@
#include "render.h"
#include "sokol/sokol_gfx.h"
#include <math.h>
#include <stb_ds.h>
#include <stb_image.h>
#include "resources.h"
@ -23,14 +22,6 @@
struct rect ST_UNIT = {0.f, 0.f, 1.f, 1.f};
static struct {
char *key;
struct texture *value;
} *texhash = NULL;
struct texture *tex_default;
struct texture *texture_notex() { return texture_from_file("icons/no_tex.gif"); }
unsigned int next_pow2(unsigned int v)
{
v--;
@ -70,31 +61,14 @@ int mip_wh(int w, int h, int *mw, int *mh, int lvl)
return 0;
}
int gif_nframes(const char *path)
{
struct texture *t = texture_from_file(path);
return t->frames;
}
int *gif_delays(const char *path)
{
struct texture *t = texture_from_file(path);
return t->delays;
}
/* If an empty string or null is put for path, loads default texture */
struct texture *texture_from_file(const char *path) {
if (!path) return texture_notex();
if (shlen(texhash) == 0) sh_new_arena(texhash);
int index = shgeti(texhash, path);
if (index != -1)
return texhash[index].value;
if (!path) return NULL;
size_t rawlen;
unsigned char *raw = slurp_file(path, &rawlen);
if (!raw) return texture_notex();
if (!raw) return NULL;
unsigned char *data;
@ -115,8 +89,7 @@ struct texture *texture_from_file(const char *path) {
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];
for (int i = 0; i < tex->frames; i++) tex->delays[i] = dd[i];
free(dd);
tex->height *= tex->frames;
} else if (!strcmp(ext, ".svg")) {
@ -141,10 +114,8 @@ struct texture *texture_from_file(const char *path) {
}
free(raw);
if (data == NULL) {
YughError("STBI failed to load file %s with message: %s\nOpening default instead.", path, stbi_failure_reason());
return texture_notex();
}
if (data == NULL)
return NULL;
unsigned int nw = next_pow2(tex->width);
unsigned int nh = next_pow2(tex->height);
@ -189,30 +160,19 @@ struct texture *texture_from_file(const char *path) {
.data = sg_img_data
});
shput(texhash, path, tex);
for (int i = 1; i < mips; i++)
free(mipdata[i]);
return tex;
}
void texture_sync(const char *path) { YughWarn("Need to implement texture sync."); }
void texture_free(texture *tex)
{
}
char *tex_get_path(struct texture *tex) {
for (int i = 0; i < shlen(texhash); i++) {
if (tex == texhash[i].value) {
YughSpam("Found key %s", texhash[i].key);
return texhash[i].key;
}
}
return "";
if (!tex) return;
free(tex->data);
if (tex->delays) arrfree(tex->delays);
sg_destroy_image(tex->id);
free(tex);
}
struct texture *texture_fromdata(void *raw, long size)
@ -222,10 +182,8 @@ struct texture *texture_fromdata(void *raw, long size)
int n;
void *data = stbi_load_from_memory(raw, size, &tex->width, &tex->height, &n, 4);
if (data == NULL) {
YughError("Given raw data not valid. Loading default instead.");
return texture_notex();
}
if (data == NULL)
NULL;
unsigned int nw = next_pow2(tex->width);
unsigned int nh = next_pow2(tex->height);
@ -276,14 +234,6 @@ struct texture *texture_fromdata(void *raw, long size)
return tex;
}
HMM_Vec2 tex_get_dimensions(struct texture *tex) {
if (!tex) return (HMM_Vec2){0,0};
HMM_Vec2 d;
d.x = tex->width;
d.y = tex->height;
return d;
}
static double fade (double t) { return t*t*t*(t*(t*6-15)+10); }
double grad (int hash, double x, double y, double z)
{

View file

@ -37,21 +37,10 @@ typedef struct img_sampler{
int mip_filter;
} img_sampler;
struct texture *texture_from_file(const char *path); // Create texture from image
struct texture *texture_fromdata(void *raw, long size);
texture *texture_from_file(const char *path);
void texture_free(texture *tex);
/* Hot reloads a texture, if needed */
void texture_sync(const char *path);
char * tex_get_path(struct texture *tex); // Get image path for texture
int gif_nframes(const char *path);
int *gif_delays(const char *path);
struct glrect tex_get_rect(struct texture *tex);
HMM_Vec2 tex_get_dimensions(struct texture *tex);
struct texture *texture_fromdata(void *raw, long size);
double perlin(double x, double y, double z);

View file

@ -112,6 +112,7 @@ void quickjs_set_dumpout(FILE *f)
//#define DUMP_READ_OBJECT
#ifdef DUMP
#define DUMP_MEM
#define DUMP_CLOSURE
#define DUMP_GC
#define DUMP_GC_FREE

View file

@ -60,7 +60,6 @@ void c_init() {
window_resize(sapp_width(), sapp_height());
phys2d_init();
render_init();
set_icon("icons/moon.gif");
particle_init();
script_call_sym(c_start,0,NULL);
}