From 2fe4e825aa5d5f3773cf7eddca3af35dd20b80e7 Mon Sep 17 00:00:00 2001 From: John Alanbrook Date: Tue, 18 Jun 2024 16:14:23 -0500 Subject: [PATCH] add thumbnail making --- Makefile | 2 +- scripts/components.js | 1 + scripts/engine.js | 539 ++++++++++++++++++----------------- scripts/entity.js | 2 +- source/engine/HandmadeMath.c | 15 + source/engine/HandmadeMath.h | 4 + source/engine/jsffi.c | 21 ++ source/engine/texture.c | 79 +++++ source/engine/texture.h | 5 +- 9 files changed, 400 insertions(+), 268 deletions(-) diff --git a/Makefile b/Makefile index 881324b..11f4a5f 100755 --- a/Makefile +++ b/Makefile @@ -267,7 +267,7 @@ crossmac: Prosperon.icns cp Prosperon.icns Prosperon.app/Contents/Resources crosswin: - make CROSS=x86_64-w64-mingw32ucrt- OS=Windows_NT CC=gcc + make CROSS=x86_64-w64-mingw32- OS=Windows_NT CC=gcc crossweb: make CC=emcc OS=wasm diff --git a/scripts/components.js b/scripts/components.js index c2ff043..d574329 100644 --- a/scripts/components.js +++ b/scripts/components.js @@ -61,6 +61,7 @@ var sprite = { this._p = p; this.del_anim?.(); this.texture = game.texture(p); + say(p); this.diffuse = this.texture; this.rect = [0,0,1,1]; diff --git a/scripts/engine.js b/scripts/engine.js index 7e8469e..36b9220 100644 --- a/scripts/engine.js +++ b/scripts/engine.js @@ -1,107 +1,97 @@ "use math"; -Object.defineProperty(String.prototype, 'tolast', { - value: function(val) { +Object.defineProperty(String.prototype, "tolast", { + value: function (val) { var idx = this.lastIndexOf(val); if (idx === -1) return this.slice(); - return this.slice(0,idx); - } + return this.slice(0, idx); + }, }); -Object.defineProperty(String.prototype, 'dir', { - value: function() { - if (!this.includes('/')) return ""; - return this.tolast('/'); - } +Object.defineProperty(String.prototype, "dir", { + value: function () { + if (!this.includes("/")) return ""; + return this.tolast("/"); + }, }); -Object.defineProperty(String.prototype, 'folder', { - value: function() { +Object.defineProperty(String.prototype, "folder", { + value: function () { var dir = this.dir(); if (!dir) return ""; else return dir + "/"; - } + }, }); globalThis.Resources = {}; -Resources.replpath = function(str, path) -{ +Resources.replpath = function (str, path) { if (!str) return str; - if (str[0] === "/") - return str.rm(0); + if (str[0] === "/") return str.rm(0); - if (str[0] === "@") - return os.prefpath() + "/" + str.rm(0); + if (str[0] === "@") return os.prefpath() + "/" + str.rm(0); if (!path) return str; - + var stem = path.dir(); while (stem) { - var tr = stem + "/" +str; + var tr = stem + "/" + str; if (io.exists(tr)) return tr; stem = stem.updir(); } - - return str; -} -Resources.replstrs = function(path) -{ + return str; +}; + +Resources.replstrs = function (path) { if (!path) return; var script = io.slurp(path); var regexp = /"[^"\s]*?\.[^"\s]+?"/g; var stem = path.dir(); - script = script.replace(regexp,function(str) { + script = script.replace(regexp, function (str) { var newstr = Resources.replpath(str.trimchr('"'), path); return `"${newstr}"`; }); return script; -} +}; globalThis.json = {}; -json.encode = function(value, replacer, space = 1) -{ +json.encode = function (value, replacer, space = 1) { return JSON.stringify(value, replacer, space); -} +}; -json.decode = function(text, reviver) -{ +json.decode = function (text, reviver) { if (!text) return undefined; - return JSON.parse(text,reviver); -} + return JSON.parse(text, reviver); +}; -json.readout = function(obj) -{ +json.readout = function (obj) { var j = {}; for (var k in obj) - if (typeof obj[k] === 'function') - j[k] = 'function ' + obj[k].toString(); - else - j[k] = obj[k]; + if (typeof obj[k] === "function") j[k] = "function " + obj[k].toString(); + else j[k] = obj[k]; return json.encode(j); -} +}; json.doc = { doc: "json implementation.", encode: "Encode a value to json.", decode: "Decode a json string to a value.", - readout: "Encode an object fully, including function definitions." + readout: "Encode an object fully, including function definitions.", }; Resources.scripts = ["jsoc", "jsc", "jso", "js"]; Resources.images = ["png", "gif", "jpg", "jpeg"]; -Resources.sounds = ["wav", 'flac', 'mp3', "qoa"]; -Resources.is_image = function(path) { +Resources.sounds = ["wav", "flac", "mp3", "qoa"]; +Resources.is_image = function (path) { var ext = path.ext(); - return Resources.images.any(x => x === ext); -} + return Resources.images.any((x) => x === ext); +}; -function find_ext(file, ext) -{ +function find_ext(file, ext) { if (io.exists(file)) return file; for (var e of ext) { var nf = `${file}.${e}`; @@ -110,46 +100,49 @@ function find_ext(file, ext) return; } -Resources.find_image = function(file) { return find_ext(file,Resources.images); } -Resources.find_sound = function(file) { return find_ext(file,Resources.sounds); } -Resources.find_script = function(file) { return find_ext(file,Resources.scripts); } +Resources.find_image = function (file) { + return find_ext(file, Resources.images); +}; +Resources.find_sound = function (file) { + return find_ext(file, Resources.sounds); +}; +Resources.find_script = function (file) { + return find_ext(file, Resources.scripts); +}; -profile.best_t = function(t) { - var qq = 'ns'; +profile.best_t = function (t) { + var qq = "ns"; if (t > 1000) { t /= 1000; - qq = 'us'; + qq = "us"; if (t > 1000) { t /= 1000; -qq = 'ms'; + qq = "ms"; } } return `${t.toPrecision(4)} ${qq}`; -} +}; -profile.report = function(start, msg = "[undefined report]") -{ - console.info(`${msg} in ${profile.best_t(profile.now()-start)}`); -} +profile.report = function (start, msg = "[undefined report]") { + console.info(`${msg} in ${profile.best_t(profile.now() - start)}`); +}; -profile.addreport = function(cache, line, start) -{ +profile.addreport = function (cache, line, start) { cache[line] ??= []; - cache[line].push(profile.now()-start); -} + cache[line].push(profile.now() - start); +}; -profile.printreport = function(cache, name) -{ +profile.printreport = function (cache, name) { var report = name + "\n"; for (var i in cache) - report += `${i} ${profile.best_t(cache[i].reduce((a,b) => a+b)/cache[i].length)}\n`; - + report += `${i} ${profile.best_t(cache[i].reduce((a, b) => a + b) / cache[i].length)}\n`; + return report; -} +}; console.transcript = ""; -console.say = function(msg) { - msg += "\n"; +console.say = function (msg) { + msg += "\n"; console.print(msg); console.transcript += msg; }; @@ -157,17 +150,15 @@ console.log = console.say; globalThis.say = console.say; globalThis.print = console.print; -console.pprint = function(msg,lvl = 0) { - - if (typeof msg === 'object') - msg = JSON.stringify(msg, null, 2); +console.pprint = function (msg, lvl = 0) { + if (typeof msg === "object") msg = JSON.stringify(msg, null, 2); var file = "nofile"; var line = 0; - console.rec(0,msg,file,line); - - var caller = (new Error()).stack.split('\n')[2]; - if (caller) { + console.rec(0, msg, file, line); + + var caller = new Error().stack.split("\n")[2]; + if (caller) { var md = caller.match(/\((.*)\:/); var m = md ? md[1] : "SCRIPT"; if (m) file = m; @@ -175,23 +166,37 @@ console.pprint = function(msg,lvl = 0) { m = md ? md[1] : 0; if (m) line = m; } - + console.rec(lvl, msg, file, line); }; -console.spam = function(msg) { console.pprint (msg,0); }; -console.debug = function(msg) { console.pprint(msg,1); }; -console.info = function(msg) { console.pprint(msg, 2); }; -console.warn = function(msg) { console.pprint(msg, 3); }; -console.error = function(msg) { console.pprint(msg + "\n" + console.stackstr(2), 4);}; -console.panic = function(msg) { console.pprint(msg + "\n" + console.stackstr(2), 5); }; -console.stackstr = function(skip=0) { +console.spam = function (msg) { + console.pprint(msg, 0); +}; +console.debug = function (msg) { + console.pprint(msg, 1); +}; +console.info = function (msg) { + console.pprint(msg, 2); +}; +console.warn = function (msg) { + console.pprint(msg, 3); +}; +console.error = function (msg) { + console.pprint(msg + "\n" + console.stackstr(2), 4); +}; +console.panic = function (msg) { + console.pprint(msg + "\n" + console.stackstr(2), 5); +}; +console.stackstr = function (skip = 0) { var err = new Error(); - var stack = err.stack.split('\n'); - return stack.slice(skip,stack.length).join('\n'); + var stack = err.stack.split("\n"); + return stack.slice(skip, stack.length).join("\n"); }; -console.stack = function(skip = 0) { console.log(console.stackstr(skip+1)); }; +console.stack = function (skip = 0) { + console.log(console.stackstr(skip + 1)); +}; console.stdout_lvl = 1; console.trace = console.stack; @@ -206,20 +211,19 @@ console.doc = { say: "Write raw text to console, plus a newline.", stack: "Output a stacktrace to console.", console: "Output directly to in game console.", - clear: "Clear console." + clear: "Clear console.", }; globalThis.global = globalThis; var profcache = {}; -function use(file, env = {}, script) -{ +function use(file, env = {}, script) { file = Resources.find_script(file); var st = profile.now(); - + profcache[file] ??= []; - + if (use.cache[file]) { var ret = use.cache[file].call(env); profile.addreport(profcache, file, st); @@ -227,7 +231,7 @@ function use(file, env = {}, script) } script ??= Resources.replstrs(file); script = `(function() { var self = this; ${script}; })`; - var fn = os.eval(file,script); + var fn = os.eval(file, script); use.cache[file] = fn; var ret = fn.call(env); profile.addreport(profcache, file, st); @@ -236,35 +240,34 @@ function use(file, env = {}, script) use.cache = {}; -global.check_registers = function(obj) -{ - if (typeof obj.update === 'function') - obj.timers.push(Register.update.register(obj.update.bind(obj))); +global.check_registers = function (obj) { + if (typeof obj.update === "function") + obj.timers.push(Register.update.register(obj.update.bind(obj))); - if (typeof obj.physupdate === 'function') - obj.timers.push(Register.physupdate.register(obj.physupdate.bind(obj))); + if (typeof obj.physupdate === "function") + obj.timers.push(Register.physupdate.register(obj.physupdate.bind(obj))); - if (typeof obj.draw === 'function') - obj.timers.push(Register.draw.register(obj.draw.bind(obj), obj)); + if (typeof obj.draw === "function") + obj.timers.push(Register.draw.register(obj.draw.bind(obj), obj)); - if (typeof obj.debug === 'function') - obj.timers.push(Register.debug.register(obj.debug.bind(obj))); + if (typeof obj.debug === "function") + obj.timers.push(Register.debug.register(obj.debug.bind(obj))); - if (typeof obj.gui === 'function') - obj.timers.push(Register.gui.register(obj.gui.bind(obj))); - - if (typeof obj.screengui === 'function') - obj.timers.push(Register.screengui.register(obj.screengui.bind(obj))); + if (typeof obj.gui === "function") + obj.timers.push(Register.gui.register(obj.gui.bind(obj))); - for (var k in obj) { - if (!k.startswith("on_")) continue; - var signal = k.fromfirst("on_"); - Event.observe(signal, obj, obj[k]); - }; -} + if (typeof obj.screengui === "function") + obj.timers.push(Register.screengui.register(obj.screengui.bind(obj))); + + for (var k in obj) { + if (!k.startswith("on_")) continue; + var signal = k.fromfirst("on_"); + Event.observe(signal, obj, obj[k]); + } +}; Object.assign(global, use("scripts/base")); -global.obscure('global'); +global.obscure("global"); global.mixin("scripts/render"); global.mixin("scripts/debug"); @@ -272,82 +275,89 @@ var frame_t = profile.secs(profile.now()); var sim = {}; sim.mode = "play"; -sim.play = function() { this.mode = "play"; os.reindex_static(); }; -sim.playing = function() { return this.mode === 'play'; }; -sim.pause = function() { this.mode = "pause"; }; -sim.paused = function() { return this.mode === 'pause'; }; -sim.step = function() { this.mode = 'step'; }; -sim.stepping = function() { return this.mode === 'step'; } +sim.play = function () { + this.mode = "play"; + os.reindex_static(); +}; +sim.playing = function () { + return this.mode === "play"; +}; +sim.pause = function () { + this.mode = "pause"; +}; +sim.paused = function () { + return this.mode === "pause"; +}; +sim.step = function () { + this.mode = "step"; +}; +sim.stepping = function () { + return this.mode === "step"; +}; var physlag = 0; var gggstart = game.engine_start; -game.engine_start = function(s) { +game.engine_start = function (s) { game.startengine = 1; - gggstart(function() { - global.mixin("scripts/sound.js"); - world_start(); - window.set_icon(os.make_texture("icons/moon.gif")) - Object.readonly(window.__proto__, 'vsync'); - Object.readonly(window.__proto__, 'enable_dragndrop'); - Object.readonly(window.__proto__, 'enable_clipboard'); - Object.readonly(window.__proto__, 'high_dpi'); - Object.readonly(window.__proto__, 'sample_count'); - s(); - - shape.quad = { - pos:os.make_buffer([ - 0,0,0, - 1,0,0, - 0,1,0, - 1,1,0 - ],0), - verts: 4, - uv: os.make_buffer([0,1,1,1,0,0,1,0],2), - index: os.make_buffer([0,1,2,2,1,3], 1), - count: 6 - }; - - shape.triangle = { - pos: os.make_buffer([ - 0,0,0, - 0.5,1,0, - 1,0,0] - ,0), - uv: os.make_buffer([0,0,0.5,1,1,0],2), - verts: 3, - count: 3, - index: os.make_buffer([0,2,1],1), - }; - - render.init(); - }, process, window.size.x, window.size.y); -} + gggstart( + function () { + global.mixin("scripts/sound.js"); + world_start(); + window.set_icon(os.make_texture("icons/moon.gif")); + Object.readonly(window.__proto__, "vsync"); + Object.readonly(window.__proto__, "enable_dragndrop"); + Object.readonly(window.__proto__, "enable_clipboard"); + Object.readonly(window.__proto__, "high_dpi"); + Object.readonly(window.__proto__, "sample_count"); + s(); + + shape.quad = { + pos: os.make_buffer([0, 0, 0, 1, 0, 0, 0, 1, 0, 1, 1, 0], 0), + verts: 4, + uv: os.make_buffer([0, 1, 1, 1, 0, 0, 1, 0], 2), + index: os.make_buffer([0, 1, 2, 2, 1, 3], 1), + count: 6, + }; + + shape.triangle = { + pos: os.make_buffer([0, 0, 0, 0.5, 1, 0, 1, 0, 0], 0), + uv: os.make_buffer([0, 0, 0.5, 1, 1, 0], 2), + verts: 3, + count: 3, + index: os.make_buffer([0, 2, 1], 1), + }; + + render.init(); + }, + process, + window.size.x, + window.size.y, + ); +}; game.startengine = 0; var frames = []; -function process() -{ +function process() { var startframe = profile.now(); var dt = profile.secs(profile.now()) - frame_t; frame_t = profile.secs(profile.now()); - + prosperon.appupdate(dt); input.procdown(); - + if (sim.mode === "play" || sim.mode === "step") { - prosperon.update(dt*game.timescale); - if (sim.mode === "step") - sim.pause(); + prosperon.update(dt * game.timescale); + if (sim.mode === "step") sim.pause(); physlag += dt; while (physlag > physics.delta) { physlag -= physics.delta; var st = profile.now(); - prosperon.phys2d_step(physics.delta*game.timescale); - prosperon.physupdate(physics.delta*game.timescale); + prosperon.phys2d_step(physics.delta * game.timescale); + prosperon.physupdate(physics.delta * game.timescale); profile.addreport(profcache, "physics step", st); } } @@ -359,58 +369,52 @@ function process() prosperon.screengui(); prosperon.hookend?.(); profile.addreport(profcache, "render frame", st); - frames.push(profile.secs(profile.now()-startframe)); + frames.push(profile.secs(profile.now() - startframe)); if (frames.length > 20) frames.shift(); } -globalThis.fps = function() -{ +globalThis.fps = function () { var sum = 0; - for (var i = 0; i < frames.length; i++) - sum += frames[i]; - return frames.length/sum; -} + for (var i = 0; i < frames.length; i++) sum += frames[i]; + return frames.length / sum; +}; game.timescale = 1; -var eachobj = function(obj,fn) -{ +var eachobj = function (obj, fn) { var val = fn(obj); if (val) return val; for (var o in obj.objects) { if (obj.objects[o] === obj) console.error(`Object ${obj.toString()} is referenced by itself.`); - val = eachobj(obj.objects[o],fn); + val = eachobj(obj.objects[o], fn); if (val) return val; } -} +}; -game.all_objects = function(fn, startobj = world) { return eachobj(startobj,fn); }; -game.find_object = function(fn, startobj = world) { - -} +game.all_objects = function (fn, startobj = world) { + return eachobj(startobj, fn); +}; +game.find_object = function (fn, startobj = world) {}; game.tags = {}; -game.tag_add = function(tag, obj) { +game.tag_add = function (tag, obj) { game.tags[tag] ??= {}; game.tags[tag][obj.guid] = obj; -} +}; -game.tag_rm = function(tag, obj) { +game.tag_rm = function (tag, obj) { delete game.tags[tag][obj.guid]; -} +}; -game.tag_clear_guid = function(guid) -{ - for (var tag in game.tags) - delete game.tags[tag][guid]; -} +game.tag_clear_guid = function (guid) { + for (var tag in game.tags) delete game.tags[tag][guid]; +}; -game.objects_with_tag = function(tag) -{ +game.objects_with_tag = function (tag) { if (!game.tags[tag]) return []; return Object.values(game.tags[tag]); -} +}; game.doc = {}; game.doc.object = "Returns the entity belonging to a given id."; @@ -418,89 +422,85 @@ game.doc.pause = "Pause game simulation."; game.doc.play = "Resume or start game simulation."; game.doc.camera = "Current camera."; -game.texture = function(path) -{ +game.texture = function (path) { if (game.texture.cache[path]) return game.texture.cache[path]; - + if (!io.exists(path)) { console.warn(`Missing texture: ${path}`); game.texture.cache[path] = game.texture("icons/no_tex.gif"); - } else - game.texture.cache[path] ??= os.make_texture(path); - + } else game.texture.cache[path] ??= os.make_texture(path); + return game.texture.cache[path]; -} +}; game.texture.cache = {}; prosperon.semver = {}; -prosperon.semver.valid = function(v, range) -{ - v = v.split('.'); - range = range.split('.'); +prosperon.semver.valid = function (v, range) { + v = v.split("."); + range = range.split("."); if (v.length !== 3) return undefined; if (range.length !== 3) return undefined; - if (range[0][0] === '^') { + if (range[0][0] === "^") { range[0] = range[0].slice(1); if (parseInt(v[0]) >= parseInt(range[0])) return true; - + return false; } - - if (range[0] === '~') { + + if (range[0] === "~") { range[0] = range[0].slice(1); for (var i = 0; i < 2; i++) if (parseInt(v[i]) < parseInt(range[i])) return false; return true; } - return prosperon.semver.cmp(v.join('.'), range.join('.')) === 0; -} + return prosperon.semver.cmp(v.join("."), range.join(".")) === 0; +}; -prosperon.semver.cmp = function(v1, v2) -{ - var ver1 = v1.split('.'); - var ver2 = v2.split('.'); +prosperon.semver.cmp = function (v1, v2) { + var ver1 = v1.split("."); + var ver2 = v2.split("."); for (var i = 0; i < 3; i++) { var n1 = parseInt(ver1[i]); var n2 = parseInt(ver2[i]); - if (n1 > n2) - return 1; - else if (n1 < n2) - return -1; + if (n1 > n2) return 1; + else if (n1 < n2) return -1; } - - return 0; -} -prosperon.semver.doc = "Functions for semantic versioning numbers. Semantic versioning is given as a triple digit number, as MAJOR.MINOR.PATCH."; -prosperon.semver.cmp.doc = "Compare two semantic version numbers, given like X.X.X."; + return 0; +}; + +prosperon.semver.doc = + "Functions for semantic versioning numbers. Semantic versioning is given as a triple digit number, as MAJOR.MINOR.PATCH."; +prosperon.semver.cmp.doc = + "Compare two semantic version numbers, given like X.X.X."; prosperon.semver.valid.doc = `Test if semantic version v is valid, given a range. Range is given by a semantic versioning number, prefixed with nothing, a ~, or a ^. ~ means that MAJOR and MINOR must match exactly, but any PATCH greater or equal is valid. ^ means that MAJOR must match exactly, but any MINOR and PATCH greater or equal is valid.`; -prosperon.iconified = function(icon) {}; -prosperon.focus = function(focus) {}; -prosperon.resize = function(dimensions) { +prosperon.iconified = function (icon) {}; +prosperon.focus = function (focus) {}; +prosperon.resize = function (dimensions) { window.size.x = dimensions.x; window.size.y = dimensions.y; }; -prosperon.suspended = function(sus) {}; -prosperon.mouseenter = function(){}; -prosperon.mouseleave = function(){}; -prosperon.touchpress = function(touches){}; -prosperon.touchrelease = function(touches){}; -prosperon.touchmove = function(touches){}; -prosperon.clipboardpaste = function(str){}; -prosperon.quit = function(){ +prosperon.suspended = function (sus) {}; +prosperon.mouseenter = function () {}; +prosperon.mouseleave = function () {}; +prosperon.touchpress = function (touches) {}; +prosperon.touchrelease = function (touches) {}; +prosperon.touchmove = function (touches) {}; +prosperon.clipboardpaste = function (str) {}; +prosperon.quit = function () { say(profile.printreport(profcache, "USE REPORT")); say(profile.printreport(entityreport, "ENTITY REPORT")); - + console.info("QUITTING"); for (var i in debug.log.time) - say(debug.log.time[i].map(x=>profile.ms(x))); + say(debug.log.time[i].map((x) => profile.ms(x))); }; global.mixin("scripts/input"); @@ -524,7 +524,7 @@ var timer = { this.end(); delete this.fn; }, - + delay(fn, secs) { var t = Object.create(this); t.time = secs; @@ -550,23 +550,26 @@ var Register = { add_cb(name) { var n = {}; var fns = []; - - n.register = function(fn, obj) { - if (typeof fn !== 'function') return; - if (typeof obj === 'object') - fn = fn.bind(obj); + + n.register = function (fn, obj) { + if (typeof fn !== "function") return; + if (typeof obj === "object") fn = fn.bind(obj); fns.push(fn); - return function() { + return function () { fns.remove(fn); }; - } - prosperon[name] = function(...args) { fns.forEach(x => x(...args)); } + }; + prosperon[name] = function (...args) { + fns.forEach((x) => x(...args)); + }; prosperon[name].fns = fns; - n.clear = function() { fns = []; } + n.clear = function () { + fns = []; + }; Register[name] = n; Register.registries.push(n); - + return n; }, }; @@ -588,16 +591,16 @@ var Event = { }, unobserve(name, obj) { - this.events[name] = this.events[name].filter(x => x[0] !== obj); + this.events[name] = this.events[name].filter((x) => x[0] !== obj); }, rm_obj(obj) { - Object.keys(this.events).forEach(name => Event.unobserve(name,obj)); + Object.keys(this.events).forEach((name) => Event.unobserve(name, obj)); }, notify(name, ...args) { if (!this.events[name]) return; - this.events[name].forEach(function(x) { + this.events[name].forEach(function (x) { x[1].call(x[0], ...args); }); }, @@ -623,9 +626,11 @@ global.mixin("scripts/spline"); global.mixin("scripts/components"); window.doc = {}; -window.doc.dimensions = "Window width and height packaged in an array [width,height]"; +window.doc.dimensions = + "Window width and height packaged in an array [width,height]"; window.doc.title = "Name in the title bar of the window."; -window.doc.boundingbox = "Boundingbox of the window, with top and right being its height and width."; +window.doc.boundingbox = + "Boundingbox of the window, with top and right being its height and width."; global.mixin("scripts/actor"); global.mixin("scripts/entity"); @@ -634,9 +639,13 @@ function world_start() { globalThis.world = Object.create(entity); world.transform = os.make_transform(); world.objects = {}; - world.toString = function() { return "world"; }; + world.toString = function () { + return "world"; + }; world.ur = "world"; - world.kill = function() { this.clear(); }; + world.kill = function () { + this.clear(); + }; world.phys = 2; world.zoom = 1; world._ed = { selectable: false }; @@ -651,9 +660,9 @@ global.mixin("scripts/widget"); globalThis.mum = app.spawn("scripts/mum"); window.title = `Prosperon v${prosperon.version}`; -window.size = [500,500]; -window.boundingbox = function() { +window.size = [500, 500]; +window.boundingbox = function () { var pos = game.camera.pos; var wh = window.rendersize.scale(game.camera.zoom); - return bbox.fromcwh(pos,wh); -} + return bbox.fromcwh(pos, wh); +}; diff --git a/scripts/entity.js b/scripts/entity.js index 5899f26..0128afa 100644 --- a/scripts/entity.js +++ b/scripts/entity.js @@ -105,7 +105,7 @@ var entity = { set scale(x) { this.transform.scale = x; }, move(vec) { this.pos = this.pos.add(vec); }, - rotate(x) { this.angle += x; }, + rotate(x) { this.transform.rotate(x, [0,0,-1]); }, grow(vec) { if (typeof vec === 'number') vec = [vec,vec]; this.scale = this.scale.map((x,i) => x*vec[i]); diff --git a/source/engine/HandmadeMath.c b/source/engine/HandmadeMath.c index d691177..b0a641f 100644 --- a/source/engine/HandmadeMath.c +++ b/source/engine/HandmadeMath.c @@ -1909,6 +1909,21 @@ HMM_Quat HMM_M4ToQ_LH(HMM_Mat4 M) { return Q; } +float HMM_Q_Roll(HMM_Quat q) +{ + return atan2(2.0*(q.x*q.y + q.w*q.z), q.w*q.w + q.x*q.x - q.y*q.y - q.z*q.z); +} + +float HMM_Q_Pitch(HMM_Quat q) +{ + return asin(-2.0*(q.x*q.z - q.w*q.y)); +} + +float HMM_Q_Yaw(HMM_Quat q) +{ + return atan2(2.0*(q.y*q.z + q.w*q.x), q.w*q.w - q.x*q.x - q.y*q.y + q.z*q.z); +} + HMM_Quat HMM_QFromAxisAngle_RH(HMM_Vec3 Axis, float AngleOfRotation) { HMM_Quat Result; diff --git a/source/engine/HandmadeMath.h b/source/engine/HandmadeMath.h index 08a1f13..455d42a 100644 --- a/source/engine/HandmadeMath.h +++ b/source/engine/HandmadeMath.h @@ -658,4 +658,8 @@ HMM_Quat HMM_M4ToQ_LH(HMM_Mat4 M); HMM_Quat HMM_QFromAxisAngle_RH(HMM_Vec3 Axis, float AngleOfRotation); HMM_Quat HMM_QFromAxisAngle_LH(HMM_Vec3 Axis, float AngleOfRotation); +float HMM_Q_Roll(HMM_Quat q); +float HMM_Q_Yaw(HMM_Quat q); +float HMM_Q_Pitch(HMM_Quat q); + #endif /* HANDMADE_MATH_H */ diff --git a/source/engine/jsffi.c b/source/engine/jsffi.c index d1119cd..d9cfe44 100644 --- a/source/engine/jsffi.c +++ b/source/engine/jsffi.c @@ -1474,6 +1474,7 @@ JSC_GETSET(transform, pos, vec3) JSC_GETSET(transform, scale, vec3) JSC_GETSET(transform, rotation, quat) JSC_CCALL(transform_move, transform_move(js2transform(self), js2vec3(argv[0])); ) + JSC_CCALL(transform_lookat, HMM_Vec3 point = js2vec3(argv[0]); transform *go = js2transform(self); @@ -1488,6 +1489,15 @@ JSC_CCALL(transform_rotate, t->rotation = HMM_MulQ(t->rotation,rot); ) +JSC_CCALL(transform_angle, + HMM_Vec3 axis = js2vec3(argv[0]); + transform *t = js2transform(self); + if (axis.x) return angle2js(HMM_Q_Roll(t->rotation)); + if (axis.y) return angle2js(HMM_Q_Pitch(t->rotation)); + if (axis.z) return angle2js(HMM_Q_Yaw(t->rotation)); + return angle2js(0); +) + JSC_CCALL(transform_direction, transform *t = js2transform(self); return vec32js(HMM_QVRot(js2vec3(argv[0]), t->rotation)); @@ -1499,6 +1509,7 @@ static const JSCFunctionListEntry js_transform_funcs[] = { CGETSET_ADD(transform, rotation), MIST_FUNC_DEF(transform, move, 1), MIST_FUNC_DEF(transform, rotate, 2), + MIST_FUNC_DEF(transform, angle, 1), MIST_FUNC_DEF(transform, lookat, 1), MIST_FUNC_DEF(transform, direction, 1), }; @@ -1988,11 +1999,18 @@ JSC_GET(texture, height, number) JSC_GET(texture, frames, number) JSC_GET(texture, delays, ints) +JSC_SCALL(texture_save, texture_save(js2texture(self), str)); + +JSC_CCALL(texture_blit, + texture_blit(js2texture(self), js2texture(argv[0]), js2number(argv[1]), js2number(argv[2]), js2number(argv[3]), js2number(argv[4]))) + static const JSCFunctionListEntry js_texture_funcs[] = { MIST_GET(texture, width), MIST_GET(texture, height), MIST_GET(texture, frames), MIST_GET(texture, delays), + MIST_FUNC_DEF(texture, save, 2), + MIST_FUNC_DEF(texture, blit, 3) }; JSC_GETSET(font, linegap, number) @@ -2289,6 +2307,8 @@ JSC_SCALL(os_make_texture, JS_SetPropertyStr(js, ret, "path", JS_DupValue(js,argv[0])); ) +JSC_CCALL(os_make_tex_data, ret = texture2js(texture_empty(js2number(argv[0]), js2number(argv[1]), js2number(argv[2])))) + JSC_CCALL(os_make_font, font *f = MakeFont(js2str(argv[0]), js2number(argv[1])); ret = font2js(f); @@ -2517,6 +2537,7 @@ static const JSCFunctionListEntry js_os_funcs[] = { MIST_FUNC_DEF(os, make_poly2d, 2), MIST_FUNC_DEF(os, make_seg2d, 1), MIST_FUNC_DEF(os, make_texture, 1), + MIST_FUNC_DEF(os, make_tex_data, 3), MIST_FUNC_DEF(os, make_font, 2), MIST_FUNC_DEF(os, make_model, 1), MIST_FUNC_DEF(os, make_transform, 0), diff --git a/source/engine/texture.c b/source/engine/texture.c index 5ca8d30..2b82737 100644 --- a/source/engine/texture.c +++ b/source/engine/texture.c @@ -5,6 +5,7 @@ #include "sokol/sokol_gfx.h" #include #include +#include #include "resources.h" @@ -173,6 +174,26 @@ void texture_free(texture *tex) free(tex); } +struct texture *texture_empty(int w, int h, int n) +{ + texture *tex = calloc(1,sizeof(*tex)); + tex->data = calloc(w*h*n, sizeof(unsigned char)); + tex->width = w; + tex->height = h; + sg_image_data sgdata; + sgdata.subimage[0][0] = (sg_range){.ptr = tex->data, .size = w*h*4}; + tex->id = sg_make_image(&(sg_image_desc){ + .type = SG_IMAGETYPE_2D, + .width = tex->width, + .height = tex->height, + .usage = SG_USAGE_IMMUTABLE, + .num_mipmaps = 1, + .data = sgdata, + }); + + return tex; +} + struct texture *texture_fromdata(void *raw, long size) { struct texture *tex = calloc(1, sizeof(*tex)); @@ -260,6 +281,64 @@ double grad (int hash, double x, double y, double z) }*/ } +void texture_save(texture *tex, const char *file) +{ + char *ext = strrchr(file, '.'); + printf("SAVING TO %s with ext %s\n", file, ext); + if (!strcmp(ext, ".png")) + stbi_write_png(file, tex->width, tex->height, 4, tex->data, 4*tex->width); + else if (!strcmp(ext, ".bmp")) + stbi_write_bmp(file, tex->width, tex->height, 4, tex->data); + else if (!strcmp(ext, ".tga")) + stbi_write_tga(file, tex->width, tex->height, 4, tex->data); + else if (!strcmp(ext, ".jpg") || !strcmp(ext, ".jpeg")) + stbi_write_jpg(file, tex->width, tex->height, 4, tex->data, 5); +} + +void blit_image(uint8_t* src, uint8_t* dest, int src_width, int src_height, int dest_width, int dest_height, int sx, int sy, int sw, int sh) { + int src_stride = src_width * 4; + int dest_stride = dest_width * 4; + + for (int y = 0; y < sw; y++) { + for (int x = 0; x < sh; x++) { + int src_index = (y * src_stride) + (x * 4); + int dest_index = ((y + sy) * dest_stride) + ((x + sx) * 4); + + // Calculate the alpha value for the source pixel + uint8_t src_alpha = src[src_index + 3]; + + // Calculate the alpha value for the destination pixel + uint8_t dest_alpha = dest[dest_index + 3]; + + // Calculate the resulting alpha value + uint8_t result_alpha = src_alpha + (255 - src_alpha) * dest_alpha / 255; + + // Calculate the resulting RGB values + uint8_t result_red = (src[src_index + 0] * src_alpha + dest[dest_index + 0] * (255 - src_alpha) * dest_alpha / 255) / result_alpha; + uint8_t result_green = (src[src_index + 1] * src_alpha + dest[dest_index + 1] * (255 - src_alpha) * dest_alpha / 255) / result_alpha; + uint8_t result_blue = (src[src_index + 2] * src_alpha + dest[dest_index + 2] * (255 - src_alpha) * dest_alpha / 255) / result_alpha; + + // Set the resulting pixel values + dest[dest_index + 0] = result_red; + dest[dest_index + 1] = result_green; + dest[dest_index + 2] = result_blue; + dest[dest_index + 3] = result_alpha; + } + } +} + + + +// Function to draw source image pixels on top of a destination image +void texture_blit(texture *dest, texture *src, int x, int y, int w, int h) { + blit_image(src->data, dest->data, src->width, src->height, dest->height, dest->width, x, y, w, h); +} + +void texture_flip(texture *tex, int y) +{ + +} + static int p[512] = {151,160,137,91,90,15, 131,13,201,95,96,53,194,233,7,225,140,36,103,30,69,142,8,99,37,240,21,10,23, 190, 6,148,247,120,234,75,0,26,197,62,94,252,219,203,117,35,11,32,57,177,33, diff --git a/source/engine/texture.h b/source/engine/texture.h index 74d3349..9fa3b5f 100644 --- a/source/engine/texture.h +++ b/source/engine/texture.h @@ -40,8 +40,11 @@ typedef struct img_sampler{ texture *texture_from_file(const char *path); void texture_free(texture *tex); - struct texture *texture_fromdata(void *raw, long size); +texture *texture_empty(int width, int height, int n); +void texture_blit(texture *dest, texture *src, int x, int y, int w, int h); +void texture_flip(texture *tex, int y); +void texture_save(texture *tex, const char *file); double perlin(double x, double y, double z);