shader hotreload; better circle shader; add nogame for setup help; fix io.mod; add joystick input; fix entity hotreload; implement array length in C; add default game.size

This commit is contained in:
John Alanbrook 2024-08-07 16:55:08 -05:00
parent a28230a647
commit 515eb65b22
9 changed files with 184 additions and 80 deletions

View file

@ -976,11 +976,7 @@ swizz.forEach(function(x) {
make_swizz(); make_swizz();
Object.defineProperty(Array.prototype, 'normalized', { Object.defineProperty(Array.prototype, 'normalized', {
value: function() { value: function() { return vector.norm(this); }
var c = this.slice();
var len = Vector.length(c);
return c.map(v => v/len);
}
}); });
Object.defineProperty(Array.prototype, 'newfirst', { Object.defineProperty(Array.prototype, 'newfirst', {

View file

@ -72,8 +72,8 @@ var entity = {
delay(fn, seconds) { delay(fn, seconds) {
var timers = this.timers; var timers = this.timers;
var stop = function() { var stop = function() {
delete timers[guid]; timers.remove(stop);
execute = undefined; execute = undefined;
stop = undefined; stop = undefined;
rm?.(); rm?.();
@ -98,8 +98,7 @@ var entity = {
} }
var rm = Register.update.register(update); var rm = Register.update.register(update);
var guid = prosperon.guid(); timers.push(stop);
timers[guid] = (stop);
return stop; return stop;
}, },
@ -151,7 +150,7 @@ var entity = {
ent.guid = prosperon.guid(); ent.guid = prosperon.guid();
ent.components = {}; ent.components = {};
ent.objects = {}; ent.objects = {};
ent.timers = {}; ent.timers = [];
ent.ur = {}; ent.ur = {};
}); });
/* /*

View file

@ -213,10 +213,26 @@ input.print_pawn_kbm = function(pawn) {
return str; return str;
}; };
var joysticks = {};
joysticks["wasd"] = {
uy: "w",
dy: "s",
ux: "d",
dx: "a"
};
input.procdown = function() input.procdown = function()
{ {
for (var k in downkeys) for (var k in downkeys)
player[0].raw_input(keyname_extd(k), "down"); player[0].raw_input(keyname_extd(k), "down");
for (var i in joysticks) {
var joy = joysticks[i];
var x = joy.ux - joy.dx;
var y = joy.uy - joy.dy;
player[0].joy_input(i, joysticks[i]);
}
} }
input.print_md_kbm = function(pawn) { input.print_md_kbm = function(pawn) {
@ -301,6 +317,23 @@ var Player = {
}; };
}, },
joy_input(name, joystick) {
for (var pawn of this.pawns.reversed()) {
if (!pawn.inputs) return;
if (!pawn.inputs.joystick) return;
if (!pawn.inputs.joystick[name]) return;
var x = 0;
if (input.keyboard.down(joystick.ux)) x++;
if (input.keyboard.down(joystick.dx)) x--;
var y = 0;
if (input.keyboard.down(joystick.uy)) y++;
if (input.keyboard.down(joystick.dy)) y--;
pawn.inputs.joystick[name](x,y);
}
},
raw_input(cmd, state, ...args) { raw_input(cmd, state, ...args) {
for (var pawn of this.pawns.reversed()) { for (var pawn of this.pawns.reversed()) {
if (!pawn.inputs) { if (!pawn.inputs) {

4
scripts/nogame.js Normal file
View file

@ -0,0 +1,4 @@
this.hud = function()
{
mum.label("No game yet! Make game.js to get started!", {pos:game.size.scale(0.5), anchor:[0.5,0.5]});
}

View file

@ -154,49 +154,35 @@ render.set_camera = function(cam)
} }
var shader_cache = {}; var shader_cache = {};
var shader_times = {};
function strip_shader_inputs(shader) function strip_shader_inputs(shader)
{ {
for (var a of shader.vs.inputs) for (var a of shader.vs.inputs)
a.name = a.name.slice(2); a.name = a.name.slice(2);
} }
function make_shader(shader) render.hotreload = function()
{ {
if (shader_cache[shader]) return shader_cache[shader]; for (var i in shader_times) {
if (io.mod(i) <= shader_times[i]) continue;
var file = shader; say(`HOT RELOADING SHADER ${i}`);
shader = io.slurp(shader); shader_times[i] = io.mod(i);
if (!shader) { var obj = create_shader_obj(i);
console.info(`not found! slurping shaders/${file}`); obj = obj[os.sys()];
shader = io.slurp(`shaders/${file}`);
}
var writejson = `.prosperon/${file.name()}.shader.json`;
profile.cache("shader", file);
breakme: if (io.exists(writejson)) {
var data = json.decode(io.slurp(writejson));
var filemod = io.mod(writejson);
if (!data.files) break breakme;
for (var i of data.files) {
if (io.mod(i) > filemod) {
break breakme;
}
}
profile.endcache(" [cached]");
var shaderobj = json.decode(io.slurp(writejson));
var obj = shaderobj[os.sys()];
strip_shader_inputs(obj);
obj.pipe = render.pipeline(obj); obj.pipe = render.pipeline(obj);
shader_cache[shader] = obj; var old = shader_cache[i];
return obj; Object.assign(shader_cache[i], obj);
cur.bind = undefined;
cur.mesh = undefined;
} }
}
var out = `.prosperon/${file.name()}.shader`;
function create_shader_obj(file)
{
var files = [file]; var files = [file];
var out = ".prosperon/tmp.shader";
var shader = io.slurp(file);
var incs = shader.match(/#include <.*>/g); var incs = shader.match(/#include <.*>/g);
if (incs) if (incs)
@ -223,7 +209,6 @@ function make_shader(shader)
shader = shader.replace(/uniform\s+(\w+)\s+(\w+);/g, "uniform _$2 { $1 $2; };"); shader = shader.replace(/uniform\s+(\w+)\s+(\w+);/g, "uniform _$2 { $1 $2; };");
shader = shader.replace(/(texture2D|sampler) /g, "uniform $1 "); shader = shader.replace(/(texture2D|sampler) /g, "uniform $1 ");
// shader = shader.replace(/uniform texture2D ?(.*);/g, "uniform _$1_size { vec2 $1_size; };\nuniform texture2D $1;");
io.slurpwrite(out, shader); io.slurpwrite(out, shader);
@ -253,11 +238,11 @@ function make_shader(shader)
} }
add_code(obj.vs); add_code(obj.vs);
if (!obj.fs) if (!obj.fs && obj.vs.fs) {
if (obj.vs.fs) {
obj.fs = obj.vs.fs; obj.fs = obj.vs.fs;
delete obj.vs.fs; delete obj.vs.fs;
} }
add_code(obj.fs); add_code(obj.fs);
obj.blend = blend; obj.blend = blend;
@ -296,19 +281,58 @@ function make_shader(shader)
obj.name = file; obj.name = file;
strip_shader_inputs(obj);
compiled[platform] = obj; compiled[platform] = obj;
} }
compiled.files = files; compiled.files = files;
compiled.source = shader;
return compiled;
}
function make_shader(shader)
{
if (shader_cache[shader]) return shader_cache[shader];
var file = shader;
shader = io.slurp(file);
if (!shader) {
console.info(`not found! slurping shaders/${file}`);
shader = io.slurp(`shaders/${file}`);
}
var writejson = `.prosperon/${file.name()}.shader.json`;
profile.cache("shader", file);
breakme: if (io.exists(writejson)) {
var data = json.decode(io.slurp(writejson));
var filemod = io.mod(writejson);
if (!data.files) break breakme;
for (var i of data.files) {
if (io.mod(i) > filemod) {
break breakme;
}
}
profile.endcache(" [cached]");
var shaderobj = json.decode(io.slurp(writejson));
var obj = shaderobj[os.sys()];
obj.pipe = render.pipeline(obj);
shader_cache[file] = obj;
shader_times[file] = io.mod(file);
return obj;
}
var compiled = create_shader_obj(file);
io.slurpwrite(writejson, json.encode(compiled)); io.slurpwrite(writejson, json.encode(compiled));
profile.endcache();
var obj = compiled[os.sys()]; var obj = compiled[os.sys()];
strip_shader_inputs(obj);
obj.pipe = render.pipeline(obj); obj.pipe = render.pipeline(obj);
shader_cache[shader] = obj; shader_cache[file] = obj;
shader_times[file] = io.mod(file);
return obj; return obj;
} }
@ -372,7 +396,7 @@ function sg_bind(mesh, ssbo)
if (cur.shader.vs.inputs) if (cur.shader.vs.inputs)
for (var a of cur.shader.vs.inputs) { for (var a of cur.shader.vs.inputs) {
if (!(a.name in mesh)) { if (!(a.name in mesh)) {
console.error(`cannot draw shader ${cur.shader.name}; there is no attrib ${a.name} in the given mesh.`); console.error(`cannot draw shader ${cur.shader.name}; there is no attrib ${a.name} in the given mesh. ${json.encode(mesh)}`);
return undefined; return undefined;
} else } else
bind.attrib.push(mesh[a.name]); bind.attrib.push(mesh[a.name]);
@ -540,17 +564,25 @@ render.sprites = function render_sprites(gridsize = 1)
profile.endframe(); profile.endframe();
} }
render.circle = function render_circle(pos, radius, color) { render.circle = function render_circle(pos, radius, color, inner_radius = 1) {
flush(); flush();
if (inner_radius >= 1)
inner_radius = inner_radius/radius;
else if (inner_radius < 0)
inner_radius = 1.0;
var mat = { var mat = {
radius: radius, radius: radius,
inner_r: inner_radius,
coord: pos, coord: pos,
shade: color shade: color,
}; };
render.use_shader(circleshader); render.use_shader(circleshader);
render.use_mat(mat); render.use_mat(mat);
render.draw(shape.quad); render.draw(shape.quad);
} }
render.circle.doc = "Draw a circle at pos, with a given radius and color. If inner_radius is between 0 and 1, it acts as a percentage of radius. If it is above 1, is acts as a unit (usually a pixel).";
render.poly = function render_poly(points, color, transform) { render.poly = function render_poly(points, color, transform) {
var buffer = render.poly_prim(points); var buffer = render.poly_prim(points);
@ -799,7 +831,6 @@ render.set_font = function(path, size) {
} }
render.doc = "Draw shapes in screen space."; render.doc = "Draw shapes in screen space.";
//render.circle.doc = "Draw a circle at pos, with a given radius and color.";
render.cross.doc = "Draw a cross centered at pos, with arm length size."; render.cross.doc = "Draw a cross centered at pos, with arm length size.";
render.arrow.doc = "Draw an arrow from start to end, with wings of length wingspan at angle wingangle."; render.arrow.doc = "Draw an arrow from start to end, with wings of length wingspan at angle wingangle.";
render.rectangle.doc = "Draw a rectangle, with its corners at lowerleft and upperright."; render.rectangle.doc = "Draw a rectangle, with its corners at lowerleft and upperright.";
@ -972,13 +1003,17 @@ prosperon.process = function process() {
var dt = profile.secs(profile.now()) - frame_t; var dt = profile.secs(profile.now()) - frame_t;
frame_t = profile.secs(profile.now()); frame_t = profile.secs(profile.now());
profile.frame("hotreload");
actor.hotreload();
render.hotreload();
profile.endframe();
/* debugging: check for gc */ /* debugging: check for gc */
profile.print_gc(); profile.print_gc();
var cycles = os.check_cycles(); var cycles = os.check_cycles();
if (cycles) say(cycles); if (cycles) say(cycles);
profile.frame("app update"); profile.frame("app update");
prosperon.appupdate(dt); prosperon.appupdate(dt);
profile.endframe(); profile.endframe();

View file

@ -251,12 +251,18 @@ Cmdline.register_order("play", function(argv) {
var project = json.decode(io.slurp(projectfile)); var project = json.decode(io.slurp(projectfile));
game.title = project.title; game.title = project.title;
game.size = [1280,720];
window.size = game.size;
global.mixin("config.js"); global.mixin("config.js");
if (project.title) window.title = project.title; if (project.title) window.title = project.title;
game.engine_start(function() { game.engine_start(function() {
render.set_font("fonts/c64.ttf", 8); render.set_font("fonts/c64.ttf", 8);
global.app = actor.spawn("game.js"); if (io.exists("game.js"))
global.app = actor.spawn("game.js");
else
global.app = actor.spawn("scripts/nogame.js");
if (project.icon) window.set_icon(game.texture(project.icon)); if (project.icon) window.set_icon(game.texture(project.icon));
game.camera = world.spawn("scripts/camera2d"); game.camera = world.spawn("scripts/camera2d");
}); });

View file

@ -27,17 +27,19 @@ void main() {
in vec2 coords; in vec2 coords;
in float rad; in float rad;
uniform vec4 shade; uniform vec4 shade;
uniform float inner_r;
out vec4 color; out vec4 color;
void main() { void main() {
float px = 1/rad; float px = 1/rad;
float blur = 1.0+px;
float R = 1; float R = 1;
float R2 = 0.90; float R2 = 1-inner_r;
float dist = sqrt(dot(coords,coords)); float dist = distance(vec2(0,0),coords);
float sm = 1 - smoothstep(R-px,R,dist); float sm = 1 - smoothstep(R-px,R*blur,dist);
float sm2 = smoothstep(R2-px,R2,dist); float sm2 = smoothstep(R2-px,R2*blur,dist);
float alpha = sm*sm2; float alpha = sm*sm2;
color = vec4(shade.xyz, alpha*alpha); color = vec4(shade.xyz, alpha*shade.a);
} }
@end @end

View file

@ -157,6 +157,7 @@ void js_setprop_num(JSValue obj, uint32_t i, JSValue v) { JS_SetPropertyUint32(j
JSValue js_getpropidx(JSValue v, uint32_t i) JSValue js_getpropidx(JSValue v, uint32_t i)
{ {
if (!JS_IsArray(js, v)) return JS_UNDEFINED;
JSValue p = JS_GetPropertyUint32(js, v, i); JSValue p = JS_GetPropertyUint32(js, v, i);
JS_FreeValue(js,p); JS_FreeValue(js,p);
return p; return p;
@ -401,6 +402,13 @@ HMM_Vec3 js2vec3(JSValue v)
return v3; return v3;
} }
HMM_Vec3 js2vec3f(JSValue v)
{
HMM_Vec3 vec;
vec.x = vec.y = vec.z = js2number(v);
return vec;
}
JSValue vec32js(HMM_Vec3 v) JSValue vec32js(HMM_Vec3 v)
{ {
JSValue array = JS_NewArray(js); JSValue array = JS_NewArray(js);
@ -410,6 +418,8 @@ JSValue vec32js(HMM_Vec3 v)
return array; return array;
} }
JSValue vec3f2js(HMM_Vec3 v) { return vec32js(v); }
JSValue quat2js(HMM_Quat q) JSValue quat2js(HMM_Quat q)
{ {
JSValue arr = JS_NewArray(js); JSValue arr = JS_NewArray(js);
@ -428,6 +438,22 @@ HMM_Vec4 js2vec4(JSValue v)
return v4; return v4;
} }
double arr_vec_length(JSValue v)
{
int len = js_arrlen(v);
switch(len) {
case 2: return HMM_LenV2(js2vec2(v));
case 3: return HMM_LenV3(js2vec3(v));
case 4: return HMM_LenV4(js2vec4(v));
}
double sum = 0;
for (int i = 0; i < len; i++)
sum += pow(js2number(js_getpropidx(v, i)), 2);
return sqrt(sum);
}
HMM_Quat js2quat(JSValue v) HMM_Quat js2quat(JSValue v)
{ {
return js2vec4(v).quat; return js2vec4(v).quat;
@ -1242,7 +1268,14 @@ JSC_CCALL(vector_norm,
case 3: return vec32js(HMM_NormV3(js2vec3(argv[0]))); case 3: return vec32js(HMM_NormV3(js2vec3(argv[0])));
case 4: return vec42js(HMM_NormV4(js2vec4(argv[0]))); case 4: return vec42js(HMM_NormV4(js2vec4(argv[0])));
} }
return argv[0];
double length = arr_vec_length(argv[0]);
JSValue newarr = JS_NewArray(js);
for (int i = 0; i < len; i++)
js_setprop_num(newarr, i, number2js(js2number(js_getpropidx(argv[0],i))/length));
return newarr;
) )
JSC_CCALL(vector_angle_between, JSC_CCALL(vector_angle_between,
@ -1318,18 +1351,7 @@ JSC_CCALL(vector_angledist,
) )
JSC_CCALL(vector_length, JSC_CCALL(vector_length,
int len = js_arrlen(argv[0]); return number2js(arr_vec_length(argv[0]));
switch(len) {
case 2: return number2js(HMM_LenV2(js2vec2(argv[0])));
case 3: return number2js(HMM_LenV3(js2vec3(argv[0])));
case 4: return number2js(HMM_LenV4(js2vec4(argv[0])));
}
double sum = 0;
for (int i = 0; i < len; i++)
sum += pow(js2number(js_getpropidx(argv[0], i)), 2);
return number2js(sqrt(sum));
) )
double r2() double r2()
@ -1853,7 +1875,7 @@ static const JSCFunctionListEntry js_physics_funcs[] = {
}; };
JSC_GETSET(transform, pos, vec3) JSC_GETSET(transform, pos, vec3)
JSC_GETSET(transform, scale, vec3) JSC_GETSET(transform, scale, vec3f)
JSC_GETSET(transform, rotation, quat) JSC_GETSET(transform, rotation, quat)
JSC_CCALL(transform_move, transform_move(js2transform(self), js2vec3(argv[0])); ) JSC_CCALL(transform_move, transform_move(js2transform(self), js2vec3(argv[0])); )
@ -2617,6 +2639,10 @@ JSC_CCALL(os_dump_mem,
ret = tmp2js(tmp); ret = tmp2js(tmp);
) )
JSC_CCALL(os_value_id,
return number2js((intptr_t)JS_VALUE_GET_PTR(argv[0]));
)
static double gc_t = 0; static double gc_t = 0;
static double gc_mem = 0; static double gc_mem = 0;
static double gc_startmem = 0; static double gc_startmem = 0;
@ -3106,7 +3132,8 @@ static const JSCFunctionListEntry js_os_funcs[] = {
MIST_FUNC_DEF(os, calc_mem, 1), MIST_FUNC_DEF(os, calc_mem, 1),
MIST_FUNC_DEF(os, check_gc, 0), MIST_FUNC_DEF(os, check_gc, 0),
MIST_FUNC_DEF(os, check_cycles, 0), MIST_FUNC_DEF(os, check_cycles, 0),
MIST_FUNC_DEF(os, memstate, 0) MIST_FUNC_DEF(os, memstate, 0),
MIST_FUNC_DEF(os, value_id, 1)
}; };
#include "steam.h" #include "steam.h"

View file

@ -158,8 +158,10 @@ time_t file_mod_secs(const char *file) {
struct stat attr; struct stat attr;
mz_uint index; mz_uint index;
mz_zip_archive_file_stat pstat; mz_zip_archive_file_stat pstat;
if ((index = mz_zip_reader_locate_file(&game_cdb, file, NULL, 0)) != -1) { if (!stat(file,&attr))
return attr.st_mtime;
else if ((index = mz_zip_reader_locate_file(&game_cdb, file, NULL, 0)) != -1) {
mz_zip_reader_file_stat(&game_cdb, index,&pstat); mz_zip_reader_file_stat(&game_cdb, index,&pstat);
return pstat.m_time; return pstat.m_time;
} }
@ -170,7 +172,7 @@ time_t file_mod_secs(const char *file) {
else else
stat(file, &attr); stat(file, &attr);
return attr.st_mtime; return -1;
} }
// TODO: Not reentrant // TODO: Not reentrant