Compare commits

...

20 commits

Author SHA1 Message Date
John Alanbrook 33d4ebf14a make profiling togglable 2024-08-04 15:20:11 -05:00
John Alanbrook 570c12e3db Add many C based math and statistic functions 2024-08-02 21:52:50 -04:00
John Alanbrook 05bc965d10 update quickjs 2024-07-25 22:07:22 -05:00
John Alanbrook 1142bfb896 made engine into a small bootstrap script 2024-07-25 17:53:53 -05:00
John Alanbrook 6047452b62 speed up string functions 2024-07-25 16:14:37 -05:00
John Alanbrook 0a7f5a5cdd add sprite sort 2024-07-25 10:33:51 -05:00
John Alanbrook 55ae7e2171 remove spurious profiling lines 2024-07-25 08:38:10 -05:00
John Alanbrook cb6f64925e add profiling 2024-07-24 14:17:32 -05:00
John Alanbrook 90940b42f5 add quickjs cpu profiling 2024-07-24 08:26:29 -05:00
John Alanbrook ef802bb6f2 add sprite and rendering to engine js 2024-07-23 17:21:27 -05:00
John Alanbrook 08725d474d add transform cache mat4 2024-07-23 14:30:41 -05:00
John Alanbrook f905dbc571 batch drawing for debug shapes 2024-07-23 12:02:46 -05:00
John Alanbrook 531cc1b43e particle emitters store and reuse particles 2024-07-23 08:40:20 -05:00
John Alanbrook 9d51858266 sprite ssbo batch render 2024-07-22 19:07:02 -05:00
John Alanbrook d4b057dc6f javascript based particle system 2024-07-22 15:40:58 -05:00
John Alanbrook ff71ee9db6 particle texture 2024-07-22 08:29:31 -05:00
John Alanbrook 2eb75491ea particle system support 2024-07-21 15:03:43 -05:00
John Alanbrook cf6feffda2 custom ssbo buffers 2024-07-18 17:09:35 -05:00
John Alanbrook 066b213fbe particle ssbo 2024-07-18 12:39:58 -05:00
John Alanbrook 63239fa51a mum image fix 2024-07-16 20:34:09 -05:00
55 changed files with 14848 additions and 1621 deletions

View file

@ -1,9 +1,172 @@
# mum # Mum
#### screengui() #### padding
**array**
[
0,
0
]
#### offset
**array**
[
0,
0
]
#### font
**string**
#### prompt(msg = "prompt", value = "", list = [], cb = function() #### selectable
**boolean**
#### selected
**boolean**
#### font_size
**number**
#### text_align
**string**
#### scale
**number**
#### angle
**number**
#### anchor
**array**
[
0,
1
]
#### hovered
**object**
#### text_shadow
**object**
#### text_outline
**number**
#### color
**array**
[
1,
1,
1,
1
]
#### margin
**array**
[
0,
0
]
#### width
**number**
#### height
**number**
#### max_width
**number**
#### max_height
**number**
#### image_repeat
**boolean**
#### image_repeat_offset
**array**
[
0,
0
]
#### debug
**boolean**
#### make(def)
#### prestart()
#### start()
#### extend(def)
#### text(def)
#### button(def)
#### window(def)
#### image(def)
#### column(def)
#### debug_colors
**object**

View file

@ -1 +1,15 @@
scripts/debug.js:205: [2024-07-03 12:13:12] warn, script: Cannot print the API of something that isn't an object. # Tween
#### default
**object**
#### start(obj, target, tvals, options)
#### make(obj, target, tvals, options)

View file

@ -0,0 +1,9 @@
# Profiling
There are a handful of profiling methods available to you.
## Frame by frame profile
## Cpu instrumentation
## Game stats

View file

@ -12,11 +12,18 @@ actor.spawn = function(script, config, callback){
padawan.timers = []; padawan.timers = [];
padawan.master = this; padawan.master = this;
Object.hide(padawan, "master", "timers", "padawans"); Object.hide(padawan, "master", "timers", "padawans");
padawan.toString = function() { return script; }
check_registers(padawan); check_registers(padawan);
this.padawans.push(padawan); this.padawans.push(padawan);
return padawan; return padawan;
}; };
actor.tween = function(from,to,time,fn) {
var stop = tween(from,to,time,fn);
this.timers.push(stop);
return stop;
}
actor.spawn.doc = `Create a new actor, using this actor as the master, initializing it with 'script' and with data (as a JSON or Nota file) from 'config'.`; actor.spawn.doc = `Create a new actor, using this actor as the master, initializing it with 'script' and with data (as a JSON or Nota file) from 'config'.`;
actor.rm_pawn = function(pawn) actor.rm_pawn = function(pawn)
@ -36,26 +43,11 @@ actor.kill = function(){
this.__dead__ = true; this.__dead__ = true;
if (typeof this.die === 'function') this.die(); if (typeof this.die === 'function') this.die();
if (typeof this.stop === 'function') this.stop(); if (typeof this.stop === 'function') this.stop();
if (typeof this.garbage === 'function') this.garbage();
}; };
actor.kill.doc = `Remove this actor and all its padawans from existence.`; actor.kill.doc = `Remove this actor and all its padawans from existence.`;
actor.interval = function(fn, seconds) {
var cur;
var stop = function() {
cur();
}
var f = function() {
fn.call(this);
cur = this.delay(f,seconds);
}
cur = this.delay(f,seconds);
return stop;
}
actor.delay = function(fn, seconds) { actor.delay = function(fn, seconds) {
var timers = this.timers; var timers = this.timers;
var stop = function() { var stop = function() {
@ -64,7 +56,8 @@ actor.delay = function(fn, seconds) {
} }
function execute() { function execute() {
fn(); if (fn) fn();
if (stop.then) stop.then();
stop(); stop();
} }
@ -73,9 +66,10 @@ actor.delay = function(fn, seconds) {
stop.pct = function() { return 1-(stop.remain / stop.seconds); }; stop.pct = function() { return 1-(stop.remain / stop.seconds); };
function update(dt) { function update(dt) {
profile.frame("timer");
stop.remain -= dt; stop.remain -= dt;
if (stop.remain <= 0) if (stop.remain <= 0) execute();
execute(); profile.endframe();
} }
var rm = Register.appupdate.register(update); var rm = Register.appupdate.register(update);

View file

@ -409,7 +409,7 @@ Object.dainty_assign = function(target, source)
Object.isObject = function(o) Object.isObject = function(o)
{ {
return (typeof o === 'object' && !Array.isArray(o)); return (o instanceof Object && !(o instanceof Array));
} }
Object.setter_assign = function(target, source) Object.setter_assign = function(target, source)
@ -512,7 +512,7 @@ Object.copy = function(proto, ...objs)
return c; return c;
} }
/* OBJECT DEFININTioNS */ /* OBJECT DEFININTIONS */
Object.defHidden = function(obj, prop) Object.defHidden = function(obj, prop)
{ {
Object.defineProperty(obj, prop, {enumerable:false, writable:true}); Object.defineProperty(obj, prop, {enumerable:false, writable:true});
@ -657,19 +657,6 @@ Object.defineProperty(Object.prototype, 'push', {
} }
}); });
Object.defineProperty(Object.prototype, 'findIndex', {
value: function(x) {
var i = 0;
for (var key in this) {
if (this[key] === x) return i;
i++;
}
return -1;
}
});
/* STRING DEFS */ /* STRING DEFS */
Object.defineProperty(String.prototype, 'next', { Object.defineProperty(String.prototype, 'next', {
@ -763,12 +750,6 @@ Object.defineProperty(String.prototype, 'up_path', {
} }
}); });
Object.defineProperty(String.prototype, 'resolve', {
value: function(path) {
},
});
Object.defineProperty(String.prototype, 'fromlast', { Object.defineProperty(String.prototype, 'fromlast', {
value: function(val) { value: function(val) {
var idx = this.lastIndexOf(val); var idx = this.lastIndexOf(val);
@ -824,10 +805,6 @@ Object.defineProperty(String.prototype, 'sub', {
} }
}); });
Object.defineProperty(String.prototype, 'rm', {
value: function(index, endidx = index+1) { return this.slice(0,index) + this.slice(endidx); }
});
Object.defineProperty(String.prototype, 'updir', { Object.defineProperty(String.prototype, 'updir', {
value: function() { value: function() {
if (this.lastIndexOf('/') === this.length-1) if (this.lastIndexOf('/') === this.length-1)
@ -840,42 +817,7 @@ Object.defineProperty(String.prototype, 'updir', {
Object.defineProperty(String.prototype, 'trimchr', { Object.defineProperty(String.prototype, 'trimchr', {
value: function(chars) { value: function(chars) {
var start = this.length; return vector.trimchr(this, chars);
var end = 0;
for (var i = 0; i < this.length; i++) {
if (!chars.includes(this[i])) {
start = i;
break;
}
}
for (var i = this.length-1; i >= 0; i--) {
if (!chars.includes(this[i])) {
end = i+1;
break;
}
}
return this.substring(start,end);
}
});
Object.defineProperty(String.prototype, 'startswith', {
value: function(val) {
if (!val) return false;
return this.startsWith(val);
}
});
Object.defineProperty(String.prototype, 'endswith', {
value: function(val) {
if (!val) return false;
return this.endsWith(val);
}
});
Object.defineProperty(String.prototype, 'pct', {
value: function(val) {
} }
}); });
@ -883,11 +825,6 @@ Object.defineProperty(String.prototype, 'uc', { value: function() { return this.
Object.defineProperty(String.prototype, 'lc', {value:function() { return this.toLowerCase(); }}); Object.defineProperty(String.prototype, 'lc', {value:function() { return this.toLowerCase(); }});
/* ARRAY DEFS */ /* ARRAY DEFS */
Object.defineProperty(Array.prototype, 'aspect', {
value: function() {
return this.x/this.y;
}
});
Object.defineProperty(Array.prototype, 'copy', { Object.defineProperty(Array.prototype, 'copy', {
value: function() { value: function() {
var c = []; var c = [];
@ -900,6 +837,18 @@ Object.defineProperty(Array.prototype, 'copy', {
} }
}); });
Object.defineProperty(Array.prototype, 'forFrom', {
value: function(n, fn) {
for (var i = n; i < this.length; i++) fn(this[i]);
}
});
Object.defineProperty(Array.prototype, 'forTo', {
value: function(n, fn) {
for (var i = 0; i < n; i++) fn(this[i]);
}
});
Object.defineProperty(Array.prototype, 'dofilter', { Object.defineProperty(Array.prototype, 'dofilter', {
value: function(fn) { value: function(fn) {
for (let i = 0; i < this.length; i++) { for (let i = 0; i < this.length; i++) {
@ -1026,13 +975,6 @@ swizz.forEach(function(x) {
}; };
make_swizz(); make_swizz();
Object.defineProperty(Array.prototype, 'add', {
value: function(b) {
var c = [];
for (var i = 0; i < this.length; i++) { c[i] = this[i] + b[i]; }
return c;
}});
Object.defineProperty(Array.prototype, 'normalized', { Object.defineProperty(Array.prototype, 'normalized', {
value: function() { value: function() {
var c = this.slice(); var c = this.slice();
@ -1067,13 +1009,6 @@ Object.defineProperty(Array.prototype, 'doubleup', {
} }
}); });
Object.defineProperty(Array.prototype, 'sub', {
value: function(b) {
var c = [];
for (var i = 0; i < this.length; i++) { c[i] = this[i] - b[i]; }
return c;
}});
Object.defineProperty(Array.prototype, 'mult', { Object.defineProperty(Array.prototype, 'mult', {
value: function(arr) { value: function(arr) {
var c = []; var c = [];
@ -1087,16 +1022,6 @@ Object.defineProperty(Array.prototype, 'apply', {
} }
}); });
Object.defineProperty(Array.prototype, 'scale', {
value: function(s) {
if (Array.isArray(s)) {
var c = this.slice();
c.forEach(function(x,i) { c[i] = x * s[i]; });
return c;
}
return this.map(function(x) { return x*s; });
}});
Object.defineProperty(Array.prototype, 'sorted', { Object.defineProperty(Array.prototype, 'sorted', {
value: function() { value: function() {
return this.toSorted(); return this.toSorted();
@ -1131,7 +1056,6 @@ Object.defineProperty(Array.prototype, 'mapvec', {
} }
}); });
Object.defineProperty(Array.prototype, 'remove', { Object.defineProperty(Array.prototype, 'remove', {
value: function(b) { value: function(b) {
var idx = this.indexOf(b); var idx = this.indexOf(b);
@ -1268,19 +1192,23 @@ Object.defineProperty(Array.prototype, 'mirrored', {
} }
}); });
Object.defineProperty(Array.prototype, 'lerp', { Math.lerp = vector.lerp;
value: function(to, t) { Math.gcd = vector.gcd;
var c = []; Math.lcm = vector.lcm;
this.forEach(function(x,i) { Math.sum = vector.sum;
c[i] = (to[i] - x) * t + x; Math.mean = vector.mean;
}); Math.sigma = vector.sigma;
return c; Math.median = vector.median;
}
});
Math.lerp = function(s,f,t) { return (f-s)*t + s; }; Math.variance = function(series) {
Math.gcd = function(a,b) { return b === 0 ? a : gcd(b,a%b); } var mean = Math.mean(series);
Math.lcm = function(a,b) { return (a*b)/gcd(a,b); } var vnce = 0;
for (var i = 0; i < series.length; i++)
vnce += Math.pow(series[i]-mean, 2);
return vnce/(series.length);
}
Math.ci = function(series) { return 3*Math.sigma(series)/Math.sqrt(series.length); }
Math.grab_from_points = function(pos, points, slop) { Math.grab_from_points = function(pos, points, slop) {
var shortest = slop; var shortest = slop;
@ -1324,22 +1252,18 @@ Object.defineProperty(Object.prototype, 'lerp',{
return obj; return obj;
}}); }});
/* MATH EXTENSioNS */ /* MATH EXTENSIONS */
Object.defineProperty(Number.prototype, 'lerp', { Object.defineProperty(Number.prototype, 'lerp', {
value: function(to, t) { value: function(to, t) { return Math.lerp(this, to, t); }
var s = this;
return (to - this) * t + this;
}
}); });
Object.defineProperty(Number.prototype, 'clamp', { Object.defineProperty(Number.prototype, 'clamp', {
value: function(from,to) { value: function(from,to) { return Math.clamp(this,from,to); }
return Math.clamp(this,from,to);
}
}); });
Math.clamp = function (x, l, h) { return x > h ? h : x < l ? l : x; } Math.clamp = vector.clamp;
Math.random_range = function(min,max) { return Math.random() * (max-min) + min; }; Math.random_range = vector.random_range;
Math.rand_int = function(max) { return Math.floor(Math.random()*max); }; Math.rand_int = function(max) { return Math.floor(Math.random()*max); };
Math.snap = function(val, grid) { Math.snap = function(val, grid) {
@ -1351,32 +1275,17 @@ Math.snap = function(val, grid) {
return d+i; return d+i;
} }
Math.angledist = function (a1, a2) { Math.angledist = vector.angledist;
a1 = a1%1;
a2 = a2%1;
var dist = a2 - a1;
if (dist == 0) return dist;
if (dist > 0) {
if (dist > 0.5) return dist-1;
return dist;
}
if (dist < -0.5) return dist+1;
return dist;
};
Math.angledist.doc = "Find the shortest angle between two angles."; Math.angledist.doc = "Find the shortest angle between two angles.";
Math.TAU = Math.PI*2; Math.TAU = Math.PI*2;
Math.deg2rad = function(deg) { return deg * 0.0174533; }; Math.deg2rad = function(deg) { return deg * 0.0174533; };
Math.rad2deg = function(rad) { return rad / 0.0174533; }; Math.rad2deg = function(rad) { return rad / 0.0174533; };
Math.deg2rad = function(x) { return x; };
Math.rad2deg = function(x) { return x; };
Math.turn2rad = function(x) { return x*Math.TAU; }; Math.turn2rad = function(x) { return x*Math.TAU; };
Math.rad2turn = function(x) { return x/Math.TAU; }; Math.rad2turn = function(x) { return x/Math.TAU; };
Math.turn2deg = function(x) { return x*360; }; Math.turn2deg = function(x) { return x*360; };
Math.deg2turn = function(x) { return x/360; }; Math.deg2turn = function(x) { return x/360; };
Math.randomint = function(max) { return Math.clamp(Math.floor(Math.random() * max), 0, max-1); }; Math.randomint = function(max) { return Math.clamp(Math.floor(Math.random() * max), 0, max-1); };
Math.variate = vector.variate;
/* BOUNDINGBOXES */ /* BOUNDINGBOXES */
var bbox = {}; var bbox = {};
@ -1509,39 +1418,18 @@ bbox.fromobjs = function(objs)
/* VECTORS */ /* VECTORS */
var Vector = {}; var Vector = {};
Vector.length = function(v) { Vector.length = function(v) { return Math.hypot(...v); }
var sum = v.reduce(function(acc, val) { return acc + val**2; }, 0);
return Math.sqrt(sum); Vector.norm = vector.norm;
} Vector.project = vector.project;
Vector.norm = function(v) { Vector.dot = vector.dot;
var len = Vector.length(v);
if (!len) return [0,0];
return [v.x/len, v.y/len];
}
Vector.project = function(a, b) { return vector.project(a,b); }
Vector.dot = function(a, b) { return vector.dot(a,b); },
Vector.random = function() { Vector.random = function() {
var vec = [Math.random()-0.5, Math.random()-0.5]; var vec = [Math.random()-0.5, Math.random()-0.5];
return Vector.norm(vec); return Vector.norm(vec);
} }
Vector.angle_between = function(a,b) Vector.angle_between = vector.angle_between;
{ Vector.rotate = vector.rotate;
var dot = Vector.dot(a,b);
var am = Vector.length(a);
var bm = Vector.length(b);
var cos_a = dot / (am*bm);
var angle = Math.acos(cos_a);
return Math.rad2turn(angle);
}
Vector.angle = function(v) { return Math.rad2turn(Math.atan2(v.y, v.x)); }
Vector.rotate = function(v,angle) {
var r = Vector.length(v);
angle += Vector.angle(v);
angle = Math.turn2rad(angle);
return [r*Math.cos(angle), r*Math.sin(angle)];
}
Vector.equal = function(v1, v2, tol) { Vector.equal = function(v1, v2, tol) {
if (!tol) if (!tol)
@ -1652,7 +1540,6 @@ Math.sign = function(n) { return n >= 0 ? 1 : -1; }
return { return {
convert, convert,
time, time,
json,
Vector, Vector,
bbox, bbox,
yaml yaml

View file

@ -15,6 +15,21 @@ var make_point_obj = function(o, p)
var fullrect = [0,0,1,1]; var fullrect = [0,0,1,1];
var sprite_addbucket = function(sprite)
{
var layer = sprite.gameobject.drawlayer;
sprite_buckets[layer] ??= {};
sprite_buckets[layer][sprite.path] ??= {};
sprite_buckets[layer][sprite.path][sprite.guid] = sprite;
}
var sprite_rmbucket = function(sprite)
{
for (var layer of Object.values(sprite_buckets))
for (var path of Object.values(layer))
delete path[sprite.guid];
}
var sprite = { var sprite = {
loop: true, loop: true,
rect: fullrect, rect: fullrect,
@ -41,6 +56,7 @@ var sprite = {
//self.path = playing.path; //self.path = playing.path;
self.frame = playing.frames[f].rect; self.frame = playing.frames[f].rect;
self.rect = [self.frame.x, self.frame.y, self.frame.w, self.frame.h]; self.rect = [self.frame.x, self.frame.y, self.frame.w, self.frame.h];
self.update_dimensions();
f = (f+1)%playing.frames.length; f = (f+1)%playing.frames.length;
if (f === 0) { if (f === 0) {
self.anim_done?.(); self.anim_done?.();
@ -62,6 +78,7 @@ var sprite = {
} }
if (p === this.path) return; if (p === this.path) return;
this._p = p; this._p = p;
this.del_anim?.(); this.del_anim?.();
this.texture = game.texture(p); this.texture = game.texture(p);
@ -69,17 +86,20 @@ var sprite = {
this.rect = fullrect; this.rect = fullrect;
var anim = SpriteAnim.make(p); var anim = SpriteAnim.make(p);
this.update_dimensions();
this.sync();
if (!anim) return; if (!anim) return;
this.anim = anim; this.anim = anim;
this.play(); this.play();
this.pos = this.dimensions().scale(this.anchor); this.pos = this.dimensions().scale(this.anchor);
}, },
get path() { get path() {
return this._p; return this._p;
}, },
kill() { kill() {
sprite_rmbucket(this);
this.del_anim?.(); this.del_anim?.();
this.anim = undefined; this.anim = undefined;
this.gameobject = undefined; this.gameobject = undefined;
@ -92,7 +112,10 @@ var sprite = {
this.pos = this.pos.scale(x); this.pos = this.pos.scale(x);
}, },
anchor:[0,0], anchor:[0,0],
sync() { }, sync() {
sprite_rmbucket(this);
sprite_addbucket(this);
},
pick() { return this; }, pick() { return this; },
boundingbox() { boundingbox() {
var dim = this.dimensions(); var dim = this.dimensions();
@ -101,16 +124,21 @@ var sprite = {
return bbox.fromcwh(realpos,dim); return bbox.fromcwh(realpos,dim);
}, },
update_dimensions() {
this._dimensions = [this.texture.width*this.rect[2], this.texture.height*this.rect[3]];
component.sprite_dim_hook?.(this);
},
dimensions() { dimensions() {
var dim = [this.texture.width, this.texture.height]; return this._dimensions;
dim.x *= this.frame.w;
dim.y *= this.frame.h;
return dim;
}, },
width() { return this.dimensions().x; }, width() { return this.dimensions().x; },
height() { return this.dimensions().y; }, height() { return this.dimensions().y; },
}; };
globalThis.allsprites = {}; globalThis.allsprites = {};
var sprite_buckets = {};
component.sprite_buckets = function() { return sprite_buckets; }
sprite.doc = { sprite.doc = {
path: "Path to the texture.", path: "Path to the texture.",
@ -149,10 +177,14 @@ sprite.inputs.kp1 = function() { this.setanchor("ul"); }
component.sprite = function(obj) { component.sprite = function(obj) {
var sp = Object.create(sprite); var sp = Object.create(sprite);
sp.gameobject = obj; sp.gameobject = obj;
sp.transform = obj.transform;
sp.guid = prosperon.guid(); sp.guid = prosperon.guid();
allsprites[sp.guid] = sp; allsprites[sp.guid] = sp;
if (component.sprite.make_hook) component.sprite.make_hook(sp);
sprite_addbucket(sp);
return sp; return sp;
} }
sprite.shade = [1,1,1,1]; sprite.shade = [1,1,1,1];
Object.mixin(os.make_seg2d(), { Object.mixin(os.make_seg2d(), {

View file

@ -1,5 +1,3 @@
var debug = {};
debug.build = function(fn) { fn(); } debug.build = function(fn) { fn(); }
debug.fn_break = function(fn,obj = globalThis) { debug.fn_break = function(fn,obj = globalThis) {
@ -50,7 +48,7 @@ debug.draw = function() {
"EDIT", [0, 0], 1); "EDIT", [0, 0], 1);
} }
function assert(op, str = `assertion failed [value '${op}']`) var assert = function(op, str = `assertion failed [value '${op}']`)
{ {
if (!op) if (!op)
console.panic(str); console.panic(str);
@ -64,21 +62,6 @@ var Gizmos = {
}, },
}; };
profile.cpu = function(fn, times = 1, q = "unnamed") {
var start = profile.now();
for (var i = 0; i < times; i++)
fn();
var elapsed = profile.now() - start;
var avgt = profile.best_t(elapsed/times);
var totalt = profile.best_t(elapsed);
say(`profile [${q}]: ${profile.best_t(avgt)} average [${profile.best_t(totalt)} for ${times} loops]`);
}
profile.ms = function(t) { return t/1000000; }
profile.secs = function(t) { return t/1000000000; }
/* These controls are available during editing, and during play of debug builds */ /* These controls are available during editing, and during play of debug builds */
debug.inputs = {}; debug.inputs = {};
debug.inputs.f1 = function () { debug.draw_phys = !debug.draw_phys; }; debug.inputs.f1 = function () { debug.draw_phys = !debug.draw_phys; };
@ -226,23 +209,6 @@ debug.api.print_doc = function(name)
return mdoc; return mdoc;
} }
debug.log = {};
debug.log.time = function(fn, name, avg=0)
{
debug.log.time[name] ??= [];
var start = profile.now();
fn();
debug.log.time[name].push(profile.now()-start);
}
debug.kill = function()
{
assert = function() {};
debug.build = function() {};
debug.fn_break = function() {};
}
return { return {
debug, debug,
Gizmos, Gizmos,

View file

@ -569,7 +569,7 @@ var editor = {
obj.ur = sub; obj.ur = sub;
return; return;
} else if (!sub.startswith(obj.ur)) { } else if (!sub.startsWith(obj.ur)) {
console.warn(`Cannot make an ur of type ${sub} from an object with the ur ${obj.ur}`); console.warn(`Cannot make an ur of type ${sub} from an object with the ur ${obj.ur}`);
return; return;
} }
@ -1433,7 +1433,7 @@ replpanel.inputs.tab = function() {
var stub = this.value.fromlast('.'); var stub = this.value.fromlast('.');
var replobj = (editor.selectlist.length === 1) ? "editor.selectlist[0]" : "editor.edit_level"; var replobj = (editor.selectlist.length === 1) ? "editor.selectlist[0]" : "editor.edit_level";
if (this.value.startswith("this.")) if (this.value.startsWith("this."))
keyobj = keyobj.replace("this", replobj); keyobj = keyobj.replace("this", replobj);
if (!this.value.includes('.')) keys.push("this"); if (!this.value.includes('.')) keys.push("this");
@ -1461,7 +1461,7 @@ replpanel.inputs.tab = function() {
if (stub) if (stub)
this.value = o + '.' + comp; this.value = o + '.' + comp;
else if (this.value.endswith('.')) else if (this.value.endsWith('.'))
this.value = o + '.' + comp; this.value = o + '.' + comp;
else else
this.value = comp; this.value = comp;
@ -1677,7 +1677,7 @@ var openlevelpanel = Object.copy(inputpanel, {
keycb() { keycb() {
if(this.value) if(this.value)
this.assets = this.allassets.filter(x => x.startswith(this.value)); this.assets = this.allassets.filter(x => x.startsWith(this.value));
else else
this.assets = this.allassets.slice(); this.assets = this.allassets.slice();
for (var m in this.mumlist) for (var m in this.mumlist)

View file

@ -1,5 +1,9 @@
"use math"; "use math";
Object.defineProperty(String.prototype, 'rm', {
value: function(index, endidx = index+1) { return this.slice(0,index) + this.slice(endidx); }
});
Object.defineProperty(String.prototype, "tolast", { Object.defineProperty(String.prototype, "tolast", {
value: function (val) { value: function (val) {
var idx = this.lastIndexOf(val); var idx = this.lastIndexOf(val);
@ -25,7 +29,26 @@ Object.defineProperty(String.prototype, "folder", {
globalThis.Resources = {}; globalThis.Resources = {};
Resources.replpath = function (str, path) { Resources.rm_fn = function rm_fn(fn, text)
{
var reg = new RegExp(fn.source + "\\s*\\(");
var match;
while (match = text.match(reg)) {
var last = match.index+match[0].length;
var par = 1;
while (par !== 0) {
if (text[last] === '(') par++;
if (text[last] === ')') par--;
last++;
}
text = text.rm(match.index, last);
}
return text;
}
Resources.rm_fn.doc = "Remove calls to a given function from a given text script.";
Resources.replpath = function replpath(str, path) {
if (!str) return str; if (!str) return str;
if (str[0] === "/") return str.rm(0); if (str[0] === "/") return str.rm(0);
@ -43,12 +66,24 @@ Resources.replpath = function (str, path) {
return str; return str;
}; };
Resources.replstrs = function (path) { Resources.replstrs = function replstrs(path) {
if (!path) return; if (!path) return;
var script = io.slurp(path); var script = io.slurp(path);
var regexp = /"[^"\s]*?\.[^"\s]+?"/g; var regexp = /"[^"\s]*?\.[^"\s]+?"/g;
var stem = path.dir(); var stem = path.dir();
// remove console statements
if (!console.enabled)
script = Resources.rm_fn(/console\.(spam|info|warn|error)/, script);
if (!profile.enabled)
script = Resources.rm_fn(/profile\.(cache|frame|endcache|endframe)/, script);
if (!debug.enabled) {
script = Resources.rm_fn(/assert/, script);
script = Resources.rm_fn(/debug\.(build|fn_break)/, script);
}
script = script.replace(regexp, function (str) { script = script.replace(regexp, function (str) {
var newstr = Resources.replpath(str.trimchr('"'), path); var newstr = Resources.replpath(str.trimchr('"'), path);
return `"${newstr}"`; return `"${newstr}"`;
@ -110,38 +145,6 @@ Resources.find_script = function (file) {
return find_ext(file, Resources.scripts); return find_ext(file, Resources.scripts);
}; };
profile.best_t = function (t) {
var qq = "ns";
if (t > 1000) {
t /= 1000;
qq = "us";
if (t > 1000) {
t /= 1000;
qq = "ms";
}
}
return `${t.toPrecision(4)} ${qq}`;
};
profile.report = function (start, msg = "[undefined report]") {
console.info(`${msg} in ${profile.best_t(profile.now() - start)}`);
};
profile.addreport = function (cache, line, start) {
cache ??= profcache;
cache[line] ??= [];
cache[line].push(profile.now() - start);
return profile.now();
};
profile.printreport = function (cache, name) {
var report = name + "\n";
for (var i in cache)
report += `${i} ${profile.best_t(cache[i].reduce((a, b) => a + b) / cache[i].length)}\n`;
return report;
};
console.transcript = ""; console.transcript = "";
console.say = function (msg) { console.say = function (msg) {
msg += "\n"; msg += "\n";
@ -218,425 +221,71 @@ console.doc = {
globalThis.global = globalThis; globalThis.global = globalThis;
var profcache = {}; var use_cache = {};
function use(file, env = {}, script) { globalThis.use = function use(file, env = {}, script) {
file = Resources.find_script(file); file = Resources.find_script(file);
var st = profile.now(); profile.cache("USE", file);
profcache[file] ??= []; if (use_cache[file]) {
var ret = use_cache[file].call(env);
if (use.cache[file]) {
var ret = use.cache[file].call(env);
profile.addreport(profcache, file, st);
return; return;
} }
script ??= Resources.replstrs(file); script ??= Resources.replstrs(file);
script = `(function() { var self = this; ${script}; })`; script = `(function() { var self = this; ${script}; })`;
var fn = os.eval(file, script); var fn = os.eval(file, script);
use.cache[file] = fn; use_cache[file] = fn;
var ret = fn.call(env); var ret = fn.call(env);
profile.addreport(profcache, file, st); profile.endcache();
return ret; return ret;
} }
use.cache = {}; function stripped_use (file, env = {}, script) {
file = Resources.find_script(file);
global.check_registers = function (obj) { if (use_cache[file]) {
for (var reg in Register.registries) var ret = use_cache[file].call(env);
if (typeof obj[reg] === 'function') return;
obj.timers.push(Register.registries[reg].register(obj[reg].bind(obj)));
for (var k in obj) {
if (!k.startswith("on_")) continue;
var signal = k.fromfirst("on_");
Event.observe(signal, obj, obj[k]);
} }
}; script ??= Resources.replstrs(file);
Object.assign(global, use("scripts/base")); script = `(function() { var self = this; ${script}; })`;
global.obscure("global"); var fn = os.eval(file, script);
global.mixin("scripts/render"); var ret = fn.call(env);
global.mixin("scripts/debug"); profile.endcache();
var frame_t = profile.secs(profile.now()); return ret;
}
var sim = {}; function bare_use(file)
sim.mode = "play";
sim.play = function () {
this.mode = "play";
os.reindex_static();
};
sim.playing = function () {
return this.mode === "play";
};
sim.pause = function () {
this.mode = "pause";
};
sim.paused = function () {
return this.mode === "pause";
};
sim.step = function () {
this.mode = "step";
};
sim.stepping = function () {
return this.mode === "step";
};
var physlag = 0;
var gggstart = game.engine_start;
game.engine_start = function (s) {
game.startengine = 1;
gggstart(
function () {
global.mixin("scripts/sound.js");
world_start();
window.set_icon(os.make_texture("icons/moon.gif"));
Object.readonly(window.__proto__, "vsync");
Object.readonly(window.__proto__, "enable_dragndrop");
Object.readonly(window.__proto__, "enable_clipboard");
Object.readonly(window.__proto__, "high_dpi");
Object.readonly(window.__proto__, "sample_count");
s();
shape.quad = {
pos: os.make_buffer([0, 0, 0, 1, 0, 0, 0, 1, 0, 1, 1, 0], 0),
verts: 4,
uv: os.make_buffer([0, 1, 1, 1, 0, 0, 1, 0], 2),
index: os.make_buffer([0, 1, 2, 2, 1, 3], 1),
count: 6,
};
shape.triangle = {
pos: os.make_buffer([0, 0, 0, 0.5, 1, 0, 1, 0, 0], 0),
uv: os.make_buffer([0, 0, 0.5, 1, 1, 0], 2),
verts: 3,
count: 3,
index: os.make_buffer([0, 2, 1], 1),
};
render.init();
},
process,
window.size.x,
window.size.y,
);
};
game.startengine = 0;
var frames = [];
prosperon.release_mode = function()
{ {
prosperon.debug = false; var script = io.slurp(file);
mum.debug = false; if (!script) return;
debug.kill(); script = `(function() { var self = this; ${script}; })`;
} Object.assign(globalThis, os.eval(file, script).call(globalThis));
prosperon.debug = true;
function process() {
var startframe = profile.now();
var dt = profile.secs(profile.now()) - frame_t;
frame_t = profile.secs(profile.now());
prosperon.appupdate(dt);
input.procdown();
if (sim.mode === "play" || sim.mode === "step") {
prosperon.update(dt * game.timescale);
if (sim.mode === "step") sim.pause();
physlag += dt;
while (physlag > physics.delta) {
physlag -= physics.delta;
var st = profile.now();
prosperon.phys2d_step(physics.delta * game.timescale);
prosperon.physupdate(physics.delta * game.timescale);
profile.addreport(profcache, "physics step", st);
}
}
var st = profile.now();
prosperon.window_render(window.size);
prosperon.render();
profile.addreport(profcache, "render frame", st);
frames.push(profile.secs(profile.now() - startframe));
if (frames.length > 20) frames.shift();
} }
globalThis.fps = function () { globalThis.debug = {};
var sum = 0;
for (var i = 0; i < frames.length; i++) sum += frames[i];
return frames.length / sum;
};
game.timescale = 1; profile.enabled = true;
console.enabled = true;
debug.enabled = true;
var eachobj = function (obj, fn) { bare_use("scripts/base.js");
var val = fn(obj); bare_use("scripts/profile.js");
if (val) return val;
for (var o in obj.objects) {
if (obj.objects[o] === obj)
console.error(`Object ${obj.toString()} is referenced by itself.`);
val = eachobj(obj.objects[o], fn);
if (val) return val;
}
};
game.all_objects = function (fn, startobj = world) { prosperon.release = function()
return eachobj(startobj, fn); {
}; profile.enabled = false;
game.find_object = function (fn, startobj = world) {}; console.enabled = false;
debug.enabled = false;
game.tags = {};
game.tag_add = function (tag, obj) {
game.tags[tag] ??= {};
game.tags[tag][obj.guid] = obj;
};
game.tag_rm = function (tag, obj) {
delete game.tags[tag][obj.guid];
};
game.tag_clear_guid = function (guid) {
for (var tag in game.tags) delete game.tags[tag][guid];
};
game.objects_with_tag = function (tag) {
if (!game.tags[tag]) return [];
return Object.values(game.tags[tag]);
};
game.doc = {};
game.doc.object = "Returns the entity belonging to a given id.";
game.doc.pause = "Pause game simulation.";
game.doc.play = "Resume or start game simulation.";
game.doc.camera = "Current camera.";
game.texture = function (path, force = false) {
if (force && game.texture.cache[path]) return game.texture.cache[path];
if (!io.exists(path)) {
console.warn(`Missing texture: ${path}`);
game.texture.cache[path] = game.texture("icons/no_tex.gif");
} else game.texture.cache[path] ??= os.make_texture(path);
return game.texture.cache[path];
};
game.texture.cache = {};
prosperon.semver = {};
prosperon.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;
} }
if (range[0] === "~") { bare_use("preconfig.js");
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 prosperon.semver.cmp(v.join("."), range.join(".")) === 0; if (!profile.enabled)
}; use = stripped_use;
prosperon.semver.cmp = function (v1, v2) { Object.assign(globalThis, use("scripts/prosperon.js"));
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;
};
prosperon.semver.doc =
"Functions for semantic versioning numbers. Semantic versioning is given as a triple digit number, as MAJOR.MINOR.PATCH.";
prosperon.semver.cmp.doc =
"Compare two semantic version numbers, given like X.X.X.";
prosperon.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.`;
prosperon.iconified = function (icon) {};
prosperon.focus = function (focus) {};
prosperon.resize = function (dimensions) {
window.size.x = dimensions.x;
window.size.y = dimensions.y;
};
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) {};
prosperon.quit = function () {
say(profile.printreport(profcache, "USE REPORT"));
say(profile.printreport(entityreport, "ENTITY REPORT"));
console.info("QUITTING");
for (var i in debug.log.time)
say(debug.log.time[i].map((x) => profile.ms(x)));
};
window.size = [640, 480];
window.mode = "keep";
window.toggle_fullscreen = function() { window.fullscreen = !window.fullscreen; }
window.set_icon.doc = "Set the icon of the window using the PNG image at path.";
window.doc = {};
window.doc.dimensions = "Window width and height packaged in an array [width,height]";
window.doc.title = "Name in the title bar of the window.";
window.doc.boundingbox = "Boundingbox of the window, with top and right being its height and width.";
global.mixin("scripts/input");
global.mixin("scripts/std");
global.mixin("scripts/diff");
global.mixin("scripts/color");
global.mixin("scripts/gui");
global.mixin("scripts/tween");
global.mixin("scripts/ai");
var timer = {
update(dt) {
this.remain -= dt;
if (this.remain <= 0) {
this.fn();
this.kill();
}
},
kill() {
this.end();
delete this.fn;
},
delay(fn, secs) {
var t = Object.create(this);
t.time = secs;
t.remain = secs;
t.fn = fn;
t.end = Register.update.register(timer.update.bind(t));
var returnfn = timer.kill.bind(t);
returnfn.remain = secs;
return returnfn;
},
};
global.mixin("scripts/physics");
global.mixin("scripts/geometry");
/*
Factory for creating registries. Register one with 'X.register',
which returns a function that, when invoked, cancels the registry.
*/
var Register = {
registries: [],
add_cb(name, e_event = false) {
var n = {};
var fns = [];
n.register = function (fn, obj) {
if (typeof fn !== "function") return;
if (typeof obj === "object") fn = fn.bind(obj);
fns.push(fn);
return function () {
fns.remove(fn);
};
};
prosperon[name] = function (...args) {
fns.forEach((x) => x(...args));
};
prosperon[name].fns = fns;
n.clear = function () {
fns = [];
};
Register[name] = n;
Register.registries[name] = n;
return n;
},
};
Register.add_cb("appupdate", true);
Register.add_cb("update", true).doc = "Called once per frame.";
Register.add_cb("physupdate", true);
Register.add_cb("gui", true);
Register.add_cb("hud", true);
Register.add_cb("draw_dbg", true);
Register.add_cb("gui_dbg", true);
Register.add_cb("hud_dbg", true);
Register.add_cb("draw", true);
var Event = {
events: {},
observe(name, obj, fn) {
this.events[name] ??= [];
this.events[name].push([obj, fn]);
},
unobserve(name, obj) {
this.events[name] = this.events[name].filter((x) => x[0] !== obj);
},
rm_obj(obj) {
Object.keys(this.events).forEach((name) => Event.unobserve(name, obj));
},
notify(name, ...args) {
if (!this.events[name]) return;
this.events[name].forEach(function (x) {
x[1].call(x[0], ...args);
});
},
};
global.mixin("scripts/spline");
global.mixin("scripts/components");
global.mixin("scripts/actor");
global.mixin("scripts/entity");
function world_start() {
globalThis.world = Object.create(entity);
world.transform = os.make_transform();
world.objects = {};
world.toString = function () {
return "world";
};
world.ur = "world";
world.kill = function () {
this.clear();
};
world.phys = 2;
world.zoom = 1;
world._ed = { selectable: false };
world.ur = {};
world.ur.fresh = {};
game.cam = world;
}
global.mixin("scripts/physics");
global.mixin("scripts/widget");
global.mixin("scripts/mum");
window.title = `Prosperon v${prosperon.version}`;
window.size = [500, 500];

View file

@ -1,5 +1,10 @@
globalThis.entityreport = {}; globalThis.entityreport = {};
var timer_update = function(dt)
{
this.fn();
}
function obj_unique_name(name, obj) { function obj_unique_name(name, obj) {
name = name.replaceAll('.', '_'); name = name.replaceAll('.', '_');
if (!(name in obj)) return name; if (!(name in obj)) return name;
@ -24,6 +29,7 @@ function unique_name(list, name = "new_object") {
}; };
var entity = { var entity = {
drawlayer: -1,
get_comp_by_name(name) { get_comp_by_name(name) {
var comps = []; var comps = [];
for (var c of Object.values(this.components)) for (var c of Object.values(this.components))
@ -37,7 +43,6 @@ var entity = {
this.body = os.make_body(this.transform); this.body = os.make_body(this.transform);
}, },
path_from(o) { path_from(o) {
var p = this.toString(); var p = this.toString();
var c = this.master; var c = this.master;
@ -61,14 +66,14 @@ var entity = {
}, },
sync() { sync() {
this.components.forEach(function(x) { x.sync?.(); }); for (var c of Object.values(this.components)) c.sync?.();
this.objects.forEach(function(x) { x.sync(); }); for (var o of Object.values(this.objects)) o.sync();
}, },
delay(fn, seconds) { delay(fn, seconds) {
var timers = this.timers; var timers = this.timers;
var stop = function() { var stop = function() {
timers.remove(stop); delete timers[guid];
execute = undefined; execute = undefined;
stop = undefined; stop = undefined;
rm?.(); rm?.();
@ -86,12 +91,15 @@ var entity = {
stop.pct = function() { return 1 - (stop.remain/stop.seconds); }; stop.pct = function() { return 1 - (stop.remain/stop.seconds); };
function update(dt) { function update(dt) {
profile.frame("timer");
stop.remain -= dt; stop.remain -= dt;
if (stop.remain <= 0) execute(); if (stop.remain <= 0) execute();
profile.endframe();
} }
var rm = Register.update.register(update); var rm = Register.update.register(update);
timers.push(stop); var guid = prosperon.guid();
timers[guid] = (stop);
return stop; return stop;
}, },
@ -138,7 +146,7 @@ var entity = {
}, },
spawn(text, config, callback) { spawn(text, config, callback) {
var st = profile.now();
var ent = Object.create(entity); var ent = Object.create(entity);
ent.transform = os.make_transform(); ent.transform = os.make_transform();
@ -146,11 +154,11 @@ var entity = {
ent.components = {}; ent.components = {};
ent.objects = {}; ent.objects = {};
ent.timers = []; ent.timers = {};
if (!text) if (!text)
ent.ur = emptyur; ent.ur = emptyur;
else if (typeof text === 'object' && text) {// assume it's an ur else if (text instanceof Object) {// assume it's an ur
ent.ur = text; ent.ur = text;
text = ent.ur.text; text = ent.ur.text;
config = [ent.ur.data, config].filter(x => x).flat(); config = [ent.ur.data, config].filter(x => x).flat();
@ -164,19 +172,20 @@ var entity = {
if (typeof text === 'string') if (typeof text === 'string')
use(text, ent); use(text, ent);
else if (Array.isArray(text)) else if (Array.isArray(text))
text.forEach(path => use(path,ent)); for (var path of text) use(path,ent);
profile.cache("ENTITY TIME", ent.ur.name);
var st = profile.now();
if (typeof config === 'string') if (typeof config === 'string')
Object.merge(ent, json.decode(Resources.replstrs(config))); Object.merge(ent, json.decode(Resources.replstrs(config)));
else if (Array.isArray(config)) else if (Array.isArray(config))
config.forEach(function(path) { for (var path of config) {
if (typeof path === 'string') { if (typeof path === 'string') {
console.info(`ingesting ${path} ...`); console.info(`ingesting ${path} ...`);
Object.merge(ent, json.decode(Resources.replstrs(path))); Object.merge(ent, json.decode(Resources.replstrs(path)));
} }
else if (typeof path === 'object') else if (path instanceof Object)
Object.merge(ent,path); Object.merge(ent,path);
}); };
ent.reparent(this); ent.reparent(this);
@ -191,9 +200,9 @@ var entity = {
check_registers(ent); check_registers(ent);
if (typeof ent.load === 'function') ent.load(); if (ent.load instanceof Function) ent.load();
if (sim.playing()) if (sim.playing())
if (typeof ent.start === 'function') ent.start(); if (ent.start instanceof Function) ent.start();
Object.hide(ent, 'ur', 'components', 'objects', 'timers', 'guid', 'master'); Object.hide(ent, 'ur', 'components', 'objects', 'timers', 'guid', 'master');
@ -230,13 +239,13 @@ var entity = {
for (var i in ent.objects) for (var i in ent.objects)
ent.ur.fresh.objects[i] = ent.objects[i].instance_obj(); ent.ur.fresh.objects[i] = ent.objects[i].instance_obj();
profile.addreport(entityreport, ent.ur.name, st); profile.endcache();
return ent; return ent;
}, },
disable() { this.components.forEach(function(x) { x.disable(); }); }, disable() { for (var x of this.components) x.disable(); },
enable() { this.components.forEach(function(x) { x.enable(); }); }, enable() { for (var x of this.components) x.enable(); },
this2screen(pos) { return game.camera.world2view(this.this2world(pos)); }, this2screen(pos) { return game.camera.world2view(this.this2world(pos)); },
screen2this(pos) { return this.world2this(game.camera.view2world(pos)); }, screen2this(pos) { return this.world2this(game.camera.view2world(pos)); },
@ -280,7 +289,7 @@ var entity = {
var bb = boxes.shift(); var bb = boxes.shift();
boxes.forEach(function(x) { bb = bbox.expand(bb, x); }); for (var x of boxes) bb = bbox.expand(bb, x);
bb = bbox.move(bb, this.pos); bb = bbox.move(bb, this.pos);
@ -338,8 +347,8 @@ dup(diff) {
this.__kill = true; this.__kill = true;
console.spam(`Killing entity of type ${this.ur}`); console.spam(`Killing entity of type ${this.ur}`);
this.timers.forEach(t => t()); this.timers.forEach(x => x());
this.timers = []; delete this.timers;
Event.rm_obj(this); Event.rm_obj(this);
input.do_uncontrol(this); input.do_uncontrol(this);
@ -354,20 +363,19 @@ dup(diff) {
this.components[key].enabled = false; this.components[key].enabled = false;
delete this.components[key]; delete this.components[key];
} }
delete this.components; delete this.components;
this.clear(); this.clear();
if (typeof this.stop === 'function') this.stop(); if (this.stop instanceof Function) this.stop();
game.tag_clear_guid(this.guid); game.tag_clear_guid(this.guid);
for (var i in this) { for (var i in this) {
if (typeof this[i] === 'object') delete this[i]; if (this[i] instanceof Object || this[i] instanceof Function) delete this[i];
if (typeof this[i] === 'function') delete this[i];
} }
}, },
make_objs(objs) { make_objs(objs) {
for (var prop in objs) { for (var prop in objs) {
say(`spawning ${json.encode(objs[prop])}`); say(`spawning ${json.encode(objs[prop])}`);
@ -482,17 +490,17 @@ var gameobject = {
var newpos = relative.this2world(x); var newpos = relative.this2world(x);
var move = newpos.sub(this.pos); var move = newpos.sub(this.pos);
this.rpos = newpos; this.rpos = newpos;
this.objects.forEach(x => x.move(move)); for (var o of this.objects) o.move(move);
}, },
set_angle(x, relative = world) { set_angle(x, relative = world) {
var newangle = relative.angle + x; var newangle = relative.angle + x;
var diff = newangle - this.angle; var diff = newangle - this.angle;
this.rangle = newangle; this.rangle = newangle;
this.objects.forEach(obj => { for (var obj of this.objects) {
obj.rotate(diff); obj.rotate(diff);
obj.set_pos(Vector.rotate(obj.get_pos(obj.master), diff), obj.master); obj.set_pos(Vector.rotate(obj.get_pos(obj.master), diff), obj.master);
}); }
}, },
set_scale(x, relative = world) { set_scale(x, relative = world) {
@ -500,10 +508,10 @@ var gameobject = {
var newscale = relative.scale.map((s,i) => x[i]*s); var newscale = relative.scale.map((s,i) => x[i]*s);
var pct = this.scale.map((s,i) => newscale[i]/s); var pct = this.scale.map((s,i) => newscale[i]/s);
this.rscale = newscale; this.rscale = newscale;
this.objects.forEach(obj => { for (var obj of this.objects) {
obj.grow(pct); obj.grow(pct);
obj.set_pos(obj.get_pos(obj.master).map((x,i) => x*pct[i]), obj.master); obj.set_pos(obj.get_pos(obj.master).map((x,i) => x*pct[i]), obj.master);
}); };
}, },
get_pos(relative = world) { get_pos(relative = world) {
@ -650,17 +658,17 @@ function apply_ur(u, ent) {
if (typeof text === 'string') if (typeof text === 'string')
use(text, ent); use(text, ent);
else if (Array.isArray(text)) else if (Array.isArray(text))
text.forEach(path => use(path,ent)); for (var path of text) use(path,ent);
if (typeof data === 'string') if (typeof data === 'string')
Object.merge(ent, json.decode(Resources.replstrs(data))); Object.merge(ent, json.decode(Resources.replstrs(data)));
else if (Array.isArray(data)) { else if (Array.isArray(data)) {
data.forEach(function(path) { for (var path of data) {
if (typeof path === 'string') if (typeof path === 'string')
Object.merge(ent, json.decode(Resources.replstrs(data))); Object.merge(ent, json.decode(Resources.replstrs(data)));
else if (typeof path === 'object') else if (path instanceof Object)
Object.merge(ent,path); Object.merge(ent,path);
}); };
} }
} }
} }
@ -677,7 +685,10 @@ var getur = function(text, data)
name: "empty" name: "empty"
}; };
} }
var urstr = text + "+" + data; var urstr = text;
if (data)
urstr += "+" + data;
if (!ur[urstr]) { if (!ur[urstr]) {
ur[urstr] = { ur[urstr] = {
name: urstr, name: urstr,
@ -726,16 +737,6 @@ game.loadurs = function() {
newur.data = data; newur.data = data;
} }
} }
return;
for (var file of io.glob("**.json").filter(f => !ur[f.name()])) {
if (file[0] === '.' || file[0] === '_') continue;
var newur = ur_from_file(file);
if (!newur) continue;
Object.assign(newur, {
data: file
});
}
}; };
game.ur = {}; game.ur = {};

View file

@ -190,18 +190,28 @@ mum.image = function(path, data = {})
if (typeof path === 'string') if (typeof path === 'string')
tex = game.texture(path); tex = game.texture(path);
data.width ??= tex.width; if (!data.height)
data.height ??= tex.height; if (data.width)
data.height = tex.height * (data.width/tex.width);
else
data.height = tex.height;
var aa = [0,0].sub(data.anchor); if (!data.width)
if (data.height)
data.width = tex.width * (data.height/tex.height);
else
data.height = tex.height;
if (!data.width) data.width = tex.width;
if (!data.height) data.height = tex.height;
var aa = [0,1].sub(data.anchor);
data.drawpos = data.drawpos.add(aa.scale([data.width,data.height])); data.drawpos = data.drawpos.add(aa.scale([data.width,data.height]));
if (data.slice) if (data.slice)
render.slice9(tex, data.drawpos, data.slice, [data.width,data.height]); render.slice9(tex, data.drawpos, data.slice, [data.width,data.height]);
else { else
cursor.y -= tex.height*data.scale; data.bb = render.image(tex, data.drawpos, [data.width, data.height]);
data.bb = render.image(tex, data.drawpos, [data.scale*tex.width, data.scale*tex.height]);
}
end(data); end(data);
} }
@ -262,3 +272,41 @@ mum.ex_hud = function()
mum.label("TOP RIGHT", {pos:game.size, anchor:[1,1]}); mum.label("TOP RIGHT", {pos:game.size, anchor:[1,1]});
mum.label("BOTTOM RIGHT", {pos:[game.size.x, 0], anchor:[1,0]}); mum.label("BOTTOM RIGHT", {pos:[game.size.x, 0], anchor:[1,0]});
} }
mum.drawinput = undefined;
var ptext = "";
var panpan = {
draw() {
mum.rectangle({pos:[0,0], anchor:[0,0], height:20, width: window.size.x, padding:[10,16], color:Color.black});
mum.label("input level: ");
mum.label(ptext, {offset:[50,0], color:Color.red});
},
inputs: {
block: true,
char(c) {
ptext += c
},
enter() {
delete mum.drawinput;
player[0].uncontrol(panpan);
},
escape() {
delete mum.drawinput;
player[0].uncontrol(panpan);
},
backspace() {
ptext = ptext.slice(0,ptext.length-1);
}
},
}
mum.textinput = function (fn, str = "") {
mum.drawinput = panpan.draw;
ptext = str;
player[0].control(panpan);
panpan.inputs.enter = function() {
fn(ptext);
delete mum.drawinput;
player[0].uncontrol(panpan);
}
}

116
scripts/particle.js Normal file
View file

@ -0,0 +1,116 @@
var emitter = {};
emitter.particles = {};
emitter.life = 10;
emitter.scale = 1;
emitter.grow_for = 0;
emitter.spawn_timer = 0;
emitter.pps = 0;
emitter.color = Color.white;
emitter.draw = function()
{
var amt = Object.values(this.particles).length;
if (amt === 0) return;
render.use_shader(this.shader);
render.use_mat(this);
render.make_particle_ssbo(Object.values(this.particles), this.ssbo);
render.draw(this.shape, this.ssbo, amt);
}
var std_spawn = function(par)
{
}
var std_step = function(p)
{
if (p.time < this.grow_for) {
var s = Math.lerp(0, this.scale, p.time/this.grow_for);
p.transform.scale = [s,s,s];
}
else if (p.time > (p.life - this.shrink_for)) {
var s = Math.lerp(0,this.scale,(p.life-p.time)/this.shrink_for);
p.transform.scale=[s,s,s];
} else
p.transform.scale = [this.scale,this.scale,this.scale];
}
var std_die = function(par)
{
}
emitter.spawn_hook = std_spawn;
emitter.step_hook = std_step;
emitter.die_hook = std_die;
emitter.spawn = function(t)
{
t ??= this.transform;
var par = this.dead.shift();
if (par) {
par.body.pos = t.pos;
par.transform.scale = [this.scale,this.scale,this.scale];
this.particles[par.id] = par;
par.time = 0;
this.spawn_hook(par);
return;
}
par = {
transform: os.make_transform(),
life: this.life,
time: 0,
color: this.color
};
par.body = os.make_body(par.transform);
par.body.pos = t.pos;
par.transform.scale = [this.scale,this.scale,this.scale];
par.id = prosperon.guid();
this.particles[par.id] = par;
this.spawn_hook(par);
}
emitter.step = function(dt)
{
// update spawning particles
if (this.on && this.pps > 0) {
this.spawn_timer += dt;
var pp = 1/this.pps;
while (this.spawn_timer > pp) {
this.spawn_timer -= pp;
this.spawn();
}
}
// update all particles
for (var p of Object.values(this.particles)) {
p.time += dt;
this.step_hook?.(p);
if (p.time >= p.life) {
this.die_hook(p);
this.dead.push(this.particles[p.id]);
delete this.particles[p.id];
}
}
}
emitter.burst = function(count, t) {
for (var i = 0; i < count; i++) this.spawn(t);
}
var make_emitter = function()
{
var e = Object.create(emitter);
e.ssbo = render.make_textssbo();
e.shape = shape.centered_quad;
e.shader = "shaders/baseparticle.cg";
e.dead = [];
return e;
}
return {make_emitter};

279
scripts/profile.js Normal file
View file

@ -0,0 +1,279 @@
var t_units = ["ns", "us", "ms", "s", "ks", "Ms"];
function calc_cpu(fn, times, diff=0)
{
var series = [];
for (var i = 0; i < times; i++) {
var st = profile.now();
fn(i);
series.push(profile.now()-st-diff);
}
return series;
}
function empty_fn() {}
profile.cpu = function profile_cpu(fn, times = 1, q = "unnamed") {
profile.gather_stop();
var empty = calc_cpu(empty_fn, 100000);
var mean = Math.mean(empty);
var series = calc_cpu(fn,times, mean);
var elapsed = Math.sum(series);
var avgt = profile.best_t(elapsed/series.length);
var totalt = profile.best_t(elapsed);
say(`profile [${q}]: ${avgt} ± ${profile.best_t(Math.ci(series))} [${totalt} for ${times} loops]`);
start_prof_gather();
}
profile.ms = function(t) { return t/1000000; }
profile.secs = function(t) { return t/1000000000; }
var callgraph = {};
var st = profile.now();
function add_callgraph(fn, line, time) {
var cc = callgraph[line];
if (!cc) {
var cc = {};
callgraph[line] = cc;
cc.time = 0;
cc.hits = 0;
cc.fn = fn;
cc.line = line;
}
cc.time += time;
cc.hits++;
}
var hittar = 500;
var hitpct = 0.2;
var start_gather = profile.now();
var gathering_cpu = false;
profile.start_cpu_gather = function()
{
if (gathering_cpu) return;
gathering_cpu = true;
profile.gather(hittar, function() {
var time = profile.now()-st;
var err = new Error();
var stack = err.stack.split("\n");
stack = stack.slice(1);
stack = stack.map(x => x.slice(7).split(' '));
var fns = stack.map(x => x[0]).filter(x=>x);
var lines = stack.map(x => x[1]).filter(x => x);
lines = lines.map(x => x.slice(1,x.length-1));
for (var i = 0; i < fns.length; i++)
add_callgraph(fns[i], lines[i], time);
st = profile.now();
profile.gather_rate(Math.variate(hittar,hitpct));
});
}
profile.cpu_frame = function()
{
if (gathering_cpu) return;
profile.gather(Math.random_range(300,600), function() {
console.stack(2);
profile.gather_stop();
});
}
var filecache = {};
function get_line(file, line) {
var text = filecache[file];
if (!text) {
var f = io.slurp(file);
if (!f) {
filecache[file] = "undefined";
return filecache[file];
}
filecache[file] = io.slurp(file).split('\n');
text = filecache[file];
}
if (typeof text === 'string') return text;
text = text[Number(line)-1];
if (!text) return "NULL";
return text.trim();
}
profile.stop_cpu_instr = function()
{
if (!gathering_cpu) return;
say("===CPU INSTRUMENTATION===\n");
var gather_time = profile.now()-start_gather;
var e = Object.values(callgraph);
e = e.sort((a,b) => {
if (a.time > b.time) return -1;
return 1;
});
e.forEach(x => {
var ffs = x.line.split(':');
var time = profile.best_t(x.time);
var pct = x.time/gather_time*100;
say(`${x.line}::${x.fn}:: ${time} (${pct.toPrecision(3)}%) (${x.hits} hits) --> ${get_line(ffs[0], ffs[1])}`);
});
}
profile.best_t = function (t) {
var qq = 0;
while (t > 1000 && qq < t_units.length-1) {
t /= 1000;
qq++;
}
return `${t.toPrecision(4)} ${t_units[qq]}`;
};
profile.report = function (start, msg = "[undefined report]") { console.info(`${msg} in ${profile.best_t(profile.now() - start)}`); };
var frame_avg = false;
profile.start_frame_avg = function()
{
if (frame_avg) return;
say("===STARTING FRAME AVERAGE MEASUREMENTS===");
profile_frames = {};
profile_frame_ts = [];
profile_cframe = profile_frames;
pframe = 0;
frame_avg = true;
}
profile.stop_frame_avg = function()
{
if (!frame_avg) return;
frame_avg = false;
profile.print_frame_avg();
}
profile.toggle_frame_avg = function()
{
if (frame_avg) profile.stop_frame_avg();
else profile.start_frame_avg();
}
var profile_frames = {};
var profile_frame_ts = [];
var profile_cframe = profile_frames;
var pframe = 0;
profile.frame = function profile_frame(title)
{
if (!frame_avg) return;
profile_frame_ts.push(profile_cframe);
profile_cframe[title] ??= {};
profile_cframe = profile_cframe[title];
profile_cframe._times ??= [];
profile_cframe._times[pframe] ??= 0;
profile_cframe._times[pframe] = profile.now() - profile_cframe._times[pframe];
}
profile.endframe = function profile_endframe()
{
if (!frame_avg) return;
if (profile_cframe === profile_frames) return;
profile_cframe._times[pframe] = profile.now() - profile_cframe._times[pframe];
profile_cframe = profile_frame_ts.pop();
if (profile_cframe === profile_frames) pframe++;
}
var print_frame = function(frame, indent, title)
{
say(indent + `${title} ::::: ${profile.best_t(Math.mean(frame._times))} ± ${profile.best_t(Math.ci(frame._times))} (${frame._times.length} hits)`);
for (var i in frame) {
if (i === '_times') continue;
print_frame(frame[i], indent + " ", i);
}
}
profile.print_frame_avg = function()
{
say("===FRAME AVERAGES===\n");
var indent = "";
for (var i in profile_frames)
print_frame(profile_frames[i], "", 'frame');
say("\n");
}
var report_cache = {};
var cachest = 0;
var cachegroup;
var cachetitle;
profile.cache = function profile_cache(group, title)
{
cachest = profile.now();
cachegroup = group;
cachetitle = title;
}
profile.endcache = function profile_endcache(tag = "")
{
addreport(cachegroup, cachetitle + tag, cachest);
}
profile.print_cache_report = function()
{
var str = "===START CACHE REPORTS===\n";
for (var i in report_cache)
str += printreport(report_cache[i], i) + "\n";
say(str);
}
function addreport(group, line, start) {
if (typeof group !== 'string') group = 'UNGROUPED';
report_cache[group] ??= {};
var cache = report_cache[group];
cache[line] ??= [];
var t = profile.now();
cache[line].push(t - start);
return t;
};
function printreport(cache, name) {
var report = `==${name}==` + "\n";
var reports = [];
for (var i in cache) {
var time = cache[i].reduce((a,b)=>a+b);
reports.push({
time:time,
name:i,
hits:cache[i].length,
avg:time/cache[i].length
});
}
reports = reports.sort((a,b) => {
if (a.avg<b.avg) return 1;
return -1;
});
for (var rep of reports)
report += `${rep.name} ${profile.best_t(rep.avg)} (${rep.hits} hits) (total ${profile.best_t(rep.time)})\n`;
return report;
};

396
scripts/prosperon.js Normal file
View file

@ -0,0 +1,396 @@
globalThis.gamestate = {};
global.check_registers = function (obj) {
for (var reg in Register.registries) {
if (typeof obj[reg] === 'function') {
var fn = obj[reg].bind(obj);
var name = obj.ur ? obj.ur.name : obj.toString();
obj.timers.push(Register.registries[reg].register(fn, name));
}
}
for (var k in obj) {
if (!k.startsWith("on_")) continue;
var signal = k.fromfirst("on_");
Event.observe(signal, obj, obj[k]);
}
};
global.obscure("global");
global.mixin("scripts/render");
global.mixin("scripts/debug");
var frame_t = profile.secs(profile.now());
var sim = {};
sim.mode = "play";
sim.play = function () {
this.mode = "play";
os.reindex_static();
};
sim.playing = function () {
return this.mode === "play";
};
sim.pause = function () {
this.mode = "pause";
};
sim.paused = function () {
return this.mode === "pause";
};
sim.step = function () {
this.mode = "step";
};
sim.stepping = function () {
return this.mode === "step";
};
var physlag = 0;
var gggstart = game.engine_start;
game.engine_start = function (s) {
game.startengine = 1;
gggstart(
function () {
global.mixin("scripts/sound.js");
world_start();
window.set_icon(os.make_texture("icons/moon.gif"));
Object.readonly(window.__proto__, "vsync");
Object.readonly(window.__proto__, "enable_dragndrop");
Object.readonly(window.__proto__, "enable_clipboard");
Object.readonly(window.__proto__, "high_dpi");
Object.readonly(window.__proto__, "sample_count");
s();
shape.quad = {
pos: os.make_buffer([0, 0, 0, 1, 0, 0, 0, 1, 0, 1, 1, 0], 0),
verts: 4,
uv: os.make_buffer([0, 1, 1, 1, 0, 0, 1, 0], 2),
index: os.make_buffer([0, 1, 2, 2, 1, 3], 1),
count: 6,
};
shape.triangle = {
pos: os.make_buffer([0, 0, 0, 0.5, 1, 0, 1, 0, 0], 0),
uv: os.make_buffer([0, 0, 0.5, 1, 1, 0], 2),
verts: 3,
count: 3,
index: os.make_buffer([0, 2, 1], 1),
};
shape.centered_quad = {
pos: os.make_buffer([-0.5, -0.5, -0.5, 0.5, -0.5, -0.5, -0.5, 0.5, -0.5, 0.5, 0.5, -0.5], 0),
verts: 4,
uv: os.make_buffer([0,1,1,1,0,0,1,0],2),
index: os.make_buffer([0,1,2,2,1,3],1),
count: 6
};
render.init();
prosperon.camera = prosperon.make_camera();
var camera = prosperon.camera;
camera.transform.pos = [0,0,-100];
camera.mode = "keep";
camera.break = "fit";
camera.size = game.size;
gamestate.camera = camera;
prosperon.hudcam = prosperon.make_camera();
var hudcam = prosperon.hudcam;
hudcam.near = 0;
hudcam.size = camera.size;
hudcam.mode = "keep";
hudcam.break = "fit";
prosperon.appcam = prosperon.make_camera();
var appcam = prosperon.appcam;
appcam.near = 0;
appcam.size = window.size;
appcam.transform.pos = [window.size.x,window.size.y,-100];
prosperon.screencolor = render.screencolor();
},
prosperon.process,
window.size.x,
window.size.y,
);
};
game.startengine = 0;
var frames = [];
prosperon.release_mode = function()
{
prosperon.debug = false;
mum.debug = false;
debug.kill();
}
prosperon.debug = true;
globalThis.fps = function () {
return 0;
// var sum = 0;
// for (var i = 0; i < frames.length; i++) sum += frames[i];
// return frames.length / sum;
};
game.timescale = 1;
var eachobj = function (obj, fn) {
var val = fn(obj);
if (val) return val;
for (var o in obj.objects) {
if (obj.objects[o] === obj)
console.error(`Object ${obj.toString()} is referenced by itself.`);
val = eachobj(obj.objects[o], fn);
if (val) return val;
}
};
game.all_objects = function (fn, startobj = world) {
return eachobj(startobj, fn);
};
game.find_object = function (fn, startobj = world) {};
game.tags = {};
game.tag_add = function (tag, obj) {
game.tags[tag] ??= {};
game.tags[tag][obj.guid] = obj;
};
game.tag_rm = function (tag, obj) {
delete game.tags[tag][obj.guid];
};
game.tag_clear_guid = function (guid) {
for (var tag in game.tags) delete game.tags[tag][guid];
};
game.objects_with_tag = function (tag) {
if (!game.tags[tag]) return [];
return Object.values(game.tags[tag]);
};
game.doc = {};
game.doc.object = "Returns the entity belonging to a given id.";
game.doc.pause = "Pause game simulation.";
game.doc.play = "Resume or start game simulation.";
game.doc.camera = "Current camera.";
game.texture = function (path, force = false) {
if (force && game.texture.cache[path]) return game.texture.cache[path];
if (!io.exists(path)) {
console.warn(`Missing texture: ${path}`);
game.texture.cache[path] = game.texture("icons/no_tex.gif");
} else game.texture.cache[path] ??= os.make_texture(path);
return game.texture.cache[path];
};
game.texture.cache = {};
prosperon.semver = {};
prosperon.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;
}
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 prosperon.semver.cmp(v.join("."), range.join(".")) === 0;
};
prosperon.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;
};
prosperon.semver.doc =
"Functions for semantic versioning numbers. Semantic versioning is given as a triple digit number, as MAJOR.MINOR.PATCH.";
prosperon.semver.cmp.doc =
"Compare two semantic version numbers, given like X.X.X.";
prosperon.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.`;
prosperon.iconified = function (icon) {};
prosperon.focus = function (focus) {};
prosperon.resize = function (dimensions) {
window.size.x = dimensions.x;
window.size.y = dimensions.y;
};
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) {};
prosperon.quit = function () {
profile.print_cache_report();
profile.stop_frame_avg()
profile.stop_cpu_instr();
};
window.size = [640, 480];
window.mode = "keep";
window.toggle_fullscreen = function() { window.fullscreen = !window.fullscreen; }
window.set_icon.doc = "Set the icon of the window using the PNG image at path.";
window.doc = {};
window.doc.dimensions = "Window width and height packaged in an array [width,height]";
window.doc.title = "Name in the title bar of the window.";
window.doc.boundingbox = "Boundingbox of the window, with top and right being its height and width.";
global.mixin("scripts/input");
global.mixin("scripts/std");
global.mixin("scripts/diff");
global.mixin("scripts/color");
global.mixin("scripts/gui");
global.mixin("scripts/tween");
global.mixin("scripts/ai");
global.mixin("scripts/particle");
global.mixin("scripts/physics");
global.mixin("scripts/geometry");
/*
Factory for creating registries. Register one with 'X.register',
which returns a function that, when invoked, cancels the registry.
*/
var Register = {
registries: [],
add_cb(name, e_event = false) {
var n = {};
var fns = {};
n.register = function (fn, oname) {
if (!(fn instanceof Function)) return;
var guid = prosperon.guid();
var dofn = function(...args) {
profile.cache(name,oname);
var st = profile.now();
fn(...args);
profile.endcache();
}
fns[guid] = dofn;
return function () {
delete fns[guid];
};
};
prosperon[name] = function (...args) {
fns.forEach(x => x(...args));
};
prosperon[name].fns = fns;
n.clear = function () {
fns = [];
};
Register[name] = n;
Register.registries[name] = n;
return n;
},
};
Register.add_cb("appupdate", true);
Register.add_cb("update", true).doc = "Called once per frame.";
Register.add_cb("physupdate", true);
Register.add_cb("gui", true);
Register.add_cb("hud", true);
Register.add_cb("draw_dbg", true);
Register.add_cb("gui_dbg", true);
Register.add_cb("hud_dbg", true);
Register.add_cb("draw", true);
Register.add_cb("imgui", true);
var Event = {
events: {},
observe(name, obj, fn) {
this.events[name] ??= [];
this.events[name].push([obj, fn]);
},
unobserve(name, obj) {
this.events[name] = this.events[name].filter((x) => x[0] !== obj);
},
rm_obj(obj) {
Object.keys(this.events).forEach((name) => Event.unobserve(name, obj));
},
notify(name, ...args) {
if (!this.events[name]) return;
this.events[name].forEach(function (x) {
x[1].call(x[0], ...args);
});
},
};
global.mixin("scripts/spline");
global.mixin("scripts/components");
global.mixin("scripts/actor");
global.mixin("scripts/entity");
function world_start() {
globalThis.world = Object.create(entity);
world.transform = os.make_transform();
world.objects = {};
world.toString = function () {
return "world";
};
world.ur = "world";
world.kill = function () {
this.clear();
};
world.phys = 2;
world.zoom = 1;
world._ed = { selectable: false };
world.ur = {};
world.ur.fresh = {};
game.cam = world;
}
global.mixin("scripts/physics");
global.mixin("scripts/widget");
global.mixin("scripts/mum");
window.title = `Prosperon v${prosperon.version}`;
window.size = [500, 500];
return {
Register,
sim,
frame_t,
physlag,
Event
}

View file

@ -6,22 +6,24 @@ render.doc = {
var cur = {}; var cur = {};
render.use_shader = function(shader) render.use_shader = function use_shader(shader)
{ {
if (typeof shader === 'string')
shader = make_shader(shader);
if (cur.shader === shader) return; if (cur.shader === shader) return;
cur.shader = shader; cur.shader = shader;
cur.globals = {};
cur.bind = undefined; cur.bind = undefined;
cur.mesh = undefined; cur.mesh = undefined;
render.setpipeline(shader.pipe); render.setpipeline(shader.pipe);
shader_globals(cur.shader);
} }
render.use_mat = function(mat) render.use_mat = function use_mat(mat)
{ {
if (!cur.shader) return; if (!cur.shader) return;
if (cur.mat === mat) return; if (cur.mat === mat) return;
render.shader_apply_material(cur.shader, mat, cur.mat); shader_apply_material(cur.shader, mat, cur.mat);
cur.mat = mat; cur.mat = mat;
@ -36,7 +38,7 @@ render.use_mat = function(mat)
var models_array = []; var models_array = [];
render.set_model = function(t) function set_model(t)
{ {
if (cur.shader.vs.unimap.model) if (cur.shader.vs.unimap.model)
render.setunim4(0, cur.shader.vs.unimap.model.slot, t); render.setunim4(0, cur.shader.vs.unimap.model.slot, t);
@ -94,7 +96,7 @@ var face_map = {
ccw: 1 ccw: 1
} }
render.poly_prim = function(verts) render.poly_prim = function poly_prim(verts)
{ {
var index = []; var index = [];
if (verts.length < 1) return undefined; if (verts.length < 1) return undefined;
@ -129,42 +131,40 @@ function shader_directive(shader, name, map)
return ff; return ff;
} }
function global_uni(uni, stage) var uni_globals = {
{ time(stage, slot) { render.setuniv(stage, slot, profile.secs(profile.now())); },
cur.globals[stage] ??= {}; projection(stage,slot) { render.setuniproj(stage, slot); },
if (cur.globals[stage][uni.name]) return true; view(stage,slot) { render.setuniview(stage, slot); },
switch(uni.name) { vp(stage,slot) { render.setunivp(stage,slot); },
case "time":
cur.globals[stage][uni.name]
render.setuniv(stage, uni.slot, profile.secs(profile.now()));
cur.globals[stage][uni.name] = true;
return true;
case "projection":
render.setuniproj(stage, uni.slot);
cur.globals[stage][uni.name] = true;
return true;
case "view":
render.setuniview(stage, uni.slot);
cur.globals[stage][uni.name] = true;
return true;
case "vp":
render.setunivp(stage, uni.slot);
cur.globals[stage][uni.name] = true;
return true;
} }
return false; function set_global_uni(uni, stage) {
uni_globals[uni.name]?.(stage, uni.slot);
} }
var setcam = render.set_camera; var setcam = render.set_camera;
render.set_camera = function(cam) render.set_camera = function(cam)
{ {
if (nextflush) {
nextflush();
nextflush = undefined;
}
delete cur.shader; delete cur.shader;
setcam(cam); setcam(cam);
} }
render.make_shader = function(shader) var shader_cache = {};
function strip_shader_inputs(shader)
{ {
for (var a of shader.vs.inputs)
a.name = a.name.slice(2);
}
function make_shader(shader)
{
if (shader_cache[shader]) return shader_cache[shader];
var file = shader; var file = shader;
shader = io.slurp(shader); shader = io.slurp(shader);
if (!shader) { if (!shader) {
@ -172,20 +172,25 @@ render.make_shader = function(shader)
shader = io.slurp(`shaders/${file}`); shader = io.slurp(`shaders/${file}`);
} }
var writejson = `.prosperon/${file.name()}.shader.json`; var writejson = `.prosperon/${file.name()}.shader.json`;
var st = profile.now();
profile.cache("shader", file);
breakme: if (io.exists(writejson)) { breakme: if (io.exists(writejson)) {
var data = json.decode(io.slurp(writejson)); var data = json.decode(io.slurp(writejson));
var filemod = io.mod(writejson); var filemod = io.mod(writejson);
if (!data.files) break breakme; if (!data.files) break breakme;
for (var i of data.files) for (var i of data.files) {
if (io.mod(i) > filemod) if (io.mod(i) > filemod) {
break breakme; break breakme;
}
}
profile.report(st, `CACHE make shader from ${file}`); profile.endcache(" [cached]");
var shaderobj = json.decode(io.slurp(writejson)); var shaderobj = json.decode(io.slurp(writejson));
var obj = shaderobj[os.sys()]; var obj = shaderobj[os.sys()];
strip_shader_inputs(obj);
obj.pipe = render.pipeline(obj); obj.pipe = render.pipeline(obj);
shader_cache[shader] = obj;
return obj; return obj;
} }
@ -297,11 +302,13 @@ render.make_shader = function(shader)
compiled.files = files; compiled.files = files;
io.slurpwrite(writejson, json.encode(compiled)); io.slurpwrite(writejson, json.encode(compiled));
profile.report(st, `make shader from ${file}`); profile.endcache();
var obj = compiled[os.sys()]; var obj = compiled[os.sys()];
strip_shader_inputs(obj);
obj.pipe = render.pipeline(obj); obj.pipe = render.pipeline(obj);
shader_cache[shader] = obj;
return obj; return obj;
} }
@ -312,10 +319,19 @@ var shader_unisize = {
16: render.setuniv4 16: render.setuniv4
}; };
render.shader_apply_material = function(shader, material = {}, old = {}) function shader_globals(shader)
{
for (var p in shader.vs.unimap)
set_global_uni(shader.vs.unimap[p], 0);
for (var p in shader.fs.unimap)
set_global_uni(shader.fs.unimap[p], 1);
}
function shader_apply_material(shader, material = {}, old = {})
{ {
for (var p in shader.vs.unimap) { for (var p in shader.vs.unimap) {
if (global_uni(shader.vs.unimap[p], 0)) continue; if (!(p in material)) continue;
if (material[p] === old[p]) continue; if (material[p] === old[p]) continue;
assert(p in material, `shader ${shader.name} has no uniform for ${p}`); assert(p in material, `shader ${shader.name} has no uniform for ${p}`);
var s = shader.vs.unimap[p]; var s = shader.vs.unimap[p];
@ -323,7 +339,7 @@ render.shader_apply_material = function(shader, material = {}, old = {})
} }
for (var p in shader.fs.unimap) { for (var p in shader.fs.unimap) {
if (global_uni(shader.fs.unimap[p], 1)) continue; if (!(p in material)) continue;
if (material[p] === old[p]) continue; if (material[p] === old[p]) continue;
assert(p in material, `shader ${shader.name} has no uniform for ${p}`); assert(p in material, `shader ${shader.name} has no uniform for ${p}`);
var s = shader.fs.unimap[p]; var s = shader.fs.unimap[p];
@ -340,7 +356,7 @@ render.shader_apply_material = function(shader, material = {}, old = {})
render.setuniv2(0, shader.vs.unimap.diffuse_size.slot, [material.diffuse.width, material.diffuse.height]); render.setuniv2(0, shader.vs.unimap.diffuse_size.slot, [material.diffuse.width, material.diffuse.height]);
} }
render.sg_bind = function(mesh, ssbo) function sg_bind(mesh, ssbo)
{ {
if (cur.mesh === mesh && cur.bind) { if (cur.mesh === mesh && cur.bind) {
cur.bind.inst = 1; cur.bind.inst = 1;
@ -356,12 +372,9 @@ render.sg_bind = function(mesh, ssbo)
if (cur.shader.vs.inputs) if (cur.shader.vs.inputs)
for (var a of cur.shader.vs.inputs) { for (var a of cur.shader.vs.inputs) {
if (!(a.name in mesh)) { if (!(a.name in mesh)) {
if (!(a.name.slice(2) 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.`);
return undefined; return undefined;
} else } else
bind.attrib.push(mesh[a.name.slice(2)]);
} else
bind.attrib.push(mesh[a.name]); bind.attrib.push(mesh[a.name]);
} }
@ -426,14 +439,27 @@ var textshader;
var circleshader; var circleshader;
var polyshader; var polyshader;
var slice9shader; var slice9shader;
var parshader;
var spritessboshader;
var polyssboshader;
var sprite_ssbo;
render.init = function() { render.init = function() {
textshader = render.make_shader("shaders/text_base.cg"); textshader = make_shader("shaders/text_base.cg");
render.spriteshader = render.make_shader("shaders/sprite.cg"); render.spriteshader = make_shader("shaders/sprite.cg");
render.postshader = render.make_shader("shaders/simplepost.cg"); spritessboshader = make_shader("shaders/sprite_ssbo.cg");
slice9shader = render.make_shader("shaders/9slice.cg"); render.postshader = make_shader("shaders/simplepost.cg");
circleshader = render.make_shader("shaders/circle.cg"); slice9shader = make_shader("shaders/9slice.cg");
polyshader = render.make_shader("shaders/poly.cg"); circleshader = make_shader("shaders/circle.cg");
polyshader = make_shader("shaders/poly.cg");
parshader = make_shader("shaders/baseparticle.cg");
polyssboshader = make_shader("shaders/poly_ssbo.cg");
textssbo = render.make_textssbo();
poly_ssbo = render.make_textssbo();
sprite_ssbo = render.make_textssbo();
globalThis.imgui = render.imgui_init();
render.textshader = textshader; render.textshader = textshader;
@ -467,7 +493,55 @@ render.init = function() {
} }
} }
render.circle = function(pos, radius, color) { render.sprites = function render_sprites(gridsize = 1)
{
/*
profile.frame("bucketing");
var sps = Object.values(allsprites);
var buckets = [];
for (var i = 0; i <= 20; i++)
buckets[i] = {};
for (var sprite of sps) {
var layer = sprite.gameobject.drawlayer+10;
if (buckets[layer][sprite.path])
buckets[layer][sprite.path].push(sprite);
else
buckets[layer][sprite.path] = [sprite];
}
profile.endframe();
*/
profile.frame("sorting");
var sprite_buckets = component.sprite_buckets();
var buckets = Object.entries(sprite_buckets).sort((a,b) => {
var na = Number(a[0]);
var nb = Number(b[0]);
if (na < nb) return -1;
return 1;
});
profile.endframe();
profile.frame("drawing");
render.use_shader(spritessboshader);
for (var layer of buckets) {
for (var img of Object.values(layer[1])) {
var sparray = Object.values(img);
if (sparray.length === 0) continue;
var ss = sparray[0];
render.use_mat(ss);
render.make_sprite_ssbo(sparray, sprite_ssbo);
render.draw(shape.quad, sprite_ssbo, sparray.length);
}
}
profile.endframe();
}
render.circle = function render_circle(pos, radius, color) {
flush();
var mat = { var mat = {
radius: radius, radius: radius,
coord: pos, coord: pos,
@ -478,24 +552,71 @@ render.circle = function(pos, radius, color) {
render.draw(shape.quad); render.draw(shape.quad);
} }
render.poly = function(points, color, transform) { render.poly = function render_poly(points, color, transform) {
var buffer = render.poly_prim(points); var buffer = render.poly_prim(points);
var mat = { shade: color}; var mat = { shade: color};
render.use_shader(polyshader); render.use_shader(polyshader);
render.set_model(transform); set_model(transform);
render.use_mat(mat); render.use_mat(mat);
render.draw(buffer); render.draw(buffer);
} }
render.line = function(points, color = Color.white, thickness = 1, transform) { var nextflush = undefined;
var buffer = os.make_line_prim(points, thickness, 0, false); function flush()
render.use_shader(polyshader); {
var mat = { nextflush?.();
shade: color nextflush = undefined;
}
function check_flush(flush_fn)
{
if (!nextflush)
nextflush = flush_fn;
else if (nextflush !== flush_fn) {
nextflush();
nextflush = flush_fn;
}
}
var poly_cache = [];
var poly_idx = 0;
var poly_ssbo;
function poly_e()
{
var e;
poly_idx++;
if (poly_idx > poly_cache.length) {
e = {
transform:os.make_transform(),
color: Color.white
}; };
render.use_mat(mat); poly_cache.push(e);
render.set_model(transform); return e;
render.draw(buffer); }
var e = poly_cache[poly_idx-1];
e.transform.unit();
return e;
}
function flush_poly()
{
if (poly_idx === 0) return;
render.use_shader(polyssboshader);
render.use_mat({});
render.make_particle_ssbo(poly_cache.slice(0,poly_idx), poly_ssbo);
render.draw(shape.centered_quad, poly_ssbo, poly_cache.length);
poly_idx = 0;
}
render.line = function render_line(points, color = Color.white, thickness = 1) {
var poly = poly_e();
var dist = vector.distance(points[0],points[1]);
poly.transform.move(vector.midpoint(points[0],points[1]));
poly.transform.rotate([0,0,-1], vector.angle([points[1].x-points[0].x, points[1].y-points[0].y]));
poly.transform.scale = [dist, thickness, 1];
poly.color = color;
check_flush(flush_poly);
} }
/* All draw in screen space */ /* All draw in screen space */
@ -503,7 +624,7 @@ render.point = function(pos,size,color = Color.blue) {
render.circle(pos,size,size,color); render.circle(pos,size,size,color);
}; };
render.cross = function(pos, size, color = Color.red, thickness = 1) { render.cross = function render_cross(pos, size, color = Color.red, thickness = 1) {
var a = [ var a = [
pos.add([0,size]), pos.add([0,size]),
pos.add([0,-size]) pos.add([0,-size])
@ -516,7 +637,7 @@ render.cross = function(pos, size, color = Color.red, thickness = 1) {
render.line(b,color,thickness); render.line(b,color,thickness);
}; };
render.arrow = function(start, end, color = Color.red, wingspan = 4, wingangle = 10) { render.arrow = function render_arrow(start, end, color = Color.red, wingspan = 4, wingangle = 10) {
var dir = end.sub(start).normalized(); var dir = end.sub(start).normalized();
var wing1 = [ var wing1 = [
Vector.rotate(dir, wingangle).scale(wingspan).add(end), Vector.rotate(dir, wingangle).scale(wingspan).add(end),
@ -531,31 +652,34 @@ render.arrow = function(start, end, color = Color.red, wingspan = 4, wingangle =
render.line(wing2,color); render.line(wing2,color);
}; };
render.coordinate = function(pos, size, color) { render.coordinate = function render_coordinate(pos, size, color) {
render.text(JSON.stringify(pos.map(p=>Math.round(p))), pos, size, color); render.text(JSON.stringify(pos.map(p=>Math.round(p))), pos, size, color);
render.point(pos, 2, color); render.point(pos, 2, color);
} }
render.boundingbox = function(bb, color = Color.white) { render.boundingbox = function render_boundingbox(bb, color = Color.white) {
render.line(bbox.topoints(bb).wrapped(1), color); render.line(bbox.topoints(bb).wrapped(1), color);
} }
render.rectangle = function(lowerleft, upperright, color) { render.rectangle = function render_rectangle(lowerleft, upperright, color) {
var points = [lowerleft, lowerleft.add([upperright.x-lowerleft.x,0]), upperright, lowerleft.add([0,upperright.y-lowerleft.y])]; var transform = os.make_transform();
render.poly(points, color); var wh = [upperright.x-lowerleft.x, upperright.y-lowerleft.y];
var poly = poly_e();
poly.transform.move(vector.midpoint(lowerleft,upperright));
poly.transform.scale = [wh.x,wh.y,1];
poly.color = color;
check_flush(flush_poly);
}; };
render.box = function(pos, wh, color = Color.white) { render.box = function render_box(pos, wh, color = Color.white) {
var lower = pos.sub(wh.scale(0.5)); var poly = poly_e();
var upper = pos.add(wh.scale(0.5)); poly.transform.move(pos);
render.rectangle(lower,upper,color); poly.transform.scale = [wh.x,wh.y,1];
poly.color = color;
check_flush(flush_poly);
}; };
render.window = function(pos, wh, color) { render.window = function render_window(pos, wh, color) { render.box(pos.add(wh.scale(0.5)),wh,color); };
var p = pos.slice();
p = p.add(wh.scale(0.5));
render.box(p,wh,color);
};
render.text_bb = function(str, size = 1, wrap = -1, pos = [0,0]) render.text_bb = function(str, size = 1, wrap = -1, pos = [0,0])
{ {
@ -573,6 +697,7 @@ render.text_bb = function(str, size = 1, wrap = -1, pos = [0,0])
render.text = function(str, pos, size = 1, color = Color.white, wrap = -1, anchor = [0,1], cursor = -1) { render.text = function(str, pos, size = 1, color = Color.white, wrap = -1, anchor = [0,1], cursor = -1) {
var bb = render.text_bb(str, size, wrap, pos); var bb = render.text_bb(str, size, wrap, pos);
gui.text(str, pos, size, color, wrap, cursor); gui.text(str, pos, size, color, wrap, cursor);
check_flush(render.flush_text);
return bb; return bb;
p.x -= w * anchor.x; p.x -= w * anchor.x;
@ -586,11 +711,12 @@ render.text = function(str, pos, size = 1, color = Color.white, wrap = -1, ancho
}; };
render.image = function(tex, pos, scale = [tex.width, tex.height], rotation = 0, color = Color.white) { render.image = function(tex, pos, scale = [tex.width, tex.height], rotation = 0, color = Color.white) {
flush();
var t = os.make_transform(); var t = os.make_transform();
t.pos = pos; t.pos = pos;
t.scale = [scale.x/tex.width,scale.y/tex.height,1]; t.scale = [scale.x/tex.width,scale.y/tex.height,1];
render.use_shader(render.spriteshader); render.use_shader(render.spriteshader);
render.set_model(t); set_model(t);
render.use_mat({ render.use_mat({
shade: color, shade: color,
diffuse: tex, diffuse: tex,
@ -621,7 +747,7 @@ render.slice9 = function(tex, pos, bb, scale = [tex.width,tex.height], color = C
border = [bb.l/tex.width, bb.b/tex.height, bb.r/tex.width, bb.t/tex.height]; border = [bb.l/tex.width, bb.b/tex.height, bb.r/tex.width, bb.t/tex.height];
render.use_shader(slice9shader); render.use_shader(slice9shader);
render.set_model(t); set_model(t);
render.use_mat({ render.use_mat({
shade: color, shade: color,
diffuse:tex, diffuse:tex,
@ -633,27 +759,43 @@ render.slice9 = function(tex, pos, bb, scale = [tex.width,tex.height], color = C
render.draw(shape.quad); render.draw(shape.quad);
} }
var textssbo = render.text_ssbo(); render.emitter = function(emit)
{
var amt = Object.values(emit.particles).length;
if (amt === 0) return;
render.use_shader(parshader);
render.use_mat(emit);
var ts = [];
for (var p of Object.values(emit.particles)) ts.push([p.transform,p.color]);
render.make_particle_ssbo(ts, emit.ssbo);
render.draw(shape.quad, emit.ssbo, amt);
}
var textssbo;
render.flush_text = function() render.flush_text = function()
{ {
if (!render.textshader) return; if (!render.textshader) return;
var amt = render.flushtext(textssbo);
render.use_shader(render.textshader); render.use_shader(render.textshader);
render.use_mat({text:render.font.texture}); render.use_mat({text:render.font.texture});
render.draw(shape.quad, textssbo, render.flushtext()); if (amt === 0) return;
render.draw(shape.quad, textssbo, amt);
} }
render.fontcache = {}; var fontcache = {};
render.set_font = function(path, size) { render.set_font = function(path, size) {
var fontstr = `${path}-${size}`; var fontstr = `${path}-${size}`;
if (render.font && render.fontcache[fontstr] === render.font) return; if (render.font && fontcache[fontstr] === render.font) return;
if (!render.fontcache[fontstr]) render.fontcache[fontstr] = os.make_font(path, size); if (!fontcache[fontstr]) fontcache[fontstr] = os.make_font(path, size);
render.flush_text(); render.flush_text();
gui.font_set(render.fontcache[fontstr]); gui.font_set(fontcache[fontstr]);
render.font = render.fontcache[fontstr]; render.font = fontcache[fontstr];
} }
render.doc = "Draw shapes in screen space."; render.doc = "Draw shapes in screen space.";
@ -662,10 +804,204 @@ render.cross.doc = "Draw a cross centered at pos, with arm length size.";
render.arrow.doc = "Draw an arrow from start to end, with wings of length wingspan at angle wingangle."; render.arrow.doc = "Draw an arrow from start to end, with wings of length wingspan at angle wingangle.";
render.rectangle.doc = "Draw a rectangle, with its corners at lowerleft and upperright."; render.rectangle.doc = "Draw a rectangle, with its corners at lowerleft and upperright.";
render.draw = function(mesh, ssbo, inst = 1) render.draw = function render_draw(mesh, ssbo, inst = 1)
{ {
render.sg_bind(mesh, ssbo); sg_bind(mesh, ssbo);
profile.frame("gpu");
render.spdraw(cur.bind.count, inst); render.spdraw(cur.bind.count, inst);
profile.endframe();
} }
// Returns an array in the form of [left, bottom, right, top] in pixels of the camera to render to
// Camera viewport is [left,bottom,right,top] in relative values
function camviewport()
{
var aspect = (this.viewport[2]-this.viewport[0])/(this.viewport[3]-this.viewport[1])*window.size.x/window.size.y;
var raspect = this.size.x/this.size.y;
var left = this.viewport[0]*window.size.x;
var bottom = this.viewport[1]*window.size.y;
var usemode = this.mode;
if (this.break && this.size.x > window.size.x && this.size.y > window.size.y)
usemode = this.break;
if (usemode === "fit")
if (raspect < aspect) usemode = "height";
else usemode = "width";
switch(usemode) {
case "stretch":
case "expand":
return [0, 0, window.size.x, window.size.y];
case "keep":
return [left, bottom, left+this.size.x, bottom+this.size.y];
case "height":
var ret = [left, 0, this.size.x*(window.size.y/this.size.y), window.size.y];
ret[0] = (window.size.x-(ret[2]-ret[0]))/2;
return ret;
case "width":
var ret = [0, bottom, window.size.x, this.size.y*(window.size.x/this.size.x)];
ret[1] = (window.size.y-(ret[3]-ret[1]))/2;
return ret;
}
return [0, 0, window.size.x, window.size.y];
}
// pos is pixels on the screen, lower left[0,0]
function camscreen2world(pos)
{
var view = this.screen2cam(pos);
view.x *= this.size.x;
view.y *= this.size.y;
view = view.sub([this.size.x/2, this.size.y/2]);
view = view.add(this.pos.xy);
return view;
}
camscreen2world.doc = "Convert a view position for a camera to world."
function screen2cam(pos)
{
var viewport = this.view();
var width = viewport[2]-viewport[0];
var height = viewport[3]-viewport[1];
var left = pos.x-viewport[0];
var bottom = pos.y-viewport[1];
var p = [left/width, bottom/height];
return p;
}
screen2cam.doc = "Convert a screen space position in pixels to a normalized viewport position in a camera."
prosperon.make_camera = function()
{
var cam = world.spawn();
cam.near = 0.1;
cam.far = 1000;
cam.ortho = true;
cam.viewport = [0,0,1,1];
cam.size = window.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.mousepos = function() { return this.screen2world(input.mouse.screenpos()); }
cam.view = camviewport;
cam.offscreen = false;
return cam;
}
var screencolor;
prosperon.render = function()
{
profile.frame("world");
render.set_camera(prosperon.camera);
profile.frame("sprites");
render.sprites();
profile.endframe();
profile.frame("draws");
prosperon.draw();
profile.endframe();
prosperon.hudcam.size = prosperon.camera.size;
prosperon.hudcam.transform.pos = [prosperon.hudcam.size.x/2, prosperon.hudcam.size.y/2, -100];
render.set_camera(prosperon.hudcam);
profile.endframe();
profile.frame("hud");
prosperon.hud();
render.flush_text();
render.end_pass();
profile.endframe();
profile.frame("post process");
/* draw the image of the game world first */
render.glue_pass();
render.viewport(...prosperon.camera.view());
render.use_shader(render.postshader);
render.use_mat({diffuse:prosperon.screencolor});
render.draw(shape.quad);
profile.endframe();
profile.frame("app");
// Flush & render
prosperon.appcam.transform.pos = [window.size.x/2, window.size.y/2, -100];
prosperon.appcam.size = window.size.slice();
if (os.sys() !== 'macos')
prosperon.appcam.size.y *= -1;
render.set_camera(prosperon.appcam);
render.viewport(...prosperon.appcam.view());
// Call gui functions
mum.style = mum.dbg_style;
prosperon.gui();
if (mum.drawinput) mum.drawinput();
prosperon.gui_dbg();
render.flush_text();
mum.style = mum.base;
profile.endframe();
profile.frame("imgui");
render.imgui_new(window.size.x, window.size.y, 0.01);
prosperon.imgui();
render.imgui_end();
profile.endframe();
render.end_pass();
render.commit();
}
prosperon.process = function process() {
profile.frame("frame");
var dt = profile.secs(profile.now()) - frame_t;
frame_t = profile.secs(profile.now());
profile.frame("app update");
prosperon.appupdate(dt);
profile.endframe();
profile.frame("input");
input.procdown();
profile.endframe();
if (sim.mode === "play" || sim.mode === "step") {
profile.frame("update");
prosperon.update(dt * game.timescale);
profile.endframe();
if (sim.mode === "step") sim.pause();
profile.frame("physics");
physlag += dt;
while (physlag > physics.delta) {
physlag -= physics.delta;
prosperon.phys2d_step(physics.delta * game.timescale);
prosperon.physupdate(physics.delta * game.timescale);
}
profile.endframe();
}
profile.frame("render");
prosperon.window_render(window.size);
prosperon.render();
profile.endframe();
profile.endframe();
}
return {render}; return {render};

View file

@ -14,8 +14,10 @@ if (os.sys() === 'macos') {
appy.inputs['S-g'] = os.gc; appy.inputs['S-g'] = os.gc;
} }
appy.inputs.f12 = function() { mum.debug = !mum.debug; } //appy.inputs.f12 = function() { mum.debug = !mum.debug; }
appy.inputs.f12 = function() { profile.cpu_frame(); }
appy.inputs.f11 = window.toggle_fullscreen; appy.inputs.f11 = window.toggle_fullscreen;
appy.inputs.f10 = function() { profile.toggle_frame_avg(); }
appy.inputs['M-f4'] = prosperon.quit; appy.inputs['M-f4'] = prosperon.quit;
player[0].control(appy); player[0].control(appy);

View file

@ -103,19 +103,22 @@ Ease.elastic = {
Ease.elastic.c4 = 2*Math.PI/3; Ease.elastic.c4 = 2*Math.PI/3;
Ease.elastic.c5 = 2*Math.PI / 4.5; Ease.elastic.c5 = 2*Math.PI / 4.5;
var tween = function(obj, val, to, time) var tween = function(from, to, time, fn)
{ {
var start = profile.secs(profile.now()); var start = profile.secs(profile.now());
var startval = obj[val];
var update = function(dt) { var update = function(dt) {
profile.frame("tween");
var elapsed = profile.secs(profile.now()) - start; var elapsed = profile.secs(profile.now()) - start;
obj[val] = startval.lerp(to, elapsed/time); fn(from.lerp(to,elapsed/time));
if (elapsed >= time) { if (elapsed >= time) {
obj[val] = to; fn(to);
if (stop.then) stop.then();
stop(); stop();
} }
profile.endframe();
}; };
var stop = Register.update.register(update); var stop = Register.update.register(update);
return stop;
} }
var Tween = { var Tween = {

View file

@ -171,7 +171,7 @@ var listpanel = Object.copy(inputpanel, {
keycb() { keycb() {
if(this.value) if(this.value)
this.assets = this.allassets.filter(x => x.startswith(this.value)); this.assets = this.allassets.filter(x => x.startsWith(this.value));
else else
this.assets = this.allassets.slice(); this.assets = this.allassets.slice();
for (var m in this.mumlist) for (var m in this.mumlist)

View file

@ -1,7 +1,7 @@
#blend mix #blend mix
#depth off
#primitive triangle #primitive triangle
#cull none #cull none
#depth off
@vs vs @vs vs
in vec3 a_pos; in vec3 a_pos;

49
shaders/baseparticle.cg Normal file
View file

@ -0,0 +1,49 @@
#depth off
#blend mix
@vs vs
in vec2 a_pos;
in vec2 a_uv;
uniform vec2 diffuse_size;
struct particle {
mat4 model;
vec4 color;
};
readonly buffer ssbo {
particle par[];
};
out vec2 uv;
out vec4 color0;
uniform mat4 vp;
void main()
{
particle p = par[gl_InstanceIndex];
gl_Position = vp * p.model * vec4(a_pos, 0.0, 1.0);
uv = a_uv;
color0 = p.color;
}
@end
@fs fs
in vec2 uv;
in vec4 color0;
out vec4 color;
texture2D diffuse;
sampler smp;
void main()
{
color = texture(sampler2D(diffuse,smp),uv);
color *= color0;
}
@end
@program text vs fs

View file

@ -1,5 +1,5 @@
#blend mix
#depth off #depth off
#blend mix
#primitive triangle #primitive triangle
#cull none #cull none

37
shaders/poly_ssbo.cg Normal file
View file

@ -0,0 +1,37 @@
#depth off
#blend mix
#primitive triangle
#cull none
@vs vs
in vec3 a_pos;
uniform mat4 vp;
out vec4 shade;
struct poly {
mat4 model;
vec4 color;
};
readonly buffer ssbo {
poly polys[];
};
void main() {
poly p = polys[gl_InstanceIndex];
gl_Position = vp * p.model * vec4(a_pos, 1);
shade = p.color;
}
@end
@fs fs
in vec4 shade;
out vec4 color;
void main() {
color = shade;
}
@end
@program sprite vs fs

View file

@ -13,7 +13,6 @@ uniform vec4 shade;
void frag() void frag()
{ {
color = texture(sampler2D(diffuse,smp), uv); color = texture(sampler2D(diffuse,smp), uv);
if (color.a < 0.1) discard;
color *= shade; color *= shade;
} }
@end @end

60
shaders/sprite_ssbo.cg Normal file
View file

@ -0,0 +1,60 @@
#blend mix
#depth off
#primitive triangle
#cull none
@vs vs
in vec3 a_pos;
in vec2 a_uv;
out vec2 uv;
vec3 pos;
struct sprite {
mat4 model;
vec4 rect;
};
readonly buffer ssbo {
sprite sprites[];
};
uniform mat4 projection;
uniform mat4 view;
uniform mat4 vp;
uniform vec2 diffuse_size;
void main()
{
sprite s = sprites[gl_InstanceIndex];
pos = a_pos;
uv = a_uv;
pos *= vec3(diffuse_size * s.rect.zw,1);
uv = (uv*s.rect.zw)+s.rect.xy;
gl_Position = vp * s.model * vec4(pos, 1.0);
}
@end
@fs fs
in vec2 uv;
out vec4 color;
texture2D diffuse;
sampler smp;
uniform vec4 shade;
void frag()
{
color = texture(sampler2D(diffuse,smp), uv);
color *= shade;
}
void main()
{
frag();
}
@end
@program sprite vs fs

View file

@ -3,6 +3,7 @@
const HMM_Vec2 v2zero = {0,0}; const HMM_Vec2 v2zero = {0,0};
const HMM_Vec2 v2one = {1,1}; const HMM_Vec2 v2one = {1,1};
const HMM_Vec3 v3zero = {0,0,0}; const HMM_Vec3 v3zero = {0,0,0};
const HMM_Vec3 v3one = {1,1,1};
const HMM_Vec4 v4zero = {0,0,0,0}; const HMM_Vec4 v4zero = {0,0,0,0};
const HMM_Vec3 vX = {1.0,0.0,0.0}; const HMM_Vec3 vX = {1.0,0.0,0.0};
@ -17,7 +18,7 @@ const HMM_Vec3 vLEFT = {-1,0,0};
const HMM_Vec3 vRIGHT = {1,0,0}; const HMM_Vec3 vRIGHT = {1,0,0};
const HMM_Mat4 MAT1 = {1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1}; const HMM_Mat4 MAT1 = {1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1};
const HMM_Quat QUAT1 = {1,0,0,0}; const HMM_Quat QUAT1 = {0,0,0,1};
/* /*
* Angle unit conversion functions * Angle unit conversion functions
@ -267,11 +268,6 @@ HMM_Vec4 HMM_SubV4(HMM_Vec4 Left, HMM_Vec4 Right) {
return HMM_V2(v.X*s, v.Y*s); return HMM_V2(v.X*s, v.Y*s);
} }
HMM_Vec3 HMM_ScaleV3(HMM_Vec3 v, double s)
{
return HMM_V3(v.x*s,v.y*s,v.z*s);
}
HMM_Vec2 HMM_MulV2(HMM_Vec2 Left, HMM_Vec2 Right) { HMM_Vec2 HMM_MulV2(HMM_Vec2 Left, HMM_Vec2 Right) {
HMM_Vec2 Result; HMM_Vec2 Result;
@ -529,7 +525,6 @@ float HMM_AngleV4(HMM_Vec4 a, HMM_Vec4 b)
} }
HMM_Vec2 HMM_NormV2(HMM_Vec2 A) { HMM_Vec2 HMM_NormV2(HMM_Vec2 A) {
// HMM_MulV2F(A, 1.0/HMM_LenV2(A)+FLOAT_MIN);
return HMM_MulV2F(A, HMM_InvSqrtF(HMM_DotV2(A, A))); return HMM_MulV2F(A, HMM_InvSqrtF(HMM_DotV2(A, A)));
} }
@ -1769,11 +1764,6 @@ HMM_Mat4 HMM_QToM4(HMM_Quat Left) {
HMM_Mat4 HMM_M4TRS(HMM_Vec3 t, HMM_Quat q, HMM_Vec3 s) HMM_Mat4 HMM_M4TRS(HMM_Vec3 t, HMM_Quat q, HMM_Vec3 s)
{ {
HMM_Mat4 T = HMM_Translate(t);
HMM_Mat4 R = HMM_QToM4(q);
HMM_Mat4 S = HMM_Scale(s);
return HMM_MulM4(T, HMM_MulM4(R, S));
//return HMM_MulM4(T,S);
HMM_Mat4 l; HMM_Mat4 l;
float *lm = (float*)&l; float *lm = (float*)&l;

View file

@ -416,6 +416,7 @@ typedef union HMM_Mat4 {
extern const HMM_Vec2 v2zero; extern const HMM_Vec2 v2zero;
extern const HMM_Vec2 v2one; extern const HMM_Vec2 v2one;
extern const HMM_Vec3 v3zero; extern const HMM_Vec3 v3zero;
extern const HMM_Vec3 v3one;
extern const HMM_Vec4 v4zero; extern const HMM_Vec4 v4zero;
typedef signed int HMM_Bool; typedef signed int HMM_Bool;
@ -477,7 +478,6 @@ HMM_Vec4 HMM_AddV4(HMM_Vec4 Left, HMM_Vec4 Right);
HMM_Vec2 HMM_SubV2(HMM_Vec2 Left, HMM_Vec2 Right); HMM_Vec2 HMM_SubV2(HMM_Vec2 Left, HMM_Vec2 Right);
HMM_Vec3 HMM_SubV3(HMM_Vec3 Left, HMM_Vec3 Right); HMM_Vec3 HMM_SubV3(HMM_Vec3 Left, HMM_Vec3 Right);
HMM_Vec4 HMM_SubV4(HMM_Vec4 Left, HMM_Vec4 Right); HMM_Vec4 HMM_SubV4(HMM_Vec4 Left, HMM_Vec4 Right);
HMM_Vec3 HMM_ScaleV3(HMM_Vec3 v, double s);
HMM_Vec2 HMM_MulV2(HMM_Vec2 Left, HMM_Vec2 Right); HMM_Vec2 HMM_MulV2(HMM_Vec2 Left, HMM_Vec2 Right);
HMM_Vec2 HMM_MulV2F(HMM_Vec2 Left, float Right); HMM_Vec2 HMM_MulV2F(HMM_Vec2 Left, float Right);
HMM_Vec3 HMM_MulV3(HMM_Vec3 Left, HMM_Vec3 Right); HMM_Vec3 HMM_MulV3(HMM_Vec3 Left, HMM_Vec3 Right);

View file

@ -20,8 +20,6 @@
struct sFont *use_font; struct sFont *use_font;
sg_buffer text_ssbo;
struct text_vert { struct text_vert {
HMM_Vec2 pos; HMM_Vec2 pos;
HMM_Vec2 wh; HMM_Vec2 wh;
@ -32,15 +30,6 @@ struct text_vert {
static struct text_vert *text_buffer; static struct text_vert *text_buffer;
void font_init() {
text_ssbo = sg_make_buffer(&(sg_buffer_desc){
.size = sizeof(struct text_vert),
.type = SG_BUFFERTYPE_STORAGEBUFFER,
.usage = SG_USAGE_STREAM,
.label = "text buffer"
});
}
void font_free(font *f) void font_free(font *f)
{ {
sg_destroy_image(f->texID); sg_destroy_image(f->texID);
@ -115,7 +104,6 @@ struct sFont *MakeFont(const char *fontfile, int height) {
newfont->descent = descent*emscale; newfont->descent = descent*emscale;
newfont->linegap = linegap*emscale; newfont->linegap = linegap*emscale;
newfont->linegap = ((newfont->ascent - newfont->descent) - newfont->linegap); newfont->linegap = ((newfont->ascent - newfont->descent) - newfont->linegap);
printf("newfont : %g, %g, %g\n", newfont->ascent, newfont->descent, newfont->linegap);
newfont->texture = malloc(sizeof(texture)); newfont->texture = malloc(sizeof(texture));
newfont->texture->id = sg_make_image(&(sg_image_desc){ newfont->texture->id = sg_make_image(&(sg_image_desc){
@ -177,15 +165,15 @@ void draw_char_box(struct Character c, HMM_Vec2 cursor, float scale, struct rgba
b.y = cursor.Y + wh.y/2; b.y = cursor.Y + wh.y/2;
} }
int text_flush() { int text_flush(sg_buffer *buf) {
if (arrlen(text_buffer) == 0) return 0; if (arrlen(text_buffer) == 0) return 0;
sg_range verts; sg_range verts;
verts.ptr = text_buffer; verts.ptr = text_buffer;
verts.size = sizeof(struct text_vert) * arrlen(text_buffer); verts.size = sizeof(struct text_vert) * arrlen(text_buffer);
if (sg_query_buffer_will_overflow(text_ssbo, verts.size)) { if (sg_query_buffer_will_overflow(*buf, verts.size)) {
sg_destroy_buffer(text_ssbo); sg_destroy_buffer(*buf);
text_ssbo = sg_make_buffer(&(sg_buffer_desc){ *buf = sg_make_buffer(&(sg_buffer_desc){
.size = verts.size, .size = verts.size,
.type = SG_BUFFERTYPE_STORAGEBUFFER, .type = SG_BUFFERTYPE_STORAGEBUFFER,
.usage = SG_USAGE_STREAM, .usage = SG_USAGE_STREAM,
@ -193,7 +181,7 @@ int text_flush() {
}); });
} }
sg_append_buffer(text_ssbo, &verts); sg_append_buffer(*buf, &verts);
int n = arrlen(text_buffer); int n = arrlen(text_buffer);
arrsetlen(text_buffer, 0); arrsetlen(text_buffer, 0);
return n; return n;

View file

@ -34,7 +34,6 @@ typedef struct sFont font;
void font_free(font *f); void font_free(font *f);
void font_init();
struct sFont *MakeFont(const char *fontfile, int height); struct sFont *MakeFont(const char *fontfile, int height);
void font_set(font *f); void font_set(font *f);
void sdrawCharacter(struct Character c, HMM_Vec2 cursor, float scale, struct rgba color); void sdrawCharacter(struct Character c, HMM_Vec2 cursor, float scale, struct rgba color);
@ -42,6 +41,6 @@ void text_settype(struct sFont *font);
struct boundingbox text_bb(const char *text, float scale, float lw, float tracking); struct boundingbox text_bb(const char *text, float scale, float lw, float tracking);
int renderText(const char *text, HMM_Vec2 pos, float scale, struct rgba color, float lw, int caret, float tracking); int renderText(const char *text, HMM_Vec2 pos, float scale, struct rgba color, float lw, int caret, float tracking);
int text_flush(); int text_flush(sg_buffer *buf);
#endif #endif

View file

@ -3,6 +3,7 @@
#include "render.h" #include "render.h"
#include "sokol/sokol_app.h" #include "sokol/sokol_app.h"
#include "imgui.h" #include "imgui.h"
#include "implot.h"
#define SOKOL_IMPL #define SOKOL_IMPL
#include "sokol/util/sokol_imgui.h" #include "sokol/util/sokol_imgui.h"
#include "sokol/util/sokol_gfx_imgui.h" #include "sokol/util/sokol_gfx_imgui.h"
@ -37,6 +38,22 @@ JSC_CCALL(imgui_menuitem,
free(hotkey); free(hotkey);
) )
JSC_SCALL(imgui_startplot,
ImPlot::SetNextAxisToFit(ImAxis_X1);
ImPlot::SetNextAxisToFit(ImAxis_Y1);
ImPlot::BeginPlot(str);
)
JSC_CCALL(imgui_endplot, ImPlot::EndPlot() )
JSC_SCALL(imgui_lineplot,
double data[js_arrlen(argv[1])];
for (int i = 0; i < js_arrlen(argv[1]); i++)
data[i] = js2number(js_getpropidx(argv[1], i));
ImPlot::PlotLine(str, data, js_arrlen(argv[1]));
)
JSC_CCALL(imgui_beginmenubar, ImGui::BeginMenuBar()) JSC_CCALL(imgui_beginmenubar, ImGui::BeginMenuBar())
JSC_CCALL(imgui_endmenubar, ImGui::EndMenuBar()) JSC_CCALL(imgui_endmenubar, ImGui::EndMenuBar())
@ -55,6 +72,9 @@ static const JSCFunctionListEntry js_imgui_funcs[] = {
MIST_FUNC_DEF(imgui, beginmenubar, 0), MIST_FUNC_DEF(imgui, beginmenubar, 0),
MIST_FUNC_DEF(imgui, endmenubar, 0), MIST_FUNC_DEF(imgui, endmenubar, 0),
MIST_FUNC_DEF(imgui, textinput, 2), MIST_FUNC_DEF(imgui, textinput, 2),
MIST_FUNC_DEF(imgui, startplot,1),
MIST_FUNC_DEF(imgui,endplot,0),
MIST_FUNC_DEF(imgui,lineplot,2)
}; };
static int started = 0; static int started = 0;
@ -67,6 +87,8 @@ JSValue gui_init(JSContext *js)
sgimgui_desc_t desc = {0}; sgimgui_desc_t desc = {0};
sgimgui_init(&sgimgui, &desc); sgimgui_init(&sgimgui, &desc);
ImPlot::CreateContext();
JSValue imgui = JS_NewObject(js); JSValue imgui = JS_NewObject(js);
JS_SetPropertyFunctionList(js, imgui, js_imgui_funcs, countof(js_imgui_funcs)); JS_SetPropertyFunctionList(js, imgui, js_imgui_funcs, countof(js_imgui_funcs));
started = 1; started = 1;

View file

@ -15,7 +15,6 @@
#include "window.h" #include "window.h"
#include "spline.h" #include "spline.h"
#include "yugine.h" #include "yugine.h"
#include "particle.h"
#include <assert.h> #include <assert.h>
#include "resources.h" #include "resources.h"
#include <sokol/sokol_time.h> #include <sokol/sokol_time.h>
@ -35,6 +34,7 @@
#include "sokol_glue.h" #include "sokol_glue.h"
#include <chipmunk/chipmunk_unsafe.h> #include <chipmunk/chipmunk_unsafe.h>
#include "gui.h" #include "gui.h"
#include "timer.h"
#if (defined(_WIN32) || defined(__WIN32__)) #if (defined(_WIN32) || defined(__WIN32__))
#include <direct.h> #include <direct.h>
@ -96,7 +96,6 @@ void cpConstraint_free(cpConstraint *c)
void jsfreestr(const char *s) { JS_FreeCString(js, s); } void jsfreestr(const char *s) { JS_FreeCString(js, s); }
QJSCLASS(gameobject) QJSCLASS(gameobject)
QJSCLASS(transform) QJSCLASS(transform)
QJSCLASS(emitter)
QJSCLASS(dsp_node) QJSCLASS(dsp_node)
QJSCLASS(texture) QJSCLASS(texture)
QJSCLASS(font) QJSCLASS(font)
@ -107,6 +106,7 @@ QJSCLASS(sg_buffer)
QJSCLASS(datastream) QJSCLASS(datastream)
QJSCLASS(cpShape) QJSCLASS(cpShape)
QJSCLASS(cpConstraint) QJSCLASS(cpConstraint)
QJSCLASS(timer)
static JSValue js_circle2d; static JSValue js_circle2d;
static JSValue js_poly2d; static JSValue js_poly2d;
@ -661,32 +661,20 @@ sg_bindings js2bind(JSValue v)
return bind; return bind;
} }
JSC_GETSET(emitter, life, number) JSC_CCALL(render_flushtext,
JSC_GETSET(emitter, life_var, number) sg_buffer *buf = js2sg_buffer(argv[0]);
JSC_GETSET(emitter, speed, number) int amt = text_flush(buf);
JSC_GETSET(emitter, variation, number) return number2js(amt);
JSC_GETSET(emitter, divergence, number)
JSC_GETSET(emitter, scale, number)
JSC_GETSET(emitter, scale_var, number)
JSC_GETSET(emitter, grow_for, number)
JSC_GETSET(emitter, shrink_for, number)
JSC_GETSET(emitter, max, number)
JSC_GETSET(emitter, explosiveness, number)
JSC_GETSET(emitter, bounce, number)
JSC_GETSET(emitter, collision_mask, bitmask)
JSC_GETSET(emitter, die_after_collision, boolean)
JSC_GETSET(emitter, persist, number)
JSC_GETSET(emitter, persist_var, number)
JSC_GETSET(emitter, warp_mask, bitmask)
JSC_CCALL(emitter_emit, emitter_emit(js2emitter(self), js2number(argv[0]), js2transform(argv[1])))
JSC_CCALL(emitter_step, emitter_step(js2emitter(self), js2number(argv[0]), js2transform(argv[1])))
JSC_CCALL(emitter_draw,
emitter_draw(js2emitter(self));
return number2js(arrlen(js2emitter(self)->verts));
) )
JSC_CCALL(render_flushtext, JSC_CCALL(render_make_textssbo,
return number2js(text_flush()); sg_buffer *b = malloc(sizeof(*b));
*b = sg_make_buffer(&(sg_buffer_desc){
.type = SG_BUFFERTYPE_STORAGEBUFFER,
.size = 4,
.usage = SG_USAGE_STREAM
});
return sg_buffer2js(b);
) )
JSC_CCALL(render_glue_pass, JSC_CCALL(render_glue_pass,
@ -934,15 +922,15 @@ JSC_CCALL(render_setunim4,
JSValue arr = argv[2]; JSValue arr = argv[2];
int n = js_arrlen(arr); int n = js_arrlen(arr);
if (n == 1) if (n == 1)
m = transform2mat(*js2transform(js_getpropidx(arr,0))); m = transform2mat(js2transform(js_getpropidx(arr,0)));
else { else {
for (int i = 0; i < n; i++) { for (int i = 0; i < n; i++) {
HMM_Mat4 p = transform2mat(*js2transform(js_getpropidx(arr, i))); HMM_Mat4 p = transform2mat(js2transform(js_getpropidx(arr, i)));
m = HMM_MulM4(p,m); m = HMM_MulM4(p,m);
} }
} }
} else if (!JS_IsUndefined(argv[2])) } else if (!JS_IsUndefined(argv[2]))
m = transform2mat(*js2transform(argv[2])); m = transform2mat(js2transform(argv[2]));
sg_apply_uniforms(js2number(argv[0]), js2number(argv[1]), SG_RANGE_REF(m.e)); sg_apply_uniforms(js2number(argv[0]), js2number(argv[1]), SG_RANGE_REF(m.e));
); );
@ -952,24 +940,101 @@ JSC_CCALL(render_setbind,
sg_apply_bindings(&bind); sg_apply_bindings(&bind);
) )
JSC_CCALL(render_make_t_ssbo, typedef struct particle_ss {
JSValue array = argv[0]; HMM_Mat4 model;
HMM_Mat4 ms[js_arrlen(array)]; HMM_Vec4 color;
for (int i = 0; i < js_arrlen(array); i++) } particle_ss;
ms[i] = transform2mat(*js2transform(js_getpropidx(array, i)));
sg_buffer *rr = malloc(sizeof(sg_buffer)); JSC_CCALL(render_make_particle_ssbo,
*rr = sg_make_buffer(&(sg_buffer_desc){ JSValue array = argv[0];
.data = { size_t size = js_arrlen(array)*(sizeof(particle_ss));
.ptr = ms, sg_buffer *b = js2sg_buffer(argv[1]);
.size = sizeof(HMM_Mat4)*js_arrlen(array), if (!b) return JS_UNDEFINED;
},
particle_ss ms[js_arrlen(array)];
if (sg_query_buffer_will_overflow(*b, size)) {
sg_destroy_buffer(*b);
*b = sg_make_buffer(&(sg_buffer_desc){
.type = SG_BUFFERTYPE_STORAGEBUFFER, .type = SG_BUFFERTYPE_STORAGEBUFFER,
.usage = SG_USAGE_IMMUTABLE, .size = size,
.usage = SG_USAGE_STREAM,
.label = "transform buffer" .label = "transform buffer"
}); });
}
return sg_buffer2js(rr); for (int i = 0; i < js_arrlen(array); i++) {
JSValue sub = js_getpropidx(array,i);
ms[i].model = transform2mat(js2transform(js_getpropstr(sub, "transform")));
ms[i].color = js2vec4(js_getpropstr(sub,"color"));
}
sg_append_buffer(*b, (&(sg_range){
.ptr = ms,
.size = size
}));
)
typedef struct sprite_ss {
HMM_Mat4 model;
HMM_Vec4 rect;
} sprite_ss;
JSC_CCALL(render_make_sprite_ssbo,
JSValue array = argv[0];
size_t size = js_arrlen(array)*(sizeof(sprite_ss));
sg_buffer *b = js2sg_buffer(argv[1]);
if (!b) return JS_UNDEFINED;
sprite_ss ms[js_arrlen(array)];
if (sg_query_buffer_will_overflow(*b, size)) {
sg_destroy_buffer(*b);
*b = sg_make_buffer(&(sg_buffer_desc){
.type = SG_BUFFERTYPE_STORAGEBUFFER,
.size = size,
.usage = SG_USAGE_STREAM,
.label = "transform buffer"
});
}
for (int i = 0; i < js_arrlen(array); i++) {
JSValue sub = js_getpropidx(array,i);
ms[i].model = transform2mat(js2transform(js_getpropstr(sub, "transform")));
ms[i].rect = js2vec4(js_getpropstr(sub,"rect"));
}
sg_append_buffer(*b, (&(sg_range){
.ptr = ms,
.size = size
}));
)
JSC_CCALL(render_make_t_ssbo,
JSValue array = argv[0];
size_t size = js_arrlen(array)*sizeof(HMM_Mat4);
sg_buffer *b = js2sg_buffer(argv[1]);
if (!b) return JS_UNDEFINED;
HMM_Mat4 ms[js_arrlen(array)];
if (sg_query_buffer_will_overflow(*b, size)) {
sg_destroy_buffer(*b);
*b = sg_make_buffer(&(sg_buffer_desc){
.type = SG_BUFFERTYPE_STORAGEBUFFER,
.size = size,
.usage = SG_USAGE_STREAM,
.label = "transform buffer"
});
}
for (int i = 0; i < js_arrlen(array); i++)
ms[i] = transform2mat(js2transform(js_getpropidx(array, i)));
sg_append_buffer(*b, (&(sg_range){
.ptr = ms,
.size = size
}));
) )
JSC_CCALL(render_spdraw, JSC_CCALL(render_spdraw,
@ -982,7 +1047,6 @@ JSC_CCALL(render_setpipeline,
sg_apply_pipeline(p); sg_apply_pipeline(p);
) )
JSC_CCALL(render_text_ssbo, return sg_buffer2js(&text_ssbo))
JSC_CCALL(render_screencolor, JSC_CCALL(render_screencolor,
texture *t = calloc(sizeof(*t), 1); texture *t = calloc(sizeof(*t), 1);
t->id = screencolor; t->id = screencolor;
@ -996,14 +1060,14 @@ JSC_CCALL(render_imgui_end, gui_endframe())
JSC_CCALL(render_imgui_init, return gui_init(js)) JSC_CCALL(render_imgui_init, return gui_init(js))
static const JSCFunctionListEntry js_render_funcs[] = { static const JSCFunctionListEntry js_render_funcs[] = {
MIST_FUNC_DEF(render, flushtext, 0), MIST_FUNC_DEF(render, flushtext, 1),
MIST_FUNC_DEF(render, camera_screen2world, 2), MIST_FUNC_DEF(render, camera_screen2world, 2),
MIST_FUNC_DEF(render, make_textssbo, 0),
MIST_FUNC_DEF(render, viewport, 4), MIST_FUNC_DEF(render, viewport, 4),
MIST_FUNC_DEF(render, end_pass, 0), MIST_FUNC_DEF(render, end_pass, 0),
MIST_FUNC_DEF(render, commit, 0), MIST_FUNC_DEF(render, commit, 0),
MIST_FUNC_DEF(render, glue_pass, 0), MIST_FUNC_DEF(render, glue_pass, 0),
MIST_FUNC_DEF(render, text_size, 3), MIST_FUNC_DEF(render, text_size, 3),
MIST_FUNC_DEF(render, text_ssbo, 0),
MIST_FUNC_DEF(render, set_camera, 1), MIST_FUNC_DEF(render, set_camera, 1),
MIST_FUNC_DEF(render, pipeline, 1), MIST_FUNC_DEF(render, pipeline, 1),
MIST_FUNC_DEF(render, setuniv3, 2), MIST_FUNC_DEF(render, setuniv3, 2),
@ -1022,7 +1086,9 @@ static const JSCFunctionListEntry js_render_funcs[] = {
MIST_FUNC_DEF(render, gfx_gui, 0), MIST_FUNC_DEF(render, gfx_gui, 0),
MIST_FUNC_DEF(render, imgui_end, 0), MIST_FUNC_DEF(render, imgui_end, 0),
MIST_FUNC_DEF(render, imgui_init, 0), MIST_FUNC_DEF(render, imgui_init, 0),
MIST_FUNC_DEF(render, make_t_ssbo, 1) MIST_FUNC_DEF(render, make_t_ssbo, 2),
MIST_FUNC_DEF(render, make_particle_ssbo, 2),
MIST_FUNC_DEF(render, make_sprite_ssbo, 2)
}; };
JSC_CCALL(gui_scissor, sg_apply_scissor_rect(js2number(argv[0]), js2number(argv[1]), js2number(argv[2]), js2number(argv[3]), 0)) JSC_CCALL(gui_scissor, sg_apply_scissor_rect(js2number(argv[0]), js2number(argv[1]), js2number(argv[2]), js2number(argv[3]), 0))
@ -1078,6 +1144,25 @@ JSValue js_vector_dot(JSContext *js, JSValue self, int argc, JSValue *argv) { re
JSC_CCALL(vector_project, return vec22js(HMM_ProjV2(js2vec2(argv[0]), js2vec2(argv[1])))) JSC_CCALL(vector_project, return vec22js(HMM_ProjV2(js2vec2(argv[0]), js2vec2(argv[1]))))
JSC_CCALL(vector_midpoint,
HMM_Vec2 a = js2vec2(argv[0]);
HMM_Vec2 b = js2vec2(argv[1]);
// HMM_Vec2 c = HMM_AddV2(a,b);
// c = HMM_DivV2F(c, 2);
return vec22js((HMM_Vec2){(a.x+b.x)/2, (a.y+b.y)/2});
)
JSC_CCALL(vector_distance,
HMM_Vec2 a = js2vec2(argv[0]);
HMM_Vec2 b = js2vec2(argv[1]);
return number2js(HMM_DistV2(a,b));
)
JSC_CCALL(vector_angle,
HMM_Vec2 a = js2vec2(argv[0]);
return angle2js(atan2(a.y,a.x));
)
/* Given a series of points p, computes a new series with them expanded on either side by d */ /* Given a series of points p, computes a new series with them expanded on either side by d */
HMM_Vec2 *inflatepoints(HMM_Vec2 *p, float d, int n) HMM_Vec2 *inflatepoints(HMM_Vec2 *p, float d, int n)
{ {
@ -1130,10 +1215,261 @@ JSC_CCALL(vector_inflate,
arrfree(p); arrfree(p);
) )
JSC_CCALL(vector_rotate,
HMM_Vec2 vec = js2vec2(argv[0]);
float r = HMM_LenV2(vec);
double angle = js2angle(argv[1]);
angle += atan2(vec.y,vec.x);
vec.x = r*cos(angle);
vec.y = r*sin(angle);
return vec22js(vec);
)
JSC_CCALL(vector_add,
HMM_Vec4 a = js2vec4(argv[0]);
HMM_Vec4 b = js2vec4(argv[1]);
HMM_Vec4 c = HMM_AddV4(a,b);
return vec42js(c);
)
JSC_CCALL(vector_norm,
int len = js_arrlen(argv[0]);
switch(len) {
case 2: return vec22js(HMM_NormV2(js2vec2(argv[0])));
case 3: return vec32js(HMM_NormV3(js2vec3(argv[0])));
case 4: return vec42js(HMM_NormV4(js2vec4(argv[0])));
}
return argv[0];
)
JSC_CCALL(vector_angle_between,
int len = js_arrlen(argv[0]);
switch(len) {
case 2: return angle2js(HMM_AngleV2(js2vec2(argv[0]), js2vec2(argv[1])));
case 3: return angle2js(HMM_AngleV3(js2vec3(argv[0]), js2vec3(argv[1])));
case 4: return angle2js(HMM_AngleV4(js2vec4(argv[0]), js2vec4(argv[1])));
}
return angle2js(0);
)
JSC_CCALL(vector_lerp,
double s = js2number(argv[0]);
double f = js2number(argv[1]);
double t = js2number(argv[2]);
return number2js((f-s)*t+s);
)
int gcd(int a, int b) {
if (b == 0)
return a;
return gcd(b, a % b);
}
JSC_CCALL(vector_gcd,
return number2js(gcd(js2number(argv[0]), js2number(argv[1])));
)
JSC_CCALL(vector_lcm,
double a = js2number(argv[0]);
double b = js2number(argv[1]);
return number2js((a*b)/gcd(a,b));
)
JSC_CCALL(vector_clamp,
double x = js2number(argv[0]);
double l = js2number(argv[1]);
double h = js2number(argv[2]);
return number2js(x > h ? h : x < l ? l : x);
)
JSC_SSCALL(vector_trimchr,
int len = js2number(js_getpropstr(argv[0], "length"));
char *start = str;
while (*start == *str2)
start++;
char *end = str + len-1;
while(*end == *str2)
end--;
ret = JS_NewStringLen(js, start, end-start+1);
)
JSC_CCALL(vector_angledist,
double a1 = js2number(argv[0]);
double a2 = js2number(argv[1]);
a1 = fmod(a1,1);
a2 = fmod(a2,1);
double dist = a2-a1;
if (dist == 0) return number2js(dist);
if (dist > 0) {
if (dist > 0.5) return number2js(dist-1);
return number2js(dist);
}
if (dist < -0.5) return number2js(dist+1);
return number2js(dist);
)
double r2()
{
return (double)rand() / (double)RAND_MAX ;
}
double rand_range(double min, double max) {
return r2() * (max-min) + min;
}
JSC_CCALL(vector_variate,
double n = js2number(argv[0]);
double pct = js2number(argv[1]);
return number2js(n + (rand_range(-pct,pct)*n));
)
JSC_CCALL(vector_random_range, return number2js(rand_range(js2number(argv[0]), js2number(argv[1]))))
JSC_CCALL(vector_mean,
double len = js_arrlen(argv[0]);
double sum;
for (int i = 0; i < len; i++)
sum += js2number(js_getpropidx(argv[0], i));
return number2js(sum/len);
)
JSC_CCALL(vector_sum,
double sum;
int len = js_arrlen(argv[0]);
for (int i = 0; i < len; i++)
sum += js2number(js_getpropidx(argv[0], i));
return number2js(sum);
)
JSC_CCALL(vector_sigma,
int len = js_arrlen(argv[0]);
double sum;
for (int i = 0; i < len; i++)
sum += js2number(js_getpropidx(argv[0], i));
double mean = sum/(double)len;
double sq_diff = 0;
for (int i = 0; i < len; i++) {
double x = js2number(js_getpropidx(argv[0],i));
sq_diff += pow(x-mean, 2);
}
double variance = sq_diff/((double)len);
return number2js(sqrt(variance));
)
JSC_CCALL(vector_median,
int len = js_arrlen(argv[0]);
double arr[len];
double temp;
for (int i = 0; i < len; i++)
arr[i] = js2number(js_getpropidx(argv[0], i));
for (int i = 0; i < len-1; i++) {
for (int j = i+1; j < len; j++) {
if (arr[i] > arr[j]) {
temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
}
}
}
if (len % 2 == 0) return number2js((arr[len/2-1] + arr[len/2])/2.0);
return number2js(arr[len/2]);
)
static const JSCFunctionListEntry js_vector_funcs[] = { static const JSCFunctionListEntry js_vector_funcs[] = {
MIST_FUNC_DEF(vector, dot,2), MIST_FUNC_DEF(vector, dot,2),
MIST_FUNC_DEF(vector, project,2), MIST_FUNC_DEF(vector, project,2),
MIST_FUNC_DEF(vector, inflate, 2) MIST_FUNC_DEF(vector, inflate, 2),
MIST_FUNC_DEF(vector, rotate, 2),
MIST_FUNC_DEF(vector, add, 2),
MIST_FUNC_DEF(vector, midpoint, 2),
MIST_FUNC_DEF(vector, distance, 2),
MIST_FUNC_DEF(vector, angle, 1),
MIST_FUNC_DEF(vector, norm, 1),
MIST_FUNC_DEF(vector, angle_between, 2),
MIST_FUNC_DEF(vector, lerp, 3),
MIST_FUNC_DEF(vector, gcd, 2),
MIST_FUNC_DEF(vector, lcm, 2),
MIST_FUNC_DEF(vector, clamp, 3),
MIST_FUNC_DEF(vector, trimchr, 2),
MIST_FUNC_DEF(vector, angledist, 2),
MIST_FUNC_DEF(vector, variate, 2),
MIST_FUNC_DEF(vector, random_range, 2),
MIST_FUNC_DEF(vector, mean, 1),
MIST_FUNC_DEF(vector, sum, 1),
MIST_FUNC_DEF(vector, sigma, 1),
MIST_FUNC_DEF(vector, median, 1)
};
#define JS_HMM_FN(OP, HMM, SIGN) \
JSC_CCALL(array_##OP, \
int len = js_arrlen(self); \
if (!JS_IsArray(js, argv[0])) { \
double n = js2number(argv[0]); \
JSValue arr = JS_NewArray(js); \
for (int i = 0; i < len; i++) \
js_setprop_num(arr, i, number2js(js2number(js_getpropidx(self,i)) SIGN n)); \
return arr; \
} \
switch(len) { \
case 2: \
return vec22js(HMM_##HMM##V2(js2vec2(self), js2vec2(argv[0]))); \
case 3: \
return vec32js(HMM_##HMM##V3(js2vec3(self), js2vec3(argv[0]))); \
case 4: \
return vec42js(HMM_##HMM##V4(js2vec4(self), js2vec4(argv[0]))); \
} \
\
JSValue arr = JS_NewArray(js); \
for (int i = 0; i < len; i++) { \
double a = js2number(js_getpropidx(self,i)); \
double b = js2number(js_getpropidx(argv[0],i)); \
js_setprop_num(arr, i, number2js(a SIGN b)); \
} \
return arr; \
) \
JS_HMM_FN(add, Add, +)
JS_HMM_FN(sub, Sub, -)
JS_HMM_FN(div, Div, /)
JS_HMM_FN(scale, Mul, *)
JSC_CCALL(array_lerp,
int len = js_arrlen(self);
JSValue arr = JS_NewArray(js);
double t = js2number(argv[1]);
for (int i = 0; i < len; i++) {
double from = js2number(js_getpropidx(self, i));
double to = js2number(js_getpropidx(argv[0], i));
js_setprop_num(arr, i, number2js((to - from) * t + from));
}
return arr;
)
static const JSCFunctionListEntry js_array_funcs[] = {
MIST_FUNC_DEF(array, add, 1),
MIST_FUNC_DEF(array, sub, 1),
MIST_FUNC_DEF(array, div,1),
MIST_FUNC_DEF(array, scale, 1),
MIST_FUNC_DEF(array, lerp, 2)
}; };
JSC_CCALL(game_engine_start, engine_start(argv[0],argv[1], js2number(argv[2]), js2number(argv[3]))) JSC_CCALL(game_engine_start, engine_start(argv[0],argv[1], js2number(argv[2]), js2number(argv[3])))
@ -1159,13 +1495,18 @@ static const JSCFunctionListEntry js_input_funcs[] = {
JSC_CCALL(prosperon_phys2d_step, phys2d_update(js2number(argv[0]))) JSC_CCALL(prosperon_phys2d_step, phys2d_update(js2number(argv[0])))
JSC_CCALL(prosperon_window_render, openglRender(js2vec2(argv[0]))) JSC_CCALL(prosperon_window_render, openglRender(js2vec2(argv[0])))
JSC_CCALL(prosperon_guid, JSC_CCALL(prosperon_guid,
uint8_t bytes[16]; int bits = 32;
for (int i = 0; i < 16; i++) bytes[i] = rand()%256; char guid[33];
char uuid[37]; for (int i = 0; i < 4; i++) {
snprintf(uuid, 37, "%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x", int r = rand();
bytes[0], bytes[1], bytes[2], bytes[3], bytes[4], bytes[5], bytes[6], bytes[7], for (int j = 0; j < 8; j++) {
bytes[8], bytes[9], bytes[10], bytes[11], bytes[12], bytes[13], bytes[14], bytes[15]); guid[i*8+j] = "0123456789abcdef"[r%16];
return str2js(uuid); r /= 16;
}
}
guid[32] = 0;
return str2js(guid);
) )
static const JSCFunctionListEntry js_prosperon_funcs[] = { static const JSCFunctionListEntry js_prosperon_funcs[] = {
@ -1235,8 +1576,32 @@ static const JSCFunctionListEntry js_audio_funcs[] = {
JSC_CCALL(profile_now, return number2js(stm_now())) JSC_CCALL(profile_now, return number2js(stm_now()))
static JSValue instr_v = JS_UNDEFINED;
int iiihandle(JSRuntime *rt, void *data)
{
script_call_sym(instr_v, 0, NULL);
return 0;
}
JSC_CCALL(profile_gather,
int count = js2number(argv[0]);
instr_v = JS_DupValue(js, argv[1]);
JS_SetInterruptHandler(rt, iiihandle, NULL);
)
JSC_CCALL(profile_gather_rate,
JS_SetInterruptRate(js2number(argv[0]));
)
JSC_CCALL(profile_gather_stop,
JS_SetInterruptHandler(rt,NULL,NULL);
)
static const JSCFunctionListEntry js_profile_funcs[] = { static const JSCFunctionListEntry js_profile_funcs[] = {
MIST_FUNC_DEF(profile,now,0), MIST_FUNC_DEF(profile,now,0),
MIST_FUNC_DEF(profile,gather,2),
MIST_FUNC_DEF(profile,gather_rate,1),
MIST_FUNC_DEF(profile,gather_stop,0),
}; };
JSC_SCALL(io_exists, ret = boolean2js(fexists(str))) JSC_SCALL(io_exists, ret = boolean2js(fexists(str)))
@ -1469,29 +1834,6 @@ static const JSCFunctionListEntry js_physics_funcs[] = {
CGETSET_ADD(physics, collision_persistence), CGETSET_ADD(physics, collision_persistence),
}; };
static const JSCFunctionListEntry js_emitter_funcs[] = {
CGETSET_ADD(emitter, life),
CGETSET_ADD(emitter, life_var),
CGETSET_ADD(emitter, speed),
CGETSET_ADD(emitter, variation),
CGETSET_ADD(emitter, divergence),
CGETSET_ADD(emitter, scale),
CGETSET_ADD(emitter, scale_var),
CGETSET_ADD(emitter, grow_for),
CGETSET_ADD(emitter, shrink_for),
CGETSET_ADD(emitter, max),
CGETSET_ADD(emitter, explosiveness),
CGETSET_ADD(emitter, bounce),
CGETSET_ADD(emitter, collision_mask),
CGETSET_ADD(emitter, die_after_collision),
CGETSET_ADD(emitter, persist),
CGETSET_ADD(emitter, persist_var),
CGETSET_ADD(emitter, warp_mask),
MIST_FUNC_DEF(emitter, emit, 1),
MIST_FUNC_DEF(emitter, step, 1),
MIST_FUNC_DEF(emitter, draw, 1)
};
JSC_GETSET(transform, pos, vec3) JSC_GETSET(transform, pos, vec3)
JSC_GETSET(transform, scale, vec3) JSC_GETSET(transform, scale, vec3)
JSC_GETSET(transform, rotation, quat) JSC_GETSET(transform, rotation, quat)
@ -1502,6 +1844,7 @@ JSC_CCALL(transform_lookat,
transform *go = js2transform(self); transform *go = js2transform(self);
HMM_Mat4 m = HMM_LookAt_LH(go->pos, point, vUP); HMM_Mat4 m = HMM_LookAt_LH(go->pos, point, vUP);
go->rotation = HMM_M4ToQ_LH(m); go->rotation = HMM_M4ToQ_LH(m);
go->dirty = true;
) )
JSC_CCALL(transform_rotate, JSC_CCALL(transform_rotate,
@ -1509,6 +1852,7 @@ JSC_CCALL(transform_rotate,
transform *t = js2transform(self); transform *t = js2transform(self);
HMM_Quat rot = HMM_QFromAxisAngle_LH(axis, js2angle(argv[1])); HMM_Quat rot = HMM_QFromAxisAngle_LH(axis, js2angle(argv[1]));
t->rotation = HMM_MulQ(t->rotation,rot); t->rotation = HMM_MulQ(t->rotation,rot);
t->dirty = true;
) )
JSC_CCALL(transform_angle, JSC_CCALL(transform_angle,
@ -1525,15 +1869,34 @@ JSC_CCALL(transform_direction,
return vec32js(HMM_QVRot(js2vec3(argv[0]), t->rotation)); return vec32js(HMM_QVRot(js2vec3(argv[0]), t->rotation));
) )
JSC_CCALL(transform_phys2d,
transform *t = js2transform(self);
HMM_Vec2 v = js2vec2(argv[0]);
float av = js2number(argv[1]);
float dt = js2number(argv[2]);
transform_move(t, (HMM_Vec3){v.x*dt,v.y*dt,0});
HMM_Quat rot = HMM_QFromAxisAngle_LH((HMM_Vec3){0,0,1}, av*dt);
t->rotation = HMM_MulQ(t->rotation, rot);
)
JSC_CCALL(transform_unit,
transform *t = js2transform(self);
t->pos = v3zero;
t->rotation = QUAT1;
t->scale = v3one;
)
static const JSCFunctionListEntry js_transform_funcs[] = { static const JSCFunctionListEntry js_transform_funcs[] = {
CGETSET_ADD(transform, pos), CGETSET_ADD(transform, pos),
CGETSET_ADD(transform, scale), CGETSET_ADD(transform, scale),
CGETSET_ADD(transform, rotation), CGETSET_ADD(transform, rotation),
MIST_FUNC_DEF(transform, phys2d, 3),
MIST_FUNC_DEF(transform, move, 1), MIST_FUNC_DEF(transform, move, 1),
MIST_FUNC_DEF(transform, rotate, 2), MIST_FUNC_DEF(transform, rotate, 2),
MIST_FUNC_DEF(transform, angle, 1), MIST_FUNC_DEF(transform, angle, 1),
MIST_FUNC_DEF(transform, lookat, 1), MIST_FUNC_DEF(transform, lookat, 1),
MIST_FUNC_DEF(transform, direction, 1), MIST_FUNC_DEF(transform, direction, 1),
MIST_FUNC_DEF(transform, unit, 0),
}; };
JSC_GETSET(dsp_node, pass, boolean) JSC_GETSET(dsp_node, pass, boolean)
@ -2036,6 +2399,9 @@ static const JSCFunctionListEntry js_texture_funcs[] = {
MIST_FUNC_DEF(texture, blit, 5) MIST_FUNC_DEF(texture, blit, 5)
}; };
static const JSCFunctionListEntry js_timer_funcs[] = {
};
JSC_GETSET(font, linegap, number) JSC_GETSET(font, linegap, number)
JSC_GET(font, height, number) JSC_GET(font, height, number)
@ -2380,11 +2746,6 @@ JSC_SCALL(os_make_model,
js_setpropstr(v, "material", material2js(me->primitives[0].mat)); js_setpropstr(v, "material", material2js(me->primitives[0].mat));
return v; return v;
) )
JSC_CCALL(os_make_emitter,
emitter *e = make_emitter();
ret = emitter2js(e);
js_setpropstr(ret, "buffer", sg_buffer2js(&e->buffer));
)
JSC_CCALL(os_make_buffer, JSC_CCALL(os_make_buffer,
int type = js2number(argv[1]); int type = js2number(argv[1]);
@ -2565,7 +2926,6 @@ static const JSCFunctionListEntry js_os_funcs[] = {
MIST_FUNC_DEF(os, make_font, 2), MIST_FUNC_DEF(os, make_font, 2),
MIST_FUNC_DEF(os, make_model, 1), MIST_FUNC_DEF(os, make_model, 1),
MIST_FUNC_DEF(os, make_transform, 0), MIST_FUNC_DEF(os, make_transform, 0),
MIST_FUNC_DEF(os, make_emitter, 0),
MIST_FUNC_DEF(os, make_buffer, 1), MIST_FUNC_DEF(os, make_buffer, 1),
MIST_FUNC_DEF(os, make_line_prim, 4), MIST_FUNC_DEF(os, make_line_prim, 4),
MIST_FUNC_DEF(os, make_cylinder, 2), MIST_FUNC_DEF(os, make_cylinder, 2),
@ -2598,7 +2958,6 @@ void ffi_load() {
QJSCLASSPREP_FUNCS(gameobject); QJSCLASSPREP_FUNCS(gameobject);
QJSCLASSPREP_FUNCS(transform); QJSCLASSPREP_FUNCS(transform);
QJSCLASSPREP_FUNCS(dsp_node); QJSCLASSPREP_FUNCS(dsp_node);
QJSCLASSPREP_FUNCS(emitter);
QJSCLASSPREP_FUNCS(warp_gravity); QJSCLASSPREP_FUNCS(warp_gravity);
QJSCLASSPREP_FUNCS(warp_damp); QJSCLASSPREP_FUNCS(warp_damp);
QJSCLASSPREP_FUNCS(texture); QJSCLASSPREP_FUNCS(texture);
@ -2607,6 +2966,7 @@ void ffi_load() {
QJSCLASSPREP_FUNCS(window); QJSCLASSPREP_FUNCS(window);
QJSCLASSPREP_FUNCS(datastream); QJSCLASSPREP_FUNCS(datastream);
QJSCLASSPREP_FUNCS(cpShape); QJSCLASSPREP_FUNCS(cpShape);
QJSCLASSPREP_FUNCS(timer);
QJSGLOBALCLASS(nota); QJSGLOBALCLASS(nota);
QJSGLOBALCLASS(input); QJSGLOBALCLASS(input);
@ -2659,6 +3019,10 @@ void ffi_load() {
JSSTATIC(damped_spring, cpConstraint_proto) JSSTATIC(damped_spring, cpConstraint_proto)
JSSTATIC(groove, cpConstraint_proto) JSSTATIC(groove, cpConstraint_proto)
JSValue array_proto = js_getpropstr(globalThis, "Array");
array_proto = js_getpropstr(array_proto, "prototype");
JS_SetPropertyFunctionList(js, array_proto, js_array_funcs, 4);
JS_FreeValue(js,globalThis); JS_FreeValue(js,globalThis);
} }

View file

@ -99,6 +99,7 @@ static JSClassDef js_##TYPE##_class = {\
.finalizer = js_##TYPE##_finalizer,\ .finalizer = js_##TYPE##_finalizer,\
};\ };\
TYPE *js2##TYPE (JSValue val) { \ TYPE *js2##TYPE (JSValue val) { \
if (JS_IsUndefined(val)) return NULL; \
assert(JS_GetClassID(val) == js_##TYPE##_id); \ assert(JS_GetClassID(val) == js_##TYPE##_id); \
return JS_GetOpaque(val,js_##TYPE##_id); \ return JS_GetOpaque(val,js_##TYPE##_id); \
}\ }\

View file

@ -508,7 +508,7 @@ sg_bindings primitive_bind(primitive *p)
void model_draw_go(model *model, transform *go) void model_draw_go(model *model, transform *go)
{ {
HMM_Mat4 gom = transform2mat(*go); HMM_Mat4 gom = transform2mat(go);
animation_run(&model->anim, apptime()); animation_run(&model->anim, apptime());

View file

@ -1,124 +0,0 @@
#include "particle.h"
#include "stb_ds.h"
#include "render.h"
#include "2dphysics.h"
#include "math.h"
#include "log.h"
emitter *make_emitter() {
emitter *e = calloc(sizeof(*e),1);
e->max = 20;
arrsetcap(e->particles, 10);
for (int i = 0; i < arrlen(e->particles); i++)
e->particles[i].life = 0;
e->life = 10;
e->tte = lerp(e->explosiveness, e->life/e->max, 0);
e->scale = 1;
e->speed = 20;
e->buffer = sg_make_buffer(&(sg_buffer_desc){
.size = sizeof(struct par_vert),
.type = SG_BUFFERTYPE_STORAGEBUFFER,
.usage = SG_USAGE_STREAM
});
return e;
}
void emitter_free(emitter *e)
{
arrfree(e->particles);
arrfree(e->verts);
free(e);
}
/* Variate a value around variance. Variance between 0 and 1. */
float variate(float val, float variance)
{
return val + val*(frand(variance)-(variance/2));
}
int emitter_spawn(emitter *e, transform *t)
{
if (arrlen(e->particles) == e->max) return 0;
particle p = {0};
p.life = e->life;
p.pos = (HMM_Vec4){t->pos.x,t->pos.y,t->pos.z,0};
HMM_Vec3 up = transform_direction(t, vFWD);
float newan = (frand(e->divergence)-(e->divergence/2))*HMM_TurnToRad;
HMM_Vec2 v2n = HMM_V2Rotate((HMM_Vec2){0,1}, newan);
HMM_Vec3 norm = (HMM_Vec3){v2n.x, v2n.y,0};
p.v = HMM_MulV4F((HMM_Vec4){norm.x,norm.y,norm.z,0}, variate(e->speed, e->variation));
p.angle = 0.25;
p.scale = variate(e->scale*t->scale.x, e->scale_var);
arrput(e->particles,p);
return 1;
}
void emitter_emit(emitter *e, int count, transform *t)
{
for (int i = 0; i < count; i++)
emitter_spawn(e, t);
}
void emitter_draw(emitter *e)
{
if (arrlen(e->particles) == 0) return;
arrsetlen(e->verts, arrlen(e->particles));
for (int i = 0; i < arrlen(e->particles); i++) {
if (e->particles[i].time >= e->particles[i].life) continue;
particle *p = e->particles+i;
e->verts[i].pos = p->pos.xy;
e->verts[i].angle = p->angle;
e->verts[i].scale = p->scale;
/* if (p->time < e->grow_for)
e->verts[i].scale = lerp(p->time/e->grow_for, 0, p->scale);
else if (p->time > (p->life - e->shrink_for))
e->verts[i].scale = lerp((p->time-(p->life-e->shrink_for))/e->shrink_for, p->scale, 0);*/
e->verts[i].color = p->color;
}
sg_range verts;
verts.ptr = e->verts;
verts.size = sizeof(*e->verts)*arrlen(e->verts);
if (sg_query_buffer_will_overflow(e->buffer, verts.size)) {
sg_destroy_buffer(e->buffer);
e->buffer = sg_make_buffer(&(sg_buffer_desc){
.size = verts.size,
.type = SG_BUFFERTYPE_STORAGEBUFFER,
.usage = SG_USAGE_STREAM
});
}
sg_append_buffer(e->buffer, &verts);
}
void emitter_step(emitter *e, double dt, transform *t) {
HMM_Vec4 g_accel = HMM_MulV4F((HMM_Vec4){cpSpaceGetGravity(space).x, cpSpaceGetGravity(space).y, 0, 0}, dt);
for (int i = 0; i < arrlen(e->particles); i++) {
if (e->particles[i].time >= e->particles[i].life) continue;
//if (e->warp_mask & gravmask)
// e->particles[i].v = HMM_AddV4(e->particles[i].v, g_accel);
e->particles[i].pos = HMM_AddV4(e->particles[i].pos, HMM_MulV4F(e->particles[i].v, dt));
e->particles[i].angle += e->particles[i].av*dt;
e->particles[i].time += dt;
e->particles[i].color = sample_sampler(&e->color, e->particles[i].time/e->particles[i].life);
e->particles[i].scale = e->scale;
if (e->particles[i].time >= e->particles[i].life)
arrdelswap(e->particles, i);
// else if (query_point(e->particles[i].pos.xy))
// arrdelswap(e->particles,i);
}
e->tte-=dt;
float step = lerp(e->explosiveness, e->life/e->max,0);
while (e->tte <= 0) {
e->tte += step;
if (!emitter_spawn(e, t)) break;
}
}

View file

@ -1,75 +0,0 @@
#ifndef PARTICLE_H
#define PARTICLE_H
#include "HandmadeMath.h"
#include "warp.h"
#include "transform.h"
#include "texture.h"
#include "anim.h"
#include "gameobject.h"
#include "render.h"
typedef struct particle {
HMM_Vec4 pos;
HMM_Vec4 v; /* velocity */
float angle;
float av; /* angular velocity */
float scale;
double time;
double life;
HMM_Vec4 color;
} particle;
#define SPRAY 0
#define CLOUD 1
#define MESH 2
typedef struct par_vert {
HMM_Vec2 pos;
float angle;
float scale;
HMM_Vec4 color;
} par_vert;
typedef struct emitter {
struct particle *particles;
par_vert *verts;
HMM_Vec3 *mesh; /* list of points to optionally spawn from */
HMM_Vec3 *norm; /* norm at each point */
int type; /* spray, cloud, or mesh */
float explosiveness; /* 0 for a stream, 1 for all at once. Range of values allowed. */
int max; /* number of particles */
double life; /* how long a particle lasts */
double life_var;
/* SPRAY PARTICLE GEN */
float speed; /* initial speed of particle */
float variation; /* variation on speed */
float divergence; /* angular degree of variation from emitter normal, up to 1 */
float tumble; /* amount of random rotation of particles */
float tumble_rate; /* tumble rotation */
sampler color; /* color over particle lifetime */
float scale;
float scale_var;
float grow_for; /* seconds to grow from small until scale */
float shrink_for; /* seconds to shrink to small prior to its death */
/* ROTATION AND COLLISION */
int collision_mask; /* mask for collision */
float bounce; /* bounce back after collision */
/* PARTICLE SPAWN */
int die_after_collision;
float persist; /* how long to linger after death */
float persist_var;
/* TRAILS */
warpmask warp_mask;
double tte; /* time to emit */
sg_buffer buffer;
} emitter;
emitter *make_emitter();
void emitter_free(emitter *e);
void emitter_emit(emitter *e, int count, transform *t);
void emitter_step(emitter *e, double dt, transform *t);
void emitter_draw(emitter *e);
#endif

View file

@ -5,7 +5,6 @@
#include "font.h" #include "font.h"
#include "gameobject.h" #include "gameobject.h"
#include "log.h" #include "log.h"
#include "particle.h"
#include "window.h" #include "window.h"
#include "model.h" #include "model.h"
#include "stb_ds.h" #include "stb_ds.h"
@ -149,8 +148,6 @@ void render_init() {
sg_trace_hooks hh = sg_install_trace_hooks(&hooks); sg_trace_hooks hh = sg_install_trace_hooks(&hooks);
#endif #endif
font_init();
sg_features feat = sg_query_features(); sg_features feat = sg_query_features();
TOPLEFT = feat.origin_top_left; TOPLEFT = feat.origin_top_left;

View file

@ -3,6 +3,7 @@
#include "jsffi.h" #include "jsffi.h"
#include "stb_ds.h" #include "stb_ds.h"
#include "resources.h" #include "resources.h"
#include <sokol/sokol_time.h>
#include <stdarg.h> #include <stdarg.h>

View file

@ -9,6 +9,7 @@ extern "C" {
#include <time.h> #include <time.h>
extern JSContext *js; extern JSContext *js;
extern JSRuntime *rt;
struct phys_cbs { struct phys_cbs {
JSValue begin; JSValue begin;

5885
source/engine/thirdparty/imgui/implot.cpp vendored Normal file

File diff suppressed because it is too large Load diff

1297
source/engine/thirdparty/imgui/implot.h vendored Normal file

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -51,6 +51,12 @@
#define container_of(ptr, type, member) ((type *)((uint8_t *)(ptr) - offsetof(type, member))) #define container_of(ptr, type, member) ((type *)((uint8_t *)(ptr) - offsetof(type, member)))
#endif #endif
#if !defined(_MSC_VER) && defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L
#define minimum_length(n) static n
#else
#define minimum_length(n) n
#endif
typedef int BOOL; typedef int BOOL;
#ifndef FALSE #ifndef FALSE

View file

@ -136,6 +136,7 @@ static inline slimb_t ceil_div(slimb_t a, slimb_t b)
return a / b; return a / b;
} }
#ifdef USE_BF_DEC
/* b must be >= 1 */ /* b must be >= 1 */
static inline slimb_t floor_div(slimb_t a, slimb_t b) static inline slimb_t floor_div(slimb_t a, slimb_t b)
{ {
@ -145,6 +146,7 @@ static inline slimb_t floor_div(slimb_t a, slimb_t b)
return (a - b + 1) / b; return (a - b + 1) / b;
} }
} }
#endif
/* return r = a modulo b (0 <= r <= b - 1. b must be >= 1 */ /* return r = a modulo b (0 <= r <= b - 1. b must be >= 1 */
static inline limb_t smod(slimb_t a, slimb_t b) static inline limb_t smod(slimb_t a, slimb_t b)

View file

@ -30,6 +30,7 @@
#include "cutils.h" #include "cutils.h"
#include "libregexp.h" #include "libregexp.h"
#include "libunicode.h"
/* /*
TODO: TODO:
@ -141,32 +142,6 @@ static const uint16_t char_range_s[] = {
0xFEFF, 0xFEFF + 1, 0xFEFF, 0xFEFF + 1,
}; };
BOOL lre_is_space(int c)
{
int i, n, low, high;
n = (countof(char_range_s) - 1) / 2;
for(i = 0; i < n; i++) {
low = char_range_s[2 * i + 1];
if (c < low)
return FALSE;
high = char_range_s[2 * i + 2];
if (c < high)
return TRUE;
}
return FALSE;
}
uint32_t const lre_id_start_table_ascii[4] = {
/* $ A-Z _ a-z */
0x00000000, 0x00000010, 0x87FFFFFE, 0x07FFFFFE
};
uint32_t const lre_id_continue_table_ascii[4] = {
/* $ 0-9 A-Z _ a-z */
0x00000000, 0x03FF0010, 0x87FFFFFE, 0x07FFFFFE
};
static const uint16_t char_range_w[] = { static const uint16_t char_range_w[] = {
4, 4,
0x0030, 0x0039 + 1, 0x0030, 0x0039 + 1,
@ -186,7 +161,7 @@ typedef enum {
CHAR_RANGE_W, CHAR_RANGE_W,
} CharRangeEnum; } CharRangeEnum;
static const uint16_t *char_range_table[] = { static const uint16_t * const char_range_table[] = {
char_range_d, char_range_d,
char_range_s, char_range_s,
char_range_w, char_range_w,
@ -1513,15 +1488,13 @@ static int re_parse_term(REParseState *s, BOOL is_backward_dir)
if (dbuf_error(&s->byte_code)) if (dbuf_error(&s->byte_code))
goto out_of_memory; goto out_of_memory;
}
/* the spec tells that if there is no advance when /* the spec tells that if there is no advance when
running the atom after the first quant_min times, running the atom after the first quant_min times,
then there is no match. We remove this test when we then there is no match. We remove this test when we
are sure the atom always advances the position. */ are sure the atom always advances the position. */
add_zero_advance_check = re_need_check_advance(s->byte_code.buf + last_atom_start, add_zero_advance_check = re_need_check_advance(s->byte_code.buf + last_atom_start,
s->byte_code.size - last_atom_start); s->byte_code.size - last_atom_start);
} else {
add_zero_advance_check = FALSE;
}
{ {
int len, pos; int len, pos;

View file

@ -25,10 +25,7 @@
#define LIBREGEXP_H #define LIBREGEXP_H
#include <stddef.h> #include <stddef.h>
#include <stdint.h>
#include "libunicode.h"
#define LRE_BOOL int /* for documentation purposes */
#define LRE_FLAG_GLOBAL (1 << 0) #define LRE_FLAG_GLOBAL (1 << 0)
#define LRE_FLAG_IGNORECASE (1 << 1) #define LRE_FLAG_IGNORECASE (1 << 1)
@ -50,43 +47,9 @@ int lre_exec(uint8_t **capture,
int cbuf_type, void *opaque); int cbuf_type, void *opaque);
int lre_parse_escape(const uint8_t **pp, int allow_utf16); int lre_parse_escape(const uint8_t **pp, int allow_utf16);
LRE_BOOL lre_is_space(int c);
/* must be provided by the user */ /* must be provided by the user, return non zero if overflow */
LRE_BOOL lre_check_stack_overflow(void *opaque, size_t alloca_size); int lre_check_stack_overflow(void *opaque, size_t alloca_size);
void *lre_realloc(void *opaque, void *ptr, size_t size); void *lre_realloc(void *opaque, void *ptr, size_t size);
/* JS identifier test */
extern uint32_t const lre_id_start_table_ascii[4];
extern uint32_t const lre_id_continue_table_ascii[4];
static inline int lre_js_is_ident_first(int c)
{
if ((uint32_t)c < 128) {
return (lre_id_start_table_ascii[c >> 5] >> (c & 31)) & 1;
} else {
#ifdef CONFIG_ALL_UNICODE
return lre_is_id_start(c);
#else
return !lre_is_space(c);
#endif
}
}
static inline int lre_js_is_ident_next(int c)
{
if ((uint32_t)c < 128) {
return (lre_id_continue_table_ascii[c >> 5] >> (c & 31)) & 1;
} else {
/* ZWNJ and ZWJ are accepted in identifiers */
#ifdef CONFIG_ALL_UNICODE
return lre_is_id_continue(c) || c == 0x200C || c == 0x200D;
#else
return !lre_is_space(c) || c == 0x200C || c == 0x200D;
#endif
}
}
#undef LRE_BOOL
#endif /* LIBREGEXP_H */ #endif /* LIBREGEXP_H */

View file

@ -189,9 +189,13 @@ static const uint8_t unicode_prop_Cased1_table[196] = {
}; };
static const uint8_t unicode_prop_Cased1_index[21] = { static const uint8_t unicode_prop_Cased1_index[21] = {
0xb9, 0x02, 0xe0, 0xc0, 0x1d, 0x20, 0xe5, 0x2c, 0xb9, 0x02, 0xe0, // 002B9 at 39
0x20, 0xb1, 0x07, 0x21, 0xc1, 0xd6, 0x21, 0x4a, 0xc0, 0x1d, 0x20, // 01DC0 at 65
0xf1, 0x01, 0x8a, 0xf1, 0x01, 0xe5, 0x2c, 0x20, // 02CE5 at 97
0xb1, 0x07, 0x21, // 107B1 at 129
0xc1, 0xd6, 0x21, // 1D6C1 at 161
0x4a, 0xf1, 0x01, // 1F14A at 192
0x8a, 0xf1, 0x01, // 1F18A at 224 (upper bound)
}; };
static const uint8_t unicode_prop_Case_Ignorable_table[737] = { static const uint8_t unicode_prop_Case_Ignorable_table[737] = {
@ -291,15 +295,29 @@ static const uint8_t unicode_prop_Case_Ignorable_table[737] = {
}; };
static const uint8_t unicode_prop_Case_Ignorable_index[69] = { static const uint8_t unicode_prop_Case_Ignorable_index[69] = {
0xbe, 0x05, 0x00, 0xfe, 0x07, 0x00, 0x52, 0x0a, 0xbe, 0x05, 0x00, // 005BE at 32
0xa0, 0xc1, 0x0b, 0x00, 0x82, 0x0d, 0x00, 0x3f, 0xfe, 0x07, 0x00, // 007FE at 64
0x10, 0x80, 0xd4, 0x17, 0x40, 0xcf, 0x1a, 0x20, 0x52, 0x0a, 0xa0, // 00A52 at 101
0xf5, 0x1c, 0x00, 0x80, 0x20, 0x00, 0x16, 0xa0, 0xc1, 0x0b, 0x00, // 00BC1 at 128
0x00, 0xc6, 0xa8, 0x00, 0xc2, 0xaa, 0x60, 0x56, 0x82, 0x0d, 0x00, // 00D82 at 160
0xfe, 0x20, 0xb1, 0x07, 0x01, 0x75, 0x10, 0x01, 0x3f, 0x10, 0x80, // 0103F at 196
0xeb, 0x12, 0x21, 0x41, 0x16, 0x01, 0x5c, 0x1a, 0xd4, 0x17, 0x40, // 017D4 at 226
0x01, 0x43, 0x1f, 0x01, 0x2e, 0xcf, 0x41, 0x25, 0xcf, 0x1a, 0x20, // 01ACF at 257
0xe0, 0x01, 0xf0, 0x01, 0x0e, 0xf5, 0x1c, 0x00, // 01CF5 at 288
0x80, 0x20, 0x00, // 02080 at 320
0x16, 0xa0, 0x00, // 0A016 at 352
0xc6, 0xa8, 0x00, // 0A8C6 at 384
0xc2, 0xaa, 0x60, // 0AAC2 at 419
0x56, 0xfe, 0x20, // 0FE56 at 449
0xb1, 0x07, 0x01, // 107B1 at 480
0x75, 0x10, 0x01, // 11075 at 512
0xeb, 0x12, 0x21, // 112EB at 545
0x41, 0x16, 0x01, // 11641 at 576
0x5c, 0x1a, 0x01, // 11A5C at 608
0x43, 0x1f, 0x01, // 11F43 at 640
0x2e, 0xcf, 0x41, // 1CF2E at 674
0x25, 0xe0, 0x01, // 1E025 at 704
0xf0, 0x01, 0x0e, // E01F0 at 736 (upper bound)
}; };
static const uint8_t unicode_prop_ID_Start_table[1100] = { static const uint8_t unicode_prop_ID_Start_table[1100] = {
@ -444,20 +462,41 @@ static const uint8_t unicode_prop_ID_Start_table[1100] = {
}; };
static const uint8_t unicode_prop_ID_Start_index[105] = { static const uint8_t unicode_prop_ID_Start_index[105] = {
0xf6, 0x03, 0x20, 0xa6, 0x07, 0x00, 0xa9, 0x09, 0xf6, 0x03, 0x20, // 003F6 at 33
0x20, 0xb1, 0x0a, 0x00, 0xba, 0x0b, 0x20, 0x3b, 0xa6, 0x07, 0x00, // 007A6 at 64
0x0d, 0x20, 0xc7, 0x0e, 0x20, 0x49, 0x12, 0x00, 0xa9, 0x09, 0x20, // 009A9 at 97
0x9b, 0x16, 0x00, 0xac, 0x19, 0x00, 0xc0, 0x1d, 0xb1, 0x0a, 0x00, // 00AB1 at 128
0x80, 0x80, 0x20, 0x20, 0x70, 0x2d, 0x00, 0x00, 0xba, 0x0b, 0x20, // 00BBA at 161
0x32, 0x00, 0xda, 0xa7, 0x00, 0x4c, 0xaa, 0x20, 0x3b, 0x0d, 0x20, // 00D3B at 193
0xc7, 0xd7, 0x20, 0xfc, 0xfd, 0x20, 0x9d, 0x02, 0xc7, 0x0e, 0x20, // 00EC7 at 225
0x21, 0x96, 0x05, 0x01, 0xf3, 0x08, 0x01, 0xb3, 0x49, 0x12, 0x00, // 01249 at 256
0x0c, 0x21, 0x73, 0x11, 0x61, 0x34, 0x13, 0x01, 0x9b, 0x16, 0x00, // 0169B at 288
0x1b, 0x17, 0x21, 0x8a, 0x1a, 0x01, 0x34, 0x1f, 0xac, 0x19, 0x00, // 019AC at 320
0x21, 0xbf, 0x6a, 0x01, 0x23, 0xb1, 0xa1, 0xad, 0xc0, 0x1d, 0x80, // 01DC0 at 356
0xd4, 0x01, 0x6f, 0xd7, 0x01, 0xff, 0xe7, 0x61, 0x80, 0x20, 0x20, // 02080 at 385
0x5e, 0xee, 0x01, 0xe1, 0xeb, 0x22, 0xb0, 0x23, 0x70, 0x2d, 0x00, // 02D70 at 416
0x03, 0x00, 0x32, 0x00, // 03200 at 448
0xda, 0xa7, 0x00, // 0A7DA at 480
0x4c, 0xaa, 0x20, // 0AA4C at 513
0xc7, 0xd7, 0x20, // 0D7C7 at 545
0xfc, 0xfd, 0x20, // 0FDFC at 577
0x9d, 0x02, 0x21, // 1029D at 609
0x96, 0x05, 0x01, // 10596 at 640
0xf3, 0x08, 0x01, // 108F3 at 672
0xb3, 0x0c, 0x21, // 10CB3 at 705
0x73, 0x11, 0x61, // 11173 at 739
0x34, 0x13, 0x01, // 11334 at 768
0x1b, 0x17, 0x21, // 1171B at 801
0x8a, 0x1a, 0x01, // 11A8A at 832
0x34, 0x1f, 0x21, // 11F34 at 865
0xbf, 0x6a, 0x01, // 16ABF at 896
0x23, 0xb1, 0xa1, // 1B123 at 933
0xad, 0xd4, 0x01, // 1D4AD at 960
0x6f, 0xd7, 0x01, // 1D76F at 992
0xff, 0xe7, 0x61, // 1E7FF at 1027
0x5e, 0xee, 0x01, // 1EE5E at 1056
0xe1, 0xeb, 0x22, // 2EBE1 at 1089
0xb0, 0x23, 0x03, // 323B0 at 1120 (upper bound)
}; };
static const uint8_t unicode_prop_ID_Continue1_table[660] = { static const uint8_t unicode_prop_ID_Continue1_table[660] = {
@ -547,14 +586,27 @@ static const uint8_t unicode_prop_ID_Continue1_table[660] = {
}; };
static const uint8_t unicode_prop_ID_Continue1_index[63] = { static const uint8_t unicode_prop_ID_Continue1_index[63] = {
0xfa, 0x06, 0x00, 0x70, 0x09, 0x00, 0xf0, 0x0a, 0xfa, 0x06, 0x00, // 006FA at 32
0x40, 0x57, 0x0c, 0x00, 0xf0, 0x0d, 0x60, 0xc7, 0x70, 0x09, 0x00, // 00970 at 64
0x0f, 0x20, 0xea, 0x17, 0x40, 0x05, 0x1b, 0x00, 0xf0, 0x0a, 0x40, // 00AF0 at 98
0x41, 0x20, 0x00, 0x0c, 0xa8, 0x80, 0x37, 0xaa, 0x57, 0x0c, 0x00, // 00C57 at 128
0x20, 0x50, 0xfe, 0x20, 0x3a, 0x0d, 0x21, 0x74, 0xf0, 0x0d, 0x60, // 00DF0 at 163
0x11, 0x01, 0x5a, 0x14, 0x21, 0x44, 0x19, 0x81, 0xc7, 0x0f, 0x20, // 00FC7 at 193
0x5a, 0x1d, 0xa1, 0xf5, 0x6a, 0x21, 0x45, 0xd2, 0xea, 0x17, 0x40, // 017EA at 226
0x41, 0xaf, 0xe2, 0x21, 0xf0, 0x01, 0x0e, 0x05, 0x1b, 0x00, // 01B05 at 256
0x41, 0x20, 0x00, // 02041 at 288
0x0c, 0xa8, 0x80, // 0A80C at 324
0x37, 0xaa, 0x20, // 0AA37 at 353
0x50, 0xfe, 0x20, // 0FE50 at 385
0x3a, 0x0d, 0x21, // 10D3A at 417
0x74, 0x11, 0x01, // 11174 at 448
0x5a, 0x14, 0x21, // 1145A at 481
0x44, 0x19, 0x81, // 11944 at 516
0x5a, 0x1d, 0xa1, // 11D5A at 549
0xf5, 0x6a, 0x21, // 16AF5 at 577
0x45, 0xd2, 0x41, // 1D245 at 610
0xaf, 0xe2, 0x21, // 1E2AF at 641
0xf0, 0x01, 0x0e, // E01F0 at 672 (upper bound)
}; };
#ifdef CONFIG_ALL_UNICODE #ifdef CONFIG_ALL_UNICODE
@ -676,17 +728,35 @@ static const uint8_t unicode_cc_table[899] = {
}; };
static const uint8_t unicode_cc_index[87] = { static const uint8_t unicode_cc_index[87] = {
0x4d, 0x03, 0x00, 0x97, 0x05, 0x20, 0xc6, 0x05, 0x4d, 0x03, 0x00, // 0034D at 32
0x00, 0xe7, 0x06, 0x00, 0x45, 0x07, 0x00, 0x9c, 0x97, 0x05, 0x20, // 00597 at 65
0x08, 0x00, 0x4d, 0x09, 0x00, 0x3c, 0x0b, 0x00, 0xc6, 0x05, 0x00, // 005C6 at 96
0x3d, 0x0d, 0x00, 0x36, 0x0f, 0x00, 0x38, 0x10, 0xe7, 0x06, 0x00, // 006E7 at 128
0x20, 0x3a, 0x19, 0x00, 0xcb, 0x1a, 0x20, 0xd3, 0x45, 0x07, 0x00, // 00745 at 160
0x1c, 0x00, 0xcf, 0x1d, 0x00, 0xe2, 0x20, 0x00, 0x9c, 0x08, 0x00, // 0089C at 192
0x2e, 0x30, 0x20, 0x2b, 0xa9, 0x20, 0xed, 0xab, 0x4d, 0x09, 0x00, // 0094D at 224
0x00, 0x39, 0x0a, 0x01, 0x51, 0x0f, 0x01, 0x73, 0x3c, 0x0b, 0x00, // 00B3C at 256
0x11, 0x01, 0x75, 0x13, 0x01, 0x2b, 0x17, 0x21, 0x3d, 0x0d, 0x00, // 00D3D at 288
0x3f, 0x1c, 0x21, 0x9e, 0xbc, 0x21, 0x08, 0xe0, 0x36, 0x0f, 0x00, // 00F36 at 320
0x01, 0x44, 0xe9, 0x01, 0x4b, 0xe9, 0x01, 0x38, 0x10, 0x20, // 01038 at 353
0x3a, 0x19, 0x00, // 0193A at 384
0xcb, 0x1a, 0x20, // 01ACB at 417
0xd3, 0x1c, 0x00, // 01CD3 at 448
0xcf, 0x1d, 0x00, // 01DCF at 480
0xe2, 0x20, 0x00, // 020E2 at 512
0x2e, 0x30, 0x20, // 0302E at 545
0x2b, 0xa9, 0x20, // 0A92B at 577
0xed, 0xab, 0x00, // 0ABED at 608
0x39, 0x0a, 0x01, // 10A39 at 640
0x51, 0x0f, 0x01, // 10F51 at 672
0x73, 0x11, 0x01, // 11173 at 704
0x75, 0x13, 0x01, // 11375 at 736
0x2b, 0x17, 0x21, // 1172B at 769
0x3f, 0x1c, 0x21, // 11C3F at 801
0x9e, 0xbc, 0x21, // 1BC9E at 833
0x08, 0xe0, 0x01, // 1E008 at 864
0x44, 0xe9, 0x01, // 1E944 at 896
0x4b, 0xe9, 0x01, // 1E94B at 928 (upper bound)
}; };
static const uint32_t unicode_decomp_table1[699] = { static const uint32_t unicode_decomp_table1[699] = {
@ -4484,3 +4554,4 @@ static const uint16_t unicode_prop_len_table[] = {
}; };
#endif /* CONFIG_ALL_UNICODE */ #endif /* CONFIG_ALL_UNICODE */
/* 62 tables / 32261 bytes, 5 index / 345 bytes */

View file

@ -262,11 +262,7 @@ int lre_canonicalize(uint32_t c, BOOL is_unicode)
static uint32_t get_le24(const uint8_t *ptr) static uint32_t get_le24(const uint8_t *ptr)
{ {
#if defined(__x86__) || defined(__x86_64__)
return *(uint16_t *)ptr | (ptr[2] << 16);
#else
return ptr[0] | (ptr[1] << 8) | (ptr[2] << 16); return ptr[0] | (ptr[1] << 8) | (ptr[2] << 16);
#endif
} }
#define UNICODE_INDEX_BLOCK_LEN 32 #define UNICODE_INDEX_BLOCK_LEN 32
@ -317,6 +313,14 @@ static BOOL lre_is_in_table(uint32_t c, const uint8_t *table,
return FALSE; /* outside the table */ return FALSE; /* outside the table */
p = table + pos; p = table + pos;
bit = 0; bit = 0;
/* Compressed run length encoding:
00..3F: 2 packed lengths: 3-bit + 3-bit
40..5F: 5-bits plus extra byte for length
60..7F: 5-bits plus 2 extra bytes for length
80..FF: 7-bit length
lengths must be incremented to get character count
Ranges alternate between false and true return value.
*/
for(;;) { for(;;) {
b = *p++; b = *p++;
if (b < 64) { if (b < 64) {
@ -833,6 +837,13 @@ static int unicode_get_cc(uint32_t c)
if (pos < 0) if (pos < 0)
return 0; return 0;
p = unicode_cc_table + pos; p = unicode_cc_table + pos;
/* Compressed run length encoding:
- 2 high order bits are combining class type
- 0:0, 1:230, 2:extra byte linear progression, 3:extra byte
- 00..2F: range length (add 1)
- 30..37: 3-bit range-length + 1 extra byte
- 38..3F: 3-bit range-length + 2 extra byte
*/
for(;;) { for(;;) {
b = *p++; b = *p++;
type = b >> 6; type = b >> 6;
@ -1185,6 +1196,15 @@ static int unicode_general_category1(CharRange *cr, uint32_t gc_mask)
p = unicode_gc_table; p = unicode_gc_table;
p_end = unicode_gc_table + countof(unicode_gc_table); p_end = unicode_gc_table + countof(unicode_gc_table);
c = 0; c = 0;
/* Compressed range encoding:
initial byte:
bits 0..4: category number (special case 31)
bits 5..7: range length (add 1)
special case bits 5..7 == 7: read an extra byte
- 00..7F: range length (add 7 + 1)
- 80..BF: 6-bits plus extra byte for range length (add 7 + 128)
- C0..FF: 6-bits plus 2 extra bytes for range length (add 7 + 128 + 16384)
*/
while (p < p_end) { while (p < p_end) {
b = *p++; b = *p++;
n = b >> 5; n = b >> 5;
@ -1238,6 +1258,14 @@ static int unicode_prop1(CharRange *cr, int prop_idx)
p_end = p + unicode_prop_len_table[prop_idx]; p_end = p + unicode_prop_len_table[prop_idx];
c = 0; c = 0;
bit = 0; bit = 0;
/* Compressed range encoding:
00..3F: 2 packed lengths: 3-bit + 3-bit
40..5F: 5-bits plus extra byte for length
60..7F: 5-bits plus 2 extra bytes for length
80..FF: 7-bit length
lengths must be incremented to get character count
Ranges alternate between false and true return value.
*/
while (p < p_end) { while (p < p_end) {
c0 = c; c0 = c;
b = *p++; b = *p++;
@ -1786,3 +1814,97 @@ int unicode_prop(CharRange *cr, const char *prop_name)
} }
#endif /* CONFIG_ALL_UNICODE */ #endif /* CONFIG_ALL_UNICODE */
/*---- lre codepoint categorizing functions ----*/
#define S UNICODE_C_SPACE
#define D UNICODE_C_DIGIT
#define X UNICODE_C_XDIGIT
#define U UNICODE_C_UPPER
#define L UNICODE_C_LOWER
#define _ UNICODE_C_UNDER
#define d UNICODE_C_DOLLAR
uint8_t const lre_ctype_bits[256] = {
0, 0, 0, 0, 0, 0, 0, 0,
0, S, S, S, S, S, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
S, 0, 0, 0, d, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
X|D, X|D, X|D, X|D, X|D, X|D, X|D, X|D,
X|D, X|D, 0, 0, 0, 0, 0, 0,
0, X|U, X|U, X|U, X|U, X|U, X|U, U,
U, U, U, U, U, U, U, U,
U, U, U, U, U, U, U, U,
U, U, U, 0, 0, 0, 0, _,
0, X|L, X|L, X|L, X|L, X|L, X|L, L,
L, L, L, L, L, L, L, L,
L, L, L, L, L, L, L, L,
L, L, L, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
S, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
};
#undef S
#undef D
#undef X
#undef U
#undef L
#undef _
#undef d
/* code point ranges for Zs,Zl or Zp property */
static const uint16_t char_range_s[] = {
10,
0x0009, 0x000D + 1,
0x0020, 0x0020 + 1,
0x00A0, 0x00A0 + 1,
0x1680, 0x1680 + 1,
0x2000, 0x200A + 1,
/* 2028;LINE SEPARATOR;Zl;0;WS;;;;;N;;;;; */
/* 2029;PARAGRAPH SEPARATOR;Zp;0;B;;;;;N;;;;; */
0x2028, 0x2029 + 1,
0x202F, 0x202F + 1,
0x205F, 0x205F + 1,
0x3000, 0x3000 + 1,
/* FEFF;ZERO WIDTH NO-BREAK SPACE;Cf;0;BN;;;;;N;BYTE ORDER MARK;;;; */
0xFEFF, 0xFEFF + 1,
};
BOOL lre_is_space_non_ascii(uint32_t c)
{
size_t i, n;
n = countof(char_range_s);
for(i = 5; i < n; i += 2) {
uint32_t low = char_range_s[i];
uint32_t high = char_range_s[i + 1];
if (c < low)
return FALSE;
if (c < high)
return TRUE;
}
return FALSE;
}

View file

@ -24,27 +24,13 @@
#ifndef LIBUNICODE_H #ifndef LIBUNICODE_H
#define LIBUNICODE_H #define LIBUNICODE_H
#include <inttypes.h> #include <stdint.h>
#define LRE_BOOL int /* for documentation purposes */
/* define it to include all the unicode tables (40KB larger) */ /* define it to include all the unicode tables (40KB larger) */
#define CONFIG_ALL_UNICODE #define CONFIG_ALL_UNICODE
#define LRE_CC_RES_LEN_MAX 3 #define LRE_CC_RES_LEN_MAX 3
typedef enum {
UNICODE_NFC,
UNICODE_NFD,
UNICODE_NFKC,
UNICODE_NFKD,
} UnicodeNormalizationEnum;
int lre_case_conv(uint32_t *res, uint32_t c, int conv_type);
int lre_canonicalize(uint32_t c, LRE_BOOL is_unicode);
LRE_BOOL lre_is_cased(uint32_t c);
LRE_BOOL lre_is_case_ignorable(uint32_t c);
/* char ranges */ /* char ranges */
typedef struct { typedef struct {
@ -102,12 +88,14 @@ int cr_op(CharRange *cr, const uint32_t *a_pt, int a_len,
int cr_invert(CharRange *cr); int cr_invert(CharRange *cr);
int cr_regexp_canonicalize(CharRange *cr, LRE_BOOL is_unicode); int cr_regexp_canonicalize(CharRange *cr, int is_unicode);
#ifdef CONFIG_ALL_UNICODE typedef enum {
UNICODE_NFC,
LRE_BOOL lre_is_id_start(uint32_t c); UNICODE_NFD,
LRE_BOOL lre_is_id_continue(uint32_t c); UNICODE_NFKC,
UNICODE_NFKD,
} UnicodeNormalizationEnum;
int unicode_normalize(uint32_t **pdst, const uint32_t *src, int src_len, int unicode_normalize(uint32_t **pdst, const uint32_t *src, int src_len,
UnicodeNormalizationEnum n_type, UnicodeNormalizationEnum n_type,
@ -115,13 +103,80 @@ int unicode_normalize(uint32_t **pdst, const uint32_t *src, int src_len,
/* Unicode character range functions */ /* Unicode character range functions */
int unicode_script(CharRange *cr, int unicode_script(CharRange *cr, const char *script_name, int is_ext);
const char *script_name, LRE_BOOL is_ext);
int unicode_general_category(CharRange *cr, const char *gc_name); int unicode_general_category(CharRange *cr, const char *gc_name);
int unicode_prop(CharRange *cr, const char *prop_name); int unicode_prop(CharRange *cr, const char *prop_name);
#endif /* CONFIG_ALL_UNICODE */ int lre_case_conv(uint32_t *res, uint32_t c, int conv_type);
int lre_canonicalize(uint32_t c, int is_unicode);
#undef LRE_BOOL /* Code point type categories */
enum {
UNICODE_C_SPACE = (1 << 0),
UNICODE_C_DIGIT = (1 << 1),
UNICODE_C_UPPER = (1 << 2),
UNICODE_C_LOWER = (1 << 3),
UNICODE_C_UNDER = (1 << 4),
UNICODE_C_DOLLAR = (1 << 5),
UNICODE_C_XDIGIT = (1 << 6),
};
extern uint8_t const lre_ctype_bits[256];
/* zero or non-zero return value */
int lre_is_cased(uint32_t c);
int lre_is_case_ignorable(uint32_t c);
int lre_is_id_start(uint32_t c);
int lre_is_id_continue(uint32_t c);
static inline int lre_is_space_byte(uint8_t c) {
return lre_ctype_bits[c] & UNICODE_C_SPACE;
}
static inline int lre_is_id_start_byte(uint8_t c) {
return lre_ctype_bits[c] & (UNICODE_C_UPPER | UNICODE_C_LOWER |
UNICODE_C_UNDER | UNICODE_C_DOLLAR);
}
static inline int lre_is_id_continue_byte(uint8_t c) {
return lre_ctype_bits[c] & (UNICODE_C_UPPER | UNICODE_C_LOWER |
UNICODE_C_UNDER | UNICODE_C_DOLLAR |
UNICODE_C_DIGIT);
}
int lre_is_space_non_ascii(uint32_t c);
static inline int lre_is_space(uint32_t c) {
if (c < 256)
return lre_is_space_byte(c);
else
return lre_is_space_non_ascii(c);
}
static inline int lre_js_is_ident_first(uint32_t c) {
if (c < 128) {
return lre_is_id_start_byte(c);
} else {
#ifdef CONFIG_ALL_UNICODE
return lre_is_id_start(c);
#else
return !lre_is_space_non_ascii(c);
#endif
}
}
static inline int lre_js_is_ident_next(uint32_t c) {
if (c < 128) {
return lre_is_id_continue_byte(c);
} else {
/* ZWNJ and ZWJ are accepted in identifiers */
if (c >= 0x200C && c <= 0x200D)
return TRUE;
#ifdef CONFIG_ALL_UNICODE
return lre_is_id_continue(c);
#else
return !lre_is_space_non_ascii(c);
#endif
}
}
#endif /* LIBUNICODE_H */ #endif /* LIBUNICODE_H */

File diff suppressed because it is too large Load diff

View file

@ -92,6 +92,7 @@ typedef struct JSRefCountHeader {
} JSRefCountHeader; } JSRefCountHeader;
void quickjs_set_dumpout(FILE *f); void quickjs_set_dumpout(FILE *f);
void JS_SetInterruptRate(int count);
#define JS_FLOAT64_NAN NAN #define JS_FLOAT64_NAN NAN
@ -635,7 +636,9 @@ static inline JS_BOOL JS_IsObject(JSValueConst v)
JSValue JS_Throw(JSContext *ctx, JSValue obj); JSValue JS_Throw(JSContext *ctx, JSValue obj);
JSValue JS_GetException(JSContext *ctx); JSValue JS_GetException(JSContext *ctx);
JS_BOOL JS_HasException(JSContext *ctx);
JS_BOOL JS_IsError(JSContext *ctx, JSValueConst val); JS_BOOL JS_IsError(JSContext *ctx, JSValueConst val);
void JS_SetUncatchableError(JSContext *ctx, JSValueConst val, JS_BOOL flag);
void JS_ResetUncatchableError(JSContext *ctx); void JS_ResetUncatchableError(JSContext *ctx);
JSValue JS_NewError(JSContext *ctx); JSValue JS_NewError(JSContext *ctx);
JSValue __js_printf_like(2, 3) JS_ThrowSyntaxError(JSContext *ctx, const char *fmt, ...); JSValue __js_printf_like(2, 3) JS_ThrowSyntaxError(JSContext *ctx, const char *fmt, ...);
@ -684,6 +687,10 @@ static inline JSValue JS_DupValueRT(JSRuntime *rt, JSValueConst v)
return (JSValue)v; return (JSValue)v;
} }
JS_BOOL JS_StrictEq(JSContext *ctx, JSValueConst op1, JSValueConst op2);
JS_BOOL JS_SameValue(JSContext *ctx, JSValueConst op1, JSValueConst op2);
JS_BOOL JS_SameValueZero(JSContext *ctx, JSValueConst op1, JSValueConst op2);
int JS_ToBool(JSContext *ctx, JSValueConst val); /* return -1 for JS_EXCEPTION */ int JS_ToBool(JSContext *ctx, JSValueConst val); /* return -1 for JS_EXCEPTION */
int JS_ToInt32(JSContext *ctx, int32_t *pres, JSValueConst val); int JS_ToInt32(JSContext *ctx, int32_t *pres, JSValueConst val);
static inline int JS_ToUint32(JSContext *ctx, uint32_t *pres, JSValueConst val) static inline int JS_ToUint32(JSContext *ctx, uint32_t *pres, JSValueConst val)
@ -726,6 +733,8 @@ JS_BOOL JS_SetConstructorBit(JSContext *ctx, JSValueConst func_obj, JS_BOOL val)
JSValue JS_NewArray(JSContext *ctx); JSValue JS_NewArray(JSContext *ctx);
int JS_IsArray(JSContext *ctx, JSValueConst val); int JS_IsArray(JSContext *ctx, JSValueConst val);
JSValue JS_NewDate(JSContext *ctx, double epoch_ms);
JSValue JS_GetPropertyInternal(JSContext *ctx, JSValueConst obj, JSValue JS_GetPropertyInternal(JSContext *ctx, JSValueConst obj,
JSAtom prop, JSValueConst receiver, JSAtom prop, JSValueConst receiver,
JS_BOOL throw_ref_error); JS_BOOL throw_ref_error);
@ -824,6 +833,23 @@ JSValue JS_NewArrayBuffer(JSContext *ctx, uint8_t *buf, size_t len,
JSValue JS_NewArrayBufferCopy(JSContext *ctx, const uint8_t *buf, size_t len); JSValue JS_NewArrayBufferCopy(JSContext *ctx, const uint8_t *buf, size_t len);
void JS_DetachArrayBuffer(JSContext *ctx, JSValueConst obj); void JS_DetachArrayBuffer(JSContext *ctx, JSValueConst obj);
uint8_t *JS_GetArrayBuffer(JSContext *ctx, size_t *psize, JSValueConst obj); uint8_t *JS_GetArrayBuffer(JSContext *ctx, size_t *psize, JSValueConst obj);
typedef enum JSTypedArrayEnum {
JS_TYPED_ARRAY_UINT8C = 0,
JS_TYPED_ARRAY_INT8,
JS_TYPED_ARRAY_UINT8,
JS_TYPED_ARRAY_INT16,
JS_TYPED_ARRAY_UINT16,
JS_TYPED_ARRAY_INT32,
JS_TYPED_ARRAY_UINT32,
JS_TYPED_ARRAY_BIG_INT64,
JS_TYPED_ARRAY_BIG_UINT64,
JS_TYPED_ARRAY_FLOAT32,
JS_TYPED_ARRAY_FLOAT64,
} JSTypedArrayEnum;
JSValue JS_NewTypedArray(JSContext *ctx, int argc, JSValueConst *argv,
JSTypedArrayEnum array_type);
JSValue JS_GetTypedArrayBuffer(JSContext *ctx, JSValueConst obj, JSValue JS_GetTypedArrayBuffer(JSContext *ctx, JSValueConst obj,
size_t *pbyte_offset, size_t *pbyte_offset,
size_t *pbyte_length, size_t *pbyte_length,

22
source/engine/timer.c Normal file
View file

@ -0,0 +1,22 @@
#include "timer.h"
#include "stb_ds.h"
timer *timers;
timer *timer_make()
{
return NULL;
}
void timer_free(timer *t)
{
}
void timer_update(double dt)
{
for (int i = 0; i < arrlen(timers); i++) {
timers[i].remain -= dt;
}
}

13
source/engine/timer.h Normal file
View file

@ -0,0 +1,13 @@
#ifndef TIMER_H
typedef struct timer {
double start;
double remain;
} timer;
timer *timer_make();
void timer_free(timer *t);
void timer_update(double dt);
#endif

View file

@ -5,16 +5,21 @@
transform *make_transform() transform *make_transform()
{ {
transform *t = calloc(sizeof(transform),1); transform *t = calloc(sizeof(transform),1);
t->scale = (HMM_Vec3){1,1,1}; t->scale = (HMM_Vec3){1,1,1};
t->rotation = (HMM_Quat){0,0,0,1}; t->rotation = (HMM_Quat){0,0,0,1};
t->dirty = 1;
return t; return t;
} }
void transform_free(transform *t) { free(t); } void transform_free(transform *t) { free(t); }
void transform_apply(transform *t) { t->dirty = 1; }
void transform_move(transform *t, HMM_Vec3 v) void transform_move(transform *t, HMM_Vec3 v)
{ {
t->pos = HMM_AddV3(t->pos, v); t->pos = HMM_AddV3(t->pos, v);
t->dirty = 1;
} }
HMM_Vec3 transform_direction(transform *t, HMM_Vec3 dir) HMM_Vec3 transform_direction(transform *t, HMM_Vec3 dir)
@ -42,7 +47,6 @@ HMM_Vec2 mat_right(HMM_Mat3 m) { return HMM_NormV2(m.Columns[0].XY); }
float vec_angle(HMM_Vec2 a, HMM_Vec2 b) { return acos(HMM_DotV2(a,b)/(HMM_LenV2(a)*HMM_LenV2(b))); } float vec_angle(HMM_Vec2 a, HMM_Vec2 b) { return acos(HMM_DotV2(a,b)/(HMM_LenV2(a)*HMM_LenV2(b))); }
float vec_dirangle(HMM_Vec2 a, HMM_Vec2 b) { return atan2(b.x, b.y) - atan2(a.x, a.y); } float vec_dirangle(HMM_Vec2 a, HMM_Vec2 b) { return atan2(b.x, b.y) - atan2(a.x, a.y); }
HMM_Vec3 mat3_t_pos(HMM_Mat4 m, HMM_Vec3 pos) { return HMM_MulM4V4(m, (HMM_Vec4){pos.X, pos.Y, pos.Z, 1}).XYZ; } HMM_Vec3 mat3_t_pos(HMM_Mat4 m, HMM_Vec3 pos) { return HMM_MulM4V4(m, (HMM_Vec4){pos.X, pos.Y, pos.Z, 1}).XYZ; }
HMM_Vec3 mat3_t_dir(HMM_Mat4 m, HMM_Vec3 dir) HMM_Vec3 mat3_t_dir(HMM_Mat4 m, HMM_Vec3 dir)
@ -51,8 +55,15 @@ HMM_Vec3 mat3_t_dir(HMM_Mat4 m, HMM_Vec3 dir)
return mat3_t_pos(m, 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); return HMM_M4TRS(t->pos, t->rotation, t->scale);
if (t->dirty) {
t->cache = HMM_M4TRS(t->pos, t->rotation, t->scale);
t->dirty = 0;
}
return t->cache;
} }
HMM_Quat angle2rotation(float angle) HMM_Quat angle2rotation(float angle)

View file

@ -12,6 +12,7 @@ typedef struct transform {
} transform; } transform;
transform *make_transform(); transform *make_transform();
void transform_apply(transform *t);
void transform_free(transform *t); void transform_free(transform *t);
#define VEC2_FMT "[%g,%g]" #define VEC2_FMT "[%g,%g]"
@ -33,7 +34,7 @@ float vec_dirangle(HMM_Vec2 a, HMM_Vec2 b);
HMM_Vec3 mat3_t_pos(HMM_Mat4 m, HMM_Vec3 pos); HMM_Vec3 mat3_t_pos(HMM_Mat4 m, HMM_Vec3 pos);
HMM_Vec3 mat3_t_dir(HMM_Mat4 m, HMM_Vec3 dir); HMM_Vec3 mat3_t_dir(HMM_Mat4 m, HMM_Vec3 dir);
HMM_Mat4 transform2mat(transform t); HMM_Mat4 transform2mat(transform *t);
transform mat2transform(HMM_Mat4 m); transform mat2transform(HMM_Mat4 m);
HMM_Quat angle2rotation(float angle); HMM_Quat angle2rotation(float angle);

View file

@ -7,7 +7,6 @@
#include "resources.h" #include "resources.h"
#include "spline.h" #include "spline.h"
#include <stdio.h> #include <stdio.h>
#include "particle.h"
#include "simplex.h" #include "simplex.h"
#include <wctype.h> #include <wctype.h>