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');
},
hides: ['gameobject', 'id'],
make(go) {
var nc = Object.create(this);
nc.gameobject = go;
Object.assign(nc, this._enghook(go.body));
Object.mixin(nc, this._enghook(go.body));
assign_impl(nc,this.impl);
Object.hide(nc, ...this.hides);
Object.hide(nc, ['gameobject', 'id']);
nc.post();
return nc;
},
kill() { console.info("Kill not created for this component yet"); },
sync() {},
sync(){},
post(){},
gui() { },
gizmo() { },
gui(){},
gizmo(){},
prepare_center() {},
finish_center() {},
extend(spec) { return Object.copy(this, spec); },
};
@ -68,126 +65,95 @@ var assign_impl = function(obj, impl)
obj[key] = tmp[key];
}
component.sprite = Object.copy(component, {
pos:[0,0],
color:[1,1,1,1],
layer:0,
enabled:true,
path: "",
rect: {s0:0, s1: 1, t0: 0, t1: 1},
toString() { return "sprite"; },
_enghook: make_sprite,
});
function json_from_whitelist(whitelist)
{
return function() {
var o = {};
for (var p of whitelist)
o[p] = this[p];
return o;
}
}
component.sprite.mode = {
simple: 0,
tile: 1
};
component.sprite.impl = {
toJSON() {
var j = {};
Object.keys(this).forEach(k => j[k] = this[k]);
delete j.rect;
return j;
Object.mixin(cmd(268,true), {
toJSON:json_from_whitelist([
"path",
"pos",
"scale",
"angle",
"color",
"emissive",
"parallax",
"frame"
]),
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();
},
set path(x) {
if (this.cancel) {
this.cancel();
this.cancel = undefined;
}
if (!Resources.is_animation(x)) {
this.rect = component.sprite.rect;
cmd(12,this.id,x,this.rect);
}
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);
}
stop() {},
set path(p) {
p = Resources.find_image(p);
if (!p) return;
if (p === this.path) return;
this.tex = cmd(269,p);
var anim = SpriteAnim.make(p);
if (!anim) return;
this.anim = anim;
this.play();
},
get path() {
var s = cmd(116,this.id);
if (s === "icons/no_tex.gif") return undefined;
return s;
return this.tex.path();
},
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"; },
hide() { this.enabled = false; },
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); },
move(d) { this.pos = this.pos.add(d); },
grow(x) {
this.scale = this.scale.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() { },
pickm() { return this; },
move(d) { this.pos = this.pos.add(d); },
pick() { return this; },
boundingbox() {
var dim = this.dimensions();
dim = dim.scale(this.gameobject.gscale());
var realpos = dim.scale(0.5).add(this.pos);
return bbox.fromcwh(realpos,dim);
},
kill() { cmd(9,this.id); },
dimensions() {
var dim = Resources.texture.dimensions(this.path);
dim.x *= (this.rect.s1-this.rect.s0);
dim.y *= (this.rect.t1-this.rect.t0);
var dim = [this.tex.width(), this.tex.height()];
dim.x *= this.frame.w;
dim.y *= this.frame.h;
return dim;
},
width() { return this.dimensions().x; },
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);
@ -195,6 +161,7 @@ component.model = Object.copy(component, {
path:"",
_enghook: make_model,
});
component.model.impl = {
set path(x) { cmd(149, this.id, x); },
draw() { cmd(150, this.id); },
@ -245,10 +212,10 @@ var SpriteAnim = {
for (var f = 0; f < frames; f++) {
var frame = {};
frame.rect = {
s0: 0,
s1: 1,
t0: yslice*f,
t1: yslice*(f+1)
x: 0,
w: 1,
y: yslice*f,
h: yslice
};
frame.time = 0.05;
anim.frames.push(frame);
@ -290,10 +257,10 @@ var SpriteAnim = {
var f = ase_frame.frame;
var frame = {};
frame.rect = {
s0: f.x/dim.w,
s1: (f.x+f.w)/dim.w,
t0: f.y/dim.h,
t1: (f.y+f.h)/dim.h
x: f.x/dim.w,
w: f.w/dim.w,
y: f.y/dim.h,
h: f.h/dim.h
};
frame.time = ase_frame.duration / 1000;
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.";
component.polygon2d = Object.copy(collider2d, {
toJSON:json_from_whitelist([
'points',
'sensor'
]),
toString() { return "polygon2d"; },
flipx: false,
flipy: false,
@ -463,6 +434,13 @@ polygon2d.inputs['C-b'] = function() {
polygon2d.inputs['C-b'].doc = "Freeze mirroring in place.";
component.edge2d = Object.copy(collider2d, {
toJSON:json_from_whitelist([
'sensor',
'thickness',
'points',
'hollow',
'hollowt',
]),
dimensions:2,
thickness:0,
/* if type === -1, point to point */
@ -821,6 +799,11 @@ component.circle2d = Object.copy(collider2d, {
});
component.circle2d.impl = Object.mix({
toJSON:json_from_whitelist([
"pos",
"radius",
]),
set radius(x) { cmd_circle2d(0,this.id,x); },
get radius() { return cmd_circle2d(2,this.id); },
@ -840,4 +823,4 @@ component.circle2d.impl = Object.mix({
}, 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('.', '_');
if (!(name in obj)) return name;
var t = 1;
var n = name + t;
while (n in obj) {
t++;
n = name+t;
n = name + t;
}
return n;
}
var gameobject_impl = {
get pos() {
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()}`);
return this.worldangle() - this.master.worldangle();
},
set angle(x) {
var diff = x - this.angle;
@ -36,64 +34,64 @@ var gameobject_impl = {
x.pos = Vector.rotate(x.pos, diff);
});
this.sworldangle(x-this.master.worldangle());
this.sworldangle(x - this.master.worldangle());
},
get scale() {
assert(this.master, `No master set on ${this.toString()}`);
var pscale = [1,1,1];
return this.gscale().map((x,i) => x/(this.master.gscale()[i]*pscale[i]));
var pscale = [1, 1, 1];
return this.gscale().map((x, i) => x / (this.master.gscale()[i] * pscale[i]));
},
set scale(x) {
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);
/* TRANSLATE ALL SUB OBJECTS */
this.objects.forEach(obj => {
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); },
set draw_layer(x) { cmd(172, this.body, x); },
set layer(x) { cmd(75,this.body,x); },
get layer() { return cmd(77,this.body); },
set warp_layer(x) { cmd(251, this.body,x); },
get warp_layer() { return cmd(252, this.body); },
get draw_layer() { return this.body.drawlayer; },
set draw_layer(x) { this.body.drawlayer = x; },
set layer(x) { this.body.layer = x; },
get layer() { return this.body.layer; },
set warp_layer(x) { this.body.warp_filter = x; },
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() {
if (!(this.phys === physics.dynamic))
return undefined;
return q_body(5, this.body);
},
get elasticity() { return cmd(107,this.body); },
set elasticity(x) { cmd(106,this.body,x); },
get friction() { return cmd(109,this.body); },
set friction(x) { cmd(108,this.body,x); },
set timescale(x) { cmd(168,this.body,x); },
get timescale() { return cmd(169,this.body); },
get elasticity() { return this.body.e; },
set elasticity(x) { this.body.e = x; },
get friction() { return this.body.f; },
set friction(x) { this.body.f = x; },
set timescale(x) { this.body.timescale = x; },
get timescale() { return this.body.timescale; },
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); },
set velocity(x) { set_body(9, this.body, x); },
get damping() { return cmd(157,this.body); },
set damping(x) { cmd(156, this.body, x); },
get angularvelocity() { return Math.rad2turn(q_body(4,this.body)); },
get damping() { return this.body.damping; },
set damping(x) { this.body.damping = x },
get angularvelocity() { return Math.rad2turn(q_body(4, this.body)); },
set angularvelocity(x) { set_body(8, this.body, Math.turn2rad(x)); },
get max_velocity() { return cmd(152, this.body); },
set max_velocity(x) { cmd(151, this.body, x); },
get max_angularvelocity() { return cmd(155,this.body); },
set max_angularvelocity(x) { cmd(154,this.body,x); },
get max_velocity() { return this.body.maxvelocity; },
set max_velocity(x) { this.body.maxvelocity = x; },
get max_angularvelocity() { return this.body.maxangularvelocity; },
set max_angularvelocity(x) { this.body.maxangularvelocity = x; },
get_moi() { return q_body(6, this.body); },
set_moi(x) {
if(x <= 0) {
if (x <= 0) {
console.error("Cannot set moment of inertia to 0 or less.");
return;
}
@ -117,7 +115,7 @@ var gameobject = {
var lur = ur[this.master.ur];
if (!lur) return;
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))
this._ed.inst = true;
else
@ -127,7 +125,7 @@ var gameobject = {
selectable: false,
dirty: false
},
namestr() {
var s = this.toString();
if (this._ed.dirty)
@ -137,41 +135,41 @@ var gameobject = {
},
urstr() {
if (this._ed.dirty) return "*"+this.ur;
if (this._ed.dirty) return "*" + this.ur;
return this.ur;
},
full_path() {
return this.path_from(world);
},
/* pin this object to the to object */
pin(to) {
var p = cmd(222,this.body, to.body);
var p = cmd(222, this.body, to.body);
},
slide(to, a, b, min, max) {
a ??= [0,0];
b ??= [0,0];
a ??= [0, 0];
b ??= [0, 0];
min ??= 0;
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.break();
},
pivot(to, piv) {
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(to, a, b, anchor) {
anchor ??= [0,0];
anchor ??= [0, 0];
var p = cmd(228, to.body, this.body, a, b, anchor);
},
damped_spring(to, length, stiffness, damping) {
length ??= Vector.length(this.worldpos(), to.worldpos());
stiffness ??= 1;
damping ??= 1;
var dc = 2*Math.sqrt(stiffness*this.mass);
var p = cmd(227, this.body, to.body, [0,0], [0,0], stiffness, damping*dc);
var dc = 2 * Math.sqrt(stiffness * this.mass);
var p = cmd(227, this.body, to.body, [0, 0], [0, 0], stiffness, damping * dc);
},
damped_rotary_spring(to, angle, stiffness, damping) {
angle ??= 0;
@ -179,14 +177,14 @@ var gameobject = {
damping ??= 1;
/* calculate actual damping value from the damping ratio */
/* 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 */
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) {
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 p = cmd(230, this.body, to.body, phase, Math.turn2rad(ratch));
},
@ -194,183 +192,187 @@ var gameobject = {
phase ??= 1;
ratio ??= 1;
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) {
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){
var t = Tween.make(this, prop, values, def);
t.play();
var k = function() { t.pause(); }
this.timers.push(k);
return k;
},
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;
},
cry(file) {
return;
this.crying = audio.sound.play(file, audio.sound.bus.sfx);
var killfn = () => {this.crying = undefined; console.warn("killed"); }
this.crying.hook = killfn;
return killfn;
},
clear() {
for (var k in this.objects) {
this.objects[k].kill();
};
this.objects = {};
},
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;
},
delay(fn, seconds) {
var t = timer.delay(fn.bind(this), seconds);
this.timers.push(t);
return t;
},
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()); },
tween(prop, values, def) {
var t = Tween.make(this, prop, values, def);
t.play();
worldangle() { return Math.rad2turn(q_body(2,this.body)); },
sworldangle(x) { set_body(0,this.body,Math.turn2rad(x)); },
var k = function() { t.pause(); }
this.timers.push(k);
return k;
},
get_ur() {
// if (this.ur === 'empty') return undefined;
return Object.access(ur,this.ur);
},
cry(file) {
return;
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:
the file path of a script
an ur object
nothing
the file path of a script
an ur object
nothing
*/
spawn(text) {
var ent = Object.create(gameobject);
if (typeof text === 'object')
text = text.name;
spawn(text) {
var ent = Object.create(gameobject);
if (typeof text === 'undefined')
ent.ur = "empty";
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);
if (typeof text === 'object')
text = text.name;
ent._ed = {
selectable: true,
dirty: false,
inst: false,
urdiff: {},
};
if (typeof text === 'undefined')
ent.ur = "empty";
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;
}
}
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');
if (ent.ur === 'empty') {
if (!ur.empty.proto) ur.empty.proto = json.decode(json.encode(ent));
return ent;
}
ent.reparent(this);
if (ent.ur === 'script')
eval_env(io.slurp(text), ent, ent.ur);
else
apply_ur(ent.ur, ent);
ent._ed = {
selectable: true,
dirty: false,
inst: false,
urdiff: {},
};
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];
};
cmd(113, ent.body, ent); // set the internal obj reference to this obj
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]);
}
}
Object.hide(ent, 'ur', 'body', 'components', 'objects', '_ed', 'timers', 'master');
if (ent.ur === 'empty') {
if (!ur.empty.proto) ur.empty.proto = json.decode(json.encode(ent));
return ent;
}
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(parent) {
@ -382,9 +384,9 @@ var gameobject = {
}
this.master?.remove_obj(this);
this.master = parent;
function unique_name(list, name) {
name ??= "new_object";
var str = name.replaceAll('.', '_');
@ -392,7 +394,7 @@ var gameobject = {
var t = str;
while (list.indexOf(t) !== -1) {
t = str + n;
n++;
n++;
}
return t;
};
@ -403,34 +405,36 @@ var gameobject = {
parent[name] = this;
this.toString = function() { return name; };
},
remove_obj(obj) {
if (this[obj.toString()] === this.objects[obj.toString()])
delete this[obj.toString()];
delete this.objects[obj.toString()];
delete this[obj.toString()];
},
remove_obj(obj) {
if (this[obj.toString()] === this.objects[obj.toString()])
delete this[obj.toString()];
delete this.objects[obj.toString()];
delete this[obj.toString()];
},
components: {},
objects: {},
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?.());},
show() { this.components.forEach(function(x) { x.show?.(); }); this.objects.forEach(function(x) { x.show?.(); }); },
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?.()); },
show() { this.components.forEach(function(x) { x.show?.(); });
this.objects.forEach(function(x) { x.show?.(); }); },
width() {
var bb = this.boundingbox();
@ -439,13 +443,13 @@ var gameobject = {
height() {
var bb = this.boundingbox();
return bb.t-bb.b;
return bb.t - bb.b;
},
/* Moving, rotating, scaling functions, world relative */
move(vec) { this.set_worldpos(this.worldpos().add(vec)); },
rotate(x) { this.sworldangle(this.worldangle()+x); },
grow(vec) { this.sgscale(this.gscale().map((x,i)=>x*vec[i])); },
rotate(x) { this.sworldangle(this.worldangle() + x); },
grow(vec) { this.sgscale(this.gscale().map((x, i) => x * vec[i])); },
/* Make a unique object the same as its prototype */
revert() {
@ -462,155 +466,155 @@ var gameobject = {
},
toString() { return "new_object"; },
flipx() { return this.scale.x < 0; },
flipy() { return this.scale.y < 0; },
mirror(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 */
boundingbox() {
var boxes = [];
boxes.push({
t:0,
r:0,
b:0,
l:0
});
save: true,
selectable: true,
ed_locked: false,
for (var key in this.components) {
if ('boundingbox' in this.components[key])
boxes.push(this.components[key].boundingbox());
}
for (var key in this.objects)
boxes.push(this.objects[key].boundingbox());
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?.(); });
},
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); });
bb = bbox.move(bb, this.pos);
for (var key in this.components) {
if ('boundingbox' in this.components[key])
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. */
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();
boxes.forEach(function(x) { bb = bbox.expand(bb, x); });
var odiff = ediff(curobjs, proto.objects);
if (odiff)
d.objects = curobjs;
bb = bbox.move(bb, this.pos);
delete d.pos;
delete d.angle;
delete d.scale;
delete d.velocity;
delete d.angularvelocity;
return d;
},
return bb ? bb : bbox.fromcwh([0, 0], [0, 0]);
},
/* 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;
},
/* The unique components of this object. Its diff. */
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.
proto() {
var u = this.get_ur();
if (!u) return {};
return u.proto;
},
var d = ediff(thiso, 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;
},
d ??= {};
/* Velocity and angular velocity of the object */
phys_obj() {
var phys = {};
phys.velocity = this.velocity;
phys.angularvelocity = this.angularvelocity;
return phys;
},
var objects = {};
proto.objects ??= {};
var curobjs = {};
for (var o in this.objects)
curobjs[o] = this.objects[o].instance_obj();
dup(diff) {
var n = this.master.spawn(this.__proto__);
Object.totalmerge(n, this.instance_obj());
return n;
},
var odiff = ediff(curobjs, proto.objects);
if (odiff)
d.objects = curobjs;
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() {
if (this.__kill) return;
this.__kill = true;
this.timers.forEach(t => t());
this.timers = [];
Event.rm_obj(this);
Player.do_uncontrol(this);
register_collide(2, undefined, this.body);
if (this.master) {
this.master.remove_obj(this);
this.master = undefined;
}
if (this.__proto__.instances)
delete this.__proto__.instances[this.toString()];
for (var key in this.components) {
this.components[key].kill();
this.components[key].kill?.();
this.components[key].gameobject = undefined;
delete this.components[key];
}
this.clear();
this.objects = undefined;
if (typeof this.stop === 'function') this.stop();
if (typeof this.die === 'function') this.die();
},
up() { return [0,1].rotate(this.angle);},
down() { return [0,-1].rotate(this.angle);},
right() { return [1,0].rotate(this.angle);},
left() { return [-1,0].rotate(this.angle); },
up() { return [0, 1].rotate(this.angle); },
down() { return [0, -1].rotate(this.angle); },
right() { return [1, 0].rotate(this.angle); },
left() { return [-1, 0].rotate(this.angle); },
make_objs(objs) {
for (var prop in objs) {
@ -659,7 +663,7 @@ var gameobject = {
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.`;
@ -737,14 +741,13 @@ ur {
/* Apply an ur u to an entity e */
/* u is given as */
function apply_ur(u, e)
{
function apply_ur(u, e) {
console.log(`applying ur ${u}`);
if (typeof u !== 'string') {
console.warn("Must give u as a string.");
return;
}
var urs = u.split('.');
var config = {};
var topur = ur;
@ -759,7 +762,7 @@ function apply_ur(u, e)
var script = Resources.replstrs(topur.text);
eval_env(script, e, topur.text);
}
if (topur.data) {
var jss = Resources.replstrs(topur.data);
Object.merge(config, json.decode(jss));
@ -769,28 +772,27 @@ function apply_ur(u, e)
Object.merge(e, config);
}
function file2fqn(file)
{
function file2fqn(file) {
var fqn = file.strip_ext();
if (fqn.folder_same_name())
fqn = fqn.up_path();
fqn = fqn.replace('/','.');
fqn = fqn.replace('/', '.');
var topur;
if (topur = Object.access(ur,fqn)) return topur;
if (topur = Object.access(ur, fqn)) return topur;
var fqnlast = fqn.split('.').last();
if (topur = Object.access(ur,fqn.tolast('.'))) {
if (topur = Object.access(ur, fqn.tolast('.'))) {
topur[fqnlast] = {
name: fqn
};
ur._list.push(fqn);
return Object.access(ur,fqn);
return Object.access(ur, fqn);
}
fqn = fqnlast;
ur[fqn] = {
name: fqn
};
@ -821,4 +823,4 @@ Game.loadurs = function() {
return {
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);
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() {
if (GUI.selected)

View file

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

View file

@ -41,14 +41,27 @@ os.prefpath = function() {
var projectfile = ".prosperon/project.json";
var Resources = {};
Resources.images = ["png", "jpg", "jpeg", "gif"];
Resources.sounds = ["wav", "mp3", "flac", "qoa"];
Resources.images = ["png", "gif", "jpg", "jpeg"];
Resources.sounds = ["wav", 'flac', 'mp3', "qoa"];
Resources.scripts = "js";
Resources.is_image = function(path) {
var ext = path.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) {
var ext = path.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_black = {0,0,0,255};
struct rgba color_clear = {0,0,0,0};
struct rgba disabled_color = {148,148,148,255};
struct rgba sleep_color = {255,140,228,255};
@ -624,13 +625,13 @@ JSValue arb2js(cpArbiter *arb)
norm.cp = cpArbiterGetNormal(arb);
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, "sensor", JS_NewBool(js, cpShapeGetSensor(shape2)));
HMM_Vec2 srfv;
srfv.cp = cpArbiterGetSurfaceVelocity(arb);
JS_SetPropertyStr(js, obj, "velocity", vec2js(srfv));
JS_SetPropertyStr(js, obj, "velocity", vec22js(srfv));
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;
} else {
// 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);
}
} 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){});
/*
cgltf_texture *tex;
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
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)

View file

@ -19,11 +19,11 @@ struct datastream {
soundbyte *ring;
};
struct Texture;
struct texture;
void MakeDatastream();
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_seek(struct datastream *ds, double);
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++) {
stbtt_packedchar glyph = glyphs[c - 32];
struct glrect r;
r.s0 = (glyph.x0) / (float)packsize;
r.s1 = (glyph.x1) / (float)packsize;
r.t0 = (glyph.y0) / (float)packsize;
r.t1 = (glyph.y1) / (float)packsize;
struct rect r;
r.x = (glyph.x0) / (float)packsize;
r.w = (glyph.x1-glyph.x0) / (float)packsize;
r.y = (glyph.y0) / (float)packsize;
r.h = (glyph.y1-glyph.y0) / (float)packsize;
stbtt_GetCodepointHMetrics(&fontinfo, c, &newfont->Characters[c].Advance, &newfont->Characters[c].leftbearing);
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;
vert.uv.u = c.rect.s0*USHRT_MAX;
vert.uv.v = c.rect.t0*USHRT_MAX;
vert.st.u = (c.rect.s1-c.rect.s0)*USHRT_MAX;
vert.st.v = (c.rect.t1-c.rect.t0)*USHRT_MAX;
vert.uv.u = c.rect.x*USHRT_MAX;
vert.uv.v = c.rect.y*USHRT_MAX;
vert.st.u = c.rect.w*USHRT_MAX;
vert.st.v = c.rect.h*USHRT_MAX;
vert.color = color;
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
int Advance; // Horizontal offset to advance to next glyph
int leftbearing;
struct glrect rect;
struct rect rect;
};
struct sFont {

View file

@ -2,7 +2,7 @@
#define FREELIST_H
/* 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
@ -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_len(p) (freelist_header(p)->len)
#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_free(p) (free(freelist_header(p)))
#define freelist_count(p) (freelist_header(p)->count)

View file

@ -67,10 +67,11 @@ static JSValue TYPE##2js(TYPE *n) { \
JS_SetOpaque(j,n);\
return j; }\
QJSCLASS(gameobject)
QJSCLASS(emitter)
QJSCLASS(dsp_node)
QJSCLASS(texture)
QJSCLASS(sprite)
QJSCLASS(warp_gravity)
QJSCLASS(warp_damp)
QJSCLASS(material)
@ -107,6 +108,7 @@ JS_SetPropertyStr(js, globalThis, #NAME, NAME); \
JS_NewClassID(&js_##TYPE##_id);\
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) \
QJSCLASSPREP(TYPE); \
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) {
return JS_NewFloat64(js,g);
}
struct sprite *js2sprite(JSValue v) { return id2sprite(js2int(v)); }
JSValue ptr2js(void *ptr) {
JSValue obj = JS_NewObjectClass(js, js_ptr_id);
@ -231,15 +232,6 @@ double js_get_prop_number(JSValue v, const char *p) {
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); }
int js_arrlen(JSValue v) {
@ -406,6 +398,14 @@ HMM_Vec2 js2vec2(JSValue v)
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 v3;
@ -474,17 +474,10 @@ void vec2float(HMM_Vec2 v, float *f) {
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 array = JS_NewArray(js);
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;
}
@ -695,11 +688,6 @@ JSValue duk_cmd(JSContext *js, JSValueConst this, int argc, JSValueConst *argv)
ret = JS_NewInt64(js, script_dofile(str));
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:
set_timescale(js2number(argv[1]));
break;
@ -715,34 +703,7 @@ JSValue duk_cmd(JSContext *js, JSValueConst this, int argc, JSValueConst *argv)
case 7:
physMS = js2number(argv[1]);
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:
gameobject_draw_debug(js2gameobject(argv[1]));
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]));
break;
case 20:
sprite_enabled(js2int(argv[1]), JS_ToBool(js, argv[2]));
break;
case 21:
ret = JS_NewBool(js, shape_get_sensor(js2ptr(argv[1])));
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])));
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:
js2gameobject(argv[1])->scale.XY = js2vec2(argv[2]);
gameobject_apply(js2gameobject(argv[1]));
cpSpaceReindexShapesForBody(space, js2gameobject(argv[1])->body);
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:
go = pos2gameobject(js2vec2(argv[1]), js2number(argv[2]));
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]));
break;
case 54:
gameobject_apply(js2gameobject(argv[1]));
break;
case 59:
v1 = js2cpvec2arr(argv[2]);
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]));
break;
case 63:
set_cam_body(NULL);
break;
case 64:
str = JS_ToCString(js, argv[1]);
ret = vec2js(tex_get_dimensions(texture_pullfromfile(str)));
break;
case 66:
ret = strarr2js(ls(","));
ret = vec22js(tex_get_dimensions(texture_from_file(str)));
break;
case 67:
@ -923,37 +801,17 @@ JSValue duk_cmd(JSContext *js, JSValueConst this, int argc, JSValueConst *argv)
break;
case 70:
ret = vec2js(world2go(js2gameobject(argv[1]), js2vec2(argv[2])));
ret = vec22js(world2go(js2gameobject(argv[1]), js2vec2(argv[2])));
break;
case 71:
ret = vec2js(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]);
ret = vec22js(go2world(js2gameobject(argv[1]), js2vec2(argv[2])));
break;
case 76:
set_cat_mask(js2int(argv[1]), js2bitmask(argv[2]));
break;
case 77:
ret = int2js(js2gameobject(argv[1])->layer);
break;
case 79:
ret = JS_NewBool(js, phys_stepping());
break;
@ -978,7 +836,7 @@ JSValue duk_cmd(JSContext *js, JSValueConst this, int argc, JSValueConst *argv)
break;
case 85:
ret = vec2js(HMM_ProjV2(js2vec2(argv[1]), js2vec2(argv[2])));
ret = vec22js(HMM_ProjV2(js2vec2(argv[1]), js2vec2(argv[2])));
break;
case 86:
@ -1010,36 +868,13 @@ JSValue duk_cmd(JSContext *js, JSValueConst this, int argc, JSValueConst *argv)
ret = int2js(logLevel);
break;
case 96:
id2sprite(js2int(argv[1]))->color = js2color(argv[2]);
break;
case 97:
str = js2str(argv[1]);
cursor_img(str);
break;
case 103:
ret = vec2js(js2gameobject(argv[1])->scale.XY);
ret = vec22js(js2gameobject(argv[1])->scale.XY);
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:
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];
break;
case 114:
ret = bool2js(js2sprite(argv[1])->enabled);
break;
case 115:
draw_circle(js2vec2(argv[1]), js2number(argv[2]), js2number(argv[2]), js2color(argv[3]), -1);
break;
case 116:
ret = str2js(tex_get_path(js2sprite(argv[1])->tex));
break;
case 117:
str = JS_ToCString(js, argv[1]);
ret = script_runfile(str);
@ -1141,16 +968,11 @@ JSValue duk_cmd(JSContext *js, JSValueConst this, int argc, JSValueConst *argv)
break;
case 136:
ret = vec2js(world2screen(js2vec2(argv[1])));
ret = vec22js(world2screen(js2vec2(argv[1])));
break;
case 137:
ret = vec2js(screen2world(js2vec2(argv[1])));
break;
case 138:
str = JS_ToCString(js, argv[1]);
ret = JS_NewInt64(js, jso_file(str));
ret = vec22js(screen2world(js2vec2(argv[1])));
break;
case 139:
@ -1171,13 +993,6 @@ JSValue duk_cmd(JSContext *js, JSValueConst this, int argc, JSValueConst *argv)
console_print(str);
break;
case 143:
str = JS_ToCString(js, argv[1]);
if (!getenv(str)) ret = JS_UNDEFINED;
else
ret = str2js(getenv(str));
break;
case 145:
if (js2bool(argv[1])) window_makefullscreen(&mainwin);
else window_unfullscreen(&mainwin);
@ -1186,12 +1001,6 @@ JSValue duk_cmd(JSContext *js, JSValueConst this, int argc, JSValueConst *argv)
log_clear();
break;
case 147:
exit(js2int(argv[1]));
break;
case 148:
ret = color2js(id2sprite(js2int(argv[1]))->color);
break;
case 149:
((struct drawmodel *)js2ptr(argv[1]))->model = GetExistingModel(js2str(argv[2]));
break;
@ -1200,58 +1009,18 @@ JSValue duk_cmd(JSContext *js, JSValueConst this, int argc, JSValueConst *argv)
draw_drawmodel(js2ptr(argv[1]));
break;
case 151:
js2gameobject(argv[1])->maxvelocity = js2number(argv[2]);
break;
case 152:
ret = number2js(js2gameobject(argv[1])->maxvelocity);
break;
case 153:
cpBodySetTorque(js2gameobject(argv[1])->body, js2number(argv[2]));
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:
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;
case 161:
ret = vec2js(mat_t_dir(t_go2world(js2gameobject(argv[1])), js2vec2(argv[2])));
break;
case 162:
str = JS_ToCString(js, argv[1]);
ret = int2js(remove(str));
ret = vec22js(mat_t_dir(t_go2world(js2gameobject(argv[1])), js2vec2(argv[2])));
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:
str = js2str(argv[1]);
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:
ret = int2js(go_count());
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:
ret = constraint2js(constraint_make(cpPivotJointNew(js2gameobject(argv[1])->body, js2gameobject(argv[2])->body,js2vec2(argv[3]).cp)));
break;
@ -1392,12 +1144,7 @@ JSValue duk_cmd(JSContext *js, JSValueConst this, int argc, JSValueConst *argv)
case 231:
ret = constraint2js(constraint_make(cpSimpleMotorNew(js2body(argv[1]), js2body(argv[2]), js2number(argv[3]))));
break;
case 232:
ret = number2js(js2sprite(argv[1])->parallax);
break;
case 233:
js2sprite(argv[1])->parallax = js2number(argv[2]);
break;
case 234:
ret = emitter2js(make_emitter());
break;
@ -1406,17 +1153,12 @@ JSValue duk_cmd(JSContext *js, JSValueConst this, int argc, JSValueConst *argv)
break;
case 249:
str = JS_ToCString(js,argv[2]);
js2emitter(argv[1])->texture = texture_pullfromfile(str);
js2emitter(argv[1])->texture = texture_from_file(str);
break;
case 250:
sapp_show_keyboard(js2bool(argv[1]));
break;
case 251:
js2gameobject(argv[1])->warp_filter = js2bitmask(argv[2]);
break;
case 252:
ret = bitmask2js(js2gameobject(argv[1])->warp_filter);
break;
case 253:
ret = warp_gravity2js(warp_gravity_make());
break;
@ -1460,7 +1202,7 @@ JSValue duk_cmd(JSContext *js, JSValueConst this, int argc, JSValueConst *argv)
aspect_mode = js2int(argv[1]);
break;
case 265:
ret = vec2js((HMM_Vec2){mainwin.width, mainwin.height});
ret = vec22js((HMM_Vec2){mainwin.width, mainwin.height});
break;
case 266:
mainwin.width = js2number(argv[1]);
@ -1468,6 +1210,16 @@ JSValue duk_cmd(JSContext *js, JSValueConst this, int argc, JSValueConst *argv)
case 267:
mainwin.height = js2number(argv[1]);
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);
@ -1526,10 +1278,6 @@ JSValue duk_sys_cmd(JSContext *js, JSValueConst this, int argc, JSValueConst *ar
cpSpaceReindexStatic(space);
break;
case 2:
sim_pause();
break;
case 3:
sim_pause();
break;
@ -1613,14 +1361,6 @@ JSValue duk_set_body(JSContext *js, JSValueConst this, int argc, JSValueConst *a
cpBodySetVelocity(go->body, js2vec2(argv[2]).cp);
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:
cpBodyApplyForceAtWorldPoint(go->body, js2vec2(argv[2]).cp, cpBodyGetPosition(go->body));
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));
case 1:
return vec2js((HMM_Vec2)cpBodyGetPosition(go->body));
return vec22js((HMM_Vec2)cpBodyGetPosition(go->body));
case 2:
return JS_NewFloat64(js, cpBodyGetAngle(go->body));
case 3:
return vec2js((HMM_Vec2)cpBodyGetVelocity(go->body));
return vec22js((HMM_Vec2)cpBodyGetVelocity(go->body));
case 4:
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:
return JS_NewBool(js, phys2d_in_air(go->body));
case 8:
gameobject_free(go);
break;
}
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) {
gameobject *go = js2gameobject(argv[0]);
@ -1722,7 +1452,7 @@ JSValue duk_cmd_circle2d(JSContext *js, JSValueConst this, int argc, JSValueCons
return number2js(circle->radius);
case 3:
return vec2js(circle->offset);
return vec22js(circle->offset);
}
phys2d_shape_apply(&circle->shape);
@ -2085,6 +1815,85 @@ static const JSCFunctionListEntry js_sound_funcs[] = {
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) {
cpConstraintSetMaxForce(js2constraint(this)->c, js2number(val));
return JS_UNDEFINED;
@ -2165,11 +1974,6 @@ JSValue duk_cmd_points(JSContext *js, JSValueConst this, int argc, JSValueConst
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)
{
int cmd = js2int(argv[0]);
@ -2242,7 +2046,6 @@ void ffi_load() {
DUK_FUNC(set_body, 3)
DUK_FUNC(q_body, 2)
DUK_FUNC(sys_cmd, 1)
DUK_FUNC(make_sprite, 1)
DUK_FUNC(spline_cmd, 6)
DUK_FUNC(make_circle2d, 1)
DUK_FUNC(cmd_circle2d, 6)
@ -2265,7 +2068,7 @@ void ffi_load() {
JS_FreeValue(js,globalThis);
QJSCLASSPREP(ptr);
QJSCLASSPREP(gameobject);
QJSCLASSPREP_FUNCS(gameobject);
QJSCLASSPREP_FUNCS(dsp_node);
sound_proto = JS_NewObject(js);
@ -2275,7 +2078,8 @@ void ffi_load() {
QJSCLASSPREP_FUNCS(emitter);
QJSCLASSPREP_FUNCS(warp_gravity);
QJSCLASSPREP_FUNCS(warp_damp);
QJSCLASSPREP_FUNCS(sprite);
QJSCLASSPREP_FUNCS(texture);
QJSCLASSPREP_FUNCS(constraint);
QJSGLOBALCLASS(os);

View file

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

View file

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

View file

@ -21,6 +21,7 @@
extern struct rgba color_white;
extern struct rgba color_black;
extern struct rgba color_clear;
extern int renderMode;
@ -105,16 +106,9 @@ struct boundingbox {
};
struct rect {
float h, w, x, y;
};
/* Normalized S,T coordinates for rendering */
struct glrect {
float s0;
float s1;
float t0;
float t1;
float x,y,w,h;
};
typedef struct rect rect;
struct boundingbox cwh2bb(HMM_Vec2 c, HMM_Vec2 wh);
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) {
JSValue v = vec2js(vec);
JSValue v = vec22js(vec);
js_callee_exec(&c, 1, &v);
JS_FreeValue(js, v);
}

View file

@ -1,25 +1,17 @@
#include "sprite.h"
#include "datastream.h"
#include "font.h"
#include "gameobject.h"
#include "log.h"
#include "render.h"
#include "stb_ds.h"
#include "texture.h"
#include "timer.h"
#include <string.h>
#include <ctype.h>
#include <limits.h>
#include "HandmadeMath.h"
#include "freelist.h"
#include "sprite.sglsl.h"
#include "9slice.sglsl.h"
struct TextureOptions TEX_SPRITE = {1};
static struct sprite *sprites = NULL;
static sprite **sprites = NULL;
static sg_shader shader_sprite;
static sg_pipeline pip_sprite;
@ -51,89 +43,70 @@ struct slice9_vert {
struct rgba color;
};
int make_sprite(gameobject *go) {
struct sprite sprite = {
.t = t2d_unit,
.color = color_white,
.emissive = {0,0,0,0},
.tex = texture_pullfromfile(NULL),
.go = go,
.next = -1,
.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);
sprite *sprite_make()
{
sprite *sp = calloc(sizeof(*sp), 1);
sp->pos = (HMM_Vec2){0,0};
sp->scale = (HMM_Vec2){1,1};
sp->angle = 0;
sp->color = color_white;
sp->emissive = color_clear;
sp->go = NULL;
sp->enabled = 0;
freelist_kill(sprites,id);
sp->tex = texture_from_file(NULL);
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; }
struct sprite *id2sprite(int id) {
if (id < 0) return NULL;
return &sprites[id];
void sprite_free(sprite *sprite)
{
YughWarn("Freeing sprite %p.", sprite);
free(sprite);
for (int i = arrlen(sprites)-1; i >= 0; i--)
if (sprites[i] == sprite) {
arrdelswap(sprites,i);
return;
}
}
static int 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;
struct gameobject *gob = sprites[*b].go;
sprite *a = *sa;
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 1;
return -1;
}
void sprite_draw_all() {
if (arrlen(sprites) == 0) return;
sg_apply_pipeline(pip_sprite);
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++)
if (sprites[i].next == -1 && sprites[i].go != NULL && sprites[i].enabled)
arrpush(layers, i);
qsort(sprites, arrlen(sprites), sizeof(*sprites), sprite_sort);
if (!layers || arrlen(layers) == 0) return;
if (arrlen(layers) > 1)
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);
for (int i = 0; i < arrlen(sprites); i++)
sprite_draw(sprites[i]);
}
void sprite_initialize() {
freelist_size(sprites, 500);
shader_sprite = sg_make_shader(sprite_shader_desc(sg_query_backend()));
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];
float w = tex->width*st_s_w(r);
float h = tex->height*st_s_h(r);
float w = tex->width*r.w;
float h = tex->height*r.h;
HMM_Vec2 sposes[4] = {
{0.0,0.0},
{w,0.0},
{0.0,h},
{0,0},
{w,0},
{0,h},
{w,h}
};
@ -198,18 +171,18 @@ void tex_draw(struct Texture *tex, HMM_Mat3 m, struct glrect r, struct rgba colo
}
if (wrap) {
r.s1 *= wrapscale.x;
r.t1 *= wrapscale.y;
r.w *= wrapscale.x;
r.h *= wrapscale.y;
}
verts[0].uv.X = r.s0;
verts[0].uv.Y = r.t1;
verts[1].uv.X = r.s1;
verts[1].uv.Y = r.t1;
verts[2].uv.X = r.s0;
verts[2].uv.Y = r.t0;
verts[3].uv.X = r.s1;
verts[3].uv.Y = r.t0;
verts[0].uv.X = r.x;
verts[0].uv.Y = r.y+r.h;
verts[1].uv.X = r.x+r.w;
verts[1].uv.Y = r.y+r.h;
verts[2].uv.X = r.x;
verts[2].uv.Y = r.y;
verts[3].uv.X = r.x+r.w;
verts[3].uv.Y = r.y;
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++;
}
transform2d sprite2t(sprite *s)
{
return (transform2d){
.pos = s->pos,
.scale = s->scale,
.angle = s->angle
};
}
void sprite_draw(struct sprite *sprite) {
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.y += (cam_pos().y - (cam_pos().y/sprite->parallax));
HMM_Mat3 m = transform2d2mat(t);
HMM_Mat3 sm = transform2d2mat(sprite->t);
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);
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);
}
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_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);
}
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
struct sprite {
transform2d t;
HMM_Vec2 pos;
HMM_Vec2 scale;
float angle;
struct rgba color;
struct rgba emissive;
gameobject *go;
struct Texture *tex;
struct glrect frame;
texture *tex;
struct rect frame;
int enabled;
int next;
int drawmode;
float parallax;
unsigned int next;
};
typedef struct sprite sprite;
sprite *sprite_make();
int make_sprite(gameobject *go);
struct sprite *id2sprite(int id);
void sprite_free(sprite *sprite);
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_draw(struct sprite *sprite);
void sprite_draw_all();
unsigned int incrementAnimFrame(unsigned int interval, struct sprite *sprite);
void sprite_flush();
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"
#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 {
char *key;
struct Texture *value;
struct texture *value;
} *texhash = NULL;
struct Texture *tex_default;
struct Texture *texture_notex() { return texture_pullfromfile("icons/no_tex.gif"); }
struct texture *tex_default;
struct texture *texture_notex() { return texture_from_file("icons/no_tex.gif"); }
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)
{
struct Texture *t = texture_pullfromfile(path);
struct texture *t = texture_from_file(path);
return t->frames;
}
int *gif_delays(const char *path)
{
struct Texture *t = texture_pullfromfile(path);
struct texture *t = texture_from_file(path);
return t->delays;
}
/* 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 (shlen(texhash) == 0) sh_new_arena(texhash);
@ -98,8 +98,7 @@ struct Texture *texture_pullfromfile(const char *path) {
unsigned char *data;
struct Texture *tex = calloc(1, sizeof(*tex));
tex->opts.sprite = 1;
struct texture *tex = calloc(1, sizeof(*tex));
int n;
@ -152,12 +151,7 @@ struct Texture *texture_pullfromfile(const char *path) {
tex->data = data;
int filter;
if (tex->opts.sprite) {
filter = SG_FILTER_NEAREST;
} else {
filter = SG_FILTER_LINEAR;
}
int filter = SG_FILTER_NEAREST;
sg_image_data sg_img_data;
@ -199,13 +193,18 @@ struct Texture *texture_pullfromfile(const char *path) {
for (int i = 1; i < mips; i++)
free(mipdata[i]);
return tex;
}
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++) {
if (tex == texhash[i].value) {
YughInfo("Found key %s", texhash[i].key);
@ -216,10 +215,9 @@ char *tex_get_path(struct Texture *tex) {
return "";
}
struct Texture *texture_fromdata(void *raw, long size)
struct texture *texture_fromdata(void *raw, long size)
{
struct Texture *tex = calloc(1, sizeof(*tex));
tex->opts.sprite = 1;
struct texture *tex = calloc(1, sizeof(*tex));
int n;
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;
int filter;
if (tex->opts.sprite) {
filter = SG_FILTER_NEAREST;
} else {
filter = SG_FILTER_LINEAR;
}
int filter = SG_FILTER_NEAREST;
sg_image_data sg_img_data;
@ -283,9 +276,7 @@ struct Texture *texture_fromdata(void *raw, long size)
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};
HMM_Vec2 d;
d.x = tex->width;
@ -293,10 +284,6 @@ HMM_Vec2 tex_get_dimensions(struct Texture *tex) {
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); }
double grad (int hash, double x, double y, double z)
{

View file

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

View file

@ -19,7 +19,7 @@ struct window mainwin;
static struct window *windows = NULL;
struct Texture *icon = NULL;
struct texture *icon = NULL;
void window_resize(int width, int height)
{
@ -52,7 +52,7 @@ void window_suspended(int s)
}
void window_set_icon(const char *png) {
icon = texture_pullfromfile(png);
icon = texture_from_file(png);
window_seticon(&mainwin, icon);
}
@ -71,7 +71,7 @@ void window_togglefullscreen(struct window *w) {
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 {
int size;

View file

@ -16,7 +16,7 @@ struct window {
int focus;
int shown;
};
struct Texture;
struct texture;
extern struct window mainwin;
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_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);