separate input and events; pull camera out of render

This commit is contained in:
2025-01-30 20:25:12 -06:00
parent b8857031f4
commit 8a41899c5d
22 changed files with 635 additions and 995 deletions

View File

@@ -5,7 +5,12 @@ With Javascript's C-like syntax, it makes it easy to make classic style games li
# API usage
A lot of the API usage is informed from 'duck typing'. If something "looks" like a camera - it can be used like one! If something "looks" like a sprite, it can be used like one! There are fast paths on nearly everything for well defined objects
Uniformity is prioritized.
Uniformity is prioritized. Javascript allows for a powerful abstraction - the object - which Prosperon makes much use of. It allows for another - the arraybuffer - which makes it simple to plug different parts of the engine into each other.
The object is the lingua franca of the API. For example, json.encode and json.decode converts objects to and fromt json strings; nota.encode and nota.decode converts objects to and from nota bytes, represented as a javascript arraybuffer. To convert a json string to a nota buffer, one would do:
nota.encode(json.decode(str))
Most functions take objects just like this, increasing flexibility for what you can send into functions.
# Gradual performance
Prosperon makes it easy to make something quickly, and if it runs well, you're golden! But if it's slow, there are a plethora of options to make it faster. Plus, with native C plugins, there is nothing that can't be figured out.
@@ -14,42 +19,64 @@ Prosperon makes it easy to make something quickly, and if it runs well, you're g
~~prosperon master
Here are the modules that can be included via 'use'.
// extensions
Object.id - get a unique id for the given javascript object
// global syntax level things
Globals have been kept to a minimum to ensure game objects are as flexible as possible in what they can call their state. The vast majority of access to prosperon is done through the 'use' statement.
use - import a module
on actors: /* look to web windows for a comparison */
- spawn: create a new actor
- delay: execute a function after seconds
-
prosperon global object; information about the engine; process related (ie, if there are multiple threads running, prosperon will be different in each, os will be the same)
- version: 0.6.1
- revision: git revision
- argv: the string of arguments passed to the process
- semver: functions to compare use
// core
prosperon
input
json
layout
resources
sound
os - sys calls; information about the underlying hardware
js - information about the scripting language; gc functions; etc
// mixed in
base
std
time
search
color
tween
util
io
os
// core use fns
input - user input settings
json - json encoding and decoding
resources - access resources
sound - play sounds
time - time access
actor - actor helpers
color - color constants
tween - helper to tween
util - random helpers
io - file system access, mounting, unmounting
event - system events
math - math help
geometry - geometry help
// rendering
draw2d
render
graphics
// game components
emitter
sprite
transform
// math
pmath
vector
geometry
draw2d - immediate mode draw 2d stuff functions
render - render configuration and use; shaders, pipelines, render state.
graphics - create graphics objects (textures, fonts, etc); memory usage of fonts, textures, etc.
// game component modules
emitter - particle drawing helper
sprite - sprite drawing helper
transform - helper to turn programs into transforms
layout - a gui layout helper
imgui - add for imgui calls
controller - higher level controller based on events
~~prosperon dev
nota
dmon
enet
convert
debug
diff
@@ -69,4 +96,5 @@ yaml
spline
profile
sim
dmon
noise // using stb perlin

View File

@@ -110,7 +110,7 @@ if get_option('enet')
endif
sources = []
src += ['anim.c', 'config.c', 'datastream.c','font.c','gameobject.c','HandmadeMath.c','jsffi.c','model.c','render.c','script.c','simplex.c','spline.c', 'timer.c', 'transform.c','yugine.c', 'wildmatch.c', 'sprite.c', 'quadtree.c', 'aabb.c', 'rtree.c']
src += ['anim.c', 'config.c', 'datastream.c','font.c','gameobject.c','HandmadeMath.c','jsffi.c','model.c','render.c','script.c','simplex.c','spline.c', 'timer.c', 'transform.c','prosperon.c', 'wildmatch.c', 'sprite.c', 'quadtree.c', 'aabb.c', 'rtree.c']
imsrc = ['GraphEditor.cpp','ImCurveEdit.cpp','ImGradient.cpp','imgui_draw.cpp','imgui_tables.cpp','imgui_widgets.cpp','imgui.cpp','ImGuizmo.cpp','imnodes.cpp','implot_items.cpp','implot.cpp', 'imgui_impl_sdlrenderer3.cpp', 'imgui_impl_sdl3.cpp', 'imgui_impl_sdlgpu3.cpp']

View File

@@ -338,10 +338,8 @@ Cmdline.register_order(
"Display Prosperon info.",
);
function cmd_args(cmdargs) {
function cmd_args(cmds) {
var play = false;
var cmds = cmdargs.split(/\s+/).slice(1);
if (cmds.length === 0) cmds[0] = "play";
else if (!Cmdline.orders[cmds[0]]) {
console.warn(`Command ${cmds[0]} not found. Playing instead.`);
@@ -420,4 +418,4 @@ function convertYAMLtoJSON(yamlString) {
return jsonObj;
}
return {cmd_args}
return cmd_args;

View File

@@ -1,6 +1,7 @@
var render = use('render')
var graphics = use('graphics')
var math = use('math')
var util = use('util')
var base_pipeline = {
vertex: "sprite.vert",
@@ -54,7 +55,6 @@ var base_pipeline = {
target: {}
}
var sprite_pipeline = Object.create(base_pipeline);
sprite_pipeline.blend = {
enabled:true,
@@ -88,7 +88,7 @@ draw.point = function (pos, size, color = Color.blue) {
};
draw.line = function render_line(points, color = Color.white, thickness = 1, pipeline = rect_pipeline) {
var mesh = os.make_line_prim(points,thickness, 0,0,color);
var mesh = graphics.make_line_prim(points,thickness, 0,0,color);
render.queue({
type: 'geometry',
mesh,
@@ -157,7 +157,7 @@ draw.slice9 = function slice9(image, rect = [0,0], slice = 0, color = Color.whit
if (typeof image === "string")
image = graphics.texture(image);
var mesh = render._main.slice9(image.texture, rect, gizmo.normalizeSpacing(slice), info);
var mesh = render._main.slice9(image.texture, rect, util.normalizeSpacing(slice), info);
render.queue({
type: 'geometry',
mesh,
@@ -188,7 +188,7 @@ draw.image = function image(image, rect = [0,0], rotation = 0, color, pipeline)
if (color) cmd.color = color;
render.queue(cmd)
var sprite = os.make_sprite();
var sprite = graphics.make_sprite();
sprite.set_image(image);
sprite.set_rect(rect);
return sprite;
@@ -207,14 +207,14 @@ draw.images = function images(image, rects, config)
for (var rect of rects) {
// get sprite from sprite_buf, or make a new one
rect.__proto__ = bb;
var sprite = os.make_sprite();
var sprite = graphics.make_sprite();
sprite.set_rect(rect);
sprite.set_image(image);
sprites.push(sprite)
}
// var sprites = os.rects_to_sprites(image,rects);
var cmds = render._main.make_sprite_queue(sprites, prosperon.camera, sprite_pipeline)
// var sprites = graphics.rects_to_sprites(image,rects);
var cmds = graphics.make_sprite_queue(sprites, prosperon.camera, sprite_pipeline)
for (var i = 0; i < cmds.length; i++)
render.queue(cmds[i])
@@ -223,7 +223,7 @@ draw.images = function images(image, rects, config)
draw.sprites = function(sprites, sort = 0, pipeline = sprite_pipeline)
{
var cmds = render._main.make_sprite_queue(sprites, prosperon.camera, pipeline, sort);
var cmds = graphics.make_sprite_queue(sprites, prosperon.camera, pipeline, sort);
for (var i = 0; i < cmds.length; i++)
render.queue(cmds[i]);
}
@@ -237,7 +237,7 @@ var sysfont = graphics.get_font('fonts/c64.ttf', 8);
draw.text = function text(text, rect, font = sysfont, size = 0, color = Color.white, wrap = 0, pipeline = sprite_pipeline) {
if (typeof font === 'string')
font = graphics.get_font(font)
var mesh = os.make_text_buffer(text, rect, 0, color, wrap, font);
var mesh = graphics.make_text_buffer(text, rect, 0, color, wrap, font);
render.queue({
type: 'geometry',
@@ -250,4 +250,6 @@ draw.text = function text(text, rect, font = sysfont, size = 0, color = Color.wh
});
};
draw.sprite_pipeline = sprite_pipeline;
return draw

View File

@@ -1,10 +1,42 @@
globalThis.prosperon = {}
var os = use_embed('os')
// set up events on prosperon
var listeners = new Map()
prosperon.on = function(type, callback)
{
if (!listeners.has(type)) listeners.set(type, [])
listeners.get(type).push(callback)
prosperon.SIGINT = function() {
os.exit();
return function() {
var arr = listeners.get(type)
if (!arr) return
var idx = arr.indexOf(callback)
if (idx >= 0) arr.splice(idx,1)
}
}
prosperon.dispatch = function(type, data)
{
var arr = listeners.get(type)
if (!arr) return
for (var callback of arr) callback(data)
}
var os = use_embed('os')
var js = use_embed('js')
prosperon.on('SIGINT', function() {
os.exit();
})
prosperon.on('SIGABRT', function() {
console.error(new Error('SIGABRT'));
os.exit(1);
})
prosperon.on('SIGSEGV', function() {
console.error(new Error('SIGSEGV'));
os.exit(1);
})
var use_cache = {}
Object.defineProperty(Function.prototype, "hashify", {
@@ -28,11 +60,10 @@ io.mount("core")
var canonical = io.realdir('resources.js') + 'resources.js'
var content = io.slurp('resources.js')
var resources = os.eval('resources.js', `(function setup_resources(){${content}})`).call({})
var resources = js.eval('resources.js', `(function setup_resources(){${content}})`).call({})
console.print(resources.canonical('resources.js'))
use_cache[resources.canonical('resources.js')] = resources
function print_api(obj) {
for (var prop in obj) {
if (!obj.hasOwnProperty(prop)) continue
@@ -65,7 +96,7 @@ var script_fn = function script_fn(path) {
if (parsed.module) {
var mod_script = `(function setup_${module_name}_module(){ var self = this; var $ = this; var exports = {}; var module = {exports: exports}; var define = undefined; ${parsed.module}})`;
var module_fn = os.eval(file, mod_script);
var module_fn = js.eval(file, mod_script);
parsed.module_ret = module_fn.call(parsed.module_ret);
if (parsed.module_ret === undefined || parsed.module_ret === null)
throw new Error(`Module ${module_name} must return a value`);
@@ -76,7 +107,7 @@ var script_fn = function script_fn(path) {
if (parsed.program) {
var prog_script = `(function use_${module_name}() { var self = this; var $ = this.__proto__; ${parsed.program}})`;
parsed.prog_fn = os.eval(file, prog_script);
parsed.prog_fn = js.eval(file, prog_script);
}
return parsed;
@@ -183,19 +214,7 @@ console.doc = {
var script = io.slurp("core/scripts/base.js")
var fnname = "base"
script = `(function ${fnname}() { ${script}; })`
os.eval('core/scripts/base.js', script)()
prosperon.SIGABRT = function()
{
console.error(new Error('SIGABRT'));
os.exit(1);
}
prosperon.SIGSEGV = function()
{
console.error(new Error('SIGSEGV'));
os.exit(1);
}
js.eval('core/scripts/base.js', script)()
function add_timer(obj, fn, seconds)
{
@@ -240,7 +259,7 @@ var OVERLING = Symbol()
var actor = {};
var so_ext;
switch(os.sys()) {
switch(os.platform()) {
case 'Windows':
so_ext = '.dll';
break;
@@ -471,7 +490,7 @@ try{
this[UNDERLINGS].add(underling);
if (underling.tag)
search.tag_add(underling.tag, underling)
act.tag_add(underling.tag, underling)
underling[GARBAGE] = underling.garbage
return underling;
@@ -501,7 +520,7 @@ actor.kill = function kill() {
if (typeof this.garbage === "function") this.garbage();
if (typeof this.then === "function") this.then();
search.tag_clear_guid(this)
act.tag_clear_guid(this)
};
actor.kill.doc = `Remove this actor and all its underlings from existence.`;
@@ -509,9 +528,11 @@ actor.kill.doc = `Remove this actor and all its underlings from existence.`;
actor.delay = function (fn, seconds) { add_timer(this, fn, seconds) }
actor.delay.doc = `Call 'fn' after 'seconds' with 'this' set to the actor.`;
var search = use('search')
var act = use('actor')
actor[UNDERLINGS] = new Set()
globalThis.mixin("color");
globalThis.mixin("std")
use('cmd')(prosperon.argv)

5
scripts/event.js Normal file
View File

@@ -0,0 +1,5 @@
var event = this
return event

View File

@@ -1,5 +1,4 @@
var graphics = {}
var os = use('os')
var graphics = this
var io = use('io')
var res = use('resources')
@@ -15,7 +14,7 @@ function create_image(path)
var newimg;
switch(path.ext()) {
case 'gif':
newimg = os.make_gif(data);
newimg = graphics.make_gif(data);
if (newimg.surface)
newimg.texture = prosperon.gpu.load_texture(newimg.surface);
else
@@ -24,7 +23,7 @@ function create_image(path)
break;
case 'ase':
case 'aseprite':
newimg = os.make_aseprite(data);
newimg = graphics.make_aseprite(data);
if (newimg.surface)
newimg.texture = prosperon.gpu.load_texture(newimg.surface);
else {
@@ -37,7 +36,7 @@ function create_image(path)
break;
default:
newimg = {
surface: os.make_texture(data)
surface: graphics.make_texture(data)
};
newimg.texture = prosperon.gpu.load_texture(newimg.surface);
break;
@@ -62,14 +61,14 @@ function pack_into_sheet(images)
if (images[0].texture.width > 300 && images[0].texture.height > 300) return;
sheet_frames = sheet_frames.concat(images);
var sizes = sheet_frames.map(x => [x.rect.width*x.texture.width, x.rect.height*x.texture.height]);
var pos = os.rectpack(sheetsize, sheetsize, sizes);
var pos = graphics.rectpack(sheetsize, sheetsize, sizes);
if (!pos) {
console.error(`did not make spritesheet properly from images ${images}`);
console.info(sizes);
return;
}
var newsheet = os.make_tex_data(sheetsize,sheetsize);
var newsheet = graphics.make_tex_data(sheetsize,sheetsize);
for (var i = 0; i < pos.length; i++) {
// Copy the texture to the new sheet
@@ -147,10 +146,10 @@ function make_spritesheet(paths, width, height)
return
var textures = paths.map(path => graphics.texture(path));
var sizes = textures.map(tex => [tex.width, tex.height]);
var pos = os.rectpack(width, height, sizes);
var pos = graphics.rectpack(width, height, sizes);
if (!pos) return;
var sheet = os.make_tex_data(width,height);
var sheet = graphics.make_tex_data(width,height);
var st = profile.now();
for (var i = 0; i < pos.length; i++)
@@ -177,52 +176,23 @@ graphics.get_font = function get_font(path,size)
if (fontcache[fontstr]) return fontcache[fontstr];
var data = io.slurpbytes(fullpath);
fontcache[fontstr] = os.make_font(data,size);
fontcache[fontstr] = graphics.make_font(data,size);
fontcache[fontstr].texture = prosperon.gpu.load_texture(fontcache[fontstr].surface);
return fontcache[fontstr];
}
graphics.semver = {};
graphics.semver.valid = function (v, range) {
v = v.split(".");
range = range.split(".");
if (v.length !== 3) return undefined;
if (range.length !== 3) return undefined;
if (range[0][0] === "^") {
range[0] = range[0].slice(1);
if (parseInt(v[0]) >= parseInt(range[0])) return true;
return false;
graphics.queue_sprite_mesh = function(queue)
{
var sprites = queue.filter(x => x.type === 'sprite');
if (sprites.length === 0) return [];
var mesh = graphics.make_sprite_mesh(sprites);
for (var i = 0; i < sprites.length; i++) {
sprites[i].mesh = mesh;
sprites[i].first_index = i*6;
sprites[i].num_indices = 6;
}
if (range[0] === "~") {
range[0] = range[0].slice(1);
for (var i = 0; i < 2; i++) if (parseInt(v[i]) < parseInt(range[i])) return false;
return true;
}
return graphics.semver.cmp(v.join("."), range.join(".")) === 0;
};
graphics.semver.cmp = function (v1, v2) {
var ver1 = v1.split(".");
var ver2 = v2.split(".");
for (var i = 0; i < 3; i++) {
var n1 = parseInt(ver1[i]);
var n2 = parseInt(ver2[i]);
if (n1 > n2) return 1;
else if (n1 < n2) return -1;
}
return 0;
};
graphics.semver.cmp.doc = "Compare two semantic version numbers, given like X.X.X.";
graphics.semver.valid.doc = `Test if semantic version v is valid, given a range.
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.`;
return [mesh.pos,mesh.uv,mesh.color,mesh.indices]
}
return graphics

102
scripts/modules/camera.js Normal file
View File

@@ -0,0 +1,102 @@
var cam = {}
var os = use('os')
var basecam = {}
basecam.draw_rect = function(size)
{
var mode = this.presentation || "letterbox"
var vp = {
x:this.viewport.x,
y:1-this.viewport.y-this.viewport.height,
width:this.viewport.width,
height:this.viewport.height
}
var src_rect = {x:0,y:0,width:this.size.x,height:this.size.y}
var dst_rect = {x:vp.x*size.x,y:vp.y*size.y,width:vp.width*size.x,height:vp.height*size.y};
return mode_rect(src_rect,dst_rect,mode);
}
basecam.screen2camera = function(pos)
{
var draw_rect = this.draw_rect(prosperon.window.size);
var ret = [pos.x-draw_rect.x, pos.y - draw_rect.y];
ret.x /= draw_rect.width;
ret.y /= draw_rect.height;
ret.y = 1 - ret.y;
return ret;
}
basecam.screen2hud = function(pos)
{
var cam = this.screen2camera(pos);
cam.x *= this.size.x;
cam.y *= this.size.y;
return cam;
}
basecam.screen2world = function(pos)
{
var hud = this.screen2hud(pos);
hud.x += this.transform.pos.x - this.size.x/2;
hud.y += this.transform.pos.y - this.size.y/2;
return hud;
}
function mode_rect(src,dst,mode = "stretch")
{
var aspect_src = src.width/src.height;
var aspect_dst = dst.width/dst.height;
var out = {
x:dst.x,
y:dst.y,
width:dst.width,
height:dst.height
};
if (mode == "stretch") return out;
if (mode == "letterbox") {
if (aspect_src > aspect_dst) {
var scaled_h = out.width/aspect_src;
var off = (out.height - scaled_h) * 0.5;
out.y += off;
out.height = scaled_h;
} else {
var scaled_w =out.height * aspect_src;
var off = (out.width - scaled_w) * 0.5;
out.x += off;
out.width = scaled_w;
}
} else if (mode == "overscan"){
if (aspect_src > aspect_dst) {
var scaled_w = out.height * aspect_src;
var off = (out.width - scaled_w) * 0.5;
out.x += off;
out.width = scaled_w;
} else {
var scaled_h = out.width / aspect_src;
var off = (out.height - scaled_h) * 0.5;
out.y += off;
out.height = scaled_h;
}
}
return out;
}
// If camera viewport is defined, will draw to the screen
// If target is defined, will render to a target, too
cam.make = function()
{
var c = Object.create(basecam)
c.transform = os.make_transform()
c.zoom = 1
c.size = [640,360]
c.mode = 'keep'
c.viewport = {x:0,y:0,width:1,height:1}
c.fov = 45
c.type = 'ortho'
c.aspect = 16/9
return c
}
return cam

View File

@@ -1,5 +1,4 @@
var input = this
var input = use('input')
var util = use('util')
var downkeys = {};
@@ -18,54 +17,52 @@ function modstr(mod = input.keymod()) {
return s;
}
prosperon.key_down = function key_down(e) {
prosperon.on('key_down', function key_down(e) {
downkeys[e.key] = true;
var emacs = modstr(e.mod) + keyname(e.key);
if (e.repeat) player[0].raw_input(emacs, "rep");
else player[0].raw_input(emacs, "pressed");
};
})
prosperon.quit = function()
{
prosperon.on('quit', function() {
os.exit(0);
}
})
prosperon.key_up = function key_up(e) {
prosperon.on('key_up', function key_up(e) {
delete downkeys[e.key];
var emacs = modstr(e.mod) + keyname(e.key);
player[0].raw_input(emacs, "released");
};
})
prosperon.drop_file = function (path) {
prosperon.on('drop_file', function (path) {
player[0].raw_input("drop", "pressed", path);
};
})
var mousepos = [0, 0];
prosperon.text_input = function (e) {
prosperon.on('text_input', function (e) {
player[0].raw_input("char", "pressed", e.text);
};
})
prosperon.mouse_motion = function (e)
prosperon.on('mouse_motion', function (e)
{
mousepos = e.pos;
player[0].mouse_input("move", e.pos, e.d_pos);
};
prosperon.mouse_wheel = function mousescroll(e) {
player[0].mouse_input(modstr() + "scroll", e.scroll);
};
})
prosperon.mouse_button_down = function(e)
{
prosperon.on('mouse_wheel', function mousescroll(e) {
player[0].mouse_input(modstr() + "scroll", e.scroll);
})
prosperon.on('mouse_button_down', function(e) {
player[0].mouse_input(modstr() + e.button, "pressed");
input.mouse.buttons[e.button] = true
}
})
prosperon.mouse_button_up = function(e)
{
prosperon.on('mouse_button_up', function(e) {
player[0].mouse_input(modstr() + e.button, "released");
input.mouse.buttons[e.button] = false
}
})
input.mouse = {};
input.mouse.screenpos = function mouse_screenpos() {

View File

@@ -1,5 +1,7 @@
var Color = use('color')
var os = use('os')
var graphics = use('graphics')
//var render = use('render')
var ex = {}
@@ -104,6 +106,24 @@ ex.spawn_timer = 0
ex.pps = 0
ex.color = Color.white
ex.draw = function()
{
/* var diff = graphics.texture(this.diffuse)
if (!diff) throw new Error("emitter does not have a proper diffuse texture")
var mesh = render._main.make_sprite_mesh(this.particles)
if (mesh.num_indices === 0) return
render.queue({
type:'geometry',
mesh,
image:diff,
pipeline,
first_index:0,
num_indices:mesh.num_indices
})
*/
}
return ex
---
@@ -113,5 +133,4 @@ this.dead = []
this.transform = this.overling.transform
$.emitters.add(this)

View File

@@ -1,5 +1,69 @@
var graphics = use('graphics')
var base_pipeline = {
vertex: "sprite.vert",
fragment: "sprite.frag",
primitive: "triangle", // point, line, linestrip, triangle, trianglestrip
fill: true, // false for lines
depth: {
compare: "greater_equal", // never/less/equal/less_equal/greater/not_equal/greater_equal/always
test: false,
write: false,
bias: 0,
bias_slope_scale: 0,
bias_clamp: 0
},
stencil: {
enabled: true,
front: {
compare: "equal", // never/less/equal/less_equal/greater/neq/greq/always
fail: "keep", // keep/zero/replace/incr_clamp/decr_clamp/invert/incr_wrap/decr_wrap
depth_fail: "keep",
pass: "keep"
},
back: {
compare: "equal", // never/less/equal/less_equal/greater/neq/greq/always
fail: "keep", // keep/zero/replace/incr_clamp/decr_clamp/invert/incr_wrap/decr_wrap
depth_fail: "keep",
pass: "keep"
},
test: true,
compare_mask: 0,
write_mask: 0
},
blend: {
enabled: false,
src_rgb: "zero", // zero/one/src_color/one_minus_src_color/dst_color/one_minus_dst_color/src_alpha/one_minus_src_alpha/dst_alpha/one_minus_dst_alpha/constant_color/one_minus_constant_color/src_alpha_saturate
dst_rgb: "zero",
op_rgb: "add", // add/sub/rev_sub/min/max
src_alpha: "one",
dst_alpha: "zero",
op_alpha: "add"
},
cull: "none", // none/front/back
face: "cw", // cw/ccw
alpha_to_coverage: false,
multisample: {
count: 1, // number of multisamples
mask: 0xFFFFFFFF,
domask: false
},
label: "scripted pipeline",
target: {}
}
var sprite_pipeline = Object.create(base_pipeline);
sprite_pipeline.blend = {
enabled:true,
src_rgb: "src_alpha", // zero/one/src_color/one_minus_src_color/dst_color/one_minus_dst_color/src_alpha/one_minus_src_alpha/dst_alpha/one_minus_dst_alpha/constant_color/one_minus_constant_color/src_alpha_saturate
dst_rgb: "one_minus_src_alpha",
op_rgb: "add", // add/sub/rev_sub/min/max
src_alpha: "one",
dst_alpha: "zero",
op_alpha: "add"
};
var sprite = {
image: undefined,
set color(x) { this._sprite.color = x; },
@@ -184,7 +248,7 @@ sprite.inputs.kp1 = function () {
this.setanchor("ul");
};
var tree = os.make_rtree()
var tree = graphics.make_rtree()
sprite.tree = tree;
sprite.t_hook = function() {
@@ -199,19 +263,36 @@ sprite.t_hook = function() {
Object.mixin(sprite,use("transform"))
sprite.to_queue = function(ysort = false)
{
var pos = prosperon.camera.transform.pos;
var size = prosperon.camera.size;
var camrect = {
x:pos.x-size.x/2,
y:pos.y-size.y/2,
width:size.x,
height:size.y
};
var culled = sprite.tree.query(camrect)
if (culled.length == 0) return [];
var cmd = graphics.make_sprite_queue(culled, prosperon.camera, sprite_pipeline, 1);
return cmd;
}
return sprite;
---
var Color = use('color')
var os = use('os')
var graphics = use('graphics')
this.transform = os.make_transform();
if (this.overling.transform)
this.transform.parent = this.overling.transform;
this.transform.change_hook = $.t_hook;
var msp = os.make_sprite();
var msp = graphics.make_sprite();
this._sprite = msp;
msp.color = Color.white;
this.transform.sprite = this

View File

@@ -5,23 +5,26 @@ var io = use('io')
var os = use('os')
var util = use('util')
var emitter = use('emitter')
var input = use('input')
var controller = use('controller')
var event = use('event')
var sprite = use('sprite')
var graphics = use('graphics')
var appy = {};
appy.inputs = {};
if (os.sys() === "macos") {
if (os.platform() === "macos") {
appy.inputs["S-q"] = os.exit;
}
appy.inputs["M-f4"] = os.exit;
input.player[0].control(appy);
controller.player[0].control(appy);
prosperon.window = os.engine_start(config);
prosperon.window = prosperon.engine_start(config);
var driver = "vulkan"
switch(os.sys()) {
switch(os.platform()) {
case "Linux":
driver = "vulkan"
break
@@ -40,8 +43,6 @@ render._main.window = prosperon.window
render._main.claim_window(prosperon.window)
render._main.set_swapchain('sdr', 'vsync')
var graphics = use('graphics')
var unit_transform = os.make_transform();
var cur = {};
@@ -56,19 +57,6 @@ function full_upload(buffers)
cmds.submit();
}
function queue_sprite_mesh(queue)
{
var sprites = queue.filter(x => x.type === 'sprite');
if (sprites.length === 0) return [];
var mesh = render._main.make_sprite_mesh(sprites);
for (var i = 0; i < sprites.length; i++) {
sprites[i].mesh = mesh;
sprites[i].first_index = i*6;
sprites[i].num_indices = 6;
}
return [mesh.pos,mesh.uv,mesh.color,mesh.indices]
}
function bind_pipeline(pass, pipeline)
{
make_pipeline(pipeline)
@@ -431,12 +419,12 @@ var pass;
}
var buffers = [];
buffers = buffers.concat(queue_sprite_mesh(render_queue));
buffers = buffers.concat(graphics.queue_sprite_mesh(render_queue));
var unique_meshes = [...new Set(render_queue.map(x => x.mesh))];
for (var q of unique_meshes)
buffers = buffers.concat([q.pos, q.color,q.uv,q.indices]);
buffers = buffers.concat(queue_sprite_mesh(hud_queue));
buffers = buffers.concat(graphics.queue_sprite_mesh(hud_queue));
for (var q of hud_queue)
if (q.type === 'geometry') buffers = buffers.concat([q.mesh.pos, q.mesh.color,q.mesh.uv,q.mesh.indices]);
@@ -521,102 +509,8 @@ var pass;
hud_queue = [];
}
function mode_rect(src,dst,mode = "stretch")
{
var aspect_src = src.width/src.height;
var aspect_dst = dst.width/dst.height;
var out = {
x:dst.x,
y:dst.y,
width:dst.width,
height:dst.height
};
if (mode == "stretch") return out;
if (mode == "letterbox") {
if (aspect_src > aspect_dst) {
var scaled_h = out.width/aspect_src;
var off = (out.height - scaled_h) * 0.5;
out.y += off;
out.height = scaled_h;
} else {
var scaled_w =out.height * aspect_src;
var off = (out.width - scaled_w) * 0.5;
out.x += off;
out.width = scaled_w;
}
} else if (mode == "overscan"){
if (aspect_src > aspect_dst) {
var scaled_w = out.height * aspect_src;
var off = (out.width - scaled_w) * 0.5;
out.x += off;
out.width = scaled_w;
} else {
var scaled_h = out.width / aspect_src;
var off = (out.height - scaled_h) * 0.5;
out.y += off;
out.height = scaled_h;
}
}
return out;
}
prosperon.camera = {};
// If camera viewport is defined, will draw to the screen
// If target is defined, will render to a target, too
prosperon.camera.transform = os.make_transform();
prosperon.camera.transform.unit();
prosperon.camera.zoom = 1;
prosperon.camera.size = [640,360];
prosperon.camera.mode = 'keep';
prosperon.camera.viewport = {x:0,y:0,width:1,height:1}
prosperon.camera.fov = 45;
prosperon.camera.type = 'ortho';
prosperon.camera.ortho = true
prosperon.camera.aspect = 16/9;
delete prosperon.camera.target;
prosperon.camera.draw_rect = function(size)
{
var mode = this.presentation || "letterbox"
var vp = {
x:this.viewport.x,
y:1-this.viewport.y-this.viewport.height,
width:this.viewport.width,
height:this.viewport.height
}
var src_rect = {x:0,y:0,width:this.size.x,height:this.size.y}
var dst_rect = {x:vp.x*size.x,y:vp.y*size.y,width:vp.width*size.x,height:vp.height*size.y};
return mode_rect(src_rect,dst_rect,mode);
}
// Camera coordinates are [0,0]
prosperon.camera.screen2camera = function(pos)
{
var draw_rect = this.draw_rect(prosperon.window.size);
var ret = [pos.x-draw_rect.x, pos.y - draw_rect.y];
ret.x /= draw_rect.width;
ret.y /= draw_rect.height;
ret.y = 1 - ret.y;
return ret;
}
prosperon.camera.screen2hud = function(pos)
{
var cam = this.screen2camera(pos);
cam.x *= this.size.x;
cam.y *= this.size.y;
return cam;
}
prosperon.camera.screen2world = function(pos)
{
var hud = this.screen2hud(pos);
hud.x += this.transform.pos.x - this.size.x/2;
hud.y += this.transform.pos.y - this.size.y/2;
return hud;
}
prosperon.camera = use('camera').make()
var swaps = [];
function gpupresent()
@@ -624,7 +518,7 @@ function gpupresent()
os.clean_transforms();
prosperon.prerender();
var cmds = render._main.acquire_cmd_buffer();
render_queue = sprites_to_queue().concat(render_queue);
render_queue = sprite.to_queue().concat(render_queue);
render_camera(cmds, prosperon.camera);
var swapchain_tex = cmds.acquire_swapchain();
if (!swapchain_tex)
@@ -660,39 +554,6 @@ render.toggles = {
draw_gui:true
}
function sprites_to_queue(ysort = false)
{
var pos = prosperon.camera.transform.pos;
var size = prosperon.camera.size;
var camrect = {
x:pos.x-size.x/2,
y:pos.y-size.y/2,
width:size.x,
height:size.y
};
var culled = sprite.tree.query(camrect)
if (culled.length == 0) return [];
var cmd = render._main.make_sprite_queue(culled, prosperon.camera, sprite_pipeline, 1);
return cmd;
}
render.particles = function render_particles(emitter, pipeline = sprite_pipeline)
{
var diff = graphics.texture(emitter.diffuse)
if (!diff) throw new Error("emitter does not have a proper diffuse");
var mesh = render._main.make_sprite_mesh(emitter.particles);
if (mesh.num_indices === 0) return;
current_queue.push({
type:'geometry',
mesh,
image:diff,
pipeline,
first_index:0,
num_indices:mesh.num_indices
});
}
var stencil_write = {
compare: "always",
fail_op: "replace",
@@ -766,65 +627,7 @@ render.scissor = function(rect)
{
render.viewport(rect)
}
function camscreen2world(pos) {
var view = this.screen2cam(pos);
var viewport = render._main.get_viewport();
view.x *= viewport.width;
view.y *= viewport.height;
view = view.add(this.pos.xy);
view = view.sub([viewport.width,viewport.height].scale(0.5))
// view = view.scale(this.transform.scale);
return view;
}
// world coordinates, the "actual" view relative to the game's universe
// camera coordinates, normalized from 0 to 1 inside of a camera's viewport, bottom left is 0,0, top right is 1,1
// screen coordinates, pixels, 0,0 at the top left of the window and [w,h] at the bottom right of the window
// hud coordinates, same as screen coordinates but the top left is 0,0
camscreen2world.doc = "Convert a view position for a camera to world.";
// return camera coordinates given a screen position
function screen2cam(pos) {
var tpos = render._main.coords(pos);
var viewport = render._main.get_viewport();
var viewpos = tpos.div([viewport.width,viewport.height]);
viewpos.y *= -1;
viewpos.y += 1;
return viewpos;
}
screen2cam.doc = "Convert a screen space position in pixels to a normalized viewport position in a camera.";
function screen2hud(pos)
{
var campos = this.screen2cam(pos);
var viewport = render._main.get_viewport();
campos = campos.scale([viewport.width,viewport.height]);
return campos;
}
/* cameras
* Cameras have a position and rotation. They are not affected by scale.
*/
prosperon.make_camera = function (make_camera) {
return;
/* var cam = world.spawn();
cam.near = 1;
cam.far = -1000;
cam.ortho = true; // True if this is a 2d camera
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.zoom = 1; // the "scale factor" this camera demonstrates
return cam;*/
};
//////////////////////////////////////////////////
var screencolor;
globalThis.imtoggle = function (name, obj, field) {
@@ -941,8 +744,8 @@ render.process = function process() {
var dt = last_frame_time - frame_t;
frame_t = last_frame_time;
os.engine_input(e => {
prosperon[e.type]?.(e);
event.engine_input(e => {
prosperon.dispatch(e.type, e);
});
layout.newframe();
@@ -956,7 +759,6 @@ render.process = function process() {
current_queue = render_queue;
prosperon.draw()
emitter.emitters.forEach(e => render.particles(e))
current_queue = hud_queue;
prosperon.hud()
imgui_fn()

View File

@@ -1,7 +1,7 @@
var Resources = {}
var so_ext;
switch(os.sys()) {
switch(os.platform()) {
case 'Windows':
so_ext = '.dll';
break;

View File

@@ -1,4 +1,4 @@
var util = {}
var util = this
util.deepfreeze = function (obj) {
for (var key in obj) {

View File

@@ -9,7 +9,6 @@
#include "stb_dxt.h"
#include "string.h"
#include "spline.h"
#include "yugine.h"
#include <assert.h>
#include <time.h>
#include <sys/time.h>
@@ -29,13 +28,22 @@
#include "cgltf.h"
#include "physfs.h"
#ifdef _WIN32
#include <windows.h>
#else
#include <unistd.h>
#include <sys/utsname.h>
#ifdef __linux__
#include <sys/sysinfo.h>
#endif
#endif
#include "wildmatch.h"
#include "freelist.h"
#include "sprite.h"
#include "quadtree.h"
#include "rtree.h"
typedef struct rtree rtree;
@@ -45,6 +53,7 @@ typedef struct rtree rtree;
#include <SDL3/SDL_error.h>
#include <SDL3/SDL_properties.h>
#include <SDL3/SDL_loadso.h>
#include <SDL3/SDL_cpuinfo.h>
#ifdef __APPLE__
@@ -410,8 +419,6 @@ static BufferCheckResult get_or_extend_buffer(
#include <sys/resource.h>
#endif
#include "stb_perlin.h"
#if (defined(_WIN32) || defined(__WIN32__))
#include <direct.h>
#define mkdir(x,y) _mkdir(x)
@@ -1105,10 +1112,6 @@ char *js2strdup(JSContext *js, JSValue v) {
return ret;
}
void skin_free(JSRuntime *rt,skin *sk) {
arrfree(sk->invbind);
free(sk);
}
#include "qjs_macros.h"
@@ -1194,7 +1197,6 @@ static void js_timer_mark(JSRuntime *rt, JSValueConst val, JS_MarkFunc *mark_fun
}
QJSCLASSMARK(timer)
QJSCLASS(skin)
QJSCLASS(SDL_Window)
QJSCLASS(SDL_Renderer)
QJSCLASS(SDL_Camera)
@@ -1241,13 +1243,6 @@ static void PHYSFS_File_free(JSRuntime *rt, PHYSFS_File *f)
QJSCLASS(PHYSFS_File)
void qtree_free(JSRuntime *rt, qtree *tree)
{
qtree_destroy(*tree);
}
QJSCLASS(qtree)
void rtree_free(JSRuntime *rt, rtree *tree)
{
rtree_destroy(tree);
@@ -2869,20 +2864,6 @@ static JSValue event2js(JSContext *js, SDL_Event event)
return e;
}
void gui_input(SDL_Event *e);
// Polls and handles all input events
JSC_CCALL(os_engine_input,
SDL_Event event;
while (SDL_PollEvent(&event)) {
#ifndef NEDITOR
gui_input(&event);
#endif
JSValue e = event2js(js,event);
JSValue ret = JS_Call(js,argv[0], JS_UNDEFINED, 1, &e);
uncaught_exception(js,ret);
}
)
JSC_CCALL(camera_list,
int num;
SDL_CameraID *ids = SDL_GetCameras(&num);
@@ -5061,8 +5042,6 @@ static const JSCFunctionListEntry js_SDL_GPUDevice_funcs[] = {
MIST_FUNC_DEF(gpu, make_sampler,1),
MIST_FUNC_DEF(gpu, load_texture, 2),
MIST_FUNC_DEF(gpu, texture, 1),
MIST_FUNC_DEF(gpu, make_sprite_mesh, 2),
MIST_FUNC_DEF(gpu, make_sprite_queue, 4),
MIST_FUNC_DEF(gpu, make_quad, 0),
MIST_FUNC_DEF(gpu, driver, 0),
MIST_FUNC_DEF(gpu, make_shader, 1),
@@ -5704,18 +5683,6 @@ JSC_SCALL(os_openurl,
ret = JS_ThrowReferenceError(js, "unable to open url %s: %s\n", str, SDL_GetError());
)
JSC_CCALL(os_push_event,
SDL_UserEvent e;
SDL_zero(e);
e.type = SDL_EVENT_USER;
e.timestamp = SDL_GetTicksNS();
e.code = 0;
JSValue fn = JS_DupValue(js,argv[0]);
e.data1 = malloc(sizeof(JSValue));
*(JSValue*)e.data1 = fn;
SDL_PushEvent(&e);
)
JSC_CCALL(time_now,
struct timeval ct;
gettimeofday(&ct, NULL);
@@ -6401,18 +6368,6 @@ static const JSCFunctionListEntry js_geometry_funcs[] = {
MIST_FUNC_DEF(geometry, rect_move, 2),
};
JSValue js_os_cwd(JSContext *js, JSValue self, int argc, JSValue *argv)
{
char cwd[PATH_MAX];
#ifndef __EMSCRIPTEN__
getcwd(cwd, sizeof(cwd));
#else
cwd[0] = '.';
cwd[1] = 0;
#endif
return JS_NewString(js,cwd);
}
JSC_SCALL(os_env,
char *env = getenv(str);
if (env) ret = JS_NewString(js,env);
@@ -6556,7 +6511,7 @@ JSC_CCALL(os_dump_mem,
)
JSC_CCALL(os_value_id,
return number2js(js,(intptr_t)JS_VALUE_GET_PTR(argv[0]));
return number2js(js,(intptr_t)JS_VALUE_GET_PTR(self));
)
static double gc_t = 0;
@@ -6750,271 +6705,6 @@ JSC_CCALL(os_make_sprite, return sprite2js(js,make_sprite()))
JSC_SCALL(os_system, ret = number2js(js,system(str)); )
JSC_SCALL(os_model_buffer,
/*
int mesh_idx = 0;
// Configure cgltf
cgltf_options options;
memset(&options, 0, sizeof(options));
// Parse the file
cgltf_data* data = NULL;
cgltf_result result = cgltf_parse_file(&options, str, &data);
if (result != cgltf_result_success) {
JS_ThrowInternalError(js, "Failed to load glTF model: parse error");
return JS_UNDEFINED;
}
// Load any external buffers (bin files, images)
result = cgltf_load_buffers(&options, data, str);
if (result != cgltf_result_success) {
cgltf_free(data);
JS_ThrowInternalError(js, "Failed to load glTF model: buffer load error");
return JS_UNDEFINED;
}
// We only check for mesh_idx vs. the count of cgltf_data->meshes
// (though note that glTF organizes data in scenes, nodes, etc.,
// so you might want to handle multiple nodes or node->mesh references)
if (mesh_idx < 0 || mesh_idx >= (int)data->meshes_count) {
cgltf_free(data);
JS_ThrowInternalError(js, "Invalid mesh index");
return JS_UNDEFINED;
}
JSValue materials[data->materials_count];
for (int i = 0; i < (int)data->materials_count; i++) {
JSValue mat = JS_NewObject(js);
materials[i] = mat;
cgltf_material *cgmat = &data->materials[i];
// Grab the base color texture if it exists
cgltf_texture_view *bc_view = &cgmat->pbr_metallic_roughness.base_color_texture;
if (bc_view->texture && bc_view->texture->image) {
cgltf_image *img = bc_view->texture->image;
// If the image is an external URI
if (img->uri) {
// For glTF 2.0, this often points to a .png/.jpg
JS_SetPropertyStr(js, mat, "diffuse", JS_NewString(js, img->uri));
}
// If it's an embedded buffer view (e.g., "data:" or bufferView-based image)
else if (img->buffer_view) {
size_t size = img->buffer_view->size;
uint8_t *ptr = (uint8_t*)img->buffer_view->buffer->data + img->buffer_view->offset;
JS_SetPropertyStr(js, mat, "diffuse", JS_NewArrayBufferCopy(js, ptr, size));
}
}
}
// Create an array to hold all the "model" objects
JSValue ret_arr = JS_NewArray(js);
for (int m = 0; m < (int)data->meshes_count; m++) {
cgltf_mesh *cgmesh = &data->meshes[m];
// Go through each primitive in this mesh
for (int p = 0; p < (int)cgmesh->primitives_count; p++) {
cgltf_primitive *prim = &cgmesh->primitives[p];
if (!prim->attributes) {
// No attributes => no geometry
continue;
}
// We'll collect attribute data in arrays, just like with Assimp
float *posdata = NULL, *normdata = NULL, *uvdata = NULL, *colordata = NULL;
Uint16 *indicesdata = NULL;
size_t num_verts = 0;
size_t index_count = 0;
// Helper function to find an accessor by "POSITION", "NORMAL", "TEXCOORD_0", etc.
// We'll parse it below in parseAttributeFloat.
cgltf_accessor* findAccessor(cgltf_primitive* prim, const char* semantic) {
for (int a = 0; a < (int)prim->attributes_count; a++) {
if (prim->attributes[a].name && strcmp(prim->attributes[a].name, semantic) == 0)
return prim->attributes[a].data;
}
return NULL;
}
// parseAttributeFloat:
// read floats from a cgltf_accessor into outBuffer
// The 'stride' is how many floats we read per element (e.g. 3 for POSITION)
// Returns the number of elements read
size_t parseAttributeFloat(JSContext* js, cgltf_accessor* acc, float** outBuffer, int stride, float defaultVal) {
if (!acc) {
// If the attribute doesn't exist, fill default
size_t count = 0;
*outBuffer = NULL;
return count;
}
size_t count = acc->count;
size_t total_floats = count * stride;
*outBuffer = malloc(total_floats * sizeof(float));
if (!(*outBuffer)) return 0;
for (size_t i = 0; i < count; i++) {
float tmp[4] = { defaultVal, defaultVal, defaultVal, defaultVal };
cgltf_accessor_read_float(acc, i, tmp, 4);
// copy only 'stride' components
for (int c = 0; c < stride; c++)
(*outBuffer)[i*stride + c] = tmp[c];
}
return count;
}
// 1) POSITION
cgltf_accessor *accPos = findAccessor(prim, "POSITION");
num_verts = parseAttributeFloat(js, accPos, &posdata, 3, 0.0f);
// 2) NORMAL
cgltf_accessor *accNorm = findAccessor(prim, "NORMAL");
// If missing normals, default them to (0,0,1)
parseAttributeFloat(js, accNorm, &normdata, 3, 0.0f);
if (!normdata && num_verts > 0) {
normdata = malloc(num_verts*3*sizeof(float));
for (size_t i = 0; i < num_verts; i++) {
normdata[i*3+0] = 0.0f;
normdata[i*3+1] = 0.0f;
normdata[i*3+2] = 1.0f;
}
}
// 3) TEXCOORD_0
cgltf_accessor *accUV = findAccessor(prim, "TEXCOORD_0");
parseAttributeFloat(js, accUV, &uvdata, 2, 0.0f);
if (!uvdata && num_verts > 0) {
uvdata = malloc(num_verts*2*sizeof(float));
for (size_t i = 0; i < num_verts; i++) {
uvdata[i*2+0] = 0.0f;
uvdata[i*2+1] = 0.0f;
}
}
// 4) COLOR_0
cgltf_accessor *accColor = findAccessor(prim, "COLOR_0");
parseAttributeFloat(js, accColor, &colordata, 4, 1.0f);
if (!colordata && num_verts > 0) {
colordata = malloc(num_verts*4*sizeof(float));
for (size_t i = 0; i < num_verts; i++) {
colordata[i*4+0] = 1.0f;
colordata[i*4+1] = 1.0f;
colordata[i*4+2] = 1.0f;
colordata[i*4+3] = 1.0f;
}
}
// 5) Indices (if present)
if (prim->indices) {
cgltf_accessor *idxAcc = prim->indices;
index_count = idxAcc->count;
indicesdata = malloc(index_count*sizeof(Uint16));
if (indicesdata) {
for (size_t i = 0; i < index_count; i++) {
// cgltf_accessor_read_index can read the index as uint32_t
uint32_t val = 0;
val = cgltf_accessor_read_index(idxAcc, i);
// NOTE: if val > 65535, you'll need 32-bit index buffers
indicesdata[i] = (Uint16)val;
}
}
}
// Build a JS object for the mesh data
JSValue js_mesh = JS_NewObject(js);
// Positions
if (posdata && num_verts > 0)
JS_SetProperty(js, js_mesh, pos_atom,
make_gpu_buffer(js, posdata, sizeof(float)*3*num_verts, JS_TYPED_ARRAY_FLOAT32, 3, 1, 0));
// UV
if (uvdata && num_verts > 0)
JS_SetProperty(js, js_mesh, uv_atom,
make_gpu_buffer(js, uvdata, sizeof(float)*2*num_verts, JS_TYPED_ARRAY_FLOAT32, 2, 1, 0));
// Color
if (colordata && num_verts > 0)
JS_SetProperty(js, js_mesh, color_atom,
make_gpu_buffer(js, colordata, sizeof(float)*4*num_verts, JS_TYPED_ARRAY_FLOAT32, 4, 1, 0));
// Normal
if (normdata && num_verts > 0)
JS_SetProperty(js, js_mesh, norm_atom,
make_gpu_buffer(js, normdata, sizeof(float)*3*num_verts, JS_TYPED_ARRAY_FLOAT32, 3, 1, 0));
// Indices
if (indicesdata && index_count > 0)
JS_SetProperty(js, js_mesh, indices_atom,
make_gpu_buffer(js, indicesdata, sizeof(Uint16)*index_count, JS_TYPED_ARRAY_UINT16, 0, 1, 1));
// Metadata
JS_SetProperty(js, js_mesh, vertices_atom, number2js(js, (double)num_verts));
JS_SetProperty(js, js_mesh, count_atom, number2js(js, (double)index_count));
// Build final "model" object with mesh + material
JSValue model_obj = JS_NewObject(js);
JS_SetPropertyStr(js, model_obj, "mesh", js_mesh);
// Figure out the index of the material in data->materials. If prim->material is non-null,
// its index is `prim->material - data->materials`.
int mat_index = -1;
if (prim->material) mat_index = (int)(prim->material - data->materials);
if (mat_index >= 0 && mat_index < (int)data->materials_count)
JS_SetPropertyStr(js, model_obj, "material", JS_DupValue(js, materials[mat_index]));
else
JS_SetPropertyStr(js, model_obj, "material", JS_NewObject(js));
// Place this "model" object into our return array
uint32_t idx_in_array = js_arrlen(js,ret_arr);
JS_SetPropertyUint32(js, ret_arr, idx_in_array, model_obj);
// Cleanup (per-primitive)
if (posdata) free(posdata);
if (normdata) free(normdata);
if (uvdata) free(uvdata);
if (colordata) free(colordata);
if (indicesdata) free(indicesdata);
}
}
cgltf_free(data);
ret = ret_arr;
*/
)
JSC_SCALL(os_gltf_buffer,
// int buffer_idx = js2number(js,argv[1]);
// int type = js2number(js,argv[2]);
// cgltf_options options = {0};
// cgltf_data *data = NULL;
// cgltf_result result = cgltf_parse_file(&options, str, &data);
// result = cgltf_load_buffers(&options, data, str);
// SDL_GPUBuffer *b = SDL_CreateGPUBuffer(NULL,NULL);
// *b = accessor2buffer(&data->accessors[buffer_idx], type);
// cgltf_free(data);
// ret = sg_buffer2js(js,b);
)
JSC_SCALL(os_gltf_skin,
cgltf_options options = {0};
cgltf_data *data = NULL;
cgltf_parse_file(&options,str,&data);
cgltf_load_buffers(&options,data,str);
if (data->skins_count <= 0) {
ret = (JS_UNDEFINED);
goto CLEANUP;
}
ret = skin2js(js,make_gltf_skin(data->skins+0, data));
CLEANUP:
cgltf_free(data);
)
JSValue make_color_buffer(JSContext *js, colorf c, int verts)
{
HMM_Vec4 *colordata = malloc(sizeof(*colordata)*verts);
@@ -7065,79 +6755,6 @@ JSC_CCALL(os_make_line_prim,
return prim;
)
JSValue parmesh2js(JSContext *js,par_shapes_mesh *m)
{
return JS_UNDEFINED;
/* JSValue obj = JS_NewObject(js);
sg_buffer *pos = malloc(sizeof(*pos));
*pos = float_buffer(m->points, 3*m->npoints);
JS_SetPropertyStr(js, obj, "pos", sg_buffer2js(js,pos));
if (m->tcoords) {
sg_buffer *uv = malloc(sizeof(*uv));
*uv = texcoord_floats(m->tcoords, 2*m->npoints);
JS_SetPropertyStr(js, obj, "uv", sg_buffer2js(js,uv));
}
if (m->normals) {
sg_buffer *norm = malloc(sizeof(*norm));
*norm = normal_floats(m->normals, 3*m->npoints);
JS_SetPropertyStr(js, obj, "norm", sg_buffer2js(js,norm));
}
sg_buffer *index = malloc(sizeof(*index));
*index = sg_make_buffer(&(sg_buffer_desc){
.data = {
.ptr = m->triangles,
.size = sizeof(*m->triangles)*3*m->ntriangles
},
.type = SG_BUFFERTYPE_INDEXBUFFER
});
JS_SetPropertyStr(js, obj, "index", sg_buffer2js(js,index));
JS_SetPropertyStr(js, obj, "count", number2js(js,3*m->ntriangles));
par_shapes_free_mesh(m);
return obj;
*/
}
JSC_CCALL(os_make_cylinder,
return parmesh2js(js,par_shapes_create_cylinder(js2number(js,argv[0]), js2number(js,argv[1])));
)
JSC_CCALL(os_make_cone,
return parmesh2js(js,par_shapes_create_cone(js2number(js,argv[0]), js2number(js,argv[1])));
)
JSC_CCALL(os_make_disk,
return parmesh2js(js,par_shapes_create_parametric_disk(js2number(js,argv[0]), js2number(js,argv[1])));
)
JSC_CCALL(os_make_torus,
return parmesh2js(js,par_shapes_create_torus(js2number(js,argv[0]), js2number(js,argv[1]), js2number(js,argv[2])));
)
JSC_CCALL(os_make_sphere,
return parmesh2js(js,par_shapes_create_parametric_sphere(js2number(js,argv[0]), js2number(js,argv[1])));
)
JSC_CCALL(os_make_klein_bottle,
return parmesh2js(js,par_shapes_create_klein_bottle(js2number(js,argv[0]), js2number(js,argv[1])));
)
JSC_CCALL(os_make_trefoil_knot,
return parmesh2js(js,par_shapes_create_trefoil_knot(js2number(js,argv[0]), js2number(js,argv[1]), js2number(js,argv[2])));
)
JSC_CCALL(os_make_hemisphere,
return parmesh2js(js,par_shapes_create_hemisphere(js2number(js,argv[0]), js2number(js,argv[1])));
)
JSC_CCALL(os_make_plane,
return parmesh2js(js,par_shapes_create_plane(js2number(js,argv[0]), js2number(js,argv[1])));
)
static void render_frame(plm_t *mpeg, plm_frame_t *frame, datastream *ds) {
if (JS_IsUndefined(ds->callback)) return;
uint8_t *rgb = malloc(frame->height*frame->width*4);
@@ -7164,11 +6781,6 @@ JSC_CCALL(os_make_video,
return datastream2js(js,ds);
)
JSC_CCALL(os_skin_calculate,
skin *sk = js2skin(js,argv[0]);
skin_calculate(sk);
)
JSC_CCALL(os_rectpack,
int width = js2number(js,argv[0]);
int height = js2number(js,argv[1]);
@@ -7200,27 +6812,6 @@ JSC_CCALL(os_rectpack,
}
)
JSC_CCALL(os_perlin,
HMM_Vec3 coord = js2vec3(js,argv[0]);
HMM_Vec3 wrap = js2vec3(js,argv[2]);
return number2js(js,stb_perlin_noise3_seed(coord.x, coord.y, coord.z, wrap.x, wrap.y, wrap.z, js2number(js,argv[1])));
)
JSC_CCALL(os_ridge,
HMM_Vec3 c = js2vec3(js,argv[0]);
return number2js(js,stb_perlin_ridge_noise3(c.x, c.y, c.z, js2number(js,argv[1]), js2number(js,argv[2]), js2number(js,argv[3]), js2number(js,argv[4])));
)
JSC_CCALL(os_fbm,
HMM_Vec3 c = js2vec3(js,argv[0]);
return number2js(js,stb_perlin_fbm_noise3(c.x, c.y, c.z, js2number(js,argv[1]), js2number(js,argv[2]), js2number(js,argv[3])));
)
JSC_CCALL(os_turbulence,
HMM_Vec3 c = js2vec3(js,argv[0]);
return number2js(js,stb_perlin_turbulence_noise3(c.x, c.y, c.z, js2number(js,argv[1]), js2number(js,argv[2]), js2number(js,argv[3])));
)
JSC_SCALL(os_kill,
int sig = 0;
if (!strcmp(str, "SIGABRT")) sig = SIGABRT;
@@ -7232,16 +6823,8 @@ JSC_SCALL(os_kill,
if (!sig) return JS_ThrowReferenceError(js, "string %s is not a valid signal", str);
raise(sig);
return JS_UNDEFINED;
)
int detectImageInWebcam(SDL_Surface *a, SDL_Surface *b);
/*JSC_CCALL(os_match_img,
SDL_Surface *img1 = js2SDL_Surface(js,argv[0]);
SDL_Surface *img2 = js2SDL_Surface(js,argv[1]);
int n = detectImageInWebcam(img1,img2);
return number2js(js,n);
)
*/
JSC_CCALL(os_sleep,
double time = js2number(js,argv[0]);
time *= 1000000000.;
@@ -7341,36 +6924,6 @@ JSC_CCALL(os_cull_sprites,
}
)
struct qtree_sprite {
rect rect;
JSValue value;
};
int js_qtree_cmp(struct qtree_sprite *v, aabb *range)
{
rect ab = {
.x = range->center.x-range->dims.w,
.y = range->center.y-range->dims.h,
.w = range->dims.w*2.0,
.h = range->dims.h*2.0
};
return SDL_HasRectIntersectionFloat(&v->rect, &ab);
}
int js_qtree_rm(struct qtree_sprite *val, struct qtree_sprite *cmp)
{
int same = JS_SameValue(global_js, val->value, cmp->value);
if (same)
JS_FreeValue(global_js, val->value);
return same;
}
JSC_CCALL(os_make_quadtree,
rect area = js2rect(js,argv[0]);
qtree tree = qtree_new(area.x,area.y,area.w,area.h, js_qtree_cmp, js_qtree_rm);
return qtree2js(js,tree);
)
JSC_CCALL(os_make_rtree,
struct rtree *tree = rtree_new();
if (!tree) return JS_ThrowOutOfMemory(js);
@@ -7421,106 +6974,216 @@ typedef struct {
size_t fn_count;
} ModuleEntry;
JSC_SSCALL(os_trimchr,
int len;
JS_GETPROP(js,len,argv[0],length,number)
const char *start = str;
while (*start == *str2)
start++;
const char *end = str + len-1;
while(*end == *str2)
end--;
ret = JS_NewStringLen(js, start, end-start+1);
JSC_CCALL(os_totalmem, return number2js(js, SDL_GetSystemRAM()))
JSC_CCALL(os_platform, return JS_NewString(js,SDL_GetPlatform()))
JSC_CCALL(os_hostname,
char buf[256];
if (gethostname(buf,sizeof(buf)) == 0) return JS_NewString(js,buf);
return JS_NewString(js,"");
)
JSC_CCALL(os_freemem,
#ifdef _WIN32
MEMORYSTATUSEX statex;
statex.dwLength = sizeof(statex);
if (!GlobalMemoryStatusEx(&statex)) return JS_ThrowInternalError(js,"GlobalMemoryStatusEx failed");
return JS_NewInt64(js,(int64_t)statex.ullAvailPhys);
#elif defined(__linux__)
struct sysinfo info;
if (sysinfo(&info) == 0)
return JS_NewInt64(js,(int64_t)info.freeram * info.mem_unit);
return JS_NewInt64(js,0);
#elif defined(__APPLE__) || defined(__FreeBSD__) || defined(__OpenBSD__)
// A very rough fallback using the same sysconf approach
// (macOS or *BSD typically need specialized APIs to get free mem accurately)
// This is often only "unused" pages, ignoring caches, etc.
long pages = sysconf(_SC_AVPHYS_PAGES);
long page_size = sysconf(_SC_PAGE_SIZE);
if (pages < 0 || page_size < 0) return JS_NewInt64(js,0);
return JS_NewInt64(js,(int64_t)pages * (int64_t)page_size);
#else
// Fallback: unknown
return JS_NewInt64(js,0);
#endif
)
JSC_CCALL(os_arch,
#if defined(__x86_64__) || defined(_M_X64)
return JS_NewString(js,"x64");
#elif defined(__aarch64__) || defined(_M_ARM64)
return JS_NewString(js,"arm64");
#elif defined(__arm__) || defined(_M_ARM)
return JS_NewString(js,"arm");
#elif defined(__i386__) || defined(_M_IX86)
return JS_NewString(js,"ia32");
#elif defined(__loongarch__) || defined(__loongarch32) || defined(__loongarch64)
return JS_NewString(js,"loong64");
#elif defined(__mips__) || defined(__mips) || defined(_M_MIPS)
// You might want to distinguish mips vs mipsel
return JS_NewString(js,"mips");
#elif defined(__ppc64__) || defined(__powerpc64__) || defined(_M_PPC)
// You might want to distinguish ppc vs ppc64, big-endian vs little-endian
return JS_NewString(js,"ppc64");
#elif defined(__riscv) && __riscv_xlen == 64
return JS_NewString(js,"riscv64");
#elif defined(__s390x__)
return JS_NewString(js,"s390x");
#else
return JS_NewString(js,"unknown");
#endif
)
JSC_CCALL(os_version,
return JS_NewString(js,PROSPERON_VERSION);
)
JSC_CCALL(os_commit,
return JS_NewString(js,PROSPERON_COMMIT);
#ifdef _WIN32
typedef LONG (WINAPI *RtlGetVersionPtr)(PRTL_OSVERSIONINFOW);
HMODULE h = GetModuleHandleA("ntdll.dll");
if (h) {
RtlGetVersionPtr fx = (RtlGetVersionPtr)GetProcAddress(h, "RtlGetVersion");
if (fx) {
RTL_OSVERSIONINFOW ver;
memset(&ver, 0, sizeof(ver));
ver.dwOSVersionInfoSize = sizeof(ver);
if (!fx(&ver)) {
char buf[128];
sprintf(buf, "%u.%u.%u",
(unsigned)ver.dwMajorVersion,
(unsigned)ver.dwMinorVersion,
(unsigned)ver.dwBuildNumber
);
return JS_NewString(js, buf);
}
}
}
OSVERSIONINFOW wver;
memset(&wver, 0, sizeof(wver));
wver.dwOSVersionInfoSize = sizeof(wver);
if (GetVersionExW(&wver)) {
char buf[128];
sprintf(buf, "%u.%u.%u",
(unsigned)wver.dwMajorVersion,
(unsigned)wver.dwMinorVersion,
(unsigned)wver.dwBuildNumber
);
return JS_NewString(js, buf);
}
return JS_NewString(js, "Windows_Unknown");
#else
struct utsname info;
if (!uname(&info)) return JS_NewString(js, info.release);
return JS_NewString(js, "");
#endif
)
static const JSCFunctionListEntry js_os_funcs[] = {
MIST_FUNC_DEF(os, version, 0),
MIST_FUNC_DEF(os, commit, 0),
MIST_FUNC_DEF(os, engine_start, 1),
MIST_FUNC_DEF(os, engine_input, 1),
MIST_FUNC_DEF(os, turbulence, 4),
MIST_FUNC_DEF(os, model_buffer, 1),
MIST_FUNC_DEF(os, clean_transforms, 0),
MIST_FUNC_DEF(os, fbm, 4),
MIST_FUNC_DEF(os, ridge, 5),
MIST_FUNC_DEF(os, perlin, 3),
MIST_FUNC_DEF(os, rectpack, 3),
MIST_FUNC_DEF(os, cwd, 0),
MIST_FUNC_DEF(os, rusage, 0),
MIST_FUNC_DEF(os, mallinfo, 0),
MIST_FUNC_DEF(os, env, 1),
MIST_FUNC_DEF(os, sys, 0),
MIST_FUNC_DEF(os, system, 1),
MIST_FUNC_DEF(os, exit, 1),
MIST_FUNC_DEF(os, gc, 0),
MIST_FUNC_DEF(os, now, 0),
MIST_FUNC_DEF(os, guid, 0),
MIST_FUNC_DEF(os, openurl, 1),
MIST_FUNC_DEF(os, push_event, 1),
MIST_FUNC_DEF(os, eval, 2),
MIST_FUNC_DEF(os, make_quadtree, 1),
MIST_FUNC_DEF(os, make_rtree, 0),
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_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_sprite, 0),
MIST_FUNC_DEF(os, make_line_prim, 5),
MIST_FUNC_DEF(os, make_cylinder, 2),
MIST_FUNC_DEF(os, make_cone, 2),
MIST_FUNC_DEF(os, make_disk, 2),
MIST_FUNC_DEF(os, make_torus, 3),
MIST_FUNC_DEF(os, make_sphere, 2),
MIST_FUNC_DEF(os, make_klein_bottle, 2),
MIST_FUNC_DEF(os, make_trefoil_knot, 3),
MIST_FUNC_DEF(os, make_hemisphere, 2),
MIST_FUNC_DEF(os, make_plane, 2),
MIST_FUNC_DEF(os, make_video, 1),
MIST_FUNC_DEF(os, clean_transforms, 0),
MIST_FUNC_DEF(os, platform, 0),
MIST_FUNC_DEF(os, arch, 0),
MIST_FUNC_DEF(os, totalmem, 0),
MIST_FUNC_DEF(os, freemem, 0),
MIST_FUNC_DEF(os, hostname, 0),
MIST_FUNC_DEF(os, version, 0),
MIST_FUNC_DEF(os, kill, 1),
MIST_FUNC_DEF(os, exit, 1),
MIST_FUNC_DEF(os, now, 0),
MIST_FUNC_DEF(os, openurl, 1),
MIST_FUNC_DEF(os, make_timer, 1),
MIST_FUNC_DEF(os, make_text_buffer, 6),
MIST_FUNC_DEF(os, update_timers, 1),
MIST_FUNC_DEF(os, mem, 1),
MIST_FUNC_DEF(os, mem_limit, 1),
MIST_FUNC_DEF(os, gc_threshold, 1),
MIST_FUNC_DEF(os, max_stacksize, 1),
MIST_FUNC_DEF(os, rt_info, 0),
MIST_FUNC_DEF(os, dump_mem, 0),
MIST_FUNC_DEF(os, dump_shapes, 0),
MIST_FUNC_DEF(os, dump_atoms,0),
MIST_FUNC_DEF(os, calc_mem, 1),
MIST_FUNC_DEF(os, memstate, 0),
MIST_FUNC_DEF(os, value_id, 1),
MIST_FUNC_DEF(os, gltf_buffer, 3),
MIST_FUNC_DEF(os, gltf_skin, 1),
MIST_FUNC_DEF(os, skin_calculate, 1),
MIST_FUNC_DEF(os, kill, 1),
// MIST_FUNC_DEF(os, match_img, 2),
MIST_FUNC_DEF(os, sleep, 1),
MIST_FUNC_DEF(os, battery_pct, 0),
MIST_FUNC_DEF(os, battery_voltage, 0),
MIST_FUNC_DEF(os, battery_seconds, 0),
MIST_FUNC_DEF(os, power_state, 0),
MIST_FUNC_DEF(os, insertion_sort, 2),
MIST_FUNC_DEF(os, on, 2),
MIST_FUNC_DEF(os, rt_info, 0),
MIST_FUNC_DEF(os, rusage, 0),
MIST_FUNC_DEF(os, mallinfo, 0),
// dangerous ones that need disabled for shipping
MIST_FUNC_DEF(os, env, 1),
MIST_FUNC_DEF(os, system, 1),
};
static const JSCFunctionListEntry js_js_funcs[] = {
MIST_FUNC_DEF(os, dump_mem, 0),
MIST_FUNC_DEF(os, dump_shapes, 0),
MIST_FUNC_DEF(os, dump_atoms,0),
MIST_FUNC_DEF(os, calc_mem, 1),
MIST_FUNC_DEF(os, mem, 1),
MIST_FUNC_DEF(os, mem_limit, 1),
MIST_FUNC_DEF(os, gc_threshold, 1),
MIST_FUNC_DEF(os, max_stacksize, 1),
MIST_FUNC_DEF(os, memstate, 0),
MIST_FUNC_DEF(os, gc, 0),
MIST_FUNC_DEF(os, eval, 2),
};
static const JSCFunctionListEntry js_util_funcs[] = {
MIST_FUNC_DEF(os, guid, 0),
MIST_FUNC_DEF(os, insertion_sort, 2),
};
static const JSCFunctionListEntry js_graphics_funcs[] = {
MIST_FUNC_DEF(gpu, make_sprite_mesh, 2),
MIST_FUNC_DEF(gpu, make_sprite_queue, 4),
MIST_FUNC_DEF(os, make_text_buffer, 6),
MIST_FUNC_DEF(os, rectpack, 3),
MIST_FUNC_DEF(os, make_rtree, 0),
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, cull_sprites, 2),
MIST_FUNC_DEF(os, rects_to_sprites,2),
MIST_FUNC_DEF(os, on, 2),
MIST_FUNC_DEF(os, trimchr, 2),
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_sprite, 0),
MIST_FUNC_DEF(os, make_line_prim, 5),
};
static const JSCFunctionListEntry js_video_funcs[] = {
MIST_FUNC_DEF(os, make_video, 1),
};
void gui_input(SDL_Event *e);
// Polls and handles all input events
JSC_CCALL(os_engine_input,
SDL_Event event;
while (SDL_PollEvent(&event)) {
#ifndef NEDITOR
gui_input(&event);
#endif
JSValue e = event2js(js,event);
JSValue ret = JS_Call(js,argv[0], JS_UNDEFINED, 1, &e);
uncaught_exception(js,ret);
}
)
JSC_CCALL(os_push_event,
SDL_UserEvent e;
SDL_zero(e);
e.type = SDL_EVENT_USER;
e.timestamp = SDL_GetTicksNS();
e.code = 0;
JSValue fn = JS_DupValue(js,argv[0]);
e.data1 = malloc(sizeof(JSValue));
*(JSValue*)e.data1 = fn;
SDL_PushEvent(&e);
)
static const JSCFunctionListEntry js_event_funcs[] = {
MIST_FUNC_DEF(os, push_event, 1),
MIST_FUNC_DEF(os, engine_input, 1),
};
#define MISTLINE(NAME) { #NAME, js_##NAME##_funcs, countof(js_##NAME##_funcs) }
@@ -7533,6 +7196,11 @@ static ModuleEntry module_registry[] = {
MISTLINE(math),
MISTLINE(spline),
MISTLINE(geometry),
MISTLINE(graphics),
MISTLINE(js),
MISTLINE(util),
MISTLINE(video),
MISTLINE(event),
};
JSC_SCALL(os_use_embed,
@@ -7565,41 +7233,6 @@ JSC_SCALL(os_use_dyn,
SDL_UnloadObject(ptr);
)
JSC_CCALL(qtree_insert,
qtree tree = js2qtree(js,self);
JSValue v = argv[0];
struct qtree_sprite *item = malloc(sizeof(*item));
item->value = JS_DupValue(js,v);
JS_GETATOM(js,item->rect,v,rect_atom,rect)
qtree_insert(tree, item);
)
JSC_CCALL(qtree_remove,
struct qtree_sprite tmp;
tmp.value = argv[0];
qtree tree = js2qtree(js,self);
qtree_remove(tree, &tmp);
)
JSC_CCALL(qtree_query,
qtree tree = js2qtree(js,self);
rect area = js2rect(js,argv[0]);
uint32_t n;
struct qtree_sprite **items = qtree_findInArea(tree, area.x, area.y, area.w, area.h, &n);
ret = JS_NewArray(js);
for (int i = 0; i < n; i++)
JS_SetPropertyUint32(js,ret,i, JS_DupValue(js,items[i]->value));
free(items);
)
static const JSCFunctionListEntry js_qtree_funcs[] = {
MIST_FUNC_DEF(qtree, insert, 1),
MIST_FUNC_DEF(qtree, remove, 1),
MIST_FUNC_DEF(qtree, query, 2),
};
JSC_CCALL(rtree_add,
rtree *tree = js2rtree(js,self);
JSValue v = argv[0];
@@ -7832,19 +7465,18 @@ static void signal_handler(int sig) {
break;
}
if (!str) return;
script_evalf("prosperon.%s?.();", str);
script_evalf("prosperon.dispatch('%s')", str);
}
static void exit_handler()
{
script_evalf("prosperon.exit?.();");
script_evalf("prosperon.dispatch('exit')");
script_stop();
}
void ffi_load(JSContext *js) {
void ffi_load(JSContext *js, int argc, char **argv) {
JSValue globalThis = JS_GetGlobalObject(js);
QJSCLASSPREP_FUNCS(qtree)
QJSCLASSPREP_FUNCS(rtree)
QJSCLASSPREP_FUNCS(SDL_Window)
QJSCLASSPREP_FUNCS(SDL_Surface)
@@ -7883,6 +7515,10 @@ void ffi_load(JSContext *js) {
QJSGLOBALCLASS(os);
QJSGLOBALCLASS(console);
JSValue jsobject = JS_GetPropertyStr(js,globalThis, "Object");
JS_SetPropertyStr(js, jsobject, "id", JS_NewCFunction(js, js_os_value_id, "id", 1));
JS_FreeValue(js,jsobject);
JSValue jsarray = JS_GetPropertyStr(js,globalThis, "Array");
JSValue array_proto = JS_GetPropertyStr(js,jsarray, "prototype");
JS_SetPropertyFunctionList(js, array_proto, js_array_funcs, countof(js_array_funcs));
@@ -8008,5 +7644,18 @@ void ffi_load(JSContext *js) {
m_seedRand(&mrand, time(NULL));
JSValue prosp = JS_NewObject(js);
JSValue args = JS_NewArray(js);
for (int i = 0; i < argc; i++)
JS_SetPropertyUint32(js,args, i, JS_NewString(js,argv[i]));
JS_SetPropertyStr(js,prosp,"argv", args);
JS_SetPropertyStr(js,prosp, "version", JS_NewString(js,PROSPERON_VERSION));
JS_SetPropertyStr(js,prosp,"revision",JS_NewString(js,PROSPERON_COMMIT));
JS_SetPropertyStr(js,prosp,"engine_start", JS_NewCFunction(js,js_os_engine_start, "engine_start", 1));
JS_SetPropertyStr(js,globalThis,"prosperon", prosp);
JS_FreeValue(js,globalThis);
}

View File

@@ -1,7 +1,7 @@
#ifndef FFI_H
#define FFI_H
#include <quickjs.h>
void ffi_load(JSContext *js);
void ffi_load(JSContext *js, int argc, char **argv);
int js_print_exception(JSContext *js, JSValue v);
#endif

View File

@@ -15,7 +15,6 @@
#include <limits.h>
#include <stdlib.h>
#include <string.h>
#include "yugine.h"
#include "jsffi.h"

12
source/prosperon.c Normal file
View File

@@ -0,0 +1,12 @@
#include "script.h"
#include "physfs.h"
int main(int argc, char **argv) {
PHYSFS_init(argv[0]);
char *base = PHYSFS_getBaseDir();
PHYSFS_setWriteDir(base);
PHYSFS_mount(base,NULL,0);
PHYSFS_mount(base,"/",0);
script_startup(argc, argv); // runs engine.js
return 0;
}

View File

@@ -61,7 +61,7 @@ int js_interrupt(JSRuntime *rt, void *data)
// printf("INTERRUPT\n");
}
void script_startup() {
void script_startup(int argc, char **argv) {
rt = JS_NewRuntime();
js = JS_NewContextRaw(rt);
JS_AddIntrinsicBaseObjects(js);
@@ -77,7 +77,7 @@ void script_startup() {
JS_AddIntrinsicBigDecimal(js);
JS_AddIntrinsicOperators(js);
ffi_load(js);
ffi_load(js, argc, argv);
char *eng = read_file("core/scripts/engine.js");
JSValue v = script_eval(js, "core/scripts/engine.js", eng);
@@ -87,11 +87,12 @@ void script_startup() {
void script_stop()
{
return;
JS_FreeContext(js);
js = NULL;
JS_FreeRuntime(rt);
JS_FreeValue(js,on_exception);
rt = NULL;
js = NULL;
}
void uncaught_exception(JSContext *js, JSValue v)
@@ -118,10 +119,6 @@ void uncaught_exception(JSContext *js, JSValue v)
JS_FreeValue(js,v);
}
void script_mem_limit(size_t limit) { JS_SetMemoryLimit(rt, limit); }
void script_gc_threshold(size_t threshold) { JS_SetGCThreshold(rt, threshold); }
void script_max_stacksize(size_t size) { JS_SetMaxStackSize(rt, size); }
void script_evalf(const char *format, ...)
{
JSValue obj;

View File

@@ -1,34 +0,0 @@
#include "yugine.h"
#include "script.h"
#include <string.h>
#include <SDL3/SDL.h>
#include "physfs.h"
int main(int argc, char **argv) {
PHYSFS_init(argv[0]);
char *base = PHYSFS_getBaseDir();
PHYSFS_setWriteDir(base);
PHYSFS_mount(base,NULL,0);
PHYSFS_mount(base,"/",0);
script_startup(); // runs engine.js
int argsize = 0;
for (int i = 0; i < argc; i++) {
argsize += strlen(argv[i]);
if (argc > i+1) argsize++;
}
char cmdstr[argsize+1];
cmdstr[0] = '\0';
for (int i = 0; i < argc; i++) {
strcat(cmdstr, argv[i]);
if (argc > i+1) strcat(cmdstr, " ");
}
script_evalf("cmd_args('%s');", cmdstr);
return 0;
}

View File

@@ -1,8 +0,0 @@
#ifndef YUGINE_H
#define YUGINE_H
#include "script.h"
void engine_start(JSContext *js, JSValue start_fn, JSValue proc_fn, float x, float y); /* fn runs after the engine starts */
#endif