From 79e4772f93b1cf548ef5319964bc3e92247daef0 Mon Sep 17 00:00:00 2001 From: John Alanbrook Date: Thu, 2 May 2024 17:13:09 -0500 Subject: [PATCH] Separate entity from rigidbody --- scripts/components.js | 2 +- scripts/editor.js | 20 +- scripts/engine.js | 8 +- scripts/entity.js | 852 ++++++++++++++++++------------------- source/engine/3d/model.h | 2 - source/engine/gameobject.c | 6 - source/engine/jsffi.c | 34 +- source/engine/render.c | 6 +- source/engine/render.h | 3 +- 9 files changed, 460 insertions(+), 473 deletions(-) diff --git a/scripts/components.js b/scripts/components.js index 1b556b8..e20764e 100644 --- a/scripts/components.js +++ b/scripts/components.js @@ -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; diff --git a/scripts/editor.js b/scripts/editor.js index 9c6234d..2034b6f 100644 --- a/scripts/editor.js +++ b/scripts/editor.js @@ -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; diff --git a/scripts/engine.js b/scripts/engine.js index 39ea931..6ce1609 100644 --- a/scripts/engine.js +++ b/scripts/engine.js @@ -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"; diff --git a/scripts/entity.js b/scripts/entity.js index bf5676d..faed620 100644 --- a/scripts/entity.js +++ b/scripts/entity.js @@ -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 } \ No newline at end of file +return { entity } \ No newline at end of file diff --git a/source/engine/3d/model.h b/source/engine/3d/model.h index c903b3a..aa76a33 100644 --- a/source/engine/3d/model.h +++ b/source/engine/3d/model.h @@ -84,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); diff --git a/source/engine/gameobject.c b/source/engine/gameobject.c index 5ff1aa1..83db63c 100644 --- a/source/engine/gameobject.c +++ b/source/engine/gameobject.c @@ -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); -} diff --git a/source/engine/jsffi.c b/source/engine/jsffi.c index 1100e96..356bdce 100644 --- a/source/engine/jsffi.c +++ b/source/engine/jsffi.c @@ -779,7 +779,21 @@ JSC_CCALL(render_setuniproj, ) JSC_CCALL(render_setunim4, - HMM_Mat4 m = transform2d2mat4(js2transform2d(argv[2])); + 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)); ); @@ -920,7 +934,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; @@ -1089,8 +1103,6 @@ static const JSCFunctionListEntry js_io_funcs[] = { 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) @@ -1098,7 +1110,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), @@ -1716,7 +1727,16 @@ JSC_SCALL(os_make_texture, JSC_CCALL(os_make_font, return font2js(MakeFont(js2str(argv[0]), js2number(argv[1])))) JSC_CCALL(os_make_transform2d, - return transform2d2js(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_system, return number2js(system(str)); ) @@ -1742,7 +1762,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, make_transform2d, 0), + MIST_FUNC_DEF(os, make_transform2d, 1), }; #include "steam.h" diff --git a/source/engine/render.c b/source/engine/render.c index 6537f04..937903d 100644 --- a/source/engine/render.c +++ b/source/engine/render.c @@ -258,8 +258,6 @@ void render_init() { font_init(); debugdraw_init(); - 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}, @@ -337,7 +335,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, @@ -372,7 +370,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( diff --git a/source/engine/render.h b/source/engine/render.h index cc1da84..071c06f 100644 --- a/source/engine/render.h +++ b/source/engine/render.h @@ -14,6 +14,7 @@ #include "sokol/sokol_gfx.h" #include "HandmadeMath.h" #include "gameobject.h" +#include "transform.h" #define RGBA_MAX 255 @@ -61,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);