This commit is contained in:
2024-12-09 16:00:10 -06:00
parent a5b0088695
commit 42cfccfc33
13 changed files with 337 additions and 224 deletions

View File

@@ -664,6 +664,13 @@ Object.defineProperty(String.prototype, "ext", {
},
});
Object.defineProperty(String.prototype, 'has_ext', {
value: function() {
var lastdot = this.lastIndexOf('.');
return lastdot > 0 && lastdot < this.length-1;
}
});
Object.defineProperty(String.prototype, "set_ext", {
value: function (val) {
return this.strip_ext() + val;

View File

@@ -773,4 +773,4 @@ Object.mix(os.make_circle2d(), {
},
});
return { component, SpriteAnim };
return { component };

View File

@@ -153,18 +153,21 @@ Resources.is_image = function (path) {
var res_cache = {};
function find_ext(file, ext, root = "") {
// ext is a list of extensions to search
function find_ext(file, ext) {
if (!file) return;
var file_ext = file.ext();
var has_ext = file_ext.length > 0;
if (ext.some(x => x === file_ext)) return file;
for (var e of ext) {
var nf = `${file}.${e}`;
if (io.exists(nf)) return nf;
}
var all_files = io.glob(`**/${file}.*`);
var glob_pat = has_ext ? `**/${file}` : `**/${file}.*`;
var all_files = io.glob(glob_pat);
var find = undefined;
for (var e of ext) {
var finds = all_files.filter(x => x.ext() === e);
@@ -338,13 +341,20 @@ io.slurpbytes = function(path)
}
var ignore = io.slurp('.prosperonignore').split('\n');
var allpaths = io.globfs(ignore);
var allpaths;
var tmpglob = io.glob;
io.glob = function glob(pat) {
// var allpaths = io.globfs(ignore);
if (!allpaths)
allpaths = io.globfs(ignore);
return allpaths.filter(str => game.glob(pat,str)).sort();
}
io.invalidate = function()
{
allpaths = undefined;
}
function splitPath(path) {
return path.split('/').filter(part => part.length > 0);
}

View File

@@ -103,13 +103,13 @@ var mousepos = [0, 0];
prosperon.textinput = function (c) {
player[0].raw_input("char", "pressed", c);
};
prosperon.mousemove = function (pos, dx) {
prosperon.mouse_move = function (pos, dx) {
pos.y *= -1;
dx.y *= -1;
mousepos = pos;
player[0].mouse_input("move", pos, dx);
};
prosperon.mousescroll = function mousescroll(dx) {
prosperon.mouse_scroll = function mousescroll(dx) {
player[0].mouse_input(modstr() + "scroll", dx);
};
prosperon.mousedown = function mousedown(b) {

View File

@@ -250,54 +250,12 @@ function pack_into_sheet(images)
return spritesheet;
}
var SpriteAnim = {};
globalThis.SpriteAnim = SpriteAnim;
SpriteAnim.aseprite = function (path) {
function aseframeset2anim(frameset, meta) {
var anim = {};
anim.frames = [];
var dim = meta.size;
var ase_make_frame = function (ase_frame) {
var f = ase_frame.frame;
var frame = {};
frame.rect = {
x: f.x / dim.w,
y: f.y / dim.h,
width: f.w / dim.w,
height: f.h / dim.h,
};
frame.time = ase_frame.duration / 1000;
anim.frames.push(frame);
};
frameset.forEach(ase_make_frame);
anim.dim = frameset[0].sourceSize;
anim.loop = true;
return anim;
}
var data = json.decode(io.slurp(path));
if (!data?.meta?.app.includes("aseprite")) return;
var anims = {};
var frames = Array.isArray(data.frames) ? data.frames : Object.values(data.frames);
if (!data.meta.frameTags || data.meta.frameTags.length === 0) {
anims[0] = aseframeset2anim(frames, data.meta);
return anims;
}
for (var tag of data.meta.frameTags)
anims[tag.name] = aseframeset2anim(frames.slice(tag.from, tag.to + 1), data.meta);
return anims;
};
game.is_image = function(obj)
{
if (obj.texture && obj.rect) return true;
}
// Any request to it returns an image, which is a texture and rect. But they can
// Any request to it returns an image, which is a texture and rect.
game.texture = function texture(path) {
if (typeof path !== 'string') {
return path;
@@ -307,10 +265,22 @@ game.texture = function texture(path) {
path = Resources.find_image(parts[0]);
if (game.texture.cache[path]) return game.texture.cache[path];
var newimg = {};
var data = io.slurpbytes(path);
newimg.surface = os.make_texture(data);
newimg.texture = render._main.load_texture(newimg.surface);
var newimg;
switch(path.ext()) {
case 'gif':
newimg = os.make_gif(data);
break;
case 'ase':
newimg = os.make_aseprite(data);
break;
default:
newimg = {
surface: os.make_texture(data)
};
newimg.texture = render._main.load_texture(newimg.surface);
break;
}
game.texture.cache[path] = newimg;
return newimg;
@@ -399,9 +369,6 @@ game.texture = function texture(path) {
var image;
var anim;
if (io.exists(path.set_ext(".json")))
anim = SpriteAnim.aseprite(path.set_ext(".json"));
if (!anim) {
image = {
texture: tex,
@@ -490,16 +457,6 @@ 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.`;
prosperon.iconified = function (icon) {};
prosperon.focus = function (focus) {};
prosperon.suspended = function (sus) {};
prosperon.mouseenter = function () {};
prosperon.mouseleave = function () {};
prosperon.touchpress = function (touches) {};
prosperon.touchrelease = function (touches) {};
prosperon.touchmove = function (touches) {};
prosperon.clipboardpaste = function (str) {};
global.mixin("input");
global.mixin("std");
global.mixin("diff");
@@ -669,6 +626,24 @@ function world_start() {
game.cam = world;
}
function make_spritesheet(paths, width, height)
{
var textures = paths.map(path => game.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");
gamestate.spritess = sheet;
sheet.load_gpu();
}
return {
Register,
sim,

View File

@@ -1038,47 +1038,23 @@ render.tile = function tile(image, rect = [0,0], color = Color.white)
return;
}
render.image = function image(image, rect = [0,0], rotation = 0, color) {
render.image = function image(image, rect = [0,0], rotation = 0, color = Color.white) {
if (!image) throw Error ('Need an image to render.')
if (typeof image === "string")
image = game.texture(image);
rect.width ??= image.texture.width;
rect.height ??= image.texture.height;
render._main.texture(image.texture, rect, image.rect, color);
return;
var tex = image.texture;
if (!tex) return;
var image_size = calc_image_size(image); //image.size;
var size = [rect.width ? rect.width : image_size.x, rect.height ? rect.height : image_size.y];
if (!lasttex) {
check_flush(flush_img);
lasttex = tex;
}
if (lasttex !== tex) {
flush_img();
lasttex = tex;
}
render._main.texture(image.texture, rect, image.rect);
return;
var e = img_e();
var pos = [rect.x,rect.y].sub(size.scale([rect.anchor_x, rect.anchor_y]));
e.transform.trs(pos, undefined, size);
e.image = image;
e.shade = color;
return;
};
render.images = function images(image, rects)
{
if (!image) throw Error ('Need an image to render.');
if (typeof image === "string") image = game.texture(image);
for (var rect of rects) render.image(image,rect);
return;
var tex = image.texture;
if (!tex) return;
@@ -1109,34 +1085,16 @@ render.images = function images(image, rects)
return;
}
var slice9_t = os.make_transform();
// pos is the lower left corner, scale is the width and height
// slice is given in pixels
render.slice9 = function slice9(image, rect = [0,0], slice = 0, color = Color.white) {
if (typeof image === 'string')
image = game.texture(image);
var tex = image.texture;
var image_size = calc_image_size(image);
var size = [rect.width ? rect.width : image_size.x, rect.height ? rect.height : image_size.y];
rect.width ??= image.texture.width;
rect.height ??= image.texture.height;
slice = clay.normalizeSpacing(slice);
check_flush();
slice9_t.trs([rect.x,rect.y].sub(size.scale([rect.anchor_x, rect.anchor_y])), undefined, size);
slice = clay.normalizeSpacing(slice);
var border = [slice.l/image_size.x, slice.b/image_size.y, slice.r/image_size.x, slice.t/image_size.y];
render.use_shader(slice9shader);
set_model(slice9_t);
render.use_mat({
shade: color,
diffuse: tex,
win_tex_scale: size.div(image_size),
rect: [image.rect.x, image.rect.y,image.rect.width,image.rect.height],
frag_rect: [image.rect.x, image.rect.y,image.rect.width,image.rect.height],
border: border,
});
render.draw(shape.quad);
render._main.slice9(image.texture, rect, slice);
};
var textssbos = [];
@@ -1202,6 +1160,7 @@ render.scissor = function(rect)
// Camera viewport is a rectangle with the bottom left corner defined as x,y. Units are pixels on the window.
function camviewport() {
var aspect = (((this.viewport[2] - this.viewport[0]) / (this.viewport[3] - this.viewport[1])) * prosperon.size.x) / prosperon.size.y;
var raspect = this.size.x / this.size.y;
@@ -1288,16 +1247,6 @@ function screen2cam(pos) {
return viewpos;
}
function camextents() {
var half = this.size; //.scale(0.5);
return {
l: this.pos.x - half.x,
r: this.pos.x + half.x,
t: this.pos.y + half.y,
b: this.pos.y - half.y,
};
}
screen2cam.doc = "Convert a screen space position in pixels to a normalized viewport position in a camera.";
prosperon.gizmos = function gizmos() {
@@ -1314,20 +1263,33 @@ function screen2hud(pos)
return campos;
}
/* cameras
* Cameras have a position and rotation. They are not affected by scale.
*/
prosperon.make_camera = function make_camera() {
var cam = world.spawn();
cam.near = 1;
cam.far = -1000;
cam.ortho = true;
cam.ortho = true; // True if this is a 2d camera
cam.viewport = [0, 0, 1, 1]; // normalized screen coordinates of where to draw
cam.size = game.size.slice(); // The render size of this camera in pixels
cam.size = prosperon.size.slice() // The render size of this camera in pixels
// In ortho mode, this determines how many pixels it will see
cam.mode = "stretch";
cam.screen2world = camscreen2world;
cam.screen2cam = screen2cam;
cam.screen2hud = screen2hud;
cam.extents = camextents;
cam.view = camviewport;
cam.zoom = 1; // the "scale factor" this camera demonstrates
// camera renders draw calls, and then hud
cam.render = function() {
render._main.camera(this.transform);
render._main.scale([this.zoom, this.zoom]);
prosperon.draw();
render._main.scale([1,1]);
render._main.camera(unit_transform);
prosperon.hud();
}
return cam;
};
@@ -1434,14 +1396,14 @@ prosperon.render = function prosperon_render() {
try{
render._main.draw_color(clearcolor);
render._main.clear();
// set to world cam
prosperon.draw();
if (render.draw_hud) prosperon.hud();
// render each camera
prosperon.camera.render();
// prosperon.app();
// if (debug.show) imgui_fn();
} catch(e) {
throw e;
console.log(e);
console.log(e.stack)
// throw e;
} finally {
render._main.present();
tracy.end_frame();
@@ -1452,6 +1414,8 @@ if (dmon) dmon.watch('.');
function dmon_cb(e)
{
io.invalidated();
if (e.file.startsWith('.')) return;
if (e.file.endsWith('.js'))
actor.hotreload(e.file);
@@ -1470,13 +1434,33 @@ prosperon.process = function process() {
frame_t = profile.now();
prosperon.appupdate(dt);
input.procdown();
if (sim.mode === "play" || sim.mode === "step") {
prosperon.update(dt * game.timescale);
update_emitters(dt * game.timescale);
os.update_timers(dt * game.timescale);
if (sim.mode === "step") sim.pause();
}
game.engine_input(e => {
switch(e.type) {
case "quit":
os.exit(0);
break;
case "mouse":
prosperon.mousemove(e.mouse, e.mouse_d);
break;
case "text":
prosperon.textinput(e.text);
break;
case "key":
if (e.down)
prosperon.keydown(e.key);
else
prosperon.keyup(e.key);
break;
}
});
update_emitters(dt * game.timescale);
os.update_timers(dt * game.timescale);
prosperon.update(dt*game.timescale);
if (sim.mode === "step") sim.pause();
if (sim.mode === "play" || sim.mode === "step") {
/*

View File

@@ -83,6 +83,10 @@ Resources.gif.frames = function (path) {
return render.gif_frames(path);
};
prosperon.SIGINT = function() {
os.exit();
}
/*
io path rules. Starts with, meaning:
"@": user path
@@ -221,15 +225,14 @@ Cmdline.register_order(
}
var project = json.decode(io.slurp(projectfile));
game.title = project.title;
game.size = [1280, 720];
prosperon.title = project.title;
prosperon.width = 1280;
prosperon.height = 720;
prosperon.size = [1280,720];
prosperon.size = [1280, 720];
prosperon.cleanup = function(){}
prosperon.event = function(e){
prosperon[e.type]?.(e);
switch(e.type) {
case "mouse_move":
prosperon.mousemove(e.mouse, e.mouse_d);
@@ -252,34 +255,9 @@ Cmdline.register_order(
case "char":
prosperon.textinput(e.char_code);
break;
case "resized":
prosperon.size = e.window_size;
prosperon.resize?.(e.window_size);
break;
case "iconified":
prosperon.iconified(false);
break;
case "restored":
prosperon.iconified(true);
break;
case "focused":
prosperon.focus(true);
break;
case "unfocused":
prosperon.focus(false);
break;
case "suspended":
prosperon.suspended(true);
break;
case "quit_requested":
os.exit(0);
break;
case "mouse_enter":
prosperon.mouseenter();
break;
case "mouse_leave":
prosperon.mouseleave();
break;
case "files_dropped":
console.log(json.encode(e));
break;
@@ -300,8 +278,8 @@ Cmdline.register_order(
if (io.exists("config.js")) global.mixin("config.js");
else console.warn("No config.js file found. Starting with default parameters.");
var window = game.engine_start(prosperon);
var renderer = window.make_renderer("gpu");
prosperon.window = game.engine_start(prosperon);
var renderer = prosperon.window.make_renderer("gpu");
render._main = renderer;
prosperon.init();

View File

@@ -1801,6 +1801,24 @@ HMM_Mat4 HMM_M4TRS(HMM_Vec3 t, HMM_Quat q, HMM_Vec3 s)
return l;
}
HMM_Mat3 HMM_M3TRS(HMM_Vec2 pos, float angle, HMM_Vec2 s)
{
HMM_Mat3 m;
float *lm = (float*)&m;
lm[0] = s.x*cos(angle);
lm[1] = s.x * sin(angle);
lm[2] = 0;
lm[3] = -s.y*sin(angle);
lm[4] = s.y*cos(angle);
lm[5] = 0;
lm[6] = pos.x;
lm[7] = pos.y;
lm[8] = 1;
return m;
}
// This method taken from Mike Day at Insomniac Games.
// https://d3cw3dd2w32x2b.cloudfront.net/wp-content/uploads/2015/01/matrix-to-quat.pdf
//

View File

@@ -660,6 +660,7 @@ HMM_Quat HMM_NLerp(HMM_Quat Left, float Time, HMM_Quat Right);
HMM_Quat HMM_SLerp(HMM_Quat Left, float Time, HMM_Quat Right);
HMM_Mat4 HMM_QToM4(HMM_Quat Left);
HMM_Mat4 HMM_M4TRS(HMM_Vec3 t, HMM_Quat q, HMM_Vec3 s);
HMM_Mat3 HMM_M3TRS(HMM_Vec2 t, float angle, HMM_Vec2 s);
HMM_Quat HMM_M4ToQ_RH(HMM_Mat4 M);
HMM_Quat HMM_M4ToQ_LH(HMM_Mat4 M);

View File

@@ -109,6 +109,7 @@ double js_getnum_uint32(JSContext *js, JSValue v, unsigned int i)
return ret;
}
static HMM_Mat3 cam_mat;
static HMM_Vec2 campos = (HMM_Vec2){0,0};
static HMM_Vec2 logical = {0};
@@ -260,6 +261,11 @@ void SDL_Camera_free(JSRuntime *rt, SDL_Camera *cam)
SDL_CloseCamera(cam);
}
void SDL_Cursor_free(JSRuntime *rt, SDL_Cursor *c)
{
SDL_DestroyCursor(c);
}
QJSCLASS(transform)
QJSCLASS(font)
//QJSCLASS(warp_gravity)
@@ -267,9 +273,9 @@ QJSCLASS(font)
QJSCLASS(datastream)
QJSCLASS(timer)
QJSCLASS(skin)
QJSCLASS(SDL_Window)
QJSCLASS(SDL_Renderer)
QJSCLASS(SDL_Camera)
QJSCLASS(SDL_Texture,
TracyCAllocN(n, n->w*n->h*4, "vram");
@@ -282,6 +288,8 @@ QJSCLASS(SDL_Surface,
JS_SetProperty(js,j,height_atom,number2js(js,n->h));
)
QJSCLASS(SDL_Cursor)
int js_arrlen(JSContext *js,JSValue v) {
if (JS_IsUndefined(v)) return 0;
int len;
@@ -511,13 +519,23 @@ rect js2rect(JSContext *js,JSValue v) {
JS_FreeValue(js,yv);
float anchor_x = js_getnum(js,v, anchor_x_atom);
float anchor_y = js_getnum(js,v, anchor_y_atom);
rect.y -= anchor_y*rect.h;
rect.x -= anchor_x*rect.w;
return rect;
}
rect transform_rect(rect in, HMM_Mat3 *t)
{
in.y *= -1;
in.y += logical.y;
in.y -= in.h;
in.x -= t->Columns[2].x;
in.y -= t->Columns[2].y;
return in;
}
JSValue rect2js(JSContext *js,rect rect) {
JSValue obj = JS_NewObject(js);
JS_SetProperty(js, obj, x_atom, number2js(js,rect.x));
@@ -733,8 +751,6 @@ JSC_CCALL(os_make_text_buffer,
for (int i = 0; i < arrlen(buffer); i++) {
pos[i] = buffer[i].pos;
pos[i].y *= -1;
pos[i].y += logical.y;
uv[i] = buffer[i].uv;
color[i] = buffer[i].color;
}
@@ -1426,9 +1442,9 @@ JSC_CCALL(game_open_camera,
#include "wildmatch.h"
JSC_SSCALL(game_glob,
if (wildmatch(str, str2, WM_PATHNAME | WM_PERIOD | WM_WILDSTAR) == WM_MATCH)
return JS_NewBool(js,1);
ret = JS_NewBool(js,1);
else
return JS_NewBool(js, 0);
ret = JS_NewBool(js, 0);
)
JSC_CCALL(game_camera_name,
@@ -1481,10 +1497,84 @@ JSValue js_SDL_Window_keyboard_shown(JSContext *js, JSValue self) {
return JS_NewBool(js,SDL_ScreenKeyboardShown(window));
}
JSValue js_window_theme(JSContext *js, JSValue self)
{
return JS_UNDEFINED;
}
JSValue js_window_safe_area(JSContext *js, JSValue self)
{
SDL_Window *w = js2SDL_Window(js,self);
rect r;
SDL_GetWindowSafeArea(w, &r);
return rect2js(js,r);
}
JSValue js_window_bordered(JSContext *js, JSValue self, int argc, JSValue *argv)
{
SDL_Window *w = js2SDL_Window(js,self);
SDL_SetWindowBordered(w, JS_ToBool(js,argv[0]));
return JS_UNDEFINED;
}
JSValue js_window_get_title(JSContext *js, JSValue self)
{
SDL_Window *w = js2SDL_Window(js,self);
const char *title = SDL_GetWindowTitle(w);
return JS_NewString(js,title);
}
JSValue js_window_set_title(JSContext *js, JSValue self, JSValue val)
{
SDL_Window *w = js2SDL_Window(js,self);
const char *title = JS_ToCString(js,val);
SDL_SetWindowTitle(w,title);
JS_FreeCString(js,title);
return JS_UNDEFINED;
}
JSValue js_window_get_size(JSContext *js, JSValue self)
{
SDL_Window *win = js2SDL_Window(js,self);
int w, h;
SDL_GetWindowSize(win, &w, &h);
return vec22js(js, (HMM_Vec2){w,h});
}
JSValue js_window_set_size(JSContext *js, JSValue self, JSValue val)
{
SDL_Window *w = js2SDL_Window(js,self);
HMM_Vec2 size = js2vec2(js,val);
SDL_SetWindowSize(w,size.x,size.y);
}
JSValue js_window_set_icon(JSContext *js, JSValue self, int argc, JSValue *argv)
{
SDL_Window *w = js2SDL_Window(js,self);
SDL_Surface *s = js2SDL_Surface(js,argv[0]);
if (!SDL_SetWindowIcon(w,s))
return JS_ThrowReferenceError(js, "could not set window icon: %s", SDL_GetError());
}
JSValue js_window_mouse_grab(JSContext *js, JSValue self, int argc, JSValue *argv)
{
SDL_Window *w = js2SDL_Window(js,self);
SDL_SetWindowMouseGrab(w, JS_ToBool(js,argv[0]));
}
static const JSCFunctionListEntry js_SDL_Window_funcs[] = {
MIST_FUNC_DEF(SDL_Window, fullscreen, 0),
MIST_FUNC_DEF(SDL_Window, make_renderer, 1),
MIST_FUNC_DEF(SDL_Window, keyboard_shown, 0),
MIST_FUNC_DEF(window, theme, 0),
MIST_FUNC_DEF(window, safe_area, 0),
MIST_FUNC_DEF(window, bordered, 1),
MIST_FUNC_DEF(window, set_icon, 1),
CGETSET_ADD(window, title),
CGETSET_ADD(window, size),
MIST_FUNC_DEF(window, mouse_grab, 1),
};
JSC_CCALL(SDL_Renderer_clear,
@@ -1514,10 +1604,7 @@ JSC_CCALL(SDL_Renderer_rect,
rect rects[len];
for (int i = 0; i < len; i++) {
JSValue val = JS_GetPropertyUint32(js,argv[0],i);
rects[i] = js2rect(js,val);
rects[i].y *= -1;
rects[i].y += logical.y;
rects[i].y -= rects[i].h;
rects[i] = transform_rect(js2rect(js,val), &cam_mat);
JS_FreeValue(js,val);
}
SDL_RenderRects(r,rects,len);
@@ -1525,9 +1612,7 @@ JSC_CCALL(SDL_Renderer_rect,
}
rect rect = js2rect(js,argv[0]);
rect.y *= -1;
rect.y += logical.y;
rect.y -= rect.h;
rect = transform_rect(rect, &cam_mat);
SDL_RenderRect(r, &rect);
)
@@ -1558,24 +1643,19 @@ JSC_CCALL(SDL_Renderer_fillrect,
rects[i] = js2rect(js,val);
JS_FreeValue(js,val);
}
SDL_RenderFillRects(r,rects,len);
return JS_UNDEFINED;
if (!SDL_RenderFillRects(r,rects,len))
return JS_ThrowReferenceError("Could not render rectangle: %s", SDL_GetError());
}
rect rect = js2rect(js,argv[0]);
rect.y *= -1;
rect.y += logical.y;
rect.y -= rect.h;
rect rect = transform_rect(js2rect(js,argv[0]),&cam_mat);
SDL_RenderFillRect(r, &rect);
if (!SDL_RenderFillRect(r, &rect))
return JS_ThrowReferenceError("Could not render rectangle: %s", SDL_GetError());
)
JSC_CCALL(renderer_texture,
SDL_Renderer *renderer = js2SDL_Renderer(js,self);
SDL_Texture *tex = js2SDL_Texture(js,argv[0]);
rect dst = js2rect(js,argv[1]);
dst.y *= -1;
dst.y += logical.y;
dst.y -= dst.h;
rect dst = transform_rect(js2rect(js,argv[1]), &cam_mat);
if (!JS_IsUndefined(argv[3])) {
colorf color = js2color(js,argv[3]);
@@ -1588,7 +1668,7 @@ JSC_CCALL(renderer_texture,
rect src = js2rect(js,argv[2]);
SDL_RenderTexture(renderer,tex,&src,&dst);
SDL_RenderTextureRotated(renderer, tex, &src, &dst, 0, NULL, SDL_FLIP_NONE);
}
)
@@ -1610,12 +1690,18 @@ JSC_CCALL(renderer_tile,
}
)
JSC_CCALL(renderer_9slice,
JSC_CCALL(renderer_slice9,
SDL_Renderer *renderer = js2SDL_Renderer(js,self);
SDL_Texture *tex = js2SDL_Texture(js,argv[0]);
rect *dst, *src = NULL;
lrtb bounds = js2lrtb(js,argv[2]);
SDL_RenderTexture9Grid(renderer, tex, src, bounds.l, bounds.r, bounds.t, bounds.b, 0.0, dst);
rect src, dst;
src = transform_rect(js2rect(js,argv[3]),&cam_mat);
dst = transform_rect(js2rect(js,argv[1]), &cam_mat);
SDL_RenderTexture9Grid(renderer, tex,
JS_IsUndefined(argv[3]) ? NULL : &src,
bounds.l, bounds.r, bounds.t, bounds.b, 0.0,
JS_IsUndefined(argv[1]) ? NULL : &dst);
)
JSC_CCALL(renderer_get_image,
@@ -1734,10 +1820,8 @@ JSC_CCALL(renderer_geometry,
HMM_Vec2 *trans_pos = malloc(vertices*sizeof(HMM_Vec2));
memcpy(trans_pos,posdata, sizeof(HMM_Vec2)*vertices);
for (int i = 0; i < vertices; i++) {
trans_pos[i].x -= campos.x;
trans_pos[i].y -= campos.y;
}
for (int i = 0; i < vertices; i++)
trans_pos[i] = HMM_MulM3V3(cam_mat, (HMM_Vec3){trans_pos[i].x, trans_pos[i].y, 1}).xy;
if (!SDL_RenderGeometryRaw(r, tex, trans_pos, pos_stride,colordata,color_stride,uvdata, uv_stride, vertices, idxdata, count, indices_stride))
ret = JS_ThrowReferenceError(js, "Error rendering geometry: %s",SDL_GetError());
@@ -1800,6 +1884,21 @@ JSC_CCALL(renderer_coords,
return vec22js(js,coord);
)
JSC_CCALL(renderer_camera,
HMM_Mat3 t;
t.Columns[0] = (HMM_Vec3){1,0,0};
t.Columns[1] = (HMM_Vec3){0,-1,0};
t.Columns[2] = (HMM_Vec3){0,logical.y,1};
transform *tra = js2transform(js,argv[0]);
tra->pos.x -= logical.x/2;
tra->pos.y -= logical.y/2;
HMM_Mat3 T = transform2mat3(tra);
cam_mat = HMM_MulM3(t,T);
tra->pos.x += logical.x/2;
tra->pos.y += logical.y/2;
)
static const JSCFunctionListEntry js_SDL_Renderer_funcs[] = {
MIST_FUNC_DEF(SDL_Renderer, draw_color, 1),
MIST_FUNC_DEF(SDL_Renderer, present, 0),
@@ -1810,7 +1909,7 @@ static const JSCFunctionListEntry js_SDL_Renderer_funcs[] = {
MIST_FUNC_DEF(renderer, point, 2),
MIST_FUNC_DEF(renderer, load_texture, 1),
MIST_FUNC_DEF(renderer, texture, 4),
MIST_FUNC_DEF(renderer, 9slice, 4),
MIST_FUNC_DEF(renderer, slice9, 4),
MIST_FUNC_DEF(renderer, tile, 4),
MIST_FUNC_DEF(renderer, get_image, 1),
MIST_FUNC_DEF(renderer, fasttext, 2),
@@ -1822,6 +1921,7 @@ static const JSCFunctionListEntry js_SDL_Renderer_funcs[] = {
MIST_FUNC_DEF(renderer,clip,1),
MIST_FUNC_DEF(renderer,vsync,1),
MIST_FUNC_DEF(renderer, coords, 1),
MIST_FUNC_DEF(renderer, camera, 1),
};
JSC_CCALL(surface_blit,
@@ -1931,6 +2031,8 @@ static const JSCFunctionListEntry js_SDL_Camera_funcs[] =
MIST_FUNC_DEF(camera, release_frame, 1),
};
static const JSCFunctionListEntry js_SDL_Cursor_funcs[] = {};
JSC_CCALL(texture_mode,
SDL_Texture *tex = js2SDL_Texture(js,self);
SDL_SetTextureScaleMode(tex,js2number(js,argv[0]));
@@ -1948,9 +2050,17 @@ JSC_CCALL(input_mouse_show,
SDL_HideCursor();
)
JSC_CCALL(input_cursor_set,
SDL_Cursor *c = js2SDL_Cursor(js,argv[0]);
printf("setting cursor to %p\n", c);
if (!SDL_SetCursor(c))
return JS_ThrowReferenceError(js, "could not set cursor: %s", SDL_GetError());
)
static const JSCFunctionListEntry js_input_funcs[] = {
MIST_FUNC_DEF(input, mouse_show, 1),
MIST_FUNC_DEF(input, mouse_lock, 1),
MIST_FUNC_DEF(input, cursor_set, 1),
};
JSC_CCALL(prosperon_guid,
@@ -2161,7 +2271,7 @@ int globfs_cb(struct globdata *data, char *dir, char *file)
char **glob = data->globs;
while (*glob != NULL) {
if (wildmatch(*glob, path, WM_PATHNAME | WM_PERIOD | WM_WILDSTAR) == WM_MATCH)
if (wildmatch(*glob, path, WM_WILDSTAR) == WM_MATCH)
goto END;
*glob++;
}
@@ -2669,20 +2779,40 @@ JSC_CCALL(os_make_gif,
void *pixels = stbi_load_gif_from_memory(raw, rawlen, &delays, &width, &height, &frames, &n, 4);
JSValue gif = JS_NewObject(js);
ret = gif;
if (frames == 1) {
// still image, so return just that
JS_SetPropertyStr(js, gif, "surface", SDL_Surface2js(js,SDL_CreateSurfaceFrom(width,height,SDL_PIXELFORMAT_RGBA32, pixels, width*4)));
return gif;
}
JSValue delay_arr = JS_NewArray(js);
for (int i = 0; i < frames; i++) {
JSValue frame = JS_NewObject(js);
JS_SetPropertyStr(js, frame, "time", number2js(js,(float)delays[i]/1000.0));
SDL_Surface *framesurf = SDL_CreateSurfaceFrom(width,height,SDL_PIXELFORMAT_RGBA32,pixels+(width*height*4*i), width*4);
void *frame_pixels = malloc(width*height*4);
if (!frame_pixels) {
JS_FreeValue(js,gif);
ret = JS_ThrowOutOfMemory(js);
goto CLEANUP;
}
memcpy(frame_pixels, (unsigned char*)pixels+(width*height*4*i), width*height*4);
SDL_Surface *framesurf = SDL_CreateSurfaceFrom(width,height,SDL_PIXELFORMAT_RGBA32,frame_pixels, width*4);
if (!framesurf) {
ret = JS_ThrowReferenceError(js, "failed to create SDL_Surface: %s", SDL_GetError());
goto CLEANUP;
}
JS_SetPropertyStr(js, frame, "surface", SDL_Surface2js(js,framesurf));
JS_SetPropertyUint32(js, delay_arr, i, frame);
}
JS_SetPropertyStr(js, gif, "frames", delay_arr);
CLEANUP:
free(delays);
ret = gif;
free(pixels);
)
JSValue aseframe2js(JSContext *js, ase_frame_t aframe)
@@ -2756,6 +2886,15 @@ JSC_CCALL(os_make_surface,
ret = SDL_Surface2js(js, surface);
)
JSC_CCALL(os_make_cursor,
SDL_Surface *s = js2SDL_Surface(js,argv[0]);
HMM_Vec2 hot = js2vec2(js,argv[1]);
SDL_Cursor *c = SDL_CreateColorCursor(s, hot.x, hot.y);
if (!c) return JS_ThrowReferenceError("couldn't make cursor: %s", SDL_GetError());
printf("made cursor %p\n", c);
return SDL_Cursor2js(js,c);
)
JSC_CCALL(os_make_font,
size_t len;
void *data = JS_GetArrayBuffer(js,&len,argv[0]);
@@ -3117,6 +3256,7 @@ static const JSCFunctionListEntry js_os_funcs[] = {
MIST_FUNC_DEF(os, make_gif, 1),
MIST_FUNC_DEF(os, make_aseprite, 1),
MIST_FUNC_DEF(os, make_surface, 1),
MIST_FUNC_DEF(os, make_cursor, 1),
MIST_FUNC_DEF(os, make_font, 2),
MIST_FUNC_DEF(os, make_transform, 0),
MIST_FUNC_DEF(os, make_line_prim, 4),
@@ -3216,6 +3356,7 @@ void ffi_load(JSContext *js) {
QJSCLASSPREP_FUNCS(SDL_Texture)
QJSCLASSPREP_FUNCS(SDL_Renderer)
QJSCLASSPREP_FUNCS(SDL_Camera)
QJSCLASSPREP_FUNCS(SDL_Cursor)
QJSGLOBALCLASS(os);
@@ -3226,8 +3367,6 @@ void ffi_load(JSContext *js) {
QJSCLASSPREP_FUNCS(datastream);
QJSCLASSPREP_FUNCS(timer);
QJSGLOBALCLASS(input);
QJSGLOBALCLASS(io);
QJSGLOBALCLASS(prosperon);

View File

@@ -52,6 +52,11 @@ char* read_file(const char* filename) {
return content;
}
int js_interrupt(JSRuntime *rt, void *data)
{
// printf("INTERRUPT\n");
}
void script_startup() {
rt = JS_NewRuntime();
js = JS_NewContextRaw(rt);

View File

@@ -53,19 +53,14 @@ HMM_Vec3 mat3_t_dir(HMM_Mat4 m, HMM_Vec3 dir)
return mat3_t_pos(m, dir);
}
HMM_Mat4 transform2mat(transform *t) {
HMM_Mat4 transform2mat(transform *t)
{
return HMM_M4TRS(t->pos, t->rotation, t->scale);
HMM_Mat4 scale = HMM_Scale(t->scale);
HMM_Mat4 rot = HMM_QToM4(t->rotation);
HMM_Mat4 pos = HMM_Translate(t->pos);
return HMM_MulM4(pos, HMM_MulM4(rot, scale));
}
if (t->dirty) {
t->cache = HMM_M4TRS(t->pos, t->rotation, t->scale);
t->dirty = 0;
}
return t->cache;
HMM_Mat3 transform2mat3(transform *t)
{
return HMM_M3TRS(t->pos.xy, t->rotation.x, t->scale.xy);
}
HMM_Quat angle2rotation(float angle)

View File

@@ -39,6 +39,7 @@ HMM_Vec3 mat3_t_pos(HMM_Mat4 m, HMM_Vec3 pos);
HMM_Vec3 mat3_t_dir(HMM_Mat4 m, HMM_Vec3 dir);
HMM_Mat4 transform2mat(transform *t);
HMM_Mat3 transform2mat3(transform *t);
transform mat2transform(HMM_Mat4 m);
HMM_Quat angle2rotation(float angle);