Sprites are now defined in C; gameobject getsets

This commit is contained in:
John Alanbrook 2024-03-13 03:51:44 -05:00
parent 3e8bbdbeb2
commit bb24fd2bc0
22 changed files with 763 additions and 1020 deletions

View file

@ -19,25 +19,22 @@ var component = {
return (typeof component[c.toString()] === 'object'); return (typeof component[c.toString()] === 'object');
}, },
hides: ['gameobject', 'id'],
make(go) { make(go) {
var nc = Object.create(this); var nc = Object.create(this);
nc.gameobject = go; nc.gameobject = go;
Object.assign(nc, this._enghook(go.body)); Object.mixin(nc, this._enghook(go.body));
assign_impl(nc,this.impl); assign_impl(nc,this.impl);
Object.hide(nc, ...this.hides); Object.hide(nc, ['gameobject', 'id']);
nc.post(); nc.post();
return nc; return nc;
}, },
kill() { console.info("Kill not created for this component yet"); }, kill() { console.info("Kill not created for this component yet"); },
sync() {}, sync(){},
post(){}, post(){},
gui() { }, gui(){},
gizmo() { }, gizmo(){},
prepare_center() {},
finish_center() {}, finish_center() {},
extend(spec) { return Object.copy(this, spec); }, extend(spec) { return Object.copy(this, spec); },
}; };
@ -68,126 +65,95 @@ var assign_impl = function(obj, impl)
obj[key] = tmp[key]; obj[key] = tmp[key];
} }
component.sprite = Object.copy(component, { function json_from_whitelist(whitelist)
pos:[0,0], {
color:[1,1,1,1], return function() {
layer:0, var o = {};
enabled:true, for (var p of whitelist)
path: "", o[p] = this[p];
rect: {s0:0, s1: 1, t0: 0, t1: 1}, return o;
toString() { return "sprite"; }, }
_enghook: make_sprite, }
});
component.sprite.mode = { Object.mixin(cmd(268,true), {
simple: 0, toJSON:json_from_whitelist([
tile: 1 "path",
}; "pos",
"scale",
component.sprite.impl = { "angle",
toJSON() { "color",
var j = {}; "emissive",
Object.keys(this).forEach(k => j[k] = this[k]); "parallax",
delete j.rect; "frame"
return j; ]),
anim:{},
playing: 0,
play(str) {
var sp = this;
str ??= 0;
var playing = this.anim[str];
if (!playing) return; //TODO: ERROR
var f = 0;
function advance() {
sp.path = playing.path;
sp.frame = playing.frames[f].rect;
f = (f+1)%playing.frames.length;
if (f === 0)
sp.anim_done?.();
sp.gameobject?.delay(advance, playing.frames[f].time);
}
advance();
}, },
stop() {},
set path(x) { set path(p) {
if (this.cancel) { p = Resources.find_image(p);
this.cancel(); if (!p) return;
this.cancel = undefined; if (p === this.path) return;
} this.tex = cmd(269,p);
var anim = SpriteAnim.make(p);
if (!Resources.is_animation(x)) { if (!anim) return;
this.rect = component.sprite.rect; this.anim = anim;
cmd(12,this.id,x,this.rect); this.play();
}
else {
this.anims = SpriteAnim.make(x);
Object.hide(this, 'anims');
var anim = this.anims[0];
this.rect = anim.frames[0].rect;
cmd(12,this.id,anim.path,this.rect);
}
}, },
get path() { get path() {
var s = cmd(116,this.id); return this.tex.path();
if (s === "icons/no_tex.gif") return undefined;
return s;
}, },
play(name) {
if (!this.anims) return;
if (this.cancel) this.cancel();
name ??= 0;
var frame = 0;
var anim = this.anims[name];
var advance = function() {
frame = (frame+1)%anim.frames.length;
this.rect = anim.frames[frame].rect;
cmd(12,this.id,anim.path,this.rect);
this.cancel = this.gameobject.delay(advance.bind(this), anim.frames[frame].time);
}
advance.call(this);
},
stop() {
if (!this.cancel) return;
this.cancel();
this.cancel = undefined;
},
setframe(f) {
if (!this.anims) return;
this.stop();
var anim = this.anims[0];
this.rect = anim.frames[f].rect;
cmd(12,this.id,anim.path,this.rect);
},
toString() { return "sprite"; }, toString() { return "sprite"; },
hide() { this.enabled = false; }, move(d) { this.pos = this.pos.add(d); },
show() { this.enabled = true; },
asset(str) { this.path = str; },
get enabled() { return cmd(114,this.id); },
set enabled(x) { cmd(20,this.id,x); },
set color(x) { cmd(96,this.id,x); },
get color() {return cmd(148,this.id);},
get pos() { return cmd(111, this.id); },
set pos(x) { cmd(37,this.id,x); },
get parallax() { return cmd(232, this.id); },
set parallax(x) { cmd(233,this.id,x); },
get angle() { return cmd(217,this.id); },
set angle(x) { cmd(218,this.id,x); },
get scale() { return cmd(215, this.id); },
set scale(x) { cmd(216, this.id, x); },
grow(x) { grow(x) {
this.scale = this.scale.scale(x); this.scale = this.scale.scale(x);
this.pos = this.pos.scale(x); this.pos = this.pos.scale(x);
}, },
get drawmode() { return cmd(220,this.id); },
set drawmode(x) { cmd(219,this.id,x); },
emissive(x) { cmd(170, this.id, x); },
sync() { }, sync() { },
pickm() { return this; }, pick() { return this; },
move(d) { this.pos = this.pos.add(d); },
boundingbox() { boundingbox() {
var dim = this.dimensions(); var dim = this.dimensions();
dim = dim.scale(this.gameobject.gscale()); dim = dim.scale(this.gameobject.gscale());
var realpos = dim.scale(0.5).add(this.pos); var realpos = dim.scale(0.5).add(this.pos);
return bbox.fromcwh(realpos,dim); return bbox.fromcwh(realpos,dim);
}, },
kill() { cmd(9,this.id); },
dimensions() { dimensions() {
var dim = Resources.texture.dimensions(this.path); var dim = [this.tex.width(), this.tex.height()];
dim.x *= (this.rect.s1-this.rect.s0); dim.x *= this.frame.w;
dim.y *= (this.rect.t1-this.rect.t0); dim.y *= this.frame.h;
return dim; return dim;
}, },
width() { return this.dimensions().x; }, width() { return this.dimensions().x; },
height() { return this.dimensions().y; }, height() { return this.dimensions().y; },
}; });
cmd(268,true).make = function(go)
{
var sp = cmd(268);
sp.go = go.body;
sp.gameobject = go;
return sp;
}
component.sprite = cmd(268,true);
Object.freeze(sprite); Object.freeze(sprite);
@ -195,6 +161,7 @@ component.model = Object.copy(component, {
path:"", path:"",
_enghook: make_model, _enghook: make_model,
}); });
component.model.impl = { component.model.impl = {
set path(x) { cmd(149, this.id, x); }, set path(x) { cmd(149, this.id, x); },
draw() { cmd(150, this.id); }, draw() { cmd(150, this.id); },
@ -245,10 +212,10 @@ var SpriteAnim = {
for (var f = 0; f < frames; f++) { for (var f = 0; f < frames; f++) {
var frame = {}; var frame = {};
frame.rect = { frame.rect = {
s0: 0, x: 0,
s1: 1, w: 1,
t0: yslice*f, y: yslice*f,
t1: yslice*(f+1) h: yslice
}; };
frame.time = 0.05; frame.time = 0.05;
anim.frames.push(frame); anim.frames.push(frame);
@ -290,10 +257,10 @@ var SpriteAnim = {
var f = ase_frame.frame; var f = ase_frame.frame;
var frame = {}; var frame = {};
frame.rect = { frame.rect = {
s0: f.x/dim.w, x: f.x/dim.w,
s1: (f.x+f.w)/dim.w, w: f.w/dim.w,
t0: f.y/dim.h, y: f.y/dim.h,
t1: (f.y+f.h)/dim.h h: f.h/dim.h
}; };
frame.time = ase_frame.duration / 1000; frame.time = ase_frame.duration / 1000;
anim.frames.push(frame); anim.frames.push(frame);
@ -365,6 +332,10 @@ collider2d.inputs['M-t'] = function() { this.enabled = !this.enabled; }
collider2d.inputs['M-t'].doc = "Toggle if this collider is enabled."; collider2d.inputs['M-t'].doc = "Toggle if this collider is enabled.";
component.polygon2d = Object.copy(collider2d, { component.polygon2d = Object.copy(collider2d, {
toJSON:json_from_whitelist([
'points',
'sensor'
]),
toString() { return "polygon2d"; }, toString() { return "polygon2d"; },
flipx: false, flipx: false,
flipy: false, flipy: false,
@ -463,6 +434,13 @@ polygon2d.inputs['C-b'] = function() {
polygon2d.inputs['C-b'].doc = "Freeze mirroring in place."; polygon2d.inputs['C-b'].doc = "Freeze mirroring in place.";
component.edge2d = Object.copy(collider2d, { component.edge2d = Object.copy(collider2d, {
toJSON:json_from_whitelist([
'sensor',
'thickness',
'points',
'hollow',
'hollowt',
]),
dimensions:2, dimensions:2,
thickness:0, thickness:0,
/* if type === -1, point to point */ /* if type === -1, point to point */
@ -821,6 +799,11 @@ component.circle2d = Object.copy(collider2d, {
}); });
component.circle2d.impl = Object.mix({ component.circle2d.impl = Object.mix({
toJSON:json_from_whitelist([
"pos",
"radius",
]),
set radius(x) { cmd_circle2d(0,this.id,x); }, set radius(x) { cmd_circle2d(0,this.id,x); },
get radius() { return cmd_circle2d(2,this.id); }, get radius() { return cmd_circle2d(2,this.id); },
@ -840,4 +823,4 @@ component.circle2d.impl = Object.mix({
}, collider2d.impl); }, collider2d.impl);
return {component}; return {component, SpriteAnim};

View file

@ -1,17 +1,15 @@
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;
var t = 1; var t = 1;
var n = name + t; var n = name + t;
while (n in obj) { while (n in obj) {
t++; t++;
n = name+t; n = name + t;
} }
return n; return n;
} }
var gameobject_impl = { var gameobject_impl = {
get pos() { get pos() {
assert(this.master, `Entity ${this.toString()} has no master.`); assert(this.master, `Entity ${this.toString()} has no master.`);
@ -27,7 +25,7 @@ var gameobject_impl = {
assert(this.master, `No master set on ${this.toString()}`); assert(this.master, `No master set on ${this.toString()}`);
return this.worldangle() - this.master.worldangle(); return this.worldangle() - this.master.worldangle();
}, },
set angle(x) { set angle(x) {
var diff = x - this.angle; var diff = x - this.angle;
@ -36,64 +34,64 @@ var gameobject_impl = {
x.pos = Vector.rotate(x.pos, diff); x.pos = Vector.rotate(x.pos, diff);
}); });
this.sworldangle(x-this.master.worldangle()); this.sworldangle(x - this.master.worldangle());
}, },
get scale() { get scale() {
assert(this.master, `No master set on ${this.toString()}`); assert(this.master, `No master set on ${this.toString()}`);
var pscale = [1,1,1]; var pscale = [1, 1, 1];
return this.gscale().map((x,i) => x/(this.master.gscale()[i]*pscale[i])); return this.gscale().map((x, i) => x / (this.master.gscale()[i] * pscale[i]));
}, },
set scale(x) { set scale(x) {
if (typeof x === 'number') if (typeof x === 'number')
x = [x,x]; x = [x, x];
var pct = this.scale.map((s,i) => x[i]/s); var pct = this.scale.map((s, i) => x[i] / s);
this.grow(pct); this.grow(pct);
/* TRANSLATE ALL SUB OBJECTS */ /* TRANSLATE ALL SUB OBJECTS */
this.objects.forEach(obj => { this.objects.forEach(obj => {
obj.grow(pct); obj.grow(pct);
obj.pos = obj.pos.map((x,i)=>x*pct[i]); obj.pos = obj.pos.map((x, i) => x * pct[i]);
}); });
}, },
get draw_layer() { return cmd(171, this.body); }, get draw_layer() { return this.body.drawlayer; },
set draw_layer(x) { cmd(172, this.body, x); }, set draw_layer(x) { this.body.drawlayer = x; },
set layer(x) { cmd(75,this.body,x); }, set layer(x) { this.body.layer = x; },
get layer() { return cmd(77,this.body); }, get layer() { return this.body.layer; },
set warp_layer(x) { cmd(251, this.body,x); }, set warp_layer(x) { this.body.warp_filter = x; },
get warp_layer() { return cmd(252, this.body); }, get warp_layer() { return this.body.warp_filter; },
set mass(x) { set_body(7,this.body,x); }, set mass(x) { set_body(7, this.body, x); },
get mass() { get mass() {
if (!(this.phys === physics.dynamic)) if (!(this.phys === physics.dynamic))
return undefined; return undefined;
return q_body(5, this.body); return q_body(5, this.body);
}, },
get elasticity() { return cmd(107,this.body); }, get elasticity() { return this.body.e; },
set elasticity(x) { cmd(106,this.body,x); }, set elasticity(x) { this.body.e = x; },
get friction() { return cmd(109,this.body); }, get friction() { return this.body.f; },
set friction(x) { cmd(108,this.body,x); }, set friction(x) { this.body.f = x; },
set timescale(x) { cmd(168,this.body,x); }, set timescale(x) { this.body.timescale = x; },
get timescale() { return cmd(169,this.body); }, get timescale() { return this.body.timescale; },
set phys(x) { set_body(1, this.body, x); }, set phys(x) { set_body(1, this.body, x); },
get phys() { return q_body(0,this.body); }, get phys() { return q_body(0, this.body); },
get velocity() { return q_body(3, this.body); }, get velocity() { return q_body(3, this.body); },
set velocity(x) { set_body(9, this.body, x); }, set velocity(x) { set_body(9, this.body, x); },
get damping() { return cmd(157,this.body); }, get damping() { return this.body.damping; },
set damping(x) { cmd(156, this.body, x); }, set damping(x) { this.body.damping = x },
get angularvelocity() { return Math.rad2turn(q_body(4,this.body)); }, get angularvelocity() { return Math.rad2turn(q_body(4, this.body)); },
set angularvelocity(x) { set_body(8, this.body, Math.turn2rad(x)); }, set angularvelocity(x) { set_body(8, this.body, Math.turn2rad(x)); },
get max_velocity() { return cmd(152, this.body); }, get max_velocity() { return this.body.maxvelocity; },
set max_velocity(x) { cmd(151, this.body, x); }, set max_velocity(x) { this.body.maxvelocity = x; },
get max_angularvelocity() { return cmd(155,this.body); }, get max_angularvelocity() { return this.body.maxangularvelocity; },
set max_angularvelocity(x) { cmd(154,this.body,x); }, set max_angularvelocity(x) { this.body.maxangularvelocity = x; },
get_moi() { return q_body(6, this.body); }, get_moi() { return q_body(6, this.body); },
set_moi(x) { set_moi(x) {
if(x <= 0) { if (x <= 0) {
console.error("Cannot set moment of inertia to 0 or less."); console.error("Cannot set moment of inertia to 0 or less.");
return; return;
} }
@ -117,7 +115,7 @@ var gameobject = {
var lur = ur[this.master.ur]; var lur = ur[this.master.ur];
if (!lur) return; if (!lur) return;
var lur = lur.objects[this.toString()]; var lur = lur.objects[this.toString()];
var d = ediff(this._ed.urdiff,lur); var d = ediff(this._ed.urdiff, lur);
if (!d || Object.empty(d)) if (!d || Object.empty(d))
this._ed.inst = true; this._ed.inst = true;
else else
@ -127,7 +125,7 @@ var gameobject = {
selectable: false, selectable: false,
dirty: false dirty: false
}, },
namestr() { namestr() {
var s = this.toString(); var s = this.toString();
if (this._ed.dirty) if (this._ed.dirty)
@ -137,41 +135,41 @@ var gameobject = {
}, },
urstr() { urstr() {
if (this._ed.dirty) return "*"+this.ur; if (this._ed.dirty) return "*" + this.ur;
return this.ur; return this.ur;
}, },
full_path() { full_path() {
return this.path_from(world); return this.path_from(world);
}, },
/* pin this object to the to object */ /* pin this object to the to object */
pin(to) { pin(to) {
var p = cmd(222,this.body, to.body); var p = cmd(222, this.body, to.body);
}, },
slide(to, a, b, min, max) { slide(to, a, b, min, max) {
a ??= [0,0]; a ??= [0, 0];
b ??= [0,0]; b ??= [0, 0];
min ??= 0; min ??= 0;
max ??= 50; max ??= 50;
var p = cmd(229,this.body,to.body,a,b,min,max); var p = cmd(229, this.body, to.body, a, b, min, max);
p.max_force = 500; p.max_force = 500;
p.break(); p.break();
}, },
pivot(to, piv) { pivot(to, piv) {
piv ??= this.worldpos(); piv ??= this.worldpos();
var p = cmd(221,this.body,to.body,piv); var p = cmd(221, this.body, to.body, piv);
}, },
/* groove is on to, from local points a and b, anchored to this at local anchor */ /* groove is on to, from local points a and b, anchored to this at local anchor */
groove(to, a, b, anchor) { groove(to, a, b, anchor) {
anchor ??= [0,0]; anchor ??= [0, 0];
var p = cmd(228, to.body, this.body, a, b, anchor); var p = cmd(228, to.body, this.body, a, b, anchor);
}, },
damped_spring(to, length, stiffness, damping) { damped_spring(to, length, stiffness, damping) {
length ??= Vector.length(this.worldpos(), to.worldpos()); length ??= Vector.length(this.worldpos(), to.worldpos());
stiffness ??= 1; stiffness ??= 1;
damping ??= 1; damping ??= 1;
var dc = 2*Math.sqrt(stiffness*this.mass); var dc = 2 * Math.sqrt(stiffness * this.mass);
var p = cmd(227, this.body, to.body, [0,0], [0,0], stiffness, damping*dc); var p = cmd(227, this.body, to.body, [0, 0], [0, 0], stiffness, damping * dc);
}, },
damped_rotary_spring(to, angle, stiffness, damping) { damped_rotary_spring(to, angle, stiffness, damping) {
angle ??= 0; angle ??= 0;
@ -179,14 +177,14 @@ var gameobject = {
damping ??= 1; damping ??= 1;
/* calculate actual damping value from the damping ratio */ /* calculate actual damping value from the damping ratio */
/* damping = 1 is critical */ /* damping = 1 is critical */
var dc = 2*Math.sqrt(stiffness*this.get_moi()); /* critical damping number */ var dc = 2 * Math.sqrt(stiffness * this.get_moi()); /* critical damping number */
/* zeta = actual/critical */ /* zeta = actual/critical */
var p = cmd(226,this.body,to.body,angle,stiffness,damping*dc); var p = cmd(226, this.body, to.body, angle, stiffness, damping * dc);
}, },
rotary_limit(to, min, max) { rotary_limit(to, min, max) {
var p = cmd(225,this.body,to.body, Math.turn2rad(min),Math.turn2rad(max)); var p = cmd(225, this.body, to.body, Math.turn2rad(min), Math.turn2rad(max));
}, },
ratchet(to,ratch) { ratchet(to, ratch) {
var phase = this.angle - to.angle; var phase = this.angle - to.angle;
var p = cmd(230, this.body, to.body, phase, Math.turn2rad(ratch)); var p = cmd(230, this.body, to.body, phase, Math.turn2rad(ratch));
}, },
@ -194,183 +192,187 @@ var gameobject = {
phase ??= 1; phase ??= 1;
ratio ??= 1; ratio ??= 1;
var phase = this.angle - to.angle; var phase = this.angle - to.angle;
var p = cmd(223,this.body,to.body,phase,ratio); var p = cmd(223, this.body, to.body, phase, ratio);
}, },
motor(to, rate) { motor(to, rate) {
var p = cmd(231, this.body, to.body, rate); var p = cmd(231, this.body, to.body, rate);
}, },
path_from(o) {
var p = this.toString();
var c = this.master;
while (c && c !== o && c !== world) {
p = c.toString() + "." + p;
c = c.master;
}
if (c === world) p = "world." + p;
return p;
},
clear() {
for (var k in this.objects) {
this.objects[k].kill();
};
this.objects = {};
},
delay(fn, seconds) {
var t = timer.delay(fn.bind(this), seconds);
this.timers.push(t);
return t;
},
tween(prop, values, def){ path_from(o) {
var t = Tween.make(this, prop, values, def); var p = this.toString();
t.play(); var c = this.master;
while (c && c !== o && c !== world) {
var k = function() { t.pause(); } p = c.toString() + "." + p;
this.timers.push(k); c = c.master;
return k; }
}, if (c === world) p = "world." + p;
return p;
},
cry(file) { clear() {
return; for (var k in this.objects) {
this.crying = audio.sound.play(file, audio.sound.bus.sfx); this.objects[k].kill();
var killfn = () => {this.crying = undefined; console.warn("killed"); } };
this.crying.hook = killfn; this.objects = {};
return killfn; },
},
set torque(x) { if (!(x >= 0 && x <= Infinity)) return; cmd(153, this.body, x); }, delay(fn, seconds) {
gscale() { return cmd(103,this.body); }, var t = timer.delay(fn.bind(this), seconds);
sgscale(x) { this.timers.push(t);
if (typeof x === 'number') return t;
x = [x,x]; },
cmd(36,this.body,x)
},
phys_material() {
var mat = {};
mat.elasticity = this.elasticity;
mat.friction = this.friction;
return mat;
},
worldpos() { return q_body(1,this.body); }, tween(prop, values, def) {
set_worldpos(x) { var t = Tween.make(this, prop, values, def);
var poses = this.objects.map(x => x.pos); t.play();
set_body(2,this.body,x);
this.objects.forEach((o,i) => o.set_worldpos(this.this2world(poses[i])));
},
screenpos() { return Window.world2screen(this.worldpos()); },
worldangle() { return Math.rad2turn(q_body(2,this.body)); }, var k = function() { t.pause(); }
sworldangle(x) { set_body(0,this.body,Math.turn2rad(x)); }, this.timers.push(k);
return k;
},
get_ur() { cry(file) {
// if (this.ur === 'empty') return undefined; return;
return Object.access(ur,this.ur); this.crying = audio.sound.play(file, audio.sound.bus.sfx);
}, var killfn = () => { this.crying = undefined;
console.warn("killed"); }
this.crying.hook = killfn;
return killfn;
},
/* spawn an entity set torque(x) { if (!(x >= 0 && x <= Infinity)) return;
cmd(153, this.body, x); },
gscale() { return cmd(103, this.body); },
sgscale(x) {
if (typeof x === 'number')
x = [x, x];
cmd(36, this.body, x)
},
phys_material() {
var mat = {};
mat.elasticity = this.elasticity;
mat.friction = this.friction;
return mat;
},
worldpos() { return q_body(1, this.body); },
set_worldpos(x) {
var poses = this.objects.map(x => x.pos);
set_body(2, this.body, x);
this.objects.forEach((o, i) => o.set_worldpos(this.this2world(poses[i])));
},
screenpos() { return Window.world2screen(this.worldpos()); },
worldangle() { return Math.rad2turn(q_body(2, this.body)); },
sworldangle(x) { set_body(0, this.body, Math.turn2rad(x)); },
get_ur() {
// if (this.ur === 'empty') return undefined;
return Object.access(ur, this.ur);
},
/* spawn an entity
text can be: text can be:
the file path of a script the file path of a script
an ur object an ur object
nothing nothing
*/ */
spawn(text) { spawn(text) {
var ent = Object.create(gameobject); var ent = Object.create(gameobject);
if (typeof text === 'object')
text = text.name;
if (typeof text === 'undefined') if (typeof text === 'object')
ent.ur = "empty"; text = text.name;
else if (typeof text !== 'string') {
console.error(`Must pass in an ur type or a string to make an entity.`);
return;
} else {
if (Object.access(ur,text))
ent.ur = text;
else if (io.exists(text))
ent.ur = "script";
else {
console.warn(`Cannot make an entity from '${text}'. Not a valid ur.`);
return;
}
}
Object.mixin(ent,gameobject_impl);
ent.body = make_gameobject();
ent.warp_layer = [true];
ent.phys = 2;
ent.components = {};
ent.objects = {};
ent.timers = [];
ent.reparent(this);
ent._ed = { if (typeof text === 'undefined')
selectable: true, ent.ur = "empty";
dirty: false, else if (typeof text !== 'string') {
inst: false, console.error(`Must pass in an ur type or a string to make an entity.`);
urdiff: {}, return;
}; } else {
if (Object.access(ur, text))
ent.ur = text;
else if (io.exists(text))
ent.ur = "script";
else {
console.warn(`Cannot make an entity from '${text}'. Not a valid ur.`);
return;
}
}
cmd(113, ent.body, ent); // set the internal obj reference to this obj Object.mixin(ent, gameobject_impl);
ent.body = make_gameobject();
ent.warp_layer = [true];
ent.phys = 2;
ent.components = {};
ent.objects = {};
ent.timers = [];
Object.hide(ent, 'ur', 'body', 'components', 'objects', '_ed', 'timers', 'master'); ent.reparent(this);
if (ent.ur === 'empty') {
if (!ur.empty.proto) ur.empty.proto = json.decode(json.encode(ent));
return ent;
}
if (ent.ur === 'script') ent._ed = {
eval_env(io.slurp(text), ent, ent.ur); selectable: true,
else dirty: false,
apply_ur(ent.ur, ent); inst: false,
urdiff: {},
};
for (var [prop,p] of Object.entries(ent)) { cmd(113, ent.body, ent); // set the internal obj reference to this obj
if (!p) continue;
if (typeof p !== 'object') continue;
if (component.isComponent(p)) continue;
if (!p.comp) continue;
ent[prop] = component[p.comp].make(ent);
Object.merge(ent[prop], p);
ent.components[prop] = ent[prop];
};
check_registers(ent); Object.hide(ent, 'ur', 'body', 'components', 'objects', '_ed', 'timers', 'master');
ent.components.forEach(function(x) { if (ent.ur === 'empty') {
if (typeof x.collide === 'function') if (!ur.empty.proto) ur.empty.proto = json.decode(json.encode(ent));
register_collide(1, x.collide.bind(x), ent.body, x.shape); return ent;
}); }
if (typeof ent.load === 'function') ent.load();
if (Game.playing())
if (typeof ent.start === 'function') ent.start();
var mur = ent.get_ur();
if (mur && !mur.proto)
mur.proto = json.decode(json.encode(ent));
ent.sync();
if (!Object.empty(ent.objects)) {
var o = ent.objects;
delete ent.objects;
for (var i in o) {
say(`MAKING ${i}`);
var n = ent.spawn(ur[o[i].ur]);
ent.rename_obj(n.toString(), i);
delete o[i].ur;
Object.assign(n, o[i]);
}
}
return ent; if (ent.ur === 'script')
}, eval_env(io.slurp(text), ent, ent.ur);
else
apply_ur(ent.ur, ent);
for (var [prop, p] of Object.entries(ent)) {
if (!p) continue;
if (typeof p !== 'object') continue;
if (component.isComponent(p)) continue;
if (!p.comp) continue;
ent[prop] = component[p.comp].make(ent);
Object.merge(ent[prop], p);
ent.components[prop] = ent[prop];
};
check_registers(ent);
ent.components.forEach(function(x) {
if (typeof x.collide === 'function')
register_collide(1, x.collide.bind(x), ent.body, x.shape);
});
if (typeof ent.load === 'function') ent.load();
if (Game.playing())
if (typeof ent.start === 'function') ent.start();
var mur = ent.get_ur();
if (mur && !mur.proto)
mur.proto = json.decode(json.encode(ent));
ent.sync();
if (!Object.empty(ent.objects)) {
var o = ent.objects;
delete ent.objects;
for (var i in o) {
say(`MAKING ${i}`);
var n = ent.spawn(ur[o[i].ur]);
ent.rename_obj(n.toString(), i);
delete o[i].ur;
Object.assign(n, o[i]);
}
}
this.body.phys = this.body.phys; // simple way to sync
return ent;
},
/* Reparent 'this' to be 'parent's child */ /* Reparent 'this' to be 'parent's child */
reparent(parent) { reparent(parent) {
@ -382,9 +384,9 @@ var gameobject = {
} }
this.master?.remove_obj(this); this.master?.remove_obj(this);
this.master = parent; this.master = parent;
function unique_name(list, name) { function unique_name(list, name) {
name ??= "new_object"; name ??= "new_object";
var str = name.replaceAll('.', '_'); var str = name.replaceAll('.', '_');
@ -392,7 +394,7 @@ var gameobject = {
var t = str; var t = str;
while (list.indexOf(t) !== -1) { while (list.indexOf(t) !== -1) {
t = str + n; t = str + n;
n++; n++;
} }
return t; return t;
}; };
@ -403,34 +405,36 @@ var gameobject = {
parent[name] = this; parent[name] = this;
this.toString = function() { return name; }; this.toString = function() { return name; };
}, },
remove_obj(obj) { remove_obj(obj) {
if (this[obj.toString()] === this.objects[obj.toString()]) if (this[obj.toString()] === this.objects[obj.toString()])
delete this[obj.toString()]; delete this[obj.toString()];
delete this.objects[obj.toString()]; delete this.objects[obj.toString()];
delete this[obj.toString()]; delete this[obj.toString()];
}, },
components: {}, components: {},
objects: {}, objects: {},
master: undefined, master: undefined,
pulse(vec) { set_body(4, this.body, vec);},
shove(vec) { set_body(12,this.body,vec);},
shove_at(vec, at) { set_body(14,this.body,vec,at); },
world2this(pos) { return cmd(70, this.body, pos); },
this2world(pos) { return cmd(71, this.body, pos); },
this2screen(pos) { return Window.world2screen(this.this2world(pos)); },
screen2this(pos) { return this.world2this(Window.screen2world(pos)); },
dir_world2this(dir) { return cmd(160, this.body, dir); },
dir_this2world(dir) { return cmd(161, this.body, dir); },
alive() { return this.body >= 0; },
in_air() { return q_body(7, this.body);},
hide() { this.components.forEach(x=>x.hide?.()); this.objects.forEach(x=>x.hide?.());}, pulse(vec) { set_body(4, this.body, vec); },
show() { this.components.forEach(function(x) { x.show?.(); }); this.objects.forEach(function(x) { x.show?.(); }); }, shove(vec) { set_body(12, this.body, vec); },
shove_at(vec, at) { set_body(14, this.body, vec, at); },
world2this(pos) { return cmd(70, this.body, pos); },
this2world(pos) { return cmd(71, this.body, pos); },
this2screen(pos) { return Window.world2screen(this.this2world(pos)); },
screen2this(pos) { return this.world2this(Window.screen2world(pos)); },
dir_world2this(dir) { return cmd(160, this.body, dir); },
dir_this2world(dir) { return cmd(161, this.body, dir); },
alive() { return this.body >= 0; },
in_air() { return q_body(7, this.body); },
hide() { this.components.forEach(x => x.hide?.());
this.objects.forEach(x => x.hide?.()); },
show() { this.components.forEach(function(x) { x.show?.(); });
this.objects.forEach(function(x) { x.show?.(); }); },
width() { width() {
var bb = this.boundingbox(); var bb = this.boundingbox();
@ -439,13 +443,13 @@ var gameobject = {
height() { height() {
var bb = this.boundingbox(); var bb = this.boundingbox();
return bb.t-bb.b; return bb.t - bb.b;
}, },
/* Moving, rotating, scaling functions, world relative */ /* Moving, rotating, scaling functions, world relative */
move(vec) { this.set_worldpos(this.worldpos().add(vec)); }, move(vec) { this.set_worldpos(this.worldpos().add(vec)); },
rotate(x) { this.sworldangle(this.worldangle()+x); }, rotate(x) { this.sworldangle(this.worldangle() + x); },
grow(vec) { this.sgscale(this.gscale().map((x,i)=>x*vec[i])); }, grow(vec) { this.sgscale(this.gscale().map((x, i) => x * vec[i])); },
/* Make a unique object the same as its prototype */ /* Make a unique object the same as its prototype */
revert() { revert() {
@ -462,155 +466,155 @@ var gameobject = {
}, },
toString() { return "new_object"; }, toString() { return "new_object"; },
flipx() { return this.scale.x < 0; }, flipx() { return this.scale.x < 0; },
flipy() { return this.scale.y < 0; }, flipy() { return this.scale.y < 0; },
mirror(plane) { mirror(plane) {
this.scale = Vector.reflect(this.scale, plane); this.scale = Vector.reflect(this.scale, plane);
}, },
save:true,
selectable:true,
ed_locked:false,
disable() { this.components.forEach(function(x) { x.disable(); });},
enable() { this.components.forEach(function(x) { x.enable(); });},
sync() {
this.components.forEach(function(x) { x.sync?.(); });
this.objects.forEach(function(x) { x.sync?.(); });
},
/* Bounding box of the object in world dimensions */ save: true,
boundingbox() { selectable: true,
var boxes = []; ed_locked: false,
boxes.push({
t:0,
r:0,
b:0,
l:0
});
for (var key in this.components) { disable() { this.components.forEach(function(x) { x.disable(); }); },
if ('boundingbox' in this.components[key]) enable() { this.components.forEach(function(x) { x.enable(); }); },
boxes.push(this.components[key].boundingbox()); sync() {
} this.components.forEach(function(x) { x.sync?.(); });
for (var key in this.objects) this.objects.forEach(function(x) { x.sync?.(); });
boxes.push(this.objects[key].boundingbox()); },
var bb = boxes.shift(); /* Bounding box of the object in world dimensions */
boundingbox() {
var boxes = [];
boxes.push({
t: 0,
r: 0,
b: 0,
l: 0
});
boxes.forEach(function(x) { bb = bbox.expand(bb, x); }); for (var key in this.components) {
if ('boundingbox' in this.components[key])
bb = bbox.move(bb, this.pos); boxes.push(this.components[key].boundingbox());
}
for (var key in this.objects)
boxes.push(this.objects[key].boundingbox());
return bb ? bb : bbox.fromcwh([0,0], [0,0]); var bb = boxes.shift();
},
/* The unique components of this object. Its diff. */ boxes.forEach(function(x) { bb = bbox.expand(bb, x); });
json_obj() {
var u = this.get_ur();
if (!u) return {};
var proto = u.proto;
var thiso = json.decode(json.encode(this)); // TODO: SLOW. Used to ignore properties in toJSON of components.
var d = ediff(thiso,proto);
d ??= {};
var objects = {};
proto.objects ??= {};
var curobjs = {};
for (var o in this.objects)
curobjs[o] = this.objects[o].instance_obj();
var odiff = ediff(curobjs, proto.objects); bb = bbox.move(bb, this.pos);
if (odiff)
d.objects = curobjs;
delete d.pos; return bb ? bb : bbox.fromcwh([0, 0], [0, 0]);
delete d.angle; },
delete d.scale;
delete d.velocity;
delete d.angularvelocity;
return d;
},
/* The object needed to store an object as an instance of a master */ /* The unique components of this object. Its diff. */
instance_obj() { json_obj() {
var t = this.transform(); var u = this.get_ur();
t.ur = this.ur; if (!u) return {};
return t; var proto = u.proto;
}, var thiso = json.decode(json.encode(this)); // TODO: SLOW. Used to ignore properties in toJSON of components.
proto() { var d = ediff(thiso, proto);
var u = this.get_ur();
if (!u) return {};
return u.proto;
},
transform() { d ??= {};
var t = {};
t.pos = this.pos;
if (t.pos.every(x=>x===0)) delete t.pos;
t.angle = Math.places(this.angle,4);
if (t.angle === 0) delete t.angle;
t.scale = this.scale;
t.scale = t.scale.map((x,i) => x/this.proto().scale[i]);
t.scale = t.scale.map(x => Math.places(x,3));
if (t.scale.every(x=>x===1)) delete t.scale;
return t;
},
/* Velocity and angular velocity of the object */ var objects = {};
phys_obj() { proto.objects ??= {};
var phys = {}; var curobjs = {};
phys.velocity = this.velocity; for (var o in this.objects)
phys.angularvelocity = this.angularvelocity; curobjs[o] = this.objects[o].instance_obj();
return phys;
},
dup(diff) { var odiff = ediff(curobjs, proto.objects);
var n = this.master.spawn(this.__proto__); if (odiff)
Object.totalmerge(n, this.instance_obj()); d.objects = curobjs;
return n;
}, delete d.pos;
delete d.angle;
delete d.scale;
delete d.velocity;
delete d.angularvelocity;
return d;
},
/* The object needed to store an object as an instance of a master */
instance_obj() {
var t = this.transform();
t.ur = this.ur;
return t;
},
proto() {
var u = this.get_ur();
if (!u) return {};
return u.proto;
},
transform() {
var t = {};
t.pos = this.pos;
if (t.pos.every(x => x === 0)) delete t.pos;
t.angle = Math.places(this.angle, 4);
if (t.angle === 0) delete t.angle;
t.scale = this.scale;
t.scale = t.scale.map((x, i) => x / this.proto().scale[i]);
t.scale = t.scale.map(x => Math.places(x, 3));
if (t.scale.every(x => x === 1)) delete t.scale;
return t;
},
/* Velocity and angular velocity of the object */
phys_obj() {
var phys = {};
phys.velocity = this.velocity;
phys.angularvelocity = this.angularvelocity;
return phys;
},
dup(diff) {
var n = this.master.spawn(this.__proto__);
Object.totalmerge(n, this.instance_obj());
return n;
},
kill() { kill() {
if (this.__kill) return; if (this.__kill) return;
this.__kill = true; this.__kill = true;
this.timers.forEach(t => t()); this.timers.forEach(t => t());
this.timers = []; this.timers = [];
Event.rm_obj(this); Event.rm_obj(this);
Player.do_uncontrol(this); Player.do_uncontrol(this);
register_collide(2, undefined, this.body); register_collide(2, undefined, this.body);
if (this.master) { if (this.master) {
this.master.remove_obj(this); this.master.remove_obj(this);
this.master = undefined; this.master = undefined;
} }
if (this.__proto__.instances) if (this.__proto__.instances)
delete this.__proto__.instances[this.toString()]; delete this.__proto__.instances[this.toString()];
for (var key in this.components) { for (var key in this.components) {
this.components[key].kill(); this.components[key].kill?.();
this.components[key].gameobject = undefined; this.components[key].gameobject = undefined;
delete this.components[key]; delete this.components[key];
} }
this.clear(); this.clear();
this.objects = undefined; this.objects = undefined;
if (typeof this.stop === 'function') this.stop(); if (typeof this.stop === 'function') this.stop();
if (typeof this.die === 'function') this.die(); if (typeof this.die === 'function') this.die();
}, },
up() { return [0,1].rotate(this.angle);}, up() { return [0, 1].rotate(this.angle); },
down() { return [0,-1].rotate(this.angle);}, down() { return [0, -1].rotate(this.angle); },
right() { return [1,0].rotate(this.angle);}, right() { return [1, 0].rotate(this.angle); },
left() { return [-1,0].rotate(this.angle); }, left() { return [-1, 0].rotate(this.angle); },
make_objs(objs) { make_objs(objs) {
for (var prop in objs) { for (var prop in objs) {
@ -659,7 +663,7 @@ var gameobject = {
this.objects[o].obj_descend(fn); this.objects[o].obj_descend(fn);
}, },
} }
Object.mixin(gameobject,gameobject_impl); Object.mixin(gameobject, gameobject_impl);
gameobject.spawn.doc = `Spawn an entity of type 'ur' on this entity. Returns the spawned entity.`; gameobject.spawn.doc = `Spawn an entity of type 'ur' on this entity. Returns the spawned entity.`;
@ -737,14 +741,13 @@ ur {
/* Apply an ur u to an entity e */ /* Apply an ur u to an entity e */
/* u is given as */ /* u is given as */
function apply_ur(u, e) function apply_ur(u, e) {
{
console.log(`applying ur ${u}`); console.log(`applying ur ${u}`);
if (typeof u !== 'string') { if (typeof u !== 'string') {
console.warn("Must give u as a string."); console.warn("Must give u as a string.");
return; return;
} }
var urs = u.split('.'); var urs = u.split('.');
var config = {}; var config = {};
var topur = ur; var topur = ur;
@ -759,7 +762,7 @@ function apply_ur(u, e)
var script = Resources.replstrs(topur.text); var script = Resources.replstrs(topur.text);
eval_env(script, e, topur.text); eval_env(script, e, topur.text);
} }
if (topur.data) { if (topur.data) {
var jss = Resources.replstrs(topur.data); var jss = Resources.replstrs(topur.data);
Object.merge(config, json.decode(jss)); Object.merge(config, json.decode(jss));
@ -769,28 +772,27 @@ function apply_ur(u, e)
Object.merge(e, config); Object.merge(e, config);
} }
function file2fqn(file) function file2fqn(file) {
{
var fqn = file.strip_ext(); var fqn = file.strip_ext();
if (fqn.folder_same_name()) if (fqn.folder_same_name())
fqn = fqn.up_path(); fqn = fqn.up_path();
fqn = fqn.replace('/','.'); fqn = fqn.replace('/', '.');
var topur; var topur;
if (topur = Object.access(ur,fqn)) return topur; if (topur = Object.access(ur, fqn)) return topur;
var fqnlast = fqn.split('.').last(); var fqnlast = fqn.split('.').last();
if (topur = Object.access(ur,fqn.tolast('.'))) { if (topur = Object.access(ur, fqn.tolast('.'))) {
topur[fqnlast] = { topur[fqnlast] = {
name: fqn name: fqn
}; };
ur._list.push(fqn); ur._list.push(fqn);
return Object.access(ur,fqn); return Object.access(ur, fqn);
} }
fqn = fqnlast; fqn = fqnlast;
ur[fqn] = { ur[fqn] = {
name: fqn name: fqn
}; };
@ -821,4 +823,4 @@ Game.loadurs = function() {
return { return {
gameobject gameobject
} }

View file

@ -40,6 +40,27 @@ var GUI = {
gui_img(path,pos, [1.0,1.0], 0.0, false, [0.0,0.0], Color.white); gui_img(path,pos, [1.0,1.0], 0.0, false, [0.0,0.0], Color.white);
return bbox.fromcwh([0,0], wh); return bbox.fromcwh([0,0], wh);
}, },
newmg(img) {
var def = {
path: "",
pos: [0,0],
size:[0,0],
frame: {
x: 0,
y: 0,
w: 1,
h: 1
},
angle: 0,
anchor: [0,0],
color: Color.white,
}
for (var i in def)
img[i] ??= def[i];
gui_newmg
},
input_lmouse_pressed() { input_lmouse_pressed() {
if (GUI.selected) if (GUI.selected)

View file

@ -1,10 +1,14 @@
var audio = {}; var audio = {};
var sound_pref = ['wav', 'flac', 'mp3', 'qoa'];
audio.sound = { audio.sound = {
bus: {}, bus: {},
samplerate() { return cmd(198); }, samplerate() { return cmd(198); },
sounds: [], /* array of loaded sound files */ sounds: [], /* array of loaded sound files */
play(file, bus) { play(file, bus) {
if (!io.exists(file)) { file = Resources.find_sound(file);
if (!file) {
console.error(`Cannot play sound ${file}: does not exist.`); console.error(`Cannot play sound ${file}: does not exist.`);
return; return;
} }

View file

@ -41,14 +41,27 @@ os.prefpath = function() {
var projectfile = ".prosperon/project.json"; var projectfile = ".prosperon/project.json";
var Resources = {}; var Resources = {};
Resources.images = ["png", "jpg", "jpeg", "gif"]; Resources.images = ["png", "gif", "jpg", "jpeg"];
Resources.sounds = ["wav", "mp3", "flac", "qoa"]; Resources.sounds = ["wav", 'flac', 'mp3', "qoa"];
Resources.scripts = "js"; Resources.scripts = "js";
Resources.is_image = function(path) { Resources.is_image = function(path) {
var ext = path.ext(); var ext = path.ext();
return Resources.images.any(x => x === ext); return Resources.images.any(x => x === ext);
} }
function find_ext(file, ext)
{
if (io.exists(file)) return file;
for (var e of ext) {
var nf = `${file}.${e}`;
if (io.exists(nf)) return nf;
}
return;
}
Resources.find_image = function(file) { return find_ext(file,Resources.images); }
Resources.find_sound = function(file) { return find_ext(file,Resources.sounds); }
Resources.is_sound = function(path) { Resources.is_sound = function(path) {
var ext = path.ext(); var ext = path.ext();
return Resources.sounds.any(x => x === ext); return Resources.sounds.any(x => x === ext);

View file

@ -21,6 +21,7 @@ cpSpace *space = NULL;
struct rgba color_white = {255,255,255,255}; struct rgba color_white = {255,255,255,255};
struct rgba color_black = {0,0,0,255}; struct rgba color_black = {0,0,0,255};
struct rgba color_clear = {0,0,0,0};
struct rgba disabled_color = {148,148,148,255}; struct rgba disabled_color = {148,148,148,255};
struct rgba sleep_color = {255,140,228,255}; struct rgba sleep_color = {255,140,228,255};
@ -624,13 +625,13 @@ JSValue arb2js(cpArbiter *arb)
norm.cp = cpArbiterGetNormal(arb); norm.cp = cpArbiterGetNormal(arb);
JSValue obj = JS_NewObject(js); JSValue obj = JS_NewObject(js);
JS_SetPropertyStr(js, obj, "normal", vec2js(norm)); JS_SetPropertyStr(js, obj, "normal", vec22js(norm));
JS_SetPropertyStr(js, obj, "obj", JS_DupValue(js,go2->ref)); JS_SetPropertyStr(js, obj, "obj", JS_DupValue(js,go2->ref));
JS_SetPropertyStr(js, obj, "sensor", JS_NewBool(js, cpShapeGetSensor(shape2))); JS_SetPropertyStr(js, obj, "sensor", JS_NewBool(js, cpShapeGetSensor(shape2)));
HMM_Vec2 srfv; HMM_Vec2 srfv;
srfv.cp = cpArbiterGetSurfaceVelocity(arb); srfv.cp = cpArbiterGetSurfaceVelocity(arb);
JS_SetPropertyStr(js, obj, "velocity", vec2js(srfv)); JS_SetPropertyStr(js, obj, "velocity", vec22js(srfv));
return obj; return obj;
} }

View file

@ -143,19 +143,19 @@ void mesh_add_material(mesh *mesh, cgltf_material *mat)
mesh->bind.fs.images[0] = texture_fromdata(buf->buffer->data, buf->size)->id; mesh->bind.fs.images[0] = texture_fromdata(buf->buffer->data, buf->size)->id;
} else { } else {
// char *imp = seprint("%s/%s", dirname(mesh->model->path), img->uri); // char *imp = seprint("%s/%s", dirname(mesh->model->path), img->uri);
// mesh->bind.fs.images[0] = texture_pullfromfile(imp)->id; // mesh->bind.fs.images[0] = texture_from_file(imp)->id;
// free(imp); // free(imp);
} }
} else } else
mesh->bind.fs.images[0] = texture_pullfromfile("k")->id; mesh->bind.fs.images[0] = texture_from_file("k")->id;
mesh->bind.fs.samplers[0] = sg_make_sampler(&(sg_sampler_desc){}); mesh->bind.fs.samplers[0] = sg_make_sampler(&(sg_sampler_desc){});
/* /*
cgltf_texture *tex; cgltf_texture *tex;
if (tex = mat->normal_texture.texture) if (tex = mat->normal_texture.texture)
mesh->bind.fs.images[1] = texture_pullfromfile(tex->image->uri)->id; mesh->bind.fs.images[1] = texture_from_file(tex->image->uri)->id;
else else
mesh->bind.fs.images[1] = texture_pullfromfile("k")->id;*/ mesh->bind.fs.images[1] = texture_from_file("k")->id;*/
} }
sg_buffer texcoord_floats(float *f, int verts, int comp) sg_buffer texcoord_floats(float *f, int verts, int comp)

View file

@ -19,11 +19,11 @@ struct datastream {
soundbyte *ring; soundbyte *ring;
}; };
struct Texture; struct texture;
void MakeDatastream(); void MakeDatastream();
struct datastream *ds_openvideo(const char *path); struct datastream *ds_openvideo(const char *path);
struct Texture *ds_maketexture(struct datastream *); struct texture *ds_maketexture(struct datastream *);
void ds_advance(struct datastream *ds, double); void ds_advance(struct datastream *ds, double);
void ds_seek(struct datastream *ds, double); void ds_seek(struct datastream *ds, double);
void ds_advanceframes(struct datastream *ds, int frames); void ds_advanceframes(struct datastream *ds, int frames);

View file

@ -180,11 +180,11 @@ struct sFont *MakeFont(const char *fontfile, int height) {
for (unsigned char c = 32; c < 127; c++) { for (unsigned char c = 32; c < 127; c++) {
stbtt_packedchar glyph = glyphs[c - 32]; stbtt_packedchar glyph = glyphs[c - 32];
struct glrect r; struct rect r;
r.s0 = (glyph.x0) / (float)packsize; r.x = (glyph.x0) / (float)packsize;
r.s1 = (glyph.x1) / (float)packsize; r.w = (glyph.x1-glyph.x0) / (float)packsize;
r.t0 = (glyph.y0) / (float)packsize; r.y = (glyph.y0) / (float)packsize;
r.t1 = (glyph.y1) / (float)packsize; r.h = (glyph.y1-glyph.y0) / (float)packsize;
stbtt_GetCodepointHMetrics(&fontinfo, c, &newfont->Characters[c].Advance, &newfont->Characters[c].leftbearing); stbtt_GetCodepointHMetrics(&fontinfo, c, &newfont->Characters[c].Advance, &newfont->Characters[c].leftbearing);
newfont->Characters[c].leftbearing *= newfont->emscale; newfont->Characters[c].leftbearing *= newfont->emscale;
@ -260,10 +260,10 @@ void sdrawCharacter(struct Character c, HMM_Vec2 cursor, float scale, struct rgb
// if (vert.pos.x > frame.l || vert.pos.y > frame.t || (vert.pos.y + vert.wh.y) < frame.b || (vert.pos.x + vert.wh.x) < frame.l) return; // if (vert.pos.x > frame.l || vert.pos.y > frame.t || (vert.pos.y + vert.wh.y) < frame.b || (vert.pos.x + vert.wh.x) < frame.l) return;
vert.uv.u = c.rect.s0*USHRT_MAX; vert.uv.u = c.rect.x*USHRT_MAX;
vert.uv.v = c.rect.t0*USHRT_MAX; vert.uv.v = c.rect.y*USHRT_MAX;
vert.st.u = (c.rect.s1-c.rect.s0)*USHRT_MAX; vert.st.u = c.rect.w*USHRT_MAX;
vert.st.v = (c.rect.t1-c.rect.t0)*USHRT_MAX; vert.st.v = c.rect.h*USHRT_MAX;
vert.color = color; vert.color = color;
memcpy(text_buffer + curchar, &vert, sizeof(struct text_vert)); memcpy(text_buffer + curchar, &vert, sizeof(struct text_vert));

View file

@ -14,7 +14,7 @@ struct Character {
float Bearing[2]; // Offset from baseline to left/top of glyph float Bearing[2]; // Offset from baseline to left/top of glyph
int Advance; // Horizontal offset to advance to next glyph int Advance; // Horizontal offset to advance to next glyph
int leftbearing; int leftbearing;
struct glrect rect; struct rect rect;
}; };
struct sFont { struct sFont {

View file

@ -2,7 +2,7 @@
#define FREELIST_H #define FREELIST_H
/* Given a pointer to a struct, create a free list /* Given a pointer to a struct, create a free list
Struct must have a 'next' field Struct must have an 'unsigned int next' field
*/ */
struct freelistheader struct freelistheader
@ -32,7 +32,7 @@ static inline unsigned int freelist_check(struct freelistheader *h, void *data,
#define freelist_size(p,l) do{p = freelist_make(sizeof(*p),l); for(int i = 0; i < l; i++) { p[i].next = i+1; }}while(0) #define freelist_size(p,l) do{p = freelist_make(sizeof(*p),l); for(int i = 0; i < l; i++) { p[i].next = i+1; }}while(0)
#define freelist_len(p) (freelist_header(p)->len) #define freelist_len(p) (freelist_header(p)->len)
#define freelist_first(p) (freelist_header(p)->first) #define freelist_first(p) (freelist_header(p)->first)
#define freelist_grab(i,p) do{i=freelist_header(p)->first; freelist_header(p)->first = p[i].next; p[i].next = -1;freelist_header(p)->count++;}while(0) #define freelist_grab(p,i) do{i=freelist_header(p)->first; freelist_header(p)->first = p[i].next; p[i].next = -1;freelist_header(p)->count++;}while(0)
#define freelist_kill(p,i) do{p[i].next = freelist_first(p);freelist_first(p)=i;freelist_header(p)->count--;}while(0) #define freelist_kill(p,i) do{p[i].next = freelist_first(p);freelist_first(p)=i;freelist_header(p)->count--;}while(0)
#define freelist_free(p) (free(freelist_header(p))) #define freelist_free(p) (free(freelist_header(p)))
#define freelist_count(p) (freelist_header(p)->count) #define freelist_count(p) (freelist_header(p)->count)

View file

@ -67,10 +67,11 @@ static JSValue TYPE##2js(TYPE *n) { \
JS_SetOpaque(j,n);\ JS_SetOpaque(j,n);\
return j; }\ return j; }\
QJSCLASS(gameobject) QJSCLASS(gameobject)
QJSCLASS(emitter) QJSCLASS(emitter)
QJSCLASS(dsp_node) QJSCLASS(dsp_node)
QJSCLASS(texture)
QJSCLASS(sprite)
QJSCLASS(warp_gravity) QJSCLASS(warp_gravity)
QJSCLASS(warp_damp) QJSCLASS(warp_damp)
QJSCLASS(material) QJSCLASS(material)
@ -107,6 +108,7 @@ JS_SetPropertyStr(js, globalThis, #NAME, NAME); \
JS_NewClassID(&js_##TYPE##_id);\ JS_NewClassID(&js_##TYPE##_id);\
JS_NewClass(JS_GetRuntime(js), js_##TYPE##_id, &js_##TYPE##_class);\ JS_NewClass(JS_GetRuntime(js), js_##TYPE##_id, &js_##TYPE##_class);\
/* Defines a class and uses its function list as its prototype */
#define QJSCLASSPREP_FUNCS(TYPE) \ #define QJSCLASSPREP_FUNCS(TYPE) \
QJSCLASSPREP(TYPE); \ QJSCLASSPREP(TYPE); \
JSValue TYPE##_proto = JS_NewObject(js); \ JSValue TYPE##_proto = JS_NewObject(js); \
@ -215,7 +217,6 @@ void *js2ptr(JSValue v) { return JS_GetOpaque(v,js_ptr_id); }
JSValue number2js(double g) { JSValue number2js(double g) {
return JS_NewFloat64(js,g); return JS_NewFloat64(js,g);
} }
struct sprite *js2sprite(JSValue v) { return id2sprite(js2int(v)); }
JSValue ptr2js(void *ptr) { JSValue ptr2js(void *ptr) {
JSValue obj = JS_NewObjectClass(js, js_ptr_id); JSValue obj = JS_NewObjectClass(js, js_ptr_id);
@ -231,15 +232,6 @@ double js_get_prop_number(JSValue v, const char *p) {
return num; return num;
} }
struct glrect js2glrect(JSValue v) {
struct glrect rect;
rect.s0 = js_get_prop_number(v, "s0");
rect.s1 = js_get_prop_number(v, "s1");
rect.t0 = js_get_prop_number(v, "t0");
rect.t1 = js_get_prop_number(v, "t1");
return rect;
}
JSValue js_arridx(JSValue v, int idx) { return js_getpropidx(v, idx); } JSValue js_arridx(JSValue v, int idx) { return js_getpropidx(v, idx); }
int js_arrlen(JSValue v) { int js_arrlen(JSValue v) {
@ -406,6 +398,14 @@ HMM_Vec2 js2vec2(JSValue v)
return v2; return v2;
} }
JSValue vec22js(HMM_Vec2 v)
{
JSValue array = JS_NewArray(js);
js_setprop_num(array,0,number2js(v.x));
js_setprop_num(array,1,number2js(v.y));
return array;
}
HMM_Vec3 js2vec3(JSValue v) HMM_Vec3 js2vec3(JSValue v)
{ {
HMM_Vec3 v3; HMM_Vec3 v3;
@ -474,17 +474,10 @@ void vec2float(HMM_Vec2 v, float *f) {
f[1] = v.y; f[1] = v.y;
} }
JSValue vec2js(HMM_Vec2 v) {
JSValue array = JS_NewArray(js);
js_setprop_num(array,0,number2js(v.x));
js_setprop_num(array,1,number2js(v.y));
return array;
}
JSValue vecarr2js(HMM_Vec2 *points, int n) { JSValue vecarr2js(HMM_Vec2 *points, int n) {
JSValue array = JS_NewArray(js); JSValue array = JS_NewArray(js);
for (int i = 0; i < n; i++) for (int i = 0; i < n; i++)
js_setprop_num(array,i,vec2js(points[i])); js_setprop_num(array,i,vec22js(points[i]));
return array; return array;
} }
@ -695,11 +688,6 @@ JSValue duk_cmd(JSContext *js, JSValueConst this, int argc, JSValueConst *argv)
ret = JS_NewInt64(js, script_dofile(str)); ret = JS_NewInt64(js, script_dofile(str));
break; break;
case 1:
YughWarn("Do not set pawns here anymore; Do it entirely in script.");
// set_pawn(js2ptrduk_get_heapptr(duk, 1));
break;
case 3: case 3:
set_timescale(js2number(argv[1])); set_timescale(js2number(argv[1]));
break; break;
@ -715,34 +703,7 @@ JSValue duk_cmd(JSContext *js, JSValueConst this, int argc, JSValueConst *argv)
case 7: case 7:
physMS = js2number(argv[1]); physMS = js2number(argv[1]);
break; break;
case 8:
phys2d_set_gravity(js2vec2(argv[1]));
break;
case 9:
sprite_delete(js2int(argv[1]));
break;
case 10:
YughWarn("Pawns are handled in script only now.");
break;
case 11:
str = JS_ToCString(js, argv[1]);
ret = JS_NewInt64(js, file_mod_secs(str));
break;
case 12:
str = JS_ToCString(js, argv[2]);
sprite_loadtex(id2sprite(js2int(argv[1])), str, js2glrect(argv[3]));
break;
case 13:
str = JS_ToCString(js, argv[1]);
str2 = JS_ToCString(js, argv[2]);
play_song(str, str2);
break;
case 15: case 15:
gameobject_draw_debug(js2gameobject(argv[1])); gameobject_draw_debug(js2gameobject(argv[1]));
break; break;
@ -760,10 +721,6 @@ JSValue duk_cmd(JSContext *js, JSValueConst this, int argc, JSValueConst *argv)
shape_set_sensor(js2ptr(argv[1]), JS_ToBool(js, argv[2])); shape_set_sensor(js2ptr(argv[1]), JS_ToBool(js, argv[2]));
break; break;
case 20:
sprite_enabled(js2int(argv[1]), JS_ToBool(js, argv[2]));
break;
case 21: case 21:
ret = JS_NewBool(js, shape_get_sensor(js2ptr(argv[1]))); ret = JS_NewBool(js, shape_get_sensor(js2ptr(argv[1])));
break; break;
@ -776,79 +733,12 @@ JSValue duk_cmd(JSContext *js, JSValueConst this, int argc, JSValueConst *argv)
ret = JS_NewBool(js, shape_is_enabled(js2ptr(argv[1]))); ret = JS_NewBool(js, shape_is_enabled(js2ptr(argv[1])));
break; break;
case 24:
timer_pause(js2timer(argv[1]));
break;
case 25:
timer_stop(js2timer(argv[1]));
break;
case 26:
timer_start(js2timer(argv[1]));
break;
case 27:
timer_remove(js2int(argv[1]));
break;
case 28:
timerr_settime(js2timer(argv[1]), js2number(argv[2]));
break;
case 29:
ret = JS_NewFloat64(js, js2timer(argv[1])->interval);
break;
case 31:
free(js2ptr(argv[1]));
break;
case 32:
ret = JS_NewFloat64(js, js2timer(argv[1])->remain_time);
break;
case 33:
ret = JS_NewBool(js, js2timer(argv[1])->on);
break;
case 34:
ret = JS_NewBool(js, js2timer(argv[1])->repeat);
break;
case 35:
js2timer(argv[1])->repeat = JS_ToBool(js, argv[2]);
break;
case 36: case 36:
js2gameobject(argv[1])->scale.XY = js2vec2(argv[2]); js2gameobject(argv[1])->scale.XY = js2vec2(argv[2]);
gameobject_apply(js2gameobject(argv[1])); gameobject_apply(js2gameobject(argv[1]));
cpSpaceReindexShapesForBody(space, js2gameobject(argv[1])->body); cpSpaceReindexShapesForBody(space, js2gameobject(argv[1])->body);
break; break;
case 37:
if (!id2sprite(js2int(argv[1]))) break;
id2sprite(js2int(argv[1]))->t.pos = js2vec2(argv[2]);
break;
case 40:
js2gameobject(argv[1])->filter.categories = js2bitmask(argv[2]);
gameobject_apply(js2gameobject(argv[1]));
break;
case 41:
js2gameobject(argv[1])->filter.mask = js2bitmask(argv[2]);
gameobject_apply(js2gameobject(argv[1]));
break;
case 42:
ret = bitmask2js(js2gameobject(argv[1])->filter.categories);
break;
case 43:
ret = bitmask2js(js2gameobject(argv[1])->filter.mask);
break;
case 44: case 44:
go = pos2gameobject(js2vec2(argv[1]), js2number(argv[2])); go = pos2gameobject(js2vec2(argv[1]), js2number(argv[2]));
ret = go ? JS_DupValue(js,go->ref) : JS_UNDEFINED; ret = go ? JS_DupValue(js,go->ref) : JS_UNDEFINED;
@ -884,10 +774,6 @@ JSValue duk_cmd(JSContext *js, JSValueConst this, int argc, JSValueConst *argv)
draw_box(js2vec2(argv[1]), js2vec2(argv[2]), js2color(argv[3])); draw_box(js2vec2(argv[1]), js2vec2(argv[2]), js2color(argv[3]));
break; break;
case 54:
gameobject_apply(js2gameobject(argv[1]));
break;
case 59: case 59:
v1 = js2cpvec2arr(argv[2]); v1 = js2cpvec2arr(argv[2]);
ret = JS_NewInt64(js, point2segindex(js2vec2(argv[1]), v1, js2number(argv[3]))); ret = JS_NewInt64(js, point2segindex(js2vec2(argv[1]), v1, js2number(argv[3])));
@ -901,17 +787,9 @@ JSValue duk_cmd(JSContext *js, JSValueConst this, int argc, JSValueConst *argv)
add_zoom(js2number(argv[1])); add_zoom(js2number(argv[1]));
break; break;
case 63:
set_cam_body(NULL);
break;
case 64: case 64:
str = JS_ToCString(js, argv[1]); str = JS_ToCString(js, argv[1]);
ret = vec2js(tex_get_dimensions(texture_pullfromfile(str))); ret = vec22js(tex_get_dimensions(texture_from_file(str)));
break;
case 66:
ret = strarr2js(ls(","));
break; break;
case 67: case 67:
@ -923,37 +801,17 @@ JSValue duk_cmd(JSContext *js, JSValueConst this, int argc, JSValueConst *argv)
break; break;
case 70: case 70:
ret = vec2js(world2go(js2gameobject(argv[1]), js2vec2(argv[2]))); ret = vec22js(world2go(js2gameobject(argv[1]), js2vec2(argv[2])));
break; break;
case 71: case 71:
ret = vec2js(go2world(js2gameobject(argv[1]), js2vec2(argv[2]))); ret = vec22js(go2world(js2gameobject(argv[1]), js2vec2(argv[2])));
break;
case 72:
ret = vec2js((HMM_Vec2)cpSpaceGetGravity(space));
break;
case 73:
cpSpaceSetDamping(space, js2number(argv[1]));
break;
case 74:
ret = JS_NewFloat64(js, cpSpaceGetDamping(space));
break;
case 75:
js2gameobject(argv[1])->layer = js2int(argv[2]);
break; break;
case 76: case 76:
set_cat_mask(js2int(argv[1]), js2bitmask(argv[2])); set_cat_mask(js2int(argv[1]), js2bitmask(argv[2]));
break; break;
case 77:
ret = int2js(js2gameobject(argv[1])->layer);
break;
case 79: case 79:
ret = JS_NewBool(js, phys_stepping()); ret = JS_NewBool(js, phys_stepping());
break; break;
@ -978,7 +836,7 @@ JSValue duk_cmd(JSContext *js, JSValueConst this, int argc, JSValueConst *argv)
break; break;
case 85: case 85:
ret = vec2js(HMM_ProjV2(js2vec2(argv[1]), js2vec2(argv[2]))); ret = vec22js(HMM_ProjV2(js2vec2(argv[1]), js2vec2(argv[2])));
break; break;
case 86: case 86:
@ -1010,36 +868,13 @@ JSValue duk_cmd(JSContext *js, JSValueConst this, int argc, JSValueConst *argv)
ret = int2js(logLevel); ret = int2js(logLevel);
break; break;
case 96:
id2sprite(js2int(argv[1]))->color = js2color(argv[2]);
break;
case 97: case 97:
str = js2str(argv[1]); str = js2str(argv[1]);
cursor_img(str); cursor_img(str);
break; break;
case 103: case 103:
ret = vec2js(js2gameobject(argv[1])->scale.XY); ret = vec22js(js2gameobject(argv[1])->scale.XY);
break; break;
case 106:
js2gameobject(argv[1])->e = js2number(argv[2]);
break;
case 107:
ret = number2js(js2gameobject(argv[1])->e);
break;
case 108:
js2gameobject(argv[1])->f = js2number(argv[2]);
break;
case 109:
ret = number2js(js2gameobject(argv[1])->f);
break;
case 110:
ret = number2js(js2gameobject(argv[1])->e);
break;
case 111:
ret = vec2js(js2sprite(argv[1])->t.pos);
break;
case 112: case 112:
ret = number2js(((struct phys2d_edge*)js2ptr(argv[1]))->thickness); ret = number2js(((struct phys2d_edge*)js2ptr(argv[1]))->thickness);
@ -1049,18 +884,10 @@ JSValue duk_cmd(JSContext *js, JSValueConst this, int argc, JSValueConst *argv)
js2gameobject(argv[1])->ref = argv[2]; js2gameobject(argv[1])->ref = argv[2];
break; break;
case 114:
ret = bool2js(js2sprite(argv[1])->enabled);
break;
case 115: case 115:
draw_circle(js2vec2(argv[1]), js2number(argv[2]), js2number(argv[2]), js2color(argv[3]), -1); draw_circle(js2vec2(argv[1]), js2number(argv[2]), js2number(argv[2]), js2color(argv[3]), -1);
break; break;
case 116:
ret = str2js(tex_get_path(js2sprite(argv[1])->tex));
break;
case 117: case 117:
str = JS_ToCString(js, argv[1]); str = JS_ToCString(js, argv[1]);
ret = script_runfile(str); ret = script_runfile(str);
@ -1141,16 +968,11 @@ JSValue duk_cmd(JSContext *js, JSValueConst this, int argc, JSValueConst *argv)
break; break;
case 136: case 136:
ret = vec2js(world2screen(js2vec2(argv[1]))); ret = vec22js(world2screen(js2vec2(argv[1])));
break; break;
case 137: case 137:
ret = vec2js(screen2world(js2vec2(argv[1]))); ret = vec22js(screen2world(js2vec2(argv[1])));
break;
case 138:
str = JS_ToCString(js, argv[1]);
ret = JS_NewInt64(js, jso_file(str));
break; break;
case 139: case 139:
@ -1171,13 +993,6 @@ JSValue duk_cmd(JSContext *js, JSValueConst this, int argc, JSValueConst *argv)
console_print(str); console_print(str);
break; break;
case 143:
str = JS_ToCString(js, argv[1]);
if (!getenv(str)) ret = JS_UNDEFINED;
else
ret = str2js(getenv(str));
break;
case 145: case 145:
if (js2bool(argv[1])) window_makefullscreen(&mainwin); if (js2bool(argv[1])) window_makefullscreen(&mainwin);
else window_unfullscreen(&mainwin); else window_unfullscreen(&mainwin);
@ -1186,12 +1001,6 @@ JSValue duk_cmd(JSContext *js, JSValueConst this, int argc, JSValueConst *argv)
log_clear(); log_clear();
break; break;
case 147:
exit(js2int(argv[1]));
break;
case 148:
ret = color2js(id2sprite(js2int(argv[1]))->color);
break;
case 149: case 149:
((struct drawmodel *)js2ptr(argv[1]))->model = GetExistingModel(js2str(argv[2])); ((struct drawmodel *)js2ptr(argv[1]))->model = GetExistingModel(js2str(argv[2]));
break; break;
@ -1200,58 +1009,18 @@ JSValue duk_cmd(JSContext *js, JSValueConst this, int argc, JSValueConst *argv)
draw_drawmodel(js2ptr(argv[1])); draw_drawmodel(js2ptr(argv[1]));
break; break;
case 151:
js2gameobject(argv[1])->maxvelocity = js2number(argv[2]);
break;
case 152:
ret = number2js(js2gameobject(argv[1])->maxvelocity);
break;
case 153: case 153:
cpBodySetTorque(js2gameobject(argv[1])->body, js2number(argv[2])); cpBodySetTorque(js2gameobject(argv[1])->body, js2number(argv[2]));
break; break;
case 154:
js2gameobject(argv[1])->maxangularvelocity = js2number(argv[2]);
break;
case 155:
ret = number2js(js2gameobject(argv[1])->maxangularvelocity);
break;
case 156:
js2gameobject(argv[1])->damping = js2number(argv[2]);
break;
case 157:
ret = number2js(js2gameobject(argv[1])->damping);
break;
case 160: case 160:
ret = vec2js(mat_t_dir(t_world2go(js2gameobject(argv[1])), js2vec2(argv[2]))); ret = vec22js(mat_t_dir(t_world2go(js2gameobject(argv[1])), js2vec2(argv[2])));
break; break;
case 161: case 161:
ret = vec2js(mat_t_dir(t_go2world(js2gameobject(argv[1])), js2vec2(argv[2]))); ret = vec22js(mat_t_dir(t_go2world(js2gameobject(argv[1])), js2vec2(argv[2])));
break;
case 162:
str = JS_ToCString(js, argv[1]);
ret = int2js(remove(str));
break; break;
case 164:
unplug_node(js2ptr(argv[1]));
break;
case 168:
js2gameobject(argv[1])->timescale = js2number(argv[2]);
break;
case 169:
ret = number2js(js2gameobject(argv[1])->timescale);
break;
case 170:
id2sprite(js2int(argv[1]))->emissive = js2color(argv[2]);
break;
case 171:
ret = number2js(js2gameobject(argv[1])->drawlayer);
break;
case 172:
js2gameobject(argv[1])->drawlayer = js2number(argv[2]);
break;
case 173: case 173:
str = js2str(argv[1]); str = js2str(argv[1]);
capture_screen(js2number(argv[2]), js2number(argv[3]), js2number(argv[4]), js2number(argv[5]), str); capture_screen(js2number(argv[2]), js2number(argv[3]), js2number(argv[4]), js2number(argv[5]), str);
@ -1340,24 +1109,7 @@ JSValue duk_cmd(JSContext *js, JSValueConst this, int argc, JSValueConst *argv)
case 214: case 214:
ret = int2js(go_count()); ret = int2js(go_count());
break; break;
case 215:
ret = vec2js(js2sprite(argv[1])->t.scale);
break;
case 216:
js2sprite(argv[1])->t.scale = js2vec2(argv[2]);
break;
case 217:
ret = number2js(js2sprite(argv[1])->t.angle);
break;
case 218:
js2sprite(argv[1])->t.angle = js2number(argv[2]);
break;
case 219:
js2sprite(argv[1])->drawmode = js2number(argv[2]);
break;
case 220:
ret = number2js(js2sprite(argv[1])->drawmode);
break;
case 221: case 221:
ret = constraint2js(constraint_make(cpPivotJointNew(js2gameobject(argv[1])->body, js2gameobject(argv[2])->body,js2vec2(argv[3]).cp))); ret = constraint2js(constraint_make(cpPivotJointNew(js2gameobject(argv[1])->body, js2gameobject(argv[2])->body,js2vec2(argv[3]).cp)));
break; break;
@ -1392,12 +1144,7 @@ JSValue duk_cmd(JSContext *js, JSValueConst this, int argc, JSValueConst *argv)
case 231: case 231:
ret = constraint2js(constraint_make(cpSimpleMotorNew(js2body(argv[1]), js2body(argv[2]), js2number(argv[3])))); ret = constraint2js(constraint_make(cpSimpleMotorNew(js2body(argv[1]), js2body(argv[2]), js2number(argv[3]))));
break; break;
case 232:
ret = number2js(js2sprite(argv[1])->parallax);
break;
case 233:
js2sprite(argv[1])->parallax = js2number(argv[2]);
break;
case 234: case 234:
ret = emitter2js(make_emitter()); ret = emitter2js(make_emitter());
break; break;
@ -1406,17 +1153,12 @@ JSValue duk_cmd(JSContext *js, JSValueConst this, int argc, JSValueConst *argv)
break; break;
case 249: case 249:
str = JS_ToCString(js,argv[2]); str = JS_ToCString(js,argv[2]);
js2emitter(argv[1])->texture = texture_pullfromfile(str); js2emitter(argv[1])->texture = texture_from_file(str);
break; break;
case 250: case 250:
sapp_show_keyboard(js2bool(argv[1])); sapp_show_keyboard(js2bool(argv[1]));
break; break;
case 251:
js2gameobject(argv[1])->warp_filter = js2bitmask(argv[2]);
break;
case 252:
ret = bitmask2js(js2gameobject(argv[1])->warp_filter);
break;
case 253: case 253:
ret = warp_gravity2js(warp_gravity_make()); ret = warp_gravity2js(warp_gravity_make());
break; break;
@ -1460,7 +1202,7 @@ JSValue duk_cmd(JSContext *js, JSValueConst this, int argc, JSValueConst *argv)
aspect_mode = js2int(argv[1]); aspect_mode = js2int(argv[1]);
break; break;
case 265: case 265:
ret = vec2js((HMM_Vec2){mainwin.width, mainwin.height}); ret = vec22js((HMM_Vec2){mainwin.width, mainwin.height});
break; break;
case 266: case 266:
mainwin.width = js2number(argv[1]); mainwin.width = js2number(argv[1]);
@ -1468,6 +1210,16 @@ JSValue duk_cmd(JSContext *js, JSValueConst this, int argc, JSValueConst *argv)
case 267: case 267:
mainwin.height = js2number(argv[1]); mainwin.height = js2number(argv[1]);
break; break;
case 268:
if (js2bool(argv[1]))
ret = JS_GetClassProto(js, js_sprite_id);
else
ret = sprite2js(sprite_make());
break;
case 269:
str = js2str(argv[1]);
ret = texture2js(texture_from_file(str));
break;
} }
if (str) JS_FreeCString(js, str); if (str) JS_FreeCString(js, str);
@ -1526,10 +1278,6 @@ JSValue duk_sys_cmd(JSContext *js, JSValueConst this, int argc, JSValueConst *ar
cpSpaceReindexStatic(space); cpSpaceReindexStatic(space);
break; break;
case 2:
sim_pause();
break;
case 3: case 3:
sim_pause(); sim_pause();
break; break;
@ -1613,14 +1361,6 @@ JSValue duk_set_body(JSContext *js, JSValueConst this, int argc, JSValueConst *a
cpBodySetVelocity(go->body, js2vec2(argv[2]).cp); cpBodySetVelocity(go->body, js2vec2(argv[2]).cp);
return JS_UNDEFINED; return JS_UNDEFINED;
case 10:
go->e = fmax(js2number(argv[2]), 0);
break;
case 11:
go->f = fmax(js2number(argv[2]), 0);
break;
case 12: case 12:
cpBodyApplyForceAtWorldPoint(go->body, js2vec2(argv[2]).cp, cpBodyGetPosition(go->body)); cpBodyApplyForceAtWorldPoint(go->body, js2vec2(argv[2]).cp, cpBodyGetPosition(go->body));
return JS_UNDEFINED; return JS_UNDEFINED;
@ -1649,13 +1389,13 @@ JSValue duk_q_body(JSContext *js, JSValueConst this, int argc, JSValueConst *arg
return JS_NewInt64(js, cpBodyGetType(go->body)); return JS_NewInt64(js, cpBodyGetType(go->body));
case 1: case 1:
return vec2js((HMM_Vec2)cpBodyGetPosition(go->body)); return vec22js((HMM_Vec2)cpBodyGetPosition(go->body));
case 2: case 2:
return JS_NewFloat64(js, cpBodyGetAngle(go->body)); return JS_NewFloat64(js, cpBodyGetAngle(go->body));
case 3: case 3:
return vec2js((HMM_Vec2)cpBodyGetVelocity(go->body)); return vec22js((HMM_Vec2)cpBodyGetVelocity(go->body));
case 4: case 4:
return JS_NewFloat64(js, cpBodyGetAngularVelocity(go->body)); return JS_NewFloat64(js, cpBodyGetAngularVelocity(go->body));
@ -1668,21 +1408,11 @@ JSValue duk_q_body(JSContext *js, JSValueConst this, int argc, JSValueConst *arg
case 7: case 7:
return JS_NewBool(js, phys2d_in_air(go->body)); return JS_NewBool(js, phys2d_in_air(go->body));
case 8:
gameobject_free(go);
break;
} }
return JS_UNDEFINED; return JS_UNDEFINED;
} }
JSValue duk_make_sprite(JSContext *js, JSValueConst this, int argc, JSValueConst *argv) {
JSValue sprite = JS_NewObject(js);
js_setprop_str(sprite,"id",JS_NewInt64(js, make_sprite(js2gameobject(argv[0]))));
return sprite;
}
JSValue duk_make_circle2d(JSContext *js, JSValueConst this, int argc, JSValueConst *argv) { JSValue duk_make_circle2d(JSContext *js, JSValueConst this, int argc, JSValueConst *argv) {
gameobject *go = js2gameobject(argv[0]); gameobject *go = js2gameobject(argv[0]);
@ -1722,7 +1452,7 @@ JSValue duk_cmd_circle2d(JSContext *js, JSValueConst this, int argc, JSValueCons
return number2js(circle->radius); return number2js(circle->radius);
case 3: case 3:
return vec2js(circle->offset); return vec22js(circle->offset);
} }
phys2d_shape_apply(&circle->shape); phys2d_shape_apply(&circle->shape);
@ -2085,6 +1815,85 @@ static const JSCFunctionListEntry js_sound_funcs[] = {
CGETSET_ADD(sound, hook) CGETSET_ADD(sound, hook)
}; };
#define GETSET_PAIR_BODY(ID, ENTRY, TYPE) \
JSValue ID##_set_##ENTRY (JSContext *js, JSValue this, JSValue val) { \
js2##ID (this)->ENTRY = js2##TYPE (val); \
ID##_apply(js2##ID (this)); \
return JS_UNDEFINED; \
} \
\
JSValue ID##_get_##ENTRY (JSContext *js, JSValue this) { \
return TYPE##2js(js2##ID (this)->ENTRY); \
} \
GETSET_PAIR_BODY(gameobject, f, number)
GETSET_PAIR_BODY(gameobject, e, number)
GETSET_PAIR_BODY(gameobject, mass, number)
GETSET_PAIR(gameobject, damping, number)
GETSET_PAIR(gameobject, timescale, number)
GETSET_PAIR(gameobject, maxvelocity, number)
GETSET_PAIR(gameobject, maxangularvelocity, number)
GETSET_PAIR_BODY(gameobject, layer, number)
GETSET_PAIR(gameobject, warp_filter, bitmask)
GETSET_PAIR(gameobject, scale, vec3)
GETSET_PAIR(gameobject, drawlayer, number)
static const JSCFunctionListEntry js_gameobject_funcs[] = {
CGETSET_ADD(gameobject, f),
CGETSET_ADD(gameobject, e),
CGETSET_ADD(gameobject,mass),
CGETSET_ADD(gameobject,damping),
CGETSET_ADD(gameobject,timescale),
CGETSET_ADD(gameobject,maxvelocity),
CGETSET_ADD(gameobject,maxangularvelocity),
CGETSET_ADD(gameobject,layer),
CGETSET_ADD(gameobject,warp_filter),
CGETSET_ADD(gameobject,scale),
CGETSET_ADD(gameobject,drawlayer)
};
GETSET_PAIR(sprite, color, color)
GETSET_PAIR(sprite, emissive, color)
GETSET_PAIR(sprite, enabled, bool)
GETSET_PAIR(sprite, parallax, number)
GETSET_PAIR(sprite, tex, texture)
GETSET_PAIR(sprite, pos, vec2)
GETSET_PAIR(sprite, scale, vec2)
GETSET_PAIR(sprite, angle, number)
GETSET_PAIR(sprite, frame, rect)
GETSET_PAIR(sprite,go,gameobject)
static const JSCFunctionListEntry js_sprite_funcs[] = {
CGETSET_ADD(sprite,pos),
CGETSET_ADD(sprite,scale),
CGETSET_ADD(sprite,angle),
CGETSET_ADD(sprite,tex),
CGETSET_ADD(sprite,color),
CGETSET_ADD(sprite,emissive),
CGETSET_ADD(sprite,enabled),
CGETSET_ADD(sprite,parallax),
CGETSET_ADD(sprite,frame),
CGETSET_ADD(sprite,go)
};
#define GETFN(ID, ENTRY, TYPE) \
JSValue ID##_get_##ENTRY (JSContext *js, JSValue this, JSValue val) {\
return TYPE##2js(js2##ID (this)->ENTRY); \
} \
GETFN(texture,width,number)
GETFN(texture,height,number)
JSValue texture_get_path(JSContext *js, JSValue this, JSValue val)
{
return str2js(tex_get_path(js2texture(this)));
}
static const JSCFunctionListEntry js_texture_funcs[] = {
MIST_CFUNC_DEF("width", 0, texture_get_width),
MIST_CFUNC_DEF("height", 0, texture_get_height),
MIST_CFUNC_DEF("path", 0, texture_get_path)
};
JSValue constraint_set_max_force (JSContext *js, JSValue this, JSValue val) { JSValue constraint_set_max_force (JSContext *js, JSValue this, JSValue val) {
cpConstraintSetMaxForce(js2constraint(this)->c, js2number(val)); cpConstraintSetMaxForce(js2constraint(this)->c, js2number(val));
return JS_UNDEFINED; return JS_UNDEFINED;
@ -2165,11 +1974,6 @@ JSValue duk_cmd_points(JSContext *js, JSValueConst this, int argc, JSValueConst
const char *STRTEST = "TEST STRING"; const char *STRTEST = "TEST STRING";
JSValue duk_performance_js2num(JSContext *js, JSValueConst this, int argc, JSValueConst *argv)
{
}
JSValue duk_performance(JSContext *js, JSValueConst this, int argc, JSValueConst *argv) JSValue duk_performance(JSContext *js, JSValueConst this, int argc, JSValueConst *argv)
{ {
int cmd = js2int(argv[0]); int cmd = js2int(argv[0]);
@ -2242,7 +2046,6 @@ void ffi_load() {
DUK_FUNC(set_body, 3) DUK_FUNC(set_body, 3)
DUK_FUNC(q_body, 2) DUK_FUNC(q_body, 2)
DUK_FUNC(sys_cmd, 1) DUK_FUNC(sys_cmd, 1)
DUK_FUNC(make_sprite, 1)
DUK_FUNC(spline_cmd, 6) DUK_FUNC(spline_cmd, 6)
DUK_FUNC(make_circle2d, 1) DUK_FUNC(make_circle2d, 1)
DUK_FUNC(cmd_circle2d, 6) DUK_FUNC(cmd_circle2d, 6)
@ -2265,7 +2068,7 @@ void ffi_load() {
JS_FreeValue(js,globalThis); JS_FreeValue(js,globalThis);
QJSCLASSPREP(ptr); QJSCLASSPREP(ptr);
QJSCLASSPREP(gameobject); QJSCLASSPREP_FUNCS(gameobject);
QJSCLASSPREP_FUNCS(dsp_node); QJSCLASSPREP_FUNCS(dsp_node);
sound_proto = JS_NewObject(js); sound_proto = JS_NewObject(js);
@ -2275,7 +2078,8 @@ void ffi_load() {
QJSCLASSPREP_FUNCS(emitter); QJSCLASSPREP_FUNCS(emitter);
QJSCLASSPREP_FUNCS(warp_gravity); QJSCLASSPREP_FUNCS(warp_gravity);
QJSCLASSPREP_FUNCS(warp_damp); QJSCLASSPREP_FUNCS(warp_damp);
QJSCLASSPREP_FUNCS(sprite);
QJSCLASSPREP_FUNCS(texture);
QJSCLASSPREP_FUNCS(constraint); QJSCLASSPREP_FUNCS(constraint);
QJSGLOBALCLASS(os); QJSGLOBALCLASS(os);

View file

@ -8,7 +8,7 @@
void ffi_load(); void ffi_load();
void ffi_stop(); void ffi_stop();
JSValue vec2js(HMM_Vec2 v); JSValue vec22js(HMM_Vec2 v);
HMM_Vec2 js2vec2(JSValue v); HMM_Vec2 js2vec2(JSValue v);
JSValue bitmask2js(cpBitmask mask); JSValue bitmask2js(cpBitmask mask);

View file

@ -99,7 +99,7 @@ emitter *make_emitter() {
sampler_add(&e->color, 0, (HMM_Vec4){1,1,1,1}); sampler_add(&e->color, 0, (HMM_Vec4){1,1,1,1});
e->scale = 1; e->scale = 1;
e->speed = 20; e->speed = 20;
e->texture = texture_pullfromfile("glass_chunk2.gif"); e->texture = texture_from_file("glass_chunk2.gif");
arrpush(emitters,e); arrpush(emitters,e);
return e; return e;
} }

View file

@ -21,6 +21,7 @@
extern struct rgba color_white; extern struct rgba color_white;
extern struct rgba color_black; extern struct rgba color_black;
extern struct rgba color_clear;
extern int renderMode; extern int renderMode;
@ -105,16 +106,9 @@ struct boundingbox {
}; };
struct rect { struct rect {
float h, w, x, y; float x,y,w,h;
};
/* Normalized S,T coordinates for rendering */
struct glrect {
float s0;
float s1;
float t0;
float t1;
}; };
typedef struct rect rect;
struct boundingbox cwh2bb(HMM_Vec2 c, HMM_Vec2 wh); struct boundingbox cwh2bb(HMM_Vec2 c, HMM_Vec2 wh);
float *rgba2floats(float *r, struct rgba c); float *rgba2floats(float *r, struct rgba c);

View file

@ -295,7 +295,7 @@ void callee_int(struct callee c, int i) {
} }
void callee_vec2(struct callee c, HMM_Vec2 vec) { void callee_vec2(struct callee c, HMM_Vec2 vec) {
JSValue v = vec2js(vec); JSValue v = vec22js(vec);
js_callee_exec(&c, 1, &v); js_callee_exec(&c, 1, &v);
JS_FreeValue(js, v); JS_FreeValue(js, v);
} }

View file

@ -1,25 +1,17 @@
#include "sprite.h" #include "sprite.h"
#include "datastream.h"
#include "font.h"
#include "gameobject.h" #include "gameobject.h"
#include "log.h" #include "log.h"
#include "render.h" #include "render.h"
#include "stb_ds.h" #include "stb_ds.h"
#include "texture.h" #include "texture.h"
#include "timer.h" #include "timer.h"
#include <string.h>
#include <ctype.h>
#include <limits.h>
#include "HandmadeMath.h" #include "HandmadeMath.h"
#include "freelist.h"
#include "sprite.sglsl.h" #include "sprite.sglsl.h"
#include "9slice.sglsl.h" #include "9slice.sglsl.h"
struct TextureOptions TEX_SPRITE = {1}; static sprite **sprites = NULL;
static struct sprite *sprites = NULL;
static sg_shader shader_sprite; static sg_shader shader_sprite;
static sg_pipeline pip_sprite; static sg_pipeline pip_sprite;
@ -51,89 +43,70 @@ struct slice9_vert {
struct rgba color; struct rgba color;
}; };
int make_sprite(gameobject *go) { sprite *sprite_make()
struct sprite sprite = { {
.t = t2d_unit, sprite *sp = calloc(sizeof(*sp), 1);
.color = color_white, sp->pos = (HMM_Vec2){0,0};
.emissive = {0,0,0,0}, sp->scale = (HMM_Vec2){1,1};
.tex = texture_pullfromfile(NULL), sp->angle = 0;
.go = go, sp->color = color_white;
.next = -1, sp->emissive = color_clear;
.enabled = 1,
.drawmode = DRAW_SIMPLE,
.parallax = 1
};
int id;
freelist_grab(id, sprites);
sprites[id] = sprite;
return id;
}
void sprite_delete(int id) {
struct sprite *sp = id2sprite(id);
sp->go = NULL; sp->go = NULL;
sp->enabled = 0; sp->tex = texture_from_file(NULL);
freelist_kill(sprites,id); sp->frame = ST_UNIT;
sp->drawmode = DRAW_SIMPLE;
sp->enabled = 1;
sp->parallax = 1;
arrpush(sprites,sp);
return sp;
} }
void sprite_enabled(int id, int e) { sprites[id].enabled = e; } void sprite_free(sprite *sprite)
{
struct sprite *id2sprite(int id) { YughWarn("Freeing sprite %p.", sprite);
if (id < 0) return NULL;
return &sprites[id]; free(sprite);
for (int i = arrlen(sprites)-1; i >= 0; i--)
if (sprites[i] == sprite) {
arrdelswap(sprites,i);
return;
}
} }
static int sprite_count = 0; static int sprite_count = 0;
void sprite_flush() { sprite_count = 0; } void sprite_flush() { sprite_count = 0; }
int sprite_sort(int *a, int *b) int sprite_sort(sprite **sa, sprite **sb)
{ {
struct gameobject *goa = sprites[*a].go; sprite *a = *sa;
struct gameobject *gob = sprites[*b].go; sprite *b = *sb;
struct gameobject *goa = a->go;
struct gameobject *gob= b->go;
if (!goa && !gob) return 0;
if (!goa) return -1;
if (!gob) return 1;
if (goa->drawlayer == gob->drawlayer) return 0; if (goa->drawlayer == gob->drawlayer) return 0;
if (goa->drawlayer > gob->drawlayer) return 1; if (goa->drawlayer > gob->drawlayer) return 1;
return -1; return -1;
} }
void sprite_draw_all() { void sprite_draw_all() {
if (arrlen(sprites) == 0) return;
sg_apply_pipeline(pip_sprite); sg_apply_pipeline(pip_sprite);
sg_apply_uniforms(SG_SHADERSTAGE_VS, 0, SG_RANGE_REF(projection)); sg_apply_uniforms(SG_SHADERSTAGE_VS, 0, SG_RANGE_REF(projection));
int *layers = NULL;
if (layers) arrfree(layers);
for (int i = 0; i < freelist_len(sprites); i++) qsort(sprites, arrlen(sprites), sizeof(*sprites), sprite_sort);
if (sprites[i].next == -1 && sprites[i].go != NULL && sprites[i].enabled)
arrpush(layers, i);
if (!layers || arrlen(layers) == 0) return; for (int i = 0; i < arrlen(sprites); i++)
if (arrlen(layers) > 1) sprite_draw(sprites[i]);
qsort(layers, arrlen(layers), sizeof(*layers), sprite_sort);
for (int i = 0; i < arrlen(layers); i++)
sprite_draw(&sprites[layers[i]]);
arrfree(layers);
}
void sprite_loadtex(struct sprite *sprite, const char *path, struct glrect frame) {
if (!sprite) {
YughWarn("NO SPRITE!");
return;
}
sprite->tex = texture_pullfromfile(path);
sprite_setframe(sprite, &frame);
}
void sprite_settex(struct sprite *sprite, struct Texture *tex) {
sprite->tex = tex;
sprite_setframe(sprite, &ST_UNIT);
} }
void sprite_initialize() { void sprite_initialize() {
freelist_size(sprites, 500);
shader_sprite = sg_make_shader(sprite_shader_desc(sg_query_backend())); shader_sprite = sg_make_shader(sprite_shader_desc(sg_query_backend()));
pip_sprite = sg_make_pipeline(&(sg_pipeline_desc){ pip_sprite = sg_make_pipeline(&(sg_pipeline_desc){
@ -179,15 +152,15 @@ void sprite_initialize() {
}); });
} }
void tex_draw(struct Texture *tex, HMM_Mat3 m, struct glrect r, struct rgba color, int wrap, HMM_Vec2 wrapoffset, HMM_Vec2 wrapscale, struct rgba emissive, float parallax) { void tex_draw(struct texture *tex, HMM_Mat3 m, struct rect r, struct rgba color, int wrap, HMM_Vec2 wrapoffset, HMM_Vec2 wrapscale, struct rgba emissive, float parallax) {
struct sprite_vert verts[4]; struct sprite_vert verts[4];
float w = tex->width*st_s_w(r); float w = tex->width*r.w;
float h = tex->height*st_s_h(r); float h = tex->height*r.h;
HMM_Vec2 sposes[4] = { HMM_Vec2 sposes[4] = {
{0.0,0.0}, {0,0},
{w,0.0}, {w,0},
{0.0,h}, {0,h},
{w,h} {w,h}
}; };
@ -198,18 +171,18 @@ void tex_draw(struct Texture *tex, HMM_Mat3 m, struct glrect r, struct rgba colo
} }
if (wrap) { if (wrap) {
r.s1 *= wrapscale.x; r.w *= wrapscale.x;
r.t1 *= wrapscale.y; r.h *= wrapscale.y;
} }
verts[0].uv.X = r.s0; verts[0].uv.X = r.x;
verts[0].uv.Y = r.t1; verts[0].uv.Y = r.y+r.h;
verts[1].uv.X = r.s1; verts[1].uv.X = r.x+r.w;
verts[1].uv.Y = r.t1; verts[1].uv.Y = r.y+r.h;
verts[2].uv.X = r.s0; verts[2].uv.X = r.x;
verts[2].uv.Y = r.t0; verts[2].uv.Y = r.y;
verts[3].uv.X = r.s1; verts[3].uv.X = r.x+r.w;
verts[3].uv.Y = r.t0; verts[3].uv.Y = r.y;
bind_sprite.fs.images[0] = tex->id; bind_sprite.fs.images[0] = tex->id;
@ -220,64 +193,31 @@ void tex_draw(struct Texture *tex, HMM_Mat3 m, struct glrect r, struct rgba colo
sprite_count++; sprite_count++;
} }
transform2d sprite2t(sprite *s)
{
return (transform2d){
.pos = s->pos,
.scale = s->scale,
.angle = s->angle
};
}
void sprite_draw(struct sprite *sprite) { void sprite_draw(struct sprite *sprite) {
if (!sprite->tex) return; if (!sprite->tex) return;
transform2d t = go2t(sprite->go); transform2d t;
if (!sprite->go) t = t2d_unit;
else t = go2t(sprite->go);
t.pos.x += (cam_pos().x - (cam_pos().x/sprite->parallax)); t.pos.x += (cam_pos().x - (cam_pos().x/sprite->parallax));
t.pos.y += (cam_pos().y - (cam_pos().y/sprite->parallax)); t.pos.y += (cam_pos().y - (cam_pos().y/sprite->parallax));
HMM_Mat3 m = transform2d2mat(t); HMM_Mat3 m = transform2d2mat(t);
HMM_Mat3 sm = transform2d2mat(sprite->t); HMM_Mat3 sm = transform2d2mat(sprite2t(sprite));
tex_draw(sprite->tex, HMM_MulM3(m,sm), sprite->frame, sprite->color, sprite->drawmode, (HMM_Vec2){0,0}, sprite->scale, sprite->emissive, sprite->parallax);
tex_draw(sprite->tex, HMM_MulM3(m, sm), sprite->frame, sprite->color, sprite->drawmode, (HMM_Vec2){0,0}, sprite->t.scale, sprite->emissive, sprite->parallax);
} }
void gui_draw_img(const char *img, transform2d t, int wrap, HMM_Vec2 wrapoffset, float wrapscale, struct rgba color) { void gui_draw_img(const char *img, transform2d t, int wrap, HMM_Vec2 wrapoffset, float wrapscale, struct rgba color) {
sg_apply_pipeline(pip_sprite); sg_apply_pipeline(pip_sprite);
sg_apply_uniforms(SG_SHADERSTAGE_VS, 0, SG_RANGE_REF(hudproj)); sg_apply_uniforms(SG_SHADERSTAGE_VS, 0, SG_RANGE_REF(hudproj));
struct Texture *tex = texture_pullfromfile(img); struct texture *tex = texture_from_file(img);
tex_draw(tex, transform2d2mat(t), ST_UNIT, color, wrap, wrapoffset, (HMM_Vec2){wrapscale,wrapscale}, (struct rgba){0,0,0,0}, 0); tex_draw(tex, transform2d2mat(t), ST_UNIT, color, wrap, wrapoffset, (HMM_Vec2){wrapscale,wrapscale}, (struct rgba){0,0,0,0}, 0);
} }
void slice9_draw(const char *img, HMM_Vec2 pos, HMM_Vec2 dimensions, struct rgba color)
{
sg_apply_pipeline(slice9_pipe);
sg_apply_uniforms(SG_SHADERSTAGE_VS, 0, SG_RANGE_REF(hudproj));
struct Texture *tex = texture_pullfromfile(img);
struct glrect r = ST_UNIT;
struct slice9_vert verts[4];
HMM_Vec2 sposes[4] = {
{0.0,0.0},
{1.0,0.0},
{0.0,1.0},
{1.0,1.0},
};
for (int i = 0; i < 4; i++) {
verts[i].pos = HMM_MulV2(sposes[i], dimensions);
//verts[i].uv =z sposes[i];
verts[i].color = color;
}
verts[0].uv.u = r.s0 * USHRT_MAX;
verts[0].uv.v = r.t1 * USHRT_MAX;
verts[1].uv.u = r.s1 * USHRT_MAX;
verts[1].uv.v = r.t1 * USHRT_MAX;
verts[2].uv.u = r.s0 * USHRT_MAX;
verts[2].uv.v = r.t0 * USHRT_MAX;
verts[3].uv.u = r.s1 * USHRT_MAX;
verts[3].uv.v = r.t0 * USHRT_MAX;
bind_sprite.fs.images[0] = tex->id;
sg_append_buffer(bind_sprite.vertex_buffers[0], SG_RANGE_REF(verts));
sg_apply_bindings(&bind_sprite);
sg_draw(sprite_count * 4, 4, 1);
sprite_count++;
}
void sprite_setframe(struct sprite *sprite, struct glrect *frame) {
sprite->frame = *frame;
}

View file

@ -11,29 +11,29 @@
#define DRAW_TILE 1 #define DRAW_TILE 1
struct sprite { struct sprite {
transform2d t; HMM_Vec2 pos;
HMM_Vec2 scale;
float angle;
struct rgba color; struct rgba color;
struct rgba emissive; struct rgba emissive;
gameobject *go; gameobject *go;
struct Texture *tex; texture *tex;
struct glrect frame; struct rect frame;
int enabled; int enabled;
int next;
int drawmode; int drawmode;
float parallax; float parallax;
unsigned int next;
}; };
typedef struct sprite sprite;
sprite *sprite_make();
int make_sprite(gameobject *go); int make_sprite(gameobject *go);
struct sprite *id2sprite(int id); void sprite_free(sprite *sprite);
void sprite_delete(int id); void sprite_delete(int id);
void sprite_enabled(int id, int e);
void sprite_loadtex(struct sprite *sprite, const char *path, struct glrect rect);
void sprite_settex(struct sprite *sprite, struct Texture *tex);
void sprite_setframe(struct sprite *sprite, struct glrect *frame);
void sprite_initialize(); void sprite_initialize();
void sprite_draw(struct sprite *sprite); void sprite_draw(struct sprite *sprite);
void sprite_draw_all(); void sprite_draw_all();
unsigned int incrementAnimFrame(unsigned int interval, struct sprite *sprite);
void sprite_flush(); void sprite_flush();
void gui_draw_img(const char *img, transform2d t, int wrap, HMM_Vec2 wrapoffset, float wrapscale, struct rgba color); void gui_draw_img(const char *img, transform2d t, int wrap, HMM_Vec2 wrapoffset, float wrapscale, struct rgba color);

View file

@ -21,15 +21,15 @@
#include "nanosvgrast.h" #include "nanosvgrast.h"
#endif #endif
struct glrect ST_UNIT = {0.f, 1.f, 0.f, 1.f}; struct rect ST_UNIT = {0.f, 0.f, 1.f, 1.f};
static struct { static struct {
char *key; char *key;
struct Texture *value; struct texture *value;
} *texhash = NULL; } *texhash = NULL;
struct Texture *tex_default; struct texture *tex_default;
struct Texture *texture_notex() { return texture_pullfromfile("icons/no_tex.gif"); } struct texture *texture_notex() { return texture_from_file("icons/no_tex.gif"); }
unsigned int next_pow2(unsigned int v) unsigned int next_pow2(unsigned int v)
{ {
@ -72,18 +72,18 @@ int mip_wh(int w, int h, int *mw, int *mh, int lvl)
int gif_nframes(const char *path) int gif_nframes(const char *path)
{ {
struct Texture *t = texture_pullfromfile(path); struct texture *t = texture_from_file(path);
return t->frames; return t->frames;
} }
int *gif_delays(const char *path) int *gif_delays(const char *path)
{ {
struct Texture *t = texture_pullfromfile(path); struct texture *t = texture_from_file(path);
return t->delays; return t->delays;
} }
/* If an empty string or null is put for path, loads default texture */ /* If an empty string or null is put for path, loads default texture */
struct Texture *texture_pullfromfile(const char *path) { struct texture *texture_from_file(const char *path) {
if (!path) return texture_notex(); if (!path) return texture_notex();
if (shlen(texhash) == 0) sh_new_arena(texhash); if (shlen(texhash) == 0) sh_new_arena(texhash);
@ -98,8 +98,7 @@ struct Texture *texture_pullfromfile(const char *path) {
unsigned char *data; unsigned char *data;
struct Texture *tex = calloc(1, sizeof(*tex)); struct texture *tex = calloc(1, sizeof(*tex));
tex->opts.sprite = 1;
int n; int n;
@ -152,12 +151,7 @@ struct Texture *texture_pullfromfile(const char *path) {
tex->data = data; tex->data = data;
int filter; int filter = SG_FILTER_NEAREST;
if (tex->opts.sprite) {
filter = SG_FILTER_NEAREST;
} else {
filter = SG_FILTER_LINEAR;
}
sg_image_data sg_img_data; sg_image_data sg_img_data;
@ -199,13 +193,18 @@ struct Texture *texture_pullfromfile(const char *path) {
for (int i = 1; i < mips; i++) for (int i = 1; i < mips; i++)
free(mipdata[i]); free(mipdata[i]);
return tex; return tex;
} }
void texture_sync(const char *path) { YughWarn("Need to implement texture sync."); } void texture_sync(const char *path) { YughWarn("Need to implement texture sync."); }
char *tex_get_path(struct Texture *tex) { void texture_free(texture *tex)
{
}
char *tex_get_path(struct texture *tex) {
for (int i = 0; i < shlen(texhash); i++) { for (int i = 0; i < shlen(texhash); i++) {
if (tex == texhash[i].value) { if (tex == texhash[i].value) {
YughInfo("Found key %s", texhash[i].key); YughInfo("Found key %s", texhash[i].key);
@ -216,10 +215,9 @@ char *tex_get_path(struct Texture *tex) {
return ""; return "";
} }
struct Texture *texture_fromdata(void *raw, long size) struct texture *texture_fromdata(void *raw, long size)
{ {
struct Texture *tex = calloc(1, sizeof(*tex)); struct texture *tex = calloc(1, sizeof(*tex));
tex->opts.sprite = 1;
int n; int n;
void *data = stbi_load_from_memory(raw, size, &tex->width, &tex->height, &n, 4); void *data = stbi_load_from_memory(raw, size, &tex->width, &tex->height, &n, 4);
@ -234,12 +232,7 @@ struct Texture *texture_fromdata(void *raw, long size)
tex->data = data; tex->data = data;
int filter; int filter = SG_FILTER_NEAREST;
if (tex->opts.sprite) {
filter = SG_FILTER_NEAREST;
} else {
filter = SG_FILTER_LINEAR;
}
sg_image_data sg_img_data; sg_image_data sg_img_data;
@ -283,9 +276,7 @@ struct Texture *texture_fromdata(void *raw, long size)
return tex; return tex;
} }
struct Texture *texture_loadfromfile(const char *path) { return texture_pullfromfile(path); } HMM_Vec2 tex_get_dimensions(struct texture *tex) {
HMM_Vec2 tex_get_dimensions(struct Texture *tex) {
if (!tex) return (HMM_Vec2){0,0}; if (!tex) return (HMM_Vec2){0,0};
HMM_Vec2 d; HMM_Vec2 d;
d.x = tex->width; d.x = tex->width;
@ -293,10 +284,6 @@ HMM_Vec2 tex_get_dimensions(struct Texture *tex) {
return d; return d;
} }
float st_s_w(struct glrect st) { return (st.s1 - st.s0); }
float st_s_h(struct glrect st) { return (st.t1 - st.t0); }
static double fade (double t) { return t*t*t*(t*(t*6-15)+10); } static double fade (double t) { return t*t*t*(t*(t*6-15)+10); }
double grad (int hash, double x, double y, double z) double grad (int hash, double x, double y, double z)
{ {

View file

@ -14,26 +14,20 @@
#define FILTER_NONE SG_FILTER_NONE #define FILTER_NONE SG_FILTER_NONE
#define FILTER_LINEAR SG_FILTER_LINEAR #define FILTER_LINEAR SG_FILTER_LINEAR
float st_s_w(struct glrect st); extern struct rect ST_UNIT;
float st_s_h(struct glrect st);
extern struct glrect ST_UNIT;
struct TextureOptions {
int sprite;
};
/* Represents an actual texture on the GPU */ /* Represents an actual texture on the GPU */
struct Texture { struct texture {
sg_image id; /* ID reference for the GPU memory location of the texture */ sg_image id; /* ID reference for the GPU memory location of the texture */
int width; int width;
int height; int height;
unsigned char *data; unsigned char *data;
struct TextureOptions opts;
int frames; int frames;
int *delays; int *delays;
}; };
typedef struct texture texture;
typedef struct img_sampler{ typedef struct img_sampler{
int wrap_u; int wrap_u;
int wrap_v; int wrap_v;
@ -43,21 +37,21 @@ typedef struct img_sampler{
int mip_filter; int mip_filter;
} img_sampler; } img_sampler;
typedef struct Texture texture; struct texture *texture_from_file(const char *path); // Create texture from image
struct texture *texture_fromdata(void *raw, long size);
struct Texture *texture_pullfromfile(const char *path); // Create texture from image void texture_free(texture *tex);
struct Texture *texture_fromdata(void *raw, long size);
/* Hot reloads a texture, if needed */ /* Hot reloads a texture, if needed */
void texture_sync(const char *path); void texture_sync(const char *path);
char * tex_get_path(struct Texture *tex); // Get image path for texture char * tex_get_path(struct texture *tex); // Get image path for texture
int gif_nframes(const char *path); int gif_nframes(const char *path);
int *gif_delays(const char *path); int *gif_delays(const char *path);
struct glrect tex_get_rect(struct Texture *tex); struct glrect tex_get_rect(struct texture *tex);
HMM_Vec2 tex_get_dimensions(struct Texture *tex); HMM_Vec2 tex_get_dimensions(struct texture *tex);
double perlin(double x, double y, double z); double perlin(double x, double y, double z);

View file

@ -19,7 +19,7 @@ struct window mainwin;
static struct window *windows = NULL; static struct window *windows = NULL;
struct Texture *icon = NULL; struct texture *icon = NULL;
void window_resize(int width, int height) void window_resize(int width, int height)
{ {
@ -52,7 +52,7 @@ void window_suspended(int s)
} }
void window_set_icon(const char *png) { void window_set_icon(const char *png) {
icon = texture_pullfromfile(png); icon = texture_from_file(png);
window_seticon(&mainwin, icon); window_seticon(&mainwin, icon);
} }
@ -71,7 +71,7 @@ void window_togglefullscreen(struct window *w) {
mainwin.fullscreen = sapp_is_fullscreen(); mainwin.fullscreen = sapp_is_fullscreen();
} }
void window_seticon(struct window *w, struct Texture *tex) void window_seticon(struct window *w, struct texture *tex)
{ {
struct isize { struct isize {
int size; int size;

View file

@ -16,7 +16,7 @@ struct window {
int focus; int focus;
int shown; int shown;
}; };
struct Texture; struct texture;
extern struct window mainwin; extern struct window mainwin;
void window_resize(int width, int height); void window_resize(int width, int height);
@ -29,7 +29,7 @@ void window_togglefullscreen(struct window *w);
void window_unfullscreen(struct window *w); void window_unfullscreen(struct window *w);
void window_set_icon(const char *png); void window_set_icon(const char *png);
void window_seticon(struct window *w, struct Texture *icon); void window_seticon(struct window *w, struct texture *icon);
void window_render(struct window *w); void window_render(struct window *w);