Compare commits

...

5 commits

34 changed files with 1038 additions and 1060 deletions

View file

@ -66,7 +66,7 @@ else ifeq ($(OPT), 1)
CPPFLAGS += -O3 -flto
INFO :=$(INFO)_opt
else
CPPFLAGS += -O0
CPPFLAGS += -O2
endif
CPPFLAGS += -DHAVE_CEIL -DCP_USE_CGTYPES=0 -DCP_USE_DOUBLES=0 -DHAVE_FLOOR -DHAVE_FMOD -DHAVE_LRINT -DHAVE_LRINTF $(includeflag) -MD $(WARNING_FLAGS) -I. -DVER=\"$(SEM)\" -DCOM=\"$(COM)\" -DINFO=\"$(INFO)\" #-DENABLE_SINC_MEDIUM_CONVERTER -DENABLE_SINC_FAST_CONVERTER -DCP_COLLISION_TYPE_TYPE=uintptr_t -DCP_BITMASK_TYPE=uintptr_t

View file

@ -820,6 +820,12 @@ Object.defineProperty(String.prototype, 'splice', {
}
});
Object.defineProperty(String.prototype, 'sub', {
value: function(index, str) {
return this.slice(0,index) + str + this.slice(index+str.length);
}
});
Object.defineProperty(String.prototype, 'rm', {
value: function(index, endidx = index+1) { return this.slice(0,index) + this.slice(endidx); }
});
@ -1551,10 +1557,63 @@ Math.sortpointsccw = function(points)
return ccw.map(function(x) { return x.add(cm); });
}
var yaml = {};
yaml.tojson = function(yaml)
{
yaml = yaml.replace(/(\w+):/g, '"$1":');
yaml = yaml.replace(/: ([\w\.]+)/g, ': "$1"');
yaml = yaml.split("\n");
var cont = {};
var cur = 0;
for (var i = 0; i < yaml.length; i++) {
var line = yaml[i];
var indent = line.search(/\S/);
if (indent > cur) {
if (line[indent] == "-") {
cont[indent] = "array";
yaml[i] = line.sub(indent, '[');
} else {
cont[indent] = "obj";
yaml[i] = line.sub(indent-1, '{');
}
}
if (indent < cur) {
while (cur > indent) {
if (cont[cur] === "obj")
yaml[i-1] = yaml[i-1] + "}";
else if (cont[cur] === "array")
yaml[i-1] = yaml[i-1] + "]";
delete cont[cur];
cur--;
}
}
if (indent === cur) {
if (yaml[i][indent] === '-')
yaml[i] = yaml[i].sub(indent,',');
else
yaml[i-1] = yaml[i-1] + ',';
}
cur = indent;
}
yaml = "{" + yaml.join("\n") + "}";
yaml = yaml.replace(/\s/g, '');
yaml = yaml.replace(/,}/g, '}');
yaml = yaml.replace(/,]/g, ']');
return yaml;
}
return {
convert,
time,
json,
Vector,
bbox
bbox,
yaml
};

View file

@ -13,6 +13,10 @@ this.view2world = function(pos) {
pos = pos.scale(this.zoom);
pos = pos.add(this.pos);
}
if (window.mode === window.modetypes.expand) {
pos = pos.sub(window.size.scale(0.5));
pos = pos.scale([window.rendersize.x/window.size.x, window.rendersize.y/window.size.y]);
}
return pos;
};
this.world2view = function(pos) {
@ -25,6 +29,9 @@ this.world2view = function(pos) {
pos = pos.sub(this.pos);
pos = pos.scale(1/this.zoom);
pos = pos.add(window.size.scale(0.5));
}
if (window.mode === window.modetypes.expand) {
}
return pos;
};

View file

@ -177,7 +177,7 @@ Object.mixin(os.sprite(true), {
height() { return this.dimensions().y; },
});
globalThis.allsprites = {};
os.sprite(true).make = function(go)
os.sprite().make = function(go)
{
var sp = os.sprite();
sp.go = go;

View file

@ -344,7 +344,7 @@ var editor = {
root = root ? root + "." : root;
Object.entries(obj.objects).forEach(function(x) {
var p = root + x[0];
render.text(p, x[1].screenpos(), 1, editor.color_depths[depth]);
render.text(p, x[1].this2screen(), 1, editor.color_depths[depth]);
editor.draw_objects_names(x[1], p, depth+1);
});
},
@ -402,7 +402,7 @@ var editor = {
if (this.comp_info && this.sel_comp)
render.text(input.print_pawn_kbm(this.sel_comp,false), [100,700],1);
render.cross(editor.edit_level.screenpos(),3,Color.blue);
render.cross(editor.edit_level.this2screen(),3,Color.blue);
var thiso = editor.get_this();
var clvl = thiso;
@ -447,16 +447,16 @@ var editor = {
render.text("$$$$$$", [0,ypos],1,editor.color_depths[depth]);
this.selectlist.forEach(function(x) {
render.text(x.urstr(), x.screenpos().add([0, render.font.linegap*2]), 1, Color.editor.ur);
render.text(x.pos.map(function(x) { return Math.round(x); }), x.screenpos());
render.cross(x.screenpos(), 10, Color.blue);
render.text(x.urstr(), x.this2screen().add([0, render.font.linegap*2]), 1, Color.editor.ur);
render.text(x.pos.map(function(x) { return Math.round(x); }), x.this2screen());
render.cross(x.this2screen(), 10, Color.blue);
});
Object.entries(thiso.objects).forEach(function(x) {
var p = x[1].namestr();
render.text(p, x[1].screenpos().add([0,render.font.linegap]),1,editor.color_depths[depth]);
render.point(x[1].screenpos(),5,Color.blue.alpha(0.3));
render.point(x[1].screenpos(), 1, Color.red);
render.text(p, x[1].this2screen().add([0,render.font.linegap]),1,editor.color_depths[depth]);
render.point(x[1].this2screen(),5,Color.blue.alpha(0.3));
render.point(x[1].this2screen(), 1, Color.red);
});
var mg = physics.pos_query(input.mouse.worldpos());
@ -474,7 +474,7 @@ var editor = {
for (var key in this.selectlist[0].components) {
var selected = this.sel_comp === this.selectlist[0].components[key];
var str = (selected ? ">" : " ") + key + " [" + this.selectlist[0].components[key].toString() + "]";
render.text(str, this.selectlist[0].screenpos().add([0,-render.font.linegap*(i++)]));
render.text(str, this.selectlist[0].this2screen().add([0,-render.font.linegap*(i++)]));
}
if (this.sel_comp) {
@ -879,7 +879,7 @@ editor.inputs['C-s'] = function() {
}
var savejs = saveobj.json_obj();
var tur = saveobj.get_ur();
var tur = saveobj.ur;
if (!tur) {
console.warn(`Can't save object because it has no ur.`);
return;

View file

@ -289,7 +289,6 @@ game.engine_start = function(s) {
gggstart(function() {
global.mixin("scripts/sound.js");
world_start();
go_init();
window.set_icon(os.make_texture("icons/moon.gif"))
Object.readonly(window.__proto__, 'vsync');
Object.readonly(window.__proto__, 'enable_dragndrop');
@ -330,9 +329,9 @@ function process()
}
var st = profile.now();
if (!game.camera)
prosperon.window_render(world, 1);
prosperon.window_render(world.transform, 1);
else
prosperon.window_render(game.camera, game.camera.zoom);
prosperon.window_render(game.camera.transform, game.camera.zoom);
render.set_camera();
@ -627,7 +626,8 @@ global.mixin("scripts/actor");
global.mixin("scripts/entity");
function world_start() {
globalThis.world = os.make_gameobject();
globalThis.world = Object.create(entity);
world.transform = os.make_transform2d();
world.objects = {};
world.toString = function() { return "world"; };
world.ur = "world";

View file

@ -12,16 +12,398 @@ function obj_unique_name(name, obj) {
return n;
}
var gameobject = {
function unique_name(list, name = "new_object") {
var str = name.replaceAll('.', '_');
var n = 1;
var t = str;
while (list.indexOf(t) !== -1) {
t = str + n;
n++;
}
return t;
};
var entity = {
get_comp_by_name(name) {
var comps = [];
for (var c of Object.values(this.components))
if (c.comp === name) comps.push(c);
if (comps.length) return comps;
return undefined;
},
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;
},
full_path() { return this.path_from(world); },
clear() {
for (var k in this.objects) {
this.objects[k].kill();
};
this.objects = {};
},
sync() {
this.components.forEach(function(x) { x.sync?.(); });
this.objects.forEach(function(x) { x.sync(); });
},
delay(fn, seconds) {
var timers = this.timers;
var stop = function() {
timers.remove(stop);
execute = undefined;
stop = undefined;
rm?.();
rm = undefined;
update = undefined;
}
function execute() {
fn();
stop?.();
}
stop.remain = seconds;
stop.seconds = seconds;
stop.pct = function() { return 1 - (stop.remain/stop.seconds); };
function update(dt) {
stop.remain -= dt;
if (stop.remain <= 0) execute();
}
var rm = Register.update.register(update);
timers.push(stop);
return stop;
},
cry(file) { return audio.cry(file); },
get pos() { return this.transform.pos; },
set pos(x) { this.transform.pos = x; },
get angle() { return this.transform.angle; },
set angle(x) { this.transform.angle = x; },
get scale() { return this.transform.scale; },
set scale(x) { this.transform.scale = x; },
move(vec) { this.pos = this.pos.add(vec); },
rotate(x) { this.angle += x; },
grow(vec) {
if (typeof vec === 'number') vec = [vec,vec];
this.scale = this.scale.map((x,i) => x*vec[i]);
},
/* Reparent 'this' to be 'parent's child */
reparent(parent) {
assert(parent, `Tried to reparent ${this.toString()} to nothing.`);
console.spam(`parenting ${this.toString()} to ${parent.toString()}`);
if (this.master === parent) {
console.warn("not reparenting ...");
console.warn(`${this.master} is the same as ${parent}`);
return;
}
var name = unique_name(Object.keys(parent), this.name);
this.name = name;
this.master?.remove_obj(this);
this.master = parent;
parent.objects[this.guid] = this;
parent[name] = this;
Object.hide(parent, name);
},
remove_obj(obj) {
delete this.objects[obj.guid];
delete this[obj.name];
Object.unhide(this, obj.name);
},
spawn(text, config, callback) {
var st = profile.now();
var ent = Object.create(entity);
ent.transform = os.make_transform2d();
ent.guid = prosperon.guid();
ent.components = {};
ent.objects = {};
ent.timers = [];
if (!text)
ent.ur = emptyur;
else if (typeof text === 'object' && text) {// assume it's an ur
ent.ur = text;
text = ent.ur.text;
config = [ent.ur.data, config].filter(x => x).flat();
}
else {
ent.ur = getur(text, config);
text = ent.ur.text;
config = [ent.ur.data, config];
}
if (typeof text === 'string')
use(text, ent);
else if (Array.isArray(text))
text.forEach(path => use(path,ent));
if (typeof config === 'string')
Object.merge(ent, json.decode(Resources.replstrs(config)));
else if (Array.isArray(config))
config.forEach(function(path) {
if (typeof path === 'string') {
console.info(`ingesting ${path} ...`);
Object.merge(ent, json.decode(Resources.replstrs(path)));
}
else if (typeof path === 'object')
Object.merge(ent,path);
});
ent.reparent(this);
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);
if (typeof ent.load === 'function') ent.load();
if (sim.playing())
if (typeof ent.start === 'function') ent.start();
Object.hide(ent, 'ur', 'components', 'objects', 'timers', 'guid', 'master');
ent._ed = {
selectable: true,
dirty: false,
inst: false,
urdiff: {}
};
Object.hide(ent, '_ed');
ent.sync();
if (!Object.empty(ent.objects)) {
var o = ent.objects;
delete ent.objects;
ent.objects = {};
for (var i in o) {
console.info(`creating ${i} on ${ent.toString()}`);
var newur = o[i].ur;
delete o[i].ur;
var n = ent.spawn(ur[newur], o[i]);
ent.rename_obj(n.toString(), i);
}
}
if (ent.tag) game.tag_add(ent.tag, ent);
if (callback) callback(ent);
ent.ur.fresh ??= json.decode(json.encode(ent));
ent.ur.fresh.objects = {};
for (var i in ent.objects)
ent.ur.fresh.objects[i] = ent.objects[i].instance_obj();
profile.addreport(entityreport, ent.ur.name, st);
return ent;
},
disable() { this.components.forEach(function(x) { x.disable(); }); },
enable() { this.components.forEach(function(x) { x.enable(); }); },
this2screen(pos) { return game.camera.world2view(this.this2world(pos)); },
screen2this(pos) { return this.world2this(game.camera.view2world(pos)); },
/* Make a unique object the same as its prototype */
revert() { Object.merge(this, this.ur.fresh); },
name: "new_object",
toString() { return this.name; },
width() {
var bb = this.boundingbox();
return bb.r - bb.l;
},
height() {
var bb = this.boundingbox();
return bb.t - bb.b;
},
flipx() { return this.scale.x < 0; },
flipy() { return this.scale.y < 0; },
mirror(plane) { this.scale = Vector.reflect(this.scale, plane); },
/* Bounding box of the object in world dimensions */
boundingbox() {
var boxes = [];
boxes.push({
t: 0,
r: 0,
b: 0,
l: 0
});
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());
var bb = boxes.shift();
boxes.forEach(function(x) { bb = bbox.expand(bb, x); });
bb = bbox.move(bb, this.pos);
return bb ? bb : bbox.fromcwh([0, 0], [0, 0]);
},
/* The unique components of this object. Its diff. */
json_obj(depth=0) {
var fresh = this.ur.fresh;
var thiso = json.decode(json.encode(this)); // TODO: SLOW. Used to ignore properties in toJSON of components.
var d = ediff(thiso, fresh);
d ??= {};
fresh.objects ??= {};
var curobjs = {};
for (var o in this.objects)
curobjs[o] = this.objects[o].instance_obj();
var odiff = ediff(curobjs, fresh.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.name;
return t;
},
transform() {
var t = {};
t.pos = this.get_pos(this.master).map(x => Math.places(x, 0));
t.angle = Math.places(this.get_angle(this.master), 4);
t.scale = this.get_scale(this.master).map(x => Math.places(x, 2));;
return t;
},
dup(diff) {
var n = this.master.spawn(this.ur);
Object.totalmerge(n, this.transform());
return n;
},
kill() {
if (this.__kill) return;
this.__kill = true;
console.spam(`Killing entity of type ${this.ur}`);
this.timers.forEach(t => t());
this.timers = [];
Event.rm_obj(this);
input.do_uncontrol(this);
if (this.master) {
this.master.remove_obj(this);
this.master = undefined;
}
for (var key in this.components) {
this.components[key].kill?.();
this.components[key].gameobject = undefined;
this[key].enabled = false;
delete this.components[key];
delete this[key];
}
delete this.components;
this.clear();
if (typeof this.stop === 'function') this.stop();
game.tag_clear_guid(this.guid);
for (var i in this) {
if (typeof this[i] === 'object') delete this[i];
if (typeof this[i] === 'function') delete this[i];
}
},
make_objs(objs) {
for (var prop in objs) {
say(`spawning ${json.encode(objs[prop])}`);
var newobj = this.spawn(objs[prop]);
}
},
rename_obj(name, newname) {
if (!this.objects[name]) {
console.warn(`No object with name ${name}. Could not rename to ${newname}.`);
return;
}
if (name === newname) {
Object.hide(this, name);
return;
}
if (this.objects[newname])
return;
this.objects[newname] = this.objects[name];
this[newname] = this[name];
this[newname].toString = function() { return newname; };
Object.hide(this, newname);
delete this.objects[name];
delete this[name];
return this.objects[newname];
},
add_component(comp, data, name = comp.toString()) {
if (typeof comp.make !== 'function') return;
name = obj_unique_name(name, this);
this[name] = comp.make(this);
this[name].comp = comp.toString();
this.components[name] = this[name];
if (data)
Object.assign(this[name], data);
return this[name];
},
};
var gameobject = {
check_dirty() {
this._ed.urdiff = this.json_obj();
this._ed.dirty = !Object.empty(this._ed.urdiff);
@ -50,9 +432,6 @@ var gameobject = {
return str;
},
full_path() {
return this.path_from(world);
},
/* pin this object to the to object */
pin(to) {
var p = joint.pin(this,to);
@ -95,63 +474,6 @@ var gameobject = {
var p = joint.motor(this, to, 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 timers = this.timers;
var stop = function() {
timers.remove(stop);
execute = undefined;
stop = undefined;
rm?.();
rm = undefined;
update = undefined;
}
function execute() {
fn();
stop?.();
}
stop.remain = seconds;
stop.seconds = seconds;
stop.pct = function() { return 1 - (stop.remain/stop.seconds); };
function update(dt) {
stop.remain -= dt;
if (stop.remain <= 0) execute();
}
var rm = Register.update.register(update);
timers.push(stop);
return stop;
},
cry(file) { return audio.cry(file); },
set pos(x) { this.set_pos(x); },
get pos() { return this.rpos; },
set angle(x) { this.set_angle(x); },
get angle() { return this.rangle; },
set scale(x) { this.set_scale(x); },
get scale() { return this.rscale; },
set_pos(x, relative = world) {
var newpos = relative.this2world(x);
var move = newpos.sub(this.pos);
@ -197,285 +519,8 @@ var gameobject = {
return this.scale.map((x,i) => x/masterscale[i]);
},
/* Moving, rotating, scaling functions, world relative */
move(vec) { this.set_pos(this.pos.add(vec)); },
rotate(x) { this.set_angle(this.angle + x); },
grow(vec) {
if (typeof vec === 'number') vec = [vec,vec,vec];
this.set_scale(this.scale.map((x, i) => x * vec[i]));
},
screenpos() { return game.camera.world2view(this.pos); },
get_ur() { return this.ur; },
/* spawn an entity
text can be:
the file path of a script
an ur object
nothing
*/
spawn(text, config, callback) {
var st = profile.now();
var ent = os.make_gameobject();
ent.guid = prosperon.guid();
ent.components = {};
ent.objects = {};
ent.timers = [];
Object.mixin(ent, {
set category(n) {
if (n === 0) {
this.categories = n;
return;
}
var cat = (1 << (n-1));
this.categories = cat;
},
get category() {
if (this.categories === 0) return 0;
var pos = 0;
var num = this.categories;
while (num > 0) {
if (num & 1) {
break;
}
pos++;
num >>>= 1;
}
return pos+1;
}
});
if (typeof text === 'object' && text) {// assume it's an ur
ent.ur = text;
text = ent.ur.text;
config = [ent.ur.data, config].filter(x => x).flat();
}
else {
ent.ur = getur(text, config);
text = ent.ur.text;
config = [ent.ur.data, config];
}
if (typeof text === 'string')
use(text, ent);
else if (Array.isArray(text))
text.forEach(path => use(path,ent));
if (typeof config === 'string')
Object.merge(ent, json.decode(Resources.replstrs(config)));
else if (Array.isArray(config))
config.forEach(function(path) {
if (typeof path === 'string') {
console.info(`ingesting ${path} ...`);
Object.merge(ent, json.decode(Resources.replstrs(path)));
}
else if (typeof path === 'object')
Object.merge(ent,path);
});
ent.reparent(this);
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);
if (typeof ent.load === 'function') ent.load();
if (sim.playing())
if (typeof ent.start === 'function') ent.start();
Object.hide(ent, 'ur', 'components', 'objects', 'timers', 'guid', 'master', 'categories');
ent._ed = {
selectable: true,
dirty: false,
inst: false,
urdiff: {}
};
Object.hide(ent, '_ed');
ent.sync();
if (!Object.empty(ent.objects)) {
var o = ent.objects;
delete ent.objects;
ent.objects = {};
for (var i in o) {
console.info(`creating ${i} on ${ent.toString()}`);
var newur = o[i].ur;
delete o[i].ur;
var n = ent.spawn(ur[newur], o[i]);
ent.rename_obj(n.toString(), i);
}
}
if (ent.tag) game.tag_add(ent.tag, ent);
if (callback) callback(ent);
ent.ur.fresh ??= json.decode(json.encode(ent));
ent.ur.fresh.objects = {};
for (var i in ent.objects)
ent.ur.fresh.objects[i] = ent.objects[i].instance_obj();
profile.addreport(entityreport, ent.ur.name, st);
return ent;
},
/* Reparent 'this' to be 'parent's child */
reparent(parent) {
assert(parent, `Tried to reparent ${this.toString()} to nothing.`);
console.spam(`parenting ${this.toString()} to ${parent.toString()}`);
if (this.master === parent) {
console.warn("not reparenting ...");
console.warn(`${this.master} is the same as ${parent}`);
return;
}
this.master?.remove_obj(this);
this.master = parent;
function unique_name(list, name = "new_object") {
var str = name.replaceAll('.', '_');
var n = 1;
var t = str;
while (list.indexOf(t) !== -1) {
t = str + n;
n++;
}
return t;
};
var name = unique_name(Object.keys(parent.objects), this.ur.name);
parent.objects[name] = this;
parent[name] = this;
Object.hide(parent, name);
this.toString = function() { return name; };
},
remove_obj(obj) {
delete this.objects[obj.toString()];
delete this[obj.toString()];
Object.unhide(this, obj.toString());
},
components: {},
objects: {},
master: undefined,
this2screen(pos) { return game.camera.world2view(this.this2world(pos)); },
screen2this(pos) { return this.world2this(game.camera.view2world(pos)); },
in_air() { return this.in_air(); },
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();
return bb.r - bb.l;
},
height() {
var bb = this.boundingbox();
return bb.t - bb.b;
},
/* Make a unique object the same as its prototype */
revert() { Object.merge(this, this.ur.fresh); },
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); },
disable() { this.components.forEach(function(x) { x.disable(); }); },
enable() { this.components.forEach(function(x) { x.enable(); }); },
/* Bounding box of the object in world dimensions */
boundingbox() {
var boxes = [];
boxes.push({
t: 0,
r: 0,
b: 0,
l: 0
});
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());
var bb = boxes.shift();
boxes.forEach(function(x) { bb = bbox.expand(bb, x); });
bb = bbox.move(bb, this.pos);
return bb ? bb : bbox.fromcwh([0, 0], [0, 0]);
},
/* The unique components of this object. Its diff. */
json_obj(depth=0) {
var fresh = this.ur.fresh;
var thiso = json.decode(json.encode(this)); // TODO: SLOW. Used to ignore properties in toJSON of components.
var d = ediff(thiso, fresh);
d ??= {};
fresh.objects ??= {};
var curobjs = {};
for (var o in this.objects)
curobjs[o] = this.objects[o].instance_obj();
var odiff = ediff(curobjs, fresh.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.name;
return t;
},
transform() {
var t = {};
t.pos = this.get_pos(this.master).map(x => Math.places(x, 0));
t.angle = Math.places(this.get_angle(this.master), 4);
t.scale = this.get_scale(this.master).map(x => Math.places(x, 2));;
return t;
},
/* Velocity and angular velocity of the object */
phys_obj() {
var phys = {};
@ -483,111 +528,32 @@ var gameobject = {
phys.angularvelocity = this.angularvelocity;
return phys;
},
phys_mat() {
return {
friction: this.friction,
elasticity: this.elasticity
set category(n) {
if (n === 0) {
this.categories = n;
return;
}
var cat = (1 << (n-1));
this.categories = cat;
},
dup(diff) {
var n = this.master.spawn(this.ur);
Object.totalmerge(n, this.transform());
return n;
},
kill() {
if (this.__kill) return;
this.__kill = true;
console.spam(`Killing entity of type ${this.ur}`);
this.timers.forEach(t => t());
this.timers = [];
Event.rm_obj(this);
input.do_uncontrol(this);
if (this.master) {
this.master.remove_obj(this);
this.master = undefined;
get category() {
if (this.categories === 0) return 0;
var pos = 0;
var num = this.categories;
while (num > 0) {
if (num & 1) {
break;
}
pos++;
num >>>= 1;
}
for (var key in this.components) {
this.components[key].kill?.();
this.components[key].gameobject = undefined;
this[key].enabled = false;
delete this.components[key];
delete this[key];
}
delete this.components;
this.clear();
if (typeof this.stop === 'function') this.stop();
game.tag_clear_guid(this.guid);
for (var i in this) {
if (typeof this[i] === 'object') delete this[i];
if (typeof this[i] === 'function') delete this[i];
}
},
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) {
say(`spawning ${json.encode(objs[prop])}`);
var newobj = this.spawn(objs[prop]);
}
},
rename_obj(name, newname) {
if (!this.objects[name]) {
console.warn(`No object with name ${name}. Could not rename to ${newname}.`);
return;
}
if (name === newname) {
Object.hide(this, name);
return;
}
if (this.objects[newname])
return;
this.objects[newname] = this.objects[name];
this[newname] = this[name];
this[newname].toString = function() { return newname; };
Object.hide(this, newname);
delete this.objects[name];
delete this[name];
return this.objects[newname];
},
add_component(comp, data, name = comp.toString()) {
if (typeof comp.make !== 'function') return;
name = obj_unique_name(name, this);
this[name] = comp.make(this);
this[name].comp = comp.toString();
this.components[name] = this[name];
if (data)
Object.assign(this[name], data);
return this[name];
},
}
function go_init() {
var gop = os.make_gameobject().__proto__;
Object.mixin(gop, gameobject);
gop.sync = function() {
this.selfsync();
this.components.forEach(function(x) { x.sync?.(); });
this.objects.forEach(function(x) { x.sync?.(); });
return pos+1;
}
}
gameobject.spawn.doc = `Spawn an entity of type 'ur' on this entity. Returns the spawned entity.`;
entity.spawn.doc = `Spawn an entity of type 'ur' on this entity. Returns the spawned entity.`;
gameobject.doc = {
doc: "All objects in the game created through spawning have these attributes.",
@ -695,8 +661,18 @@ function apply_ur(u, ent) {
}
}
var emptyur = {
name: "empty"
}
var getur = function(text, data)
{
if (!text && !data) {
console.info('empty ur');
return {
name: "empty"
};
}
var urstr = text + "+" + data;
if (!ur[urstr]) {
ur[urstr] = {
@ -784,4 +760,4 @@ game.ur.save = function(str)
}
}
return { go_init }
return { entity }

View file

@ -124,9 +124,9 @@ render.text = function(str, pos, size = 1, color = Color.white, wrap = -1, ancho
};
render.image = function(tex, pos, rotation = 0, color = Color.white, dimensions = [tex.width, tex.height]) {
var scale = [dimensions.x/tex.width, dimensions.y/tex.height];
gui.img(tex,pos, scale, 0.0, false, [0.0,0.0], color);
return bbox.fromcwh([0,0], [tex.width,tex.height]);
//var scale = [dimensions.x/tex.width, dimensions.y/tex.height];
//gui.img(tex,pos, scale, 0.0, false, [0.0,0.0], color);
//return bbox.fromcwh([0,0], [tex.width,tex.height]);
}
render.fontcache = {};

View file

@ -476,9 +476,54 @@ Cmdline.register_cmd("l", function(n) {
console.level = n;
}, "Set log level.");
function convertYAMLtoJSON(yamlString) {
const lines = yamlString.split('\n');
const jsonObj = {};
let currentKey = '';
let currentValue = '';
let currentDepth = 0;
for (let i = 0; i < lines.length; i++) {
const line = lines[i].trim();
if (!line || line.startsWith('#')) {
continue;
}
const depth = (line.match(/^\s+/g) || [''])[0].length;
const keyValue = line.split(':');
const key = keyValue[0].trim();
const value = keyValue[1].trim();
if (depth > currentDepth) {
jsonObj[currentKey] = convertYAMLtoJSON(currentValue);
currentKey = key;
currentValue = value;
} else if (depth === currentDepth) {
jsonObj[currentKey] = convertYAMLtoJSON(currentValue);
currentKey = key;
currentValue = value;
} else {
jsonObj[currentKey] = convertYAMLtoJSON(currentValue);
currentKey = '';
currentValue = '';
i--; // To reprocess the current line with updated values
}
currentDepth = depth;
}
if (currentKey) {
jsonObj[currentKey] = convertYAMLtoJSON(currentValue);
}
return jsonObj;
}
return {
Resources,
Cmdline,
cmd_args
cmd_args,
convertYAMLtoJSON
};

View file

@ -5,9 +5,6 @@
#include "stb_ds.h"
#include "gameobject.h"
//#include "diffuse.sglsl.h"
#include "unlit.sglsl.h"
#include "render.h"
#include "HandmadeMath.h"
@ -30,50 +27,8 @@ static void processnode();
static void processmesh();
static void processtexture();
static sg_shader model_shader;
static sg_pipeline model_pipe;
struct bone_weights {
char b1;
char b2;
char b3;
char b4;
};
struct joints {
char j1;
char j2;
char j3;
char j4;
};
void model_init() {
model_shader = sg_make_shader(unlit_shader_desc(sg_query_backend()));
model_pipe = sg_make_pipeline(&(sg_pipeline_desc){
.shader = model_shader,
.layout = {
.attrs = {
[0].format = SG_VERTEXFORMAT_FLOAT3,
[1].format = SG_VERTEXFORMAT_USHORT2N,
[1].buffer_index = 1,
[2].format = SG_VERTEXFORMAT_UINT10_N2,
[2].buffer_index = 2,
[3] = {
.format = SG_VERTEXFORMAT_UBYTE4N,
.buffer_index = 3
},
[4] = {
.format = SG_VERTEXFORMAT_UBYTE4,
.buffer_index = 4
}
},
},
.index_type = SG_INDEXTYPE_UINT16,
.cull_mode = SG_CULLMODE_FRONT,
.depth.write_enabled = true,
.depth.compare = SG_COMPAREFUNC_LESS_EQUAL
});
}
static cgltf_data *cdata;
static char *cpath;
cgltf_attribute *get_attr_type(cgltf_primitive *p, cgltf_attribute_type t)
{
@ -109,17 +64,21 @@ void mesh_add_material(primitive *prim, cgltf_material *mat)
{
if (!mat) return;
if (mat->has_pbr_metallic_roughness) {
prim->mat = calloc(sizeof(*prim->mat), 1);
material *pmat = prim->mat;
if (mat->has_pbr_metallic_roughness && mat->pbr_metallic_roughness.base_color_texture.texture) {
cgltf_image *img = mat->pbr_metallic_roughness.base_color_texture.texture->image;
if (img->buffer_view) {
cgltf_buffer_view *buf = img->buffer_view;
prim->bind.fs.images[0] = texture_fromdata(buf->buffer->data, buf->size)->id;
} else
prim->bind.fs.images[0] = texture_from_file(img->uri)->id;
if (img->buffer_view) {
cgltf_buffer_view *buf = img->buffer_view;
pmat->diffuse = texture_fromdata(buf->buffer->data, buf->size);
} else {
char *path = makepath(dirname(cpath), img->uri);
pmat->diffuse = texture_from_file(path);
free(path);
}
} else
prim->bind.fs.images[0] = texture_from_file("icons/moon.gif")->id;
prim->bind.fs.samplers[0] = std_sampler;
pmat->diffuse = texture_from_file("icons/moon.gif");
}
sg_buffer texcoord_floats(float *f, int verts, int comp)
@ -147,6 +106,24 @@ sg_buffer normal_floats(float *f, int verts, int comp)
});
}
sg_buffer ubyten_buffer(float *f, int v, int c)
{
unsigned char b[v*c];
for (int i = 0; i < (v*c); i++)
b[i] = f[i]*255;
return sg_make_buffer(&(sg_buffer_desc){.data=SG_RANGE(b)});
}
sg_buffer ubyte_buffer(float *f, int v, int c)
{
unsigned char b[v*c];
for (int i = 0; i < (v*c); i++)
b[i] = f[i];
return sg_make_buffer(&(sg_buffer_desc){.data=SG_RANGE(b)});
}
sg_buffer joint_buf(float *f, int v, int c)
{
char joints[v*c];
@ -183,7 +160,7 @@ struct primitive mesh_add_primitive(cgltf_primitive *prim)
for (int i = 0; i < n; i++)
idxs[i] = fidx[i];
retp.bind.index_buffer = sg_make_buffer(&(sg_buffer_desc){
retp.idx = sg_make_buffer(&(sg_buffer_desc){
.data.ptr = idxs,
.data.size = sizeof(*idxs) * n,
.type = SG_BUFFERTYPE_INDEXBUFFER,
@ -201,7 +178,7 @@ struct primitive mesh_add_primitive(cgltf_primitive *prim)
for (int z = 0; z < c; z++)
idxs[z] = z;
retp.bind.index_buffer = sg_make_buffer(&(sg_buffer_desc){
retp.idx = sg_make_buffer(&(sg_buffer_desc){
.data.ptr = idxs,
.data.size = sizeof(uint16_t) * c,
.type = SG_BUFFERTYPE_INDEXBUFFER});
@ -211,7 +188,6 @@ struct primitive mesh_add_primitive(cgltf_primitive *prim)
printf("adding material\n");
mesh_add_material(&retp, prim->material);
int has_norm = 0;
for (int k = 0; k < prim->attributes_count; k++) {
cgltf_attribute attribute = prim->attributes[k];
@ -224,7 +200,7 @@ struct primitive mesh_add_primitive(cgltf_primitive *prim)
switch (attribute.type) {
case cgltf_attribute_type_position:
retp.bind.vertex_buffers[0] = sg_make_buffer(&(sg_buffer_desc){
retp.pos = sg_make_buffer(&(sg_buffer_desc){
.data.ptr = vs,
.data.size = sizeof(float) * n,
.label = "mesh vert buffer"
@ -232,26 +208,26 @@ struct primitive mesh_add_primitive(cgltf_primitive *prim)
break;
case cgltf_attribute_type_normal:
has_norm = 1;
retp.bind.vertex_buffers[2] = normal_floats(vs, verts, comp);
retp.norm = normal_floats(vs, verts, comp);
break;
case cgltf_attribute_type_tangent:
break;
case cgltf_attribute_type_color:
retp.color = ubyten_buffer(vs,verts,comp);
break;
case cgltf_attribute_type_weights:
retp.bind.vertex_buffers[3] = weight_buf(vs, verts, comp);
retp.weight = ubyten_buffer(vs, verts, comp);
break;
case cgltf_attribute_type_joints:
retp.bind.vertex_buffers[4] = joint_buf(vs, verts, comp);
retp.bone = ubyte_buffer(vs, verts, comp);
break;
case cgltf_attribute_type_texcoord:
retp.bind.vertex_buffers[1] = texcoord_floats(vs, verts, comp);
retp.uv = texcoord_floats(vs, verts, comp);
break;
case cgltf_attribute_type_invalid:
YughWarn("Invalid type.");
@ -259,12 +235,31 @@ struct primitive mesh_add_primitive(cgltf_primitive *prim)
case cgltf_attribute_type_custom:
break;
case cgltf_attribute_type_max_enum:
break;
}
}
if (!has_norm) {
if (!retp.bone.id) {
char joints[retp.idx_count*4];
memset(joints, 0, retp.idx_count*4);
retp.bone = sg_make_buffer(&(sg_buffer_desc){ .data = SG_RANGE(joints)});
}
if (!retp.weight.id) {
char weights[retp.idx_count*4];
memset(weights,0,retp.idx_count*4);
retp.weight = sg_make_buffer(&(sg_buffer_desc){ .data = SG_RANGE(weights)});
}
if (!retp.color.id) {
char colors[retp.idx_count*4];
memset(colors,0,retp.idx_count*4);
retp.color = sg_make_buffer(&(sg_buffer_desc) { .data = SG_RANGE(colors) });
}
if (!retp.norm.id) {
YughInfo("Making normals.");
cgltf_attribute *pa = get_attr_type(prim, cgltf_attribute_type_position);
int n = cgltf_accessor_unpack_floats(pa->data, NULL,0);
@ -283,7 +278,7 @@ struct primitive mesh_add_primitive(cgltf_primitive *prim)
face_norms[i] = face_norms[i+1] = face_norms[i+2] = packed_norm;
}
retp.bind.vertex_buffers[2] = sg_make_buffer(&(sg_buffer_desc){
retp.norm = sg_make_buffer(&(sg_buffer_desc){
.data.ptr = face_norms,
.data.size = sizeof(uint32_t) * verts});
}
@ -291,7 +286,7 @@ struct primitive mesh_add_primitive(cgltf_primitive *prim)
return retp;
}
static cgltf_data *cdata;
void model_add_cgltf_mesh(mesh *m, cgltf_mesh *gltf_mesh)
{
@ -382,7 +377,7 @@ void model_add_cgltf_skin(model *model, cgltf_skin *skin)
sk.root = model->nodes+(skin->skeleton-cdata->nodes);
for (int i = 0; i < 50; i++)
sk.binds[i] = HMM_M4D(1);
sk.binds[i] = MAT1;
for (int i = 0; i < skin->joints_count; i++) {
int offset = skin->joints[i]-cdata->nodes;
@ -417,6 +412,7 @@ void model_process_node(model *model, cgltf_node *node)
struct model *model_make(const char *path)
{
YughInfo("Making the model from %s.", path);
cpath = path;
cgltf_options options = {0};
cgltf_data *data = NULL;
cgltf_result result = cgltf_parse_file(&options, path, &data);
@ -458,8 +454,28 @@ void model_free(model *m)
}
sg_bindings primitive_bind(primitive *p)
{
sg_bindings b = {0};
b.vertex_buffers[MAT_POS] = p->pos;
b.vertex_buffers[MAT_UV] = p->uv;
b.vertex_buffers[MAT_NORM] = p->norm;
b.vertex_buffers[MAT_BONE] = p->bone;
b.vertex_buffers[MAT_WEIGHT] = p->weight;
b.vertex_buffers[MAT_COLOR] = p->color;
b.index_buffer = p->idx;
b.fs.images[0] = p->mat->diffuse->id;
b.fs.samplers[0] = tex_sampler;
return b;
}
void model_draw_go(model *model, gameobject *go, gameobject *cam)
{
HMM_Mat4 view = t3d_go2world(cam);
HMM_Mat4 proj = HMM_Perspective_RH_NO(20, 1, 0.01, 10000);
HMM_Mat4 vp = HMM_MulM4(proj, view);
HMM_Mat4 gom = transform3d2mat(go2t3(go));
animation_run(&model->anim, apptime());
skin *sk = &model->skin;
@ -470,34 +486,21 @@ void model_draw_go(model *model, gameobject *go, gameobject *cam)
local = HMM_MulM4(md->parent->t, local);
md->t = local;
sk->binds[i] = HMM_MulM4(md->t, sk->invbind[i]);
//printf("TRANSLATION OF %d IS " HMMFMT_VEC3 "\n", i, HMMPRINT_VEC3(md->pos));
}
HMM_Mat4 view = t3d_go2world(cam);
HMM_Mat4 proj = HMM_Perspective_RH_NO(20, 1, 0.01, 10000);
HMM_Mat4 vp = HMM_MulM4(proj, view);
HMM_Mat4 gom = transform3d2mat(go2t3(go));
sg_apply_pipeline(model_pipe);
sg_apply_uniforms(SG_SHADERSTAGE_VS, SLOT_vs_p, SG_RANGE_REF(vp.e));
sg_apply_uniforms(SG_SHADERSTAGE_VS, SLOT_skinv, &(sg_range){
/*sg_apply_uniforms(SG_SHADERSTAGE_VS, SLOT_skinv, &(sg_range){
.ptr = sk->binds,
.size = sizeof(*sk->binds)*50
});
float ambient[4] = {1.0,1.0,1.0,1.0};
sg_apply_uniforms(SG_SHADERSTAGE_FS, SLOT_lightf, SG_RANGE_REF(ambient));
*/
for (int i = 0; i < arrlen(model->meshes); i++) {
HMM_Mat4 mod = *model->meshes[i].m;
mod = HMM_MulM4(mod, gom);
mesh msh = model->meshes[i];
for (int j = 0; j < arrlen(msh.primitives); j++) {
sg_apply_bindings(&(msh.primitives[j].bind));
sg_apply_uniforms(SG_SHADERSTAGE_VS, SLOT_vmodel, &(sg_range){
.ptr = mod.em,
.size = sizeof(mod)
});
sg_draw(0, model->meshes[i].primitives[j].idx_count, 1);
sg_bindings b = primitive_bind(msh.primitives+j);
sg_apply_bindings(&b);
sg_draw(0, msh.primitives[j].idx_count, 1);
}
}
}

View file

@ -8,7 +8,13 @@
#include "anim.h"
#include "texture.h"
extern HMM_Vec3 eye;
#define MAT_POS 0
#define MAT_UV 1
#define MAT_NORM 2
#define MAT_BONE 3
#define MAT_WEIGHT 4
#define MAT_COLOR 5
#define MAT_TAN 6
typedef struct material {
texture *diffuse;
@ -26,7 +32,14 @@ typedef struct material {
struct model;
typedef struct primitive {
sg_bindings bind;
sg_buffer pos;
sg_buffer norm;
sg_buffer uv;
sg_buffer bone;
sg_buffer weight;
sg_buffer color;
sg_buffer idx;
material *mat;
uint32_t idx_count;
} primitive;
@ -60,6 +73,7 @@ typedef struct skin {
typedef struct model {
struct mesh *meshes;
md5joint *nodes;
material *mats;
skin skin;
struct animation anim;
} model;
@ -70,8 +84,6 @@ void model_free(model *m);
void model_draw_go(model *m, gameobject *go, gameobject *cam);
void model_init();
material *material_make();
void material_free(material *mat);

View file

@ -221,6 +221,7 @@ typedef union HMM_Vec2 {
};
float Elements[2];
float e[2];
cpVect cp;
@ -279,6 +280,7 @@ typedef union HMM_Vec3 {
};
float Elements[3];
float e[3];
} HMM_Vec3;
@ -403,6 +405,7 @@ typedef union HMM_Mat3 {
typedef union HMM_Mat4 {
float Elements[4][4];
HMM_Vec4 Columns[4];
HMM_Vec4 col[4];
float e[4][4];
float em[16];
} HMM_Mat4;
@ -421,6 +424,8 @@ static const HMM_Vec3 vBKWD = {0,0,-1};
static const HMM_Vec3 vLEFT = {-1,0,0};
static const HMM_Vec3 vRIGHT = {1,0,0};
static const HMM_Mat4 MAT1 = {1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1};
/*
* Angle unit conversion functions
*/

View file

@ -11,7 +11,6 @@
#include <window.h>
#include "resources.h"
#include "debugdraw.h"
#include "text.sglsl.h"
#include "render.h"
#include "stb_image_write.h"
@ -22,9 +21,7 @@
struct sFont *use_font;
static sg_shader fontshader;
static sg_bindings bind_text;
static sg_pipeline pipe_text;
struct text_vert {
struct draw_p pos;
struct draw_p wh;
@ -36,26 +33,6 @@ struct text_vert {
static struct text_vert *text_buffer;
void font_init() {
fontshader = sg_make_shader(text_shader_desc(sg_query_backend()));
pipe_text = sg_make_pipeline(&(sg_pipeline_desc){
.shader = fontshader,
.layout = {
.attrs = {
[ATTR_vs_vert].format = SG_VERTEXFORMAT_FLOAT2,
[ATTR_vs_vert].buffer_index = 1,
[ATTR_vs_pos].format = SG_VERTEXFORMAT_FLOAT2,
[ATTR_vs_wh].format = SG_VERTEXFORMAT_FLOAT2,
[ATTR_vs_uv].format = SG_VERTEXFORMAT_USHORT2N,
[ATTR_vs_st].format = SG_VERTEXFORMAT_USHORT2N,
[ATTR_vs_vColor].format = SG_VERTEXFORMAT_UBYTE4N,
},
.buffers[0].step_func = SG_VERTEXSTEP_PER_INSTANCE
},
.primitive_type = SG_PRIMITIVETYPE_TRIANGLE_STRIP,
.colors[0].blend = blend_trans,
.label = "text",
});
bind_text.vertex_buffers[1] = sprite_quad;
bind_text.vertex_buffers[0] = sg_make_buffer(&(sg_buffer_desc){
@ -201,7 +178,7 @@ void draw_char_box(struct Character c, HMM_Vec2 cursor, float scale, struct rgba
draw_box(b, wh, color);
}
void text_flush(HMM_Mat4 *proj) {
void text_flush() {
if (arrlen(text_buffer) == 0) return;
sg_range verts;
@ -217,9 +194,7 @@ void text_flush(HMM_Mat4 *proj) {
sg_append_buffer(bind_text.vertex_buffers[0], &verts);
sg_apply_pipeline(pipe_text);
sg_apply_bindings(&bind_text);
sg_apply_uniforms(SG_SHADERSTAGE_VS, 0, SG_RANGE_REF(*proj));
sg_draw(0, 4, arrlen(text_buffer));
arrsetlen(text_buffer, 0);
}
@ -377,7 +352,7 @@ int renderText(const char *text, HMM_Vec2 pos, float scale, struct rgba color, f
if (*wordstart == '\e')
wordstart = esc_color(wordstart, &usecolor, color);
sdrawCharacter(use_font->Characters[*wordstart], HMM_AddV2(cursor, HMM_MulV2F((HMM_Vec2){1,-1},scale)), scale, (rgba){0,0,0,255});
//sdrawCharacter(use_font->Characters[*wordstart], HMM_AddV2(cursor, HMM_MulV2F((HMM_Vec2){1,-1},scale)), scale, (rgba){0,0,0,255});
sdrawCharacter(use_font->Characters[*wordstart], cursor, scale, usecolor);
cursor.X += use_font->Characters[*wordstart].Advance * tracking * scale;

View file

@ -40,7 +40,6 @@ void text_settype(struct sFont *font);
struct boundingbox text_bb(const char *text, float scale, float lw, float tracking);
int renderText(const char *text, HMM_Vec2 pos, float scale, struct rgba color, float lw, int caret, float tracking);
// void text_frame();
void text_flush(HMM_Mat4 *proj);
void text_flush();
#endif

View file

@ -204,9 +204,3 @@ HMM_Vec3 go_pos3d(gameobject *go)
HMM_Vec2 pos2d = go_pos(go);
return (HMM_Vec3){pos2d.x, pos2d.y, go->drawlayer};
}
void gameobject_draw_debug(gameobject *go) {
if (!go || !go->body) return;
cpBodyEachShape(go->body, body_draw_shapes_dbg, NULL);
}

View file

@ -61,7 +61,10 @@ const char *js2str(JSValue v) {
return JS_ToCString(js, v);
}
void jsfreestr(const char *s) { JS_FreeCString(js, s); }
QJSCLASS(gameobject)
QJSCLASS(transform3d)
QJSCLASS(transform2d)
QJSCLASS(emitter)
QJSCLASS(dsp_node)
QJSCLASS(texture)
@ -140,6 +143,7 @@ JSValue ptr2js(void *ptr) {
}
int js_arrlen(JSValue v) {
if (JS_IsUndefined(v)) return 0;
int len;
JS_ToInt32(js, &len, js_getpropstr( v, "length"));
return len;
@ -331,6 +335,22 @@ JSValue vec32js(HMM_Vec3 v)
return array;
}
HMM_Vec4 js2vec4(JSValue v)
{
HMM_Vec4 v4;
for (int i = 0; i < 4; i++)
v4.e[i] = js2number(js_getpropidx(v,i));
return v4;
}
JSValue vec42js(HMM_Vec4 v)
{
JSValue array = JS_NewArray(js);
for (int i = 0; i < 4; i++)
js_setprop_num(array,i,number2js(v.e[i]));
return array;
}
cpBitmask js2bitmask(JSValue v) {
cpBitmask a;
JS_ToUint32(js, &a, v);
@ -605,7 +625,10 @@ JSC_CCALL(render_line3d,
);
JSC_CCALL(render_emitters, emitters_draw(&useproj))
JSC_CCALL(render_flush, debug_flush(&useproj); text_flush(&useproj); )
JSC_CCALL(render_flush, debug_flush(&useproj); )
JSC_CCALL(render_flushtext, text_flush())
JSC_CCALL(render_end_pass,
sg_end_pass();
sg_commit();
@ -623,8 +646,199 @@ JSC_CCALL(render_clear_color,
pass_action.colors[0].clear_value = c;
)
JSC_CCALL(render_set_sprite_tex, sprite_tex(js2texture(argv[0])))
JSC_CCALL(render_sprite_flush, sprite_flush())
sg_shader js2shader(JSValue v)
{
sg_shader_desc desc = {0};
JSValue prog = v;
JSValue vs = js_getpropstr(prog, "vs");
JSValue fs = js_getpropstr(prog, "fs");
char *vsf = js2str(js_getpropstr(vs, "code"));
char *fsf = js2str(js_getpropstr(fs, "code"));
desc.vs.source = vsf;
desc.fs.source = fsf;
char *vsmain = js2str(js_getpropstr(vs, "entry_point"));
char *fsmain = js2str(js_getpropstr(fs, "entry_point"));
desc.vs.entry = vsmain;
desc.fs.entry = fsmain;
JSValue vsu = js_getpropstr(vs, "uniform_blocks");
int unin = js_arrlen(vsu);
for (int i = 0; i < unin; i++) {
JSValue u = js_getpropidx(vsu, i);
int slot = js2number(js_getpropstr(u, "slot"));
desc.vs.uniform_blocks[slot].size = js2number(js_getpropstr(u, "size"));
desc.vs.uniform_blocks[slot].layout = SG_UNIFORMLAYOUT_STD140;
}
JSValue fsu = js_getpropstr(fs, "uniform_blocks");
unin = js_arrlen(fsu);
for (int i = 0; i < unin; i++) {
JSValue u = js_getpropidx(fsu, i);
int slot = js2number(js_getpropstr(u, "slot"));
desc.fs.uniform_blocks[slot].size = js2number(js_getpropstr(u, "size"));
desc.fs.uniform_blocks[slot].layout = SG_UNIFORMLAYOUT_STD140;
}
JSValue imgs = js_getpropstr(fs, "images");
unin = js_arrlen(imgs);
for (int i = 0; i < unin; i++) {
JSValue u = js_getpropidx(imgs, i);
int slot = js2number(js_getpropstr(u, "slot"));
desc.fs.images[i].used = true;
desc.fs.images[i].multisampled = js2boolean(js_getpropstr(u, "multisampled"));
desc.fs.images[i].image_type = SG_IMAGETYPE_2D;
desc.fs.images[i].sample_type = SG_IMAGESAMPLETYPE_FLOAT;
}
JSValue samps = js_getpropstr(fs, "samplers");
unin = js_arrlen(samps);
for (int i = 0; i < unin; i++) {
desc.fs.samplers[0].used = true;
desc.fs.samplers[0].sampler_type = SG_SAMPLERTYPE_FILTERING;
}
JSValue pairs = js_getpropstr(fs, "image_sampler_pairs");
unin = js_arrlen(pairs);
for (int i = 0; i < unin; i++) {
desc.fs.image_sampler_pairs[0].used = true;
desc.fs.image_sampler_pairs[0].image_slot = 0;
desc.fs.image_sampler_pairs[0].sampler_slot = 0;
}
sg_shader sh = sg_make_shader(&desc);
jsfreestr(vsf);
jsfreestr(fsf);
jsfreestr(vsmain);
jsfreestr(fsmain);
return sh;
}
JSC_CCALL(render_pipeline3d,
sg_shader sgshader = js2shader(argv[0]);
sg_pipeline_desc p = {0};
p.shader = sgshader;
sg_vertex_layout_state st = {0};
st.attrs[MAT_POS].format = SG_VERTEXFORMAT_FLOAT3;
st.attrs[MAT_UV].format = SG_VERTEXFORMAT_USHORT2N;
st.attrs[MAT_UV].buffer_index = MAT_UV;
st.attrs[MAT_NORM].format = SG_VERTEXFORMAT_UINT10_N2;
st.attrs[MAT_NORM].buffer_index = MAT_NORM;
st.attrs[MAT_WEIGHT].format = SG_VERTEXFORMAT_UBYTE4N;
st.attrs[MAT_WEIGHT].buffer_index = MAT_WEIGHT;
st.attrs[MAT_BONE].format = SG_VERTEXFORMAT_UBYTE4;
st.attrs[MAT_BONE].buffer_index = MAT_BONE;
st.attrs[MAT_COLOR].format = SG_VERTEXFORMAT_UBYTE4N;
st.attrs[MAT_COLOR].buffer_index = MAT_COLOR;
p.layout = st;
p.index_type = SG_INDEXTYPE_UINT16;
p.depth.write_enabled = true;
p.depth.compare = SG_COMPAREFUNC_LESS_EQUAL;
p.cull_mode = SG_CULLMODE_FRONT;
sg_pipeline pipe = sg_make_pipeline(&p);
return number2js(pipe.id);
)
JSC_CCALL(render_pipelinetext,
sg_shader fontshader = js2shader(argv[0]);
sg_pipeline_desc p = {0};
p.shader = fontshader;
sg_vertex_layout_state st = {0};
st.attrs[0].format = SG_VERTEXFORMAT_FLOAT2;
st.attrs[0].buffer_index = 1;
st.attrs[1].format = SG_VERTEXFORMAT_FLOAT2;
st.attrs[2].format = SG_VERTEXFORMAT_FLOAT2;
st.attrs[3].format = SG_VERTEXFORMAT_USHORT2N;
st.attrs[4].format = SG_VERTEXFORMAT_USHORT2N;
st.attrs[5].format = SG_VERTEXFORMAT_UBYTE4N;
st.buffers[0].step_func = SG_VERTEXSTEP_PER_INSTANCE;
p.layout = st;
p.primitive_type = SG_PRIMITIVETYPE_TRIANGLE_STRIP;
p.colors[0].blend = blend_trans;
sg_pipeline pipe = sg_make_pipeline(&p);
return number2js(pipe.id);
)
JSC_CCALL(render_pipeline,
sg_shader sgshader = js2shader(argv[0]);
sg_pipeline_desc pdesc = {0};
pdesc.shader = sgshader;
pdesc.cull_mode = SG_CULLMODE_FRONT;
pdesc.layout.attrs[0].format = SG_VERTEXFORMAT_FLOAT2;
pdesc.primitive_type = SG_PRIMITIVETYPE_TRIANGLE_STRIP;
if (js2boolean(js_getpropstr(argv[0], "blend")))
pdesc.colors[0].blend = blend_trans;
sg_pipeline pipe = sg_make_pipeline(&pdesc);
return number2js(pipe.id);
)
JSC_CCALL(render_setuniv,
float f = js2number(argv[2]);
sg_apply_uniforms(js2number(argv[0]), js2number(argv[1]), SG_RANGE_REF(f));
)
JSC_CCALL(render_setuniv2,
HMM_Vec2 v = js2vec2(argv[2]);
sg_apply_uniforms(js2number(argv[0]), js2number(argv[1]), SG_RANGE_REF(v.e));
)
JSC_CCALL(render_setuniv3,
HMM_Vec3 v = js2vec3(argv[2]);
sg_apply_uniforms(js2number(argv[0]), js2number(argv[1]), SG_RANGE_REF(v.e));
)
JSC_CCALL(render_setuniv4,
HMM_Vec4 v = js2vec4(argv[2]);
sg_apply_uniforms(js2number(argv[0]), js2number(argv[1]), SG_RANGE_REF(v.e));
)
JSC_CCALL(render_setuniproj,
sg_apply_uniforms(js2number(argv[0]), js2number(argv[1]), SG_RANGE_REF(useproj));
)
JSC_CCALL(render_setunim4,
HMM_Mat4 m = MAT1;
if (JS_IsArray(js, argv[2])) {
JSValue arr = argv[2];
int n = js_arrlen(arr);
if (n == 1)
m = transform2d2mat4(js2transform2d(js_getpropidx(arr,0)));
else {
for (int i = 0; i < n; i++) {
HMM_Mat4 p = transform2d2mat4(js2transform2d(js_getpropidx(arr, i)));
m = HMM_MulM4(p,m);
}
}
} else
m = transform2d2mat4(js2transform2d(argv[2]));
sg_apply_uniforms(js2number(argv[0]), js2number(argv[1]), SG_RANGE_REF(m.e));
);
JSC_CCALL(render_spdraw,
sg_bindings bind = {0};
bind.vertex_buffers[0] = sprite_quad;
for (int i = 0; i < js_arrlen(argv[0]); i++) {
bind.fs.images[i] = js2texture(js_getpropidx(argv[0], i))->id;
bind.fs.samplers[i] = std_sampler;
}
sg_apply_bindings(&bind);
sg_draw(0,4,1);
)
JSC_CCALL(render_setpipeline,
sg_pipeline p = {0};
p.id = js2number(argv[0]);
sg_apply_pipeline(p);
)
static const JSCFunctionListEntry js_render_funcs[] = {
MIST_FUNC_DEF(render, grid, 3),
@ -634,14 +848,24 @@ static const JSCFunctionListEntry js_render_funcs[] = {
MIST_FUNC_DEF(render, line, 3),
MIST_FUNC_DEF(render, line3d, 2),
MIST_FUNC_DEF(render, emitters, 0),
MIST_FUNC_DEF(render, flushtext, 0),
MIST_FUNC_DEF(render, flush, 0),
MIST_FUNC_DEF(render, end_pass, 0),
MIST_FUNC_DEF(render, text_size, 3),
MIST_FUNC_DEF(render, set_camera, 0),
MIST_FUNC_DEF(render, hud_res, 1),
MIST_FUNC_DEF(render, clear_color, 1),
MIST_FUNC_DEF(render, set_sprite_tex, 1),
MIST_FUNC_DEF(render, sprite_flush, 0),
MIST_FUNC_DEF(render, pipeline, 1),
MIST_FUNC_DEF(render, pipeline3d, 1),
MIST_FUNC_DEF(render, pipelinetext, 1),
MIST_FUNC_DEF(render, setuniv3, 2),
MIST_FUNC_DEF(render, setuniv, 2),
MIST_FUNC_DEF(render, spdraw, 2),
MIST_FUNC_DEF(render, setuniproj, 2),
MIST_FUNC_DEF(render, setunim4, 3),
MIST_FUNC_DEF(render, setuniv2, 2),
MIST_FUNC_DEF(render, setuniv4, 2),
MIST_FUNC_DEF(render, setpipeline, 1)
};
JSC_CCALL(gui_flush, text_flush(&useproj));
@ -659,21 +883,12 @@ JSC_CCALL(gui_text,
return ret;
)
JSC_CCALL(gui_img,
transform2d t;
t.pos = js2vec2(argv[1]);
t.scale = js2vec2(argv[2]);
t.angle = js2number(argv[3]);
gui_draw_img(js2texture(argv[0]), t, js2boolean(argv[4]), js2vec2(argv[5]), 1.0, js2color(argv[6]));
)
JSC_CCALL(gui_font_set, font_set(js2font(argv[0])))
static const JSCFunctionListEntry js_gui_funcs[] = {
MIST_FUNC_DEF(gui, flush, 0),
MIST_FUNC_DEF(gui, scissor, 4),
MIST_FUNC_DEF(gui, text, 6),
MIST_FUNC_DEF(gui, img, 7),
MIST_FUNC_DEF(gui, font_set,1)
};
@ -745,7 +960,7 @@ static const JSCFunctionListEntry js_input_funcs[] = {
JSC_CCALL(prosperon_emitters_step, emitters_step(js2number(argv[0])))
JSC_CCALL(prosperon_phys2d_step, phys2d_update(js2number(argv[0])))
JSC_CCALL(prosperon_window_render, openglRender(&mainwin, js2gameobject(argv[0]), js2number(argv[1])))
JSC_CCALL(prosperon_window_render, openglRender(&mainwin, js2transform2d(argv[0]), js2number(argv[1])))
JSC_CCALL(prosperon_guid,
uint8_t bytes[16];
for (int i = 0; i < 16; i++) bytes[i] = rand()%256;
@ -893,6 +1108,7 @@ JSC_SCALL(io_save_qoa, save_qoa(str))
JSC_SCALL(io_pack_start, pack_start(str))
JSC_SCALL(io_pack_add, pack_add(str))
JSC_CCALL(io_pack_end, pack_end())
JSC_SCALL(io_mod, ret = number2js(file_mod_secs(str));)
static const JSCFunctionListEntry js_io_funcs[] = {
MIST_FUNC_DEF(io, exists,1),
@ -909,11 +1125,10 @@ static const JSCFunctionListEntry js_io_funcs[] = {
MIST_FUNC_DEF(io, save_qoa,1),
MIST_FUNC_DEF(io, pack_start, 1),
MIST_FUNC_DEF(io, pack_add, 1),
MIST_FUNC_DEF(io, pack_end, 0)
MIST_FUNC_DEF(io, pack_end, 0),
MIST_FUNC_DEF(io, mod,1)
};
JSC_CCALL(debug_draw_gameobject, gameobject_draw_debug(js2gameobject(argv[0]));)
JSC_GETSET_GLOBAL(disabled_color, color)
JSC_GETSET_GLOBAL(sleep_color, color)
JSC_GETSET_GLOBAL(dynamic_color, color)
@ -921,7 +1136,6 @@ JSC_GETSET_GLOBAL(kinematic_color, color)
JSC_GETSET_GLOBAL(static_color, color)
static const JSCFunctionListEntry js_debug_funcs[] = {
MIST_FUNC_DEF(debug, draw_gameobject, 1),
CGETSET_ADD(global, disabled_color),
CGETSET_ADD(global, sleep_color),
CGETSET_ADD(global, dynamic_color),
@ -1033,6 +1247,8 @@ static const JSCFunctionListEntry js_physics_funcs[] = {
MIST_FUNC_DEF(physics, make_gravity, 0),
};
JSC_CCALL(model_draw_go,
model_draw_go(js2model(this), js2gameobject(argv[0]), js2gameobject(argv[1]))
);
@ -1065,6 +1281,16 @@ static const JSCFunctionListEntry js_emitter_funcs[] = {
CGETSET_ADD(emitter, texture),
};
JSC_GETSET(transform2d, pos, vec2)
JSC_GETSET(transform2d, scale, vec2)
JSC_GETSET(transform2d, angle, number)
static const JSCFunctionListEntry js_transform2d_funcs[] = {
CGETSET_ADD(transform2d, pos),
CGETSET_ADD(transform2d, scale),
CGETSET_ADD(transform2d, angle)
};
JSC_GETSET(dsp_node, pass, boolean)
JSC_GETSET(dsp_node, off, boolean)
JSC_GETSET(dsp_node, gain, number)
@ -1317,22 +1543,12 @@ static const JSCFunctionListEntry js_pshape_funcs[] = {
JSC_GETSET(sprite, color, color)
JSC_GETSET(sprite, emissive, color)
JSC_GETSET(sprite, pos, vec2)
JSC_GETSET(sprite, scale, vec2)
JSC_GETSET(sprite, angle, number)
JSC_GETSET(sprite, spriteoffset, vec2)
JSC_GETSET(sprite, spritesize, vec2)
JSC_CCALL(sprite_draw, sprite_draw(js2sprite(this), js2gameobject(argv[0])))
static const JSCFunctionListEntry js_sprite_funcs[] = {
CGETSET_ADD(sprite,pos),
CGETSET_ADD(sprite,scale),
CGETSET_ADD(sprite,angle),
CGETSET_ADD(sprite,color),
CGETSET_ADD(sprite,emissive),
CGETSET_ADD(sprite, spriteoffset),
CGETSET_ADD(sprite, spritesize),
MIST_FUNC_DEF(sprite, draw, 1)
};
JSC_GET(texture, width, number)
@ -1536,11 +1752,22 @@ JSC_SCALL(os_make_texture,
JSC_CCALL(os_make_font, return font2js(MakeFont(js2str(argv[0]), js2number(argv[1]))))
JSC_SCALL(os_system, system(str); )
JSC_CCALL(os_make_transform2d,
if (JS_IsUndefined(argv[0]))
return transform2d2js(make_transform2d());
int n = js2number(argv[0]);
transform2d *t = calloc(sizeof(transform2d), n);
JSValue arr = JS_NewArray(js);
for (int i = 0; i < n; i++)
js_setprop_num(arr, i, transform2d2js(t+i));
return arr;
)
JSC_SCALL(os_make_model, return model2js(model_make(str)))
JSC_SCALL(os_system, return number2js(system(str)); )
JSC_CCALL(os_sprite_pipe, sprite_pipe())
JSC_SCALL(os_make_model, ret = model2js(model_make(str)))
static const JSCFunctionListEntry js_os_funcs[] = {
MIST_FUNC_DEF(os,sprite,1),
@ -1561,7 +1788,7 @@ static const JSCFunctionListEntry js_os_funcs[] = {
MIST_FUNC_DEF(os, make_texture, 1),
MIST_FUNC_DEF(os, make_font, 2),
MIST_FUNC_DEF(os, make_model, 1),
MIST_FUNC_DEF(os, sprite_pipe, 0)
MIST_FUNC_DEF(os, make_transform2d, 1),
};
#include "steam.h"
@ -1570,10 +1797,13 @@ void ffi_load() {
globalThis = JS_GetGlobalObject(js);
QJSCLASSPREP(ptr);
QJSCLASSPREP(transform3d);
QJSGLOBALCLASS(os);
QJSCLASSPREP_FUNCS(gameobject);
QJSCLASSPREP_FUNCS(transform2d);
QJSCLASSPREP_FUNCS(dsp_node);
QJSCLASSPREP_FUNCS(emitter);
QJSCLASSPREP_FUNCS(warp_gravity);

View file

@ -135,4 +135,9 @@ JSValue str2js(const char *c, ...);
void nota_int(char *blob);
JSValue js_getpropidx(JSValue v, uint32_t i);
JSValue js_getpropstr(JSValue v, const char *str);
const char *js2str(JSValue v);
void jsfreestr(const char *str);
#endif

View file

@ -33,6 +33,7 @@ float camzoom = 1;
sg_buffer sprite_quad;
sg_sampler std_sampler;
sg_sampler tex_sampler;
static struct {
sg_swapchain swap;
@ -242,6 +243,13 @@ void render_init() {
});
std_sampler = sg_make_sampler(&(sg_sampler_desc){});
tex_sampler = sg_make_sampler(&(sg_sampler_desc){
.min_filter = SG_FILTER_LINEAR,
.mag_filter = SG_FILTER_LINEAR,
.mipmap_filter = SG_FILTER_LINEAR,
.wrap_u = SG_WRAP_REPEAT,
.wrap_v = SG_WRAP_REPEAT
});
#ifndef NDEBUG
sg_trace_hooks hh = sg_install_trace_hooks(&hooks);
@ -249,10 +257,7 @@ void render_init() {
font_init();
debugdraw_init();
sprite_initialize();
model_init();
sg_color c = (sg_color){0,0,0,1};
pass_action = (sg_pass_action){
.colors[0] = {.load_action = SG_LOADACTION_CLEAR, .clear_value = c},
@ -289,7 +294,10 @@ void render_init() {
1, -1, 1, 0,
1, 1, 1, 1
};
sg_limits ll = sg_query_limits();
printf("attribute limits %d\n", ll.max_vertex_attrs);
sg_gif.bind.vertex_buffers[0] = sg_make_buffer(&(sg_buffer_desc){
.size = sizeof(gif_quad),
.data = gif_quad,
@ -330,7 +338,7 @@ HMM_Mat4 useproj = {0};
#define MODE_EXPAND 4
#define MODE_FULL 5
void openglRender(struct window *window, gameobject *cam, float zoom) {
void openglRender(struct window *window, transform2d *cam, float zoom) {
sg_swapchain sch = sglue_swapchain();
sg_begin_pass(&(sg_pass){
.action = pass_action,
@ -365,7 +373,7 @@ void openglRender(struct window *window, gameobject *cam, float zoom) {
}
// 2D projection
campos = go_pos(cam);
campos = cam->pos;
camzoom = zoom;
projection = HMM_Orthographic_RH_NO(

View file

@ -14,6 +14,7 @@
#include "sokol/sokol_gfx.h"
#include "HandmadeMath.h"
#include "gameobject.h"
#include "transform.h"
#define RGBA_MAX 255
@ -34,6 +35,7 @@ extern sg_pass_action pass_action;
extern sg_buffer sprite_quad;
extern sg_sampler std_sampler;
extern sg_sampler tex_sampler;
struct draw_p {
float x;
@ -60,7 +62,7 @@ void render_init();
extern HMM_Vec2 campos;
extern float camzoom;
void openglRender(struct window *window, gameobject *cam, float zoom);
void openglRender(struct window *window, transform2d *cam, float zoom);
void opengl_rendermode(enum RenderMode r);
void openglInit3d(struct window *window);

View file

@ -126,6 +126,17 @@ char *dirname(const char *path)
return dir;
}
char *makepath(char *dir, char *file)
{
int d = strlen(dir) + strlen(file) + 2;
char *path = malloc(d);
path[0] = 0;
strncat(path, dir, d);
strncat(path, "/", d);
strncat(path, file, d);
return path;
}
char *seprint(char *fmt, ...)
{
va_list args;

View file

@ -11,7 +11,8 @@ extern int LOADED_GAME;
void resources_init();
char *get_filename_from_path(char *path, int extension);
char *get_directory_from_path(char *path);
char *dirname(const char *path);
char *makepath(char *dir, char *file);
char *str_replace_ext(const char *s, const char *newext);
FILE *res_open(char *path, const char *tag);
char **ls(const char *path);
@ -22,8 +23,6 @@ void pack_start(const char *name);
void pack_add(const char *path);
void pack_end();
char *dirname(const char *path);
void *slurp_file(const char *filename, size_t *size);
char *slurp_text(const char *filename, size_t *size);
int slurp_write(const char *txt, const char *filename, size_t len);

View file

@ -7,182 +7,13 @@
#include "texture.h"
#include "HandmadeMath.h"
#include "sprite.sglsl.h"
#include "9slice.sglsl.h"
static sg_shader shader_sprite;
static sg_pipeline pip_sprite;
sg_bindings bind_sprite;
static sg_shader slice9_shader;
static sg_pipeline slice9_pipe;
static sg_bindings slice9_bind;
struct slice9_vert {
HMM_Vec2 pos;
struct uv_n uv;
unsigned short border[4];
HMM_Vec2 scale;
struct rgba color;
};
sprite *sprite_make()
{
sprite *sp = calloc(sizeof(*sp), 1);
sp->pos = v2zero;
sp->scale = v2one;
sp->angle = 0;
sp->color = color_white;
sp->emissive = color_clear;
sp->spritesize = v2one;
sp->spriteoffset = v2zero;
sp->rect = (HMM_Vec4){0,0,1,1};
return sp;
}
void sprite_free(sprite *sprite) { free(sprite); }
static texture *loadedtex;
static int sprite_count = 0;
void sprite_flush() {
return;
if (!loadedtex) return;
/*
int flushed = arrlen(spverts)/4;
sg_apply_bindings(&bind_sprite);
sg_range data = (sg_range){
.ptr = spverts,
.size = sizeof(sprite_vert)*arrlen(spverts)
};
if (sg_query_buffer_will_overflow(bind_sprite.vertex_buffers[0], data.size))
bind_sprite.vertex_buffers[0] = sg_make_buffer(&(sg_buffer_desc){
.size = data.size,
.type = SG_BUFFERTYPE_VERTEXBUFFER,
.usage = SG_USAGE_STREAM,
.label = "sprite vertex buffer"
});
sg_update_buffer(bind_sprite.vertex_buffers[0], &data);
sg_draw(sprite_count * 4, 4, flushed);
sprite_count += flushed;
*/
}
void sprite_initialize() {
shader_sprite = sg_make_shader(sprite_shader_desc(sg_query_backend()));
pip_sprite = sg_make_pipeline(&(sg_pipeline_desc){
.shader = shader_sprite,
.layout = {
.attrs = {
[0].format = SG_VERTEXFORMAT_FLOAT2
},
},
.primitive_type = SG_PRIMITIVETYPE_TRIANGLE_STRIP,
.label = "sprite pipeline",
.colors[0].blend = blend_trans,
});
bind_sprite.vertex_buffers[0] = sprite_quad;
bind_sprite.fs.samplers[0] = std_sampler;
slice9_shader = sg_make_shader(slice9_shader_desc(sg_query_backend()));
slice9_pipe = sg_make_pipeline(&(sg_pipeline_desc){
.shader = slice9_shader,
.layout = {
.attrs = {
[0].format = SG_VERTEXFORMAT_FLOAT2,
[1].format = SG_VERTEXFORMAT_FLOAT2,
[2].format = SG_VERTEXFORMAT_USHORT4N,
[3].format = SG_VERTEXFORMAT_FLOAT2,
[4].format = SG_VERTEXFORMAT_UBYTE4N
}},
.primitive_type = SG_PRIMITIVETYPE_TRIANGLE_STRIP,
});
slice9_bind.vertex_buffers[0] = sprite_quad;
}
void sprite_pipe()
{
sg_apply_pipeline(pip_sprite);
sg_apply_uniforms(SG_SHADERSTAGE_VS, SLOT_vp, SG_RANGE_REF(useproj));
sg_apply_bindings(&bind_sprite);
}
void tex_draw(HMM_Mat3 m, struct rect r, struct rgba color, int wrap, HMM_Vec2 wrapoffset, HMM_Vec2 wrapscale, struct rgba emissive) {
/*
struct sprite_vert verts[4];
float w = loadedtex->width*r.w;
float h = loadedtex->height*r.h;
HMM_Vec2 sposes[4] = {
{0,0},
{w,0},
{0,h},
{w,h}
};
for (int i = 0; i < 4; i++)
verts[i].pos = mat_t_pos(m, sposes[i]);
if (wrap) {
r.w *= wrapscale.x;
r.h *= wrapscale.y;
}
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;
for (int i = 0; i < 4; i++)
arrput(spverts, verts[i]);
*/
}
transform2d sprite2t(sprite *s)
{
return (transform2d){
.pos = s->pos,
.scale = HMM_MulV2(s->scale, (HMM_Vec2){loadedtex->width, loadedtex->height}),
.angle = HMM_TurnToRad*s->angle
};
}
void sprite_tex(texture *t)
{
loadedtex = t;
bind_sprite.fs.images[0] = t->id;
}
void sprite_draw(struct sprite *sprite, gameobject *go) {
HMM_Mat4 m = transform2d2mat4(go2t(go));
HMM_Mat4 sm = transform2d2mat4(sprite2t(sprite));
struct spriteuni spv;
rgba2floats(&spv.color.e, sprite->color);
rgba2floats(spv.emissive.e, sprite->emissive);
spv.size = sprite->spritesize;
spv.offset = sprite->spriteoffset;
spv.model = HMM_MulM4(m,sm);
sg_apply_uniforms(SG_SHADERSTAGE_VS, SLOT_sprite, SG_RANGE_REF(spv));
sg_draw(0,4,1);
}
void gui_draw_img(texture *tex, transform2d t, int wrap, HMM_Vec2 wrapoffset, float wrapscale, struct rgba color) {
sg_apply_pipeline(pip_sprite);
sg_apply_uniforms(SG_SHADERSTAGE_VS, SLOT_vp, SG_RANGE_REF(useproj));
sprite_tex(tex);
tex_draw(transform2d2mat(t), ST_UNIT, color, wrap, wrapoffset, (HMM_Vec2){wrapscale,wrapscale}, (struct rgba){0,0,0,0});
}
void slice9_draw(texture *tex, transform2d *t, HMM_Vec4 border, struct rgba color)
{
}
void sprite_free(sprite *sprite) { free(sprite); }

View file

@ -7,37 +7,14 @@
#include "transform.h"
#include "gameobject.h"
struct sprite {
HMM_Vec2 pos;
HMM_Vec2 scale;
float angle;
typedef struct sprite {
struct rgba color;
struct rgba emissive;
HMM_Vec2 spritesize;
HMM_Vec4 rect;
HMM_Vec2 spriteoffset;
};
struct spriteuni {
HMM_Vec4 color;
HMM_Vec4 emissive;
HMM_Vec2 size;
HMM_Vec2 offset;
HMM_Mat4 model;
};
typedef struct sprite sprite;
extern sg_bindings bind_sprite;
} sprite;
sprite *sprite_make();
void sprite_free(sprite *sprite);
void sprite_tex(texture *t);
void sprite_initialize();
void sprite_draw(struct sprite *sprite, gameobject *go);
void sprite_pipe();
void sprite_draw_all();
void sprite_flush();
void gui_draw_img(texture *tex, transform2d t, int wrap, HMM_Vec2 wrapoffset, float wrapscale, struct rgba color);
#endif

View file

@ -122,11 +122,9 @@ struct texture *texture_from_file(const char *path) {
unsigned int nw = next_pow2(tex->width);
unsigned int nh = next_pow2(tex->height);
int filter = SG_FILTER_NEAREST;
sg_image_data sg_img_data;
sg_img_data.subimage[0][0] = (sg_range){.ptr = data, .size=tex->width*tex->height*4};
/*
int mips = mip_levels(tex->width, tex->height)+1;
YughInfo("Has %d mip levels, from wxh %dx%d, pow2 is %ux%u.", mips, tex->width, tex->height,nw,nh);
@ -151,18 +149,18 @@ struct texture *texture_from_file(const char *path) {
mipw = w;
miph = h;
}
*/
tex->id = sg_make_image(&(sg_image_desc){
.type = SG_IMAGETYPE_2D,
.width = tex->width,
.height = tex->height,
.usage = SG_USAGE_IMMUTABLE,
//.num_mipmaps = mips,
.num_mipmaps = mips,
.data = sg_img_data
});
/*for (int i = 1; i < mips; i++)
free(mipdata[i]);*/
for (int i = 1; i < mips; i++)
free(mipdata[i]);
return tex;
}
@ -191,8 +189,6 @@ struct texture *texture_fromdata(void *raw, long size)
tex->data = data;
int filter = SG_FILTER_NEAREST;
sg_image_data sg_img_data;
int mips = mip_levels(tex->width, tex->height)+1;

View file

@ -18,7 +18,8 @@ extern struct rect ST_UNIT;
/* Represents an actual texture on the GPU */
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 height;
unsigned char *data;

View file

@ -1,5 +1,6 @@
#include "transform.h"
#include <string.h>
#include <stdio.h>
const transform2d t2d_unit = {
.pos = {0,0},
@ -7,6 +8,23 @@ const transform2d t2d_unit = {
.angle = 0
};
transform3d *make_transform3d()
{
transform3d *t = calloc(sizeof(transform3d),1);
return t;
}
void transform3d_free(transform3d *t) { free(t); }
transform2d *make_transform2d()
{
transform2d *t = calloc(sizeof(transform2d),1);
t->scale = (HMM_Vec2){1,1};
return t;
}
void transform2d_free(transform2d *t) { free(t); }
HMM_Vec3 trans_forward(const transform3d *const trans) { return HMM_QVRot(vFWD, trans->rotation); }
HMM_Vec3 trans_back(const transform3d *trans) { return HMM_QVRot(vBKWD, trans->rotation); }
HMM_Vec3 trans_up(const transform3d *trans) { return HMM_QVRot(vUP, trans->rotation); }
@ -40,13 +58,19 @@ HMM_Mat3 transform2d2mat(transform2d trn) {
return HMM_MulM3(HMM_Translate2D(trn.pos), HMM_MulM3(HMM_RotateM3(trn.angle), HMM_ScaleM3(trn.scale)));
}
HMM_Mat4 transform2d2mat4(transform2d trn)
HMM_Mat4 transform2d2mat4(transform2d *t)
{
transform3d t3d;
t3d.pos.xy = trn.pos;
t3d.scale.xy = trn.scale;
t3d.rotation = HMM_QFromAxisAngle_RH((HMM_Vec3){0,0,-1}, trn.angle);
return transform3d2mat(t3d);
HMM_Mat4 T = {0};
float c = cosf(t->angle);
float s = sinf(t->angle);
T.col[0].x = c*t->scale.x;
T.col[0].y = s*t->scale.y;
T.col[1].x = -s*t->scale.x;
T.col[1].y = c*t->scale.y;
T.col[3].xy = t->pos;
T.col[2].z = 1;
T.col[3].w = 1;
return T;
}
transform2d mat2transform2d(HMM_Mat3 m)

View file

@ -9,12 +9,18 @@ typedef struct transform3d {
HMM_Quat rotation;
} transform3d;
transform3d *make_transform3d();
void transform3d_free(transform3d *t);
typedef struct {
HMM_Vec2 pos;
HMM_Vec2 scale;
float angle;
} transform2d;
transform2d *make_transform2d();
void transform2d_free(transform2d *t);
extern const transform2d t2d_unit;
#define VEC2_FMT "[%g,%g]"
@ -27,7 +33,7 @@ HMM_Vec3 trans_down(const transform3d *trans);
HMM_Vec3 trans_right(const transform3d *trans);
HMM_Vec3 trans_left(const transform3d *trans);
HMM_Mat4 transform2d2mat4(transform2d trn);
HMM_Mat4 transform2d2mat4(transform2d *t);
/* Transform a position via the matrix */
HMM_Vec2 mat_t_pos(HMM_Mat3 m, HMM_Vec2 pos);

View file

@ -1,65 +0,0 @@
@vs vs9
in vec2 vert;
in vec2 vuv;
in vec4 vborder;
in vec2 vscale;
in vec4 vcolor;
out vec2 uv;
out vec4 border;
out vec2 scale;
out vec4 fcolor;
uniform vs9_params { mat4 projection; };
void main()
{
gl_Position = projection * vec4(vert, 0.0, 1.0);
uv = vuv;
border = vborder;
scale = vscale;
fcolor = vcolor;
}
@end
@fs fs9
in vec2 uv; /* image uv */
in vec4 border; /* uv length of border, normalized to image dimensions; left, bottom, right, top */
in vec2 scale; /* polygon dimensions ~ texture dimensions */
in vec4 fcolor;
out vec4 color;
uniform texture2D image;
uniform sampler smp;
float map(float value, float min1, float max1, float min2, float max2)
{
return min2 + (value - min1) * (max2 - min2) / (max1 - min1);
}
float processAxis(float coord, float texBorder, float winBorder)
{
if (coord < winBorder)
return map(coord, 0, winBorder, 0, texBorder);
if (coord < 1 - winBorder)
return map(coord, winBorder, 1 - winBorder, texBorder, 1 - texBorder);
return map(coord, 1 - winBorder, 1, 1 - texBorder, 1);
}
vec2 uv9slice(vec2 uv, vec2 s, vec4 b)
{
vec2 t = clamp((s * uv - b.xy) / (s - b.xy - b.zw), 0.0, 1.0);
return mix(uv * s, 1.0 - s * (1.0 - uv), t);
}
void main()
{
vec2 nuv = uv9slice(uv, scale, border);
color = fcolor * texture(sampler2D(image,smp), nuv);
}
@end
@program slice9 vs9 fs9

View file

@ -1,14 +0,0 @@
#version 330
in vec2 TexCoords;
out vec4 color;
uniform sampler2DArray image;
uniform float frame;
uniform vec3 spriteColor;
void main()
{
color = vec4(spriteColor, 1.f) * texture(image, vec3(TexCoords,frame));
if (color.a < 0.1)
discard;
}

View file

@ -1,17 +0,0 @@
#version 330 core
layout (location = 0) in vec2 vertex; // <vec2 position, vec2 texCoords>
out vec2 TexCoords;
layout (std140) uniform Projection
{
mat4 projection;
};
uniform mat4 model;
void main()
{
TexCoords = vertex.xy;
gl_Position = projection * model * vec4(vertex.xy, 0.0, 1.0);
}

View file

@ -1,53 +0,0 @@
@vs vs
in vec2 vertex;
out vec2 texcoords;
out vec4 fcolor;
out vec4 femissive;
uniform app {
float time;
vec2 window;
};
uniform vp {
mat4 proj;
};
uniform sprite {
vec4 vcolor;
vec4 vemissive;
vec2 spritesize;
vec2 spriteoff;
mat4 model;
};
void main()
{
texcoords = vertex;
texcoords.y = 1 - texcoords.y;
gl_Position = proj * model * vec4(vertex, 0, 1.0);
fcolor = vcolor;
femissive = vemissive;
}
@end
@fs fs
in vec2 texcoords;
in vec4 fcolor;
in vec4 femissive;
out vec4 color;
uniform texture2D image;
uniform sampler smp;
void main()
{
color = texture(sampler2D(image,smp), texcoords);
color *= fcolor;
color.xyz = mix(color.xyz, femissive.xyz, femissive.a);
}
@end
@program sprite vs fs

View file

@ -1,12 +0,0 @@
#version 330 core
in vec2 texcoords;
out vec4 color;
uniform sampler2D image;
uniform vec3 spriteColor;
void main()
{
color = vec4(spriteColor, 1.f) * texture(image, texcoords);
color.a = 1.f;
}

View file

@ -1,42 +0,0 @@
@vs vs
in vec2 vert;
in vec2 pos;
in vec2 wh;
in vec2 uv;
in vec2 st;
in vec4 vColor;
out vec2 TexCoords;
out vec4 fColor;
uniform vs_params { mat4 projection; };
void main()
{
gl_Position = projection * vec4(pos + (vert * wh), 0.0, 1.0);
TexCoords = uv + vec2(vert.x*st.x, st.y - vert.y*st.y);
fColor = vColor;
}
@end
@fs fs
in vec2 TexCoords;
in vec4 fColor;
out vec4 color;
uniform texture2D text;
uniform sampler smp;
void main()
{
float lettera = texture(sampler2D(text,smp),TexCoords).r;
if (lettera < 0.1f) discard;
color = fColor;
}
@end
@program text vs fs

View file

@ -1,30 +1,45 @@
@vs vs
@block vptr
in vec3 a_pos;
in vec2 a_tex_coords;
in vec2 a_uv;
in vec4 a_norm;
in vec4 a_weight;
in vec4 a_joint;
out vec2 tex_coords;
out vec3 normal;
uniform vs_p { uniform mat4 vp; };
uniform vmodel { uniform mat4 model; };
@end
@vs vs
@include_block vptr
in vec4 a_bone;
in vec4 a_weight;
uniform skinv { uniform mat4 bones[50]; };
void main() {
mat4 skin = bones[int(a_joint.x)] * a_weight.x;
skin += bones[int(a_joint.y)] * a_weight.y;
skin += bones[int(a_joint.z)] * a_weight.z;
skin += bones[int(a_joint.w)] * a_weight.w;
mat4 skinm = model * skin;
gl_Position = vp * skinm * vec4(a_pos,1.0);
tex_coords = a_tex_coords;
mat4 tt = vp;
mat4 mm = model;
mat4 skin = bones[int(a_bone.x)] * a_weight.x;
skin += bones[int(a_bone.y)] * a_weight.y;
skin += bones[int(a_bone.z)] * a_weight.z;
skin += bones[int(a_bone.w)] * a_weight.w;
mat4 skinm = mm * skin;
gl_Position = tt * skinm * vec4(a_pos,1.0);
tex_coords = a_uv;
normal = (skinm * vec4(a_norm.xyz*2-1,0)).xyz;
}
@end
@vs vs_st
@include_block vptr
void main() {
gl_Position = vp * model * vec4(a_pos,1.0);
tex_coords = a_uv;
normal = (model * vec4(a_norm.xyz*2-1,0)).xyz;
}
@end
@fs fs
in vec2 tex_coords;
in vec3 normal;
@ -45,3 +60,4 @@ void main() {
@end
@program unlit vs fs
@program unlit_st vs_st fs