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();
Object.defineProperty(Array.prototype, 'normalized', {
value: function() {
var c = this.slice();
var len = Vector.length(c);
return c.map(v => v/len);
}
value: function() { return vector.norm(this); }
});
Object.defineProperty(Array.prototype, 'newfirst', {

View file

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

View file

@ -213,10 +213,26 @@ input.print_pawn_kbm = function(pawn) {
return str;
};
var joysticks = {};
joysticks["wasd"] = {
uy: "w",
dy: "s",
ux: "d",
dx: "a"
};
input.procdown = function()
{
for (var k in downkeys)
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) {
@ -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) {
for (var pawn of this.pawns.reversed()) {
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_times = {};
function strip_shader_inputs(shader)
{
for (var a of shader.vs.inputs)
a.name = a.name.slice(2);
}
function make_shader(shader)
render.hotreload = function()
{
if (shader_cache[shader]) return shader_cache[shader];
var file = shader;
shader = io.slurp(shader);
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()];
strip_shader_inputs(obj);
for (var i in shader_times) {
if (io.mod(i) <= shader_times[i]) continue;
say(`HOT RELOADING SHADER ${i}`);
shader_times[i] = io.mod(i);
var obj = create_shader_obj(i);
obj = obj[os.sys()];
obj.pipe = render.pipeline(obj);
shader_cache[shader] = obj;
return obj;
var old = shader_cache[i];
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 out = ".prosperon/tmp.shader";
var shader = io.slurp(file);
var incs = shader.match(/#include <.*>/g);
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(/(texture2D|sampler) /g, "uniform $1 ");
// shader = shader.replace(/uniform texture2D ?(.*);/g, "uniform _$1_size { vec2 $1_size; };\nuniform texture2D $1;");
io.slurpwrite(out, shader);
@ -253,11 +238,11 @@ function make_shader(shader)
}
add_code(obj.vs);
if (!obj.fs)
if (obj.vs.fs) {
if (!obj.fs && obj.vs.fs) {
obj.fs = obj.vs.fs;
delete obj.vs.fs;
}
}
add_code(obj.fs);
obj.blend = blend;
@ -296,19 +281,58 @@ function make_shader(shader)
obj.name = file;
strip_shader_inputs(obj);
compiled[platform] = obj;
}
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));
profile.endcache();
var obj = compiled[os.sys()];
strip_shader_inputs(obj);
obj.pipe = render.pipeline(obj);
shader_cache[shader] = obj;
shader_cache[file] = obj;
shader_times[file] = io.mod(file);
return obj;
}
@ -372,7 +396,7 @@ function sg_bind(mesh, ssbo)
if (cur.shader.vs.inputs)
for (var a of cur.shader.vs.inputs) {
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;
} else
bind.attrib.push(mesh[a.name]);
@ -540,17 +564,25 @@ render.sprites = function render_sprites(gridsize = 1)
profile.endframe();
}
render.circle = function render_circle(pos, radius, color) {
render.circle = function render_circle(pos, radius, color, inner_radius = 1) {
flush();
if (inner_radius >= 1)
inner_radius = inner_radius/radius;
else if (inner_radius < 0)
inner_radius = 1.0;
var mat = {
radius: radius,
inner_r: inner_radius,
coord: pos,
shade: color
shade: color,
};
render.use_shader(circleshader);
render.use_mat(mat);
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) {
var buffer = render.poly_prim(points);
@ -799,7 +831,6 @@ render.set_font = function(path, size) {
}
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.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.";
@ -972,13 +1003,17 @@ prosperon.process = function process() {
var dt = profile.secs(profile.now()) - frame_t;
frame_t = profile.secs(profile.now());
profile.frame("hotreload");
actor.hotreload();
render.hotreload();
profile.endframe();
/* debugging: check for gc */
profile.print_gc();
var cycles = os.check_cycles();
if (cycles) say(cycles);
profile.frame("app update");
prosperon.appupdate(dt);
profile.endframe();

View file

@ -251,12 +251,18 @@ Cmdline.register_order("play", function(argv) {
var project = json.decode(io.slurp(projectfile));
game.title = project.title;
game.size = [1280,720];
window.size = game.size;
global.mixin("config.js");
if (project.title) window.title = project.title;
game.engine_start(function() {
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));
game.camera = world.spawn("scripts/camera2d");
});

View file

@ -27,17 +27,19 @@ void main() {
in vec2 coords;
in float rad;
uniform vec4 shade;
uniform float inner_r;
out vec4 color;
void main() {
float px = 1/rad;
float blur = 1.0+px;
float R = 1;
float R2 = 0.90;
float dist = sqrt(dot(coords,coords));
float sm = 1 - smoothstep(R-px,R,dist);
float sm2 = smoothstep(R2-px,R2,dist);
float R2 = 1-inner_r;
float dist = distance(vec2(0,0),coords);
float sm = 1 - smoothstep(R-px,R*blur,dist);
float sm2 = smoothstep(R2-px,R2*blur,dist);
float alpha = sm*sm2;
color = vec4(shade.xyz, alpha*alpha);
color = vec4(shade.xyz, alpha*shade.a);
}
@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)
{
if (!JS_IsArray(js, v)) return JS_UNDEFINED;
JSValue p = JS_GetPropertyUint32(js, v, i);
JS_FreeValue(js,p);
return p;
@ -401,6 +402,13 @@ HMM_Vec3 js2vec3(JSValue v)
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 array = JS_NewArray(js);
@ -410,6 +418,8 @@ JSValue vec32js(HMM_Vec3 v)
return array;
}
JSValue vec3f2js(HMM_Vec3 v) { return vec32js(v); }
JSValue quat2js(HMM_Quat q)
{
JSValue arr = JS_NewArray(js);
@ -428,6 +438,22 @@ HMM_Vec4 js2vec4(JSValue v)
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)
{
return js2vec4(v).quat;
@ -1242,7 +1268,14 @@ JSC_CCALL(vector_norm,
case 3: return vec32js(HMM_NormV3(js2vec3(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,
@ -1318,18 +1351,7 @@ JSC_CCALL(vector_angledist,
)
JSC_CCALL(vector_length,
int len = js_arrlen(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));
return number2js(arr_vec_length(argv[0]));
)
double r2()
@ -1853,7 +1875,7 @@ static const JSCFunctionListEntry js_physics_funcs[] = {
};
JSC_GETSET(transform, pos, vec3)
JSC_GETSET(transform, scale, vec3)
JSC_GETSET(transform, scale, vec3f)
JSC_GETSET(transform, rotation, quat)
JSC_CCALL(transform_move, transform_move(js2transform(self), js2vec3(argv[0])); )
@ -2617,6 +2639,10 @@ JSC_CCALL(os_dump_mem,
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_mem = 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, check_gc, 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"

View file

@ -158,8 +158,10 @@ time_t file_mod_secs(const char *file) {
struct stat attr;
mz_uint index;
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);
return pstat.m_time;
}
@ -170,7 +172,7 @@ time_t file_mod_secs(const char *file) {
else
stat(file, &attr);
return attr.st_mtime;
return -1;
}
// TODO: Not reentrant