Compare commits

...

56 commits

Author SHA1 Message Date
John Alanbrook a3f68316ca upgrade imgui to 1.91.1, sokol to sept 10 2024 2024-09-14 12:15:08 -05:00
John Alanbrook 640c8389f3 attempt at sequencer 2024-09-14 09:36:40 -05:00
John Alanbrook 79c5e7f3a4 add imnodes and neo sequencer 2024-09-13 17:56:02 -05:00
John Alanbrook 2a8dae2bf6 add then to entities 2024-09-13 14:34:54 -05:00
John Alanbrook 01905a26a2 add layers to callbacks 2024-09-13 10:28:44 -05:00
John Alanbrook ad6b82ded1 add imgui drag n drop 2024-09-12 23:17:09 -05:00
John Alanbrook 41c76e1a70 add render image string capability 2024-09-11 12:25:42 -05:00
John Alanbrook e3029994f9 add color to sprite ssbo 2024-09-10 14:33:49 -05:00
John Alanbrook 0d5ce8f62c fix particles 2024-09-09 18:55:07 -05:00
John Alanbrook 8da89f1032 add imgui popups 2024-09-08 11:24:21 -05:00
John Alanbrook a1cd43bcfd fix sounds; add imgui 2024-09-07 15:14:03 -05:00
John Alanbrook 188c55a8e0 fix text issue 2024-09-07 00:11:34 -05:00
John Alanbrook ec5e9249f0 text fixing 2024-09-06 22:47:04 -05:00
John Alanbrook 67fe729d8c fix double input bug 2024-09-06 18:51:09 -05:00
John Alanbrook af79dba48a add imgui int input 2024-09-06 10:52:00 -05:00
John Alanbrook fd8b1b8006 fix uickjs string print issue 2024-09-04 14:34:45 -05:00
John Alanbrook 6d2696437c add imgui listbox 2024-09-04 13:23:20 -05:00
John Alanbrook d33c1af92c vector imgui 2024-09-04 07:41:48 -05:00
John Alanbrook 5ddf3558ef unsure 2024-09-03 14:08:46 -05:00
John Alanbrook 99bd0091f9 fix input down 2024-09-01 16:16:35 -05:00
John Alanbrook ef0a59f489 fix entity delete error 2024-09-01 13:59:34 -05:00
John Alanbrook 794e581336 add imgui text box; fix image blit 2024-08-31 00:38:26 -05:00
John Alanbrook 1305a762e9 add imgui collapsing header 2024-08-30 18:22:53 -05:00
John Alanbrook dc59f0642c fix image finding 2024-08-30 14:17:37 -05:00
John Alanbrook 2c1f34177b add entity start on play 2024-08-29 16:55:01 -05:00
John Alanbrook dc25e23aa8 add optional pivot for vector.rotate 2024-08-29 10:20:22 -05:00
John Alanbrook bc313d6f8c add more imgui functions 2024-08-28 16:38:31 -05:00
John Alanbrook fce8ff6081 remove spam logging 2024-08-28 13:49:24 -05:00
John Alanbrook 603d9a5406 not sure 2024-08-27 20:56:45 -05:00
John Alanbrook 6c6593c3d9 fix gizmo drawing 2024-08-27 14:58:08 -05:00
John Alanbrook 31a47b5b7d add easy hashify function 2024-08-27 13:57:38 -05:00
John Alanbrook 2ad2d18af7 add cheat menu 2024-08-26 11:13:26 -05:00
John Alanbrook 8abb0ae10f fix imgui input handle capture 2024-08-25 17:42:43 -05:00
John Alanbrook 88de7fb547 add console switcher 2024-08-25 15:29:35 -05:00
John Alanbrook 94d4d86944 fix particle render bug 2024-08-25 14:23:22 -05:00
John Alanbrook 76497fb600 add music audio bus 2024-08-25 06:35:04 -05:00
John Alanbrook 05aeb62242 add more imgui functions 2024-08-25 00:18:30 -05:00
John Alanbrook 9fd958f414 add alternate output term path 2024-08-24 18:46:21 -05:00
John Alanbrook 39cbd92bfb fix polygon rendering bug 2024-08-24 18:40:29 -05:00
John Alanbrook ed7d65523a fix resource finding 2024-08-24 08:04:41 -05:00
John Alanbrook 27fa286625 add optional end function call to tweens 2024-08-23 14:17:51 -05:00
John Alanbrook b589a742cf add y sort option 2024-08-22 14:43:04 -05:00
John Alanbrook b31069fe91 animated gifs correctly hotsync 2024-08-22 14:41:01 -05:00
John Alanbrook 285a227691 fix camera view calculation 2024-08-22 13:31:00 -05:00
John Alanbrook 5690ee7fba add profiling and testing 2024-08-20 02:17:56 -05:00
John Alanbrook 05acefd683 fix gui.cpp 2024-08-19 07:47:27 -05:00
John Alanbrook 849c85cd7a imgui window stuff 2024-08-15 12:26:37 -05:00
John Alanbrook daad058225 image hotreload 2024-08-12 15:19:34 -05:00
John Alanbrook a29ce7e8d0 skeleton animation 2024-08-10 04:06:44 -05:00
John Alanbrook f5a89915eb model skinning 2024-08-09 14:39:31 -05:00
John Alanbrook 57c2285019 clean up 3d model creation 2024-08-08 17:32:58 -05:00
John Alanbrook ceccd032c2 add colors; add repl check 2024-08-08 13:25:47 -05:00
John Alanbrook 515eb65b22 shader hotreload; better circle shader; add nogame for setup help; fix io.mod; add joystick input; fix entity hotreload; implement array length in C; add default game.size 2024-08-07 16:55:08 -05:00
John Alanbrook a28230a647 rudimentary hot reload for actors 2024-08-06 16:05:24 -05:00
John Alanbrook 432d8200f3 add memory profiling tooling 2024-08-06 14:23:21 -05:00
John Alanbrook ceb728b1a7 memory output 2024-08-05 15:26:18 -05:00
80 changed files with 18258 additions and 2777 deletions

View file

@ -75,7 +75,7 @@ else
CPPFLAGS += -O2
endif
CPPFLAGS += -DHAVE_CEIL -DCP_USE_CGTYPES=0 -DCP_USE_DOUBLES=0 -DHAVE_FLOOR -DHAVE_FMOD -DHAVE_LRINT -DHAVE_LRINTF $(includeflag) $(WARNING_FLAGS) -I. -DVER=\"$(SEM)\" -DCOM=\"$(COM)\" -DINFO=\"$(INFO)\" -Wno-narrowing #-DENABLE_SINC_MEDIUM_CONVERTER -DENABLE_SINC_FAST_CONVERTER -DCP_COLLISION_TYPE_TYPE=uintptr_t -DCP_BITMASK_TYPE=uintptr_t
CPPFLAGS += -DHAVE_CEIL -DCP_USE_CGTYPES=0 -DCP_USE_DOUBLES=0 -DHAVE_FLOOR -DHAVE_FMOD -DHAVE_LRINT -DHAVE_LRINTF $(includeflag) $(WARNING_FLAGS) -I. -DVER=\"$(SEM)\" -DCOM=\"$(COM)\" -DDATE=\"$(DATE)\" -DINFO=\"$(INFO)\" -Wno-narrowing #-DENABLE_SINC_MEDIUM_CONVERTER -DENABLE_SINC_FAST_CONVERTER -DCP_COLLISION_TYPE_TYPE=uintptr_t -DCP_BITMASK_TYPE=uintptr_t
CPPFLAGS += -DCONFIG_VERSION=\"2024-02-14\" -DCONFIG_BIGNUM #for quickjs
# ENABLE_SINC_[BEST|FAST|MEDIUM]_CONVERTER
@ -166,6 +166,7 @@ APP = prosperon
NAME = $(APP)$(INFO)$(EXT)
SEM != git describe --tags --abbrev=0
COM != git rev-parse --short HEAD
DATE != date +%s
LDLIBS += $(STEAMAPI)
LDLIBS := $(addprefix -l, $(LDLIBS))

View file

@ -1,20 +1,89 @@
var actor = {};
actor.spawn = function(script, config, callback){
if (typeof script !== 'string') return undefined;
var padawan = Object.create(actor);
use(script, padawan);
var actor_urs = {};
var script_times = {};
var actor_spawns = {};
globalThis.class_use = function(script, config, base, callback)
{
var file = Resources.find_script(script);
if (!file) {
var ret = Object.create(base);
if (callback) callback(ret);
return ret;
}
if (!actor_urs[file]) {
var newur = Object.create(base);
actor_urs[file] = newur;
script_times[file] = io.mod(file);
actor_spawns[file] = [];
}
var padawan = Object.create(actor_urs[file]);
actor_spawns[file].push(padawan);
padawan._file = file;
padawan._root = file.dir();
if (callback) callback(padawan);
var script = Resources.replstrs(file);
script = `(function() { var self = this; var $ = this.__proto__; ${script}; })`;
var fn = os.eval(file,script);
fn.call(padawan);
if (typeof config === 'object')
Object.merge(padawan,config);
return padawan;
}
actor.hotreload = function()
{
profile.cache("hotreload", "check");
for (var i in script_times) {
if (io.mod(i) > script_times[i]) {
say(`HOT RELAODING ${i}`);
script_times[i] = io.mod(i);
var script = Resources.replstrs(i);
script = `(function() {
var self = this;
var $ = this.__proto__;
${script};
})`;
var fn = os.eval(i,script);
for (var obj of actor_spawns[i]) {
var a = obj;
for (var t of a.timers)
t();
a.timers = [];
var save = json.decode(json.encode(a));
fn.call(a);
Object.merge(a,save);
check_registers(a);
}
}
}
profile.endcache();
}
actor.spawn = function(script, config){
if (typeof script !== 'string') return undefined;
var padawan = class_use(script, config, actor);
padawan.padawans = [];
padawan.timers = [];
padawan.master = this;
Object.hide(padawan, "master", "timers", "padawans");
Object.hide(padawan, "master","padawans");
padawan.toString = function() { return script; }
check_registers(padawan);
this.padawans.push(padawan);
if (padawan.awake) padawan.awake();
return padawan;
};
@ -41,9 +110,11 @@ actor.kill = function(){
this.padawans.forEach(p => p.kill());
this.padawans = [];
this.__dead__ = true;
actor_spawns[this._file].remove(this);
if (typeof this.die === 'function') this.die();
if (typeof this.stop === 'function') this.stop();
if (typeof this.garbage === 'function') this.garbage();
if (typeof this.then === 'function') this.then();
};
actor.kill.doc = `Remove this actor and all its padawans from existence.`;
@ -52,12 +123,13 @@ actor.delay = function(fn, seconds) {
var timers = this.timers;
var stop = function() {
timers.remove(stop);
stop = undefined;
rm();
}
function execute() {
if (fn) fn();
if (stop.then) stop.then();
if (stop && stop.then) stop.then();
stop();
}
@ -77,9 +149,21 @@ actor.delay = function(fn, seconds) {
timers.push(stop);
return stop;
};
actor.delay.doc = `Call 'fn' after 'seconds' with 'this' set to the actor.`;
actor.interval = function(fn, seconds)
{
var self = this;
var stop;
var usefn = function() {
fn();
stop = self.delay(usefn, seconds);
}
stop = self.delay(usefn, seconds);
return stop;
}
actor.padawans = [];
global.app = Object.create(actor);

View file

@ -522,21 +522,27 @@ Object.hide = function(obj,...props)
{
for (var prop of props) {
var p = Object.getOwnPropertyDescriptor(obj,prop);
if (!p) {
console.error(`No property of name ${prop}.`);
continue;
}
if (!p) continue;
p.enumerable = false;
Object.defineProperty(obj, prop, p);
}
}
Object.enumerable = function(obj, val, ...props)
{
for (var prop of props) {
p = Object.getOwnPropertyDescriptor(obj,prop);
if (!p) continue;
p.enumerable = val;
Object.defineProperty(obj, prop, p);
}
}
Object.unhide = function(obj, ...props)
{
for (var prop of props) {
var p = Object.getOwnPropertyDescriptor(obj,prop);
if (!p)
continue;
if (!p) continue;
p.enumerable = true;
Object.defineProperty(obj, prop, p);
}
@ -551,7 +557,7 @@ Object.defineProperty(Object.prototype, 'obscure', {
Object.defineProperty(Object.prototype, 'mixin', {
value: function(obj) {
if (typeof obj === 'string')
obj = use(obj, this);
obj = use(obj);
if (obj)
Object.mixin(this, obj);
@ -785,7 +791,7 @@ Object.defineProperty(String.prototype, 'name', {
Object.defineProperty(String.prototype, 'set_name', {
value: function(name) {
var dir = this.dir();
return this.dir() + name + "." + this.ext();
return this.dir() + "/" + name + "." + this.ext();
}
});
@ -976,11 +982,7 @@ swizz.forEach(function(x) {
make_swizz();
Object.defineProperty(Array.prototype, 'normalized', {
value: function() {
var c = this.slice();
var len = Vector.length(c);
return c.map(v => v/len);
}
value: function() { return vector.norm(this); }
});
Object.defineProperty(Array.prototype, 'newfirst', {
@ -1081,15 +1083,6 @@ Object.defineProperty(Array.prototype, 'flat', {
}
});
Object.defineProperty(Array.prototype, 'anyjs', {
value: function(fn) {
var ev = this.every(function(x) {
return !fn(x);
});
return !ev;
}
});
/* Return true if array contains x */
Object.defineProperty(Array.prototype, 'empty', {
get: function() { return this.length === 0; },
@ -1264,7 +1257,7 @@ Object.defineProperty(Number.prototype, 'clamp', {
Math.clamp = vector.clamp;
Math.random_range = vector.random_range;
Math.rand_int = function(max) { return Math.floor(Math.random()*max); };
Math.rand_int = function(max = 9007199254740991) { return Math.floor(Math.random()*max); };
Math.snap = function(val, grid) {
if (!grid || grid === 1) return Math.round(val);
@ -1418,8 +1411,7 @@ bbox.fromobjs = function(objs)
/* VECTORS */
var Vector = {};
Vector.length = function(v) { return Math.hypot(...v); }
Vector.length = vector.length;
Vector.norm = vector.norm;
Vector.project = vector.project;
Vector.dot = vector.dot;
@ -1430,6 +1422,11 @@ Vector.random = function() {
Vector.angle_between = vector.angle_between;
Vector.rotate = vector.rotate;
vector.direction = function(from,to)
{
return vector.norm(to.sub(from));
}
Vector.equal = function(v1, v2, tol) {
if (!tol)

View file

@ -1,14 +1,15 @@
var Color = {
white: [255,255,255],
black: [0,0,0],
blue: [84,110,255],
green: [120,255,10],
yellow: [251,255,43],
red: [255,36,20],
teal: [96, 252, 237],
blue: [0,0,255],
green: [0,255,0],
yellow: [255,255,0],
red: [255,0,0],
gray: [181,181,181],
cyan: [0,255,255],
purple: [162,93,227],
orange: [255,144,64],
magenta: [255,0,255],
};
Color.editor = {};

View file

@ -35,7 +35,8 @@ var sprite = {
rect: fullrect,
anim:{},
playing: 0,
play(str = 0) {
anim_speed: 1,
play(str = 0, fn, loop = true, reverse = false) {
this.del_anim?.();
var self = this;
var stop;
@ -48,44 +49,43 @@ var sprite = {
var playing = self.anim[str];
if (!playing) return;
var f = 0;
if (reverse)
f = playing.frames.length-1;
self.path = playing.path;
function advance() {
if (!self) return;
if (!self.gameobject) return;
//self.path = playing.path;
self.frame = playing.frames[f].rect;
self.rect = [self.frame.x, self.frame.y, self.frame.w, self.frame.h];
self.update_dimensions();
f = (f+1)%playing.frames.length;
if (f === 0) {
self.anim_done?.();
if (!self.loop) { self.stop(); return; }
var done = false;
if (reverse) {
f = (((f-1)%playing.frames.length)+playing.frames.length)%playing.frames.length;
if (f === playing.frames.length-1) done = true;
}
stop = self.gameobject.delay(advance, playing.frames[f].time);
else {
f = (f+1)%playing.frames.length;
if (f === 0) done = true;
}
if (done) {
fn?.();
if (!loop) { self?.stop(); return; }
// self?.anim_done?.();
// if (!self.loop) { self.stop(); return; }
}
if (self)
stop = self.gameobject.delay(advance, playing.frames[f].time/self.anim_speed);
}
advance();
},
stop() {
this.del_anim?.();
},
set path(p) {
p = Resources.find_image(p);
if (!p) {
console.warn(`Could not find image ${p}.`);
return;
}
if (p === this.path) return;
this._p = p;
this.del_anim?.();
this.texture = game.texture(p);
this.diffuse = this.texture;
this.rect = fullrect;
var anim = SpriteAnim.make(p);
tex_sync() {
if (this.anim) this.stop();
this.rect = fullrect;
var anim = SpriteAnim.make(this.path);
this.update_dimensions();
this.sync();
@ -95,6 +95,26 @@ var sprite = {
this.pos = this.dimensions().scale(this.anchor);
},
stop() {
this.del_anim?.();
},
set path(p) {
p = Resources.find_image(p, this.gameobject._root);
if (!p) {
console.warn(`Could not find image ${p}.`);
return;
}
if (p === this.path) return;
this._p = p;
this.del_anim?.();
this.texture = game.texture(p);
this.diffuse = this.texture;
this.tex_sync();
},
get path() {
return this._p;
},
@ -200,7 +220,6 @@ Object.mixin(os.make_seg2d(), {
var animcache = {};
var SpriteAnim = {};
SpriteAnim.make = function(path) {
if (path in animcache) return animcache[path];
var anim;
if (io.exists(path.set_ext(".ase")))
anim = SpriteAnim.aseprite(path.set_ext(".ase"));

View file

@ -1,4 +1,11 @@
debug.build = function(fn) { fn(); }
debug.build = function(fn) { if (!debug.show) return; fn(); }
debug.show = true;
debug.urnames = false;
debug.termout = true;
debug.console = false;
debug.cheat = false;
debug.meta = false;
debug.showprofiler = false;
debug.fn_break = function(fn,obj = globalThis) {
if (typeof fn !== 'function') return;
@ -14,6 +21,7 @@ debug.draw_phys = false;
debug.draw_bb = false;
debug.draw_gizmos = false;
debug.draw_names = false;
debug.sprite_nums = false;
debug.draw = function() {
if (this.draw_phys) game.all_objects(function(x) { debug.draw_gameobject(x); });
@ -74,7 +82,7 @@ debug.inputs.f4 = function() {
};
debug.inputs.f4.doc = "Toggle drawing gizmos and names of objects.";
debug.gif = {
var gif = {
w: 640, /* Max width */
h: 480, /* Max height */
stretch: false, /* True if you want to stretch */

View file

@ -1,5 +1,9 @@
"use math";
os.mem_limit.doc = "Set the memory limit of the runtime in bytes.";
os.gc_threshold.doc = "Set the threshold before a GC pass is triggered in bytes. This is set to malloc_size + malloc_size>>1 after a GC pass.";
os.max_stacksize.doc = "Set the max stack size in bytes.";
Object.defineProperty(String.prototype, 'rm', {
value: function(index, endidx = index+1) { return this.slice(0,index) + this.slice(endidx); }
});
@ -62,7 +66,6 @@ Resources.replpath = function replpath(str, path) {
if (io.exists(tr)) return tr;
stem = stem.updir();
}
return str;
};
@ -70,9 +73,9 @@ Resources.replstrs = function replstrs(path) {
if (!path) return;
var script = io.slurp(path);
var regexp = /"[^"\s]*?\.[^"\s]+?"/g;
var stem = path.dir();
// remove console statements
if (!console.enabled)
script = Resources.rm_fn(/console\.(spam|info|warn|error)/, script);
@ -92,6 +95,24 @@ Resources.replstrs = function replstrs(path) {
return script;
};
Resources.is_sound = function(path) {
var ext = path.ext();
return Resources.sounds.any(x => x === ext);
}
Resources.is_animation = function(path)
{
if (path.ext() === 'gif' && Resources.gif.frames(path) > 1) return true;
if (path.ext() === 'ase') return true;
return false;
}
Resources.is_path = function(str)
{
return !/[\\\/:*?"<>|]/.test(str);
}
globalThis.json = {};
json.encode = function (value, replacer, space = 1) {
return JSON.stringify(value, replacer, space);
@ -123,32 +144,69 @@ Resources.images = ["png", "gif", "jpg", "jpeg"];
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.some(x => x === ext);
};
function find_ext(file, ext) {
if (io.exists(file)) return file;
function find_ext(file, ext, root = "") {
if (!file) return;
var file_ext = file.ext();
if (ext.some(x => x === file_ext)) return file;
for (var e of ext) {
var nf = `${file}.${e}`;
if (io.exists(nf)) return nf;
if (io.exists(nf))
return nf;
}
return;
var all_files = io.glob(`**/${file}.*`);
var find = undefined;
for (var e of ext) {
var finds = all_files.filter(x => x.ext() === e);
if (finds.length > 1)
console.warn(`Found conflicting files when searching for '${file}': ${json.encode(finds)}. Returning the first one.`);
if (finds.length > 0) {
find = finds[0];
break;
}
}
return find;
}
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);
};
var hashhit = 0;
var hashmiss = 0;
Object.defineProperty(Function.prototype, 'hashify', {
value: function() {
var hash = new Map();
var fn = this;
function ret() {
if (!hash.has(arguments[0]))
hash.set(arguments[0], fn(...arguments));
return hash.get(arguments[0]);
}
return ret;
}
});
Resources.find_image = function (file, root = "") {
return find_ext(file, Resources.images, root);
}.hashify();
Resources.find_sound = function (file, root = "") {
return find_ext(file, Resources.sounds, root);
}.hashify();
Resources.find_script = function (file, root = "") {
return find_ext(file, Resources.scripts, root);
}.hashify();
console.transcript = "";
console.say = function (msg) {
msg += "\n";
console.print(msg);
if (debug.termout) console.term_print(msg);
console.transcript += msg;
};
console.log = console.say;
@ -200,7 +258,9 @@ console.stackstr = function (skip = 0) {
};
console.stack = function (skip = 0) {
console.log(console.stackstr(skip + 1));
var stack = console.stackstr(skip+1);
console.log(stack);
return stack;
};
console.stdout_lvl = 1;
@ -223,37 +283,37 @@ globalThis.global = globalThis;
var use_cache = {};
globalThis.use = function use(file, env = {}, script) {
globalThis.use = function use(file) {
file = Resources.find_script(file);
profile.cache("USE", file);
if (use_cache[file]) {
var ret = use_cache[file].call(env);
return;
var ret = use_cache[file]();
profile.endcache(" [cached]");
return ret;
}
script ??= Resources.replstrs(file);
var script = Resources.replstrs(file);
script = `(function() { var self = this; ${script}; })`;
var fn = os.eval(file, script);
use_cache[file] = fn;
var ret = fn.call(env);
var ret = fn();
profile.endcache();
return ret;
}
function stripped_use (file, env = {}, script) {
function stripped_use (file, script) {
file = Resources.find_script(file);
if (use_cache[file]) {
var ret = use_cache[file].call(env);
return;
var ret = use_cache[file]();
return ret;
}
script ??= Resources.replstrs(file);
script = `(function() { var self = this; ${script}; })`;
var fn = os.eval(file, script);
var ret = fn.call(env);
var ret = fn();
profile.endcache();
return ret;
@ -264,7 +324,7 @@ function bare_use(file)
var script = io.slurp(file);
if (!script) return;
script = `(function() { var self = this; ${script}; })`;
Object.assign(globalThis, os.eval(file, script).call(globalThis));
Object.assign(globalThis, os.eval(file, script)());
}
globalThis.debug = {};

View file

@ -1,3 +1,4 @@
globalThis.entityreport = {};
var timer_update = function(dt)
@ -72,8 +73,8 @@ var entity = {
delay(fn, seconds) {
var timers = this.timers;
var stop = function() {
delete timers[guid];
var stop = function() {
timers.remove(stop);
execute = undefined;
stop = undefined;
rm?.();
@ -92,14 +93,15 @@ var entity = {
function update(dt) {
profile.frame("timer");
if (stop) { // TODO: This seems broken
stop.remain -= dt;
if (stop.remain <= 0) execute();
}
profile.endframe();
}
var rm = Register.update.register(update);
var guid = prosperon.guid();
timers[guid] = (stop);
timers.push(stop);
return stop;
},
@ -122,10 +124,8 @@ var entity = {
/* 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}`);
console.warn(`not reparenting ... ${this.master} is the same as ${parent}`);
return;
}
@ -140,22 +140,25 @@ var entity = {
},
remove_obj(obj) {
delete this.objects[obj.guid];
if (this.objects)
delete this.objects[obj.guid];
else
console.warn(`Object ${this.guid} has no objects file.`);
delete this[obj.name];
Object.unhide(this, obj.name);
},
spawn(text, config, callback) {
var ent = Object.create(entity);
ent.transform = os.make_transform();
ent.guid = prosperon.guid();
ent.components = {};
ent.objects = {};
ent.timers = {};
var ent = class_use(text, config, entity, function(ent) {
ent.transform = os.make_transform();
ent.guid = prosperon.guid();
ent.components = {};
ent.objects = {};
ent.timers = [];
ent.ur = {};
ent.urname = text;
});
/*
if (!text)
ent.ur = emptyur;
else if (text instanceof Object) {// assume it's an ur
@ -168,13 +171,7 @@ var entity = {
text = ent.ur.text;
config = [ent.ur.data, config];
}
if (typeof text === 'string')
use(text, ent);
else if (Array.isArray(text))
for (var path of text) use(path,ent);
profile.cache("ENTITY TIME", ent.ur.name);
var st = profile.now();
if (typeof config === 'string')
Object.merge(ent, json.decode(Resources.replstrs(config)));
else if (Array.isArray(config))
@ -186,9 +183,17 @@ var entity = {
else if (path instanceof Object)
Object.merge(ent,path);
};
if (typeof text === 'string') {
class_use(
use(text, ent);
}
else if (Array.isArray(text))
for (var path of text) use(path,ent);
profile.cache("ENTITY TIME", ent.ur.name);
*/
ent.reparent(this);
for (var [prop, p] of Object.entries(ent)) {
if (!p) continue;
if (typeof p !== 'object') continue;
@ -200,11 +205,13 @@ var entity = {
check_registers(ent);
if (ent.load instanceof Function) ent.load();
if (sim.playing())
if (ent.awake instanceof Function) ent.awake();
if (sim.playing()) {
ent._started = true;
if (ent.start instanceof Function) ent.start();
}
Object.hide(ent, 'ur', 'components', 'objects', 'timers', 'guid', 'master');
Object.hide(ent, 'ur', 'components', 'objects', 'timers', 'guid', 'master', 'guid');
ent._ed = {
selectable: true,
@ -295,6 +302,10 @@ var entity = {
return bb ? bb : bbox.fromcwh([0, 0], [0, 0]);
},
toJSON() {
return {guid:this.guid};
},
/* The unique components of this object. Its diff. */
json_obj(depth=0) {
@ -323,7 +334,8 @@ var entity = {
/* The object needed to store an object as an instance of a master */
instance_obj() {
var t = this.transform();
var t = os.make_transform();
t = this.transform;
t.ur = this.ur.name;
return t;
},
@ -345,7 +357,6 @@ dup(diff) {
kill() {
if (this.__kill) return;
this.__kill = true;
console.spam(`Killing entity of type ${this.ur}`);
this.timers.forEach(x => x());
delete this.timers;
@ -368,6 +379,8 @@ dup(diff) {
this.clear();
if (this.stop instanceof Function) this.stop();
if (typeof this.garbage === 'function') this.garbage();
if (typeof this.then === 'function') this.then();
game.tag_clear_guid(this.guid);

View file

@ -68,4 +68,14 @@ shape.circle.points = function(radius, n) {
return shape.arc(radius, 360, n);
};
shape.corners2points = function(ll, ur)
{
return [
ll,
ll.add([ur.x,0]),
ur,
ll.add([0,ur.y]),
];
}
return {shape};

View file

@ -1,242 +0,0 @@
/*
gui functions take screen space coordinates
*/
gui.scissor_win = function() { gui.scissor(0,0,window.size.x,window.y); }
gui.input_lmouse_pressed = function() {
if (gui.selected)
gui.selected.action();
};
gui.input_s_pressed = function() {
if (gui.selected?.down) {
gui.selected.selected = false;
gui.selected = gui.selected.down;
gui.selected.selected = true;
}
};
gui.input_w_pressed = function() {
if (gui.selected?.up) {
gui.selected.selected = false;
gui.selected = gui.selected.up;
gui.selected.selected = true;
}
};
gui.input_enter_pressed = function() {
if (gui.selected) {
gui.selected.action();
}
}
gui.controls = {};
gui.controls.toString = function() { return "gui controls"; };
gui.controls.update = function() { };
gui.controls.set_mum = function(mum)
{
mum.selected = true;
if (this.selected && this.selected !== mum)
this.selected.selected = false;
this.selected = mum;
}
gui.controls.check_bb = function(mum)
{
if (bbox.pointin(mum.bb, input.mouse.screenpos()))
gui.controls.set_mum(mum);
}
gui.controls.inputs = {};
gui.controls.inputs.fallthru = false;
gui.controls.inputs.mouse = {};
gui.controls.inputs.mouse.move = function(pos,dpos)
{
}
gui.controls.inputs.mouse.scroll = function(scroll)
{
}
gui.controls.check_submit = function() {
if (this.selected && this.selected.action)
this.selected.action(this.selected);
}
var Mum = {
padding:[0,0], /* Each element inset with this padding on all sides */
offset:[0,0],
font: "fonts/c64.ttf",
selectable: false,
selected: false,
font_size: 1,
text_align: "left", /* left, center, right */
scale: 1,
angle: 0,
anchor: [0,1],
hovered: {},
text_shadow: {
pos: [0,0],
color: Color.white,
},
text_outline: 1, /* outline in pixels */
color: Color.white,
margin: [0,0], /* Distance between elements for things like columns */
width: 0,
height: 0,
max_width: Infinity,
max_height: Infinity,
image_repeat: false,
image_repeat_offset: [0,0],
debug: false, /* set to true to draw debug boxes */
make(def) {
var n = Object.create(this);
Object.assign(n, def);
return n;
},
prestart() {
this.hovered.__proto__ = this;
},
start() {},
extend(def) {
var n = Object.create(this);
Object.assign(n, def);
var fn = function(def) {
var p = n.make(def);
p.prestart();
p.start();
return p;
};
fn._int = n;
return fn;
},
}
Mum.text = Mum.extend({
draw(cursor = [0,0], cnt = Mum) {
if (this.hide) return;
if (this.selectable) gui.controls.check_bb(this);
this.caret ??= -1;
/* if (!this.bb)
this.calc_bb(cursor);
else
this.update_bb(cursor);
*/
var params = this.selected ? this.hovered : this;
this.width = Math.min(params.max_width, cnt.max_width);
this.calc_bb(cursor);
this.height = this.wh.y;
var aa = [0,1].sub(params.anchor);
var pos = cursor.add(params.wh.scale(aa)).add(params.offset);
// gui.font_set(params.font);
render.text(params.str, pos, params.font_size, params.color, this.width, undefined, params.caret);
},
update_bb(cursor) {
this.bb = bbox.move(this.bb, cursor.sub(this.wh.scale(this.anchor)));
},
calc_bb(cursor) {
var bb = render.text_size(this.str, this.font_size, this.width);
this.wh = bbox.towh(bb);
var pos = cursor.add(this.wh.scale([0,1].sub(this.anchor))).add(this.offset);
this.bb = bbox.move(bb,pos.add([this.wh.x/2,0]));
},
start() {
this.calc_bb([0,0]);
},
});
Mum.button = Mum.text._int.extend({
selectable: true,
color: Color.blue,
hovered:{
color: Color.red
},
action() { console.warn("Button has no action."); },
});
Mum.window = Mum.extend({
start() {
this.wh = [this.width, this.height];
this.bb = bbox.fromcwh([0,0], this.wh);
},
draw(cursor = [0,0], cnt = Mum) {
var p = cursor.sub(this.wh.scale(this.anchor)).add(this.padding);
render.window(p,this.wh, this.color);
this.bb = bbox.blwh(p, this.wh);
this.max_width = this.width;
if (this.selectable) gui.controls.check_bb(this);
var pos = [this.bb.l, this.bb.t].add(this.padding);
this.items.forEach(function(item) {
if (item.hide) return;
item.draw(pos.slice(),this);
}, this);
bind.inst = render.flushtext();
render.spdraw(bind);
gui.scissor_win();
},
});
Mum.image = Mum.extend({
start() {
if (!this.path) {
console.warn("Mum image needs a path.");
this.draw = function(){};
return;
}
var tex_wh = texture.dimensions(this.path);
this.wh = tex_wh.slice();
if (this.width !== 0) this.wh.x = this.width;
if (this.height !== 0) this.wh.y = this.height;
this.wh = wh.scale(this.scale);
this.sendscale = [wh.x/tex_wh.x, wh.y/tex_wh.y];
},
draw(pos) {
this.calc_bb(pos);
gui_img(this.path, pos.sub(this.anchor.scale([this.width, this.height])), this.sendscale, this.angle, this.image_repeat, this.image_repeat_offset, this.color);
},
calc_bb(pos) {
this.bb = bbox.fromcwh(this.wh.scale([0.5,0.5]), wh);
this.bb = bbox.move(this.bb, pos.sub(this.wh.scale(this.anchor)));
}
});
Mum.column = Mum.extend({
draw(cursor = [0,0], cnt = Mum) {
if (this.hide) return;
cursor = cursor.add(this.offset);
this.max_width = cnt.width;
this.items.forEach(function(item) {
if (item.hide) return;
item.draw(cursor, this);
cursor.y -= item.height;
cursor.y -= this.padding.y;
}, this);
},
});
/*
Mum.debug_colors = {
bounds: Color.red.slice(),
margin: Color.blue.slice(),
padding: Color.green.slice()
};*/
//Object.values(Mum.debug_colors).forEach(function(v) { v.a = 100; });
//return { Mum };

View file

@ -32,7 +32,6 @@ var mod = {
released
rep
pressed
pressrep
down
*/
@ -40,8 +39,8 @@ function keycode(name) { return charCodeAt(name); }
function keyname_extd(key)
{
if (typeof key === 'string') return key;
if (!parseInt(key)) return key;
if (key > 289 && key < 302) {
var num = key-289;
return `f${num}`;
@ -83,7 +82,6 @@ prosperon.keydown = function(key, repeat)
mod.shift = 1;
else {
var emacs = modstr() + keyname_extd(key);
player[0].raw_input(emacs, "pressrep");
if (repeat)
player[0].raw_input(emacs, "rep");
else
@ -213,10 +211,26 @@ input.print_pawn_kbm = function(pawn) {
return str;
};
var joysticks = {};
joysticks["wasd"] = {
uy: "w",
dy: "s",
ux: "d",
dx: "a"
};
input.procdown = function()
{
for (var k in downkeys)
player[0].raw_input(keyname_extd(k), "down");
for (var i in joysticks) {
var joy = joysticks[i];
var x = joy.ux - joy.dx;
var y = joy.uy - joy.dy;
player[0].joy_input(i, joysticks[i]);
}
}
input.print_md_kbm = function(pawn) {
@ -273,6 +287,11 @@ input.tabcomplete = function(val, list) {
}
/* May be a human player; may be an AI player */
/*
'block' on a pawn's input blocks any input from reaching below for the
*/
var Player = {
players: [],
input(fn, ...args) {
@ -301,20 +320,32 @@ var Player = {
};
},
joy_input(name, joystick) {
for (var pawn of this.pawns.reversed()) {
if (!pawn.inputs) return;
if (!pawn.inputs.joystick) return;
if (!pawn.inputs.joystick[name]) return;
var x = 0;
if (input.keyboard.down(joystick.ux)) x++;
if (input.keyboard.down(joystick.dx)) x--;
var y = 0;
if (input.keyboard.down(joystick.uy)) y++;
if (input.keyboard.down(joystick.dy)) y--;
pawn.inputs.joystick[name](x,y);
}
},
raw_input(cmd, state, ...args) {
for (var pawn of this.pawns.reversed()) {
if (!pawn.inputs) {
console.error(`pawn no longer has inputs object.`);
continue;
}
if (typeof pawn.inputs.any === 'function') {
pawn.inputs.any(cmd);
if (!pawn.inputs.fallthru)
return;
}
var block = pawn.inputs.block;
if (!pawn.inputs[cmd]) {
if (pawn.inputs.block) return;
continue;
@ -339,17 +370,15 @@ var Player = {
fn = pawn.inputs[cmd];
}
if (typeof fn === 'function') {
if (typeof fn === 'function')
fn.call(pawn, ... args);
if (!pawn.inputs) continue; // TODO: OK? Checking if the call uncontrolled the pawn
pawn.inputs.post?.call(pawn);
}
switch (state) {
case 'released':
pawn.inputs.release_post?.call(pawn);
break;
}
if (!pawn.inputs)
if (block) return;
else continue;
if (state === 'released')
pawn.inputs.release_post?.call(pawn);
if (!pawn.inputs.fallthru) return;
if (pawn.inputs.block) return;

View file

@ -25,7 +25,7 @@ mum.base = {
inset: null,
anchor: [0,1], // where to draw the item from, relative to the cursor. [0,1] is from the top left corner. [1,0] is from the bottom right
background_image: null,
slice: null,
slice: null, // pass to slice an image as a 9 slice. see render.slice9 for its format
hover: {
color: Color.red,
},
@ -76,7 +76,7 @@ var pre = function(data)
if (data.pos) cursor = data.pos.slice();
data.drawpos = cursor.slice().add(data.offset);
if (data.opacity !== 1) {
if (data.opacity && data.opacity !== 1) {
data.color = data.color.slice();
data.color[3] = data.opacity;
}

4
scripts/nogame.js Normal file
View file

@ -0,0 +1,4 @@
this.hud = function()
{
mum.label("No game yet! Make game.js to get started!", {pos:game.size.scale(0.5), anchor:[0.5,0.5]});
}

View file

@ -9,17 +9,17 @@ emitter.color = Color.white;
emitter.draw = function()
{
var amt = Object.values(this.particles).length;
if (amt === 0) return;
var pars = Object.values(this.particles);
if (pars.length === 0) return;
render.use_shader(this.shader);
render.use_mat(this);
render.make_particle_ssbo(Object.values(this.particles), this.ssbo);
render.draw(this.shape, this.ssbo, amt);
render.make_particle_ssbo(pars, this.ssbo);
render.draw(this.shape, this.ssbo, pars.length);
}
var std_spawn = function(par)
emitter.kill = function()
{
emitters.remove(this);
}
var std_step = function(p)
@ -35,13 +35,7 @@ var std_step = function(p)
p.transform.scale = [this.scale,this.scale,this.scale];
}
var std_die = function(par)
{
}
emitter.spawn_hook = std_spawn;
emitter.step_hook = std_step;
emitter.die_hook = std_die;
emitter.spawn = function(t)
{
@ -53,7 +47,8 @@ emitter.spawn = function(t)
par.transform.scale = [this.scale,this.scale,this.scale];
this.particles[par.id] = par;
par.time = 0;
this.spawn_hook(par);
this.spawn_hook?.(par);
par.life = this.life;
return;
}
@ -90,9 +85,9 @@ emitter.step = function(dt)
for (var p of Object.values(this.particles)) {
p.time += dt;
this.step_hook?.(p);
if (p.time >= p.life) {
this.die_hook(p);
if (this.kill_hook?.(p) || p.time >= p.life) {
this.die_hook?.(p);
this.dead.push(this.particles[p.id]);
delete this.particles[p.id];
}
@ -103,6 +98,8 @@ emitter.burst = function(count, t) {
for (var i = 0; i < count; i++) this.spawn(t);
}
var emitters = [];
var make_emitter = function()
{
var e = Object.create(emitter);
@ -110,7 +107,19 @@ var make_emitter = function()
e.shape = shape.centered_quad;
e.shader = "shaders/baseparticle.cg";
e.dead = [];
emitters.push(e);
return e;
}
return {make_emitter};
function update_emitters(dt)
{
for (var e of emitters)
e.step(dt);
}
function draw_emitters()
{
for (var e of emitters) e.draw();
}
return {make_emitter, update_emitters, draw_emitters};

View file

@ -16,6 +16,7 @@ function calc_cpu(fn, times, diff=0)
function empty_fn() {}
profile.cpu = function profile_cpu(fn, times = 1, q = "unnamed") {
var retgather = gathering_cpu;
profile.gather_stop();
var empty = calc_cpu(empty_fn, 100000);
var mean = Math.mean(empty);
@ -26,7 +27,10 @@ profile.cpu = function profile_cpu(fn, times = 1, q = "unnamed") {
var totalt = profile.best_t(elapsed);
say(`profile [${q}]: ${avgt} ± ${profile.best_t(Math.ci(series))} [${totalt} for ${times} loops]`);
start_prof_gather();
say(`result of function is ${fn()}`);
if (retgather)
profile.start_prof_gather();
}
profile.ms = function(t) { return t/1000000; }
@ -81,12 +85,21 @@ profile.start_cpu_gather = function()
});
}
function push_time(arr, ob, max)
{
arr.push({
time:profile.now(),
ob
});
}
profile.cpu_frames = [];
profile.cpu_frame = function()
{
if (gathering_cpu) return;
profile.gather(Math.random_range(300,600), function() {
console.stack(2);
push_time(profile.cpu_frames, console.stack(2));
profile.gather_stop();
});
}
@ -144,6 +157,7 @@ profile.best_t = function (t) {
profile.report = function (start, msg = "[undefined report]") { console.info(`${msg} in ${profile.best_t(profile.now() - start)}`); };
var frame_avg = false;
profile.frame_avg_t = 72000;
profile.start_frame_avg = function()
{
@ -170,31 +184,38 @@ profile.toggle_frame_avg = function()
else profile.start_frame_avg();
}
var profile_frames = {};
var profile_frame_ts = [];
var profile_cframe = profile_frames;
var profile_framer = {
series: [],
avg: {},
frame: 72000,
};
var profile_cframe = undefined;
var pframe = 0;
var profile_stack = [];
profile.frame = function profile_frame(title)
{
if (!frame_avg) return;
profile_frame_ts.push(profile_cframe);
if (!profile_cframe) {
profile_cframe = {};
profile_framer.series.push({
time:profile.now(),
data:profile_cframe
});
} else
profile_stack.push(profile_cframe);
profile_cframe[title] ??= {};
profile_cframe = profile_cframe[title];
profile_cframe._times ??= [];
profile_cframe._times[pframe] ??= 0;
profile_cframe._times[pframe] = profile.now() - profile_cframe._times[pframe];
profile_cframe.time = profile.now();
}
profile.endframe = function profile_endframe()
{
if (!frame_avg) return;
if (profile_cframe === profile_frames) return;
profile_cframe._times[pframe] = profile.now() - profile_cframe._times[pframe];
profile_cframe.time = profile.now() - profile_cframe.time;
profile_cframe = profile_frame_ts.pop();
if (profile_cframe === profile_frames) pframe++;
}
var print_frame = function(frame, indent, title)
@ -218,13 +239,28 @@ profile.print_frame_avg = function()
say("\n");
}
var cache_reporting = false;
var report_cache = {};
var cachest = 0;
var cachegroup;
var cachetitle;
profile.imgui = function()
{
}
profile.cache_reporting = function() { return cache_reporting; }
profile.cache_toggle = function() { cache_reporting = !cache_reporting; }
profile.cache_dump = function() {
report_cache = {};
}
profile.cache = function profile_cache(group, title)
{
if (!cache_reporting) return;
cachest = profile.now();
cachegroup = group;
cachetitle = title;
@ -277,3 +313,42 @@ function printreport(cache, name) {
return report;
};
profile.best_mem = function(bytes)
{
var sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB'];
if (bytes == 0) return '0 Bytes';
var i = parseInt(Math.floor(Math.log(bytes) / Math.log(1024)));
return (bytes / Math.pow(1024, i)).toPrecision(3) + ' ' + sizes[i];
}
profile.print_mem = function()
{
var mem = os.mem();
say('total memory used: ' + profile.best_mem(mem.memory_used_size));
say('total malloced: ' + profile.best_mem(mem.malloc_size));
delete mem.memory_used_size;
delete mem.malloc_size;
for (var i in mem) {
if (i. includes("size"))
say(" " + i + " :: " + profile.best_mem(mem[i]));
}
}
profile.last_mem = undefined;
profile.mems = [];
profile.gcs = [];
profile.print_gc = function()
{
var gc = os.check_gc();
if (!gc) return;
profile.gcs.push(gc);
profile.mems.push(os.mem());
say("GC Hit");
say (`time: ${profile.best_t(gc.time)}`);
say(`new threshold: ${profile.best_mem(profile.mems.last().gc_threshold)}`);
say(`memory checked: ${profile.best_mem(profile.gcs.last().mem)}`);
say(`memory freed: ${profile.best_mem(profile.gcs.last().startmem - profile.gcs.last().mem)}`);
}
return {profile};

View file

@ -4,6 +4,7 @@ global.check_registers = function (obj) {
for (var reg in Register.registries) {
if (typeof obj[reg] === 'function') {
var fn = obj[reg].bind(obj);
fn.layer = obj[reg].layer;
var name = obj.ur ? obj.ur.name : obj.toString();
obj.timers.push(Register.registries[reg].register(fn, name));
}
@ -19,6 +20,7 @@ global.check_registers = function (obj) {
global.obscure("global");
global.mixin("scripts/render");
global.mixin("scripts/debug");
global.mixin("scripts/repl");
var frame_t = profile.secs(profile.now());
@ -27,6 +29,12 @@ sim.mode = "play";
sim.play = function () {
this.mode = "play";
os.reindex_static();
game.all_objects(o => {
if (!o._started) {
o._started = true;
o.start?.();
}
});
};
sim.playing = function () {
return this.mode === "play";
@ -53,12 +61,37 @@ game.engine_start = function (s) {
function () {
global.mixin("scripts/sound.js");
world_start();
window.set_icon(os.make_texture("icons/moon.gif"));
window.set_icon(game.texture("moon"));
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");
prosperon.camera = prosperon.make_camera();
var camera = prosperon.camera;
camera.transform.pos = [0,0,-100];
camera.mode = "keep";
camera.break = "fit";
camera.size = game.size;
gamestate.camera = camera;
prosperon.hudcam = prosperon.make_camera();
var hudcam = prosperon.hudcam;
hudcam.near = 0;
hudcam.size = camera.size;
hudcam.mode = "keep";
hudcam.break = "fit";
prosperon.appcam = prosperon.make_camera();
var appcam = prosperon.appcam;
appcam.near = 0;
appcam.size = window.size;
appcam.transform.pos = [window.size.x,window.size.y,-100];
prosperon.screencolor = render.screencolor();
globalThis.imgui = render.imgui_init();
s();
shape.quad = {
@ -86,28 +119,6 @@ game.engine_start = function (s) {
};
render.init();
prosperon.camera = prosperon.make_camera();
var camera = prosperon.camera;
camera.transform.pos = [0,0,-100];
camera.mode = "keep";
camera.break = "fit";
camera.size = game.size;
gamestate.camera = camera;
prosperon.hudcam = prosperon.make_camera();
var hudcam = prosperon.hudcam;
hudcam.near = 0;
hudcam.size = camera.size;
hudcam.mode = "keep";
hudcam.break = "fit";
prosperon.appcam = prosperon.make_camera();
var appcam = prosperon.appcam;
appcam.near = 0;
appcam.size = window.size;
appcam.transform.pos = [window.size.x,window.size.y,-100];
prosperon.screencolor = render.screencolor();
},
prosperon.process,
window.size.x,
@ -116,7 +127,6 @@ game.engine_start = function (s) {
};
game.startengine = 0;
var frames = [];
prosperon.release_mode = function()
{
@ -126,13 +136,6 @@ prosperon.release_mode = function()
}
prosperon.debug = true;
globalThis.fps = function () {
return 0;
// var sum = 0;
// for (var i = 0; i < frames.length; i++) sum += frames[i];
// return frames.length / sum;
};
game.timescale = 1;
var eachobj = function (obj, fn) {
@ -176,17 +179,41 @@ game.doc.pause = "Pause game simulation.";
game.doc.play = "Resume or start game simulation.";
game.doc.camera = "Current camera.";
game.texture = function (path, force = false) {
if (force && game.texture.cache[path]) return game.texture.cache[path];
game.tex_hotreload = function()
{
for (var path in game.texture.cache) {
if (io.mod(path) > game.texture.time_cache[path]) {
say(`HOT SWAPPING IMAGE ${path}`);
var tex = game.texture.cache[path];
game.texture.time_cache[path] = io.mod(path);
os.texture_swap(path, game.texture.cache[path]);
for (var sprite of Object.values(allsprites)) {
if (sprite.texture == tex) {
say('syncing a sprite ...');
sprite.tex_sync();
}
}
}
}
}
game.texture = function (path) {
if (!path) return game.texture("icons/no_text.gif");
path = Resources.find_image(path);
if (!io.exists(path)) {
console.warn(`Missing texture: ${path}`);
console.error(`Missing texture: ${path}`);
game.texture.cache[path] = game.texture("icons/no_tex.gif");
} else game.texture.cache[path] ??= os.make_texture(path);
game.texture.time_cache[path] = io.mod(path);
return game.texture.cache[path];
}
if (game.texture.cache[path]) return game.texture.cache[path];
game.texture.cache[path] = os.make_texture(path);
game.texture.time_cache[path] = io.mod(path);
return game.texture.cache[path];
};
game.texture.cache = {};
game.texture.time_cache = {};
prosperon.semver = {};
prosperon.semver.valid = function (v, range) {
@ -226,8 +253,6 @@ prosperon.semver.cmp = function (v1, v2) {
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.
@ -252,6 +277,7 @@ prosperon.quit = function () {
profile.print_cache_report();
profile.stop_frame_avg()
profile.stop_cpu_instr();
prosperon.quit_hook?.();
};
window.size = [640, 480];
@ -264,17 +290,24 @@ window.doc = {};
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.__proto__.toJSON = function()
{
return {
size: this.size,
fullscreen: this.fullscreen,
title: this.title
};
}
global.mixin("scripts/input");
global.mixin("scripts/std");
global.mixin("scripts/diff");
global.mixin("scripts/color");
global.mixin("scripts/gui");
global.mixin("scripts/tween");
global.mixin("scripts/ai");
global.mixin("scripts/particle");
global.mixin("scripts/physics");
global.mixin("scripts/geometry");
global.mixin("scripts/geometry")
/*
Factory for creating registries. Register one with 'X.register',
@ -283,9 +316,9 @@ which returns a function that, when invoked, cancels the registry.
var Register = {
registries: [],
add_cb(name, e_event = false) {
add_cb(name, e_event = false, flush = undefined) {
var n = {};
var fns = {};
var fns = [];
n.register = function (fn, oname) {
if (!(fn instanceof Function)) return;
@ -298,16 +331,34 @@ var Register = {
fn(...args);
profile.endcache();
}
fns.push(dofn);
dofn.layer = fn.layer;
dofn.layer ??= 0;
fns.sort((a,b) => a.layer > b.layer);
fns[guid] = dofn;
return function () {
delete fns[guid];
fns.remove(dofn);
};
};
prosperon[name] = function (...args) {
fns.forEach(x => x(...args));
};
if (!flush) {
prosperon[name] = function(...args) {
fns.forEach(fn => fn(...args));
}
}
else
prosperon[name] = function(...args) {
var layer = undefined;
for (var fn of fns) {
if (layer !== fn.layer) {
flush();
layer = fn.layer;
}
fn();
}
}
prosperon[name].fns = fns;
n.clear = function () {
@ -325,10 +376,7 @@ Register.add_cb("appupdate", true);
Register.add_cb("update", true).doc = "Called once per frame.";
Register.add_cb("physupdate", true);
Register.add_cb("gui", true);
Register.add_cb("hud", true);
Register.add_cb("draw_dbg", true);
Register.add_cb("gui_dbg", true);
Register.add_cb("hud_dbg", true);
Register.add_cb("hud", true, render.flush);
Register.add_cb("draw", true);
Register.add_cb("imgui", true);
@ -358,7 +406,9 @@ var Event = {
global.mixin("scripts/spline");
global.mixin("scripts/components");
global.mixin("scripts/actor");
global.mixin("scripts/entity");
function world_start() {

View file

@ -44,6 +44,8 @@ function set_model(t)
render.setunim4(0, cur.shader.vs.unimap.model.slot, t);
}
render.set_model = set_model;
var shaderlang = {
macos: "metal_macos",
windows: "hlsl5",
@ -56,7 +58,7 @@ var attr_map = {
a_pos: 0,
a_uv: 1,
a_norm: 2,
a_bone: 3,
a_joint: 3,
a_weight: 4,
a_color: 5,
a_tan: 6,
@ -154,49 +156,35 @@ render.set_camera = function(cam)
}
var shader_cache = {};
var shader_times = {};
function strip_shader_inputs(shader)
{
for (var a of shader.vs.inputs)
a.name = a.name.slice(2);
}
function make_shader(shader)
render.hotreload = function()
{
if (shader_cache[shader]) return shader_cache[shader];
var file = shader;
shader = io.slurp(shader);
if (!shader) {
console.info(`not found! slurping shaders/${file}`);
shader = io.slurp(`shaders/${file}`);
}
var writejson = `.prosperon/${file.name()}.shader.json`;
profile.cache("shader", file);
breakme: if (io.exists(writejson)) {
var data = json.decode(io.slurp(writejson));
var filemod = io.mod(writejson);
if (!data.files) break breakme;
for (var i of data.files) {
if (io.mod(i) > filemod) {
break breakme;
}
}
profile.endcache(" [cached]");
var shaderobj = json.decode(io.slurp(writejson));
var obj = shaderobj[os.sys()];
strip_shader_inputs(obj);
for (var i in shader_times) {
if (io.mod(i) <= shader_times[i]) continue;
say(`HOT RELOADING SHADER ${i}`);
shader_times[i] = io.mod(i);
var obj = create_shader_obj(i);
obj = obj[os.sys()];
obj.pipe = render.pipeline(obj);
shader_cache[shader] = obj;
return obj;
var old = shader_cache[i];
Object.assign(shader_cache[i], obj);
cur.bind = undefined;
cur.mesh = undefined;
}
var out = `.prosperon/${file.name()}.shader`;
}
function create_shader_obj(file)
{
var files = [file];
var out = ".prosperon/tmp.shader";
var shader = io.slurp(file);
var incs = shader.match(/#include <.*>/g);
if (incs)
@ -223,7 +211,6 @@ function make_shader(shader)
shader = shader.replace(/uniform\s+(\w+)\s+(\w+);/g, "uniform _$2 { $1 $2; };");
shader = shader.replace(/(texture2D|sampler) /g, "uniform $1 ");
// shader = shader.replace(/uniform texture2D ?(.*);/g, "uniform _$1_size { vec2 $1_size; };\nuniform texture2D $1;");
io.slurpwrite(out, shader);
@ -253,11 +240,11 @@ function make_shader(shader)
}
add_code(obj.vs);
if (!obj.fs)
if (obj.vs.fs) {
if (!obj.fs && obj.vs.fs) {
obj.fs = obj.vs.fs;
delete obj.vs.fs;
}
}
add_code(obj.fs);
obj.blend = blend;
@ -296,19 +283,58 @@ function make_shader(shader)
obj.name = file;
strip_shader_inputs(obj);
compiled[platform] = obj;
}
compiled.files = files;
compiled.source = shader;
return compiled;
}
function make_shader(shader)
{
if (shader_cache[shader]) return shader_cache[shader];
var file = shader;
shader = io.slurp(file);
if (!shader) {
console.info(`not found! slurping shaders/${file}`);
shader = io.slurp(`shaders/${file}`);
}
var writejson = `.prosperon/${file.name()}.shader.json`;
profile.cache("shader", file);
breakme: if (io.exists(writejson)) {
var data = json.decode(io.slurp(writejson));
var filemod = io.mod(writejson);
if (!data.files) break breakme;
for (var i of data.files) {
if (io.mod(i) > filemod) {
break breakme;
}
}
profile.endcache(" [cached]");
var shaderobj = json.decode(io.slurp(writejson));
var obj = shaderobj[os.sys()];
obj.pipe = render.pipeline(obj);
shader_cache[file] = obj;
shader_times[file] = io.mod(file);
return obj;
}
var compiled = create_shader_obj(file);
io.slurpwrite(writejson, json.encode(compiled));
profile.endcache();
var obj = compiled[os.sys()];
strip_shader_inputs(obj);
obj.pipe = render.pipeline(obj);
shader_cache[shader] = obj;
shader_cache[file] = obj;
shader_times[file] = io.mod(file);
return obj;
}
@ -335,6 +361,12 @@ function shader_apply_material(shader, material = {}, old = {})
if (material[p] === old[p]) continue;
assert(p in material, `shader ${shader.name} has no uniform for ${p}`);
var s = shader.vs.unimap[p];
if (p === 'bones') {
render.setunibones(0, s.slot, material[p]);
continue;
}
shader_unisize[s.size](0, s.slot, material[p]);
}
@ -358,13 +390,6 @@ function shader_apply_material(shader, material = {}, old = {})
function sg_bind(mesh, ssbo)
{
if (cur.mesh === mesh && cur.bind) {
cur.bind.inst = 1;
cur.bind.images = cur.images;
render.setbind(cur.bind);
return cur.bind;
}
cur.mesh = mesh;
var bind = {};
@ -372,7 +397,7 @@ function sg_bind(mesh, ssbo)
if (cur.shader.vs.inputs)
for (var a of cur.shader.vs.inputs) {
if (!(a.name in mesh)) {
console.error(`cannot draw shader ${cur.shader.name}; there is no attrib ${a.name} in the given mesh.`);
console.error(`cannot draw shader ${cur.shader.name}; there is no attrib ${a.name} in the given mesh. ${json.encode(mesh)}`);
return undefined;
} else
bind.attrib.push(mesh[a.name]);
@ -455,12 +480,9 @@ render.init = function() {
polyshader = make_shader("shaders/poly.cg");
parshader = make_shader("shaders/baseparticle.cg");
polyssboshader = make_shader("shaders/poly_ssbo.cg");
textssbo = render.make_textssbo();
poly_ssbo = render.make_textssbo();
sprite_ssbo = render.make_textssbo();
globalThis.imgui = render.imgui_init();
render.textshader = textshader;
os.make_circle2d().draw = function() {
@ -493,8 +515,52 @@ render.init = function() {
}
}
render.draw_sprites = true;
render.draw_particles = true;
render.draw_hud = true;
render.draw_gui = true;
render.draw_gizmos = true;
render.sprites = function render_sprites(gridsize = 1)
{
// When y sorting, draw layer is firstly important followed by the gameobject position
var buckets;
if (render.y_sort) {
profile.frame("y bucketing");
var sps = Object.values(allsprites);
buckets = {};
for (var sprite of sps) {
var layer = (sprite.gameobject.drawlayer*10000)-sprite.gameobject.pos.y;
buckets[layer] ??= {};
if (buckets[layer][sprite.path])
buckets[layer][sprite.path].push(sprite);
else
buckets[layer][sprite.path] = [sprite];
}
profile.endframe();
profile.frame("y sorting");
buckets = Object.entries(buckets).sort((a,b) => {
var na = Number(a[0]);
var nb = Number(b[0]);
if (na < nb) return -1;
return 1;
});
profile.endframe();
} else {
profile.frame("sorting");
var sprite_buckets = component.sprite_buckets();
var buckets = Object.entries(sprite_buckets).sort((a,b) => {
var na = Number(a[0]);
var nb = Number(b[0]);
if (na < nb) return -1;
return 1;
});
profile.endframe();
}
/*
profile.frame("bucketing");
var sps = Object.values(allsprites);
@ -512,19 +578,8 @@ render.sprites = function render_sprites(gridsize = 1)
}
profile.endframe();
*/
*/
profile.frame("sorting");
var sprite_buckets = component.sprite_buckets();
var buckets = Object.entries(sprite_buckets).sort((a,b) => {
var na = Number(a[0]);
var nb = Number(b[0]);
if (na < nb) return -1;
return 1;
});
profile.endframe();
profile.frame("drawing");
render.use_shader(spritessboshader);
for (var layer of buckets) {
@ -535,22 +590,31 @@ render.sprites = function render_sprites(gridsize = 1)
render.use_mat(ss);
render.make_sprite_ssbo(sparray, sprite_ssbo);
render.draw(shape.quad, sprite_ssbo, sparray.length);
if (debug.sprite_nums) render.text(ss.diffuse.getid(), ss.transform.pos);
}
}
profile.endframe();
}
render.circle = function render_circle(pos, radius, color) {
flush();
render.circle = function render_circle(pos, radius, color, inner_radius = 1) {
check_flush(undefined);
if (inner_radius >= 1)
inner_radius = inner_radius/radius;
else if (inner_radius < 0)
inner_radius = 1.0;
var mat = {
radius: radius,
inner_r: inner_radius,
coord: pos,
shade: color
shade: color,
};
render.use_shader(circleshader);
render.use_mat(mat);
render.draw(shape.quad);
}
render.circle.doc = "Draw a circle at pos, with a given radius and color. If inner_radius is between 0 and 1, it acts as a percentage of radius. If it is above 1, is acts as a unit (usually a pixel).";
render.poly = function render_poly(points, color, transform) {
var buffer = render.poly_prim(points);
@ -578,6 +642,8 @@ function check_flush(flush_fn)
}
}
render.flush = check_flush;
var poly_cache = [];
var poly_idx = 0;
var poly_ssbo;
@ -605,17 +671,21 @@ function flush_poly()
render.use_shader(polyssboshader);
render.use_mat({});
render.make_particle_ssbo(poly_cache.slice(0,poly_idx), poly_ssbo);
render.draw(shape.centered_quad, poly_ssbo, poly_cache.length);
render.draw(shape.centered_quad, poly_ssbo, poly_idx);
poly_idx = 0;
}
render.line = function render_line(points, color = Color.white, thickness = 1) {
var poly = poly_e();
var dist = vector.distance(points[0],points[1]);
poly.transform.move(vector.midpoint(points[0],points[1]));
poly.transform.rotate([0,0,-1], vector.angle([points[1].x-points[0].x, points[1].y-points[0].y]));
poly.transform.scale = [dist, thickness, 1];
poly.color = color;
for (var i = 0; i < points.length-1; i++) {
var a = points[i];
var b = points[i+1];
var poly = poly_e();
var dist = vector.distance(a,b);
poly.transform.move(vector.midpoint(a,b));
poly.transform.rotate([0,0,-1], vector.angle([b.x-a.x, b.y-a.y]));
poly.transform.scale = [dist, thickness, 1];
poly.color = color;
}
check_flush(flush_poly);
}
@ -696,7 +766,7 @@ render.text_bb = function(str, size = 1, wrap = -1, pos = [0,0])
render.text = function(str, pos, size = 1, color = Color.white, wrap = -1, anchor = [0,1], cursor = -1) {
var bb = render.text_bb(str, size, wrap, pos);
gui.text(str, pos, size, color, wrap, cursor);
gui.text(str, pos, size, color, wrap, cursor); // this puts text into buffer
check_flush(render.flush_text);
return bb;
@ -711,6 +781,12 @@ render.text = function(str, pos, size = 1, color = Color.white, wrap = -1, ancho
};
render.image = function(tex, pos, scale = [tex.width, tex.height], rotation = 0, color = Color.white) {
if (typeof tex === 'string') {
tex = game.texture(tex);
scale.x ??= tex.width;
scale.y ??= tex.height;
}
if (!tex) return;
flush();
var t = os.make_transform();
t.pos = pos;
@ -759,29 +835,31 @@ render.slice9 = function(tex, pos, bb, scale = [tex.width,tex.height], color = C
render.draw(shape.quad);
}
render.emitter = function(emit)
function endframe()
{
var amt = Object.values(emit.particles).length;
if (amt === 0) return;
render.use_shader(parshader);
render.use_mat(emit);
var ts = [];
for (var p of Object.values(emit.particles)) ts.push([p.transform,p.color]);
render.make_particle_ssbo(ts, emit.ssbo);
render.draw(shape.quad, emit.ssbo, amt);
tdraw = 0;
}
var textssbo;
var textssbos = [];
var tdraw = 0;
render.flush_text = function()
{
if (!render.textshader) return;
var amt = render.flushtext(textssbo);
tdraw++;
if (textssbos.length < tdraw)
textssbos.push(render.make_textssbo());
var textssbo = textssbos[tdraw-1];
var amt = render.flushtext(textssbo); // load from buffer into ssbo
if (amt === 0) {
tdraw--;
return;
}
render.use_shader(render.textshader);
render.use_mat({text:render.font.texture});
if (amt === 0) return;
render.draw(shape.quad, textssbo, amt);
}
@ -799,21 +877,20 @@ render.set_font = function(path, size) {
}
render.doc = "Draw shapes in screen space.";
//render.circle.doc = "Draw a circle at pos, with a given radius and color.";
render.cross.doc = "Draw a cross centered at pos, with arm length size.";
render.arrow.doc = "Draw an arrow from start to end, with wings of length wingspan at angle wingangle.";
render.rectangle.doc = "Draw a rectangle, with its corners at lowerleft and upperright.";
render.draw = function render_draw(mesh, ssbo, inst = 1)
render.draw = function render_draw(mesh, ssbo, inst = 1, e_start = 0)
{
sg_bind(mesh, ssbo);
profile.frame("gpu");
render.spdraw(cur.bind.count, inst);
render.spdraw(e_start, cur.bind.count, inst);
profile.endframe();
}
// Returns an array in the form of [left, bottom, right, top] in pixels of the camera to render to
// Camera viewport is [left,bottom,right,top] in relative values
// Camera viewport is [left,bottom,width,height] in relative values
function camviewport()
{
var aspect = (this.viewport[2]-this.viewport[0])/(this.viewport[3]-this.viewport[1])*window.size.x/window.size.y;
@ -861,21 +938,43 @@ function camscreen2world(pos)
return view;
}
// three coordinatse
// world coordinates, the "actual" view relative to the game's universe
// camera coordinates, normalized from 0 to 1 inside of a camera's viewport, bottom left is 0,0, top right is 1,1
// screen coordinates, pixels, 0,0 at the bottom left of the window and [w,h] at the top right of the screen
camscreen2world.doc = "Convert a view position for a camera to world."
// return camera coordinates given a screen position
function screen2cam(pos)
{
var viewport = this.view();
var width = viewport[2]-viewport[0];
var height = viewport[3]-viewport[1];
var left = pos.x-viewport[0];
var bottom = pos.y-viewport[1];
var p = [left/width, bottom/height];
return p;
var width = viewport[2];
var height = viewport[3];
var viewpos = pos.sub([viewport[0],viewport[1]]);
return viewpos.div([width,height]);
}
function camextents()
{
var half = this.size;//.scale(0.5);
return {
l: this.pos.x-half.x,
r: this.pos.x+half.x,
t:this.pos.y+half.y,
b:this.pos.y-half.y
};
}
screen2cam.doc = "Convert a screen space position in pixels to a normalized viewport position in a camera."
prosperon.gizmos = function()
{
game.all_objects(o => {
if (o.gizmo) render.image(game.texture(o.gizmo), o.pos);
});
}
prosperon.make_camera = function()
{
var cam = world.spawn();
@ -888,7 +987,7 @@ prosperon.make_camera = function()
cam.mode = "stretch";
cam.screen2world = camscreen2world;
cam.screen2cam = screen2cam;
cam.extents = camextents;
cam.mousepos = function() { return this.screen2world(input.mouse.screenpos()); }
cam.view = camviewport;
cam.offscreen = false;
@ -897,12 +996,83 @@ prosperon.make_camera = function()
var screencolor;
globalThis.imtoggle = function(name, obj, field) { obj[field] = imgui.checkbox(name, obj[field]); }
var replstr = "";
var imdebug = function()
{
imtoggle("Physics", debug, 'draw_phys');
imtoggle("Bouning boxes", debug, 'draw_bb');
imtoggle("Gizmos", debug, 'draw_gizmos');
imtoggle("Names", debug, 'draw_names');
imtoggle("Sprite nums", debug, 'sprite_nums');
imtoggle("Debug overlay", debug, 'show');
imtoggle("Show ur names", debug, 'urnames');
}
var imgui_fn = function()
{
render.imgui_new(window.size.x, window.size.y, 0.01);
if (debug.console) debug.console = imgui.window("console", _ => { imgui.text(console.transcript); replstr = imgui.textinput(undefined, replstr); imgui.button("submit", _ => { eval(replstr); replstr = ""; }); });
imgui.mainmenubar(_=>{
imgui.menu("File", _ => {
imgui.menu("Game settings", _ => {
window.title = imgui.textinput("Title", window.title);
window.icon = imgui.textinput("Icon", window.icon);
imgui.button("Refresh window", _ => {
window.set_icon(game.texture(window.icon));
});
});
imgui.button("quit", os.quit);
});
imgui.menu("Debug", imdebug);
imgui.menu("View", _ => {
imtoggle("Profiler", debug, 'showprofiler');
imtoggle("Terminal out", debug, 'termout');
imtoggle("Meta [f7]", debug, 'meta');
imtoggle("Cheats [f8]", debug, 'cheat');
imtoggle("Console [f9]", debug, 'console');
});
imgui.sokol_gfx();
imgui.menu("Graphics", _ => {
imtoggle("Draw sprites", render, 'draw_sprites');
imtoggle("Draw particles", render, 'draw_particles');
imtoggle("Draw HUD", render, 'draw_hud');
imtoggle("Draw GUI", render, 'draw_gui');
imtoggle("Draw gizmos", render, 'draw_gizmos');
imgui.menu("Window", _ => {
window.fullscreen = imgui.checkbox("fullscreen", window.fullscreen);
// window.vsync = imgui.checkbox("vsync", window.vsync);
imgui.menu("MSAA", _ => {
for (var msaa of gamestate.msaa)
imgui.button(msaa + "x", _ => window.sample_count = msaa);
});
imgui.menu("Resolution", _ => {
for (var res of gamestate.resolutions)
imgui.button(res, _ => window.resolution = res);
});
});
});
prosperon.menu_hook?.();
});
prosperon.imgui();
render.imgui_end();
}
prosperon.render = function()
{
profile.frame("world");
render.set_camera(prosperon.camera);
profile.frame("sprites");
render.sprites();
if (render.draw_sprites) render.sprites();
if (render.draw_particles) draw_emitters();
profile.endframe();
profile.frame("draws");
prosperon.draw();
@ -913,17 +1083,23 @@ prosperon.render = function()
profile.endframe();
profile.frame("hud");
if (render.draw_hud) prosperon.hud();
render.flush_text();
prosperon.hud();
render.set_camera(prosperon.camera);
if (render.draw_gizmos && prosperon.gizmos) prosperon.gizmos();
render.flush_text();
render.end_pass();
profile.endframe();
profile.frame("post process");
profile.endframe();
profile.endframe();
/* draw the image of the game world first */
render.glue_pass();
profile.frame("frame");
profile.frame("render");
profile.frame("post process");
render.viewport(...prosperon.camera.view());
render.use_shader(render.postshader);
render.use_mat({diffuse:prosperon.screencolor});
@ -944,24 +1120,26 @@ prosperon.render = function()
// Call gui functions
mum.style = mum.dbg_style;
prosperon.gui();
if (render.draw_gui) prosperon.gui();
if (mum.drawinput) mum.drawinput();
prosperon.gui_dbg();
render.flush_text();
mum.style = mum.base;
check_flush();
profile.endframe();
profile.frame("imgui");
render.imgui_new(window.size.x, window.size.y, 0.01);
prosperon.imgui();
render.imgui_end();
if (debug.show)
imgui_fn();
profile.endframe();
render.end_pass();
render.commit();
endframe();
}
prosperon.process = function process() {
@ -969,6 +1147,19 @@ prosperon.process = function process() {
var dt = profile.secs(profile.now()) - frame_t;
frame_t = profile.secs(profile.now());
profile.frame("hotreload");
actor.hotreload();
render.hotreload();
game.tex_hotreload();
repl.hotreload();
profile.endframe();
/* debugging: check for gc */
profile.print_gc();
var cycles = os.check_cycles();
if (cycles) say(cycles);
profile.frame("app update");
prosperon.appupdate(dt);
profile.endframe();
@ -980,6 +1171,7 @@ prosperon.process = function process() {
if (sim.mode === "play" || sim.mode === "step") {
profile.frame("update");
prosperon.update(dt * game.timescale);
update_emitters(dt * game.timescale);
profile.endframe();
if (sim.mode === "step") sim.pause();
@ -1002,6 +1194,4 @@ prosperon.process = function process() {
profile.endframe();
}
return {render};

15
scripts/repl.js Normal file
View file

@ -0,0 +1,15 @@
var repl = {};
var file = "repl.jj";
var last = 0;
repl.hotreload = function() {
if (io.mod(file) > last) {
say("REPL:::");
last = io.mod(file);
var script = io.slurp(file);
eval(script);
}
}
return {repl:repl};

View file

@ -22,9 +22,18 @@ audio.bus.master = dspsound.master();
audio.dsp = {};
audio.dsp = dspsound;
audio.cry = function(file)
audio.dsp.mix().__proto__.imgui = function()
{
var player = audio.play(file);
imgui.pushid(this.memid());
this.volume = imgui.slider("Volume", this.volume);
this.off = imgui.checkbox("Mute", this.off);
imgui.popid();
}
audio.cry = function(file, bus = audio.bus.sfx)
{
file = Resources.find_sound(file);
var player = audio.play(file, bus);
if (!player) return;
player.guid = prosperon.guid();
@ -45,29 +54,43 @@ var killer = Register.appupdate.register(function() {
var song;
audio.music = function(file, fade = 0) {
// Play 'file' for new song, cross fade for seconds
audio.music = function(file, fade = 0.5) {
if (!file) {
if (song)
song.volume = 0;
return;
}
file = Resources.find_sound(file);
if (!fade) {
song = audio.play(file);
song = audio.play(file, audio.bus.music);
song.loop = true;
return;
}
if (!song) {
song = audio.play(file, audio.bus.music);
song.volume = 1;
// tween(song,'volume', 1, fade);
return;
}
var temp = audio.play(file);
var temp = audio.play(file, audio.bus.music);
if (!temp) return;
temp.volume = 0;
temp.volume = 1;
var temp2 = song;
tween(temp, 'volume', 1, fade);
tween(temp2, 'volume', 0, fade);
// tween(temp, 'volume', 1, fade);
// tween(temp2, 'volume', 0, fade);
song = temp;
song.loop = true;
}
audio.dsp.mix = function(to) {
var n = audio.dsp.mix();
if (to) n.plugin(to);
return n;
};
audio.bus.music = audio.dsp.mix();
audio.bus.music.plugin(audio.bus.master);
audio.bus.sfx = audio.dsp.mix();
audio.bus.sfx.plugin(audio.bus.master);
audio.dsp.allpass = function(secs, decay) {
var composite = {};
@ -96,6 +119,8 @@ audio.dsp.doc = {
red: "Red noise"
};
audio.dsp.obscure('doc');
Object.mixin(audio.bus.master.__proto__, {
get db() { return 20*Math.log10(Math.abs(this.volume)); },
set db(x) { x = Math.clamp(x,-100,0); this.volume = Math.pow(10, x/20); },
@ -103,6 +128,16 @@ Object.mixin(audio.bus.master.__proto__, {
set volume(x) { this.gain = x; },
});
audio.bus.master.__proto__.toJSON = function()
{
return {
volume: this.volume,
off: this.off,
pan: this.pan,
pass: this.pass
};
}
/*Object.mixin(audio.dsp.source().__proto__, {
length() { return this.frames()/audio.samplerate(); },
time() { return this.frame/sound.samplerate(); },

View file

@ -10,15 +10,16 @@ var appy = {};
appy.inputs = {};
if (os.sys() === 'macos') {
appy.inputs['S-q'] = os.quit;
appy.inputs['S-h'] = function() { };
appy.inputs['S-g'] = os.gc;
}
//appy.inputs.f12 = function() { mum.debug = !mum.debug; }
appy.inputs.f12 = function() { profile.cpu_frame(); }
appy.inputs.f7 = function() { debug.meta = !debug.meta; }
appy.inputs.f8 = function() { debug.cheat = !debug.cheat; }
appy.inputs.f9 = function() { debug.console = !debug.console; }
appy.inputs.f10 = function() { debug.show = !debug.show; }
appy.inputs.f11 = window.toggle_fullscreen;
appy.inputs.f10 = function() { profile.toggle_frame_avg(); }
appy.inputs['M-f4'] = prosperon.quit;
appy.inputs.f11.doc = "Toggle window fullscreen.";
appy.inputs.f11.title = "Toggle Fullscreen";
appy.inputs['M-f4'] = os.quit;
player[0].control(appy);
@ -53,24 +54,6 @@ os.openurl = function(url) {
var projectfile = "project.prosperon";
io.dumpfolder = '.prosperon';
Resources.is_sound = function(path) {
var ext = path.ext();
return Resources.sounds.any(x => x === ext);
}
Resources.is_animation = function(path)
{
if (path.ext() === 'gif' && Resources.gif.frames(path) > 1) return true;
if (path.ext() === 'ase') return true;
return false;
}
Resources.is_path = function(str)
{
return !/[\\\/:*?"<>|]/.test(str);
}
Resources.texture = {};
Resources.texture.dimensions = function(path) { texture.dimensions(path); }
@ -139,6 +122,23 @@ io.rm = function(f)
tmprm(Resources.replpath(f));
}
io.globToRegex = function(glob) {
// Escape special regex characters
// Replace glob characters with regex equivalents
let regexStr = glob
.replace(/[\.\\]/g, '\\$&') // Escape literal backslashes and dots
.replace(/([^\*])\*/g, '$1[^/]*') // * matches any number of characters except /
.replace(/\*\*/g, '.*') // ** matches any number of characters, including none
.replace(/\[(.*?)\]/g, '[$1]') // Character sets
.replace(/\?/g, '.'); // ? matches any single character
// Ensure the regex matches the whole string
regexStr = '^' + regexStr + '$';
// Create and return the regex object
return new RegExp(regexStr);
}
io.mixin({
extensions(ext) {
var paths = io.ls();
@ -148,12 +148,7 @@ io.mixin({
glob(pat) {
var paths = io.ls('.');
pat = pat.replaceAll(/([\[\]\(\)\^\$\.\|\+])/g, "\\$1");
pat = pat.replaceAll('**', '.*');
pat = pat.replaceAll(/[^\.]\*/g, '[^\\/]*');
pat = pat.replaceAll('?', '.');
var regex = new RegExp("^"+pat+"$", "");
var regex = io.globToRegex(pat);
paths = paths.filter(str => str.match(regex)).sort();
return paths;
},
@ -216,11 +211,6 @@ Cmdline.register_order("init", function() {
return;
}
if (!(io.ls().filter(x => x[0] !== '.').length === 0)) {
say("Directory is not empty. Make an empty one and init there.");
return;
}
io.mkdir(io.dumpfolder);
var project = {};
project.version = prosperon.version;
@ -240,7 +230,7 @@ Cmdline.register_order("play", function(argv) {
if (argv[0])
io.chdir(argv[0]);
game.loadurs();
// game.loadurs();
if (!io.exists(projectfile)) {
say("No game to play. Try making one with 'prosperon init'.");
@ -249,12 +239,22 @@ Cmdline.register_order("play", function(argv) {
var project = json.decode(io.slurp(projectfile));
game.title = project.title;
global.mixin("config.js");
game.size = [1280,720];
window.size = game.size;
if (io.exists("config.js"))
global.mixin("config.js");
else
console.warn('No config.js file found. Starting with default parameters.');
if (project.title) window.title = project.title;
game.engine_start(function() {
render.set_font("fonts/c64.ttf", 8);
global.app = actor.spawn("game.js");
if (io.exists("game.js"))
global.app = actor.spawn("game.js");
else
global.app = actor.spawn("scripts/nogame.js");
if (project.icon) window.set_icon(game.texture(project.icon));
game.camera = world.spawn("scripts/camera2d");
});
@ -320,7 +320,7 @@ Cmdline.register_order("about", function(argv) {
}, "Get information about this game.");
Cmdline.register_order("ur", function(argv) {
game.loadurs();
// game.loadurs();
for (var i of ur._list.sort()) say(i);
}, "Get information about the ur types in your game.");

69
scripts/stdprofile.js Normal file
View file

@ -0,0 +1,69 @@
var aa = [1,2,3,4,5,6,7,8,9,10];
/*
var ta = [1,2,3,4,5,6,7,8,9,10];
function tadd(a,b)
{
var c = a.slice();
for (var i = 0; i < a.length; i++)
c[i] += b[i];
return c;
}
say(vector.angle_between([0,1], [1,0]));
function test_arrays(ta, tb)
{
profile.cpu(_=> ta.add(tb), 100000, "add two arrays");
say(ta.add(tb));
}
*/
//for (var i = 2; i <= 10; i++) {
// say(`TESTING FOR ${i} ARRAY`);
// test_arrays(ta.slice(0,i), ta.slice(0,i));
//}
/*
say(prosperon.guid());
say(prosperon.guid());
var aa = [];
var ao = {};
var amt = 10000;
profile.cpu(_ => prosperon.guid(), 10000, "guid generation");
profile.cpu(_ => aa.push(1), amt, "add one to array");
profile.cpu(_ => ao[prosperon.guid()] = 1, amt, "add one via guid");
var aos = Object.keys(ao);
say(aa.length);
var useguid = aos[amt/2];
say(useguid);
var tries = [];
for (var i = 0; i < amt; i++)
tries[i] = aa.slice();
var rmamt = amt/2;
profile.cpu(x => tries[x].remove(rmamt), amt, "remove from array");
for (var i = 0; i < amt; i++) {
tries[i] = {};
for (var j in ao) tries[i][j] = ao[j];
}
profile.cpu(x => delete tries[x][useguid], amt, "delete with guid");
//profile.cpu(_ => Object.values(ao), 10000, "make array from objects");
var dofn = function(x) { x += 1; }
profile.cpu(_ => aa.forEach(dofn), 1000, "iterate array");
profile.cpu(_ => ao.forEach(dofn), 1000, "iterate object foreach");
profile.cpu(_ => {
for (var i in ao) ao[i] += 1;
}, 1000, "iterate in object values");
*/

10
scripts/stdtest.js Normal file
View file

@ -0,0 +1,10 @@
test.run("set transform scale", _ => {
var t = os.make_transform();
var s1 = t.scale.slice();
t.scale = s1;
return (t.scale.equal(s1));
});

View file

@ -1,11 +1,23 @@
/* Tests for prosperon */
var test = {};
var tests = [];
var pass = 0;
var fail = 0;
var failed = [];
var test = function(name, fn)
test.run_suite = function(file)
{
test = [];
pass = 0;
fail = 0;
failed = [];
}
test.run = function(name, fn)
{
var func = function() {
print(`${pass+fail+1}/${tests.length}: ${name} ... `);
@ -36,3 +48,5 @@ for (var f of failed)
say(f);
os.quit();
return {test};

View file

@ -103,7 +103,7 @@ Ease.elastic = {
Ease.elastic.c4 = 2*Math.PI/3;
Ease.elastic.c5 = 2*Math.PI / 4.5;
var tween = function(from, to, time, fn)
var tween = function(from, to, time, fn, endfn)
{
var start = profile.secs(profile.now());
var update = function(dt) {
@ -114,6 +114,7 @@ var tween = function(from, to, time, fn)
fn(to);
if (stop.then) stop.then();
stop();
endfn?.();
}
profile.endframe();
};

View file

@ -27,17 +27,19 @@ void main() {
in vec2 coords;
in float rad;
uniform vec4 shade;
uniform float inner_r;
out vec4 color;
void main() {
float px = 1/rad;
float blur = 1.0+px;
float R = 1;
float R2 = 0.90;
float dist = sqrt(dot(coords,coords));
float sm = 1 - smoothstep(R-px,R,dist);
float sm2 = smoothstep(R2-px,R2,dist);
float R2 = 1-inner_r;
float dist = distance(vec2(0,0),coords);
float sm = 1 - smoothstep(R-px,R*blur,dist);
float sm2 = smoothstep(R2-px,R2*blur,dist);
float alpha = sm*sm2;
color = vec4(shade.xyz, alpha*alpha);
color = vec4(shade.xyz, alpha*shade.a);
}
@end

View file

@ -7,12 +7,14 @@
in vec3 a_pos;
in vec2 a_uv;
out vec2 uv;
out vec4 shade;
vec3 pos;
struct sprite {
mat4 model;
vec4 rect;
vec4 shade;
};
readonly buffer ssbo {
@ -32,19 +34,20 @@ void main()
pos *= vec3(diffuse_size * s.rect.zw,1);
uv = (uv*s.rect.zw)+s.rect.xy;
gl_Position = vp * s.model * vec4(pos, 1.0);
shade = s.shade;
}
@end
@fs fs
in vec2 uv;
in vec4 shade;
out vec4 color;
texture2D diffuse;
sampler smp;
uniform vec4 shade;
void frag()
{
color = texture(sampler2D(diffuse,smp), uv);

View file

@ -25,12 +25,11 @@ struct anim_channel {
sampler *sampler;
};
struct animation {
char *name;
typedef struct animation {
double time;
struct anim_channel *channels;
sampler *samplers;
};
} animation;
void animation_run(struct animation *anim, float now);
HMM_Vec4 sample_sampler(sampler *sampler, float time);

View file

@ -4,48 +4,73 @@
#include "sokol/sokol_app.h"
#include "imgui.h"
#include "implot.h"
#include "imnodes.h"
#include "imgui_neo_sequencer.h"
#include "ImSequencer.h"
#include "stb_ds.h"
#define SOKOL_IMPL
#include "sokol/util/sokol_imgui.h"
#include "sokol/util/sokol_gfx_imgui.h"
#include <stdlib.h>
static sgimgui_t sgimgui;
#include "jsffi.h"
JSC_CCALL(imgui_begin,
char *title = js2strdup(argv[0]);
static int wantkeys = 0;
static int wantmouse = 0;
JSC_SCALL(imgui_window,
bool active = true;
ImGui::Begin(title, &active, ImGuiWindowFlags_MenuBar);
free(title);
return boolean2js(active);
ImGui::Begin(str, &active);
script_call_sym(argv[1], 0, NULL);
ImGui::End();
ret = boolean2js(active);
)
JSC_SCALL(imgui_menu,
if (ImGui::BeginMenu(str)) {
script_call_sym(argv[1], 0, NULL);
ImGui::EndMenu();
}
)
JSC_CCALL(imgui_end, ImGui::End())
JSC_CCALL(imgui_menubar,
if (ImGui::BeginMenuBar()) {
script_call_sym(argv[0], 0, NULL);
ImGui::EndMenuBar();
}
)
JSC_CCALL(imgui_beginmenu,
char *title = js2strdup(argv[0]);
bool active = ImGui::BeginMenu(title);
free(title);
return boolean2js(active);
JSC_CCALL(imgui_mainmenubar,
if (ImGui::BeginMainMenuBar()) {
script_call_sym(argv[0], 0, NULL);
ImGui::EndMainMenuBar();
}
)
JSC_CCALL(imgui_menuitem,
char *name = js2strdup(argv[0]);
char *hotkey = js2strdup(argv[1]);
if (ImGui::MenuItem(name,hotkey))
char *keyfn = JS_IsUndefined(argv[1]) ? NULL : js2strdup(argv[1]);
bool on = JS_IsUndefined(argv[3]) ? false : js2boolean(argv[3]);
if (ImGui::MenuItem(JS_Is(argv[0]) ? name : "##empty" ,keyfn, &on))
script_call_sym(argv[2], 0, NULL);
free(name);
free(hotkey);
if (JS_Is(argv[0])) free(name);
if (keyfn) free(keyfn);
return boolean2js(on);
)
JSC_SCALL(imgui_startplot,
JSC_SCALL(imgui_plot,
ImPlot::SetNextAxisToFit(ImAxis_X1);
ImPlot::SetNextAxisToFit(ImAxis_Y1);
ImPlot::BeginPlot(str);
script_call_sym(argv[1], 0, NULL);
ImPlot::EndPlot();
)
JSC_CCALL(imgui_endplot, ImPlot::EndPlot() )
JSC_SCALL(imgui_lineplot,
double data[js_arrlen(argv[1])];
for (int i = 0; i < js_arrlen(argv[1]); i++)
@ -54,9 +79,6 @@ JSC_SCALL(imgui_lineplot,
ImPlot::PlotLine(str, data, js_arrlen(argv[1]));
)
JSC_CCALL(imgui_beginmenubar, ImGui::BeginMenuBar())
JSC_CCALL(imgui_endmenubar, ImGui::EndMenuBar())
JSC_SSCALL(imgui_textinput,
char buffer[512];
strncpy(buffer, str2, 512);
@ -64,17 +86,379 @@ JSC_SSCALL(imgui_textinput,
ret = str2js(buffer);
)
JSC_SSCALL(imgui_textbox,
char buffer[512];
strncpy(buffer, str2, 512);
ImGui::InputTextMultiline(str, buffer, sizeof(buffer));
ret = str2js(buffer);
)
JSC_SCALL(imgui_text, ImGui::Text(str) )
JSC_SCALL(imgui_button,
if (ImGui::Button(str))
script_call_sym(argv[1], 1, argv);
)
JSC_CCALL(imgui_sokol_gfx,
sgimgui_draw(&sgimgui);
if (ImGui::BeginMenu("sokol-gfx")) {
ImGui::MenuItem("Capabilities", 0, &sgimgui.caps_window.open);
ImGui::MenuItem("Frame Stats", 0, &sgimgui.frame_stats_window.open);
ImGui::MenuItem("Buffers", 0, &sgimgui.buffer_window.open);
ImGui::MenuItem("Images", 0, &sgimgui.image_window.open);
ImGui::MenuItem("Samplers", 0, &sgimgui.sampler_window.open);
ImGui::MenuItem("Shaders", 0, &sgimgui.shader_window.open);
ImGui::MenuItem("Pipelines", 0, &sgimgui.pipeline_window.open);
ImGui::MenuItem("Attachments", 0, &sgimgui.attachments_window.open);
ImGui::MenuItem("Calls", 0, &sgimgui.capture_window.open);
ImGui::EndMenu();
}
)
JSC_SCALL(imgui_slider,
float low = JS_IsUndefined(argv[2]) ? 0.0 : js2number(argv[2]);
float high = JS_IsUndefined(argv[3]) ? 1.0 : js2number(argv[3]);
if (JS_IsArray(js, argv[1])) {
int n = js_arrlen(argv[1]);
float a[n];
js2floatarr(argv[1], n, a);
switch(n) {
case 2:
ImGui::SliderFloat2(str, a, low, high);
break;
case 3:
ImGui::SliderFloat3(str, a, low, high);
break;
case 4:
ImGui::SliderFloat3(str, a, low, high);
break;
}
ret = floatarr2js(n, a);
} else {
float val = js2number(argv[1]);
ImGui::SliderFloat(str, &val, low, high, "%.3f");
ret = number2js(val);
}
)
JSC_SCALL(imgui_checkbox,
bool val = js2boolean(argv[1]);
ImGui::Checkbox(str, &val);
ret = boolean2js(val);
)
JSC_CCALL(imgui_pushid,
ImGui::PushID(js2number(argv[0]));
)
JSC_CCALL(imgui_popid, ImGui::PopID(); )
JSC_CCALL(imgui_image,
texture *tex = js2texture(argv[0]);
simgui_image_desc_t simgd;
simgd.image = tex->id;
simgd.sampler = std_sampler;
simgui_image_t simgui_img = simgui_make_image(&simgd);
ImTextureID tex_id = simgui_imtextureid(simgui_img);
ImGui::Image(tex_id, ImVec2(tex->width, tex->height), ImVec2(0,0), ImVec2(1,1));
simgui_destroy_image(simgui_img);
)
JSC_CCALL(imgui_sameline, ImGui::SameLine(js2number(argv[0])) )
JSC_CCALL(imgui_columns, ImGui::Columns(js2number(argv[0])) )
JSC_CCALL(imgui_nextcolumn, ImGui::NextColumn() )
JSC_SCALL(imgui_collapsingheader, ret = boolean2js(ImGui::CollapsingHeader(str)) )
JSC_SCALL(imgui_radio, ret = boolean2js(ImGui::RadioButton(str, js2boolean(argv[1]))))
JSC_SCALL(imgui_tree,
if (ImGui::TreeNode(str)) {
script_call_sym(argv[1],0,NULL);
ImGui::TreePop();
}
)
JSC_SCALL(imgui_tabbar,
if (ImGui::BeginTabBar(str)) {
script_call_sym(argv[1],0,NULL);
ImGui::EndTabBar();
}
)
JSC_SCALL(imgui_tab,
if (ImGui::BeginTabItem(str)) {
script_call_sym(argv[1],0,NULL);
ImGui::EndTabItem();
}
)
JSC_SCALL(imgui_listbox,
char **arr = js2strarr(argv[1]);
int n = js_arrlen(argv[1]);
int idx = js2number(argv[2]);
ImGui::ListBox(str, &idx, arr, n, 4);
for (int i = 0; i < n; i++)
free(arr[i]);
// arrfree(arr); // TODO: Doesn't this need freed?
ret = number2js(idx);
)
JSC_SCALL(imgui_int,
int n = js2number(argv[1]);
ImGui::InputInt(str, &n);
ret = number2js(n);
)
JSC_SCALL(imgui_open_popup,
ImGui::OpenPopup(str);
)
JSC_SCALL(imgui_popup,
if (ImGui::BeginPopup(str)) {
script_call_sym(argv[1],0,NULL);
ImGui::EndPopup();
}
)
JSC_CCALL(imgui_close_popup,
ImGui::CloseCurrentPopup();
)
JSC_SCALL(imgui_modal,
if (ImGui::BeginPopupModal(str)) {
script_call_sym(argv[1],0,NULL);
ImGui::EndPopup();
}
)
JSC_SCALL(imgui_context,
if (ImGui::BeginPopupContextItem(str)) {
script_call_sym(argv[1],0,NULL);
ImGui::EndPopup();
}
)
JSC_SCALL(imgui_table,
if (ImGui::BeginTable(str, js2number(argv[1]))) {
script_call_sym(argv[2],0,NULL);
ImGui::EndTable();
}
)
JSC_CCALL(imgui_tablenextrow, ImGui::TableNextRow())
JSC_CCALL(imgui_tablenextcolumn, ImGui::TableNextColumn())
JSC_SCALL(imgui_dnd,
if (ImGui::BeginDragDropSource(ImGuiDragDropFlags_None)) {
double n = js2number(argv[1]);
ImGui::SetDragDropPayload(str, &n, sizeof(n));
script_call_sym(argv[2],0,NULL);
ImGui::EndDragDropSource();
}
)
JSC_SCALL(imgui_dndtarget,
if (ImGui::BeginDragDropTarget()) {
if (const ImGuiPayload *payload = ImGui::AcceptDragDropPayload(str)) {
JSValue n = number2js(*(double*)payload->Data);
script_call_sym(argv[1], 1, &n);
}
ImGui::EndDragDropTarget();
}
)
JSC_SCALL(imgui_color,
int n = js_arrlen(argv[1]);
float color[n];
js2floatarr(argv[1],n,color);
if (n == 3)
ImGui::ColorEdit3(str, color);
else if (n == 4)
ImGui::ColorEdit4(str, color);
ret = floatarr2js(n, color);
)
JSC_CCALL(imgui_startnode,
ImNodes::BeginNodeEditor();
script_call_sym(argv[0],0,NULL);
ImNodes::EndNodeEditor();
int start_attr;
int end_attr;
if (ImNodes::IsLinkCreated(&start_attr, &end_attr))
{
JSValue ab[2];
ab[0] = number2js(start_attr);
ab[1] = number2js(end_attr);
script_call_sym(argv[1], 2, ab);
for (int i = 0; i < 2; i++) JS_FreeValue(js, ab[i]);
}
int node_id;
if (ImNodes::IsNodeHovered(&node_id))
{
JSValue a = number2js(node_id);
script_call_sym(argv[2],1,&a);
JS_FreeValue(js,a);
}
int link_id;
if (ImNodes::IsLinkHovered(&link_id))
{
JSValue a = number2js(link_id);
script_call_sym(argv[3],1,&a);
JS_FreeValue(js,a);
}
)
JSC_CCALL(imgui_node,
ImNodes::BeginNode(js2number(argv[0]));
script_call_sym(argv[1],0,NULL);
ImNodes::EndNode();
)
JSC_CCALL(imgui_nodein,
ImNodes::BeginInputAttribute(js2number(argv[0]));
script_call_sym(argv[1],0,NULL);
ImNodes::EndInputAttribute();
)
JSC_CCALL(imgui_nodeout,
ImNodes::BeginOutputAttribute(js2number(argv[0]));
script_call_sym(argv[1],0,NULL);
ImNodes::EndOutputAttribute();
)
JSC_CCALL(imgui_nodelink,
ImNodes::Link(js2number(argv[0]), js2number(argv[1]), js2number(argv[2]));
)
JSC_CCALL(imgui_nodemini, ImNodes::MiniMap(js2number(argv[0])))
struct MySequence : public ImSequencer::SequenceInterface {
int GetFrameMin() const { return 0; }
int GetFrameMax() const { return 100; }
int GetItemCount() const { return 0; }
virtual void Get(int index, int **start, int **end, int *type, unsigned int *color) {
}
};
JSC_SCALL(imgui_seq,
struct MySequence mseq = {};
int selected = -1;
int first = 0;
int current = 100;
bool expanded = true;
Sequencer(&mseq, &current, &expanded, &selected, &first, ImSequencer::SEQUENCER_EDIT_STARTEND | ImSequencer::SEQUENCER_ADD | ImSequencer::SEQUENCER_DEL | ImSequencer::SEQUENCER_COPYPASTE | ImSequencer::SEQUENCER_CHANGE_FRAME);
)
JSC_SCALL(imgui_sequencer,
int32_t current = js2number(js_getpropstr(argv[1], "current"));
int32_t start = js2number(js_getpropstr(argv[1], "start"));
int32_t end = js2number(js_getpropstr(argv[1], "end"));
if(ImGui::BeginNeoSequencer(str, &current, &start, &end, {700,200},
ImGuiNeoSequencerFlags_AllowLengthChanging |
ImGuiNeoSequencerFlags_EnableSelection |
ImGuiNeoSequencerFlags_Selection_EnableDragging |
ImGuiNeoSequencerFlags_Selection_EnableDeletion)) {
script_call_sym(argv[2], 0, NULL);
ImGui::EndNeoSequencer();
}
js_setpropstr(argv[1], "current", number2js(current));
js_setpropstr(argv[1], "start", number2js(start));
js_setpropstr(argv[1], "end", number2js(end));
)
JSC_SCALL(imgui_timeline,
float *k = js2newfloatarr(argv[1]);
int n = arrlen(k);
int32_t *keys = (int32_t*)malloc(n*sizeof(*keys));
for (int i = 0; i < n; i++) {
keys[i] = k[i];
}
arrfree(k);
if (ImGui::BeginNeoTimelineEx(str)) {
for (int i = 0; i < n; i++)
ImGui::NeoKeyframe(keys+i);
ImGui::EndNeoTimeLine();
}
JSValue arr = JS_NewArray(js);
for (int i = 0; i < n; i++)
js_setprop_num(arr, i, number2js(keys[i]));
free(keys);
ret = arr;
)
JSC_SCALL(imgui_tlgroup,
if (ImGui::BeginNeoGroup(str)) {
script_call_sym(argv[1], 0, NULL);
ImGui::EndNeoGroup();
}
)
static const JSCFunctionListEntry js_imgui_funcs[] = {
MIST_FUNC_DEF(imgui, begin, 1),
MIST_FUNC_DEF(imgui, end,0),
MIST_FUNC_DEF(imgui, beginmenu, 1),
MIST_FUNC_DEF(imgui, window, 2),
MIST_FUNC_DEF(imgui, menu, 2),
MIST_FUNC_DEF(imgui, sameline, 1),
MIST_FUNC_DEF(imgui, int, 2),
MIST_FUNC_DEF(imgui, pushid, 1),
MIST_FUNC_DEF(imgui, popid, 0),
MIST_FUNC_DEF(imgui, slider, 4),
MIST_FUNC_DEF(imgui, menubar, 1),
MIST_FUNC_DEF(imgui, mainmenubar, 1),
MIST_FUNC_DEF(imgui, menuitem, 3),
MIST_FUNC_DEF(imgui, beginmenubar, 0),
MIST_FUNC_DEF(imgui, endmenubar, 0),
MIST_FUNC_DEF(imgui, radio, 2),
MIST_FUNC_DEF(imgui, image, 1),
MIST_FUNC_DEF(imgui, textinput, 2),
MIST_FUNC_DEF(imgui, startplot,1),
MIST_FUNC_DEF(imgui,endplot,0),
MIST_FUNC_DEF(imgui,lineplot,2)
MIST_FUNC_DEF(imgui, textbox, 2),
MIST_FUNC_DEF(imgui, button, 2),
MIST_FUNC_DEF(imgui, checkbox, 2),
MIST_FUNC_DEF(imgui, text, 1),
MIST_FUNC_DEF(imgui, plot,1),
MIST_FUNC_DEF(imgui, lineplot,2),
MIST_FUNC_DEF(imgui, sokol_gfx, 0),
MIST_FUNC_DEF(imgui, columns, 1),
MIST_FUNC_DEF(imgui, nextcolumn, 0),
MIST_FUNC_DEF(imgui, collapsingheader, 1),
MIST_FUNC_DEF(imgui, tree, 2),
MIST_FUNC_DEF(imgui, listbox, 3),
MIST_FUNC_DEF(imgui, tabbar, 2),
MIST_FUNC_DEF(imgui, tab, 2),
MIST_FUNC_DEF(imgui, open_popup, 1),
MIST_FUNC_DEF(imgui, modal, 2),
MIST_FUNC_DEF(imgui, popup, 2),
MIST_FUNC_DEF(imgui, close_popup,0),
MIST_FUNC_DEF(imgui, context,2),
MIST_FUNC_DEF(imgui, table, 3),
MIST_FUNC_DEF(imgui, tablenextcolumn,0),
MIST_FUNC_DEF(imgui, tablenextrow,0),
MIST_FUNC_DEF(imgui, dnd, 3),
MIST_FUNC_DEF(imgui, dndtarget, 2),
MIST_FUNC_DEF(imgui, color, 2),
MIST_FUNC_DEF(imgui, startnode, 1),
MIST_FUNC_DEF(imgui, node, 2),
MIST_FUNC_DEF(imgui, nodein, 2),
MIST_FUNC_DEF(imgui, nodeout, 2),
MIST_FUNC_DEF(imgui, nodelink, 3),
MIST_FUNC_DEF(imgui, nodemini, 1),
MIST_FUNC_DEF(imgui, sequencer, 3),
MIST_FUNC_DEF(imgui, timeline, 3),
MIST_FUNC_DEF(imgui, tlgroup, 2),
MIST_FUNC_DEF(imgui, seq, 3),
};
static int started = 0;
@ -88,6 +472,7 @@ JSValue gui_init(JSContext *js)
sgimgui_init(&sgimgui, &desc);
ImPlot::CreateContext();
ImNodes::CreateContext();
JSValue imgui = JS_NewObject(js);
JS_SetPropertyFunctionList(js, imgui, js_imgui_funcs, countof(js_imgui_funcs));
@ -97,8 +482,11 @@ JSValue gui_init(JSContext *js)
void gui_input(sapp_event *e)
{
if (started)
simgui_handle_event(e);
if (!started) return;
simgui_handle_event(e);
ImGuiIO io = ImGui::GetIO();
wantkeys = io.WantCaptureKeyboard;
wantmouse = io.WantCaptureMouse;
}
void gui_newframe(int x, int y, float dt)
@ -111,12 +499,6 @@ void gui_newframe(int x, int y, float dt)
simgui_new_frame(&frame);
}
void gfx_gui()
{
sgimgui_draw(&sgimgui);
sgimgui_draw_menu(&sgimgui, "sokol-gfx");
}
void gui_endframe()
{
simgui_render();
@ -126,3 +508,6 @@ void gui_exit()
{
sgimgui_discard(&sgimgui);
}
int gui_wantmouse() { return wantmouse; }
int gui_wantkeys() { return wantkeys; }

View file

@ -15,6 +15,9 @@ void gui_input(sapp_event *e);
void gui_endframe();
void gui_exit();
int gui_wantmouse();
int gui_wantkeys();
#ifdef __cplusplus
}
#endif

View file

@ -33,6 +33,8 @@
#include "par/par_shapes.h"
#include "sokol_glue.h"
#include <chipmunk/chipmunk_unsafe.h>
#include <chipmunk/chipmunk_structs.h>
#include <stdint.h>
#include "gui.h"
#include "timer.h"
@ -46,8 +48,6 @@ static JSValue globalThis;
static JSClassID js_ptr_id;
static JSClassDef js_ptr_class = { "POINTER" };
static JSValue JSUNDEF;
JSValue str2js(const char *c, ...) {
if (!c) return JS_UNDEFINED;
char *result = NULL;
@ -62,6 +62,8 @@ JSValue str2js(const char *c, ...) {
return JS_NewString(js, buf);
}
int JS_Is(JSValue v) { return JS_ToBool(js, v); }
const char *js2str(JSValue v) {
return JS_ToCString(js, v);
}
@ -93,6 +95,11 @@ void cpConstraint_free(cpConstraint *c)
cpConstraintFree(c);
}
void skin_free(skin *sk) {
arrfree(sk->invbind);
free(sk);
}
void jsfreestr(const char *s) { JS_FreeCString(js, s); }
QJSCLASS(gameobject)
QJSCLASS(transform)
@ -107,6 +114,7 @@ QJSCLASS(datastream)
QJSCLASS(cpShape)
QJSCLASS(cpConstraint)
QJSCLASS(timer)
QJSCLASS(skin);
static JSValue js_circle2d;
static JSValue js_poly2d;
@ -155,6 +163,7 @@ void js_setprop_num(JSValue obj, uint32_t i, JSValue v) { JS_SetPropertyUint32(j
JSValue js_getpropidx(JSValue v, uint32_t i)
{
if (!JS_IsArray(js, v)) return JS_UNDEFINED;
JSValue p = JS_GetPropertyUint32(js, v, i);
JS_FreeValue(js,p);
return p;
@ -175,6 +184,17 @@ void js_setpropstr(JSValue v, const char *str, JSValue p)
static inline cpBody *js2body(JSValue v) { return js2gameobject(v)->body; }
char **js2strarr(JSValue v)
{
int n = js_arrlen(v);
char **arr = malloc(sizeof(*arr));
arr = NULL;
for (int i = 0; i < n; i++)
arrput(arr, js2strdup(js_getpropidx(v, i)));
return arr;
}
JSValue strarr2js(char **c)
{
JSValue arr = JS_NewArray(js);
@ -202,6 +222,7 @@ uint64_t js2uint64(JSValue v)
JSValue angle2js(double g) {
return number2js(g*HMM_RadToTurn);
}
double js2angle(JSValue v) {
double n = js2number(v);
return n * HMM_TurnToRad;
@ -374,6 +395,29 @@ struct boundingbox js2bb(JSValue v)
cpVect js2cvec2(JSValue v) { return js2vec2(v).cp; }
JSValue cvec22js(cpVect v) { return vec22js((HMM_Vec2)v); }
void js2floatarr(JSValue v, int n, float *a) {
if (!JS_IsArray(js,v)) return;
for (int i = 0; i < n; i++)
a[i] = js2number(js_getpropidx(v,i));
}
JSValue floatarr2js(int n, float *a) {
JSValue arr = JS_NewArray(js);
for (int i = 0; i < n; i++)
js_setprop_num(arr, i, number2js(a[i]));
return arr;
}
float *js2newfloatarr(JSValue v)
{
float *arr = NULL;
for (int i = 0; i < js_arrlen(v); i++)
arrpush(arr, js2number(js_getpropidx(v,i)));
return arr;
}
HMM_Vec2 js2vec2(JSValue v)
{
HMM_Vec2 v2;
@ -399,6 +443,16 @@ HMM_Vec3 js2vec3(JSValue v)
return v3;
}
HMM_Vec3 js2vec3f(JSValue v)
{
HMM_Vec3 vec;
if (JS_IsArray(js, v))
return js2vec3(v);
else
vec.x = vec.y = vec.z = js2number(v);
return vec;
}
JSValue vec32js(HMM_Vec3 v)
{
JSValue array = JS_NewArray(js);
@ -408,6 +462,8 @@ JSValue vec32js(HMM_Vec3 v)
return array;
}
JSValue vec3f2js(HMM_Vec3 v) { return vec32js(v); }
JSValue quat2js(HMM_Quat q)
{
JSValue arr = JS_NewArray(js);
@ -426,6 +482,22 @@ HMM_Vec4 js2vec4(JSValue v)
return v4;
}
double arr_vec_length(JSValue v)
{
int len = js_arrlen(v);
switch(len) {
case 2: return HMM_LenV2(js2vec2(v));
case 3: return HMM_LenV3(js2vec3(v));
case 4: return HMM_LenV4(js2vec4(v));
}
double sum = 0;
for (int i = 0; i < len; i++)
sum += pow(js2number(js_getpropidx(v, i)), 2);
return sqrt(sum);
}
HMM_Quat js2quat(JSValue v)
{
return js2vec4(v).quat;
@ -504,6 +576,7 @@ int js_print_exception(JSValue v)
JS_FreeCString(js, msg);
JS_FreeCString(js, stack);
JS_FreeValue(js,ex);
return 1;
#endif
return 0;
@ -641,8 +714,10 @@ sg_bindings js2bind(JSValue v)
{
sg_bindings bind = {0};
JSValue attrib = js_getpropstr(v, "attrib");
for (int i = 0; i < js_arrlen(attrib); i++)
for (int i = 0; i < js_arrlen(attrib); i++) {
bind.vertex_buffers[i] = *js2sg_buffer(js_getpropidx(attrib,i));
}
JSValue index = js_getpropstr(v, "index");
if (!JS_IsUndefined(index))
@ -836,6 +911,33 @@ sg_shader js2shader(JSValue v)
return sh;
}
static int mat2type(int mat)
{
switch(mat) {
case MAT_POS:
case MAT_NORM:
return SG_VERTEXFORMAT_FLOAT3;
case MAT_PPOS:
case MAT_WH:
case MAT_ST:
return SG_VERTEXFORMAT_FLOAT2;
case MAT_UV:
return SG_VERTEXFORMAT_USHORT2N;
case MAT_TAN:
return SG_VERTEXFORMAT_UINT10_N2;
case MAT_BONE:
return SG_VERTEXFORMAT_UBYTE4;
case MAT_WEIGHT:
case MAT_COLOR:
return SG_VERTEXFORMAT_UBYTE4N;
case MAT_ANGLE:
case MAT_SCALE:
return SG_VERTEXFORMAT_FLOAT;
};
return -1;
}
sg_vertex_layout_state js2layout(JSValue v)
{
sg_vertex_layout_state st = {0};
@ -916,6 +1018,11 @@ JSC_CCALL(render_setunivp,
sg_apply_uniforms(js2number(argv[0]), js2number(argv[1]), SG_RANGE_REF(globalview.vp));
)
JSC_CCALL(render_setunibones,
skin *sk = js2skin(argv[2]);
sg_apply_uniforms(js2number(argv[0]), js2number(argv[1]), SG_RANGE_REF(sk->binds));
)
JSC_CCALL(render_setunim4,
HMM_Mat4 m = MAT1;
if (JS_IsArray(js, argv[2])) {
@ -950,7 +1057,6 @@ JSC_CCALL(render_make_particle_ssbo,
size_t size = js_arrlen(array)*(sizeof(particle_ss));
sg_buffer *b = js2sg_buffer(argv[1]);
if (!b) return JS_UNDEFINED;
particle_ss ms[js_arrlen(array)];
if (sg_query_buffer_will_overflow(*b, size)) {
@ -978,6 +1084,7 @@ JSC_CCALL(render_make_particle_ssbo,
typedef struct sprite_ss {
HMM_Mat4 model;
HMM_Vec4 rect;
HMM_Vec4 shade;
} sprite_ss;
JSC_CCALL(render_make_sprite_ssbo,
@ -1002,6 +1109,7 @@ JSC_CCALL(render_make_sprite_ssbo,
JSValue sub = js_getpropidx(array,i);
ms[i].model = transform2mat(js2transform(js_getpropstr(sub, "transform")));
ms[i].rect = js2vec4(js_getpropstr(sub,"rect"));
ms[i].shade = js2vec4(js_getpropstr(sub,"shade"));
}
sg_append_buffer(*b, (&(sg_range){
@ -1038,7 +1146,7 @@ JSC_CCALL(render_make_t_ssbo,
)
JSC_CCALL(render_spdraw,
sg_draw(0,js2number(argv[0]),js2number(argv[1]));
sg_draw(js2number(argv[0]),js2number(argv[1]),js2number(argv[2]));
)
JSC_CCALL(render_setpipeline,
@ -1054,7 +1162,6 @@ JSC_CCALL(render_screencolor,
)
JSC_CCALL(render_imgui_new, gui_newframe(js2number(argv[0]),js2number(argv[1]),js2number(argv[2])); )
JSC_CCALL(render_gfx_gui, gfx_gui())
JSC_CCALL(render_imgui_end, gui_endframe())
JSC_CCALL(render_imgui_init, return gui_init(js))
@ -1072,7 +1179,8 @@ static const JSCFunctionListEntry js_render_funcs[] = {
MIST_FUNC_DEF(render, pipeline, 1),
MIST_FUNC_DEF(render, setuniv3, 2),
MIST_FUNC_DEF(render, setuniv, 2),
MIST_FUNC_DEF(render, spdraw, 2),
MIST_FUNC_DEF(render, spdraw, 3),
MIST_FUNC_DEF(render, setunibones, 3),
MIST_FUNC_DEF(render, setbind, 1),
MIST_FUNC_DEF(render, setuniproj, 2),
MIST_FUNC_DEF(render, setuniview, 2),
@ -1083,7 +1191,6 @@ static const JSCFunctionListEntry js_render_funcs[] = {
MIST_FUNC_DEF(render, setpipeline, 1),
MIST_FUNC_DEF(render, screencolor, 0),
MIST_FUNC_DEF(render, imgui_new, 3),
MIST_FUNC_DEF(render, gfx_gui, 0),
MIST_FUNC_DEF(render, imgui_end, 0),
MIST_FUNC_DEF(render, imgui_init, 0),
MIST_FUNC_DEF(render, make_t_ssbo, 2),
@ -1217,12 +1324,14 @@ JSC_CCALL(vector_inflate,
JSC_CCALL(vector_rotate,
HMM_Vec2 vec = js2vec2(argv[0]);
float r = HMM_LenV2(vec);
double angle = js2angle(argv[1]);
HMM_Vec2 pivot = JS_IsUndefined(argv[2]) ? v2zero : js2vec2(argv[2]);
vec = HMM_SubV2(vec,pivot);
float r = HMM_LenV2(vec);
angle += atan2(vec.y,vec.x);
vec.x = r*cos(angle);
vec.y = r*sin(angle);
return vec22js(vec);
return vec22js(HMM_AddV2(vec,pivot));
)
JSC_CCALL(vector_add,
@ -1240,7 +1349,14 @@ JSC_CCALL(vector_norm,
case 3: return vec32js(HMM_NormV3(js2vec3(argv[0])));
case 4: return vec42js(HMM_NormV4(js2vec4(argv[0])));
}
return argv[0];
double length = arr_vec_length(argv[0]);
JSValue newarr = JS_NewArray(js);
for (int i = 0; i < len; i++)
js_setprop_num(newarr, i, number2js(js2number(js_getpropidx(argv[0],i))/length));
return newarr;
)
JSC_CCALL(vector_angle_between,
@ -1315,6 +1431,10 @@ JSC_CCALL(vector_angledist,
return number2js(dist);
)
JSC_CCALL(vector_length,
return number2js(arr_vec_length(argv[0]));
)
double r2()
{
return (double)rand() / (double)RAND_MAX ;
@ -1415,7 +1535,8 @@ static const JSCFunctionListEntry js_vector_funcs[] = {
MIST_FUNC_DEF(vector, mean, 1),
MIST_FUNC_DEF(vector, sum, 1),
MIST_FUNC_DEF(vector, sigma, 1),
MIST_FUNC_DEF(vector, median, 1)
MIST_FUNC_DEF(vector, median, 1),
MIST_FUNC_DEF(vector, length, 1)
};
#define JS_HMM_FN(OP, HMM, SIGN) \
@ -1452,9 +1573,9 @@ JS_HMM_FN(div, Div, /)
JS_HMM_FN(scale, Mul, *)
JSC_CCALL(array_lerp,
double t = js2number(argv[1]);
int len = js_arrlen(self);
JSValue arr = JS_NewArray(js);
double t = js2number(argv[1]);
for (int i = 0; i < len; i++) {
double from = js2number(js_getpropidx(self, i));
@ -1465,11 +1586,11 @@ JSC_CCALL(array_lerp,
)
static const JSCFunctionListEntry js_array_funcs[] = {
MIST_FUNC_DEF(array, add, 1),
MIST_FUNC_DEF(array, sub, 1),
MIST_FUNC_DEF(array, div,1),
MIST_FUNC_DEF(array, scale, 1),
MIST_FUNC_DEF(array, lerp, 2)
PROTO_FUNC_DEF(array, add, 1),
PROTO_FUNC_DEF(array, sub, 1),
PROTO_FUNC_DEF(array, div,1),
PROTO_FUNC_DEF(array, scale, 1),
PROTO_FUNC_DEF(array, lerp, 2)
};
JSC_CCALL(game_engine_start, engine_start(argv[0],argv[1], js2number(argv[2]), js2number(argv[3])))
@ -1540,6 +1661,7 @@ static const JSCFunctionListEntry js_time_funcs[] = {
};
JSC_SCALL(console_print, log_print(str))
JSC_SCALL(console_term_print, term_print(str));
JSValue js_console_rec(JSContext *js, JSValue self, int argc, JSValue *argv)
{
@ -1560,6 +1682,7 @@ JSC_GETSET_GLOBAL(stdout_lvl, number)
static const JSCFunctionListEntry js_console_funcs[] = {
MIST_FUNC_DEF(console,print,1),
MIST_FUNC_DEF(console, term_print, 1),
MIST_FUNC_DEF(console,rec,4),
CGETSET_ADD(global, stdout_lvl)
};
@ -1629,6 +1752,7 @@ JSValue js_io_slurpbytes(JSContext *js, JSValue self, int argc, JSValue *argv)
JSValue js_io_slurp(JSContext *js, JSValue self, int argc, JSValue *argv)
{
char *f = js2str(argv[0]);
size_t len;
@ -1835,7 +1959,7 @@ static const JSCFunctionListEntry js_physics_funcs[] = {
};
JSC_GETSET(transform, pos, vec3)
JSC_GETSET(transform, scale, vec3)
JSC_GETSET(transform, scale, vec3f)
JSC_GETSET(transform, rotation, quat)
JSC_CCALL(transform_move, transform_move(js2transform(self), js2vec3(argv[0])); )
@ -2383,6 +2507,8 @@ JSC_GET(texture, width, number)
JSC_GET(texture, height, number)
JSC_GET(texture, frames, number)
JSC_GET(texture, delays, ints)
JSC_GET(texture, size, number)
JSC_GET(texture, gpusize, number)
JSC_SCALL(texture_save, texture_save(js2texture(self), str));
@ -2390,13 +2516,21 @@ JSC_CCALL(texture_blit,
texture *tex = js2texture(self);
texture_blit(js2texture(self), js2texture(argv[0]), js2number(argv[1]), js2number(argv[2]), js2number(argv[3]), js2number(argv[4])))
JSC_CCALL(texture_getid,
texture *tex = js2texture(self);
return number2js(tex->id.id);
)
static const JSCFunctionListEntry js_texture_funcs[] = {
MIST_GET(texture, width),
MIST_GET(texture, height),
MIST_GET(texture, frames),
MIST_GET(texture, delays),
MIST_GET(texture, gpusize),
MIST_GET(texture, size),
MIST_FUNC_DEF(texture, save, 1),
MIST_FUNC_DEF(texture, blit, 5)
MIST_FUNC_DEF(texture, blit, 5),
MIST_FUNC_DEF(texture, getid, 0),
};
static const JSCFunctionListEntry js_timer_funcs[] = {
@ -2466,7 +2600,6 @@ static const JSCFunctionListEntry js_nota_funcs[] = {
MIST_FUNC_DEF(nota, decode, 1)
};
JSValue js_os_cwd(JSContext *js, JSValue self, int argc, JSValue *argv)
{
char cwd[PATH_MAX];
@ -2501,6 +2634,141 @@ JSC_CCALL(os_quit, quit();)
JSC_CCALL(os_exit, exit(js2number(argv[0]));)
JSC_CCALL(os_reindex_static, cpSpaceReindexStatic(space));
JSC_CCALL(os_gc, script_gc());
JSC_CCALL(os_mem_limit, script_mem_limit(js2number(argv[0])))
JSC_CCALL(os_gc_threshold, script_gc_threshold(js2number(argv[0])))
JSC_CCALL(os_max_stacksize, script_max_stacksize(js2number(argv[0])))
static JSValue tmp2js(FILE *tmp)
{
size_t size = ftell(tmp);
rewind(tmp);
char *buffer = calloc(size+1, sizeof(char));
fread(buffer, sizeof(char),size, tmp);
JSValue ret = str2js(buffer);
free(buffer);
return ret;
}
JSC_CCALL(os_dump_value,
FILE *tmp = tmpfile();
quickjs_set_dumpout(tmp);
JS_DumpMyValue(rt, argv[0]);
ret = tmp2js(tmp);
)
JSC_CCALL(os_dump_atoms,
FILE *tmp = tmpfile();
quickjs_set_dumpout(tmp);
JS_PrintAtoms(rt);
ret = tmp2js(tmp);
)
JSC_CCALL(os_dump_shapes,
FILE *tmp = tmpfile();
quickjs_set_dumpout(tmp);
JS_PrintShapes(rt);
size_t size = ftell(tmp);
rewind(tmp);
char buffer[size];
fgets(buffer, sizeof(char)*size, tmp);
fclose(tmp);
ret = str2js(buffer);
)
JSC_CCALL(os_calc_mem,
return number2js(JS_MyValueSize(rt, argv[0]));
)
#define JSOBJ_ADD_FIELD(OBJ, STRUCT, FIELD, TYPE) \
js_setpropstr(OBJ, #FIELD, TYPE##2js(STRUCT.FIELD));\
#define JSJMEMRET(FIELD) JSOBJ_ADD_FIELD(ret, jsmem, FIELD, number)
JSC_CCALL(os_memstate,
JSMemoryUsage jsmem;
JS_FillMemoryState(rt, &jsmem);
ret = JS_NewObject(js);
JSJMEMRET(malloc_size)
JSJMEMRET(malloc_limit)
JSJMEMRET(gc_threshold)
)
JSC_CCALL(os_mem,
JSMemoryUsage jsmem;
JS_ComputeMemoryUsage(rt, &jsmem);
ret = JS_NewObject(js);
JSJMEMRET(malloc_size)
JSJMEMRET(malloc_limit)
JSJMEMRET(gc_threshold)
JSJMEMRET(memory_used_size)
JSJMEMRET(memory_used_count)
JSJMEMRET(atom_count)
JSJMEMRET(atom_size)
JSJMEMRET(str_count)
JSJMEMRET(str_size)
JSJMEMRET(obj_count)
JSJMEMRET(obj_size)
JSJMEMRET(prop_count)
JSJMEMRET(prop_size)
JSJMEMRET(shape_count)
JSJMEMRET(shape_size)
JSJMEMRET(js_func_count)
JSJMEMRET(js_func_size)
JSJMEMRET(js_func_code_size)
JSJMEMRET(js_func_pc2line_count)
JSJMEMRET(js_func_pc2line_size)
JSJMEMRET(c_func_count)
JSJMEMRET(array_count)
JSJMEMRET(fast_array_count)
JSJMEMRET(fast_array_elements)
JSJMEMRET(binary_object_count)
JSJMEMRET(binary_object_size)
)
JSC_CCALL(os_dump_mem,
FILE *tmp = tmpfile();
JSMemoryUsage mem = {0};
JS_DumpMemoryUsage(tmp, &mem, rt);
ret = tmp2js(tmp);
)
JSC_CCALL(os_value_id,
return number2js((intptr_t)JS_VALUE_GET_PTR(argv[0]));
)
static double gc_t = 0;
static double gc_mem = 0;
static double gc_startmem = 0;
void script_report_gc_time(double t, double startmem, double mem)
{
gc_t = t;
gc_mem = mem;
gc_startmem = startmem;
}
static FILE *cycles;
JSC_CCALL(os_check_cycles,
if (ftell(cycles) == 0) return JS_UNDEFINED;
cycles = tmpfile();
quickjs_set_cycleout(cycles);
ret = tmp2js(cycles);
)
JSC_CCALL(os_check_gc,
if (gc_t == 0) return JS_UNDEFINED;
JSValue gc = JS_NewObject(js);
js_setpropstr(gc, "time", number2js(gc_t));
js_setpropstr(gc, "mem", number2js(gc_mem));
js_setpropstr(gc, "startmem", number2js(gc_startmem));
gc_t = 0;
gc_mem = 0;
gc_startmem = 0;
return gc;
)
JSC_SSCALL(os_eval, ret = script_eval(str, str2))
JSC_CCALL(os_make_body,
@ -2618,6 +2886,13 @@ JSC_CCALL(os_make_circle2d,
return ret;
)
JSC_CCALL(os_make_timer, return timer2js(timer_make()))
JSC_CCALL(os_update_timers, timer_update(js2number(argv[0])))
JSC_CCALL(os_obj_size,
)
JSC_CCALL(poly2d_setverts,
cpShape *s = js2cpShape(self);
HMM_Vec2 *v = js2cpvec2arr(argv[0]);
@ -2695,6 +2970,13 @@ JSC_SCALL(os_make_texture,
JS_SetPropertyStr(js, ret, "path", JS_DupValue(js,argv[0]));
)
JSC_SCALL(os_texture_swap,
texture *old = js2texture(argv[1]);
texture *tex = texture_from_file(str);
JS_SetOpaque(argv[1], tex);
texture_free(old);
)
JSC_CCALL(os_make_tex_data,
ret = texture2js(texture_empty(js2number(argv[0]), js2number(argv[1]), js2number(argv[2])))
)
@ -2709,42 +2991,36 @@ JSC_CCALL(os_make_transform, return transform2js(make_transform()))
JSC_SCALL(os_system, return number2js(system(str)); )
#define PRIMCHECK(VAR, JS, VAL) \
if (VAR->VAL.id) js_setpropstr(JS, #VAL, sg_buffer2js(&VAR->VAL)); \
JSC_SCALL(os_gltf_buffer,
int buffer_idx = js2number(argv[1]);
int type = js2number(argv[2]);
cgltf_options options = {0};
cgltf_data *data = NULL;
cgltf_result result = cgltf_parse_file(&options, str, &data);
result = cgltf_load_buffers(&options, data, str);
JSValue primitive2js(primitive *p)
{
JSValue v = JS_NewObject(js);
PRIMCHECK(p, v, pos)
PRIMCHECK(p,v,norm)
PRIMCHECK(p,v,uv)
PRIMCHECK(p,v,bone)
PRIMCHECK(p,v,weight)
PRIMCHECK(p,v,color)
PRIMCHECK(p,v,index)
js_setpropstr(v, "count", number2js(p->idx_count));
return v;
}
sg_buffer *b = malloc(sizeof(*b));
*b = accessor2buffer(&data->accessors[buffer_idx], type);
cgltf_free(data);
JSValue material2js(material *m)
{
JSValue v = JS_NewObject(js);
if (m->diffuse)
js_setpropstr(v, "diffuse", texture2js(m->diffuse));
return v;
}
ret = sg_buffer2js(b);
)
JSC_SCALL(os_make_model,
model *m = model_make(str);
if (arrlen(m->meshes) != 1) return JSUNDEF;
mesh *me = m->meshes+0;
JSValue v = JS_NewObject(js);
js_setpropstr(v, "mesh", primitive2js(me->primitives+0));
js_setpropstr(v, "material", material2js(me->primitives[0].mat));
return v;
JSC_SCALL(os_gltf_skin,
cgltf_options options = {0};
cgltf_data *data = NULL;
cgltf_parse_file(&options,str,&data);
cgltf_load_buffers(&options,data,str);
if (data->skins_count <= 0) {
ret = (JS_UNDEFINED);
goto CLEANUP;
}
ret = skin2js(make_gltf_skin(data->skins+0, data));
CLEANUP:
cgltf_free(data);
)
JSC_CCALL(os_make_buffer,
@ -2907,6 +3183,11 @@ JSC_SCALL(os_make_video,
js_setpropstr(ret, "texture", texture2js(t));
)
JSC_CCALL(os_skin_calculate,
skin *sk = js2skin(argv[0]);
skin_calculate(sk);
)
static const JSCFunctionListEntry js_os_funcs[] = {
MIST_FUNC_DEF(os, cwd, 0),
MIST_FUNC_DEF(os, env, 1),
@ -2922,9 +3203,9 @@ 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, texture_swap, 2),
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),
MIST_FUNC_DEF(os, make_buffer, 1),
MIST_FUNC_DEF(os, make_line_prim, 4),
@ -2938,6 +3219,24 @@ static const JSCFunctionListEntry js_os_funcs[] = {
MIST_FUNC_DEF(os, make_hemisphere, 2),
MIST_FUNC_DEF(os, make_plane, 2),
MIST_FUNC_DEF(os, make_video, 1),
MIST_FUNC_DEF(os, make_timer, 0),
MIST_FUNC_DEF(os, update_timers, 1),
MIST_FUNC_DEF(os, mem, 1),
MIST_FUNC_DEF(os, mem_limit, 1),
MIST_FUNC_DEF(os, gc_threshold, 1),
MIST_FUNC_DEF(os, max_stacksize, 1),
MIST_FUNC_DEF(os, dump_value, 1),
MIST_FUNC_DEF(os, dump_mem, 0),
MIST_FUNC_DEF(os, dump_shapes, 0),
MIST_FUNC_DEF(os, dump_atoms,0),
MIST_FUNC_DEF(os, calc_mem, 1),
MIST_FUNC_DEF(os, check_gc, 0),
MIST_FUNC_DEF(os, check_cycles, 0),
MIST_FUNC_DEF(os, memstate, 0),
MIST_FUNC_DEF(os, value_id, 1),
MIST_FUNC_DEF(os, gltf_buffer, 3),
MIST_FUNC_DEF(os, gltf_skin, 1),
MIST_FUNC_DEF(os, skin_calculate, 1),
};
#include "steam.h"
@ -2948,7 +3247,9 @@ JS_SetPropertyFunctionList(js, js_##NAME, js_##NAME##_funcs, countof(js_##NAME##
JS_SetPrototype(js, js_##NAME, PARENT); \
void ffi_load() {
JSUNDEF = JS_UNDEFINED;
cycles = tmpfile();
quickjs_set_cycleout(cycles);
globalThis = JS_GetGlobalObject(js);
QJSCLASSPREP(ptr);
@ -2990,6 +3291,7 @@ void ffi_load() {
JS_SetPropertyStr(js, prosperon, "version", str2js(VER));
JS_SetPropertyStr(js, prosperon, "revision", str2js(COM));
JS_SetPropertyStr(js, prosperon, "date", str2js(DATE));
JS_SetPropertyStr(js, globalThis, "window", window2js(&mainwin));
JS_SetPropertyStr(js, globalThis, "texture", JS_DupValue(js,texture_proto));
@ -3021,7 +3323,7 @@ void ffi_load() {
JSValue array_proto = js_getpropstr(globalThis, "Array");
array_proto = js_getpropstr(array_proto, "prototype");
JS_SetPropertyFunctionList(js, array_proto, js_array_funcs, 4);
JS_SetPropertyFunctionList(js, array_proto, js_array_funcs, countof(js_array_funcs));
JS_FreeValue(js,globalThis);
}

View file

@ -10,17 +10,22 @@ extern "C" {
#include <stdarg.h>
#include <chipmunk/chipmunk.h>
void script_report_gc_time(double t, double startmem, double mem);
int JS_Is(JSValue v);
extern JSValue cpShape2js(cpShape *s);
#define MIST_CFUNC_DEF(name, length, func1) { name, JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE | JS_PROP_ENUMERABLE, JS_DEF_CFUNC, 0, .u = { .func = { length, JS_CFUNC_generic, { .generic = func1 } } } }
#define MIST_CFUNC_DEF(name, length, func1, props) { name, props, JS_DEF_CFUNC, 0, .u = { .func = { length, JS_CFUNC_generic, { .generic = func1 } } } }
#define MIST_FUNC_DEF(TYPE, FN, LEN) MIST_CFUNC_DEF(#FN, LEN, js_##TYPE##_##FN)
#define MIST_FUNC_DEF(TYPE, FN, LEN) MIST_CFUNC_DEF(#FN, LEN, js_##TYPE##_##FN, JS_PROP_C_W_E)
#define PROTO_FUNC_DEF(TYPE, FN, LEN) MIST_CFUNC_DEF(#FN, LEN, js_##TYPE##_##FN, 0)
#define JS_SETSIG JSContext *js, JSValue self, JSValue val
#define JSC_SCALL(NAME, FN) JSC_CCALL(NAME, \
const char *str = js2str(argv[0]); \
FN ; \
{FN;} ; \
JS_FreeCString(js,str); \
) \
@ -92,7 +97,6 @@ JSValue js_##ID##_get_##ENTRY (JSContext *js, JSValue self) { \
static JSClassID js_##TYPE##_id;\
static void js_##TYPE##_finalizer(JSRuntime *rt, JSValue val){\
TYPE *n = JS_GetOpaque(val, js_##TYPE##_id);\
YughSpam("Freeing " #TYPE " at %p", n); \
TYPE##_free(n);}\
static JSClassDef js_##TYPE##_class = {\
#TYPE,\
@ -105,11 +109,11 @@ TYPE *js2##TYPE (JSValue val) { \
}\
JSValue TYPE##2js(TYPE *n) { \
JSValue j = JS_NewObjectClass(js,js_##TYPE##_id);\
YughSpam("Created " #TYPE " at %p", n); \
JS_SetOpaque(j,n);\
return j; }\
\
static JSValue js_##TYPE##_memid (JSContext *js, JSValue self) { return str2js("%p", js2##TYPE(self)); } \
static JSValue js_##TYPE##_memsize (JSContext *js, JSValue self) { return number2js(sizeof(TYPE)); } \
#define QJSGLOBALCLASS(NAME) \
JSValue NAME = JS_NewObject(js); \
@ -126,6 +130,7 @@ QJSCLASSPREP(TYPE); \
JSValue TYPE##_proto = JS_NewObject(js); \
JS_SetPropertyFunctionList(js, TYPE##_proto, js_##TYPE##_funcs, countof(js_##TYPE##_funcs)); \
JS_SetPropertyStr(js, TYPE##_proto, "memid", JS_NewCFunction(js, &js_##TYPE##_memid, "memid", 0)); \
JS_SetPropertyStr(js, TYPE##_proto, "memsize", JS_NewCFunction(js, &js_##TYPE##_memsize, "memsize", 0)); \
JS_SetClassProto(js, js_##TYPE##_id, TYPE##_proto); \
#define countof(x) (sizeof(x)/sizeof((x)[0]))
@ -150,17 +155,27 @@ uint64_t js2uint64(JSValue v);
JSValue str2js(const char *c, ...);
struct texture;
struct texture *js2texture(JSValue v);
void nota_int(char *blob);
void js_setprop_num(JSValue a, uint32_t n, JSValue v);
JSValue js_getpropidx(JSValue v, uint32_t i);
JSValue js_getpropstr(JSValue v, const char *str);
void jsfreestr(const char *str);
int js_arrlen(JSValue v);
void js_setpropstr(JSValue v, const char *str, JSValue p);
void js2floatarr(JSValue v, int n, float *a);
JSValue floatarr2js(int n, float *a);
float *js2newfloatarr(JSValue v);
int js2boolean(JSValue v);
JSValue boolean2js(int b);
char **js2strarr(JSValue v);
#define PREP_PARENT(TYPE, PARENT) \
TYPE##_proto = JS_NewObject(js); \
JS_SetPropertyFunctionList(js, TYPE##_proto, js_##TYPE##_funcs, countof(js_##TYPE##_funcs)); \

View file

@ -83,7 +83,7 @@ void mYughLog(int category, int priority, int line, const char *file, const char
vfprintf(logout, message, args);
va_end(args);
fprintf(logout, "\n");
if (priority == LOG_DEBUG || priority >= stdout_lvl) {
printf(logfmt, file, line, timebuf, logcolor[priority], catstr[category]);
va_list args;
@ -108,15 +108,19 @@ void mYughLog(int category, int priority, int line, const char *file, const char
void log_print(const char *str)
{
#ifndef NDEBUG
fprintf(stdout, "%s", str);
fprintf(writeout, "%s", str);
fflush(stdout);
#else
printf(str);
fflush(stdout);
#endif
}
void term_print(const char *str)
{
fprintf(stdout, "%s", str);
fflush(stdout);
}
void sg_logging(const char *tag, uint32_t lvl, uint32_t id, const char *msg, uint32_t line, const char *file, void *data) {
lvl = LOG_PANIC-lvl;
mYughLog(LOG_RENDER, lvl, line, file, "tag: %s, id: %d, msg: %s", tag, id, msg);

View file

@ -41,5 +41,6 @@ void mYughLog(int category, int priority, int line, const char *file, const char
void sg_logging(const char *tag, uint32_t lvl, uint32_t id, const char *msg, uint32_t line, const char *file, void *data);
void log_print(const char *str);
void term_print(const char *str);
#endif

View file

@ -25,47 +25,10 @@
#include "sokol/sokol_gfx.h"
static void processnode();
static void processmesh();
static void processtexture();
static cgltf_data *cdata;
static char *cpath;
cgltf_attribute *get_attr_type(cgltf_primitive *p, cgltf_attribute_type t)
{
for (int i = 0; i < p->attributes_count; i++) {
if (p->attributes[i].type == t)
return &p->attributes[i];
}
return NULL;
}
#include "jsffi.h"
unsigned short pack_short_tex(float c) { return c * USHRT_MAX; }
void mesh_add_material(primitive *prim, cgltf_material *mat)
{
if (!mat) return;
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;
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
pmat->diffuse = texture_from_file("icons/moon.gif");
}
sg_buffer texcoord_floats(float *f, int n)
{
unsigned short packed[n];
@ -125,6 +88,7 @@ uint32_t pack_int10_n2(float *norm)
return ret;
}
// Pack an array of normals into
sg_buffer normal_floats(float *f, int n)
{
return float_buffer(f, n);
@ -156,196 +120,61 @@ sg_buffer ubyte_buffer(float *f, int v)
return sg_make_buffer(&(sg_buffer_desc){.data=SG_RANGE(b)});
}
sg_buffer joint_buf(float *f, int v)
sg_buffer accessor2buffer(cgltf_accessor *a, int type)
{
char joints[v];
for (int i = 0; i < (v); i++)
joints[i] = f[i];
return sg_make_buffer(&(sg_buffer_desc){ .data = SG_RANGE(joints)});
}
int n = cgltf_accessor_unpack_floats(a, NULL, 0);
float vs[n];
cgltf_accessor_unpack_floats(a, vs, n);
sg_buffer weight_buf(float *f, int v)
{
unsigned char weights[v];
for (int i = 0; i < (v); i++)
weights[i] = f[i]*255;
return sg_make_buffer(&(sg_buffer_desc){ .data = SG_RANGE(weights)});
}
HMM_Vec3 index_to_vert(uint32_t idx, float *f)
{
return (HMM_Vec3){f[idx*3], f[idx*3+1], f[idx*3+2]};
}
void primitive_gen_indices(primitive *prim)
{
if (prim->idx_count != 0) return;
uint16_t *idxs = malloc(sizeof(*idxs)*prim->idx_count);
for (int z = 0; z < prim->idx_count; z++)
idxs[z] = z;
prim->index = sg_make_buffer(&(sg_buffer_desc){
.data.ptr = idxs,
.data.size = sizeof(uint16_t) * prim->idx_count,
.type = SG_BUFFERTYPE_INDEXBUFFER});
free(idxs);
}
struct primitive mesh_add_primitive(cgltf_primitive *prim)
{
primitive retp = (primitive){0};
uint16_t *idxs;
if (prim->indices) {
int n = cgltf_accessor_unpack_floats(prim->indices, NULL, 0);
float fidx[n];
cgltf_accessor_unpack_floats(prim->indices, fidx, n);
idxs = malloc(sizeof(*idxs)*n);
for (int i = 0; i < n; i++)
idxs[i] = fidx[i];
retp.index = sg_make_buffer(&(sg_buffer_desc){
.data.ptr = idxs,
.data.size = sizeof(*idxs) * n,
.type = SG_BUFFERTYPE_INDEXBUFFER,
.label = "mesh index buffer",
});
retp.idx_count = n;
free(idxs);
} else {
retp.idx_count = cgltf_accessor_unpack_floats(prim->attributes[0].data, NULL, 0);
primitive_gen_indices(&retp);
}
printf("adding material\n");
mesh_add_material(&retp, prim->material);
for (int k = 0; k < prim->attributes_count; k++) {
cgltf_attribute attribute = prim->attributes[k];
int n = cgltf_accessor_unpack_floats(attribute.data, NULL, 0); /* floats per vertex x num elements. In other words, total floats pulled */
int comp = cgltf_num_components(attribute.data->type);
int verts = n/comp;
float vs[n];
cgltf_accessor_unpack_floats(attribute.data, vs, n);
switch (attribute.type) {
case cgltf_attribute_type_position:
retp.pos = sg_make_buffer(&(sg_buffer_desc){
switch(type) {
case MAT_POS:
return sg_make_buffer(&(sg_buffer_desc){
.data.ptr = vs,
.data.size = sizeof(float) * n,
.label = "mesh vert buffer"
});
break;
case cgltf_attribute_type_normal:
retp.norm = normal_floats(vs, n);
break;
case cgltf_attribute_type_tangent:
break;
case cgltf_attribute_type_color:
retp.color = ubyten_buffer(vs,n);
break;
case cgltf_attribute_type_weights:
retp.weight = ubyten_buffer(vs, n);
break;
case cgltf_attribute_type_joints:
retp.bone = ubyte_buffer(vs, n);
break;
case cgltf_attribute_type_texcoord:
retp.uv = texcoord_floats(vs, n);
break;
case cgltf_attribute_type_invalid:
YughWarn("Invalid type.");
break;
case cgltf_attribute_type_custom:
break;
case cgltf_attribute_type_max_enum:
break;
}
.data.size = sizeof(float)*n
});
case MAT_NORM:
return normal_floats(vs,n);
case MAT_TAN:
return normal_floats(vs,n); // TODO: MAKE A TANGENT READER
case MAT_COLOR:
return ubyten_buffer(vs,n);
case MAT_WEIGHT:
return ubyten_buffer(vs,n);
case MAT_BONE:
return ubyte_buffer(vs,n);
case MAT_UV:
return texcoord_floats(vs,n);
case MAT_INDEX:
return index_buffer(vs,n);
}
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);
int comp = 3;
int verts = n/comp;
uint32_t face_norms[verts];
float ps[n];
cgltf_accessor_unpack_floats(pa->data,ps,n);
for (int i = 0; i < verts; i+=3) {
HMM_Vec3 a = index_to_vert(i,ps);
HMM_Vec3 b = index_to_vert(i+1,ps);
HMM_Vec3 c = index_to_vert(i+2,ps);
HMM_Vec3 norm = HMM_NormV3(HMM_Cross(HMM_SubV3(b,a), HMM_SubV3(c,a)));
uint32_t packed_norm = pack_int10_n2(norm.Elements);
face_norms[i] = face_norms[i+1] = face_norms[i+2] = packed_norm;
}
retp.norm = sg_make_buffer(&(sg_buffer_desc){
.data.ptr = face_norms,
.data.size = sizeof(uint32_t) * verts});
}
return retp;
}
void model_add_cgltf_mesh(mesh *m, cgltf_mesh *gltf_mesh)
{
for (int i = 0; i < gltf_mesh->primitives_count; i++)
arrput(m->primitives, mesh_add_primitive(gltf_mesh->primitives+i));
return sg_make_buffer(&(sg_buffer_desc) {
.data.size = 4,
.usage = SG_USAGE_STREAM
});
}
void packFloats(float *src, float *dest, int srcLength) {
int i, j;
for (i = 0, j = 0; i < srcLength; i += 3, j += 4) {
dest[j] = src[i];
dest[j + 1] = src[i + 1];
dest[j + 2] = src[i + 2];
dest[j + 3] = 0.0f;
}
int i, j;
for (i = 0, j = 0; i < srcLength; i += 3, j += 4) {
dest[j] = src[i];
dest[j + 1] = src[i + 1];
dest[j + 2] = src[i + 2];
dest[j + 3] = 0.0f;
}
}
void model_add_cgltf_anim(model *model, cgltf_animation *anim)
static md5joint *node2joint(skin *sk, cgltf_node *n, cgltf_skin *skin)
{
YughInfo("FOUND ANIM, using %d channels and %d samplers", anim->channels_count, anim->samplers_count);
struct animation an = (struct animation){0};
int k = 0;
while (skin->joints[k] != n && k < skin->joints_count) k++;
return sk->joints+k;
}
animation *gltf_anim(cgltf_animation *anim, skin *sk, cgltf_skin *skin)
{
animation *ret = calloc(sizeof(*ret), 1);
animation an = *ret;
arrsetlen(an.samplers, anim->samplers_count);
for (int i = 0; i < anim->samplers_count; i++) {
@ -378,7 +207,7 @@ void model_add_cgltf_anim(model *model, cgltf_animation *anim)
for (int i = 0; i < anim->channels_count; i++) {
cgltf_animation_channel ch = anim->channels[i];
struct anim_channel ach = (struct anim_channel){0};
md5joint *md = model->nodes+(ch.target_node-cdata->nodes);
md5joint *md = node2joint(sk, ch.target_node, skin);
switch(ch.target_path) {
case cgltf_animation_path_type_translation:
ach.target = &md->pos;
@ -395,32 +224,35 @@ void model_add_cgltf_anim(model *model, cgltf_animation *anim)
arrput(an.channels, ach);
}
model->anim = an;
model->anim.time = apptime();
an.time = 0;
*ret = an;
return ret;
}
void model_add_cgltf_skin(model *model, cgltf_skin *skin)
skin *make_gltf_skin(cgltf_skin *skin, cgltf_data *data)
{
int n = cgltf_accessor_unpack_floats(skin->inverse_bind_matrices, NULL, 0);
struct skin sk = (struct skin){0};
arrsetlen(sk.invbind, n/16);
cgltf_accessor_unpack_floats(skin->inverse_bind_matrices, sk.invbind, n);
YughInfo("FOUND SKIN, of %d bones, and %d vert comps", skin->joints_count, n);
struct skin *sk = NULL;
sk = calloc(sizeof(*sk),1);
arrsetlen(sk->invbind, n/16);
cgltf_accessor_unpack_floats(skin->inverse_bind_matrices, sk->invbind, n);
cgltf_node *root = skin->skeleton;
arrsetlen(sk.joints, skin->joints_count);
sk.root = model->nodes+(skin->skeleton-cdata->nodes);
arrsetlen(sk->joints, skin->joints_count);
for (int i = 0; i < 50; i++)
sk.binds[i] = MAT1;
sk->binds[i] = MAT1;
for (int i = 0; i < skin->joints_count; i++) {
int offset = skin->joints[i]-cdata->nodes;
sk.joints[i] = model->nodes+offset;
md5joint *j = sk.joints[i];
cgltf_node *n = skin->joints[i];
md5joint *j = sk->joints+i;
if (n == skin->skeleton)
j->parent = NULL;
else
j->parent = node2joint(sk, n->parent, skin);
for (int i = 0; i < 3; i++) {
j->pos.e[i] = n->translation[i];
j->scale.e[i] = n->scale[i];
@ -428,197 +260,21 @@ void model_add_cgltf_skin(model *model, cgltf_skin *skin)
for (int i = 0; i < 4; i++)
j->rot.e[i] = n->rotation[i];
}
model->skin = sk;
sk->anim = gltf_anim(data->animations+0, sk, skin);
return sk;
}
void model_process_node(model *model, cgltf_node *node)
void skin_calculate(skin *sk)
{
int n = node-cdata->nodes;
cgltf_node_transform_world(node, model->nodes[n].t.e);
model->nodes[n].parent = model->nodes+(node->parent-cdata->nodes);
if (node->mesh) {
int meshn = node->mesh-cdata->meshes;
arrsetlen(model->meshes, meshn+1);
model->meshes[meshn].m = &model->nodes[n].t;
model_add_cgltf_mesh(model->meshes+meshn, node->mesh);
}
}
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);
struct model *model = NULL;
if (result) {
YughError("CGLTF could not parse file %s, err %d.", path, result);
goto CLEAN;
}
result = cgltf_load_buffers(&options, data, path);
if (result) {
YughError("CGLTF could not load buffers for file %s, err %d.", path, result);
goto CLEAN;
}
cdata = data;
model = calloc(1, sizeof(*model));
arrsetlen(model->nodes, data->nodes_count);
for (int i = 0; i < data->nodes_count; i++)
model_process_node(model, data->nodes+i);
for (int i = 0; i < data->animations_count; i++)
model_add_cgltf_anim(model, data->animations+i);
for (int i = 0; i < data->skins_count; i++)
model_add_cgltf_skin(model, data->skins+i);
CLEAN:
cgltf_free(data);
return model;
}
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->index;
b.fs.images[0] = p->mat->diffuse->id;
b.fs.samplers[0] = tex_sampler;
return b;
}
void model_draw_go(model *model, transform *go)
{
HMM_Mat4 gom = transform2mat(go);
animation_run(&model->anim, apptime());
skin *sk = &model->skin;
animation_run(sk->anim, apptime());
for (int i = 0; i < arrlen(sk->joints); i++) {
md5joint *md = sk->joints[i];
HMM_Mat4 local = HMM_M4TRS(md->pos.xyz, md->rot, md->scale.xyz);
md5joint *md = sk->joints+i;
md->t = HMM_M4TRS(md->pos.xyz, md->rot, md->scale.xyz);
if (md->parent)
local = HMM_MulM4(md->parent->t, local);
md->t = local;
md->t = HMM_MulM4(md->parent->t, md->t);
sk->binds[i] = HMM_MulM4(md->t, sk->invbind[i]);
}
/*sg_apply_uniforms(SG_SHADERSTAGE_VS, SLOT_skinv, &(sg_range){
.ptr = sk->binds,
.size = sizeof(*sk->binds)*50
});
*/
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_bindings b = primitive_bind(msh.primitives+j);
sg_apply_bindings(&b);
sg_draw(0, msh.primitives[j].idx_count, 1);
}
}
}
int mat2type(int mat)
{
switch(mat) {
case MAT_POS:
case MAT_NORM:
return SG_VERTEXFORMAT_FLOAT3;
case MAT_PPOS:
case MAT_WH:
case MAT_ST:
return SG_VERTEXFORMAT_FLOAT2;
case MAT_UV:
case MAT_TAN:
return SG_VERTEXFORMAT_USHORT2N;
return SG_VERTEXFORMAT_UINT10_N2;
case MAT_BONE:
return SG_VERTEXFORMAT_UBYTE4;
case MAT_WEIGHT:
case MAT_COLOR:
return SG_VERTEXFORMAT_UBYTE4N;
case MAT_ANGLE:
case MAT_SCALE:
return SG_VERTEXFORMAT_FLOAT;
};
return 0;
}
int mat2step(int mat)
{
switch(mat) {
case MAT_POS:
case MAT_UV:
case MAT_TAN:
case MAT_NORM:
case MAT_BONE:
case MAT_WEIGHT:
return SG_VERTEXSTEP_PER_VERTEX;
};
return SG_VERTEXSTEP_PER_INSTANCE;
}
sg_buffer mat2buffer(int mat, primitive *p)
{
switch(mat) {
case MAT_POS: return p->pos;
case MAT_NORM: return p->norm;
case MAT_UV: return p->uv;
case MAT_BONE: return p->bone;
case MAT_WEIGHT: return p->weight;
case MAT_COLOR: return p->color;
};
return p->pos;
}
sg_bindings primitive_bindings(primitive *p, JSValue v)
{
sg_bindings b = {0};
JSValue inputs = js_getpropstr(js_getpropstr(v, "vs"), "inputs");
for (int i = 0; i < js_arrlen(inputs); i++) {
JSValue attr = js_getpropidx(inputs, i);
int mat = js2number(js_getpropstr(attr, "mat"));
int slot = js2number(js_getpropstr(attr, "slot"));
sg_buffer buf = mat2buffer(mat,p);
if (!buf.id) {
// ERROR
}
b.vertex_buffers[slot] = buf;
}
b.index_buffer = p->index;
return b;
}
void primitive_free(primitive *prim)
{
}
void material_free(material *mat)
{
}

View file

@ -7,6 +7,7 @@
#include "gameobject.h"
#include "anim.h"
#include "texture.h"
#include "cgltf.h"
#define MAT_POS 0
#define MAT_UV 1
@ -20,44 +21,7 @@
#define MAT_ST 9
#define MAT_PPOS 10
#define MAT_SCALE 11
typedef struct material {
struct texture *diffuse;
struct texture *metalrough;
float metal;
float rough;
struct texture *normal;
float nrm;
struct texture *occlusion;
float occl;
struct texture *emissive;
HMM_Vec3 emis;
} material;
struct model;
typedef struct primitive {
sg_buffer pos;
sg_buffer norm;
sg_buffer uv;
sg_buffer bone;
sg_buffer weight;
sg_buffer color;
sg_buffer index;
material *mat;
uint32_t idx_count;
} primitive;
/* A single mesh */
typedef struct mesh {
primitive *primitives;
HMM_Mat4 *m;
} mesh;
typedef struct joint {
int me;
struct joint *children;
} joint_t;
#define MAT_INDEX 100
typedef struct md5joint {
struct md5joint *parent;
@ -68,29 +32,15 @@ typedef struct md5joint {
} md5joint;
typedef struct skin {
md5joint **joints;
md5joint *joints;
HMM_Mat4 *invbind;
HMM_Mat4 binds[50]; /* binds = joint * invbind */
md5joint *root;
animation *anim;
} skin;
/* A collection of meshes which create a full figure */
typedef struct model {
struct mesh *meshes;
md5joint *nodes;
material *mats;
skin skin;
struct animation anim;
} model;
/* Make a Model struct */
struct model *model_make(const char *path);
void model_free(model *m);
void model_draw_go(model *m, transform *go);
sg_bindings primitive_bindings(primitive *p, JSValue pipe);
void primitive_gen_indices(primitive *prim);
int mat2type(int mat);
sg_buffer accessor2buffer(cgltf_accessor *a, int type);
skin *make_gltf_skin(cgltf_skin *skin, cgltf_data *data);
void skin_calculate(skin *sk);
sg_buffer float_buffer(float *f, int v);
sg_buffer index_buffer(float *f, int verts);
@ -101,9 +51,5 @@ sg_buffer ubyten_buffer(float *f, int v);
sg_buffer ubyte_buffer(float *f, int v);
sg_buffer joint_buf(float *f, int v);
sg_buffer weight_buf(float *f, int v);
void primitive_free(primitive *prim);
material *material_make();
void material_free(material *mat);
#endif

View file

@ -57,35 +57,29 @@ void trace_begin_pass(sg_pass pass, const sg_pass_action *action, void *data)
void trace_alloc_##NAME (sg_##NAME id, void *data) \
{ \
sg_##NAME##_desc desc = sg_query_##NAME##_desc(id); \
YughSpam("Alloc " #NAME " %d [%s]", id, desc.label); \
} \
\
void trace_dealloc_##NAME(sg_##NAME id, void *data) \
{ \
sg_##NAME##_desc desc = sg_query_##NAME##_desc(id); \
YughSpam("Dealloc " #NAME " %d [%s]", id, desc.label); \
} \
\
void trace_make_##NAME(sg_##NAME##_desc *desc, void *data) \
{ \
YughSpam("Make " #NAME " [%s]", desc->label); \
} \
\
void trace_destroy_##NAME(sg_##NAME id, void *data) \
{ \
sg_##NAME##_desc desc = sg_query_##NAME##_desc(id); \
YughSpam("Destroy " #NAME " %d [%s]", id, desc.label); \
} \
\
void trace_init_##NAME(sg_##NAME id, sg_##NAME##_desc *desc, void *data) \
{ \
YughSpam("Init " #NAME " %d [%s]", id, desc->label); \
} \
\
void trace_uninit_##NAME(sg_##NAME id, void *data) \
{ \
sg_##NAME##_desc desc = sg_query_##NAME##_desc(id); \
YughSpam("Init " #NAME " %d [%s]", id, desc.label); \
} \
\
void trace_fail_##NAME(sg_##NAME id, void *data) \
@ -132,7 +126,8 @@ void render_init() {
sg_setup(&(sg_desc){
.environment = sglue_environment(),
.logger = { .func = sg_logging },
.buffer_pool_size = 1024
.buffer_pool_size = 1024,
.image_pool_size = 1024,
});
std_sampler = sg_make_sampler(&(sg_sampler_desc){});

View file

@ -158,8 +158,10 @@ time_t file_mod_secs(const char *file) {
struct stat attr;
mz_uint index;
mz_zip_archive_file_stat pstat;
if ((index = mz_zip_reader_locate_file(&game_cdb, file, NULL, 0)) != -1) {
if (!stat(file,&attr))
return attr.st_mtime;
else if ((index = mz_zip_reader_locate_file(&game_cdb, file, NULL, 0)) != -1) {
mz_zip_reader_file_stat(&game_cdb, index,&pstat);
return pstat.m_time;
}
@ -167,10 +169,8 @@ time_t file_mod_secs(const char *file) {
mz_zip_reader_file_stat(&corecdb, index, &pstat);
return pstat.m_time;
}
else
stat(file, &attr);
return attr.st_mtime;
return -1;
}
// TODO: Not reentrant

View file

@ -16,6 +16,8 @@ JSRuntime *rt = NULL;
#define JS_EVAL_FLAGS JS_EVAL_FLAG_STRICT | JS_EVAL_FLAG_STRIP
#endif
static JSValue report_gc;
void script_startup() {
rt = JS_NewRuntime();
js = JS_NewContext(rt);
@ -44,6 +46,9 @@ return;
}
void script_gc() { JS_RunGC(rt); }
void script_mem_limit(size_t limit) { JS_SetMemoryLimit(rt, limit); }
void script_gc_threshold(size_t threshold) { JS_SetGCThreshold(rt, threshold); }
void script_max_stacksize(size_t size) { JS_SetMaxStackSize(rt, size); }
void js_stacktrace() {
if (!js) return;
@ -52,6 +57,7 @@ void js_stacktrace() {
#endif
}
void script_evalf(const char *format, ...)
{
JSValue obj;

View file

@ -30,6 +30,14 @@ JSValue script_eval(const char *file, const char *script);
void script_call_sym(JSValue sym, int argc, JSValue *argv);
void script_gc();
void script_mem_limit(size_t limit);
void script_gc_threshold(size_t threshold);
void script_max_stacksize(size_t size);
void _script_gcstart();
void _script_gcend();
#ifdef __cplusplus
}

View file

@ -117,6 +117,8 @@ struct texture *texture_from_file(const char *path) {
return NULL;
tex->data = data;
tex->size = tex->width*tex->height*4;
tex->gpusize = tex->size;
unsigned int nw = next_pow2(tex->width);
unsigned int nh = next_pow2(tex->height);
@ -142,6 +144,7 @@ struct texture *texture_from_file(const char *path) {
mipdata[i] = malloc(w * h * 4);
stbir_resize_uint8_linear(mipdata[i-1], mipw, miph, 0, mipdata[i], w, h, 0, 4);
sg_img_data.subimage[0][i] = (sg_range){ .ptr = mipdata[i], .size = w*h*4 };
tex->gpusize += w*h*4;
mipw = w;
miph = h;
@ -158,6 +161,8 @@ struct texture *texture_from_file(const char *path) {
for (int i = 1; i < mips; i++)
free(mipdata[i]);
return tex;
}
@ -166,7 +171,7 @@ void texture_free(texture *tex)
{
if (!tex) return;
if (tex->data)
free(tex->data);
free(tex->data);
if (tex->delays) arrfree(tex->delays);
sg_destroy_image(tex->id);
free(tex);
@ -292,16 +297,21 @@ void texture_save(texture *tex, const char *file)
stbi_write_jpg(file, tex->width, tex->height, 4, tex->data, 5);
}
// all coordinates start at bottom left
// src and dest, width, height are pixel buffers and their widths and heights
// sx the x coordinate of the destination to copy to
// sy the y coordinate of the destination to copy to
// sw the width of the destination to take in pixels
// sh the height of the destination to take in pixels
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) {
if (sx + sw > dest_width) return;
if (sy + sh > dest_height) return;
int src_stride = src_width * 4;
int dest_stride = dest_width * 4;
// if (sx + sw > dest_width) return;
// if (sy + sh > dest_height) return;
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);
for (int x = 0; x < sw; x++) {
for (int y = 0; y < sh; y++) {
int src_index = ((y * src_width) + x ) * 4;
int dest_index = ((y + sy) * dest_width) + (x + sx);
dest_index *= 4;
// Calculate the alpha value for the source pixel
uint8_t src_alpha = src[src_index + 3];
@ -326,11 +336,10 @@ void blit_image(uint8_t* src, uint8_t* dest, int src_width, int src_height, int
}
}
// Function to draw source image pixels on top of a destination image
// x,y are the pixel coordinates in the destination image, w,h are the amount of pixels to take from the src 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);
blit_image(src->data, dest->data, src->width, src->height, dest->width, dest->height, x, y, w, h);
}
void texture_flip(texture *tex, int y)

View file

@ -23,6 +23,8 @@ struct texture {
int width;
int height;
unsigned char *data;
int size;
int gpusize;
int frames;
int *delays;
};
@ -44,7 +46,7 @@ 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);
void texture_save(texture *tex, const char *file); // save the texture data to the given file
double perlin(double x, double y, double z);

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,151 @@
// https://github.com/CedricGuillemet/ImGuizmo
// v 1.89 WIP
//
// The MIT License(MIT)
//
// Copyright(c) 2021 Cedric Guillemet
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files(the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and / or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions :
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
//
#pragma once
#include <vector>
#include <stdint.h>
#include <string>
#include "imgui.h"
#include "imgui_internal.h"
namespace GraphEditor {
typedef size_t NodeIndex;
typedef size_t SlotIndex;
typedef size_t LinkIndex;
typedef size_t TemplateIndex;
// Force the view to be respositionned and zoom to fit nodes with Show function.
// Parameter value will be changed to Fit_None by the function.
enum FitOnScreen
{
Fit_None,
Fit_AllNodes,
Fit_SelectedNodes
};
// Display options and colors
struct Options
{
ImRect mMinimap{{0.75f, 0.8f, 0.99f, 0.99f}}; // rectangle coordinates of minimap
ImU32 mBackgroundColor{ IM_COL32(40, 40, 40, 255) }; // full background color
ImU32 mGridColor{ IM_COL32(0, 0, 0, 60) }; // grid lines color
ImU32 mGridColor2{ IM_COL32(0, 0, 0, 160) }; // grid lines color every 10th
ImU32 mSelectedNodeBorderColor{ IM_COL32(255, 130, 30, 255) }; // node border color when it's selected
ImU32 mNodeBorderColor{ IM_COL32(100, 100, 100, 0) }; // node border color when it's not selected
ImU32 mQuadSelection{ IM_COL32(255, 32, 32, 64) }; // quad selection inside color
ImU32 mQuadSelectionBorder{ IM_COL32(255, 32, 32, 255) }; // quad selection border color
ImU32 mDefaultSlotColor{ IM_COL32(128, 128, 128, 255) }; // when no color is provided in node template, use this value
ImU32 mFrameFocus{ IM_COL32(64, 128, 255, 255) }; // rectangle border when graph editor has focus
float mLineThickness{ 5 }; // links width in pixels when zoom value is 1
float mGridSize{ 64.f }; // background grid size in pixels when zoom value is 1
float mRounding{ 3.f }; // rounding at node corners
float mZoomRatio{ 0.1f }; // factor per mouse wheel delta
float mZoomLerpFactor{ 0.25f }; // the smaller, the smoother
float mBorderSelectionThickness{ 6.f }; // thickness of selection border around nodes
float mBorderThickness{ 6.f }; // thickness of selection border around nodes
float mNodeSlotRadius{ 8.f }; // circle radius for inputs and outputs
float mNodeSlotHoverFactor{ 1.2f }; // increase size when hovering
float mMinZoom{ 0.2f }, mMaxZoom { 1.1f };
float mSnap{ 5.f };
bool mDisplayLinksAsCurves{ true }; // false is straight and 45deg lines
bool mAllowQuadSelection{ true }; // multiple selection using drag and drop
bool mRenderGrid{ true }; // grid or nothing
bool mDrawIONameOnHover{ true }; // only draw node input/output when hovering
};
// View state: scroll position and zoom factor
struct ViewState
{
ImVec2 mPosition{0.0f, 0.0f}; // scroll position
float mFactor{ 1.0f }; // current zoom factor
float mFactorTarget{ 1.0f }; // targeted zoom factor interpolated using Options.mZoomLerpFactor
};
struct Template
{
ImU32 mHeaderColor;
ImU32 mBackgroundColor;
ImU32 mBackgroundColorOver;
ImU8 mInputCount;
const char** mInputNames; // can be nullptr. No text displayed.
ImU32* mInputColors; // can be nullptr, default slot color will be used.
ImU8 mOutputCount;
const char** mOutputNames; // can be nullptr. No text displayed.
ImU32* mOutputColors; // can be nullptr, default slot color will be used.
};
struct Node
{
const char* mName;
TemplateIndex mTemplateIndex;
ImRect mRect;
bool mSelected{ false };
};
struct Link
{
NodeIndex mInputNodeIndex;
SlotIndex mInputSlotIndex;
NodeIndex mOutputNodeIndex;
SlotIndex mOutputSlotIndex;
};
struct Delegate
{
virtual bool AllowedLink(NodeIndex from, NodeIndex to) = 0;
virtual void SelectNode(NodeIndex nodeIndex, bool selected) = 0;
virtual void MoveSelectedNodes(const ImVec2 delta) = 0;
virtual void AddLink(NodeIndex inputNodeIndex, SlotIndex inputSlotIndex, NodeIndex outputNodeIndex, SlotIndex outputSlotIndex) = 0;
virtual void DelLink(LinkIndex linkIndex) = 0;
// user is responsible for clipping
virtual void CustomDraw(ImDrawList* drawList, ImRect rectangle, NodeIndex nodeIndex) = 0;
// use mouse position to open context menu
// if nodeIndex != -1, right click happens on the specified node
virtual void RightClick(NodeIndex nodeIndex, SlotIndex slotIndexInput, SlotIndex slotIndexOutput) = 0;
virtual const size_t GetTemplateCount() = 0;
virtual const Template GetTemplate(TemplateIndex index) = 0;
virtual const size_t GetNodeCount() = 0;
virtual const Node GetNode(NodeIndex index) = 0;
virtual const size_t GetLinkCount() = 0;
virtual const Link GetLink(LinkIndex index) = 0;
virtual ~Delegate() = default;
};
void Show(Delegate& delegate, const Options& options, ViewState& viewState, bool enabled, FitOnScreen* fit = nullptr);
void GraphEditorClear();
bool EditOptions(Options& options);
} // namespace

View file

@ -0,0 +1,457 @@
// https://github.com/CedricGuillemet/ImGuizmo
// v 1.89 WIP
//
// The MIT License(MIT)
//
// Copyright(c) 2021 Cedric Guillemet
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files(the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and / or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions :
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
//
#include "ImCurveEdit.h"
#include "imgui.h"
#include "imgui_internal.h"
#include <stdint.h>
#include <set>
#include <vector>
#if defined(_MSC_VER) || defined(__MINGW32__)
#include <malloc.h>
#endif
#if !defined(_MSC_VER) && !defined(__MINGW64_VERSION_MAJOR)
#define _malloca(x) alloca(x)
#define _freea(x)
#endif
namespace ImCurveEdit
{
#ifndef IMGUI_DEFINE_MATH_OPERATORS
static ImVec2 operator+(const ImVec2& a, const ImVec2& b) {
return ImVec2(a.x + b.x, a.y + b.y);
}
static ImVec2 operator-(const ImVec2& a, const ImVec2& b) {
return ImVec2(a.x - b.x, a.y - b.y);
}
static ImVec2 operator*(const ImVec2& a, const ImVec2& b) {
return ImVec2(a.x * b.x, a.y * b.y);
}
static ImVec2 operator/(const ImVec2& a, const ImVec2& b) {
return ImVec2(a.x / b.x, a.y / b.y);
}
static ImVec2 operator*(const ImVec2& a, const float b) {
return ImVec2(a.x * b, a.y * b);
}
#endif
static float smoothstep(float edge0, float edge1, float x)
{
x = ImClamp((x - edge0) / (edge1 - edge0), 0.0f, 1.0f);
return x * x * (3 - 2 * x);
}
static float distance(float x, float y, float x1, float y1, float x2, float y2)
{
float A = x - x1;
float B = y - y1;
float C = x2 - x1;
float D = y2 - y1;
float dot = A * C + B * D;
float len_sq = C * C + D * D;
float param = -1.f;
if (len_sq > FLT_EPSILON)
param = dot / len_sq;
float xx, yy;
if (param < 0.f) {
xx = x1;
yy = y1;
}
else if (param > 1.f) {
xx = x2;
yy = y2;
}
else {
xx = x1 + param * C;
yy = y1 + param * D;
}
float dx = x - xx;
float dy = y - yy;
return sqrtf(dx * dx + dy * dy);
}
static int DrawPoint(ImDrawList* draw_list, ImVec2 pos, const ImVec2 size, const ImVec2 offset, bool edited)
{
int ret = 0;
ImGuiIO& io = ImGui::GetIO();
static const ImVec2 localOffsets[4] = { ImVec2(1,0), ImVec2(0,1), ImVec2(-1,0), ImVec2(0,-1) };
ImVec2 offsets[4];
for (int i = 0; i < 4; i++)
{
offsets[i] = pos * size + localOffsets[i] * 4.5f + offset;
}
const ImVec2 center = pos * size + offset;
const ImRect anchor(center - ImVec2(5, 5), center + ImVec2(5, 5));
draw_list->AddConvexPolyFilled(offsets, 4, 0xFF000000);
if (anchor.Contains(io.MousePos))
{
ret = 1;
if (io.MouseDown[0])
ret = 2;
}
if (edited)
draw_list->AddPolyline(offsets, 4, 0xFFFFFFFF, true, 3.0f);
else if (ret)
draw_list->AddPolyline(offsets, 4, 0xFF80B0FF, true, 2.0f);
else
draw_list->AddPolyline(offsets, 4, 0xFF0080FF, true, 2.0f);
return ret;
}
int Edit(Delegate& delegate, const ImVec2& size, unsigned int id, const ImRect* clippingRect, ImVector<EditPoint>* selectedPoints)
{
static bool selectingQuad = false;
static ImVec2 quadSelection;
static int overCurve = -1;
static int movingCurve = -1;
static bool scrollingV = false;
static std::set<EditPoint> selection;
static bool overSelectedPoint = false;
int ret = 0;
ImGuiIO& io = ImGui::GetIO();
ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(0, 0));
ImGui::PushStyleColor(ImGuiCol_Border, 0);
ImGui::BeginChildFrame(id, size);
delegate.focused = ImGui::IsWindowFocused();
ImDrawList* draw_list = ImGui::GetWindowDrawList();
if (clippingRect)
draw_list->PushClipRect(clippingRect->Min, clippingRect->Max, true);
const ImVec2 offset = ImGui::GetCursorScreenPos() + ImVec2(0.f, size.y);
const ImVec2 ssize(size.x, -size.y);
const ImRect container(offset + ImVec2(0.f, ssize.y), offset + ImVec2(ssize.x, 0.f));
ImVec2& min = delegate.GetMin();
ImVec2& max = delegate.GetMax();
// handle zoom and VScroll
if (container.Contains(io.MousePos))
{
if (fabsf(io.MouseWheel) > FLT_EPSILON)
{
const float r = (io.MousePos.y - offset.y) / ssize.y;
float ratioY = ImLerp(min.y, max.y, r);
auto scaleValue = [&](float v) {
v -= ratioY;
v *= (1.f - io.MouseWheel * 0.05f);
v += ratioY;
return v;
};
min.y = scaleValue(min.y);
max.y = scaleValue(max.y);
}
if (!scrollingV && ImGui::IsMouseDown(2))
{
scrollingV = true;
}
}
ImVec2 range = max - min + ImVec2(1.f, 0.f); // +1 because of inclusive last frame
const ImVec2 viewSize(size.x, -size.y);
const ImVec2 sizeOfPixel = ImVec2(1.f, 1.f) / viewSize;
const size_t curveCount = delegate.GetCurveCount();
if (scrollingV)
{
float deltaH = io.MouseDelta.y * range.y * sizeOfPixel.y;
min.y -= deltaH;
max.y -= deltaH;
if (!ImGui::IsMouseDown(2))
scrollingV = false;
}
draw_list->AddRectFilled(offset, offset + ssize, delegate.GetBackgroundColor());
auto pointToRange = [&](ImVec2 pt) { return (pt - min) / range; };
auto rangeToPoint = [&](ImVec2 pt) { return (pt * range) + min; };
draw_list->AddLine(ImVec2(-1.f, -min.y / range.y) * viewSize + offset, ImVec2(1.f, -min.y / range.y) * viewSize + offset, 0xFF000000, 1.5f);
bool overCurveOrPoint = false;
int localOverCurve = -1;
// make sure highlighted curve is rendered last
int* curvesIndex = (int*)_malloca(sizeof(int) * curveCount);
for (size_t c = 0; c < curveCount; c++)
curvesIndex[c] = int(c);
int highLightedCurveIndex = -1;
if (overCurve != -1 && curveCount)
{
ImSwap(curvesIndex[overCurve], curvesIndex[curveCount - 1]);
highLightedCurveIndex = overCurve;
}
for (size_t cur = 0; cur < curveCount; cur++)
{
int c = curvesIndex[cur];
if (!delegate.IsVisible(c))
continue;
const size_t ptCount = delegate.GetPointCount(c);
if (ptCount < 1)
continue;
CurveType curveType = delegate.GetCurveType(c);
if (curveType == CurveNone)
continue;
const ImVec2* pts = delegate.GetPoints(c);
uint32_t curveColor = delegate.GetCurveColor(c);
if ((c == highLightedCurveIndex && selection.empty() && !selectingQuad) || movingCurve == c)
curveColor = 0xFFFFFFFF;
for (size_t p = 0; p < ptCount - 1; p++)
{
const ImVec2 p1 = pointToRange(pts[p]);
const ImVec2 p2 = pointToRange(pts[p + 1]);
if (curveType == CurveSmooth || curveType == CurveLinear)
{
size_t subStepCount = (curveType == CurveSmooth) ? 20 : 2;
float step = 1.f / float(subStepCount - 1);
for (size_t substep = 0; substep < subStepCount - 1; substep++)
{
float t = float(substep) * step;
const ImVec2 sp1 = ImLerp(p1, p2, t);
const ImVec2 sp2 = ImLerp(p1, p2, t + step);
const float rt1 = smoothstep(p1.x, p2.x, sp1.x);
const float rt2 = smoothstep(p1.x, p2.x, sp2.x);
const ImVec2 pos1 = ImVec2(sp1.x, ImLerp(p1.y, p2.y, rt1)) * viewSize + offset;
const ImVec2 pos2 = ImVec2(sp2.x, ImLerp(p1.y, p2.y, rt2)) * viewSize + offset;
if (distance(io.MousePos.x, io.MousePos.y, pos1.x, pos1.y, pos2.x, pos2.y) < 8.f && !scrollingV)
{
localOverCurve = int(c);
overCurve = int(c);
overCurveOrPoint = true;
}
draw_list->AddLine(pos1, pos2, curveColor, 1.3f);
} // substep
}
else if (curveType == CurveDiscrete)
{
ImVec2 dp1 = p1 * viewSize + offset;
ImVec2 dp2 = ImVec2(p2.x, p1.y) * viewSize + offset;
ImVec2 dp3 = p2 * viewSize + offset;
draw_list->AddLine(dp1, dp2, curveColor, 1.3f);
draw_list->AddLine(dp2, dp3, curveColor, 1.3f);
if ((distance(io.MousePos.x, io.MousePos.y, dp1.x, dp1.y, dp3.x, dp1.y) < 8.f ||
distance(io.MousePos.x, io.MousePos.y, dp3.x, dp1.y, dp3.x, dp3.y) < 8.f)
/*&& localOverCurve == -1*/)
{
localOverCurve = int(c);
overCurve = int(c);
overCurveOrPoint = true;
}
}
} // point loop
for (size_t p = 0; p < ptCount; p++)
{
const int drawState = DrawPoint(draw_list, pointToRange(pts[p]), viewSize, offset, (selection.find({ int(c), int(p) }) != selection.end() && movingCurve == -1 && !scrollingV));
if (drawState && movingCurve == -1 && !selectingQuad)
{
overCurveOrPoint = true;
overSelectedPoint = true;
overCurve = -1;
if (drawState == 2)
{
if (!io.KeyShift && selection.find({ int(c), int(p) }) == selection.end())
selection.clear();
selection.insert({ int(c), int(p) });
}
}
}
} // curves loop
if (localOverCurve == -1)
overCurve = -1;
// move selection
static bool pointsMoved = false;
static ImVec2 mousePosOrigin;
static std::vector<ImVec2> originalPoints;
if (overSelectedPoint && io.MouseDown[0])
{
if ((fabsf(io.MouseDelta.x) > 0.f || fabsf(io.MouseDelta.y) > 0.f) && !selection.empty())
{
if (!pointsMoved)
{
delegate.BeginEdit(0);
mousePosOrigin = io.MousePos;
originalPoints.resize(selection.size());
int index = 0;
for (auto& sel : selection)
{
const ImVec2* pts = delegate.GetPoints(sel.curveIndex);
originalPoints[index++] = pts[sel.pointIndex];
}
}
pointsMoved = true;
ret = 1;
auto prevSelection = selection;
int originalIndex = 0;
for (auto& sel : prevSelection)
{
const ImVec2 p = rangeToPoint(pointToRange(originalPoints[originalIndex]) + (io.MousePos - mousePosOrigin) * sizeOfPixel);
const int newIndex = delegate.EditPoint(sel.curveIndex, sel.pointIndex, p);
if (newIndex != sel.pointIndex)
{
selection.erase(sel);
selection.insert({ sel.curveIndex, newIndex });
}
originalIndex++;
}
}
}
if (overSelectedPoint && !io.MouseDown[0])
{
overSelectedPoint = false;
if (pointsMoved)
{
pointsMoved = false;
delegate.EndEdit();
}
}
// add point
if (overCurve != -1 && io.MouseDoubleClicked[0])
{
const ImVec2 np = rangeToPoint((io.MousePos - offset) / viewSize);
delegate.BeginEdit(overCurve);
delegate.AddPoint(overCurve, np);
delegate.EndEdit();
ret = 1;
}
// move curve
if (movingCurve != -1)
{
const size_t ptCount = delegate.GetPointCount(movingCurve);
const ImVec2* pts = delegate.GetPoints(movingCurve);
if (!pointsMoved)
{
mousePosOrigin = io.MousePos;
pointsMoved = true;
originalPoints.resize(ptCount);
for (size_t index = 0; index < ptCount; index++)
{
originalPoints[index] = pts[index];
}
}
if (ptCount >= 1)
{
for (size_t p = 0; p < ptCount; p++)
{
delegate.EditPoint(movingCurve, int(p), rangeToPoint(pointToRange(originalPoints[p]) + (io.MousePos - mousePosOrigin) * sizeOfPixel));
}
ret = 1;
}
if (!io.MouseDown[0])
{
movingCurve = -1;
pointsMoved = false;
delegate.EndEdit();
}
}
if (movingCurve == -1 && overCurve != -1 && ImGui::IsMouseClicked(0) && selection.empty() && !selectingQuad)
{
movingCurve = overCurve;
delegate.BeginEdit(overCurve);
}
// quad selection
if (selectingQuad)
{
const ImVec2 bmin = ImMin(quadSelection, io.MousePos);
const ImVec2 bmax = ImMax(quadSelection, io.MousePos);
draw_list->AddRectFilled(bmin, bmax, 0x40FF0000, 1.f);
draw_list->AddRect(bmin, bmax, 0xFFFF0000, 1.f);
const ImRect selectionQuad(bmin, bmax);
if (!io.MouseDown[0])
{
if (!io.KeyShift)
selection.clear();
// select everythnig is quad
for (size_t c = 0; c < curveCount; c++)
{
if (!delegate.IsVisible(c))
continue;
const size_t ptCount = delegate.GetPointCount(c);
if (ptCount < 1)
continue;
const ImVec2* pts = delegate.GetPoints(c);
for (size_t p = 0; p < ptCount; p++)
{
const ImVec2 center = pointToRange(pts[p]) * viewSize + offset;
if (selectionQuad.Contains(center))
selection.insert({ int(c), int(p) });
}
}
// done
selectingQuad = false;
}
}
if (!overCurveOrPoint && ImGui::IsMouseClicked(0) && !selectingQuad && movingCurve == -1 && !overSelectedPoint && container.Contains(io.MousePos))
{
selectingQuad = true;
quadSelection = io.MousePos;
}
if (clippingRect)
draw_list->PopClipRect();
ImGui::EndChildFrame();
ImGui::PopStyleVar();
ImGui::PopStyleColor(1);
if (selectedPoints)
{
selectedPoints->resize(int(selection.size()));
int index = 0;
for (auto& point : selection)
(*selectedPoints)[index++] = point;
}
return ret;
}
}

View file

@ -0,0 +1,82 @@
// https://github.com/CedricGuillemet/ImGuizmo
// v 1.89 WIP
//
// The MIT License(MIT)
//
// Copyright(c) 2021 Cedric Guillemet
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files(the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and / or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions :
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
//
#pragma once
#include <stdint.h>
#include "imgui.h"
struct ImRect;
namespace ImCurveEdit
{
enum CurveType
{
CurveNone,
CurveDiscrete,
CurveLinear,
CurveSmooth,
CurveBezier,
};
struct EditPoint
{
int curveIndex;
int pointIndex;
bool operator <(const EditPoint& other) const
{
if (curveIndex < other.curveIndex)
return true;
if (curveIndex > other.curveIndex)
return false;
if (pointIndex < other.pointIndex)
return true;
return false;
}
};
struct Delegate
{
bool focused = false;
virtual size_t GetCurveCount() = 0;
virtual bool IsVisible(size_t /*curveIndex*/) { return true; }
virtual CurveType GetCurveType(size_t /*curveIndex*/) const { return CurveLinear; }
virtual ImVec2& GetMin() = 0;
virtual ImVec2& GetMax() = 0;
virtual size_t GetPointCount(size_t curveIndex) = 0;
virtual uint32_t GetCurveColor(size_t curveIndex) = 0;
virtual ImVec2* GetPoints(size_t curveIndex) = 0;
virtual int EditPoint(size_t curveIndex, int pointIndex, ImVec2 value) = 0;
virtual void AddPoint(size_t curveIndex, ImVec2 value) = 0;
virtual unsigned int GetBackgroundColor() { return 0xFF202020; }
// handle undo/redo thru this functions
virtual void BeginEdit(int /*index*/) {}
virtual void EndEdit() {}
virtual ~Delegate() = default;
};
int Edit(Delegate& delegate, const ImVec2& size, unsigned int id, const ImRect* clippingRect = NULL, ImVector<EditPoint>* selectedPoints = NULL);
}

View file

@ -0,0 +1,116 @@
// https://github.com/CedricGuillemet/ImGuizmo
// v 1.89 WIP
//
// The MIT License(MIT)
//
// Copyright(c) 2021 Cedric Guillemet
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files(the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and / or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions :
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
//
#include "ImGradient.h"
#include "imgui.h"
#include "imgui_internal.h"
namespace ImGradient
{
#ifndef IMGUI_DEFINE_MATH_OPERATORS
static inline ImVec2 operator*(const ImVec2& lhs, const float rhs) { return ImVec2(lhs.x * rhs, lhs.y * rhs); }
static inline ImVec2 operator/(const ImVec2& lhs, const float rhs) { return ImVec2(lhs.x / rhs, lhs.y / rhs); }
static inline ImVec2 operator+(const ImVec2& lhs, const ImVec2& rhs) { return ImVec2(lhs.x + rhs.x, lhs.y + rhs.y); }
static inline ImVec2 operator-(const ImVec2& lhs, const ImVec2& rhs) { return ImVec2(lhs.x - rhs.x, lhs.y - rhs.y); }
static inline ImVec2 operator*(const ImVec2& lhs, const ImVec2& rhs) { return ImVec2(lhs.x * rhs.x, lhs.y * rhs.y); }
static inline ImVec2 operator/(const ImVec2& lhs, const ImVec2& rhs) { return ImVec2(lhs.x / rhs.x, lhs.y / rhs.y); }
#endif
static int DrawPoint(ImDrawList* draw_list, ImVec4 color, const ImVec2 size, bool editing, ImVec2 pos)
{
ImGuiIO& io = ImGui::GetIO();
ImVec2 p1 = ImLerp(pos, ImVec2(pos + ImVec2(size.x - size.y, 0.f)), color.w) + ImVec2(3, 3);
ImVec2 p2 = ImLerp(pos + ImVec2(size.y, size.y), ImVec2(pos + size), color.w) - ImVec2(3, 3);
ImRect rc(p1, p2);
color.w = 1.f;
draw_list->AddRectFilled(p1, p2, ImColor(color));
if (editing)
draw_list->AddRect(p1, p2, 0xFFFFFFFF, 2.f, 15, 2.5f);
else
draw_list->AddRect(p1, p2, 0x80FFFFFF, 2.f, 15, 1.25f);
if (rc.Contains(io.MousePos))
{
if (io.MouseClicked[0])
return 2;
return 1;
}
return 0;
}
bool Edit(Delegate& delegate, const ImVec2& size, int& selection)
{
bool ret = false;
ImGuiIO& io = ImGui::GetIO();
ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(0, 0));
ImGui::BeginChildFrame(137, size);
ImDrawList* draw_list = ImGui::GetWindowDrawList();
const ImVec2 offset = ImGui::GetCursorScreenPos();
const ImVec4* pts = delegate.GetPoints();
static int currentSelection = -1;
static int movingPt = -1;
if (currentSelection >= int(delegate.GetPointCount()))
currentSelection = -1;
if (movingPt != -1)
{
ImVec4 current = pts[movingPt];
current.w += io.MouseDelta.x / size.x;
current.w = ImClamp(current.w, 0.f, 1.f);
delegate.EditPoint(movingPt, current);
ret = true;
if (!io.MouseDown[0])
movingPt = -1;
}
for (size_t i = 0; i < delegate.GetPointCount(); i++)
{
int ptSel = DrawPoint(draw_list, pts[i], size, i == currentSelection, offset);
if (ptSel == 2)
{
currentSelection = int(i);
ret = true;
}
if (ptSel == 1 && io.MouseDown[0] && movingPt == -1)
{
movingPt = int(i);
}
}
ImRect rc(offset, offset + size);
if (rc.Contains(io.MousePos) && io.MouseDoubleClicked[0])
{
float t = (io.MousePos.x - offset.x) / size.x;
delegate.AddPoint(delegate.GetPoint(t));
ret = true;
}
ImGui::EndChildFrame();
ImGui::PopStyleVar();
selection = currentSelection;
return ret;
}
}

View file

@ -0,0 +1,45 @@
// https://github.com/CedricGuillemet/ImGuizmo
// v 1.89 WIP
//
// The MIT License(MIT)
//
// Copyright(c) 2021 Cedric Guillemet
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files(the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and / or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions :
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
//
#pragma once
#include <cstddef>
struct ImVec4;
struct ImVec2;
namespace ImGradient
{
struct Delegate
{
virtual size_t GetPointCount() = 0;
virtual ImVec4* GetPoints() = 0;
virtual int EditPoint(int pointIndex, ImVec4 value) = 0;
virtual ImVec4 GetPoint(float t) = 0;
virtual void AddPoint(ImVec4 value) = 0;
virtual ~Delegate() = default;
};
bool Edit(Delegate& delegate, const ImVec2& size, int& selection);
}

2996
source/engine/thirdparty/imgui/ImGuizmo.cpp vendored Executable file

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,272 @@
// https://github.com/CedricGuillemet/ImGuizmo
// v 1.89 WIP
//
// The MIT License(MIT)
//
// Copyright(c) 2021 Cedric Guillemet
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files(the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and / or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions :
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
//
// -------------------------------------------------------------------------------------------
// History :
// 2019/11/03 View gizmo
// 2016/09/11 Behind camera culling. Scaling Delta matrix not multiplied by source matrix scales. local/world rotation and translation fixed. Display message is incorrect (X: ... Y:...) in local mode.
// 2016/09/09 Hatched negative axis. Snapping. Documentation update.
// 2016/09/04 Axis switch and translation plan autohiding. Scale transform stability improved
// 2016/09/01 Mogwai changed to Manipulate. Draw debug cube. Fixed inverted scale. Mixing scale and translation/rotation gives bad results.
// 2016/08/31 First version
//
// -------------------------------------------------------------------------------------------
// Future (no order):
//
// - Multi view
// - display rotation/translation/scale infos in local/world space and not only local
// - finish local/world matrix application
// - OPERATION as bitmask
//
// -------------------------------------------------------------------------------------------
// Example
#if 0
void EditTransform(const Camera& camera, matrix_t& matrix)
{
static ImGuizmo::OPERATION mCurrentGizmoOperation(ImGuizmo::ROTATE);
static ImGuizmo::MODE mCurrentGizmoMode(ImGuizmo::WORLD);
if (ImGui::IsKeyPressed(90))
mCurrentGizmoOperation = ImGuizmo::TRANSLATE;
if (ImGui::IsKeyPressed(69))
mCurrentGizmoOperation = ImGuizmo::ROTATE;
if (ImGui::IsKeyPressed(82)) // r Key
mCurrentGizmoOperation = ImGuizmo::SCALE;
if (ImGui::RadioButton("Translate", mCurrentGizmoOperation == ImGuizmo::TRANSLATE))
mCurrentGizmoOperation = ImGuizmo::TRANSLATE;
ImGui::SameLine();
if (ImGui::RadioButton("Rotate", mCurrentGizmoOperation == ImGuizmo::ROTATE))
mCurrentGizmoOperation = ImGuizmo::ROTATE;
ImGui::SameLine();
if (ImGui::RadioButton("Scale", mCurrentGizmoOperation == ImGuizmo::SCALE))
mCurrentGizmoOperation = ImGuizmo::SCALE;
float matrixTranslation[3], matrixRotation[3], matrixScale[3];
ImGuizmo::DecomposeMatrixToComponents(matrix.m16, matrixTranslation, matrixRotation, matrixScale);
ImGui::InputFloat3("Tr", matrixTranslation, 3);
ImGui::InputFloat3("Rt", matrixRotation, 3);
ImGui::InputFloat3("Sc", matrixScale, 3);
ImGuizmo::RecomposeMatrixFromComponents(matrixTranslation, matrixRotation, matrixScale, matrix.m16);
if (mCurrentGizmoOperation != ImGuizmo::SCALE)
{
if (ImGui::RadioButton("Local", mCurrentGizmoMode == ImGuizmo::LOCAL))
mCurrentGizmoMode = ImGuizmo::LOCAL;
ImGui::SameLine();
if (ImGui::RadioButton("World", mCurrentGizmoMode == ImGuizmo::WORLD))
mCurrentGizmoMode = ImGuizmo::WORLD;
}
static bool useSnap(false);
if (ImGui::IsKeyPressed(83))
useSnap = !useSnap;
ImGui::Checkbox("", &useSnap);
ImGui::SameLine();
vec_t snap;
switch (mCurrentGizmoOperation)
{
case ImGuizmo::TRANSLATE:
snap = config.mSnapTranslation;
ImGui::InputFloat3("Snap", &snap.x);
break;
case ImGuizmo::ROTATE:
snap = config.mSnapRotation;
ImGui::InputFloat("Angle Snap", &snap.x);
break;
case ImGuizmo::SCALE:
snap = config.mSnapScale;
ImGui::InputFloat("Scale Snap", &snap.x);
break;
}
ImGuiIO& io = ImGui::GetIO();
ImGuizmo::SetRect(0, 0, io.DisplaySize.x, io.DisplaySize.y);
ImGuizmo::Manipulate(camera.mView.m16, camera.mProjection.m16, mCurrentGizmoOperation, mCurrentGizmoMode, matrix.m16, NULL, useSnap ? &snap.x : NULL);
}
#endif
#pragma once
#ifdef USE_IMGUI_API
#include "imconfig.h"
#endif
#ifndef IMGUI_API
#define IMGUI_API
#endif
#ifndef IMGUIZMO_NAMESPACE
#define IMGUIZMO_NAMESPACE ImGuizmo
#endif
namespace IMGUIZMO_NAMESPACE
{
// call inside your own window and before Manipulate() in order to draw gizmo to that window.
// Or pass a specific ImDrawList to draw to (e.g. ImGui::GetForegroundDrawList()).
IMGUI_API void SetDrawlist(ImDrawList* drawlist = nullptr);
// call BeginFrame right after ImGui_XXXX_NewFrame();
IMGUI_API void BeginFrame();
// this is necessary because when imguizmo is compiled into a dll, and imgui into another
// globals are not shared between them.
// More details at https://stackoverflow.com/questions/19373061/what-happens-to-global-and-static-variables-in-a-shared-library-when-it-is-dynam
// expose method to set imgui context
IMGUI_API void SetImGuiContext(ImGuiContext* ctx);
// return true if mouse cursor is over any gizmo control (axis, plan or screen component)
IMGUI_API bool IsOver();
// return true if mouse IsOver or if the gizmo is in moving state
IMGUI_API bool IsUsing();
// return true if any gizmo is in moving state
IMGUI_API bool IsUsingAny();
// enable/disable the gizmo. Stay in the state until next call to Enable.
// gizmo is rendered with gray half transparent color when disabled
IMGUI_API void Enable(bool enable);
// helper functions for manualy editing translation/rotation/scale with an input float
// translation, rotation and scale float points to 3 floats each
// Angles are in degrees (more suitable for human editing)
// example:
// float matrixTranslation[3], matrixRotation[3], matrixScale[3];
// ImGuizmo::DecomposeMatrixToComponents(gizmoMatrix.m16, matrixTranslation, matrixRotation, matrixScale);
// ImGui::InputFloat3("Tr", matrixTranslation, 3);
// ImGui::InputFloat3("Rt", matrixRotation, 3);
// ImGui::InputFloat3("Sc", matrixScale, 3);
// ImGuizmo::RecomposeMatrixFromComponents(matrixTranslation, matrixRotation, matrixScale, gizmoMatrix.m16);
//
// These functions have some numerical stability issues for now. Use with caution.
IMGUI_API void DecomposeMatrixToComponents(const float* matrix, float* translation, float* rotation, float* scale);
IMGUI_API void RecomposeMatrixFromComponents(const float* translation, const float* rotation, const float* scale, float* matrix);
IMGUI_API void SetRect(float x, float y, float width, float height);
// default is false
IMGUI_API void SetOrthographic(bool isOrthographic);
// Render a cube with face color corresponding to face normal. Usefull for debug/tests
IMGUI_API void DrawCubes(const float* view, const float* projection, const float* matrices, int matrixCount);
IMGUI_API void DrawGrid(const float* view, const float* projection, const float* matrix, const float gridSize);
// call it when you want a gizmo
// Needs view and projection matrices.
// matrix parameter is the source matrix (where will be gizmo be drawn) and might be transformed by the function. Return deltaMatrix is optional
// translation is applied in world space
enum OPERATION
{
TRANSLATE_X = (1u << 0),
TRANSLATE_Y = (1u << 1),
TRANSLATE_Z = (1u << 2),
ROTATE_X = (1u << 3),
ROTATE_Y = (1u << 4),
ROTATE_Z = (1u << 5),
ROTATE_SCREEN = (1u << 6),
SCALE_X = (1u << 7),
SCALE_Y = (1u << 8),
SCALE_Z = (1u << 9),
BOUNDS = (1u << 10),
SCALE_XU = (1u << 11),
SCALE_YU = (1u << 12),
SCALE_ZU = (1u << 13),
TRANSLATE = TRANSLATE_X | TRANSLATE_Y | TRANSLATE_Z,
ROTATE = ROTATE_X | ROTATE_Y | ROTATE_Z | ROTATE_SCREEN,
SCALE = SCALE_X | SCALE_Y | SCALE_Z,
SCALEU = SCALE_XU | SCALE_YU | SCALE_ZU, // universal
UNIVERSAL = TRANSLATE | ROTATE | SCALEU
};
inline OPERATION operator|(OPERATION lhs, OPERATION rhs)
{
return static_cast<OPERATION>(static_cast<int>(lhs) | static_cast<int>(rhs));
}
enum MODE
{
LOCAL,
WORLD
};
IMGUI_API bool Manipulate(const float* view, const float* projection, OPERATION operation, MODE mode, float* matrix, float* deltaMatrix = NULL, const float* snap = NULL, const float* localBounds = NULL, const float* boundsSnap = NULL);
//
// Please note that this cubeview is patented by Autodesk : https://patents.google.com/patent/US7782319B2/en
// It seems to be a defensive patent in the US. I don't think it will bring troubles using it as
// other software are using the same mechanics. But just in case, you are now warned!
//
IMGUI_API void ViewManipulate(float* view, float length, ImVec2 position, ImVec2 size, ImU32 backgroundColor);
// use this version if you did not call Manipulate before and you are just using ViewManipulate
IMGUI_API void ViewManipulate(float* view, const float* projection, OPERATION operation, MODE mode, float* matrix, float length, ImVec2 position, ImVec2 size, ImU32 backgroundColor);
IMGUI_API void SetID(int id);
// return true if the cursor is over the operation's gizmo
IMGUI_API bool IsOver(OPERATION op);
IMGUI_API void SetGizmoSizeClipSpace(float value);
// Allow axis to flip
// When true (default), the guizmo axis flip for better visibility
// When false, they always stay along the positive world/local axis
IMGUI_API void AllowAxisFlip(bool value);
// Configure the limit where axis are hidden
IMGUI_API void SetAxisLimit(float value);
// Configure the limit where planes are hiden
IMGUI_API void SetPlaneLimit(float value);
enum COLOR
{
DIRECTION_X, // directionColor[0]
DIRECTION_Y, // directionColor[1]
DIRECTION_Z, // directionColor[2]
PLANE_X, // planeColor[0]
PLANE_Y, // planeColor[1]
PLANE_Z, // planeColor[2]
SELECTION, // selectionColor
INACTIVE, // inactiveColor
TRANSLATION_LINE, // translationLineColor
SCALE_LINE,
ROTATION_USING_BORDER,
ROTATION_USING_FILL,
HATCHED_AXIS_LINES,
TEXT,
TEXT_SHADOW,
COUNT
};
struct Style
{
IMGUI_API Style();
float TranslationLineThickness; // Thickness of lines for translation gizmo
float TranslationLineArrowSize; // Size of arrow at the end of lines for translation gizmo
float RotationLineThickness; // Thickness of lines for rotation gizmo
float RotationOuterLineThickness; // Thickness of line surrounding the rotation gizmo
float ScaleLineThickness; // Thickness of lines for scale gizmo
float ScaleLineCircleSize; // Size of circle at the end of lines for scale gizmo
float HatchedAxisLineThickness; // Thickness of hatched axis lines
float CenterCircleSize; // Size of circle at the center of the translate/scale gizmo
ImVec4 Colors[COLOR::COUNT];
};
IMGUI_API Style& GetStyle();
}

View file

@ -0,0 +1,695 @@
// https://github.com/CedricGuillemet/ImGuizmo
// v 1.89 WIP
//
// The MIT License(MIT)
//
// Copyright(c) 2021 Cedric Guillemet
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files(the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and / or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions :
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
//
#include "ImSequencer.h"
#include "imgui.h"
#include "imgui_internal.h"
#include <cstdlib>
namespace ImSequencer
{
#ifndef IMGUI_DEFINE_MATH_OPERATORS
static ImVec2 operator+(const ImVec2& a, const ImVec2& b) {
return ImVec2(a.x + b.x, a.y + b.y);
}
#endif
static bool SequencerAddDelButton(ImDrawList* draw_list, ImVec2 pos, bool add = true)
{
ImGuiIO& io = ImGui::GetIO();
ImRect btnRect(pos, ImVec2(pos.x + 16, pos.y + 16));
bool overBtn = btnRect.Contains(io.MousePos);
bool containedClick = overBtn && btnRect.Contains(io.MouseClickedPos[0]);
bool clickedBtn = containedClick && io.MouseReleased[0];
int btnColor = overBtn ? 0xAAEAFFAA : 0x77A3B2AA;
if (containedClick && io.MouseDownDuration[0] > 0)
btnRect.Expand(2.0f);
float midy = pos.y + 16 / 2 - 0.5f;
float midx = pos.x + 16 / 2 - 0.5f;
draw_list->AddRect(btnRect.Min, btnRect.Max, btnColor, 4);
draw_list->AddLine(ImVec2(btnRect.Min.x + 3, midy), ImVec2(btnRect.Max.x - 3, midy), btnColor, 2);
if (add)
draw_list->AddLine(ImVec2(midx, btnRect.Min.y + 3), ImVec2(midx, btnRect.Max.y - 3), btnColor, 2);
return clickedBtn;
}
bool Sequencer(SequenceInterface* sequence, int* currentFrame, bool* expanded, int* selectedEntry, int* firstFrame, int sequenceOptions)
{
bool ret = false;
ImGuiIO& io = ImGui::GetIO();
int cx = (int)(io.MousePos.x);
int cy = (int)(io.MousePos.y);
static float framePixelWidth = 10.f;
static float framePixelWidthTarget = 10.f;
int legendWidth = 200;
static int movingEntry = -1;
static int movingPos = -1;
static int movingPart = -1;
int delEntry = -1;
int dupEntry = -1;
int ItemHeight = 20;
bool popupOpened = false;
int sequenceCount = sequence->GetItemCount();
if (!sequenceCount)
return false;
ImGui::BeginGroup();
ImDrawList* draw_list = ImGui::GetWindowDrawList();
ImVec2 canvas_pos = ImGui::GetCursorScreenPos(); // ImDrawList API uses screen coordinates!
ImVec2 canvas_size = ImGui::GetContentRegionAvail(); // Resize canvas to what's available
int firstFrameUsed = firstFrame ? *firstFrame : 0;
int controlHeight = sequenceCount * ItemHeight;
for (int i = 0; i < sequenceCount; i++)
controlHeight += int(sequence->GetCustomHeight(i));
int frameCount = ImMax(sequence->GetFrameMax() - sequence->GetFrameMin(), 1);
static bool MovingScrollBar = false;
static bool MovingCurrentFrame = false;
struct CustomDraw
{
int index;
ImRect customRect;
ImRect legendRect;
ImRect clippingRect;
ImRect legendClippingRect;
};
ImVector<CustomDraw> customDraws;
ImVector<CustomDraw> compactCustomDraws;
// zoom in/out
const int visibleFrameCount = (int)floorf((canvas_size.x - legendWidth) / framePixelWidth);
const float barWidthRatio = ImMin(visibleFrameCount / (float)frameCount, 1.f);
const float barWidthInPixels = barWidthRatio * (canvas_size.x - legendWidth);
ImRect regionRect(canvas_pos, canvas_pos + canvas_size);
static bool panningView = false;
static ImVec2 panningViewSource;
static int panningViewFrame;
if (ImGui::IsWindowFocused() && io.KeyAlt && io.MouseDown[2])
{
if (!panningView)
{
panningViewSource = io.MousePos;
panningView = true;
panningViewFrame = *firstFrame;
}
*firstFrame = panningViewFrame - int((io.MousePos.x - panningViewSource.x) / framePixelWidth);
*firstFrame = ImClamp(*firstFrame, sequence->GetFrameMin(), sequence->GetFrameMax() - visibleFrameCount);
}
if (panningView && !io.MouseDown[2])
{
panningView = false;
}
framePixelWidthTarget = ImClamp(framePixelWidthTarget, 0.1f, 50.f);
framePixelWidth = ImLerp(framePixelWidth, framePixelWidthTarget, 0.33f);
frameCount = sequence->GetFrameMax() - sequence->GetFrameMin();
if (visibleFrameCount >= frameCount && firstFrame)
*firstFrame = sequence->GetFrameMin();
// --
if (expanded && !*expanded)
{
ImGui::InvisibleButton("canvas", ImVec2(canvas_size.x - canvas_pos.x, (float)ItemHeight));
draw_list->AddRectFilled(canvas_pos, ImVec2(canvas_size.x + canvas_pos.x, canvas_pos.y + ItemHeight), 0xFF3D3837, 0);
char tmps[512];
ImFormatString(tmps, IM_ARRAYSIZE(tmps), sequence->GetCollapseFmt(), frameCount, sequenceCount);
draw_list->AddText(ImVec2(canvas_pos.x + 26, canvas_pos.y + 2), 0xFFFFFFFF, tmps);
}
else
{
bool hasScrollBar(true);
/*
int framesPixelWidth = int(frameCount * framePixelWidth);
if ((framesPixelWidth + legendWidth) >= canvas_size.x)
{
hasScrollBar = true;
}
*/
// test scroll area
ImVec2 headerSize(canvas_size.x, (float)ItemHeight);
ImVec2 scrollBarSize(canvas_size.x, 14.f);
ImGui::InvisibleButton("topBar", headerSize);
draw_list->AddRectFilled(canvas_pos, canvas_pos + headerSize, 0xFFFF0000, 0);
ImVec2 childFramePos = ImGui::GetCursorScreenPos();
ImVec2 childFrameSize(canvas_size.x, canvas_size.y - 8.f - headerSize.y - (hasScrollBar ? scrollBarSize.y : 0));
ImGui::PushStyleColor(ImGuiCol_FrameBg, 0);
ImGui::BeginChildFrame(889, childFrameSize);
sequence->focused = ImGui::IsWindowFocused();
ImGui::InvisibleButton("contentBar", ImVec2(canvas_size.x, float(controlHeight)));
const ImVec2 contentMin = ImGui::GetItemRectMin();
const ImVec2 contentMax = ImGui::GetItemRectMax();
const ImRect contentRect(contentMin, contentMax);
const float contentHeight = contentMax.y - contentMin.y;
// full background
draw_list->AddRectFilled(canvas_pos, canvas_pos + canvas_size, 0xFF242424, 0);
// current frame top
ImRect topRect(ImVec2(canvas_pos.x + legendWidth, canvas_pos.y), ImVec2(canvas_pos.x + canvas_size.x, canvas_pos.y + ItemHeight));
if (!MovingCurrentFrame && !MovingScrollBar && movingEntry == -1 && sequenceOptions & SEQUENCER_CHANGE_FRAME && currentFrame && *currentFrame >= 0 && topRect.Contains(io.MousePos) && io.MouseDown[0])
{
MovingCurrentFrame = true;
}
if (MovingCurrentFrame)
{
if (frameCount)
{
*currentFrame = (int)((io.MousePos.x - topRect.Min.x) / framePixelWidth) + firstFrameUsed;
if (*currentFrame < sequence->GetFrameMin())
*currentFrame = sequence->GetFrameMin();
if (*currentFrame >= sequence->GetFrameMax())
*currentFrame = sequence->GetFrameMax();
}
if (!io.MouseDown[0])
MovingCurrentFrame = false;
}
//header
draw_list->AddRectFilled(canvas_pos, ImVec2(canvas_size.x + canvas_pos.x, canvas_pos.y + ItemHeight), 0xFF3D3837, 0);
if (sequenceOptions & SEQUENCER_ADD)
{
if (SequencerAddDelButton(draw_list, ImVec2(canvas_pos.x + legendWidth - ItemHeight, canvas_pos.y + 2), true))
ImGui::OpenPopup("addEntry");
if (ImGui::BeginPopup("addEntry"))
{
for (int i = 0; i < sequence->GetItemTypeCount(); i++)
if (ImGui::Selectable(sequence->GetItemTypeName(i)))
{
sequence->Add(i);
*selectedEntry = sequence->GetItemCount() - 1;
}
ImGui::EndPopup();
popupOpened = true;
}
}
//header frame number and lines
int modFrameCount = 10;
int frameStep = 1;
while ((modFrameCount * framePixelWidth) < 150)
{
modFrameCount *= 2;
frameStep *= 2;
};
int halfModFrameCount = modFrameCount / 2;
auto drawLine = [&](int i, int regionHeight) {
bool baseIndex = ((i % modFrameCount) == 0) || (i == sequence->GetFrameMax() || i == sequence->GetFrameMin());
bool halfIndex = (i % halfModFrameCount) == 0;
int px = (int)canvas_pos.x + int(i * framePixelWidth) + legendWidth - int(firstFrameUsed * framePixelWidth);
int tiretStart = baseIndex ? 4 : (halfIndex ? 10 : 14);
int tiretEnd = baseIndex ? regionHeight : ItemHeight;
if (px <= (canvas_size.x + canvas_pos.x) && px >= (canvas_pos.x + legendWidth))
{
draw_list->AddLine(ImVec2((float)px, canvas_pos.y + (float)tiretStart), ImVec2((float)px, canvas_pos.y + (float)tiretEnd - 1), 0xFF606060, 1);
draw_list->AddLine(ImVec2((float)px, canvas_pos.y + (float)ItemHeight), ImVec2((float)px, canvas_pos.y + (float)regionHeight - 1), 0x30606060, 1);
}
if (baseIndex && px > (canvas_pos.x + legendWidth))
{
char tmps[512];
ImFormatString(tmps, IM_ARRAYSIZE(tmps), "%d", i);
draw_list->AddText(ImVec2((float)px + 3.f, canvas_pos.y), 0xFFBBBBBB, tmps);
}
};
auto drawLineContent = [&](int i, int /*regionHeight*/) {
int px = (int)canvas_pos.x + int(i * framePixelWidth) + legendWidth - int(firstFrameUsed * framePixelWidth);
int tiretStart = int(contentMin.y);
int tiretEnd = int(contentMax.y);
if (px <= (canvas_size.x + canvas_pos.x) && px >= (canvas_pos.x + legendWidth))
{
//draw_list->AddLine(ImVec2((float)px, canvas_pos.y + (float)tiretStart), ImVec2((float)px, canvas_pos.y + (float)tiretEnd - 1), 0xFF606060, 1);
draw_list->AddLine(ImVec2(float(px), float(tiretStart)), ImVec2(float(px), float(tiretEnd)), 0x30606060, 1);
}
};
for (int i = sequence->GetFrameMin(); i <= sequence->GetFrameMax(); i += frameStep)
{
drawLine(i, ItemHeight);
}
drawLine(sequence->GetFrameMin(), ItemHeight);
drawLine(sequence->GetFrameMax(), ItemHeight);
/*
draw_list->AddLine(canvas_pos, ImVec2(canvas_pos.x, canvas_pos.y + controlHeight), 0xFF000000, 1);
draw_list->AddLine(ImVec2(canvas_pos.x, canvas_pos.y + ItemHeight), ImVec2(canvas_size.x, canvas_pos.y + ItemHeight), 0xFF000000, 1);
*/
// clip content
draw_list->PushClipRect(childFramePos, childFramePos + childFrameSize, true);
// draw item names in the legend rect on the left
size_t customHeight = 0;
for (int i = 0; i < sequenceCount; i++)
{
int type;
sequence->Get(i, NULL, NULL, &type, NULL);
ImVec2 tpos(contentMin.x + 3, contentMin.y + i * ItemHeight + 2 + customHeight);
draw_list->AddText(tpos, 0xFFFFFFFF, sequence->GetItemLabel(i));
if (sequenceOptions & SEQUENCER_DEL)
{
if (SequencerAddDelButton(draw_list, ImVec2(contentMin.x + legendWidth - ItemHeight + 2 - 10, tpos.y + 2), false))
delEntry = i;
if (SequencerAddDelButton(draw_list, ImVec2(contentMin.x + legendWidth - ItemHeight - ItemHeight + 2 - 10, tpos.y + 2), true))
dupEntry = i;
}
customHeight += sequence->GetCustomHeight(i);
}
// slots background
customHeight = 0;
for (int i = 0; i < sequenceCount; i++)
{
unsigned int col = (i & 1) ? 0xFF3A3636 : 0xFF413D3D;
size_t localCustomHeight = sequence->GetCustomHeight(i);
ImVec2 pos = ImVec2(contentMin.x + legendWidth, contentMin.y + ItemHeight * i + 1 + customHeight);
ImVec2 sz = ImVec2(canvas_size.x + canvas_pos.x, pos.y + ItemHeight - 1 + localCustomHeight);
if (!popupOpened && cy >= pos.y && cy < pos.y + (ItemHeight + localCustomHeight) && movingEntry == -1 && cx>contentMin.x && cx < contentMin.x + canvas_size.x)
{
col += 0x80201008;
pos.x -= legendWidth;
}
draw_list->AddRectFilled(pos, sz, col, 0);
customHeight += localCustomHeight;
}
draw_list->PushClipRect(childFramePos + ImVec2(float(legendWidth), 0.f), childFramePos + childFrameSize, true);
// vertical frame lines in content area
for (int i = sequence->GetFrameMin(); i <= sequence->GetFrameMax(); i += frameStep)
{
drawLineContent(i, int(contentHeight));
}
drawLineContent(sequence->GetFrameMin(), int(contentHeight));
drawLineContent(sequence->GetFrameMax(), int(contentHeight));
// selection
bool selected = selectedEntry && (*selectedEntry >= 0);
if (selected)
{
customHeight = 0;
for (int i = 0; i < *selectedEntry; i++)
customHeight += sequence->GetCustomHeight(i);
draw_list->AddRectFilled(ImVec2(contentMin.x, contentMin.y + ItemHeight * *selectedEntry + customHeight), ImVec2(contentMin.x + canvas_size.x, contentMin.y + ItemHeight * (*selectedEntry + 1) + customHeight), 0x801080FF, 1.f);
}
// slots
customHeight = 0;
for (int i = 0; i < sequenceCount; i++)
{
int* start, * end;
unsigned int color;
sequence->Get(i, &start, &end, NULL, &color);
size_t localCustomHeight = sequence->GetCustomHeight(i);
ImVec2 pos = ImVec2(contentMin.x + legendWidth - firstFrameUsed * framePixelWidth, contentMin.y + ItemHeight * i + 1 + customHeight);
ImVec2 slotP1(pos.x + *start * framePixelWidth, pos.y + 2);
ImVec2 slotP2(pos.x + *end * framePixelWidth + framePixelWidth, pos.y + ItemHeight - 2);
ImVec2 slotP3(pos.x + *end * framePixelWidth + framePixelWidth, pos.y + ItemHeight - 2 + localCustomHeight);
unsigned int slotColor = color | 0xFF000000;
unsigned int slotColorHalf = (color & 0xFFFFFF) | 0x40000000;
if (slotP1.x <= (canvas_size.x + contentMin.x) && slotP2.x >= (contentMin.x + legendWidth))
{
draw_list->AddRectFilled(slotP1, slotP3, slotColorHalf, 2);
draw_list->AddRectFilled(slotP1, slotP2, slotColor, 2);
}
if (ImRect(slotP1, slotP2).Contains(io.MousePos) && io.MouseDoubleClicked[0])
{
sequence->DoubleClick(i);
}
// Ensure grabbable handles
const float max_handle_width = slotP2.x - slotP1.x / 3.0f;
const float min_handle_width = ImMin(10.0f, max_handle_width);
const float handle_width = ImClamp(framePixelWidth / 2.0f, min_handle_width, max_handle_width);
ImRect rects[3] = { ImRect(slotP1, ImVec2(slotP1.x + handle_width, slotP2.y))
, ImRect(ImVec2(slotP2.x - handle_width, slotP1.y), slotP2)
, ImRect(slotP1, slotP2) };
const unsigned int quadColor[] = { 0xFFFFFFFF, 0xFFFFFFFF, slotColor + (selected ? 0 : 0x202020) };
if (movingEntry == -1 && (sequenceOptions & SEQUENCER_EDIT_STARTEND))// TODOFOCUS && backgroundRect.Contains(io.MousePos))
{
for (int j = 2; j >= 0; j--)
{
ImRect& rc = rects[j];
if (!rc.Contains(io.MousePos))
continue;
draw_list->AddRectFilled(rc.Min, rc.Max, quadColor[j], 2);
}
for (int j = 0; j < 3; j++)
{
ImRect& rc = rects[j];
if (!rc.Contains(io.MousePos))
continue;
if (!ImRect(childFramePos, childFramePos + childFrameSize).Contains(io.MousePos))
continue;
if (ImGui::IsMouseClicked(0) && !MovingScrollBar && !MovingCurrentFrame)
{
movingEntry = i;
movingPos = cx;
movingPart = j + 1;
sequence->BeginEdit(movingEntry);
break;
}
}
}
// custom draw
if (localCustomHeight > 0)
{
ImVec2 rp(canvas_pos.x, contentMin.y + ItemHeight * i + 1 + customHeight);
ImRect customRect(rp + ImVec2(legendWidth - (firstFrameUsed - sequence->GetFrameMin() - 0.5f) * framePixelWidth, float(ItemHeight)),
rp + ImVec2(legendWidth + (sequence->GetFrameMax() - firstFrameUsed - 0.5f + 2.f) * framePixelWidth, float(localCustomHeight + ItemHeight)));
ImRect clippingRect(rp + ImVec2(float(legendWidth), float(ItemHeight)), rp + ImVec2(canvas_size.x, float(localCustomHeight + ItemHeight)));
ImRect legendRect(rp + ImVec2(0.f, float(ItemHeight)), rp + ImVec2(float(legendWidth), float(localCustomHeight)));
ImRect legendClippingRect(canvas_pos + ImVec2(0.f, float(ItemHeight)), canvas_pos + ImVec2(float(legendWidth), float(localCustomHeight + ItemHeight)));
customDraws.push_back({ i, customRect, legendRect, clippingRect, legendClippingRect });
}
else
{
ImVec2 rp(canvas_pos.x, contentMin.y + ItemHeight * i + customHeight);
ImRect customRect(rp + ImVec2(legendWidth - (firstFrameUsed - sequence->GetFrameMin() - 0.5f) * framePixelWidth, float(0.f)),
rp + ImVec2(legendWidth + (sequence->GetFrameMax() - firstFrameUsed - 0.5f + 2.f) * framePixelWidth, float(ItemHeight)));
ImRect clippingRect(rp + ImVec2(float(legendWidth), float(0.f)), rp + ImVec2(canvas_size.x, float(ItemHeight)));
compactCustomDraws.push_back({ i, customRect, ImRect(), clippingRect, ImRect() });
}
customHeight += localCustomHeight;
}
// moving
if (/*backgroundRect.Contains(io.MousePos) && */movingEntry >= 0)
{
#if IMGUI_VERSION_NUM >= 18723
ImGui::SetNextFrameWantCaptureMouse(true);
#else
ImGui::CaptureMouseFromApp();
#endif
int diffFrame = int((cx - movingPos) / framePixelWidth);
if (std::abs(diffFrame) > 0)
{
int* start, * end;
sequence->Get(movingEntry, &start, &end, NULL, NULL);
if (selectedEntry)
*selectedEntry = movingEntry;
int& l = *start;
int& r = *end;
if (movingPart & 1)
l += diffFrame;
if (movingPart & 2)
r += diffFrame;
if (l < 0)
{
if (movingPart & 2)
r -= l;
l = 0;
}
if (movingPart & 1 && l > r)
l = r;
if (movingPart & 2 && r < l)
r = l;
movingPos += int(diffFrame * framePixelWidth);
}
if (!io.MouseDown[0])
{
// single select
if (!diffFrame && movingPart && selectedEntry)
{
*selectedEntry = movingEntry;
ret = true;
}
movingEntry = -1;
sequence->EndEdit();
}
}
// cursor
if (currentFrame && firstFrame && *currentFrame >= *firstFrame && *currentFrame <= sequence->GetFrameMax())
{
static const float cursorWidth = 8.f;
float cursorOffset = contentMin.x + legendWidth + (*currentFrame - firstFrameUsed) * framePixelWidth + framePixelWidth / 2 - cursorWidth * 0.5f;
draw_list->AddLine(ImVec2(cursorOffset, canvas_pos.y), ImVec2(cursorOffset, contentMax.y), 0xA02A2AFF, cursorWidth);
char tmps[512];
ImFormatString(tmps, IM_ARRAYSIZE(tmps), "%d", *currentFrame);
draw_list->AddText(ImVec2(cursorOffset + 10, canvas_pos.y + 2), 0xFF2A2AFF, tmps);
}
draw_list->PopClipRect();
draw_list->PopClipRect();
for (auto& customDraw : customDraws)
sequence->CustomDraw(customDraw.index, draw_list, customDraw.customRect, customDraw.legendRect, customDraw.clippingRect, customDraw.legendClippingRect);
for (auto& customDraw : compactCustomDraws)
sequence->CustomDrawCompact(customDraw.index, draw_list, customDraw.customRect, customDraw.clippingRect);
// copy paste
if (sequenceOptions & SEQUENCER_COPYPASTE)
{
ImRect rectCopy(ImVec2(contentMin.x + 100, canvas_pos.y + 2)
, ImVec2(contentMin.x + 100 + 30, canvas_pos.y + ItemHeight - 2));
bool inRectCopy = rectCopy.Contains(io.MousePos);
unsigned int copyColor = inRectCopy ? 0xFF1080FF : 0xFF000000;
draw_list->AddText(rectCopy.Min, copyColor, "Copy");
ImRect rectPaste(ImVec2(contentMin.x + 140, canvas_pos.y + 2)
, ImVec2(contentMin.x + 140 + 30, canvas_pos.y + ItemHeight - 2));
bool inRectPaste = rectPaste.Contains(io.MousePos);
unsigned int pasteColor = inRectPaste ? 0xFF1080FF : 0xFF000000;
draw_list->AddText(rectPaste.Min, pasteColor, "Paste");
if (inRectCopy && io.MouseReleased[0])
{
sequence->Copy();
}
if (inRectPaste && io.MouseReleased[0])
{
sequence->Paste();
}
}
//
ImGui::EndChildFrame();
ImGui::PopStyleColor();
if (hasScrollBar)
{
ImGui::InvisibleButton("scrollBar", scrollBarSize);
ImVec2 scrollBarMin = ImGui::GetItemRectMin();
ImVec2 scrollBarMax = ImGui::GetItemRectMax();
// ratio = number of frames visible in control / number to total frames
float startFrameOffset = ((float)(firstFrameUsed - sequence->GetFrameMin()) / (float)frameCount) * (canvas_size.x - legendWidth);
ImVec2 scrollBarA(scrollBarMin.x + legendWidth, scrollBarMin.y - 2);
ImVec2 scrollBarB(scrollBarMin.x + canvas_size.x, scrollBarMax.y - 1);
draw_list->AddRectFilled(scrollBarA, scrollBarB, 0xFF222222, 0);
ImRect scrollBarRect(scrollBarA, scrollBarB);
bool inScrollBar = scrollBarRect.Contains(io.MousePos);
draw_list->AddRectFilled(scrollBarA, scrollBarB, 0xFF101010, 8);
ImVec2 scrollBarC(scrollBarMin.x + legendWidth + startFrameOffset, scrollBarMin.y);
ImVec2 scrollBarD(scrollBarMin.x + legendWidth + barWidthInPixels + startFrameOffset, scrollBarMax.y - 2);
draw_list->AddRectFilled(scrollBarC, scrollBarD, (inScrollBar || MovingScrollBar) ? 0xFF606060 : 0xFF505050, 6);
ImRect barHandleLeft(scrollBarC, ImVec2(scrollBarC.x + 14, scrollBarD.y));
ImRect barHandleRight(ImVec2(scrollBarD.x - 14, scrollBarC.y), scrollBarD);
bool onLeft = barHandleLeft.Contains(io.MousePos);
bool onRight = barHandleRight.Contains(io.MousePos);
static bool sizingRBar = false;
static bool sizingLBar = false;
draw_list->AddRectFilled(barHandleLeft.Min, barHandleLeft.Max, (onLeft || sizingLBar) ? 0xFFAAAAAA : 0xFF666666, 6);
draw_list->AddRectFilled(barHandleRight.Min, barHandleRight.Max, (onRight || sizingRBar) ? 0xFFAAAAAA : 0xFF666666, 6);
ImRect scrollBarThumb(scrollBarC, scrollBarD);
static const float MinBarWidth = 44.f;
if (sizingRBar)
{
if (!io.MouseDown[0])
{
sizingRBar = false;
}
else
{
float barNewWidth = ImMax(barWidthInPixels + io.MouseDelta.x, MinBarWidth);
float barRatio = barNewWidth / barWidthInPixels;
framePixelWidthTarget = framePixelWidth = framePixelWidth / barRatio;
int newVisibleFrameCount = int((canvas_size.x - legendWidth) / framePixelWidthTarget);
int lastFrame = *firstFrame + newVisibleFrameCount;
if (lastFrame > sequence->GetFrameMax())
{
framePixelWidthTarget = framePixelWidth = (canvas_size.x - legendWidth) / float(sequence->GetFrameMax() - *firstFrame);
}
}
}
else if (sizingLBar)
{
if (!io.MouseDown[0])
{
sizingLBar = false;
}
else
{
if (fabsf(io.MouseDelta.x) > FLT_EPSILON)
{
float barNewWidth = ImMax(barWidthInPixels - io.MouseDelta.x, MinBarWidth);
float barRatio = barNewWidth / barWidthInPixels;
float previousFramePixelWidthTarget = framePixelWidthTarget;
framePixelWidthTarget = framePixelWidth = framePixelWidth / barRatio;
int newVisibleFrameCount = int(visibleFrameCount / barRatio);
int newFirstFrame = *firstFrame + newVisibleFrameCount - visibleFrameCount;
newFirstFrame = ImClamp(newFirstFrame, sequence->GetFrameMin(), ImMax(sequence->GetFrameMax() - visibleFrameCount, sequence->GetFrameMin()));
if (newFirstFrame == *firstFrame)
{
framePixelWidth = framePixelWidthTarget = previousFramePixelWidthTarget;
}
else
{
*firstFrame = newFirstFrame;
}
}
}
}
else
{
if (MovingScrollBar)
{
if (!io.MouseDown[0])
{
MovingScrollBar = false;
}
else
{
float framesPerPixelInBar = barWidthInPixels / (float)visibleFrameCount;
*firstFrame = int((io.MousePos.x - panningViewSource.x) / framesPerPixelInBar) - panningViewFrame;
*firstFrame = ImClamp(*firstFrame, sequence->GetFrameMin(), ImMax(sequence->GetFrameMax() - visibleFrameCount, sequence->GetFrameMin()));
}
}
else
{
if (scrollBarThumb.Contains(io.MousePos) && ImGui::IsMouseClicked(0) && firstFrame && !MovingCurrentFrame && movingEntry == -1)
{
MovingScrollBar = true;
panningViewSource = io.MousePos;
panningViewFrame = -*firstFrame;
}
if (!sizingRBar && onRight && ImGui::IsMouseClicked(0))
sizingRBar = true;
if (!sizingLBar && onLeft && ImGui::IsMouseClicked(0))
sizingLBar = true;
}
}
}
}
ImGui::EndGroup();
if (regionRect.Contains(io.MousePos))
{
bool overCustomDraw = false;
for (auto& custom : customDraws)
{
if (custom.customRect.Contains(io.MousePos))
{
overCustomDraw = true;
}
}
if (overCustomDraw)
{
}
else
{
#if 0
frameOverCursor = *firstFrame + (int)(visibleFrameCount * ((io.MousePos.x - (float)legendWidth - canvas_pos.x) / (canvas_size.x - legendWidth)));
//frameOverCursor = max(min(*firstFrame - visibleFrameCount / 2, frameCount - visibleFrameCount), 0);
/**firstFrame -= frameOverCursor;
*firstFrame *= framePixelWidthTarget / framePixelWidth;
*firstFrame += frameOverCursor;*/
if (io.MouseWheel < -FLT_EPSILON)
{
*firstFrame -= frameOverCursor;
*firstFrame = int(*firstFrame * 1.1f);
framePixelWidthTarget *= 0.9f;
*firstFrame += frameOverCursor;
}
if (io.MouseWheel > FLT_EPSILON)
{
*firstFrame -= frameOverCursor;
*firstFrame = int(*firstFrame * 0.9f);
framePixelWidthTarget *= 1.1f;
*firstFrame += frameOverCursor;
}
#endif
}
}
if (expanded)
{
if (SequencerAddDelButton(draw_list, ImVec2(canvas_pos.x + 2, canvas_pos.y + 2), !*expanded))
*expanded = !*expanded;
}
if (delEntry != -1)
{
sequence->Del(delEntry);
if (selectedEntry && (*selectedEntry == delEntry || *selectedEntry >= sequence->GetItemCount()))
*selectedEntry = -1;
}
if (dupEntry != -1)
{
sequence->Duplicate(dupEntry);
}
return ret;
}
}

View file

@ -0,0 +1,79 @@
// https://github.com/CedricGuillemet/ImGuizmo
// v 1.89 WIP
//
// The MIT License(MIT)
//
// Copyright(c) 2021 Cedric Guillemet
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files(the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and / or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions :
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
//
#pragma once
#include <cstddef>
struct ImDrawList;
struct ImRect;
namespace ImSequencer
{
enum SEQUENCER_OPTIONS
{
SEQUENCER_EDIT_NONE = 0,
SEQUENCER_EDIT_STARTEND = 1 << 1,
SEQUENCER_CHANGE_FRAME = 1 << 3,
SEQUENCER_ADD = 1 << 4,
SEQUENCER_DEL = 1 << 5,
SEQUENCER_COPYPASTE = 1 << 6,
SEQUENCER_EDIT_ALL = SEQUENCER_EDIT_STARTEND | SEQUENCER_CHANGE_FRAME
};
struct SequenceInterface
{
bool focused = false;
virtual int GetFrameMin() const = 0;
virtual int GetFrameMax() const = 0;
virtual int GetItemCount() const = 0;
virtual void BeginEdit(int /*index*/) {}
virtual void EndEdit() {}
virtual int GetItemTypeCount() const { return 0; }
virtual const char* GetItemTypeName(int /*typeIndex*/) const { return ""; }
virtual const char* GetItemLabel(int /*index*/) const { return ""; }
virtual const char* GetCollapseFmt() const { return "%d Frames / %d entries"; }
virtual void Get(int index, int** start, int** end, int* type, unsigned int* color) = 0;
virtual void Add(int /*type*/) {}
virtual void Del(int /*index*/) {}
virtual void Duplicate(int /*index*/) {}
virtual void Copy() {}
virtual void Paste() {}
virtual size_t GetCustomHeight(int /*index*/) { return 0; }
virtual void DoubleClick(int /*index*/) {}
virtual void CustomDraw(int /*index*/, ImDrawList* /*draw_list*/, const ImRect& /*rc*/, const ImRect& /*legendRect*/, const ImRect& /*clippingRect*/, const ImRect& /*legendClippingRect*/) {}
virtual void CustomDrawCompact(int /*index*/, ImDrawList* /*draw_list*/, const ImRect& /*rc*/, const ImRect& /*clippingRect*/) {}
virtual ~SequenceInterface() = default;
};
// return true if selection is made
bool Sequencer(SequenceInterface* sequence, int* currentFrame, bool* expanded, int* selectedEntry, int* firstFrame, int sequenceOptions);
}

View file

@ -0,0 +1,245 @@
// https://github.com/CedricGuillemet/ImGuizmo
// v 1.89 WIP
//
// The MIT License(MIT)
//
// Copyright(c) 2021 Cedric Guillemet
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files(the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and / or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions :
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
//
#pragma once
namespace ImZoomSlider
{
typedef int ImGuiZoomSliderFlags;
enum ImGuiPopupFlags_
{
ImGuiZoomSliderFlags_None = 0,
ImGuiZoomSliderFlags_Vertical = 1,
ImGuiZoomSliderFlags_NoAnchors = 2,
ImGuiZoomSliderFlags_NoMiddleCarets = 4,
ImGuiZoomSliderFlags_NoWheel = 8,
};
template<typename T> bool ImZoomSlider(const T lower, const T higher, T& viewLower, T& viewHigher, float wheelRatio = 0.01f, ImGuiZoomSliderFlags flags = ImGuiZoomSliderFlags_None)
{
bool interacted = false;
ImGuiIO& io = ImGui::GetIO();
ImDrawList* draw_list = ImGui::GetWindowDrawList();
static const float handleSize = 12;
static const float roundRadius = 3.f;
static const char* controlName = "ImZoomSlider";
static bool movingScrollBarSvg = false;
static bool sizingRBarSvg = false;
static bool sizingLBarSvg = false;
static ImGuiID editingId = (ImGuiID)-1;
static float scrollingSource = 0.f;
static float saveViewLower;
static float saveViewHigher;
const bool isVertical = flags & ImGuiZoomSliderFlags_Vertical;
const ImVec2 canvasPos = ImGui::GetCursorScreenPos();
const ImVec2 canvasSize = ImGui::GetContentRegionAvail();
const float canvasSizeLength = isVertical ? ImGui::GetItemRectSize().y : canvasSize.x;
const ImVec2 scrollBarSize = isVertical ? ImVec2(14.f, canvasSizeLength) : ImVec2(canvasSizeLength, 14.f);
ImGui::InvisibleButton(controlName, scrollBarSize);
const ImGuiID currentId = ImGui::GetID(controlName);
const bool usingEditingId = currentId == editingId;
const bool canUseControl = usingEditingId || editingId == -1;
const bool movingScrollBar = usingEditingId ? movingScrollBarSvg : false;
const bool sizingRBar = usingEditingId ? sizingRBarSvg : false;
const bool sizingLBar = usingEditingId ? sizingLBarSvg : false;
const int componentIndex = isVertical ? 1 : 0;
const ImVec2 scrollBarMin = ImGui::GetItemRectMin();
const ImVec2 scrollBarMax = ImGui::GetItemRectMax();
const ImVec2 scrollBarA = ImVec2(scrollBarMin.x, scrollBarMin.y) - (isVertical ? ImVec2(2,0) : ImVec2(0,2));
const ImVec2 scrollBarB = isVertical ? ImVec2(scrollBarMax.x - 1.f, scrollBarMin.y + canvasSizeLength) : ImVec2(scrollBarMin.x + canvasSizeLength, scrollBarMax.y - 1.f);
const float scrollStart = ((viewLower - lower) / (higher - lower)) * canvasSizeLength + scrollBarMin[componentIndex];
const float scrollEnd = ((viewHigher - lower) / (higher - lower)) * canvasSizeLength + scrollBarMin[componentIndex];
const float screenSize = scrollEnd - scrollStart;
const ImVec2 scrollTopLeft = isVertical ? ImVec2(scrollBarMin.x, scrollStart) : ImVec2(scrollStart, scrollBarMin.y);
const ImVec2 scrollBottomRight = isVertical ? ImVec2(scrollBarMax.x - 2.f, scrollEnd) : ImVec2(scrollEnd, scrollBarMax.y - 2.f);
const bool inScrollBar = canUseControl && ImRect(scrollTopLeft, scrollBottomRight).Contains(io.MousePos);
const ImRect scrollBarRect(scrollBarA, scrollBarB);
const float deltaScreen = io.MousePos[componentIndex] - scrollingSource;
const float deltaView = ((higher - lower) / canvasSizeLength) * deltaScreen;
const uint32_t barColor = ImGui::GetColorU32((inScrollBar || movingScrollBar) ? ImGuiCol_FrameBgHovered : ImGuiCol_FrameBg);
const float middleCoord = (scrollStart + scrollEnd) * 0.5f;
const bool insideControl = canUseControl && ImRect(scrollBarMin, scrollBarMax).Contains(io.MousePos);
const bool hasAnchors = !(flags & ImGuiZoomSliderFlags_NoAnchors);
const float viewMinSize = ((3.f * handleSize) / canvasSizeLength) * (higher - lower);
const auto ClipView = [lower, higher, &viewLower, &viewHigher]() {
if (viewLower < lower)
{
const float deltaClip = lower - viewLower;
viewLower += deltaClip;
viewHigher += deltaClip;
}
if (viewHigher > higher)
{
const float deltaClip = viewHigher - higher;
viewLower -= deltaClip;
viewHigher -= deltaClip;
}
};
bool onLeft = false;
bool onRight = false;
draw_list->AddRectFilled(scrollBarA, scrollBarB, 0xFF101010, roundRadius);
draw_list->AddRectFilled(scrollBarA, scrollBarB, 0xFF222222, 0);
draw_list->AddRectFilled(scrollTopLeft, scrollBottomRight, barColor, roundRadius);
if (!(flags & ImGuiZoomSliderFlags_NoMiddleCarets))
{
for (float i = 0.5f; i < 3.f; i += 1.f)
{
const float coordA = middleCoord - handleSize * 0.5f;
const float coordB = middleCoord + handleSize * 0.5f;
ImVec2 base = scrollBarMin;
base.x += scrollBarSize.x * 0.25f * i;
base.y += scrollBarSize.y * 0.25f * i;
if (isVertical)
{
draw_list->AddLine(ImVec2(base.x, coordA), ImVec2(base.x, coordB), ImGui::GetColorU32(ImGuiCol_SliderGrab));
}
else
{
draw_list->AddLine(ImVec2(coordA, base.y), ImVec2(coordB, base.y), ImGui::GetColorU32(ImGuiCol_SliderGrab));
}
}
}
// Mouse wheel
if (io.MouseClicked[0] && insideControl && !inScrollBar)
{
const float ratio = (io.MousePos[componentIndex] - scrollBarMin[componentIndex]) / (scrollBarMax[componentIndex] - scrollBarMin[componentIndex]);
const float size = (higher - lower);
const float halfViewSize = (viewHigher - viewLower) * 0.5f;
const float middle = ratio * size + lower;
viewLower = middle - halfViewSize;
viewHigher = middle + halfViewSize;
ClipView();
interacted = true;
}
if (!(flags & ImGuiZoomSliderFlags_NoWheel) && inScrollBar && fabsf(io.MouseWheel) > 0.f)
{
const float ratio = (io.MousePos[componentIndex] - scrollStart) / (scrollEnd - scrollStart);
const float amount = io.MouseWheel * wheelRatio * (viewHigher - viewLower);
viewLower -= ratio * amount;
viewHigher += (1.f - ratio) * amount;
ClipView();
interacted = true;
}
if (screenSize > handleSize * 2.f && hasAnchors)
{
const ImRect barHandleLeft(scrollTopLeft, isVertical ? ImVec2(scrollBottomRight.x, scrollTopLeft.y + handleSize) : ImVec2(scrollTopLeft.x + handleSize, scrollBottomRight.y));
const ImRect barHandleRight(isVertical ? ImVec2(scrollTopLeft.x, scrollBottomRight.y - handleSize) : ImVec2(scrollBottomRight.x - handleSize, scrollTopLeft.y), scrollBottomRight);
onLeft = barHandleLeft.Contains(io.MousePos);
onRight = barHandleRight.Contains(io.MousePos);
draw_list->AddRectFilled(barHandleLeft.Min, barHandleLeft.Max, ImGui::GetColorU32((onLeft || sizingLBar) ? ImGuiCol_SliderGrabActive : ImGuiCol_SliderGrab), roundRadius);
draw_list->AddRectFilled(barHandleRight.Min, barHandleRight.Max, ImGui::GetColorU32((onRight || sizingRBar) ? ImGuiCol_SliderGrabActive : ImGuiCol_SliderGrab), roundRadius);
}
if (sizingRBar)
{
if (!io.MouseDown[0])
{
sizingRBarSvg = false;
editingId = (ImGuiID)-1;
}
else
{
viewHigher = ImMin(saveViewHigher + deltaView, higher);
}
}
else if (sizingLBar)
{
if (!io.MouseDown[0])
{
sizingLBarSvg = false;
editingId = (ImGuiID)-1;
}
else
{
viewLower = ImMax(saveViewLower + deltaView, lower);
}
}
else
{
if (movingScrollBar)
{
if (!io.MouseDown[0])
{
movingScrollBarSvg = false;
editingId = (ImGuiID)-1;
}
else
{
viewLower = saveViewLower + deltaView;
viewHigher = saveViewHigher + deltaView;
ClipView();
}
}
else
{
if (inScrollBar && ImGui::IsMouseClicked(0))
{
movingScrollBarSvg = true;
scrollingSource = io.MousePos[componentIndex];
saveViewLower = viewLower;
saveViewHigher = viewHigher;
editingId = currentId;
}
if (!sizingRBar && onRight && ImGui::IsMouseClicked(0) && hasAnchors)
{
sizingRBarSvg = true;
editingId = currentId;
}
if (!sizingLBar && onLeft && ImGui::IsMouseClicked(0) && hasAnchors)
{
sizingLBarSvg = true;
editingId = currentId;
}
}
}
// minimal size check
if ((viewHigher - viewLower) < viewMinSize)
{
const float middle = (viewLower + viewHigher) * 0.5f;
viewLower = middle - viewMinSize * 0.5f;
viewHigher = middle + viewMinSize * 0.5f;
ClipView();
}
return movingScrollBar || sizingRBar || sizingLBar || interacted;
}
} // namespace

View file

@ -21,10 +21,11 @@
//---- Define attributes of all API symbols declarations, e.g. for DLL under Windows
// Using Dear ImGui via a shared library is not recommended, because of function call overhead and because we don't guarantee backward nor forward ABI compatibility.
// DLL users: heaps and globals are not shared across DLL boundaries! You will need to call SetCurrentContext() + SetAllocatorFunctions()
// for each static/DLL boundary you are calling from. Read "Context and Memory Allocators" section of imgui.cpp for more details.
//#define IMGUI_API __declspec( dllexport )
//#define IMGUI_API __declspec( dllimport )
// - Windows DLL users: heaps and globals are not shared across DLL boundaries! You will need to call SetCurrentContext() + SetAllocatorFunctions()
// for each static/DLL boundary you are calling from. Read "Context and Memory Allocators" section of imgui.cpp for more details.
//#define IMGUI_API __declspec(dllexport) // MSVC Windows: DLL export
//#define IMGUI_API __declspec(dllimport) // MSVC Windows: DLL import
//#define IMGUI_API __attribute__((visibility("default"))) // GCC/Clang: override visibility when set is hidden
//---- Don't define obsolete functions/enums/behaviors. Consider enabling from time to time after updating to clean your code of obsolete function/names.
//#define IMGUI_DISABLE_OBSOLETE_FUNCTIONS
@ -42,6 +43,7 @@
//#define IMGUI_DISABLE_WIN32_DEFAULT_IME_FUNCTIONS // [Win32] [Default with non-Visual Studio compilers] Don't implement default IME handler (won't require imm32.lib/.a)
//#define IMGUI_DISABLE_WIN32_FUNCTIONS // [Win32] Won't use and link with any Win32 function (clipboard, IME).
//#define IMGUI_ENABLE_OSX_DEFAULT_CLIPBOARD_FUNCTIONS // [OSX] Implement default OSX clipboard handler (need to link with '-framework ApplicationServices', this is why this is not the default).
//#define IMGUI_DISABLE_DEFAULT_SHELL_FUNCTIONS // Don't implement default platform_io.Platform_OpenInShellFn() handler (Win32: ShellExecute(), require shell32.lib/.a, Mac/Linux: use system("")).
//#define IMGUI_DISABLE_DEFAULT_FORMAT_FUNCTIONS // Don't implement ImFormatString/ImFormatStringV so you can implement them yourself (e.g. if you don't want to link with vsnprintf)
//#define IMGUI_DISABLE_DEFAULT_MATH_FUNCTIONS // Don't implement ImFabs/ImSqrt/ImPow/ImFmod/ImCos/ImSin/ImAcos/ImAtan2 so you can implement them yourself.
//#define IMGUI_DISABLE_FILE_FUNCTIONS // Don't implement ImFileOpen/ImFileClose/ImFileRead/ImFileWrite and ImFileHandle at all (replace them with dummies)
@ -49,6 +51,9 @@
//#define IMGUI_DISABLE_DEFAULT_ALLOCATORS // Don't implement default allocators calling malloc()/free() to avoid linking with them. You will need to call ImGui::SetAllocatorFunctions().
//#define IMGUI_DISABLE_SSE // Disable use of SSE intrinsics even if available
//---- Enable Test Engine / Automation features.
//#define IMGUI_ENABLE_TEST_ENGINE // Enable imgui_test_engine hooks. Generally set automatically by include "imgui_te_config.h", see Test Engine for details.
//---- Include imgui_user.h at the end of imgui.h as a convenience
// May be convenient for some users to only explicitly include vanilla imgui.h and have extra stuff included.
//#define IMGUI_INCLUDE_IMGUI_USER_H

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -1,4 +1,4 @@
// dear imgui, v1.90.8 WIP
// dear imgui, v1.91.1
// (drawing and font code)
/*
@ -211,11 +211,13 @@ void ImGui::StyleColorsDark(ImGuiStyle* dst)
colors[ImGuiCol_ResizeGrip] = ImVec4(0.26f, 0.59f, 0.98f, 0.20f);
colors[ImGuiCol_ResizeGripHovered] = ImVec4(0.26f, 0.59f, 0.98f, 0.67f);
colors[ImGuiCol_ResizeGripActive] = ImVec4(0.26f, 0.59f, 0.98f, 0.95f);
colors[ImGuiCol_Tab] = ImLerp(colors[ImGuiCol_Header], colors[ImGuiCol_TitleBgActive], 0.80f);
colors[ImGuiCol_TabHovered] = colors[ImGuiCol_HeaderHovered];
colors[ImGuiCol_TabActive] = ImLerp(colors[ImGuiCol_HeaderActive], colors[ImGuiCol_TitleBgActive], 0.60f);
colors[ImGuiCol_TabUnfocused] = ImLerp(colors[ImGuiCol_Tab], colors[ImGuiCol_TitleBg], 0.80f);
colors[ImGuiCol_TabUnfocusedActive] = ImLerp(colors[ImGuiCol_TabActive], colors[ImGuiCol_TitleBg], 0.40f);
colors[ImGuiCol_Tab] = ImLerp(colors[ImGuiCol_Header], colors[ImGuiCol_TitleBgActive], 0.80f);
colors[ImGuiCol_TabSelected] = ImLerp(colors[ImGuiCol_HeaderActive], colors[ImGuiCol_TitleBgActive], 0.60f);
colors[ImGuiCol_TabSelectedOverline] = colors[ImGuiCol_HeaderActive];
colors[ImGuiCol_TabDimmed] = ImLerp(colors[ImGuiCol_Tab], colors[ImGuiCol_TitleBg], 0.80f);
colors[ImGuiCol_TabDimmedSelected] = ImLerp(colors[ImGuiCol_TabSelected], colors[ImGuiCol_TitleBg], 0.40f);
colors[ImGuiCol_TabDimmedSelectedOverline] = ImVec4(0.50f, 0.50f, 0.50f, 1.00f);
colors[ImGuiCol_PlotLines] = ImVec4(0.61f, 0.61f, 0.61f, 1.00f);
colors[ImGuiCol_PlotLinesHovered] = ImVec4(1.00f, 0.43f, 0.35f, 1.00f);
colors[ImGuiCol_PlotHistogram] = ImVec4(0.90f, 0.70f, 0.00f, 1.00f);
@ -225,6 +227,7 @@ void ImGui::StyleColorsDark(ImGuiStyle* dst)
colors[ImGuiCol_TableBorderLight] = ImVec4(0.23f, 0.23f, 0.25f, 1.00f); // Prefer using Alpha=1.0 here
colors[ImGuiCol_TableRowBg] = ImVec4(0.00f, 0.00f, 0.00f, 0.00f);
colors[ImGuiCol_TableRowBgAlt] = ImVec4(1.00f, 1.00f, 1.00f, 0.06f);
colors[ImGuiCol_TextLink] = colors[ImGuiCol_HeaderActive];
colors[ImGuiCol_TextSelectedBg] = ImVec4(0.26f, 0.59f, 0.98f, 0.35f);
colors[ImGuiCol_DragDropTarget] = ImVec4(1.00f, 1.00f, 0.00f, 0.90f);
colors[ImGuiCol_NavHighlight] = ImVec4(0.26f, 0.59f, 0.98f, 1.00f);
@ -271,11 +274,13 @@ void ImGui::StyleColorsClassic(ImGuiStyle* dst)
colors[ImGuiCol_ResizeGrip] = ImVec4(1.00f, 1.00f, 1.00f, 0.10f);
colors[ImGuiCol_ResizeGripHovered] = ImVec4(0.78f, 0.82f, 1.00f, 0.60f);
colors[ImGuiCol_ResizeGripActive] = ImVec4(0.78f, 0.82f, 1.00f, 0.90f);
colors[ImGuiCol_Tab] = ImLerp(colors[ImGuiCol_Header], colors[ImGuiCol_TitleBgActive], 0.80f);
colors[ImGuiCol_TabHovered] = colors[ImGuiCol_HeaderHovered];
colors[ImGuiCol_TabActive] = ImLerp(colors[ImGuiCol_HeaderActive], colors[ImGuiCol_TitleBgActive], 0.60f);
colors[ImGuiCol_TabUnfocused] = ImLerp(colors[ImGuiCol_Tab], colors[ImGuiCol_TitleBg], 0.80f);
colors[ImGuiCol_TabUnfocusedActive] = ImLerp(colors[ImGuiCol_TabActive], colors[ImGuiCol_TitleBg], 0.40f);
colors[ImGuiCol_Tab] = ImLerp(colors[ImGuiCol_Header], colors[ImGuiCol_TitleBgActive], 0.80f);
colors[ImGuiCol_TabSelected] = ImLerp(colors[ImGuiCol_HeaderActive], colors[ImGuiCol_TitleBgActive], 0.60f);
colors[ImGuiCol_TabSelectedOverline] = colors[ImGuiCol_HeaderActive];
colors[ImGuiCol_TabDimmed] = ImLerp(colors[ImGuiCol_Tab], colors[ImGuiCol_TitleBg], 0.80f);
colors[ImGuiCol_TabDimmedSelected] = ImLerp(colors[ImGuiCol_TabSelected], colors[ImGuiCol_TitleBg], 0.40f);
colors[ImGuiCol_TabDimmedSelectedOverline] = colors[ImGuiCol_HeaderActive];
colors[ImGuiCol_PlotLines] = ImVec4(1.00f, 1.00f, 1.00f, 1.00f);
colors[ImGuiCol_PlotLinesHovered] = ImVec4(0.90f, 0.70f, 0.00f, 1.00f);
colors[ImGuiCol_PlotHistogram] = ImVec4(0.90f, 0.70f, 0.00f, 1.00f);
@ -285,6 +290,7 @@ void ImGui::StyleColorsClassic(ImGuiStyle* dst)
colors[ImGuiCol_TableBorderLight] = ImVec4(0.26f, 0.26f, 0.28f, 1.00f); // Prefer using Alpha=1.0 here
colors[ImGuiCol_TableRowBg] = ImVec4(0.00f, 0.00f, 0.00f, 0.00f);
colors[ImGuiCol_TableRowBgAlt] = ImVec4(1.00f, 1.00f, 1.00f, 0.07f);
colors[ImGuiCol_TextLink] = colors[ImGuiCol_HeaderActive];
colors[ImGuiCol_TextSelectedBg] = ImVec4(0.00f, 0.00f, 1.00f, 0.35f);
colors[ImGuiCol_DragDropTarget] = ImVec4(1.00f, 1.00f, 0.00f, 0.90f);
colors[ImGuiCol_NavHighlight] = colors[ImGuiCol_HeaderHovered];
@ -332,11 +338,13 @@ void ImGui::StyleColorsLight(ImGuiStyle* dst)
colors[ImGuiCol_ResizeGrip] = ImVec4(0.35f, 0.35f, 0.35f, 0.17f);
colors[ImGuiCol_ResizeGripHovered] = ImVec4(0.26f, 0.59f, 0.98f, 0.67f);
colors[ImGuiCol_ResizeGripActive] = ImVec4(0.26f, 0.59f, 0.98f, 0.95f);
colors[ImGuiCol_Tab] = ImLerp(colors[ImGuiCol_Header], colors[ImGuiCol_TitleBgActive], 0.90f);
colors[ImGuiCol_TabHovered] = colors[ImGuiCol_HeaderHovered];
colors[ImGuiCol_TabActive] = ImLerp(colors[ImGuiCol_HeaderActive], colors[ImGuiCol_TitleBgActive], 0.60f);
colors[ImGuiCol_TabUnfocused] = ImLerp(colors[ImGuiCol_Tab], colors[ImGuiCol_TitleBg], 0.80f);
colors[ImGuiCol_TabUnfocusedActive] = ImLerp(colors[ImGuiCol_TabActive], colors[ImGuiCol_TitleBg], 0.40f);
colors[ImGuiCol_Tab] = ImLerp(colors[ImGuiCol_Header], colors[ImGuiCol_TitleBgActive], 0.90f);
colors[ImGuiCol_TabSelected] = ImLerp(colors[ImGuiCol_HeaderActive], colors[ImGuiCol_TitleBgActive], 0.60f);
colors[ImGuiCol_TabSelectedOverline] = colors[ImGuiCol_HeaderActive];
colors[ImGuiCol_TabDimmed] = ImLerp(colors[ImGuiCol_Tab], colors[ImGuiCol_TitleBg], 0.80f);
colors[ImGuiCol_TabDimmedSelected] = ImLerp(colors[ImGuiCol_TabSelected], colors[ImGuiCol_TitleBg], 0.40f);
colors[ImGuiCol_TabDimmedSelectedOverline] = ImVec4(0.26f, 0.59f, 1.00f, 1.00f);
colors[ImGuiCol_PlotLines] = ImVec4(0.39f, 0.39f, 0.39f, 1.00f);
colors[ImGuiCol_PlotLinesHovered] = ImVec4(1.00f, 0.43f, 0.35f, 1.00f);
colors[ImGuiCol_PlotHistogram] = ImVec4(0.90f, 0.70f, 0.00f, 1.00f);
@ -346,6 +354,7 @@ void ImGui::StyleColorsLight(ImGuiStyle* dst)
colors[ImGuiCol_TableBorderLight] = ImVec4(0.68f, 0.68f, 0.74f, 1.00f); // Prefer using Alpha=1.0 here
colors[ImGuiCol_TableRowBg] = ImVec4(0.00f, 0.00f, 0.00f, 0.00f);
colors[ImGuiCol_TableRowBgAlt] = ImVec4(0.30f, 0.30f, 0.30f, 0.09f);
colors[ImGuiCol_TextLink] = colors[ImGuiCol_HeaderActive];
colors[ImGuiCol_TextSelectedBg] = ImVec4(0.26f, 0.59f, 0.98f, 0.35f);
colors[ImGuiCol_DragDropTarget] = ImVec4(0.26f, 0.59f, 0.98f, 0.95f);
colors[ImGuiCol_NavHighlight] = colors[ImGuiCol_HeaderHovered];
@ -517,7 +526,6 @@ void ImDrawList::_OnChangedClipRect()
CmdBuffer.pop_back();
return;
}
curr_cmd->ClipRect = _CmdHeader.ClipRect;
}
@ -540,7 +548,6 @@ void ImDrawList::_OnChangedTextureID()
CmdBuffer.pop_back();
return;
}
curr_cmd->TextureId = _CmdHeader.TextureId;
}
@ -616,6 +623,15 @@ void ImDrawList::PopTextureID()
_OnChangedTextureID();
}
// This is used by ImGui::PushFont()/PopFont(). It works because we never use _TextureIdStack[] elsewhere than in PushTextureID()/PopTextureID().
void ImDrawList::_SetTextureID(ImTextureID texture_id)
{
if (_CmdHeader.TextureId == texture_id)
return;
_CmdHeader.TextureId = texture_id;
_OnChangedTextureID();
}
// Reserve space for a number of vertices and indices.
// You must finish filling your reserved data before calling PrimReserve() again, as it may reallocate or
// submit the intermediate results. PrimUnreserve() can be used to release unused allocations.
@ -2806,8 +2822,9 @@ static bool ImFontAtlasBuildWithStbTruetype(ImFontAtlas* atlas)
for (const ImWchar* src_range = src_tmp.SrcRanges; src_range[0] && src_range[1]; src_range += 2)
{
// Check for valid range. This may also help detect *some* dangling pointers, because a common
// user error is to setup ImFontConfig::GlyphRanges with a pointer to data that isn't persistent.
IM_ASSERT(src_range[0] <= src_range[1]);
// user error is to setup ImFontConfig::GlyphRanges with a pointer to data that isn't persistent,
// or to forget to zero-terminate the glyph range array.
IM_ASSERT(src_range[0] <= src_range[1] && "Invalid range: is your glyph range array persistent? it is zero-terminated?");
src_tmp.GlyphsHighest = ImMax(src_tmp.GlyphsHighest, (int)src_range[1]);
}
dst_tmp.SrcCount++;

View file

@ -1,4 +1,4 @@
// dear imgui, v1.90.8 WIP
// dear imgui, v1.91.1
// (internal structures/api)
// You may use this file to debug, understand or extend Dear ImGui features but we don't provide any guarantee of forward compatibility.
@ -14,14 +14,15 @@ Index of this file:
// [SECTION] Macros
// [SECTION] Generic helpers
// [SECTION] ImDrawList support
// [SECTION] Widgets support: flags, enums, data structures
// [SECTION] Data types support
// [SECTION] Widgets support: flags, enums, data structures
// [SECTION] Popup support
// [SECTION] Inputs support
// [SECTION] Clipper support
// [SECTION] Navigation support
// [SECTION] Typing-select support
// [SECTION] Columns support
// [SECTION] Box-select support
// [SECTION] Multi-select support
// [SECTION] Docking support
// [SECTION] Viewport support
@ -93,6 +94,7 @@ Index of this file:
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wpragmas" // warning: unknown option after '#pragma GCC diagnostic' kind
#pragma GCC diagnostic ignored "-Wclass-memaccess" // [__GNUC__ >= 8] warning: 'memset/memcpy' clearing/writing an object of type 'xxxx' with no trivial copy-assignment; use assignment or value-initialization instead
#pragma GCC diagnostic ignored "-Wdeprecated-enum-enum-conversion" // warning: bitwise operation between different enumeration types ('XXXFlags_' and 'XXXFlagsPrivate_') is deprecated
#endif
// In 1.89.4, we moved the implementation of "courtesy maths operators" from imgui_internal.h in imgui.h
@ -123,6 +125,7 @@ struct ImBitVector; // Store 1-bit per value
struct ImRect; // An axis-aligned rectangle (2 points)
struct ImDrawDataBuilder; // Helper to build a ImDrawData instance
struct ImDrawListSharedData; // Data shared between all ImDrawList instances
struct ImGuiBoxSelectState; // Box-selection state (currently used by multi-selection, could potentially be used by others)
struct ImGuiColorMod; // Stacked color modifier, backup of modified data so we can restore it
struct ImGuiContext; // Main Dear ImGui context
struct ImGuiContextHook; // Hook for extensions like ImGuiTestEngine
@ -134,8 +137,9 @@ struct ImGuiInputTextDeactivateData;// Short term storage to backup text of a de
struct ImGuiLastItemData; // Status storage for last submitted items
struct ImGuiLocEntry; // A localization entry.
struct ImGuiMenuColumns; // Simple column measurement, currently used for MenuItem() only
struct ImGuiMultiSelectState; // Multi-selection persistent state (for focused selection).
struct ImGuiMultiSelectTempData; // Multi-selection temporary state (while traversing).
struct ImGuiNavItemData; // Result of a gamepad/keyboard directional navigation move query result
struct ImGuiNavTreeNodeData; // Temporary storage for last TreeNode() being a Left arrow landing candidate.
struct ImGuiMetricsConfig; // Storage for ShowMetricsWindow() and DebugNodeXXX() functions
struct ImGuiNextWindowData; // Storage for SetNextWindow** functions
struct ImGuiNextItemData; // Storage for SetNextItem** functions
@ -154,6 +158,7 @@ struct ImGuiTableInstanceData; // Storage for one instance of a same table
struct ImGuiTableTempData; // Temporary storage for one table (one per table in the stack), shared between tables.
struct ImGuiTableSettings; // Storage for a table .ini settings
struct ImGuiTableColumnsSettings; // Storage for a column .ini settings
struct ImGuiTreeNodeStackData; // Temporary storage for TreeNode().
struct ImGuiTypingSelectState; // Storage for GetTypingSelectRequest()
struct ImGuiTypingSelectRequest; // Storage for GetTypingSelectRequest() (aimed to be public)
struct ImGuiWindow; // Storage for one window
@ -169,7 +174,6 @@ typedef int ImGuiLayoutType; // -> enum ImGuiLayoutType_ // E
typedef int ImGuiActivateFlags; // -> enum ImGuiActivateFlags_ // Flags: for navigation/focus function (will be for ActivateItem() later)
typedef int ImGuiDebugLogFlags; // -> enum ImGuiDebugLogFlags_ // Flags: for ShowDebugLogWindow(), g.DebugLogFlags
typedef int ImGuiFocusRequestFlags; // -> enum ImGuiFocusRequestFlags_ // Flags: for FocusWindow();
typedef int ImGuiItemFlags; // -> enum ImGuiItemFlags_ // Flags: for PushItemFlag(), g.LastItemData.InFlags
typedef int ImGuiItemStatusFlags; // -> enum ImGuiItemStatusFlags_ // Flags: for g.LastItemData.StatusFlags
typedef int ImGuiOldColumnFlags; // -> enum ImGuiOldColumnFlags_ // Flags: for BeginColumns()
typedef int ImGuiNavHighlightFlags; // -> enum ImGuiNavHighlightFlags_ // Flags: for RenderNavHighlight()
@ -348,6 +352,7 @@ namespace ImStb
// - Helper: ImPool<>
// - Helper: ImChunkStream<>
// - Helper: ImGuiTextIndex
// - Helper: ImGuiStorage
//-----------------------------------------------------------------------------
// Helpers: Hashing
@ -384,6 +389,7 @@ IM_MSVC_RUNTIME_CHECKS_OFF
static inline char ImToUpper(char c) { return (c >= 'a' && c <= 'z') ? c &= ~32 : c; }
static inline bool ImCharIsBlankA(char c) { return c == ' ' || c == '\t'; }
static inline bool ImCharIsBlankW(unsigned int c) { return c == ' ' || c == '\t' || c == 0x3000; }
static inline bool ImCharIsXdigitA(char c) { return (c >= '0' && c <= '9') || (c >= 'A' && c <= 'F') || (c >= 'a' && c <= 'f'); }
IM_MSVC_RUNTIME_CHECKS_RESTORE
// Helpers: Formatting
@ -488,6 +494,7 @@ static inline int ImModPositive(int a, int b)
static inline float ImDot(const ImVec2& a, const ImVec2& b) { return a.x * b.x + a.y * b.y; }
static inline ImVec2 ImRotate(const ImVec2& v, float cos_a, float sin_a) { return ImVec2(v.x * cos_a - v.y * sin_a, v.x * sin_a + v.y * cos_a); }
static inline float ImLinearSweep(float current, float target, float speed) { if (current < target) return ImMin(current + speed, target); if (current > target) return ImMax(current - speed, target); return current; }
static inline float ImLinearRemapClamp(float s0, float s1, float d0, float d1, float x) { return ImSaturate((x - s0) / (s1 - s0)) * (d1 - d0) + d0; }
static inline ImVec2 ImMul(const ImVec2& lhs, const ImVec2& rhs) { return ImVec2(lhs.x * rhs.x, lhs.y * rhs.y); }
static inline bool ImIsFloatAboveGuaranteedIntegerPrecision(float f) { return f <= -16777216 || f >= 16777216; }
static inline float ImExponentialMovingAverage(float avg, float sample, int n) { avg -= avg / n; avg += sample / n; return avg; }
@ -722,7 +729,7 @@ struct ImChunkStream
void swap(ImChunkStream<T>& rhs) { rhs.Buf.swap(Buf); }
};
// Helper: ImGuiTextIndex<>
// Helper: ImGuiTextIndex
// Maintain a line index for a text buffer. This is a strong candidate to be moved into the public API.
struct ImGuiTextIndex
{
@ -736,6 +743,8 @@ struct ImGuiTextIndex
void append(const char* base, int old_size, int new_size);
};
// Helper: ImGuiStorage
IMGUI_API ImGuiStoragePair* ImLowerBound(ImGuiStoragePair* in_begin, ImGuiStoragePair* in_end, ImGuiID key);
//-----------------------------------------------------------------------------
// [SECTION] ImDrawList support
//-----------------------------------------------------------------------------
@ -772,6 +781,7 @@ struct IMGUI_API ImDrawListSharedData
ImVec2 TexUvWhitePixel; // UV of white pixel in the atlas
ImFont* Font; // Current/default font (optional, for simplified AddText overload)
float FontSize; // Current/default font size (optional, for simplified AddText overload)
float FontScale; // Current/default font scale (== FontSize / Font->FontSize)
float CurveTessellationTol; // Tessellation tolerance when using PathBezierCurveTo()
float CircleSegmentMaxError; // Number of circle segments to use per pixel of radius for AddCircle() etc
ImVec4 ClipRectFullscreen; // Value for PushClipRectFullscreen()
@ -798,33 +808,65 @@ struct ImDrawDataBuilder
ImDrawDataBuilder() { memset(this, 0, sizeof(*this)); }
};
//-----------------------------------------------------------------------------
// [SECTION] Data types support
//-----------------------------------------------------------------------------
struct ImGuiDataVarInfo
{
ImGuiDataType Type;
ImU32 Count; // 1+
ImU32 Offset; // Offset in parent structure
void* GetVarPtr(void* parent) const { return (void*)((unsigned char*)parent + Offset); }
};
struct ImGuiDataTypeStorage
{
ImU8 Data[8]; // Opaque storage to fit any data up to ImGuiDataType_COUNT
};
// Type information associated to one ImGuiDataType. Retrieve with DataTypeGetInfo().
struct ImGuiDataTypeInfo
{
size_t Size; // Size in bytes
const char* Name; // Short descriptive name for the type, for debugging
const char* PrintFmt; // Default printf format for the type
const char* ScanFmt; // Default scanf format for the type
};
// Extend ImGuiDataType_
enum ImGuiDataTypePrivate_
{
ImGuiDataType_String = ImGuiDataType_COUNT + 1,
ImGuiDataType_Pointer,
ImGuiDataType_ID,
};
//-----------------------------------------------------------------------------
// [SECTION] Widgets support: flags, enums, data structures
//-----------------------------------------------------------------------------
// Flags used by upcoming items
// Extend ImGuiItemFlags
// - input: PushItemFlag() manipulates g.CurrentItemFlags, ItemAdd() calls may add extra flags.
// - output: stored in g.LastItemData.InFlags
// Current window shared by all windows.
// This is going to be exposed in imgui.h when stabilized enough.
enum ImGuiItemFlags_
enum ImGuiItemFlagsPrivate_
{
// Controlled by user
ImGuiItemFlags_None = 0,
ImGuiItemFlags_NoTabStop = 1 << 0, // false // Disable keyboard tabbing. This is a "lighter" version of ImGuiItemFlags_NoNav.
ImGuiItemFlags_ButtonRepeat = 1 << 1, // false // Button() will return true multiple times based on io.KeyRepeatDelay and io.KeyRepeatRate settings.
ImGuiItemFlags_Disabled = 1 << 2, // false // Disable interactions but doesn't affect visuals. See BeginDisabled()/EndDisabled(). See github.com/ocornut/imgui/issues/211
ImGuiItemFlags_NoNav = 1 << 3, // false // Disable any form of focusing (keyboard/gamepad directional navigation and SetKeyboardFocusHere() calls)
ImGuiItemFlags_NoNavDefaultFocus = 1 << 4, // false // Disable item being a candidate for default focus (e.g. used by title bar items)
ImGuiItemFlags_SelectableDontClosePopup = 1 << 5, // false // Disable MenuItem/Selectable() automatically closing their popup window
ImGuiItemFlags_MixedValue = 1 << 6, // false // [BETA] Represent a mixed/indeterminate value, generally multi-selection where values differ. Currently only supported by Checkbox() (later should support all sorts of widgets)
ImGuiItemFlags_ReadOnly = 1 << 7, // false // [ALPHA] Allow hovering interactions but underlying value is not changed.
ImGuiItemFlags_NoWindowHoverableCheck = 1 << 8, // false // Disable hoverable check in ItemHoverable()
ImGuiItemFlags_AllowOverlap = 1 << 9, // false // Allow being overlapped by another widget. Not-hovered to Hovered transition deferred by a frame.
ImGuiItemFlags_Disabled = 1 << 10, // false // Disable interactions (DOES NOT affect visuals, see BeginDisabled()/EndDisabled() for full disable feature, and github #211).
ImGuiItemFlags_ReadOnly = 1 << 11, // false // [ALPHA] Allow hovering interactions but underlying value is not changed.
ImGuiItemFlags_MixedValue = 1 << 12, // false // [BETA] Represent a mixed/indeterminate value, generally multi-selection where values differ. Currently only supported by Checkbox() (later should support all sorts of widgets)
ImGuiItemFlags_NoWindowHoverableCheck = 1 << 13, // false // Disable hoverable check in ItemHoverable()
ImGuiItemFlags_AllowOverlap = 1 << 14, // false // Allow being overlapped by another widget. Not-hovered to Hovered transition deferred by a frame.
// Controlled by widget code
ImGuiItemFlags_Inputable = 1 << 10, // false // [WIP] Auto-activate input mode when tab focused. Currently only used and supported by a few items before it becomes a generic feature.
ImGuiItemFlags_HasSelectionUserData = 1 << 11, // false // Set by SetNextItemSelectionUserData()
ImGuiItemFlags_Inputable = 1 << 20, // false // [WIP] Auto-activate input mode when tab focused. Currently only used and supported by a few items before it becomes a generic feature.
ImGuiItemFlags_HasSelectionUserData = 1 << 21, // false // Set by SetNextItemSelectionUserData()
ImGuiItemFlags_IsMultiSelect = 1 << 22, // false // Set by SetNextItemSelectionUserData()
ImGuiItemFlags_Default_ = ImGuiItemFlags_AutoClosePopups, // Please don't change, use PushItemFlag() instead.
// Obsolete
//ImGuiItemFlags_SelectableDontClosePopup = !ImGuiItemFlags_AutoClosePopups, // Can't have a redirect as we inverted the behavior
};
// Status flags for an already submitted item
@ -927,8 +969,9 @@ enum ImGuiSelectableFlagsPrivate_
// Extend ImGuiTreeNodeFlags_
enum ImGuiTreeNodeFlagsPrivate_
{
ImGuiTreeNodeFlags_ClipLabelForTrailingButton = 1 << 20,
ImGuiTreeNodeFlags_UpsideDownArrow = 1 << 21,// (FIXME-WIP) Turn Down arrow into an Up arrow, but reversed trees (#6517)
ImGuiTreeNodeFlags_ClipLabelForTrailingButton = 1 << 28,// FIXME-WIP: Hard-coded for CollapsingHeader()
ImGuiTreeNodeFlags_UpsideDownArrow = 1 << 29,// FIXME-WIP: Turn Down arrow into an Up arrow, but reversed trees (#6517)
ImGuiTreeNodeFlags_OpenOnMask_ = ImGuiTreeNodeFlags_OpenOnDoubleClick | ImGuiTreeNodeFlags_OpenOnArrow,
};
enum ImGuiSeparatorFlags_
@ -1079,7 +1122,7 @@ struct IMGUI_API ImGuiInputTextState
ImVector<char> InitialTextA; // value to revert to when pressing Escape = backup of end-user buffer at the time of focus (in UTF-8, unaltered)
bool TextAIsValid; // temporary UTF8 buffer is not initially valid before we make the widget active (until then we pull the data from user argument)
int BufCapacityA; // end-user buffer capacity
float ScrollX; // horizontal scrolling/offset
ImVec2 Scroll; // horizontal offset (managed manually) + vertical scrolling (pulled from child window's own Scroll.y)
ImStb::STB_TexteditState Stb; // state for stb_textedit.h
float CursorAnim; // timer for cursor blink, reset on every user action so the cursor reappears immediately
bool CursorFollow; // set when we want scrolling to follow the current cursor position (not always!)
@ -1167,29 +1210,30 @@ struct ImGuiNextWindowData
inline void ClearFlags() { Flags = ImGuiNextWindowDataFlags_None; }
};
// Multi-Selection item index or identifier when using SetNextItemSelectionUserData()/BeginMultiSelect()
// (Most users are likely to use this store an item INDEX but this may be used to store a POINTER as well.)
typedef ImS64 ImGuiSelectionUserData;
enum ImGuiNextItemDataFlags_
{
ImGuiNextItemDataFlags_None = 0,
ImGuiNextItemDataFlags_HasWidth = 1 << 0,
ImGuiNextItemDataFlags_HasOpen = 1 << 1,
ImGuiNextItemDataFlags_HasShortcut = 1 << 2,
ImGuiNextItemDataFlags_HasRefVal = 1 << 3,
ImGuiNextItemDataFlags_HasStorageID = 1 << 4,
};
struct ImGuiNextItemData
{
ImGuiNextItemDataFlags Flags;
ImGuiItemFlags ItemFlags; // Currently only tested/used for ImGuiItemFlags_AllowOverlap.
ImGuiItemFlags ItemFlags; // Currently only tested/used for ImGuiItemFlags_AllowOverlap and ImGuiItemFlags_HasSelectionUserData.
// Non-flags members are NOT cleared by ItemAdd() meaning they are still valid during NavProcessItem()
ImGuiID FocusScopeId; // Set by SetNextItemSelectionUserData()
ImGuiSelectionUserData SelectionUserData; // Set by SetNextItemSelectionUserData() (note that NULL/0 is a valid value, we use -1 == ImGuiSelectionUserData_Invalid to mark invalid values)
float Width; // Set by SetNextItemWidth()
ImGuiKeyChord Shortcut; // Set by SetNextItemShortcut()
ImGuiInputFlags ShortcutFlags; // Set by SetNextItemShortcut()
bool OpenVal; // Set by SetNextItemOpen()
ImGuiCond OpenCond : 8;
ImU8 OpenCond; // Set by SetNextItemOpen()
ImGuiDataTypeStorage RefVal; // Not exposed yet, for ImGuiInputTextFlags_ParseEmptyAsRefVal
ImGuiID StorageId; // Set by SetNextItemStorageID()
ImGuiNextItemData() { memset(this, 0, sizeof(*this)); SelectionUserData = -1; }
inline void ClearFlags() { Flags = ImGuiNextItemDataFlags_None; ItemFlags = ImGuiItemFlags_None; } // Also cleared manually by ItemAdd()!
@ -1211,14 +1255,16 @@ struct ImGuiLastItemData
ImGuiLastItemData() { memset(this, 0, sizeof(*this)); }
};
// Store data emitted by TreeNode() for usage by TreePop() to implement ImGuiTreeNodeFlags_NavLeftJumpsBackHere.
// This is the minimum amount of data that we need to perform the equivalent of NavApplyItemToResult() and which we can't infer in TreePop()
// Only stored when the node is a potential candidate for landing on a Left arrow jump.
struct ImGuiNavTreeNodeData
// Store data emitted by TreeNode() for usage by TreePop()
// - To implement ImGuiTreeNodeFlags_NavLeftJumpsBackHere: store the minimum amount of data
// which we can't infer in TreePop(), to perform the equivalent of NavApplyItemToResult().
// Only stored when the node is a potential candidate for landing on a Left arrow jump.
struct ImGuiTreeNodeStackData
{
ImGuiID ID;
ImGuiItemFlags InFlags;
ImRect NavRect;
ImGuiTreeNodeFlags TreeFlags;
ImGuiItemFlags InFlags; // Used for nav landing
ImRect NavRect; // Used for nav landing
};
struct IMGUI_API ImGuiStackSizes
@ -1263,40 +1309,6 @@ struct ImGuiPtrOrIndex
ImGuiPtrOrIndex(int index) { Ptr = NULL; Index = index; }
};
//-----------------------------------------------------------------------------
// [SECTION] Data types support
//-----------------------------------------------------------------------------
struct ImGuiDataVarInfo
{
ImGuiDataType Type;
ImU32 Count; // 1+
ImU32 Offset; // Offset in parent structure
void* GetVarPtr(void* parent) const { return (void*)((unsigned char*)parent + Offset); }
};
struct ImGuiDataTypeTempStorage
{
ImU8 Data[8]; // Can fit any data up to ImGuiDataType_COUNT
};
// Type information associated to one ImGuiDataType. Retrieve with DataTypeGetInfo().
struct ImGuiDataTypeInfo
{
size_t Size; // Size in bytes
const char* Name; // Short descriptive name for the type, for debugging
const char* PrintFmt; // Default printf format for the type
const char* ScanFmt; // Default scanf format for the type
};
// Extend ImGuiDataType_
enum ImGuiDataTypePrivate_
{
ImGuiDataType_String = ImGuiDataType_COUNT + 1,
ImGuiDataType_Pointer,
ImGuiDataType_ID,
};
//-----------------------------------------------------------------------------
// [SECTION] Popup support
//-----------------------------------------------------------------------------
@ -1347,8 +1359,8 @@ typedef ImBitArray<ImGuiKey_NamedKey_COUNT, -ImGuiKey_NamedKey_BEGIN> ImBitAr
#define ImGuiKey_NavKeyboardTweakFast ImGuiMod_Shift
#define ImGuiKey_NavGamepadTweakSlow ImGuiKey_GamepadL1
#define ImGuiKey_NavGamepadTweakFast ImGuiKey_GamepadR1
#define ImGuiKey_NavGamepadActivate ImGuiKey_GamepadFaceDown
#define ImGuiKey_NavGamepadCancel ImGuiKey_GamepadFaceRight
#define ImGuiKey_NavGamepadActivate (g.IO.ConfigNavSwapGamepadButtons ? ImGuiKey_GamepadFaceRight : ImGuiKey_GamepadFaceDown)
#define ImGuiKey_NavGamepadCancel (g.IO.ConfigNavSwapGamepadButtons ? ImGuiKey_GamepadFaceDown : ImGuiKey_GamepadFaceRight)
#define ImGuiKey_NavGamepadMenu ImGuiKey_GamepadFaceLeft
#define ImGuiKey_NavGamepadInput ImGuiKey_GamepadFaceUp
@ -1594,7 +1606,7 @@ struct ImGuiNavItemData
float DistBox; // Move // Best candidate box distance to current NavId
float DistCenter; // Move // Best candidate center distance to current NavId
float DistAxial; // Move // Best candidate axial distance to current NavId
ImGuiSelectionUserData SelectionUserData;//I+Mov // Best candidate SetNextItemSelectionData() value.
ImGuiSelectionUserData SelectionUserData;//I+Mov // Best candidate SetNextItemSelectionUserData() value. Valid if (InFlags & ImGuiItemFlags_HasSelectionUserData)
ImGuiNavItemData() { Clear(); }
void Clear() { Window = NULL; ID = FocusScopeId = 0; InFlags = 0; SelectionUserData = -1; DistBox = DistCenter = DistAxial = FLT_MAX; }
@ -1700,6 +1712,34 @@ struct ImGuiOldColumns
ImGuiOldColumns() { memset(this, 0, sizeof(*this)); }
};
//-----------------------------------------------------------------------------
// [SECTION] Box-select support
//-----------------------------------------------------------------------------
struct ImGuiBoxSelectState
{
// Active box-selection data (persistent, 1 active at a time)
ImGuiID ID;
bool IsActive;
bool IsStarting;
bool IsStartedFromVoid; // Starting click was not from an item.
bool IsStartedSetNavIdOnce;
bool RequestClear;
ImGuiKeyChord KeyMods : 16; // Latched key-mods for box-select logic.
ImVec2 StartPosRel; // Start position in window-contents relative space (to support scrolling)
ImVec2 EndPosRel; // End position in window-contents relative space
ImVec2 ScrollAccum; // Scrolling accumulator (to behave at high-frame spaces)
ImGuiWindow* Window;
// Temporary/Transient data
bool UnclipMode; // (Temp/Transient, here in hot area). Set/cleared by the BeginMultiSelect()/EndMultiSelect() owning active box-select.
ImRect UnclipRect; // Rectangle where ItemAdd() clipping may be temporarily disabled. Need support by multi-select supporting widgets.
ImRect BoxSelectRectPrev; // Selection rectangle in absolute coordinates (derived every frame from BoxSelectStartPosRel and MousePos)
ImRect BoxSelectRectCurr;
ImGuiBoxSelectState() { memset(this, 0, sizeof(*this)); }
};
//-----------------------------------------------------------------------------
// [SECTION] Multi-select support
//-----------------------------------------------------------------------------
@ -1707,9 +1747,45 @@ struct ImGuiOldColumns
// We always assume that -1 is an invalid value (which works for indices and pointers)
#define ImGuiSelectionUserData_Invalid ((ImGuiSelectionUserData)-1)
#ifdef IMGUI_HAS_MULTI_SELECT
// <this is filled in 'range_select' branch>
#endif // #ifdef IMGUI_HAS_MULTI_SELECT
// Temporary storage for multi-select
struct IMGUI_API ImGuiMultiSelectTempData
{
ImGuiMultiSelectIO IO; // MUST BE FIRST FIELD. Requests are set and returned by BeginMultiSelect()/EndMultiSelect() + written to by user during the loop.
ImGuiMultiSelectState* Storage;
ImGuiID FocusScopeId; // Copied from g.CurrentFocusScopeId (unless another selection scope was pushed manually)
ImGuiMultiSelectFlags Flags;
ImVec2 ScopeRectMin;
ImVec2 BackupCursorMaxPos;
ImGuiSelectionUserData LastSubmittedItem; // Copy of last submitted item data, used to merge output ranges.
ImGuiID BoxSelectId;
ImGuiKeyChord KeyMods;
ImS8 LoopRequestSetAll; // -1: no operation, 0: clear all, 1: select all.
bool IsEndIO; // Set when switching IO from BeginMultiSelect() to EndMultiSelect() state.
bool IsFocused; // Set if currently focusing the selection scope (any item of the selection). May be used if you have custom shortcut associated to selection.
bool IsKeyboardSetRange; // Set by BeginMultiSelect() when using Shift+Navigation. Because scrolling may be affected we can't afford a frame of lag with Shift+Navigation.
bool NavIdPassedBy;
bool RangeSrcPassedBy; // Set by the item that matches RangeSrcItem.
bool RangeDstPassedBy; // Set by the item that matches NavJustMovedToId when IsSetRange is set.
ImGuiMultiSelectTempData() { Clear(); }
void Clear() { size_t io_sz = sizeof(IO); ClearIO(); memset((void*)(&IO + 1), 0, sizeof(*this) - io_sz); } // Zero-clear except IO as we preserve IO.Requests[] buffer allocation.
void ClearIO() { IO.Requests.resize(0); IO.RangeSrcItem = IO.NavIdItem = ImGuiSelectionUserData_Invalid; IO.NavIdSelected = IO.RangeSrcReset = false; }
};
// Persistent storage for multi-select (as long as selection is alive)
struct IMGUI_API ImGuiMultiSelectState
{
ImGuiWindow* Window;
ImGuiID ID;
int LastFrameActive; // Last used frame-count, for GC.
int LastSelectionSize; // Set by BeginMultiSelect() based on optional info provided by user. May be -1 if unknown.
ImS8 RangeSelected; // -1 (don't have) or true/false
ImS8 NavIdSelected; // -1 (don't have) or true/false
ImGuiSelectionUserData RangeSrcItem; //
ImGuiSelectionUserData NavIdItem; // SetNextItemSelectionUserData() value for NavId (if part of submitted items)
ImGuiMultiSelectState() { Window = NULL; ID = 0; LastFrameActive = LastSelectionSize = 0; RangeSelected = NavIdSelected = -1; RangeSrcItem = NavIdItem = ImGuiSelectionUserData_Invalid; }
};
//-----------------------------------------------------------------------------
// [SECTION] Docking support
@ -1731,23 +1807,28 @@ struct ImGuiViewportP : public ImGuiViewport
ImDrawList* BgFgDrawLists[2]; // Convenience background (0) and foreground (1) draw lists. We use them to draw software mouser cursor when io.MouseDrawCursor is set and to draw most debug overlays.
ImDrawData DrawDataP;
ImDrawDataBuilder DrawDataBuilder; // Temporary data while building final ImDrawData
ImVec2 WorkOffsetMin; // Work Area: Offset from Pos to top-left corner of Work Area. Generally (0,0) or (0,+main_menu_bar_height). Work Area is Full Area but without menu-bars/status-bars (so WorkArea always fit inside Pos/Size!)
ImVec2 WorkOffsetMax; // Work Area: Offset from Pos+Size to bottom-right corner of Work Area. Generally (0,0) or (0,-status_bar_height).
ImVec2 BuildWorkOffsetMin; // Work Area: Offset being built during current frame. Generally >= 0.0f.
ImVec2 BuildWorkOffsetMax; // Work Area: Offset being built during current frame. Generally <= 0.0f.
// Per-viewport work area
// - Insets are >= 0.0f values, distance from viewport corners to work area.
// - BeginMainMenuBar() and DockspaceOverViewport() tend to use work area to avoid stepping over existing contents.
// - Generally 'safeAreaInsets' in iOS land, 'DisplayCutout' in Android land.
ImVec2 WorkInsetMin; // Work Area inset locked for the frame. GetWorkRect() always fits within GetMainRect().
ImVec2 WorkInsetMax; // "
ImVec2 BuildWorkInsetMin; // Work Area inset accumulator for current frame, to become next frame's WorkInset
ImVec2 BuildWorkInsetMax; // "
ImGuiViewportP() { BgFgDrawListsLastFrame[0] = BgFgDrawListsLastFrame[1] = -1; BgFgDrawLists[0] = BgFgDrawLists[1] = NULL; }
~ImGuiViewportP() { if (BgFgDrawLists[0]) IM_DELETE(BgFgDrawLists[0]); if (BgFgDrawLists[1]) IM_DELETE(BgFgDrawLists[1]); }
// Calculate work rect pos/size given a set of offset (we have 1 pair of offset for rect locked from last frame data, and 1 pair for currently building rect)
ImVec2 CalcWorkRectPos(const ImVec2& off_min) const { return ImVec2(Pos.x + off_min.x, Pos.y + off_min.y); }
ImVec2 CalcWorkRectSize(const ImVec2& off_min, const ImVec2& off_max) const { return ImVec2(ImMax(0.0f, Size.x - off_min.x + off_max.x), ImMax(0.0f, Size.y - off_min.y + off_max.y)); }
void UpdateWorkRect() { WorkPos = CalcWorkRectPos(WorkOffsetMin); WorkSize = CalcWorkRectSize(WorkOffsetMin, WorkOffsetMax); } // Update public fields
ImVec2 CalcWorkRectPos(const ImVec2& inset_min) const { return ImVec2(Pos.x + inset_min.x, Pos.y + inset_min.y); }
ImVec2 CalcWorkRectSize(const ImVec2& inset_min, const ImVec2& inset_max) const { return ImVec2(ImMax(0.0f, Size.x - inset_min.x - inset_max.x), ImMax(0.0f, Size.y - inset_min.y - inset_max.y)); }
void UpdateWorkRect() { WorkPos = CalcWorkRectPos(WorkInsetMin); WorkSize = CalcWorkRectSize(WorkInsetMin, WorkInsetMax); } // Update public fields
// Helpers to retrieve ImRect (we don't need to store BuildWorkRect as every access tend to change it, hence the code asymmetry)
ImRect GetMainRect() const { return ImRect(Pos.x, Pos.y, Pos.x + Size.x, Pos.y + Size.y); }
ImRect GetWorkRect() const { return ImRect(WorkPos.x, WorkPos.y, WorkPos.x + WorkSize.x, WorkPos.y + WorkSize.y); }
ImRect GetBuildWorkRect() const { ImVec2 pos = CalcWorkRectPos(BuildWorkOffsetMin); ImVec2 size = CalcWorkRectSize(BuildWorkOffsetMin, BuildWorkOffsetMax); return ImRect(pos.x, pos.y, pos.x + size.x, pos.y + size.y); }
ImRect GetBuildWorkRect() const { ImVec2 pos = CalcWorkRectPos(BuildWorkInsetMin); ImVec2 size = CalcWorkRectSize(BuildWorkInsetMin, BuildWorkInsetMax); return ImRect(pos.x, pos.y, pos.x + size.x, pos.y + size.y); }
};
//-----------------------------------------------------------------------------
@ -1801,6 +1882,7 @@ enum ImGuiLocKey : int
ImGuiLocKey_WindowingMainMenuBar,
ImGuiLocKey_WindowingPopup,
ImGuiLocKey_WindowingUntitled,
ImGuiLocKey_CopyLink,
ImGuiLocKey_COUNT
};
@ -1918,10 +2000,12 @@ struct ImGuiContext
bool Initialized;
bool FontAtlasOwnedByContext; // IO.Fonts-> is owned by the ImGuiContext and will be destructed along with it.
ImGuiIO IO;
ImGuiPlatformIO PlatformIO;
ImGuiStyle Style;
ImFont* Font; // (Shortcut) == FontStack.empty() ? IO.Font : FontStack.back()
float FontSize; // (Shortcut) == FontBaseSize * g.CurrentWindow->FontWindowScale == window->FontSize(). Text height for current window.
float FontBaseSize; // (Shortcut) == IO.FontGlobalScale * Font->Scale * Font->FontSize. Base text height.
float FontScale; // == FontSize / Font->FontSize
float CurrentDpiScale; // Current window/viewport DpiScale
ImDrawListSharedData DrawListSharedData;
double Time;
@ -1934,6 +2018,7 @@ struct ImGuiContext
bool GcCompactAll; // Request full GC
bool TestEngineHookItems; // Will call test engine hooks: ImGuiTestEngineHook_ItemAdd(), ImGuiTestEngineHook_ItemInfo(), ImGuiTestEngineHook_Log()
void* TestEngine; // Test engine user data
char ContextName[16]; // Storage for a context name (to facilitate debugging multi-context setups)
// Inputs
ImVector<ImGuiInputEvent> InputEventsQueue; // Input events which will be trickled/written into IO structure.
@ -1953,6 +2038,7 @@ struct ImGuiContext
ImGuiWindow* CurrentWindow; // Window being drawn into
ImGuiWindow* HoveredWindow; // Window the mouse is hovering. Will typically catch mouse inputs.
ImGuiWindow* HoveredWindowUnderMovingWindow; // Hovered window ignoring MovingWindow. Only set if MovingWindow is set.
ImGuiWindow* HoveredWindowBeforeClear; // Window the mouse is hovering. Filled even with _NoMouse. This is currently useful for multi-context compositors.
ImGuiWindow* MovingWindow; // Track the window we clicked on (in order to preserve focus). The actual window that is moved is generally MovingWindow->RootWindow.
ImGuiWindow* WheelingWindow; // Track the window we started mouse-wheeling on. Until a timer elapse or mouse has moved, generally keep scrolling the same window even if during the course of scrolling the mouse ends up hovering a child window.
ImVec2 WheelingWindowRefMousePos;
@ -1969,7 +2055,7 @@ struct ImGuiContext
float HoveredIdTimer; // Measure contiguous hovering time
float HoveredIdNotActiveTimer; // Measure contiguous hovering time where the item has not been active
bool HoveredIdAllowOverlap;
bool HoveredIdDisabled; // At least one widget passed the rect test, but has been discarded by disabled flag or popup inhibit. May be true even if HoveredId == 0.
bool HoveredIdIsDisabled; // At least one widget passed the rect test, but has been discarded by disabled flag or popup inhibit. May be true even if HoveredId == 0.
bool ItemUnclipByLog; // Disable ItemAdd() clipping, essentially a memory-locality friendly copy of LogEnabled
ImGuiID ActiveId; // Active widget
ImGuiID ActiveIdIsAlive; // Active widget has been seen this frame (we can't use a bool as the ActiveId may change within the frame)
@ -2003,11 +2089,9 @@ struct ImGuiContext
ImGuiKeyOwnerData KeysOwnerData[ImGuiKey_NamedKey_COUNT];
ImGuiKeyRoutingTable KeysRoutingTable;
ImU32 ActiveIdUsingNavDirMask; // Active widget will want to read those nav move requests (e.g. can activate a button and move away from it)
bool ActiveIdUsingAllKeyboardKeys; // Active widget will want to read all keyboard keys inputs. (FIXME: This is a shortcut for not taking ownership of 100+ keys but perhaps best to not have the inconsistency)
bool ActiveIdUsingAllKeyboardKeys; // Active widget will want to read all keyboard keys inputs. (this is a shortcut for not taking ownership of 100+ keys, frequently used by drag operations)
ImGuiKeyChord DebugBreakInShortcutRouting; // Set to break in SetShortcutRouting()/Shortcut() calls.
#ifndef IMGUI_DISABLE_OBSOLETE_KEYIO
ImU32 ActiveIdUsingNavInputMask; // If you used this. Since (IMGUI_VERSION_NUM >= 18804) : 'g.ActiveIdUsingNavInputMask |= (1 << ImGuiNavInput_Cancel);' becomes 'SetKeyOwner(ImGuiKey_Escape, g.ActiveId) and/or SetKeyOwner(ImGuiKey_NavGamepadCancel, g.ActiveId);'
#endif
//ImU32 ActiveIdUsingNavInputMask; // [OBSOLETE] Since (IMGUI_VERSION_NUM >= 18804) : 'g.ActiveIdUsingNavInputMask |= (1 << ImGuiNavInput_Cancel);' becomes --> 'SetKeyOwner(ImGuiKey_Escape, g.ActiveId) and/or SetKeyOwner(ImGuiKey_NavGamepadCancel, g.ActiveId);'
// Next window/item data
ImGuiID CurrentFocusScopeId; // Value for currently appending items == g.FocusScopeStack.back(). Not to be mistaken with g.NavFocusScopeId.
@ -2028,7 +2112,7 @@ struct ImGuiContext
ImVector<ImGuiGroupData> GroupStack; // Stack for BeginGroup()/EndGroup() - not inherited by Begin()
ImVector<ImGuiPopupData> OpenPopupStack; // Which popups are open (persistent)
ImVector<ImGuiPopupData> BeginPopupStack; // Which level of BeginPopup() we are in (reset every frame)
ImVector<ImGuiNavTreeNodeData> NavTreeNodeStack; // Stack for TreeNode() when a NavLeft requested is emitted.
ImVector<ImGuiTreeNodeStackData>TreeNodeStack; // Stack for TreeNode()
// Viewports
ImVector<ImGuiViewportP*> Viewports; // Active viewports (Size==1 in 'master' branch). Each viewports hold their copy of ImDrawData.
@ -2037,6 +2121,7 @@ struct ImGuiContext
ImGuiWindow* NavWindow; // Focused window for navigation. Could be called 'FocusedWindow'
ImGuiID NavId; // Focused item for navigation
ImGuiID NavFocusScopeId; // Focused focus scope (e.g. selection code often wants to "clear other items" when landing on an item of the same scope)
ImGuiNavLayer NavLayer; // Focused layer (main scrolling layer, or menu/title bar layer)
ImGuiID NavActivateId; // ~~ (g.ActiveId == 0) && (IsKeyPressed(ImGuiKey_Space) || IsKeyDown(ImGuiKey_Enter) || IsKeyPressed(ImGuiKey_NavGamepadActivate)) ? NavId : 0, also set when calling ActivateItem()
ImGuiID NavActivateDownId; // ~~ IsKeyDown(ImGuiKey_Space) || IsKeyDown(ImGuiKey_Enter) || IsKeyDown(ImGuiKey_NavGamepadActivate) ? NavId : 0
ImGuiID NavActivatePressedId; // ~~ IsKeyPressed(ImGuiKey_Space) || IsKeyPressed(ImGuiKey_Enter) || IsKeyPressed(ImGuiKey_NavGamepadActivate) ? NavId : 0 (no repeat)
@ -2044,13 +2129,9 @@ struct ImGuiContext
ImVector<ImGuiFocusScopeData> NavFocusRoute; // Reversed copy focus scope stack for NavId (should contains NavFocusScopeId). This essentially follow the window->ParentWindowForFocusRoute chain.
ImGuiID NavHighlightActivatedId;
float NavHighlightActivatedTimer;
ImGuiID NavJustMovedToId; // Just navigated to this id (result of a successfully MoveRequest).
ImGuiID NavJustMovedToFocusScopeId; // Just navigated to this focus scope id (result of a successfully MoveRequest).
ImGuiKeyChord NavJustMovedToKeyMods;
ImGuiID NavNextActivateId; // Set by ActivateItem(), queued until next frame.
ImGuiActivateFlags NavNextActivateFlags;
ImGuiInputSource NavInputSource; // Keyboard or Gamepad mode? THIS CAN ONLY BE ImGuiInputSource_Keyboard or ImGuiInputSource_Mouse
ImGuiNavLayer NavLayer; // Layer we are navigating on. For now the system is hard-coded for 0=main contents and 1=menu/title bar, may expose layers later.
ImGuiSelectionUserData NavLastValidSelectionUserData; // Last valid data passed to SetNextItemSelectionUser(), or -1. For current window. Not reset when focusing an item that doesn't have selection data.
bool NavIdIsAlive; // Nav widget has been seen this frame ~~ NavRectRel is valid
bool NavMousePosDirty; // When set we will update mouse position if (io.ConfigFlags & ImGuiConfigFlags_NavEnableSetMousePos) if set (NB: this not enabled by default)
@ -2081,6 +2162,14 @@ struct ImGuiContext
ImGuiNavItemData NavMoveResultOther; // Best move request candidate within NavWindow's flattened hierarchy (when using ImGuiWindowFlags_NavFlattened flag)
ImGuiNavItemData NavTabbingResultFirst; // First tabbing request candidate within NavWindow and flattened hierarchy
// Navigation: record of last move request
ImGuiID NavJustMovedFromFocusScopeId; // Just navigated from this focus scope id (result of a successfully MoveRequest).
ImGuiID NavJustMovedToId; // Just navigated to this id (result of a successfully MoveRequest).
ImGuiID NavJustMovedToFocusScopeId; // Just navigated to this focus scope id (result of a successfully MoveRequest).
ImGuiKeyChord NavJustMovedToKeyMods;
bool NavJustMovedToIsTabbing; // Copy of ImGuiNavMoveFlags_IsTabbing. Maybe we should store whole flags.
bool NavJustMovedToHasSelectionData; // Copy of move result's InFlags & ImGuiItemFlags_HasSelectionUserData). Maybe we should just store ImGuiNavItemData.
// Navigation: Windowing (CTRL+TAB for list, or Menu button + keys or directional pads to move/resize)
ImGuiKeyChord ConfigNavWindowingKeyNext; // = ImGuiMod_Ctrl | ImGuiKey_Tab (or ImGuiMod_Super | ImGuiKey_Tab on OS X). For reconfiguration (see #4828)
ImGuiKeyChord ConfigNavWindowingKeyPrev; // = ImGuiMod_Ctrl | ImGuiMod_Shift | ImGuiKey_Tab (or ImGuiMod_Super | ImGuiMod_Shift | ImGuiKey_Tab on OS X)
@ -2136,6 +2225,13 @@ struct ImGuiContext
ImVector<ImGuiPtrOrIndex> CurrentTabBarStack;
ImVector<ImGuiShrinkWidthItem> ShrinkWidthBuffer;
// Multi-Select state
ImGuiBoxSelectState BoxSelectState;
ImGuiMultiSelectTempData* CurrentMultiSelect;
int MultiSelectTempDataStacked; // Temporary multi-select data size (because we leave previous instances undestructed, we generally don't use MultiSelectTempData.Size)
ImVector<ImGuiMultiSelectTempData> MultiSelectTempData;
ImPool<ImGuiMultiSelectState> MultiSelectStorage;
// Hover Delay system
ImGuiID HoverItemDelayId;
ImGuiID HoverItemDelayIdPreviousFrame;
@ -2154,6 +2250,7 @@ struct ImGuiContext
ImGuiInputTextDeactivatedState InputTextDeactivatedState;
ImFont InputTextPasswordFont;
ImGuiID TempInputId; // Temporary text input when CTRL+clicking on a slider, etc.
ImGuiDataTypeStorage DataTypeZeroValue; // 0 for all data types
int BeginMenuDepth;
int BeginComboDepth;
ImGuiColorEditFlags ColorEditOptions; // Store user options for color edit widgets
@ -2184,7 +2281,7 @@ struct ImGuiContext
// Platform support
ImGuiPlatformImeData PlatformImeData; // Data updated by current frame
ImGuiPlatformImeData PlatformImeDataPrev; // Previous frame data (when changing we will call io.SetPlatformImeDataFn
ImGuiPlatformImeData PlatformImeDataPrev; // Previous frame data. When changed we call the platform_io.Platform_SetImeDataFn() handler.
// Settings
bool SettingsLoaded;
@ -2251,7 +2348,7 @@ struct ImGuiContext
Initialized = false;
FontAtlasOwnedByContext = shared_font_atlas ? false : true;
Font = NULL;
FontSize = FontBaseSize = CurrentDpiScale = 0.0f;
FontSize = FontBaseSize = FontScale = CurrentDpiScale = 0.0f;
IO.Fonts = shared_font_atlas ? shared_font_atlas : IM_NEW(ImFontAtlas)();
Time = 0.0f;
FrameCount = 0;
@ -2260,6 +2357,7 @@ struct ImGuiContext
GcCompactAll = false;
TestEngineHookItems = false;
TestEngine = NULL;
memset(ContextName, 0, sizeof(ContextName));
InputEventsNextMouseSource = ImGuiMouseSource_Mouse;
InputEventsNextEventId = 1;
@ -2268,6 +2366,7 @@ struct ImGuiContext
CurrentWindow = NULL;
HoveredWindow = NULL;
HoveredWindowUnderMovingWindow = NULL;
HoveredWindowBeforeClear = NULL;
MovingWindow = NULL;
WheelingWindow = NULL;
WheelingWindowStartFrame = WheelingWindowScrolledFrame = -1;
@ -2276,7 +2375,7 @@ struct ImGuiContext
DebugHookIdInfo = 0;
HoveredId = HoveredIdPreviousFrame = 0;
HoveredIdAllowOverlap = false;
HoveredIdDisabled = false;
HoveredIdIsDisabled = false;
HoveredIdTimer = HoveredIdNotActiveTimer = 0.0f;
ItemUnclipByLog = false;
ActiveId = 0;
@ -2304,9 +2403,6 @@ struct ImGuiContext
ActiveIdUsingNavDirMask = 0x00;
ActiveIdUsingAllKeyboardKeys = false;
#ifndef IMGUI_DISABLE_OBSOLETE_KEYIO
ActiveIdUsingNavInputMask = 0x00;
#endif
CurrentFocusScopeId = 0;
CurrentItemFlags = ImGuiItemFlags_None;
@ -2314,18 +2410,18 @@ struct ImGuiContext
NavWindow = NULL;
NavId = NavFocusScopeId = NavActivateId = NavActivateDownId = NavActivatePressedId = 0;
NavJustMovedToId = NavJustMovedToFocusScopeId = NavNextActivateId = 0;
NavLayer = ImGuiNavLayer_Main;
NavNextActivateId = 0;
NavActivateFlags = NavNextActivateFlags = ImGuiActivateFlags_None;
NavHighlightActivatedId = 0;
NavHighlightActivatedTimer = 0.0f;
NavJustMovedToKeyMods = ImGuiMod_None;
NavInputSource = ImGuiInputSource_Keyboard;
NavLayer = ImGuiNavLayer_Main;
NavLastValidSelectionUserData = ImGuiSelectionUserData_Invalid;
NavIdIsAlive = false;
NavMousePosDirty = false;
NavDisableHighlight = true;
NavDisableMouseHover = false;
NavAnyRequest = false;
NavInitRequest = false;
NavInitRequestFromMove = false;
@ -2340,6 +2436,11 @@ struct ImGuiContext
NavTabbingDir = 0;
NavTabbingCounter = 0;
NavJustMovedFromFocusScopeId = NavJustMovedToId = NavJustMovedToFocusScopeId = 0;
NavJustMovedToKeyMods = ImGuiMod_None;
NavJustMovedToIsTabbing = false;
NavJustMovedToHasSelectionData = false;
// All platforms use Ctrl+Tab but Ctrl<>Super are swapped on Mac...
// FIXME: Because this value is stored, it annoyingly interfere with toggling io.ConfigMacOSXBehaviors updating this..
ConfigNavWindowingKeyNext = IO.ConfigMacOSXBehaviors ? (ImGuiMod_Super | ImGuiKey_Tab) : (ImGuiMod_Ctrl | ImGuiKey_Tab);
@ -2368,6 +2469,8 @@ struct ImGuiContext
CurrentTable = NULL;
TablesTempDataStacked = 0;
CurrentTabBar = NULL;
CurrentMultiSelect = NULL;
MultiSelectTempDataStacked = 0;
HoverItemDelayId = HoverItemDelayIdPreviousFrame = HoverItemUnlockedStationaryId = HoverWindowUnlockedStationaryId = 0;
HoverItemDelayTimer = HoverItemDelayClearTimer = 0.0f;
@ -2376,6 +2479,7 @@ struct ImGuiContext
MouseStationaryTimer = 0.0f;
TempInputId = 0;
memset(&DataTypeZeroValue, 0, sizeof(DataTypeZeroValue));
BeginMenuDepth = BeginComboDepth = 0;
ColorEditOptions = ImGuiColorEditFlags_DefaultOptions_;
ColorEditCurrentID = ColorEditSavedID = 0;
@ -2479,7 +2583,7 @@ struct IMGUI_API ImGuiWindowTempData
ImVec2 MenuBarOffset; // MenuBarOffset.x is sort of equivalent of a per-layer CursorPos.x, saved/restored as we switch to the menu bar. The only situation when MenuBarOffset.y is > 0 if when (SafeAreaPadding.y > FramePadding.y), often used on TVs.
ImGuiMenuColumns MenuColumns; // Simplified columns storage for menu items measurement
int TreeDepth; // Current tree depth.
ImU32 TreeJumpToParentOnPopMask; // Store a copy of !g.NavIdIsAlive for TreeDepth 0..31.. Could be turned into a ImU64 if necessary.
ImU32 TreeHasStackDataDepthMask; // Store whether given depth has ImGuiTreeNodeStackData data. Could be turned into a ImU64 if necessary.
ImVector<ImGuiWindow*> ChildWindows;
ImGuiStorage* StateStorage; // Current persistent per-window storage (store e.g. tree node open/close state)
ImGuiOldColumns* CurrentColumns; // Current columns set
@ -2514,7 +2618,7 @@ struct IMGUI_API ImGuiWindow
ImVec2 WindowPadding; // Window padding at the time of Begin().
float WindowRounding; // Window rounding at the time of Begin(). May be clamped lower to avoid rendering artifacts with title bar, menu bar etc.
float WindowBorderSize; // Window border size at the time of Begin().
float TitleBarHeight, MenuBarHeight;
float TitleBarHeight, MenuBarHeight; // Note that those used to be function before 2024/05/28. If you have old code calling TitleBarHeight() you can change it to TitleBarHeight.
float DecoOuterSizeX1, DecoOuterSizeY1; // Left/Up offsets. Sum of non-scrolling outer decorations (X1 generally == 0.0f. Y1 generally = TitleBarHeight + MenuBarHeight). Locked during Begin().
float DecoOuterSizeX2, DecoOuterSizeY2; // Right/Down offsets (X2 generally == ScrollbarSize.x, Y2 == ScrollbarSizes.y).
float DecoInnerSizeX1, DecoInnerSizeY1; // Applied AFTER/OVER InnerRect. Specialized for Tables as they use specialized form of clipping and frozen rows/columns are inside InnerRect (and not part of regular decoration sizes).
@ -2611,6 +2715,7 @@ public:
ImGuiID GetID(const char* str, const char* str_end = NULL);
ImGuiID GetID(const void* ptr);
ImGuiID GetID(int n);
ImGuiID GetIDFromPos(const ImVec2& p_abs);
ImGuiID GetIDFromRectangle(const ImRect& r_abs);
// We don't use g.FontSize because the window may be != g.CurrentWindow.
@ -2722,6 +2827,7 @@ struct ImGuiTableColumn
float MaxX;
float WidthRequest; // Master width absolute value when !(Flags & _WidthStretch). When Stretch this is derived every frame from StretchWeight in TableUpdateLayout()
float WidthAuto; // Automatic width
float WidthMax; // Maximum width (FIXME: overwritten by each instance)
float StretchWeight; // Master width weight when (Flags & _WidthStretch). Often around ~1.0f initially.
float InitStretchWeightOrWidth; // Value passed to TableSetupColumn(). For Width it is a content width (_without padding_).
ImRect ClipRect; // Clipping rectangle for the column
@ -3020,6 +3126,7 @@ namespace ImGui
inline void SetWindowParentWindowForFocusRoute(ImGuiWindow* window, ImGuiWindow* parent_window) { window->ParentWindowForFocusRoute = parent_window; }
inline ImRect WindowRectAbsToRel(ImGuiWindow* window, const ImRect& r) { ImVec2 off = window->DC.CursorStartPos; return ImRect(r.Min.x - off.x, r.Min.y - off.y, r.Max.x - off.x, r.Max.y - off.y); }
inline ImRect WindowRectRelToAbs(ImGuiWindow* window, const ImRect& r) { ImVec2 off = window->DC.CursorStartPos; return ImRect(r.Min.x + off.x, r.Min.y + off.y, r.Max.x + off.x, r.Max.y + off.y); }
inline ImVec2 WindowPosAbsToRel(ImGuiWindow* window, const ImVec2& p) { ImVec2 off = window->DC.CursorStartPos; return ImVec2(p.x - off.x, p.y - off.y); }
inline ImVec2 WindowPosRelToAbs(ImGuiWindow* window, const ImVec2& p) { ImVec2 off = window->DC.CursorStartPos; return ImVec2(p.x + off.x, p.y + off.y); }
// Windows: Display Order and Focus Order
@ -3096,7 +3203,7 @@ namespace ImGui
//#endif
// Basic Accessors
inline ImGuiItemStatusFlags GetItemStatusFlags(){ ImGuiContext& g = *GImGui; return g.LastItemData.StatusFlags; }
inline ImGuiItemStatusFlags GetItemStatusFlags() { ImGuiContext& g = *GImGui; return g.LastItemData.StatusFlags; }
inline ImGuiItemFlags GetItemFlags() { ImGuiContext& g = *GImGui; return g.LastItemData.InFlags; }
inline ImGuiID GetActiveID() { ImGuiContext& g = *GImGui; return g.ActiveId; }
inline ImGuiID GetFocusID() { ImGuiContext& g = *GImGui; return g.NavId; }
@ -3122,13 +3229,9 @@ namespace ImGui
IMGUI_API ImVec2 CalcItemSize(ImVec2 size, float default_w, float default_h);
IMGUI_API float CalcWrapWidthForPos(const ImVec2& pos, float wrap_pos_x);
IMGUI_API void PushMultiItemsWidths(int components, float width_full);
IMGUI_API bool IsItemToggledSelection(); // Was the last item selection toggled? (after Selectable(), TreeNode() etc. We only returns toggle _event_ in order to handle clipping correctly)
IMGUI_API ImVec2 GetContentRegionMaxAbs();
IMGUI_API void ShrinkWidths(ImGuiShrinkWidthItem* items, int count, float width_excess);
// Parameter stacks (shared)
IMGUI_API void PushItemFlag(ImGuiItemFlags option, bool enabled);
IMGUI_API void PopItemFlag();
IMGUI_API const ImGuiDataVarInfo* GetStyleVarInfo(ImGuiStyleVar idx);
IMGUI_API void BeginDisabledOverrideReenable();
IMGUI_API void EndDisabledOverrideReenable();
@ -3139,16 +3242,16 @@ namespace ImGui
IMGUI_API void LogRenderedText(const ImVec2* ref_pos, const char* text, const char* text_end = NULL);
IMGUI_API void LogSetNextTextDecoration(const char* prefix, const char* suffix);
// Popups, Modals, Tooltips
// Childs
IMGUI_API bool BeginChildEx(const char* name, ImGuiID id, const ImVec2& size_arg, ImGuiChildFlags child_flags, ImGuiWindowFlags window_flags);
// Popups, Modals
IMGUI_API bool BeginPopupEx(ImGuiID id, ImGuiWindowFlags extra_window_flags);
IMGUI_API void OpenPopupEx(ImGuiID id, ImGuiPopupFlags popup_flags = ImGuiPopupFlags_None);
IMGUI_API void ClosePopupToLevel(int remaining, bool restore_focus_to_window_under_popup);
IMGUI_API void ClosePopupsOverWindow(ImGuiWindow* ref_window, bool restore_focus_to_window_under_popup);
IMGUI_API void ClosePopupsExceptModals();
IMGUI_API bool IsPopupOpen(ImGuiID id, ImGuiPopupFlags popup_flags);
IMGUI_API bool BeginPopupEx(ImGuiID id, ImGuiWindowFlags extra_flags);
IMGUI_API bool BeginTooltipEx(ImGuiTooltipFlags tooltip_flags, ImGuiWindowFlags extra_window_flags);
IMGUI_API bool BeginTooltipHidden();
IMGUI_API ImRect GetPopupAllowedExtentRect(ImGuiWindow* window);
IMGUI_API ImGuiWindow* GetTopMostPopupModal();
IMGUI_API ImGuiWindow* GetTopMostAndVisiblePopupModal();
@ -3156,6 +3259,10 @@ namespace ImGui
IMGUI_API ImVec2 FindBestWindowPosForPopup(ImGuiWindow* window);
IMGUI_API ImVec2 FindBestWindowPosForPopupEx(const ImVec2& ref_pos, const ImVec2& size, ImGuiDir* last_dir, const ImRect& r_outer, const ImRect& r_avoid, ImGuiPopupPositionPolicy policy);
// Tooltips
IMGUI_API bool BeginTooltipEx(ImGuiTooltipFlags tooltip_flags, ImGuiWindowFlags extra_window_flags);
IMGUI_API bool BeginTooltipHidden();
// Menus
IMGUI_API bool BeginViewportSideBar(const char* name, ImGuiViewport* viewport, ImGuiDir dir, float size, ImGuiWindowFlags window_flags);
IMGUI_API bool BeginMenuEx(const char* label, const char* icon, bool enabled = true);
@ -3173,7 +3280,7 @@ namespace ImGui
IMGUI_API void NavMoveRequestSubmit(ImGuiDir move_dir, ImGuiDir clip_dir, ImGuiNavMoveFlags move_flags, ImGuiScrollFlags scroll_flags);
IMGUI_API void NavMoveRequestForward(ImGuiDir move_dir, ImGuiDir clip_dir, ImGuiNavMoveFlags move_flags, ImGuiScrollFlags scroll_flags);
IMGUI_API void NavMoveRequestResolveWithLastItem(ImGuiNavItemData* result);
IMGUI_API void NavMoveRequestResolveWithPastTreeNode(ImGuiNavItemData* result, ImGuiNavTreeNodeData* tree_node_data);
IMGUI_API void NavMoveRequestResolveWithPastTreeNode(ImGuiNavItemData* result, ImGuiTreeNodeStackData* tree_node_data);
IMGUI_API void NavMoveRequestCancel();
IMGUI_API void NavMoveRequestApplyResult();
IMGUI_API void NavMoveRequestTryWrapping(ImGuiWindow* window, ImGuiNavMoveFlags move_flags);
@ -3200,7 +3307,7 @@ namespace ImGui
inline bool IsGamepadKey(ImGuiKey key) { return key >= ImGuiKey_Gamepad_BEGIN && key < ImGuiKey_Gamepad_END; }
inline bool IsMouseKey(ImGuiKey key) { return key >= ImGuiKey_Mouse_BEGIN && key < ImGuiKey_Mouse_END; }
inline bool IsAliasKey(ImGuiKey key) { return key >= ImGuiKey_Aliases_BEGIN && key < ImGuiKey_Aliases_END; }
inline bool IsModKey(ImGuiKey key) { return key >= ImGuiKey_LeftCtrl && key <= ImGuiKey_RightSuper; }
inline bool IsLRModKey(ImGuiKey key) { return key >= ImGuiKey_LeftCtrl && key <= ImGuiKey_RightSuper; }
ImGuiKeyChord FixupKeyChord(ImGuiKeyChord key_chord);
inline ImGuiKey ConvertSingleModFlagToKey(ImGuiKey key)
{
@ -3238,7 +3345,7 @@ namespace ImGui
IMGUI_API ImGuiID GetKeyOwner(ImGuiKey key);
IMGUI_API void SetKeyOwner(ImGuiKey key, ImGuiID owner_id, ImGuiInputFlags flags = 0);
IMGUI_API void SetKeyOwnersForKeyChord(ImGuiKeyChord key, ImGuiID owner_id, ImGuiInputFlags flags = 0);
IMGUI_API void SetItemKeyOwner(ImGuiKey key, ImGuiInputFlags flags = 0); // Set key owner to last item if it is hovered or active. Equivalent to 'if (IsItemHovered() || IsItemActive()) { SetKeyOwner(key, GetItemID());'.
IMGUI_API void SetItemKeyOwner(ImGuiKey key, ImGuiInputFlags flags); // Set key owner to last item if it is hovered or active. Equivalent to 'if (IsItemHovered() || IsItemActive()) { SetKeyOwner(key, GetItemID());'.
IMGUI_API bool TestKeyOwner(ImGuiKey key, ImGuiID owner_id); // Test that key is either not owned, either owned by 'owner_id'
inline ImGuiKeyOwnerData* GetKeyOwnerData(ImGuiContext* ctx, ImGuiKey key) { if (key & ImGuiMod_Mask_) key = ConvertSingleModFlagToKey(key); IM_ASSERT(IsNamedKey(key)); return &ctx->KeysOwnerData[key - ImGuiKey_NamedKey_BEGIN]; }
@ -3301,6 +3408,18 @@ namespace ImGui
IMGUI_API int TypingSelectFindNextSingleCharMatch(ImGuiTypingSelectRequest* req, int items_count, const char* (*get_item_name_func)(void*, int), void* user_data, int nav_item_idx);
IMGUI_API int TypingSelectFindBestLeadingMatch(ImGuiTypingSelectRequest* req, int items_count, const char* (*get_item_name_func)(void*, int), void* user_data);
// Box-Select API
IMGUI_API bool BeginBoxSelect(const ImRect& scope_rect, ImGuiWindow* window, ImGuiID box_select_id, ImGuiMultiSelectFlags ms_flags);
IMGUI_API void EndBoxSelect(const ImRect& scope_rect, ImGuiMultiSelectFlags ms_flags);
// Multi-Select API
IMGUI_API void MultiSelectItemHeader(ImGuiID id, bool* p_selected, ImGuiButtonFlags* p_button_flags);
IMGUI_API void MultiSelectItemFooter(ImGuiID id, bool* p_selected, bool* p_pressed);
IMGUI_API void MultiSelectAddSetAll(ImGuiMultiSelectTempData* ms, bool selected);
IMGUI_API void MultiSelectAddSetRange(ImGuiMultiSelectTempData* ms, bool selected, int range_dir, ImGuiSelectionUserData first_item, ImGuiSelectionUserData last_item);
inline ImGuiBoxSelectState* GetBoxSelectState(ImGuiID id) { ImGuiContext& g = *GImGui; return (id != 0 && g.BoxSelectState.ID == id && g.BoxSelectState.IsActive) ? &g.BoxSelectState : NULL; }
inline ImGuiMultiSelectState* GetMultiSelectState(ImGuiID id) { ImGuiContext& g = *GImGui; return g.MultiSelectStorage.GetByKey(id); }
// Internal Columns API (this is not exposed because we will encourage transitioning to the Tables API)
IMGUI_API void SetWindowClipRectBeforeSetChannel(ImGuiWindow* window, const ImRect& clip_rect);
IMGUI_API void BeginColumns(const char* str_id, int count, ImGuiOldColumnFlags flags = 0); // setup number of columns. use an identifier to distinguish multiple column sets. close with EndColumns().
@ -3317,7 +3436,6 @@ namespace ImGui
IMGUI_API void TableOpenContextMenu(int column_n = -1);
IMGUI_API void TableSetColumnWidth(int column_n, float width);
IMGUI_API void TableSetColumnSortDirection(int column_n, ImGuiSortDirection sort_direction, bool append_to_sort_specs);
IMGUI_API int TableGetHoveredColumn(); // May use (TableGetColumnFlags() & ImGuiTableColumnFlags_IsHovered) instead. Return hovered column. return -1 when table is not hovered. return columns_count if the unused space at the right of visible columns is hovered.
IMGUI_API int TableGetHoveredRow(); // Retrieve *PREVIOUS FRAME* hovered row. This difference with TableGetHoveredColumn() is the reason why this is not public yet.
IMGUI_API float TableGetHeaderRowHeight();
IMGUI_API float TableGetHeaderAngledMaxLabelWidth();
@ -3353,7 +3471,7 @@ namespace ImGui
IMGUI_API ImRect TableGetCellBgRect(const ImGuiTable* table, int column_n);
IMGUI_API const char* TableGetColumnName(const ImGuiTable* table, int column_n);
IMGUI_API ImGuiID TableGetColumnResizeID(ImGuiTable* table, int column_n, int instance_no = 0);
IMGUI_API float TableGetMaxColumnWidth(const ImGuiTable* table, int column_n);
IMGUI_API float TableCalcMaxColumnWidth(const ImGuiTable* table, int column_n);
IMGUI_API void TableSetColumnWidthAutoSingle(ImGuiTable* table, int column_n);
IMGUI_API void TableSetColumnWidthAutoAll(ImGuiTable* table);
IMGUI_API void TableRemove(ImGuiTable* table);
@ -3398,7 +3516,7 @@ namespace ImGui
IMGUI_API void RenderTextClipped(const ImVec2& pos_min, const ImVec2& pos_max, const char* text, const char* text_end, const ImVec2* text_size_if_known, const ImVec2& align = ImVec2(0, 0), const ImRect* clip_rect = NULL);
IMGUI_API void RenderTextClippedEx(ImDrawList* draw_list, const ImVec2& pos_min, const ImVec2& pos_max, const char* text, const char* text_end, const ImVec2* text_size_if_known, const ImVec2& align = ImVec2(0, 0), const ImRect* clip_rect = NULL);
IMGUI_API void RenderTextEllipsis(ImDrawList* draw_list, const ImVec2& pos_min, const ImVec2& pos_max, float clip_max_x, float ellipsis_max_x, const char* text, const char* text_end, const ImVec2* text_size_if_known);
IMGUI_API void RenderFrame(ImVec2 p_min, ImVec2 p_max, ImU32 fill_col, bool border = true, float rounding = 0.0f);
IMGUI_API void RenderFrame(ImVec2 p_min, ImVec2 p_max, ImU32 fill_col, bool borders = true, float rounding = 0.0f);
IMGUI_API void RenderFrameBorder(ImVec2 p_min, ImVec2 p_max, float rounding = 0.0f);
IMGUI_API void RenderColorRectWithAlphaCheckerboard(ImDrawList* draw_list, ImVec2 p_min, ImVec2 p_max, ImU32 fill_col, float grid_step, ImVec2 grid_off, float rounding = 0.0f, ImDrawFlags flags = 0);
IMGUI_API void RenderNavHighlight(const ImRect& bb, ImGuiID id, ImGuiNavHighlightFlags flags = ImGuiNavHighlightFlags_None); // Navigation highlight
@ -3438,11 +3556,13 @@ namespace ImGui
IMGUI_API bool DragBehavior(ImGuiID id, ImGuiDataType data_type, void* p_v, float v_speed, const void* p_min, const void* p_max, const char* format, ImGuiSliderFlags flags);
IMGUI_API bool SliderBehavior(const ImRect& bb, ImGuiID id, ImGuiDataType data_type, void* p_v, const void* p_min, const void* p_max, const char* format, ImGuiSliderFlags flags, ImRect* out_grab_bb);
IMGUI_API bool SplitterBehavior(const ImRect& bb, ImGuiID id, ImGuiAxis axis, float* size1, float* size2, float min_size1, float min_size2, float hover_extend = 0.0f, float hover_visibility_delay = 0.0f, ImU32 bg_col = 0);
// Widgets: Tree Nodes
IMGUI_API bool TreeNodeBehavior(ImGuiID id, ImGuiTreeNodeFlags flags, const char* label, const char* label_end = NULL);
IMGUI_API void TreePushOverrideID(ImGuiID id);
IMGUI_API void TreeNodeSetOpen(ImGuiID id, bool open);
IMGUI_API bool TreeNodeUpdateNextOpen(ImGuiID id, ImGuiTreeNodeFlags flags); // Return open state. Consume previous SetNextItemOpen() data, if any. May return true when logging.
IMGUI_API void SetNextItemSelectionUserData(ImGuiSelectionUserData selection_user_data);
IMGUI_API bool TreeNodeGetOpen(ImGuiID storage_id);
IMGUI_API void TreeNodeSetOpen(ImGuiID storage_id, bool open);
IMGUI_API bool TreeNodeUpdateNextOpen(ImGuiID storage_id, ImGuiTreeNodeFlags flags); // Return open state. Consume previous SetNextItemOpen() data, if any. May return true when logging.
// Template functions are instantiated in imgui_widgets.cpp for a finite number of types.
// To use them externally (for custom widget) you may need an "extern template" statement in your code in order to link to existing instances and silence Clang warnings (see #2036).
@ -3458,7 +3578,7 @@ namespace ImGui
IMGUI_API const ImGuiDataTypeInfo* DataTypeGetInfo(ImGuiDataType data_type);
IMGUI_API int DataTypeFormatString(char* buf, int buf_size, ImGuiDataType data_type, const void* p_data, const char* format);
IMGUI_API void DataTypeApplyOp(ImGuiDataType data_type, int op, void* output, const void* arg_1, const void* arg_2);
IMGUI_API bool DataTypeApplyFromText(const char* buf, ImGuiDataType data_type, void* p_data, const char* format);
IMGUI_API bool DataTypeApplyFromText(const char* buf, ImGuiDataType data_type, void* p_data, const char* format, void* p_data_when_empty = NULL);
IMGUI_API int DataTypeCompare(ImGuiDataType data_type, const void* arg_1, const void* arg_2);
IMGUI_API bool DataTypeClamp(ImGuiDataType data_type, void* p_data, const void* p_min, const void* p_max);
@ -3469,6 +3589,7 @@ namespace ImGui
IMGUI_API bool TempInputScalar(const ImRect& bb, ImGuiID id, const char* label, ImGuiDataType data_type, void* p_data, const char* format, const void* p_clamp_min = NULL, const void* p_clamp_max = NULL);
inline bool TempInputIsActive(ImGuiID id) { ImGuiContext& g = *GImGui; return (g.ActiveId == id && g.TempInputId == id); }
inline ImGuiInputTextState* GetInputTextState(ImGuiID id) { ImGuiContext& g = *GImGui; return (id != 0 && g.InputTextState.ID == id) ? &g.InputTextState : NULL; } // Get input text state if active
IMGUI_API void SetNextItemRefVal(ImGuiDataType data_type, void* p_data);
// Color
IMGUI_API void ColorTooltip(const char* text, const float* col, ImGuiColorEditFlags flags);
@ -3488,12 +3609,8 @@ namespace ImGui
IMGUI_API void GcCompactTransientWindowBuffers(ImGuiWindow* window);
IMGUI_API void GcAwakeTransientWindowBuffers(ImGuiWindow* window);
// Debug Log
IMGUI_API void DebugLog(const char* fmt, ...) IM_FMTARGS(1);
IMGUI_API void DebugLogV(const char* fmt, va_list args) IM_FMTLIST(1);
IMGUI_API void DebugAllocHook(ImGuiDebugAllocInfo* info, int frame_count, void* ptr, size_t size); // size >= 0 : alloc, size = -1 : free
// Debug Tools
IMGUI_API void DebugAllocHook(ImGuiDebugAllocInfo* info, int frame_count, void* ptr, size_t size); // size >= 0 : alloc, size = -1 : free
IMGUI_API void ErrorCheckEndFrameRecover(ImGuiErrorLogCallback log_callback, void* user_data = NULL);
IMGUI_API void ErrorCheckEndWindowRecover(ImGuiErrorLogCallback log_callback, void* user_data = NULL);
IMGUI_API void ErrorCheckUsingSetCursorPosToExtendParentBoundaries();
@ -3520,6 +3637,7 @@ namespace ImGui
IMGUI_API void DebugNodeTableSettings(ImGuiTableSettings* settings);
IMGUI_API void DebugNodeInputTextState(ImGuiInputTextState* state);
IMGUI_API void DebugNodeTypingSelectState(ImGuiTypingSelectState* state);
IMGUI_API void DebugNodeMultiSelectState(ImGuiMultiSelectState* state);
IMGUI_API void DebugNodeWindow(ImGuiWindow* window, const char* label);
IMGUI_API void DebugNodeWindowSettings(ImGuiWindowSettings* settings);
IMGUI_API void DebugNodeWindowsList(ImVector<ImGuiWindow*>* windows, const char* label);

View file

@ -0,0 +1,167 @@
//
// Created by Matty on 2022-01-28.
//
#define IMGUI_DEFINE_MATH_OPERATORS
#include "imgui_neo_internal.h"
#include "imgui_internal.h"
#include <cstdint>
namespace ImGui {
void RenderNeoSequencerBackground(const ImVec4 &color, const ImVec2 & cursor, const ImVec2 &size, ImDrawList * drawList, float sequencerRounding) {
if(!drawList) drawList = ImGui::GetWindowDrawList();
const ImRect area = {cursor, cursor + size};
drawList->AddRectFilled(area.Min, area.Max, ColorConvertFloat4ToU32(color), sequencerRounding);
}
void RenderNeoSequencerTopBarBackground(const ImVec4 &color, const ImVec2 &cursor, const ImVec2 &size,
ImDrawList *drawList, float sequencerRounding) {
if(!drawList) drawList = ImGui::GetWindowDrawList();
const ImRect barArea = {cursor, cursor + size};
drawList->AddRectFilled(barArea.Min, barArea.Max, ColorConvertFloat4ToU32(color), sequencerRounding);
}
void
RenderNeoSequencerTopBarOverlay(float zoom, float valuesWidth,uint32_t startFrame, uint32_t endFrame, uint32_t offsetFrame, const ImVec2 &cursor, const ImVec2 &size,
ImDrawList *drawList, bool drawFrameLines,
bool drawFrameText, float maxPixelsPerTick) {
if(!drawList) drawList = ImGui::GetWindowDrawList();
const auto & style = GetStyle();
const ImRect barArea = {cursor + ImVec2{style.FramePadding.x + valuesWidth,style.FramePadding.y}, cursor + size };
const uint32_t viewEnd = endFrame + offsetFrame;
const uint32_t viewStart = startFrame + offsetFrame;
if(drawFrameLines) {
const auto count = (int32_t)((float)((viewEnd + 1) - viewStart) / zoom);
int32_t counter = 0;
uint32_t primaryFrames = pow(10, counter++);
uint32_t secondaryFrames = pow(10, counter);
float perFrameWidth = GetPerFrameWidth(size.x, valuesWidth, endFrame, startFrame, zoom);
if(perFrameWidth <= 0.0f) return;
while (perFrameWidth < maxPixelsPerTick)
{
primaryFrames = pow(10, counter++);
secondaryFrames = pow(10, counter);
perFrameWidth *= (float)primaryFrames;
}
if(primaryFrames == 0 || secondaryFrames == 0) {
primaryFrames = 1;
secondaryFrames = 10;
}
for(int32_t i = 0; i < count; i++) {
const auto primaryFrame = ((viewStart + i) % primaryFrames == 0);
const auto secondaryFrame = ((viewStart + i) % secondaryFrames == 0);
if(!primaryFrame && !secondaryFrame) continue;
const auto lineHeight = secondaryFrame ? barArea.GetSize().y : barArea.GetSize().y / 2.0f;
const ImVec2 p1 = {barArea.Min.x + (float)i * (perFrameWidth / (float)primaryFrames), barArea.Max.y};
const ImVec2 p2 = {barArea.Min.x + (float)i * (perFrameWidth / (float)primaryFrames), barArea.Max.y - lineHeight};
drawList->AddLine(p1,p2, IM_COL32_WHITE, 1.0f);
if(drawFrameText && secondaryFrame) {
char text[10];
const auto printRes = snprintf(text, sizeof(text), "%i", viewStart + i);
if(printRes > 0) {
drawList->AddText(NULL, 0, {p1.x + 2.0f, barArea.Min.y }, IM_COL32_WHITE,text);
}
}
}
}
}
void RenderNeoTimelineLabel(const char * label,const ImVec2 & cursor,const ImVec2 & size, const ImVec4& color,bool isGroup, bool isOpen, ImDrawList *drawList)
{
const auto& imStyle = GetStyle();
if(!drawList) drawList = ImGui::GetWindowDrawList();
auto c = cursor;
if(isGroup) {
RenderArrow(drawList,c,IM_COL32_WHITE,isOpen ? ImGuiDir_Down : ImGuiDir_Right);
c.x += size.y + imStyle.ItemSpacing.x;
}
drawList->AddText(c,ColorConvertFloat4ToU32(color),label, FindRenderedTextEnd(label));
}
void RenderNeoTimelinesBorder(const ImVec4 &color, const ImVec2 &cursor, const ImVec2 &size, ImDrawList *drawList,
float rounding, float borderSize)
{
if(!drawList) drawList = ImGui::GetWindowDrawList();
drawList->AddRect(cursor,cursor + size,ColorConvertFloat4ToU32(color),rounding, 0, borderSize);
}
void RenderNeoTimelane(bool selected,const ImVec2 & cursor, const ImVec2& size, const ImVec4& highlightColor, ImDrawList *drawList) {
if(!drawList) drawList = ImGui::GetWindowDrawList();
if(selected) {
const ImRect area = {cursor, cursor + size};
drawList->AddRectFilled(area.Min, area.Max, ColorConvertFloat4ToU32(highlightColor));
}
}
float GetPerFrameWidth(float totalSizeX, float valuesWidth, uint32_t endFrame, uint32_t startFrame, float zoom) {
const auto& imStyle = GetStyle();
const auto size = totalSizeX - valuesWidth - imStyle.FramePadding.x;
auto count = (endFrame + 1) - startFrame;
return ((size / (float)count) * zoom);
}
struct Vec2Pair {
ImVec2 a;
ImVec2 b;
};
static Vec2Pair getCurrentFrameLine(const ImRect & pointerBB, float timelineHeight) {
const auto center = ImVec2{pointerBB.Min.x, pointerBB.Max.y} + ImVec2{pointerBB.GetSize().x / 2.0f, 0};
return Vec2Pair{ center, center + ImVec2{0, timelineHeight} };
}
void RenderNeoSequencerCurrentFrame(const ImVec4 &color, const ImVec4 &topColor, const ImRect &pointerBB,
float timelineHeight, float lineWidth, ImDrawList *drawList) {
if(!drawList) drawList = ImGui::GetWindowDrawList();
const auto pair = getCurrentFrameLine(pointerBB, timelineHeight);
drawList->AddLine(pair.a, pair.b, ColorConvertFloat4ToU32(color), lineWidth);
drawList->PopClipRect();
{ //Top pointer has custom shape, we have to create it
const auto size = pointerBB.GetSize();
ImVec2 pts[5];
pts[0] = pointerBB.Min;
pts[1] = pointerBB.Min + ImVec2{size.x, 0};
pts[2] = pointerBB.Min + ImVec2{size.x, size.y * 0.85f};
pts[3] = pointerBB.Min + ImVec2{size.x / 2, size.y};
pts[4] = pointerBB.Min + ImVec2{0, size.y * 0.85f};
drawList->AddConvexPolyFilled(pts, sizeof(pts) / sizeof(*pts), ColorConvertFloat4ToU32(topColor));
}
}
}

View file

@ -0,0 +1,24 @@
//
// Created by Matty on 2022-01-28.
//
#ifndef IMGUI_NEO_INTERNAL_H
#define IMGUI_NEO_INTERNAL_H
#include "imgui.h"
#include "imgui_internal.h"
#include <cstdint>
namespace ImGui {
IMGUI_API void RenderNeoSequencerBackground(const ImVec4& color, const ImVec2 & cursor, const ImVec2& size, ImDrawList * drawList = nullptr, float sequencerRounding = 0.0f);
IMGUI_API void RenderNeoSequencerTopBarBackground(const ImVec4& color, const ImVec2 & cursor, const ImVec2& size, ImDrawList * drawList = nullptr, float sequencerRounding = 0.0f);
IMGUI_API void RenderNeoSequencerTopBarOverlay(float zoom, float valuesWidth,uint32_t startFrame, uint32_t endFrame, uint32_t offsetFrame, const ImVec2 &cursor, const ImVec2& size, ImDrawList * drawList = nullptr, bool drawFrameLines = true, bool drawFrameText = true, float maxPixelsPerTick = -1.0f);
IMGUI_API void RenderNeoTimelineLabel(const char * label,const ImVec2 & cursor,const ImVec2 & size, const ImVec4& color,bool isGroup = false, bool isOpen = false, ImDrawList *drawList = nullptr );
IMGUI_API void RenderNeoTimelane(bool selected,const ImVec2 & cursor, const ImVec2& size, const ImVec4& highlightColor, ImDrawList *drawList = nullptr);
IMGUI_API void RenderNeoTimelinesBorder(const ImVec4& color, const ImVec2 & cursor, const ImVec2& size, ImDrawList * drawList = nullptr, float rounding = 0.0f, float borderSize = 1.0f);
IMGUI_API void RenderNeoSequencerCurrentFrame(const ImVec4& color,const ImVec4 & topColor,const ImRect & pointerBB ,float timelineHeight, float lineWidth = 1.0f, ImDrawList * drawList = nullptr);
IMGUI_API float GetPerFrameWidth(float totalSizeX, float valuesWidth, uint32_t endFrame, uint32_t startFrame, float zoom);
}
#endif //IMGUI_NEO_INTERNAL_H

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,155 @@
//
// Created by Matty on 2022-01-28.
//
#ifndef IMGUI_NEO_SEQUENCER_H
#define IMGUI_NEO_SEQUENCER_H
#include "imgui.h"
#include <vector>
typedef int ImGuiNeoSequencerFlags;
typedef int ImGuiNeoSequencerCol;
typedef int ImGuiNeoTimelineFlags;
typedef int ImGuiNeoTimelineIsSelectedFlags;
// Flags for ImGui::BeginNeoSequencer()
enum ImGuiNeoSequencerFlags_
{
ImGuiNeoSequencerFlags_None = 0 ,
ImGuiNeoSequencerFlags_AllowLengthChanging = 1 << 0, // Allows changing length of sequence
ImGuiNeoSequencerFlags_EnableSelection = 1 << 1, // Enables selection of keyframes
ImGuiNeoSequencerFlags_HideZoom = 1 << 2, // Disables zoom bar
//ImGuiNeoSequencerFlags_PH = 1 << 3, // PLACEHOLDER
ImGuiNeoSequencerFlags_AlwaysShowHeader = 1 << 4, // Enables overlay header, keeping it visible when scrolling
// Selection options, only work with enable selection flag
ImGuiNeoSequencerFlags_Selection_EnableDragging = 1 << 5,
ImGuiNeoSequencerFlags_Selection_EnableDeletion = 1 << 6,
};
// Flags for ImGui::BeginNeoTimeline()
enum ImGuiNeoTimelineFlags_
{
ImGuiNeoTimelineFlags_None = 0 ,
ImGuiNeoTimelineFlags_AllowFrameChanging = 1 << 0,
ImGuiNeoTimelineFlags_Group = 1 << 1,
};
// Flags for ImGui::IsNeoTimelineSelected()
enum ImGuiNeoTimelineIsSelectedFlags_
{
ImGuiNeoTimelineIsSelectedFlags_None = 0 ,
ImGuiNeoTimelineIsSelectedFlags_NewlySelected = 1 << 0,
};
enum ImGuiNeoSequencerCol_
{
ImGuiNeoSequencerCol_Bg,
ImGuiNeoSequencerCol_TopBarBg,
ImGuiNeoSequencerCol_SelectedTimeline,
ImGuiNeoSequencerCol_TimelineBorder,
ImGuiNeoSequencerCol_TimelinesBg,
ImGuiNeoSequencerCol_FramePointer,
ImGuiNeoSequencerCol_FramePointerHovered,
ImGuiNeoSequencerCol_FramePointerPressed,
ImGuiNeoSequencerCol_Keyframe,
ImGuiNeoSequencerCol_KeyframeHovered,
ImGuiNeoSequencerCol_KeyframePressed,
ImGuiNeoSequencerCol_KeyframeSelected,
ImGuiNeoSequencerCol_FramePointerLine,
ImGuiNeoSequencerCol_ZoomBarBg,
ImGuiNeoSequencerCol_ZoomBarSlider,
ImGuiNeoSequencerCol_ZoomBarSliderHovered,
ImGuiNeoSequencerCol_ZoomBarSliderEnds,
ImGuiNeoSequencerCol_ZoomBarSliderEndsHovered,
ImGuiNeoSequencerCol_SelectionBorder,
ImGuiNeoSequencerCol_Selection,
ImGuiNeoSequencerCol_COUNT
};
struct ImGuiNeoSequencerStyle {
float SequencerRounding = 2.5f; // Corner rounding around whole sequencer
float TopBarHeight = 0.0f; // Value <= 0.0f = Height is calculated by FontSize + FramePadding.y * 2.0f
bool TopBarShowFrameLines = true; // Show line for every frame in top bar
bool TopBarShowFrameTexts = true; // Show frame number every 10th frame
ImVec2 ItemSpacing = {4.0f,0.5f};
float DepthItemSpacing = 10.0f; // Amount of text offset per depth level in timeline values
float TopBarSpacing = 3.0f; // Space between top bar and timeline
float TimelineBorderSize = 1.0f;
float CurrentFramePointerSize = 7.0f; // Size of pointing arrow above current frame line
float CurrentFrameLineWidth = 1.0f; // Width of line showing current frame over timeline
float ZoomHeightScale = 1.0f; // Scale of Zoom bar, base height is font size
float CollidedKeyframeOffset = 3.5f; // Offset on which colliding keyframes are rendered
float MaxSizePerTick = 4.0f; // Maximum amount of pixels per tick on timeline (if less pixels is present, ticks are not rendered)
ImVec4 Colors[ImGuiNeoSequencerCol_COUNT];
ImGuiKey ModRemoveKey = ImGuiMod_Ctrl; // Key mod which when held removes selected keyframes from present selection
ImGuiKey ModAddKey = ImGuiMod_Shift; // Key mod which when held adds selected keyframes to present selection
ImGuiNeoSequencerStyle();
};
namespace ImGui {
typedef int32_t FrameIndexType;
IMGUI_API const ImVec4& GetStyleNeoSequencerColorVec4(ImGuiNeoSequencerCol idx);
IMGUI_API ImGuiNeoSequencerStyle& GetNeoSequencerStyle();
IMGUI_API void PushNeoSequencerStyleColor(ImGuiNeoSequencerCol idx, ImU32 col);
IMGUI_API void PushNeoSequencerStyleColor(ImGuiNeoSequencerCol idx, const ImVec4& col);
IMGUI_API void PopNeoSequencerStyleColor(int count = 1);
IMGUI_API bool BeginNeoSequencer(const char* id, FrameIndexType * frame, FrameIndexType * startFrame, FrameIndexType * endFrame,const ImVec2& size = ImVec2(0, 0),ImGuiNeoSequencerFlags flags = ImGuiNeoSequencerFlags_None);
IMGUI_API void EndNeoSequencer(); //Call only when BeginNeoSequencer() returns true!!
IMGUI_API bool BeginNeoGroup(const char* label, bool* open = nullptr);
IMGUI_API void EndNeoGroup();
IMGUI_API bool BeginNeoTimeline(const char* label,FrameIndexType ** keyframes, uint32_t keyframeCount, bool * open = nullptr, ImGuiNeoTimelineFlags flags = ImGuiNeoTimelineFlags_None);
IMGUI_API void EndNeoTimeLine(); //Call only when BeginNeoTimeline() returns true!!
// Fully customizable timeline with per key callback
IMGUI_API bool BeginNeoTimelineEx(const char* label, bool * open = nullptr, ImGuiNeoTimelineFlags flags = ImGuiNeoTimelineFlags_None);
IMGUI_API void NeoKeyframe(int32_t* value);
IMGUI_API bool IsNeoKeyframeHovered();
IMGUI_API bool IsNeoKeyframeSelected();
IMGUI_API bool IsNeoKeyframeRightClicked();
// Selection API
// DON'T delete keyframes while dragging, internal buffer will get corrupted
// Order for deletion is generally:
// CanDelete? -> DataSize? -> GetData() -> Delete your data -> ClearSelection()
IMGUI_API void NeoClearSelection(); // Clears selection
IMGUI_API bool NeoIsSelecting(); // Are we currently selecting?
IMGUI_API bool NeoHasSelection(); // Is anything selected?
IMGUI_API bool NeoIsDraggingSelection(); // Are we dragging selection?
IMGUI_API bool NeoCanDeleteSelection(); // Can selection deletion be done?
IMGUI_API bool IsNeoKeyframeSelectionRightClicked(); // Is selection rightclicked?
// Call only in BeginNeoTimeline / EndNeoTimeLine scope, returns selection per timeline and size per timeline
IMGUI_API uint32_t GetNeoKeyframeSelectionSize();
IMGUI_API void GetNeoKeyframeSelection(FrameIndexType * selection);
// Sets currently selected timeline inside BeginNeoSequencer scope
IMGUI_API void SetSelectedTimeline(const char* timelineLabel);
IMGUI_API bool IsNeoTimelineSelected(ImGuiNeoTimelineIsSelectedFlags flags = ImGuiNeoTimelineIsSelectedFlags_None);
#ifdef __cplusplus
// C++ helper
// IMGUI_API bool BeginNeoTimeline(const char* label,std::vector<int32_t> & keyframes ,bool * open = nullptr, ImGuiNeoTimelineFlags flags = ImGuiNeoTimelineFlags_None);
#endif
}
#endif //IMGUI_NEO_SEQUENCER_H

View file

@ -1,4 +1,4 @@
// dear imgui, v1.90.8 WIP
// dear imgui, v1.91.1
// (tables and columns code)
/*
@ -412,8 +412,8 @@ bool ImGui::BeginTableEx(const char* name, ImGuiID id, int columns_count, ImG
SetNextWindowScroll(ImVec2(0.0f, 0.0f));
// Create scrolling region (without border and zero window padding)
ImGuiWindowFlags child_flags = (flags & ImGuiTableFlags_ScrollX) ? ImGuiWindowFlags_HorizontalScrollbar : ImGuiWindowFlags_None;
BeginChildEx(name, instance_id, outer_rect.GetSize(), false, child_flags);
ImGuiWindowFlags child_window_flags = (flags & ImGuiTableFlags_ScrollX) ? ImGuiWindowFlags_HorizontalScrollbar : ImGuiWindowFlags_None;
BeginChildEx(name, instance_id, outer_rect.GetSize(), ImGuiChildFlags_None, child_window_flags);
table->InnerWindow = g.CurrentWindow;
table->WorkRect = table->InnerWindow->WorkRect;
table->OuterRect = table->InnerWindow->Rect();
@ -460,16 +460,27 @@ bool ImGui::BeginTableEx(const char* name, ImGuiID id, int columns_count, ImG
temp_data->HostBackupItemWidthStackSize = outer_window->DC.ItemWidthStack.Size;
inner_window->DC.PrevLineSize = inner_window->DC.CurrLineSize = ImVec2(0.0f, 0.0f);
// Make left and top borders not overlap our contents by offsetting HostClipRect (#6765)
// Make borders not overlap our contents by offsetting HostClipRect (#6765, #7428, #3752)
// (we normally shouldn't alter HostClipRect as we rely on TableMergeDrawChannels() expanding non-clipped column toward the
// limits of that rectangle, in order for ImDrawListSplitter::Merge() to merge the draw commands. However since the overlap
// problem only affect scrolling tables in this case we can get away with doing it without extra cost).
if (inner_window != outer_window)
{
// FIXME: Because inner_window's Scrollbar doesn't know about border size, since it's not encoded in window->WindowBorderSize,
// it already overlaps it and doesn't need an extra offset. Ideally we should be able to pass custom border size with
// different x/y values to BeginChild().
if (flags & ImGuiTableFlags_BordersOuterV)
{
table->HostClipRect.Min.x = ImMin(table->HostClipRect.Min.x + TABLE_BORDER_SIZE, table->HostClipRect.Max.x);
if (inner_window->DecoOuterSizeX2 == 0.0f)
table->HostClipRect.Max.x = ImMax(table->HostClipRect.Max.x - TABLE_BORDER_SIZE, table->HostClipRect.Min.x);
}
if (flags & ImGuiTableFlags_BordersOuterH)
{
table->HostClipRect.Min.y = ImMin(table->HostClipRect.Min.y + TABLE_BORDER_SIZE, table->HostClipRect.Max.y);
if (inner_window->DecoOuterSizeY2 == 0.0f)
table->HostClipRect.Max.y = ImMax(table->HostClipRect.Max.y - TABLE_BORDER_SIZE, table->HostClipRect.Min.y);
}
}
// Padding and Spacing
@ -497,7 +508,7 @@ bool ImGui::BeginTableEx(const char* name, ImGuiID id, int columns_count, ImG
table->InnerClipRect = (inner_window == outer_window) ? table->WorkRect : inner_window->ClipRect;
table->InnerClipRect.ClipWith(table->WorkRect); // We need this to honor inner_width
table->InnerClipRect.ClipWithFull(table->HostClipRect);
table->InnerClipRect.Max.y = (flags & ImGuiTableFlags_NoHostExtendY) ? ImMin(table->InnerClipRect.Max.y, inner_window->WorkRect.Max.y) : inner_window->ClipRect.Max.y;
table->InnerClipRect.Max.y = (flags & ImGuiTableFlags_NoHostExtendY) ? ImMin(table->InnerClipRect.Max.y, inner_window->WorkRect.Max.y) : table->HostClipRect.Max.y;
table->RowPosY1 = table->RowPosY2 = table->WorkRect.Min.y; // This is needed somehow
table->RowTextBaseline = 0.0f; // This will be cleared again by TableBeginRow()
@ -1059,16 +1070,12 @@ void ImGui::TableUpdateLayout(ImGuiTable* table)
continue;
}
// Detect hovered column
if (is_hovering_table && mouse_skewed_x >= column->ClipRect.Min.x && mouse_skewed_x < column->ClipRect.Max.x)
table->HoveredColumnBody = (ImGuiTableColumnIdx)column_n;
// Lock start position
column->MinX = offset_x;
// Lock width based on start position and minimum/maximum width for this position
float max_width = TableGetMaxColumnWidth(table, column_n);
column->WidthGiven = ImMin(column->WidthGiven, max_width);
column->WidthMax = TableCalcMaxColumnWidth(table, column_n);
column->WidthGiven = ImMin(column->WidthGiven, column->WidthMax);
column->WidthGiven = ImMax(column->WidthGiven, ImMin(column->WidthRequest, table->MinColumnWidth));
column->MaxX = offset_x + column->WidthGiven + table->CellSpacingX1 + table->CellSpacingX2 + table->CellPaddingX * 2.0f;
@ -1117,8 +1124,13 @@ void ImGui::TableUpdateLayout(ImGuiTable* table)
column->Flags |= ImGuiTableColumnFlags_IsVisible;
if (column->SortOrder != -1)
column->Flags |= ImGuiTableColumnFlags_IsSorted;
if (table->HoveredColumnBody == column_n)
// Detect hovered column
if (is_hovering_table && mouse_skewed_x >= column->ClipRect.Min.x && mouse_skewed_x < column->ClipRect.Max.x)
{
column->Flags |= ImGuiTableColumnFlags_IsHovered;
table->HoveredColumnBody = (ImGuiTableColumnIdx)column_n;
}
// Alignment
// FIXME-TABLE: This align based on the whole column width, not per-cell, and therefore isn't useful in
@ -1249,7 +1261,7 @@ void ImGui::TableUpdateLayout(ImGuiTable* table)
if (table->Flags & ImGuiTableFlags_NoClip)
table->DrawSplitter->SetCurrentChannel(inner_window->DrawList, TABLE_DRAW_CHANNEL_NOCLIP);
else
inner_window->DrawList->PushClipRect(inner_window->ClipRect.Min, inner_window->ClipRect.Max, false);
inner_window->DrawList->PushClipRect(inner_window->InnerClipRect.Min, inner_window->InnerClipRect.Max, false);
}
// Process hit-testing on resizing borders. Actual size change will be applied in EndTable()
@ -1996,34 +2008,37 @@ void ImGui::TableEndRow(ImGuiTable* table)
// We need to do that in TableEndRow() instead of TableBeginRow() so the list clipper can mark end of row and
// get the new cursor position.
if (unfreeze_rows_request)
{
for (int column_n = 0; column_n < table->ColumnsCount; column_n++)
table->Columns[column_n].NavLayerCurrent = ImGuiNavLayer_Main;
if (unfreeze_rows_actual)
{
IM_ASSERT(table->IsUnfrozenRows == false);
const float y0 = ImMax(table->RowPosY2 + 1, window->InnerClipRect.Min.y);
table->IsUnfrozenRows = true;
table_instance->LastFrozenHeight = y0 - table->OuterRect.Min.y;
// BgClipRect starts as table->InnerClipRect, reduce it now and make BgClipRectForDrawCmd == BgClipRect
table->BgClipRect.Min.y = table->Bg2ClipRectForDrawCmd.Min.y = ImMin(y0, window->InnerClipRect.Max.y);
table->BgClipRect.Max.y = table->Bg2ClipRectForDrawCmd.Max.y = window->InnerClipRect.Max.y;
table->Bg2DrawChannelCurrent = table->Bg2DrawChannelUnfrozen;
IM_ASSERT(table->Bg2ClipRectForDrawCmd.Min.y <= table->Bg2ClipRectForDrawCmd.Max.y);
float row_height = table->RowPosY2 - table->RowPosY1;
table->RowPosY2 = window->DC.CursorPos.y = table->WorkRect.Min.y + table->RowPosY2 - table->OuterRect.Min.y;
table->RowPosY1 = table->RowPosY2 - row_height;
for (int column_n = 0; column_n < table->ColumnsCount; column_n++)
if (unfreeze_rows_actual)
{
ImGuiTableColumn* column = &table->Columns[column_n];
column->DrawChannelCurrent = column->DrawChannelUnfrozen;
column->ClipRect.Min.y = table->Bg2ClipRectForDrawCmd.Min.y;
}
IM_ASSERT(table->IsUnfrozenRows == false);
table->IsUnfrozenRows = true;
// Update cliprect ahead of TableBeginCell() so clipper can access to new ClipRect->Min.y
SetWindowClipRectBeforeSetChannel(window, table->Columns[0].ClipRect);
table->DrawSplitter->SetCurrentChannel(window->DrawList, table->Columns[0].DrawChannelCurrent);
// BgClipRect starts as table->InnerClipRect, reduce it now and make BgClipRectForDrawCmd == BgClipRect
table->BgClipRect.Min.y = table->Bg2ClipRectForDrawCmd.Min.y = ImMin(y0, window->InnerClipRect.Max.y);
table->BgClipRect.Max.y = table->Bg2ClipRectForDrawCmd.Max.y = window->InnerClipRect.Max.y;
table->Bg2DrawChannelCurrent = table->Bg2DrawChannelUnfrozen;
IM_ASSERT(table->Bg2ClipRectForDrawCmd.Min.y <= table->Bg2ClipRectForDrawCmd.Max.y);
float row_height = table->RowPosY2 - table->RowPosY1;
table->RowPosY2 = window->DC.CursorPos.y = table->WorkRect.Min.y + table->RowPosY2 - table->OuterRect.Min.y;
table->RowPosY1 = table->RowPosY2 - row_height;
for (int column_n = 0; column_n < table->ColumnsCount; column_n++)
{
ImGuiTableColumn* column = &table->Columns[column_n];
column->DrawChannelCurrent = column->DrawChannelUnfrozen;
column->ClipRect.Min.y = table->Bg2ClipRectForDrawCmd.Min.y;
}
// Update cliprect ahead of TableBeginCell() so clipper can access to new ClipRect->Min.y
SetWindowClipRectBeforeSetChannel(window, table->Columns[0].ClipRect);
table->DrawSplitter->SetCurrentChannel(window->DrawList, table->Columns[0].DrawChannelCurrent);
}
}
if (!(table->RowFlags & ImGuiTableRowFlags_Headers))
@ -2192,8 +2207,8 @@ void ImGui::TableEndCell(ImGuiTable* table)
// Note that actual columns widths are computed in TableUpdateLayout().
//-------------------------------------------------------------------------
// Maximum column content width given current layout. Use column->MinX so this value on a per-column basis.
float ImGui::TableGetMaxColumnWidth(const ImGuiTable* table, int column_n)
// Maximum column content width given current layout. Use column->MinX so this value differs on a per-column basis.
float ImGui::TableCalcMaxColumnWidth(const ImGuiTable* table, int column_n)
{
const ImGuiTableColumn* column = &table->Columns[column_n];
float max_width = FLT_MAX;
@ -2255,7 +2270,7 @@ void ImGui::TableSetColumnWidth(int column_n, float width)
// Compare both requested and actual given width to avoid overwriting requested width when column is stuck (minimum size, bounded)
IM_ASSERT(table->MinColumnWidth > 0.0f);
const float min_width = table->MinColumnWidth;
const float max_width = ImMax(min_width, TableGetMaxColumnWidth(table, column_n));
const float max_width = ImMax(min_width, column_0->WidthMax); // Don't use TableCalcMaxColumnWidth() here as it would rely on MinX from last instance (#7933)
column_0_width = ImClamp(column_0_width, min_width, max_width);
if (column_0->WidthGiven == column_0_width || column_0->WidthRequest == column_0_width)
return;
@ -2740,7 +2755,7 @@ void ImGui::TableDrawBorders(ImGuiTable* table)
const ImU32 outer_col = table->BorderColorStrong;
if ((table->Flags & ImGuiTableFlags_BordersOuter) == ImGuiTableFlags_BordersOuter)
{
inner_drawlist->AddRect(outer_border.Min, outer_border.Max + ImVec2(1, 1), outer_col, 0.0f, 0, border_size);
inner_drawlist->AddRect(outer_border.Min, outer_border.Max, outer_col, 0.0f, 0, border_size);
}
else if (table->Flags & ImGuiTableFlags_BordersOuterV)
{
@ -3004,7 +3019,8 @@ float ImGui::TableGetHeaderAngledMaxLabelWidth()
// The intent is that advanced users willing to create customized headers would not need to use this helper
// and can create their own! For example: TableHeader() may be preceded by Checkbox() or other custom widgets.
// See 'Demo->Tables->Custom headers' for a demonstration of implementing a custom version of this.
// This code is constructed to not make much use of internal functions, as it is intended to be a template to copy.
// This code is intentionally written to not make much use of internal functions, to give you better direction
// if you need to write your own.
// FIXME-TABLE: TableOpenContextMenu() and TableGetHeaderRowHeight() are not public.
void ImGui::TableHeadersRow()
{
@ -3012,7 +3028,8 @@ void ImGui::TableHeadersRow()
ImGuiTable* table = g.CurrentTable;
IM_ASSERT(table != NULL && "Need to call TableHeadersRow() after BeginTable()!");
// Layout if not already done (this is automatically done by TableNextRow, we do it here solely to facilitate stepping in debugger as it is frequent to step in TableUpdateLayout)
// Call layout if not already done. This is automatically done by TableNextRow: we do it here _only_ to make
// it easier to debug-step in TableUpdateLayout(). Your own version of this function doesn't need this.
if (!table->IsLayoutLocked)
TableUpdateLayout(table);
@ -3029,8 +3046,7 @@ void ImGui::TableHeadersRow()
if (!TableSetColumnIndex(column_n))
continue;
// Push an id to allow unnamed labels (generally accidental, but let's behave nicely with them)
// In your own code you may omit the PushID/PopID all-together, provided you know they won't collide.
// Push an id to allow empty/unnamed headers. This is also idiomatic as it ensure there is a consistent ID path to access columns (for e.g. automation)
const char* name = (TableGetColumnFlags(column_n) & ImGuiTableColumnFlags_NoHeaderLabel) ? "" : TableGetColumnName(column_n);
PushID(column_n);
TableHeader(name);
@ -3269,7 +3285,7 @@ void ImGui::TableAngledHeadersRowEx(ImGuiID row_id, float angle, float max_label
ButtonBehavior(row_r, row_id, NULL, NULL);
KeepAliveID(row_id);
const float ascent_scaled = g.Font->Ascent * (g.FontSize / g.Font->FontSize); // FIXME: Standardize those scaling factors better
const float ascent_scaled = g.Font->Ascent * g.FontScale; // FIXME: Standardize those scaling factors better
const float line_off_for_ascent_x = (ImMax((g.FontSize - ascent_scaled) * 0.5f, 0.0f) / -sin_a) * (flip_label ? -1.0f : 1.0f);
const ImVec2 padding = g.Style.CellPadding; // We will always use swapped component
const ImVec2 align = g.Style.TableAngledHeadersTextAlign;
@ -3471,7 +3487,7 @@ void ImGui::TableDrawDefaultContextMenu(ImGuiTable* table, ImGuiTableFlags flags
Separator();
want_separator = true;
PushItemFlag(ImGuiItemFlags_SelectableDontClosePopup, true);
PushItemFlag(ImGuiItemFlags_AutoClosePopups, false);
for (int other_column_n = 0; other_column_n < table->ColumnsCount; other_column_n++)
{
ImGuiTableColumn* other_column = &table->Columns[other_column_n];
@ -4425,12 +4441,12 @@ void ImGui::EndColumns()
NavUpdateCurrentWindowIsScrollPushableX();
}
void ImGui::Columns(int columns_count, const char* id, bool border)
void ImGui::Columns(int columns_count, const char* id, bool borders)
{
ImGuiWindow* window = GetCurrentWindow();
IM_ASSERT(columns_count >= 1);
ImGuiOldColumnFlags flags = (border ? 0 : ImGuiOldColumnFlags_NoBorder);
ImGuiOldColumnFlags flags = (borders ? 0 : ImGuiOldColumnFlags_NoBorder);
//flags |= ImGuiOldColumnFlags_NoPreserveWidths; // NB: Legacy behavior
ImGuiOldColumns* columns = window->DC.CurrentColumns;
if (columns != NULL && columns->Count == columns_count && columns->Flags == flags)

File diff suppressed because it is too large Load diff

3261
source/engine/thirdparty/imgui/imnodes.cpp vendored Normal file

File diff suppressed because it is too large Load diff

438
source/engine/thirdparty/imgui/imnodes.h vendored Normal file
View file

@ -0,0 +1,438 @@
#pragma once
#include <stddef.h>
#include <imgui.h>
#ifdef IMNODES_USER_CONFIG
#include IMNODES_USER_CONFIG
#endif
#ifndef IMNODES_NAMESPACE
#define IMNODES_NAMESPACE ImNodes
#endif
typedef int ImNodesCol; // -> enum ImNodesCol_
typedef int ImNodesStyleVar; // -> enum ImNodesStyleVar_
typedef int ImNodesStyleFlags; // -> enum ImNodesStyleFlags_
typedef int ImNodesPinShape; // -> enum ImNodesPinShape_
typedef int ImNodesAttributeFlags; // -> enum ImNodesAttributeFlags_
typedef int ImNodesMiniMapLocation; // -> enum ImNodesMiniMapLocation_
enum ImNodesCol_
{
ImNodesCol_NodeBackground = 0,
ImNodesCol_NodeBackgroundHovered,
ImNodesCol_NodeBackgroundSelected,
ImNodesCol_NodeOutline,
ImNodesCol_TitleBar,
ImNodesCol_TitleBarHovered,
ImNodesCol_TitleBarSelected,
ImNodesCol_Link,
ImNodesCol_LinkHovered,
ImNodesCol_LinkSelected,
ImNodesCol_Pin,
ImNodesCol_PinHovered,
ImNodesCol_BoxSelector,
ImNodesCol_BoxSelectorOutline,
ImNodesCol_GridBackground,
ImNodesCol_GridLine,
ImNodesCol_GridLinePrimary,
ImNodesCol_MiniMapBackground,
ImNodesCol_MiniMapBackgroundHovered,
ImNodesCol_MiniMapOutline,
ImNodesCol_MiniMapOutlineHovered,
ImNodesCol_MiniMapNodeBackground,
ImNodesCol_MiniMapNodeBackgroundHovered,
ImNodesCol_MiniMapNodeBackgroundSelected,
ImNodesCol_MiniMapNodeOutline,
ImNodesCol_MiniMapLink,
ImNodesCol_MiniMapLinkSelected,
ImNodesCol_MiniMapCanvas,
ImNodesCol_MiniMapCanvasOutline,
ImNodesCol_COUNT
};
enum ImNodesStyleVar_
{
ImNodesStyleVar_GridSpacing = 0,
ImNodesStyleVar_NodeCornerRounding,
ImNodesStyleVar_NodePadding,
ImNodesStyleVar_NodeBorderThickness,
ImNodesStyleVar_LinkThickness,
ImNodesStyleVar_LinkLineSegmentsPerLength,
ImNodesStyleVar_LinkHoverDistance,
ImNodesStyleVar_PinCircleRadius,
ImNodesStyleVar_PinQuadSideLength,
ImNodesStyleVar_PinTriangleSideLength,
ImNodesStyleVar_PinLineThickness,
ImNodesStyleVar_PinHoverRadius,
ImNodesStyleVar_PinOffset,
ImNodesStyleVar_MiniMapPadding,
ImNodesStyleVar_MiniMapOffset,
ImNodesStyleVar_COUNT
};
enum ImNodesStyleFlags_
{
ImNodesStyleFlags_None = 0,
ImNodesStyleFlags_NodeOutline = 1 << 0,
ImNodesStyleFlags_GridLines = 1 << 2,
ImNodesStyleFlags_GridLinesPrimary = 1 << 3,
ImNodesStyleFlags_GridSnapping = 1 << 4
};
enum ImNodesPinShape_
{
ImNodesPinShape_Circle,
ImNodesPinShape_CircleFilled,
ImNodesPinShape_Triangle,
ImNodesPinShape_TriangleFilled,
ImNodesPinShape_Quad,
ImNodesPinShape_QuadFilled
};
// This enum controls the way the attribute pins behave.
enum ImNodesAttributeFlags_
{
ImNodesAttributeFlags_None = 0,
// Allow detaching a link by left-clicking and dragging the link at a pin it is connected to.
// NOTE: the user has to actually delete the link for this to work. A deleted link can be
// detected by calling IsLinkDestroyed() after EndNodeEditor().
ImNodesAttributeFlags_EnableLinkDetachWithDragClick = 1 << 0,
// Visual snapping of an in progress link will trigger IsLink Created/Destroyed events. Allows
// for previewing the creation of a link while dragging it across attributes. See here for demo:
// https://github.com/Nelarius/imnodes/issues/41#issuecomment-647132113 NOTE: the user has to
// actually delete the link for this to work. A deleted link can be detected by calling
// IsLinkDestroyed() after EndNodeEditor().
ImNodesAttributeFlags_EnableLinkCreationOnSnap = 1 << 1
};
struct ImNodesIO
{
struct EmulateThreeButtonMouse
{
EmulateThreeButtonMouse();
// The keyboard modifier to use in combination with mouse left click to pan the editor view.
// Set to NULL by default. To enable this feature, set the modifier to point to a boolean
// indicating the state of a modifier. For example,
//
// ImNodes::GetIO().EmulateThreeButtonMouse.Modifier = &ImGui::GetIO().KeyAlt;
const bool* Modifier;
} EmulateThreeButtonMouse;
struct LinkDetachWithModifierClick
{
LinkDetachWithModifierClick();
// Pointer to a boolean value indicating when the desired modifier is pressed. Set to NULL
// by default. To enable the feature, set the modifier to point to a boolean indicating the
// state of a modifier. For example,
//
// ImNodes::GetIO().LinkDetachWithModifierClick.Modifier = &ImGui::GetIO().KeyCtrl;
//
// Left-clicking a link with this modifier pressed will detach that link. NOTE: the user has
// to actually delete the link for this to work. A deleted link can be detected by calling
// IsLinkDestroyed() after EndNodeEditor().
const bool* Modifier;
} LinkDetachWithModifierClick;
struct MultipleSelectModifier
{
MultipleSelectModifier();
// Pointer to a boolean value indicating when the desired modifier is pressed. Set to NULL
// by default. To enable the feature, set the modifier to point to a boolean indicating the
// state of a modifier. For example,
//
// ImNodes::GetIO().MultipleSelectModifier.Modifier = &ImGui::GetIO().KeyCtrl;
//
// Left-clicking a node with this modifier pressed will add the node to the list of
// currently selected nodes. If this value is NULL, the Ctrl key will be used.
const bool* Modifier;
} MultipleSelectModifier;
// Holding alt mouse button pans the node area, by default middle mouse button will be used
// Set based on ImGuiMouseButton values
int AltMouseButton;
// Panning speed when dragging an element and mouse is outside the main editor view.
float AutoPanningSpeed;
ImNodesIO();
};
struct ImNodesStyle
{
float GridSpacing;
float NodeCornerRounding;
ImVec2 NodePadding;
float NodeBorderThickness;
float LinkThickness;
float LinkLineSegmentsPerLength;
float LinkHoverDistance;
// The following variables control the look and behavior of the pins. The default size of each
// pin shape is balanced to occupy approximately the same surface area on the screen.
// The circle radius used when the pin shape is either ImNodesPinShape_Circle or
// ImNodesPinShape_CircleFilled.
float PinCircleRadius;
// The quad side length used when the shape is either ImNodesPinShape_Quad or
// ImNodesPinShape_QuadFilled.
float PinQuadSideLength;
// The equilateral triangle side length used when the pin shape is either
// ImNodesPinShape_Triangle or ImNodesPinShape_TriangleFilled.
float PinTriangleSideLength;
// The thickness of the line used when the pin shape is not filled.
float PinLineThickness;
// The radius from the pin's center position inside of which it is detected as being hovered
// over.
float PinHoverRadius;
// Offsets the pins' positions from the edge of the node to the outside of the node.
float PinOffset;
// Mini-map padding size between mini-map edge and mini-map content.
ImVec2 MiniMapPadding;
// Mini-map offset from the screen side.
ImVec2 MiniMapOffset;
// By default, ImNodesStyleFlags_NodeOutline and ImNodesStyleFlags_Gridlines are enabled.
ImNodesStyleFlags Flags;
// Set these mid-frame using Push/PopColorStyle. You can index this color array with with a
// ImNodesCol value.
unsigned int Colors[ImNodesCol_COUNT];
ImNodesStyle();
};
enum ImNodesMiniMapLocation_
{
ImNodesMiniMapLocation_BottomLeft,
ImNodesMiniMapLocation_BottomRight,
ImNodesMiniMapLocation_TopLeft,
ImNodesMiniMapLocation_TopRight,
};
struct ImGuiContext;
struct ImVec2;
struct ImNodesContext;
// An editor context corresponds to a set of nodes in a single workspace (created with a single
// Begin/EndNodeEditor pair)
//
// By default, the library creates an editor context behind the scenes, so using any of the imnodes
// functions doesn't require you to explicitly create a context.
struct ImNodesEditorContext;
// Callback type used to specify special behavior when hovering a node in the minimap
#ifndef ImNodesMiniMapNodeHoveringCallback
typedef void (*ImNodesMiniMapNodeHoveringCallback)(int, void*);
#endif
#ifndef ImNodesMiniMapNodeHoveringCallbackUserData
typedef void* ImNodesMiniMapNodeHoveringCallbackUserData;
#endif
namespace IMNODES_NAMESPACE
{
// Call this function if you are compiling imnodes in to a dll, separate from ImGui. Calling this
// function sets the GImGui global variable, which is not shared across dll boundaries.
void SetImGuiContext(ImGuiContext* ctx);
ImNodesContext* CreateContext();
void DestroyContext(ImNodesContext* ctx = NULL); // NULL = destroy current context
ImNodesContext* GetCurrentContext();
void SetCurrentContext(ImNodesContext* ctx);
ImNodesEditorContext* EditorContextCreate();
void EditorContextFree(ImNodesEditorContext*);
void EditorContextSet(ImNodesEditorContext*);
ImVec2 EditorContextGetPanning();
void EditorContextResetPanning(const ImVec2& pos);
void EditorContextMoveToNode(const int node_id);
ImNodesIO& GetIO();
// Returns the global style struct. See the struct declaration for default values.
ImNodesStyle& GetStyle();
// Style presets matching the dear imgui styles of the same name. If dest is NULL, the active
// context's ImNodesStyle instance will be used as the destination.
void StyleColorsDark(ImNodesStyle* dest = NULL); // on by default
void StyleColorsClassic(ImNodesStyle* dest = NULL);
void StyleColorsLight(ImNodesStyle* dest = NULL);
// The top-level function call. Call this before calling BeginNode/EndNode. Calling this function
// will result the node editor grid workspace being rendered.
void BeginNodeEditor();
void EndNodeEditor();
// Add a navigable minimap to the editor; call before EndNodeEditor after all
// nodes and links have been specified
void MiniMap(
const float minimap_size_fraction = 0.2f,
const ImNodesMiniMapLocation location = ImNodesMiniMapLocation_TopLeft,
const ImNodesMiniMapNodeHoveringCallback node_hovering_callback = NULL,
const ImNodesMiniMapNodeHoveringCallbackUserData node_hovering_callback_data = NULL);
// Use PushColorStyle and PopColorStyle to modify ImNodesStyle::Colors mid-frame.
void PushColorStyle(ImNodesCol item, unsigned int color);
void PopColorStyle();
void PushStyleVar(ImNodesStyleVar style_item, float value);
void PushStyleVar(ImNodesStyleVar style_item, const ImVec2& value);
void PopStyleVar(int count = 1);
// id can be any positive or negative integer, but INT_MIN is currently reserved for internal use.
void BeginNode(int id);
void EndNode();
ImVec2 GetNodeDimensions(int id);
// Place your node title bar content (such as the node title, using ImGui::Text) between the
// following function calls. These functions have to be called before adding any attributes, or the
// layout of the node will be incorrect.
void BeginNodeTitleBar();
void EndNodeTitleBar();
// Attributes are ImGui UI elements embedded within the node. Attributes can have pin shapes
// rendered next to them. Links are created between pins.
//
// The activity status of an attribute can be checked via the IsAttributeActive() and
// IsAnyAttributeActive() function calls. This is one easy way of checking for any changes made to
// an attribute's drag float UI, for instance.
//
// Each attribute id must be unique.
// Create an input attribute block. The pin is rendered on left side.
void BeginInputAttribute(int id, ImNodesPinShape shape = ImNodesPinShape_CircleFilled);
void EndInputAttribute();
// Create an output attribute block. The pin is rendered on the right side.
void BeginOutputAttribute(int id, ImNodesPinShape shape = ImNodesPinShape_CircleFilled);
void EndOutputAttribute();
// Create a static attribute block. A static attribute has no pin, and therefore can't be linked to
// anything. However, you can still use IsAttributeActive() and IsAnyAttributeActive() to check for
// attribute activity.
void BeginStaticAttribute(int id);
void EndStaticAttribute();
// Push a single AttributeFlags value. By default, only AttributeFlags_None is set.
void PushAttributeFlag(ImNodesAttributeFlags flag);
void PopAttributeFlag();
// Render a link between attributes.
// The attributes ids used here must match the ids used in Begin(Input|Output)Attribute function
// calls. The order of start_attr and end_attr doesn't make a difference for rendering the link.
void Link(int id, int start_attribute_id, int end_attribute_id);
// Enable or disable the ability to click and drag a specific node.
void SetNodeDraggable(int node_id, const bool draggable);
// The node's position can be expressed in three coordinate systems:
// * screen space coordinates, -- the origin is the upper left corner of the window.
// * editor space coordinates -- the origin is the upper left corner of the node editor window
// * grid space coordinates, -- the origin is the upper left corner of the node editor window,
// translated by the current editor panning vector (see EditorContextGetPanning() and
// EditorContextResetPanning())
// Use the following functions to get and set the node's coordinates in these coordinate systems.
void SetNodeScreenSpacePos(int node_id, const ImVec2& screen_space_pos);
void SetNodeEditorSpacePos(int node_id, const ImVec2& editor_space_pos);
void SetNodeGridSpacePos(int node_id, const ImVec2& grid_pos);
ImVec2 GetNodeScreenSpacePos(const int node_id);
ImVec2 GetNodeEditorSpacePos(const int node_id);
ImVec2 GetNodeGridSpacePos(const int node_id);
// If ImNodesStyleFlags_GridSnapping is enabled, snap the specified node's origin to the grid.
void SnapNodeToGrid(int node_id);
// Returns true if the current node editor canvas is being hovered over by the mouse, and is not
// blocked by any other windows.
bool IsEditorHovered();
// The following functions return true if a UI element is being hovered over by the mouse cursor.
// Assigns the id of the UI element being hovered over to the function argument. Use these functions
// after EndNodeEditor() has been called.
bool IsNodeHovered(int* node_id);
bool IsLinkHovered(int* link_id);
bool IsPinHovered(int* attribute_id);
// Use The following two functions to query the number of selected nodes or links in the current
// editor. Use after calling EndNodeEditor().
int NumSelectedNodes();
int NumSelectedLinks();
// Get the selected node/link ids. The pointer argument should point to an integer array with at
// least as many elements as the respective NumSelectedNodes/NumSelectedLinks function call
// returned.
void GetSelectedNodes(int* node_ids);
void GetSelectedLinks(int* link_ids);
// Clears the list of selected nodes/links. Useful if you want to delete a selected node or link.
void ClearNodeSelection();
void ClearLinkSelection();
// Use the following functions to add or remove individual nodes or links from the current editors
// selection. Note that all functions require the id to be an existing valid id for this editor.
// Select-functions has the precondition that the object is currently considered unselected.
// Clear-functions has the precondition that the object is currently considered selected.
// Preconditions listed above can be checked via IsNodeSelected/IsLinkSelected if not already
// known.
void SelectNode(int node_id);
void ClearNodeSelection(int node_id);
bool IsNodeSelected(int node_id);
void SelectLink(int link_id);
void ClearLinkSelection(int link_id);
bool IsLinkSelected(int link_id);
// Was the previous attribute active? This will continuously return true while the left mouse button
// is being pressed over the UI content of the attribute.
bool IsAttributeActive();
// Was any attribute active? If so, sets the active attribute id to the output function argument.
bool IsAnyAttributeActive(int* attribute_id = NULL);
// Use the following functions to query a change of state for an existing link, or new link. Call
// these after EndNodeEditor().
// Did the user start dragging a new link from a pin?
bool IsLinkStarted(int* started_at_attribute_id);
// Did the user drop the dragged link before attaching it to a pin?
// There are two different kinds of situations to consider when handling this event:
// 1) a link which is created at a pin and then dropped
// 2) an existing link which is detached from a pin and then dropped
// Use the including_detached_links flag to control whether this function triggers when the user
// detaches a link and drops it.
bool IsLinkDropped(int* started_at_attribute_id = NULL, bool including_detached_links = true);
// Did the user finish creating a new link?
bool IsLinkCreated(
int* started_at_attribute_id,
int* ended_at_attribute_id,
bool* created_from_snap = NULL);
bool IsLinkCreated(
int* started_at_node_id,
int* started_at_attribute_id,
int* ended_at_node_id,
int* ended_at_attribute_id,
bool* created_from_snap = NULL);
// Was an existing link detached from a pin by the user? The detached link's id is assigned to the
// output argument link_id.
bool IsLinkDestroyed(int* link_id);
// Use the following functions to write the editor context's state to a string, or directly to a
// file. The editor context is serialized in the INI file format.
const char* SaveCurrentEditorStateToIniString(size_t* data_size = NULL);
const char* SaveEditorStateToIniString(
const ImNodesEditorContext* editor,
size_t* data_size = NULL);
void LoadCurrentEditorStateFromIniString(const char* data, size_t data_size);
void LoadEditorStateFromIniString(ImNodesEditorContext* editor, const char* data, size_t data_size);
void SaveCurrentEditorStateToIniFile(const char* file_name);
void SaveEditorStateToIniFile(const ImNodesEditorContext* editor, const char* file_name);
void LoadCurrentEditorStateFromIniFile(const char* file_name);
void LoadEditorStateFromIniFile(ImNodesEditorContext* editor, const char* file_name);
} // namespace IMNODES_NAMESPACE

View file

@ -0,0 +1,500 @@
#pragma once
#define IMGUI_DEFINE_MATH_OPERATORS
#include <imgui.h>
#include <imgui_internal.h>
#include "imnodes.h"
#include <limits.h>
// the structure of this file:
//
// [SECTION] internal enums
// [SECTION] internal data structures
// [SECTION] global and editor context structs
// [SECTION] object pool implementation
struct ImNodesContext;
extern ImNodesContext* GImNodes;
// [SECTION] internal enums
typedef int ImNodesScope;
typedef int ImNodesAttributeType;
typedef int ImNodesUIState;
typedef int ImNodesClickInteractionType;
typedef int ImNodesLinkCreationType;
enum ImNodesScope_
{
ImNodesScope_None = 1,
ImNodesScope_Editor = 1 << 1,
ImNodesScope_Node = 1 << 2,
ImNodesScope_Attribute = 1 << 3
};
enum ImNodesAttributeType_
{
ImNodesAttributeType_None,
ImNodesAttributeType_Input,
ImNodesAttributeType_Output
};
enum ImNodesUIState_
{
ImNodesUIState_None = 0,
ImNodesUIState_LinkStarted = 1 << 0,
ImNodesUIState_LinkDropped = 1 << 1,
ImNodesUIState_LinkCreated = 1 << 2
};
enum ImNodesClickInteractionType_
{
ImNodesClickInteractionType_Node,
ImNodesClickInteractionType_Link,
ImNodesClickInteractionType_LinkCreation,
ImNodesClickInteractionType_Panning,
ImNodesClickInteractionType_BoxSelection,
ImNodesClickInteractionType_ImGuiItem,
ImNodesClickInteractionType_None
};
enum ImNodesLinkCreationType_
{
ImNodesLinkCreationType_Standard,
ImNodesLinkCreationType_FromDetach
};
// [SECTION] internal data structures
// The object T must have the following interface:
//
// struct T
// {
// T();
//
// int id;
// };
template<typename T>
struct ImObjectPool
{
ImVector<T> Pool;
ImVector<bool> InUse;
ImVector<int> FreeList;
ImGuiStorage IdMap;
ImObjectPool() : Pool(), InUse(), FreeList(), IdMap() {}
};
// Emulates std::optional<int> using the sentinel value `INVALID_INDEX`.
struct ImOptionalIndex
{
ImOptionalIndex() : _Index(INVALID_INDEX) {}
ImOptionalIndex(const int value) : _Index(value) {}
// Observers
inline bool HasValue() const { return _Index != INVALID_INDEX; }
inline int Value() const
{
IM_ASSERT(HasValue());
return _Index;
}
// Modifiers
inline ImOptionalIndex& operator=(const int value)
{
_Index = value;
return *this;
}
inline void Reset() { _Index = INVALID_INDEX; }
inline bool operator==(const ImOptionalIndex& rhs) const { return _Index == rhs._Index; }
inline bool operator==(const int rhs) const { return _Index == rhs; }
inline bool operator!=(const ImOptionalIndex& rhs) const { return _Index != rhs._Index; }
inline bool operator!=(const int rhs) const { return _Index != rhs; }
static const int INVALID_INDEX = -1;
private:
int _Index;
};
struct ImNodeData
{
int Id;
ImVec2 Origin; // The node origin is in editor space
ImRect TitleBarContentRect;
ImRect Rect;
struct
{
ImU32 Background, BackgroundHovered, BackgroundSelected, Outline, Titlebar, TitlebarHovered,
TitlebarSelected;
} ColorStyle;
struct
{
float CornerRounding;
ImVec2 Padding;
float BorderThickness;
} LayoutStyle;
ImVector<int> PinIndices;
bool Draggable;
ImNodeData(const int node_id)
: Id(node_id), Origin(0.0f, 0.0f), TitleBarContentRect(),
Rect(ImVec2(0.0f, 0.0f), ImVec2(0.0f, 0.0f)), ColorStyle(), LayoutStyle(), PinIndices(),
Draggable(true)
{
}
~ImNodeData() { Id = INT_MIN; }
};
struct ImPinData
{
int Id;
int ParentNodeIdx;
ImRect AttributeRect;
ImNodesAttributeType Type;
ImNodesPinShape Shape;
ImVec2 Pos; // screen-space coordinates
int Flags;
struct
{
ImU32 Background, Hovered;
} ColorStyle;
ImPinData(const int pin_id)
: Id(pin_id), ParentNodeIdx(), AttributeRect(), Type(ImNodesAttributeType_None),
Shape(ImNodesPinShape_CircleFilled), Pos(), Flags(ImNodesAttributeFlags_None),
ColorStyle()
{
}
};
struct ImLinkData
{
int Id;
int StartPinIdx, EndPinIdx;
struct
{
ImU32 Base, Hovered, Selected;
} ColorStyle;
ImLinkData(const int link_id) : Id(link_id), StartPinIdx(), EndPinIdx(), ColorStyle() {}
};
struct ImClickInteractionState
{
ImNodesClickInteractionType Type;
struct
{
int StartPinIdx;
ImOptionalIndex EndPinIdx;
ImNodesLinkCreationType Type;
} LinkCreation;
struct
{
ImRect Rect; // Coordinates in grid space
} BoxSelector;
ImClickInteractionState() : Type(ImNodesClickInteractionType_None) {}
};
struct ImNodesColElement
{
ImU32 Color;
ImNodesCol Item;
ImNodesColElement(const ImU32 c, const ImNodesCol s) : Color(c), Item(s) {}
};
struct ImNodesStyleVarElement
{
ImNodesStyleVar Item;
float FloatValue[2];
ImNodesStyleVarElement(const ImNodesStyleVar variable, const float value) : Item(variable)
{
FloatValue[0] = value;
}
ImNodesStyleVarElement(const ImNodesStyleVar variable, const ImVec2 value) : Item(variable)
{
FloatValue[0] = value.x;
FloatValue[1] = value.y;
}
};
// [SECTION] global and editor context structs
struct ImNodesEditorContext
{
ImObjectPool<ImNodeData> Nodes;
ImObjectPool<ImPinData> Pins;
ImObjectPool<ImLinkData> Links;
ImVector<int> NodeDepthOrder;
// ui related fields
ImVec2 Panning;
ImVec2 AutoPanningDelta;
// Minimum and maximum extents of all content in grid space. Valid after final
// ImNodes::EndNode() call.
ImRect GridContentBounds;
ImVector<int> SelectedNodeIndices;
ImVector<int> SelectedLinkIndices;
// Relative origins of selected nodes for snapping of dragged nodes
ImVector<ImVec2> SelectedNodeOffsets;
// Offset of the primary node origin relative to the mouse cursor.
ImVec2 PrimaryNodeOffset;
ImClickInteractionState ClickInteraction;
// Mini-map state set by MiniMap()
bool MiniMapEnabled;
ImNodesMiniMapLocation MiniMapLocation;
float MiniMapSizeFraction;
ImNodesMiniMapNodeHoveringCallback MiniMapNodeHoveringCallback;
ImNodesMiniMapNodeHoveringCallbackUserData MiniMapNodeHoveringCallbackUserData;
// Mini-map state set during EndNodeEditor() call
ImRect MiniMapRectScreenSpace;
ImRect MiniMapContentScreenSpace;
float MiniMapScaling;
ImNodesEditorContext()
: Nodes(), Pins(), Links(), Panning(0.f, 0.f), SelectedNodeIndices(), SelectedLinkIndices(),
SelectedNodeOffsets(), PrimaryNodeOffset(0.f, 0.f), ClickInteraction(),
MiniMapEnabled(false), MiniMapSizeFraction(0.0f), MiniMapNodeHoveringCallback(NULL),
MiniMapNodeHoveringCallbackUserData(NULL), MiniMapScaling(0.0f)
{
}
};
struct ImNodesContext
{
ImNodesEditorContext* DefaultEditorCtx;
ImNodesEditorContext* EditorCtx;
// Canvas draw list and helper state
ImDrawList* CanvasDrawList;
ImGuiStorage NodeIdxToSubmissionIdx;
ImVector<int> NodeIdxSubmissionOrder;
ImVector<int> NodeIndicesOverlappingWithMouse;
ImVector<int> OccludedPinIndices;
// Canvas extents
ImVec2 CanvasOriginScreenSpace;
ImRect CanvasRectScreenSpace;
// Debug helpers
ImNodesScope CurrentScope;
// Configuration state
ImNodesIO Io;
ImNodesStyle Style;
ImVector<ImNodesColElement> ColorModifierStack;
ImVector<ImNodesStyleVarElement> StyleModifierStack;
ImGuiTextBuffer TextBuffer;
int CurrentAttributeFlags;
ImVector<int> AttributeFlagStack;
// UI element state
int CurrentNodeIdx;
int CurrentPinIdx;
int CurrentAttributeId;
ImOptionalIndex HoveredNodeIdx;
ImOptionalIndex HoveredLinkIdx;
ImOptionalIndex HoveredPinIdx;
ImOptionalIndex DeletedLinkIdx;
ImOptionalIndex SnapLinkIdx;
// Event helper state
// TODO: this should be a part of a state machine, and not a member of the global struct.
// Unclear what parts of the code this relates to.
int ImNodesUIState;
int ActiveAttributeId;
bool ActiveAttribute;
// ImGui::IO cache
ImVec2 MousePos;
bool LeftMouseClicked;
bool LeftMouseReleased;
bool AltMouseClicked;
bool LeftMouseDragging;
bool AltMouseDragging;
float AltMouseScrollDelta;
bool MultipleSelectModifier;
};
namespace IMNODES_NAMESPACE
{
static inline ImNodesEditorContext& EditorContextGet()
{
// No editor context was set! Did you forget to call ImNodes::CreateContext()?
IM_ASSERT(GImNodes->EditorCtx != NULL);
return *GImNodes->EditorCtx;
}
// [SECTION] ObjectPool implementation
template<typename T>
static inline int ObjectPoolFind(const ImObjectPool<T>& objects, const int id)
{
const int index = objects.IdMap.GetInt(static_cast<ImGuiID>(id), -1);
return index;
}
template<typename T>
static inline void ObjectPoolUpdate(ImObjectPool<T>& objects)
{
for (int i = 0; i < objects.InUse.size(); ++i)
{
const int id = objects.Pool[i].Id;
if (!objects.InUse[i] && objects.IdMap.GetInt(id, -1) == i)
{
objects.IdMap.SetInt(id, -1);
objects.FreeList.push_back(i);
(objects.Pool.Data + i)->~T();
}
}
}
template<>
inline void ObjectPoolUpdate(ImObjectPool<ImNodeData>& nodes)
{
for (int i = 0; i < nodes.InUse.size(); ++i)
{
if (nodes.InUse[i])
{
nodes.Pool[i].PinIndices.clear();
}
else
{
const int id = nodes.Pool[i].Id;
if (nodes.IdMap.GetInt(id, -1) == i)
{
// Remove node idx form depth stack the first time we detect that this idx slot is
// unused
ImVector<int>& depth_stack = EditorContextGet().NodeDepthOrder;
const int* const elem = depth_stack.find(i);
IM_ASSERT(elem != depth_stack.end());
depth_stack.erase(elem);
nodes.IdMap.SetInt(id, -1);
nodes.FreeList.push_back(i);
(nodes.Pool.Data + i)->~ImNodeData();
}
}
}
}
template<typename T>
static inline void ObjectPoolReset(ImObjectPool<T>& objects)
{
if (!objects.InUse.empty())
{
memset(objects.InUse.Data, 0, objects.InUse.size_in_bytes());
}
}
template<typename T>
static inline int ObjectPoolFindOrCreateIndex(ImObjectPool<T>& objects, const int id)
{
int index = objects.IdMap.GetInt(static_cast<ImGuiID>(id), -1);
// Construct new object
if (index == -1)
{
if (objects.FreeList.empty())
{
index = objects.Pool.size();
IM_ASSERT(objects.Pool.size() == objects.InUse.size());
const int new_size = objects.Pool.size() + 1;
objects.Pool.resize(new_size);
objects.InUse.resize(new_size);
}
else
{
index = objects.FreeList.back();
objects.FreeList.pop_back();
}
IM_PLACEMENT_NEW(objects.Pool.Data + index) T(id);
objects.IdMap.SetInt(static_cast<ImGuiID>(id), index);
}
// Flag it as used
objects.InUse[index] = true;
return index;
}
template<>
inline int ObjectPoolFindOrCreateIndex(ImObjectPool<ImNodeData>& nodes, const int node_id)
{
int node_idx = nodes.IdMap.GetInt(static_cast<ImGuiID>(node_id), -1);
// Construct new node
if (node_idx == -1)
{
if (nodes.FreeList.empty())
{
node_idx = nodes.Pool.size();
IM_ASSERT(nodes.Pool.size() == nodes.InUse.size());
const int new_size = nodes.Pool.size() + 1;
nodes.Pool.resize(new_size);
nodes.InUse.resize(new_size);
}
else
{
node_idx = nodes.FreeList.back();
nodes.FreeList.pop_back();
}
IM_PLACEMENT_NEW(nodes.Pool.Data + node_idx) ImNodeData(node_id);
nodes.IdMap.SetInt(static_cast<ImGuiID>(node_id), node_idx);
ImNodesEditorContext& editor = EditorContextGet();
editor.NodeDepthOrder.push_back(node_idx);
}
// Flag node as used
nodes.InUse[node_idx] = true;
return node_idx;
}
template<typename T>
static inline T& ObjectPoolFindOrCreateObject(ImObjectPool<T>& objects, const int id)
{
const int index = ObjectPoolFindOrCreateIndex(objects, id);
return objects.Pool[index];
}
} // namespace IMNODES_NAMESPACE

View file

@ -32,6 +32,10 @@
#include <time.h>
#include <fenv.h>
#include <math.h>
#include <script.h>
#include <jsffi.h>
#include <sokol/sokol_time.h>
#if defined(__APPLE__)
#include <malloc/malloc.h>
#elif defined(__linux__) || defined(__GLIBC__)
@ -78,15 +82,13 @@
#endif
static FILE *dumpout = NULL;
static FILE *cycleout = NULL;
void quickjs_set_dumpout(FILE *f)
{
dumpout = f;
}
void quickjs_set_dumpout(FILE *f) { dumpout = f; }
void quickjs_set_cycleout(FILE *f) { cycleout = f; }
/* dump object free */
//#define DUMP_FREE
//#define DUMP_CLOSURE
/* dump the bytecode of the compiled functions: combination of bits
1: dump pass 3 final byte code
2: dump pass 2 code
@ -112,6 +114,18 @@ void quickjs_set_dumpout(FILE *f)
//#define DUMP_PROMISE
//#define DUMP_READ_OBJECT
#ifdef DUMP
//#define DUMP_FREE
//#define DUMP_MEM
#define DUMP_GC
#define DUMP_LEAKS 1
//#define DUMP_OBJECTS
//#define DUMP_ATOMS
//#define DUMP_SHAPES
//#define DUMP_MODULE_RESOLVE
//#define DUMP_PROMISE
#endif
/* test the GC by forcing it before each object allocation */
//#define FORCE_GC_AT_MALLOC
@ -312,6 +326,25 @@ struct JSRuntime {
void *user_opaque;
};
#define SETRT(FIELD) JS_SetPropertyStr(js, v, #FIELD, JS_NewFloat64(js, rt->FIELD));
JSValue JS_GetRTInfo(JSRuntime *rt, JSContext *js)
{
JSValue v = JS_NewObject(js);
SETRT(atom_hash_size);
SETRT(atom_count);
SETRT(atom_size);
SETRT(class_count);
SETRT(stack_size);
SETRT(stack_top);
SETRT(stack_limit);
SETRT(malloc_gc_threshold);
SETRT(malloc_state.malloc_count);
SETRT(malloc_state.malloc_size);
SETRT(malloc_state.malloc_limit);
return v;
}
struct JSClass {
uint32_t class_id; /* 0 means free entry */
JSAtom class_name;
@ -1051,12 +1084,12 @@ static JSValue JS_EvalObject(JSContext *ctx, JSValueConst this_obj,
JSValue __attribute__((format(printf, 2, 3))) JS_ThrowInternalError(JSContext *ctx, const char *fmt, ...);
static __maybe_unused void JS_DumpAtoms(JSRuntime *rt);
static __maybe_unused void JS_DumpString(JSRuntime *rt, const JSString *p);
static __maybe_unused void JS_DumpObjectHeader(JSRuntime *rt);
static __maybe_unused void JS_DumpObject(JSRuntime *rt, JSObject *p);
static __maybe_unused void JS_DumpGCObject(JSRuntime *rt, JSGCObjectHeader *p);
static __maybe_unused void JS_DumpValueShort(JSRuntime *rt, JSValueConst val);
static __maybe_unused void JS_DumpValue(JSContext *ctx, JSValueConst val);
static __maybe_unused void JS_PrintValue(JSContext *ctx,
static __maybe_unused void JS_DumpObjectHeader(FILE *fp, JSRuntime *rt);
static __maybe_unused void JS_DumpObject(FILE *fp, JSRuntime *rt, JSObject *p);
static __maybe_unused void JS_DumpGCObject(FILE *fp, JSRuntime *rt, JSGCObjectHeader *p);
static __maybe_unused void JS_DumpValueShort(FILE *fp, JSRuntime *rt, JSValueConst val);
static __maybe_unused void JS_DumpValue(FILE *fp, JSContext *ctx, JSValueConst val);
static __maybe_unused void JS_PrintValue(FILE *fp,JSContext *ctx,
const char *str,
JSValueConst val);
static __maybe_unused void JS_DumpShapes(JSRuntime *rt);
@ -1292,6 +1325,27 @@ static const JSClassExoticMethods js_proxy_exotic_methods;
static const JSClassExoticMethods js_module_ns_exotic_methods;
static JSClassID js_class_id_alloc = JS_CLASS_INIT_COUNT;
////// CUSTOM DUMP FUNCTIONS
void JS_DumpMyValue(JSRuntime *rt, JSValue v)
{
uint32_t tag = JS_VALUE_GET_TAG(v);
if (tag == JS_TAG_OBJECT)
JS_DumpObject(dumpout, rt, JS_VALUE_GET_OBJ(v));
else
JS_DumpValueShort(dumpout, rt, v);
}
void JS_PrintShapes(JSRuntime *rt)
{
JS_DumpShapes(rt);
}
void JS_PrintAtoms(JSRuntime *rt)
{
JS_DumpAtoms(rt);
}
static void js_trigger_gc(JSRuntime *rt, size_t size)
{
BOOL force_gc;
@ -1303,7 +1357,7 @@ static void js_trigger_gc(JSRuntime *rt, size_t size)
#endif
if (force_gc) {
#ifdef DUMP_GC
fprintf(dumpout, dumpout, "GC: size=%" PRIu64 "\n",
fprintf(dumpout, "GC: size=%" PRIu64 "\n",
(uint64_t)rt->malloc_state.malloc_size);
#endif
JS_RunGC(rt);
@ -1987,11 +2041,11 @@ void JS_FreeRuntime(JSRuntime *rt)
p = list_entry(el, JSGCObjectHeader, link);
if (p->ref_count != 0) {
if (!header_done) {
fprintf(dumpout, dumpout, "Object leaks:\n");
JS_DumpObjectHeader(rt);
fprintf(cycleout, "Object leaks:\n");
JS_DumpObjectHeader(cycleout, rt);
header_done = TRUE;
}
JS_DumpGCObject(rt, p);
JS_DumpGCObject(cycleout, rt, p);
}
}
@ -2003,7 +2057,7 @@ void JS_FreeRuntime(JSRuntime *rt)
}
}
if (count != 0)
fprintf(dumpout, "Secondary object leaks: %d\n", count);
fprintf(cycleout, "Secondary object leaks: %d\n", count);
}
#endif
assert(list_empty(&rt->gc_obj_list));
@ -2322,7 +2376,7 @@ void JS_FreeContext(JSContext *ctx)
{
JSMemoryUsage stats;
JS_ComputeMemoryUsage(rt, &stats);
JS_DumpMemoryUsage(stdout, &stats, rt);
// JS_DumpMemoryUsage(stdout, &stats, rt);
}
#endif
@ -2521,6 +2575,7 @@ static __maybe_unused void JS_DumpChar(JSRuntime *rt, int c, int sep)
static __maybe_unused void JS_DumpString(JSRuntime *rt, const JSString *p)
{
return; // TODO: Reimplment this
int i, sep;
if (p == NULL) {
@ -5820,7 +5875,7 @@ static void gc_free_cycles(JSRuntime *rt)
{
struct list_head *el, *el1;
JSGCObjectHeader *p;
#ifdef DUMP_GC_FREE
#ifdef DUMP
BOOL header_done = FALSE;
#endif
@ -5838,13 +5893,13 @@ static void gc_free_cycles(JSRuntime *rt)
case JS_GC_OBJ_TYPE_JS_OBJECT:
case JS_GC_OBJ_TYPE_FUNCTION_BYTECODE:
case JS_GC_OBJ_TYPE_ASYNC_FUNCTION:
#ifdef DUMP_GC_FREE
#ifdef DUMP
if (!header_done) {
fprintf(dumpout, "Freeing cycles:\n");
JS_DumpObjectHeader(rt);
fprintf(cycleout, "Freeing cycles:\n");
JS_DumpObjectHeader(cycleout, rt);
header_done = TRUE;
}
JS_DumpGCObject(rt, p);
JS_DumpGCObject(cycleout, rt, p);
#endif
free_gc_object(rt, p);
break;
@ -5869,6 +5924,9 @@ static void gc_free_cycles(JSRuntime *rt)
void JS_RunGC(JSRuntime *rt)
{
double n = stm_now();
double size = rt->malloc_state.malloc_size;
/* decrement the reference of the children of each object. mark =
1 after this pass. */
gc_decref(rt);
@ -5878,6 +5936,8 @@ void JS_RunGC(JSRuntime *rt)
/* free the GC objects in a cycle */
gc_free_cycles(rt);
script_report_gc_time(stm_now()-n, size, rt->malloc_state.malloc_size);
}
/* Return false if not an object or if the object has already been
@ -5972,6 +6032,25 @@ static void compute_value_size(JSValueConst val, JSMemoryUsage_helper *hp)
}
}
double JS_MyValueSize(JSRuntime *rt, JSValue v)
{
JSMemoryUsage_helper mem = {0};
compute_value_size(v, &mem);
return mem.memory_used_count;
}
void JS_FillMemoryState(JSRuntime *rt, JSMemoryUsage *s)
{
s->malloc_count = rt->malloc_state.malloc_count;
s->malloc_size = rt->malloc_state.malloc_size;
s->malloc_limit = rt->malloc_state.malloc_limit;
s->gc_threshold = rt->malloc_gc_threshold;
s->memory_used_count = 2; /* rt + rt->class_array */
s->memory_used_size = sizeof(JSRuntime) + sizeof(JSValue) * rt->class_count;
}
void JS_ComputeMemoryUsage(JSRuntime *rt, JSMemoryUsage *s)
{
struct list_head *el, *el1;
@ -5983,6 +6062,8 @@ void JS_ComputeMemoryUsage(JSRuntime *rt, JSMemoryUsage *s)
s->malloc_size = rt->malloc_state.malloc_size;
s->malloc_limit = rt->malloc_state.malloc_limit;
s->gc_threshold = rt->malloc_gc_threshold;
s->memory_used_count = 2; /* rt + rt->class_array */
s->memory_used_size = sizeof(JSRuntime) + sizeof(JSValue) * rt->class_count;
@ -6262,7 +6343,7 @@ void JS_ComputeMemoryUsage(JSRuntime *rt, JSMemoryUsage *s)
void JS_DumpMemoryUsage(FILE *fp, const JSMemoryUsage *s, JSRuntime *rt)
{
fprintf(dumpout, fp, "QuickJS memory usage -- "
fprintf(fp, "QuickJS memory usage -- "
#ifdef CONFIG_BIGNUM
"BigNum "
#endif
@ -6288,14 +6369,14 @@ void JS_DumpMemoryUsage(FILE *fp, const JSMemoryUsage *s, JSRuntime *rt)
unsigned int size1 = js_malloc_usable_size_rt(rt, p);
if (size1 >= size) {
usage_size_ok = 1;
fprintf(dumpout, fp, " %3u + %-2u %s\n",
fprintf(fp, " %3u + %-2u %s\n",
size, size1 - size, object_types[i].name);
}
js_free_rt(rt, p);
}
}
if (!usage_size_ok) {
fprintf(dumpout, fp, " malloc_usable_size unavailable\n");
fprintf(fp, " malloc_usable_size unavailable\n");
}
{
int obj_classes[JS_CLASS_INIT_COUNT + 1] = { 0 };
@ -6309,82 +6390,82 @@ void JS_DumpMemoryUsage(FILE *fp, const JSMemoryUsage *s, JSRuntime *rt)
obj_classes[min_uint32(p->class_id, JS_CLASS_INIT_COUNT)]++;
}
}
fprintf(dumpout, fp, "\n" "JSObject classes\n");
fprintf(fp, "\n" "JSObject classes\n");
if (obj_classes[0])
fprintf(dumpout, fp, " %5d %2.0d %s\n", obj_classes[0], 0, "none");
fprintf(fp, " %5d %2.0d %s\n", obj_classes[0], 0, "none");
for (class_id = 1; class_id < JS_CLASS_INIT_COUNT; class_id++) {
if (obj_classes[class_id] && class_id < rt->class_count) {
char buf[ATOM_GET_STR_BUF_SIZE];
fprintf(dumpout, fp, " %5d %2.0d %s\n", obj_classes[class_id], class_id,
fprintf(fp, " %5d %2.0d %s\n", obj_classes[class_id], class_id,
JS_AtomGetStrRT(rt, buf, sizeof(buf), rt->class_array[class_id].class_name));
}
}
if (obj_classes[JS_CLASS_INIT_COUNT])
fprintf(dumpout, fp, " %5d %2.0d %s\n", obj_classes[JS_CLASS_INIT_COUNT], 0, "other");
fprintf(fp, " %5d %2.0d %s\n", obj_classes[JS_CLASS_INIT_COUNT], 0, "other");
}
fprintf(dumpout, fp, "\n");
fprintf(fp, "\n");
}
#endif
fprintf(dumpout, fp, "%-20s %8s %8s\n", "NAME", "COUNT", "SIZE");
fprintf(fp, "%-20s %8s %8s\n", "NAME", "COUNT", "SIZE");
if (s->malloc_count) {
fprintf(dumpout, fp, "%-20s %8"PRId64" %8"PRId64" (%0.1f per block)\n",
fprintf(fp, "%-20s %8"PRId64" %8"PRId64" (%0.1f per block)\n",
"memory allocated", s->malloc_count, s->malloc_size,
(double)s->malloc_size / s->malloc_count);
fprintf(dumpout, fp, "%-20s %8"PRId64" %8"PRId64" (%d overhead, %0.1f average slack)\n",
fprintf(fp, "%-20s %8"PRId64" %8"PRId64" (%d overhead, %0.1f average slack)\n",
"memory used", s->memory_used_count, s->memory_used_size,
MALLOC_OVERHEAD, ((double)(s->malloc_size - s->memory_used_size) /
s->memory_used_count));
}
if (s->atom_count) {
fprintf(dumpout, fp, "%-20s %8"PRId64" %8"PRId64" (%0.1f per atom)\n",
fprintf(fp, "%-20s %8"PRId64" %8"PRId64" (%0.1f per atom)\n",
"atoms", s->atom_count, s->atom_size,
(double)s->atom_size / s->atom_count);
}
if (s->str_count) {
fprintf(dumpout, fp, "%-20s %8"PRId64" %8"PRId64" (%0.1f per string)\n",
fprintf(fp, "%-20s %8"PRId64" %8"PRId64" (%0.1f per string)\n",
"strings", s->str_count, s->str_size,
(double)s->str_size / s->str_count);
}
if (s->obj_count) {
fprintf(dumpout, fp, "%-20s %8"PRId64" %8"PRId64" (%0.1f per object)\n",
fprintf(fp, "%-20s %8"PRId64" %8"PRId64" (%0.1f per object)\n",
"objects", s->obj_count, s->obj_size,
(double)s->obj_size / s->obj_count);
fprintf(dumpout, fp, "%-20s %8"PRId64" %8"PRId64" (%0.1f per object)\n",
fprintf(fp, "%-20s %8"PRId64" %8"PRId64" (%0.1f per object)\n",
" properties", s->prop_count, s->prop_size,
(double)s->prop_count / s->obj_count);
fprintf(dumpout, fp, "%-20s %8"PRId64" %8"PRId64" (%0.1f per shape)\n",
fprintf(fp, "%-20s %8"PRId64" %8"PRId64" (%0.1f per shape)\n",
" shapes", s->shape_count, s->shape_size,
(double)s->shape_size / s->shape_count);
}
if (s->js_func_count) {
fprintf(dumpout, fp, "%-20s %8"PRId64" %8"PRId64"\n",
fprintf(fp, "%-20s %8"PRId64" %8"PRId64"\n",
"bytecode functions", s->js_func_count, s->js_func_size);
fprintf(dumpout, fp, "%-20s %8"PRId64" %8"PRId64" (%0.1f per function)\n",
fprintf(fp, "%-20s %8"PRId64" %8"PRId64" (%0.1f per function)\n",
" bytecode", s->js_func_count, s->js_func_code_size,
(double)s->js_func_code_size / s->js_func_count);
if (s->js_func_pc2line_count) {
fprintf(dumpout, fp, "%-20s %8"PRId64" %8"PRId64" (%0.1f per function)\n",
fprintf(fp, "%-20s %8"PRId64" %8"PRId64" (%0.1f per function)\n",
" pc2line", s->js_func_pc2line_count,
s->js_func_pc2line_size,
(double)s->js_func_pc2line_size / s->js_func_pc2line_count);
}
}
if (s->c_func_count) {
fprintf(dumpout, fp, "%-20s %8"PRId64"\n", "C functions", s->c_func_count);
fprintf(fp, "%-20s %8"PRId64"\n", "C functions", s->c_func_count);
}
if (s->array_count) {
fprintf(dumpout, fp, "%-20s %8"PRId64"\n", "arrays", s->array_count);
fprintf(fp, "%-20s %8"PRId64"\n", "arrays", s->array_count);
if (s->fast_array_count) {
fprintf(dumpout, fp, "%-20s %8"PRId64"\n", " fast arrays", s->fast_array_count);
fprintf(dumpout, fp, "%-20s %8"PRId64" %8"PRId64" (%0.1f per fast array)\n",
fprintf(fp, "%-20s %8"PRId64"\n", " fast arrays", s->fast_array_count);
fprintf(fp, "%-20s %8"PRId64" %8"PRId64" (%0.1f per fast array)\n",
" elements", s->fast_array_elements,
s->fast_array_elements * (int)sizeof(JSValue),
(double)s->fast_array_elements / s->fast_array_count);
}
}
if (s->binary_object_count) {
fprintf(dumpout, fp, "%-20s %8"PRId64" %8"PRId64"\n",
fprintf(fp, "%-20s %8"PRId64" %8"PRId64"\n",
"binary objects", s->binary_object_count, s->binary_object_size);
}
}
@ -11857,14 +11938,14 @@ static JSValue JS_ToQuotedString(JSContext *ctx, JSValueConst val1)
return JS_EXCEPTION;
}
static __maybe_unused void JS_DumpObjectHeader(JSRuntime *rt)
static __maybe_unused void JS_DumpObjectHeader(FILE *fp, JSRuntime *rt)
{
fprintf(dumpout, "%14s %4s %4s %14s %10s %s\n",
fprintf(fp, "%14s %4s %4s %14s %10s %s\n",
"ADDRESS", "REFS", "SHRF", "PROTO", "CLASS", "PROPS");
}
/* for debug only: dump an object without side effect */
static __maybe_unused void JS_DumpObject(JSRuntime *rt, JSObject *p)
static __maybe_unused void JS_DumpObject(FILE *fp, JSRuntime *rt, JSObject *p)
{
uint32_t i;
char atom_buf[ATOM_GET_STR_BUF_SIZE];
@ -11875,28 +11956,28 @@ static __maybe_unused void JS_DumpObject(JSRuntime *rt, JSObject *p)
/* XXX: should encode atoms with special characters */
sh = p->shape; /* the shape can be NULL while freeing an object */
fprintf(dumpout, "%14p %4d ",
fprintf(fp, "%14p %4d ",
(void *)p,
p->header.ref_count);
if (sh) {
fprintf(dumpout, "%3d%c %14p ",
fprintf(fp, "%3d%c %14p ",
sh->header.ref_count,
" *"[sh->is_hashed],
(void *)sh->proto);
} else {
fprintf(dumpout, "%3s %14s ", "-", "-");
fprintf(fp, "%3s %14s ", "-", "-");
}
fprintf(dumpout, "%10s ",
fprintf(fp, "%10s ",
JS_AtomGetStrRT(rt, atom_buf, sizeof(atom_buf), rt->class_array[p->class_id].class_name));
if (p->is_exotic && p->fast_array) {
fprintf(dumpout, "[ ");
fprintf(fp, "[ ");
for(i = 0; i < p->u.array.count; i++) {
if (i != 0)
fprintf(dumpout, ", ");
fprintf(fp, ", ");
switch (p->class_id) {
case JS_CLASS_ARRAY:
case JS_CLASS_ARGUMENTS:
JS_DumpValueShort(rt, p->u.array.u.values[i]);
JS_DumpValueShort(fp, rt, p->u.array.u.values[i]);
break;
case JS_CLASS_UINT8C_ARRAY:
case JS_CLASS_INT8_ARRAY:
@ -11913,40 +11994,40 @@ static __maybe_unused void JS_DumpObject(JSRuntime *rt, JSObject *p)
int size = 1 << typed_array_size_log2(p->class_id);
const uint8_t *b = p->u.array.u.uint8_ptr + i * size;
while (size-- > 0)
fprintf(dumpout, "%02X", *b++);
fprintf(fp, "%02X", *b++);
}
break;
}
}
fprintf(dumpout, " ] ");
fprintf(fp, " ] ");
}
if (sh) {
fprintf(dumpout, "{ ");
fprintf(fp, "{ ");
for(i = 0, prs = get_shape_prop(sh); i < sh->prop_count; i++, prs++) {
if (prs->atom != JS_ATOM_NULL) {
pr = &p->prop[i];
if (!is_first)
fprintf(dumpout, ", ");
fprintf(dumpout, "%s: ",
fprintf(fp, ", ");
fprintf(fp, "%s: ",
JS_AtomGetStrRT(rt, atom_buf, sizeof(atom_buf), prs->atom));
if ((prs->flags & JS_PROP_TMASK) == JS_PROP_GETSET) {
fprintf(dumpout, "[getset %p %p]", (void *)pr->u.getset.getter,
fprintf(fp, "[getset %p %p]", (void *)pr->u.getset.getter,
(void *)pr->u.getset.setter);
} else if ((prs->flags & JS_PROP_TMASK) == JS_PROP_VARREF) {
fprintf(dumpout, "[varref %p]", (void *)pr->u.var_ref);
fprintf(fp, "[varref %p]", (void *)pr->u.var_ref);
} else if ((prs->flags & JS_PROP_TMASK) == JS_PROP_AUTOINIT) {
fprintf(dumpout, "[autoinit %p %d %p]",
fprintf(fp, "[autoinit %p %d %p]",
(void *)js_autoinit_get_realm(pr),
js_autoinit_get_id(pr),
(void *)pr->u.init.opaque);
} else {
JS_DumpValueShort(rt, pr->u.value);
JS_DumpValueShort(fp, rt, pr->u.value);
}
is_first = FALSE;
}
}
fprintf(dumpout, " }");
fprintf(fp, " }");
}
if (js_class_has_bytecode(p->class_id)) {
@ -11954,53 +12035,53 @@ static __maybe_unused void JS_DumpObject(JSRuntime *rt, JSObject *p)
JSVarRef **var_refs;
if (b->closure_var_count) {
var_refs = p->u.func.var_refs;
fprintf(dumpout, " Closure:");
fprintf(fp, " Closure:");
for(i = 0; i < b->closure_var_count; i++) {
fprintf(dumpout, " ");
JS_DumpValueShort(rt, var_refs[i]->value);
fprintf(fp, " ");
JS_DumpValueShort(fp, rt, var_refs[i]->value);
}
if (p->u.func.home_object) {
fprintf(dumpout, " HomeObject: ");
JS_DumpValueShort(rt, JS_MKPTR(JS_TAG_OBJECT, p->u.func.home_object));
fprintf(fp, " HomeObject: ");
JS_DumpValueShort(fp, rt, JS_MKPTR(JS_TAG_OBJECT, p->u.func.home_object));
}
}
}
fprintf(dumpout, "\n");
fprintf(fp, "\n");
}
static __maybe_unused void JS_DumpGCObject(JSRuntime *rt, JSGCObjectHeader *p)
static __maybe_unused void JS_DumpGCObject(FILE *fp, JSRuntime *rt, JSGCObjectHeader *p)
{
if (p->gc_obj_type == JS_GC_OBJ_TYPE_JS_OBJECT) {
JS_DumpObject(rt, (JSObject *)p);
JS_DumpObject(fp, rt, (JSObject *)p);
} else {
fprintf(dumpout, "%14p %4d ",
fprintf(fp, "%14p %4d ",
(void *)p,
p->ref_count);
switch(p->gc_obj_type) {
case JS_GC_OBJ_TYPE_FUNCTION_BYTECODE:
fprintf(dumpout, "[function bytecode]");
fprintf(fp, "[function bytecode]");
break;
case JS_GC_OBJ_TYPE_SHAPE:
fprintf(dumpout, "[shape]");
fprintf(fp, "[shape]");
break;
case JS_GC_OBJ_TYPE_VAR_REF:
fprintf(dumpout, "[var_ref]");
fprintf(fp, "[var_ref]");
break;
case JS_GC_OBJ_TYPE_ASYNC_FUNCTION:
fprintf(dumpout, "[async_function]");
fprintf(fp, "[async_function]");
break;
case JS_GC_OBJ_TYPE_JS_CONTEXT:
fprintf(dumpout, "[js_context]");
fprintf(fp, "[js_context]");
break;
default:
fprintf(dumpout, "[unknown %d]", p->gc_obj_type);
fprintf(fp, "[unknown %d]", p->gc_obj_type);
break;
}
fprintf(dumpout, "\n");
fprintf(fp, "\n");
}
}
static __maybe_unused void JS_DumpValueShort(JSRuntime *rt,
static __maybe_unused void JS_DumpValueShort(FILE *fp, JSRuntime *rt,
JSValueConst val)
{
uint32_t tag = JS_VALUE_GET_NORM_TAG(val);
@ -12008,7 +12089,7 @@ static __maybe_unused void JS_DumpValueShort(JSRuntime *rt,
switch(tag) {
case JS_TAG_INT:
fprintf(dumpout, "%d", JS_VALUE_GET_INT(val));
fprintf(fp, "%d", JS_VALUE_GET_INT(val));
break;
case JS_TAG_BOOL:
if (JS_VALUE_GET_BOOL(val))
@ -12028,10 +12109,10 @@ static __maybe_unused void JS_DumpValueShort(JSRuntime *rt,
case JS_TAG_UNDEFINED:
str = "undefined";
print_str:
fprintf(dumpout, "%s", str);
fprintf(fp, "%s", str);
break;
case JS_TAG_FLOAT64:
fprintf(dumpout, "%.14g", JS_VALUE_GET_FLOAT64(val));
fprintf(fp, "%.14g", JS_VALUE_GET_FLOAT64(val));
break;
case JS_TAG_BIG_INT:
{
@ -12039,7 +12120,7 @@ static __maybe_unused void JS_DumpValueShort(JSRuntime *rt,
char *str;
str = bf_ftoa(NULL, &p->num, 10, 0,
BF_RNDZ | BF_FTOA_FORMAT_FRAC);
fprintf(dumpout, "%sn", str);
fprintf(fp, "%sn", str);
bf_realloc(&rt->bf_ctx, str, 0);
}
break;
@ -12050,7 +12131,7 @@ static __maybe_unused void JS_DumpValueShort(JSRuntime *rt,
char *str;
str = bf_ftoa(NULL, &p->num, 16, BF_PREC_INF,
BF_RNDZ | BF_FTOA_FORMAT_FREE | BF_FTOA_ADD_PREFIX);
fprintf(dumpout, "%sl", str);
fprintf(fp, "%sl", str);
bf_free(&rt->bf_ctx, str);
}
break;
@ -12060,7 +12141,7 @@ static __maybe_unused void JS_DumpValueShort(JSRuntime *rt,
char *str;
str = bfdec_ftoa(NULL, &p->num, BF_PREC_INF,
BF_RNDZ | BF_FTOA_FORMAT_FREE);
fprintf(dumpout, "%sm", str);
fprintf(fp, "%sm", str);
bf_free(&rt->bf_ctx, str);
}
break;
@ -12076,7 +12157,7 @@ static __maybe_unused void JS_DumpValueShort(JSRuntime *rt,
{
JSFunctionBytecode *b = JS_VALUE_GET_PTR(val);
char buf[ATOM_GET_STR_BUF_SIZE];
fprintf(dumpout, "[bytecode %s]", JS_AtomGetStrRT(rt, buf, sizeof(buf), b->func_name));
fprintf(fp, "[bytecode %s]", JS_AtomGetStrRT(rt, buf, sizeof(buf), b->func_name));
}
break;
case JS_TAG_OBJECT:
@ -12084,7 +12165,7 @@ static __maybe_unused void JS_DumpValueShort(JSRuntime *rt,
JSObject *p = JS_VALUE_GET_OBJ(val);
JSAtom atom = rt->class_array[p->class_id].class_name;
char atom_buf[ATOM_GET_STR_BUF_SIZE];
fprintf(dumpout, "[%s %p]",
fprintf(fp, "[%s %p]",
JS_AtomGetStrRT(rt, atom_buf, sizeof(atom_buf), atom), (void *)p);
}
break;
@ -12092,32 +12173,32 @@ static __maybe_unused void JS_DumpValueShort(JSRuntime *rt,
{
JSAtomStruct *p = JS_VALUE_GET_PTR(val);
char atom_buf[ATOM_GET_STR_BUF_SIZE];
fprintf(dumpout, "Symbol(%s)",
fprintf(fp, "Symbol(%s)",
JS_AtomGetStrRT(rt, atom_buf, sizeof(atom_buf), js_get_atom_index(rt, p)));
}
break;
case JS_TAG_MODULE:
fprintf(dumpout, "[module]");
fprintf(fp, "[module]");
break;
default:
fprintf(dumpout, "[unknown tag %d]", tag);
fprintf(fp, "[unknown tag %d]", tag);
break;
}
}
static __maybe_unused void JS_DumpValue(JSContext *ctx,
static __maybe_unused void JS_DumpValue(FILE *fp, JSContext *ctx,
JSValueConst val)
{
JS_DumpValueShort(ctx->rt, val);
JS_DumpValueShort(fp, ctx->rt, val);
}
static __maybe_unused void JS_PrintValue(JSContext *ctx,
static __maybe_unused void JS_PrintValue(FILE *fp, JSContext *ctx,
const char *str,
JSValueConst val)
{
fprintf(dumpout, "%s=", str);
JS_DumpValueShort(ctx->rt, val);
fprintf(dumpout, "\n");
fprintf(fp, "%s=", str);
JS_DumpValueShort(fp, ctx->rt, val);
fprintf(fp, "\n");
}
/* return -1 if exception (proxy case) or TRUE/FALSE */

View file

@ -92,7 +92,11 @@ typedef struct JSRefCountHeader {
} JSRefCountHeader;
void quickjs_set_dumpout(FILE *f);
void quickjs_set_cycleout(FILE *f);
void JS_SetInterruptRate(int count);
void JS_PrintShapes(JSRuntime *rt);
void JS_PrintAtoms(JSRuntime *rt);
#define JS_FLOAT64_NAN NAN
@ -339,6 +343,7 @@ JSRuntime *JS_NewRuntime(void);
void JS_SetRuntimeInfo(JSRuntime *rt, const char *info);
void JS_SetMemoryLimit(JSRuntime *rt, size_t limit);
void JS_SetGCThreshold(JSRuntime *rt, size_t gc_threshold);
JSValue JS_GetRTInfo(JSRuntime *rt, JSContext *js);
/* use 0 to disable maximum stack size check */
void JS_SetMaxStackSize(JSRuntime *rt, size_t stack_size);
/* should be called when changing thread to update the stack top value
@ -404,6 +409,7 @@ char *js_strndup(JSContext *ctx, const char *s, size_t n);
typedef struct JSMemoryUsage {
int64_t malloc_size, malloc_limit, memory_used_size;
int64_t gc_threshold;
int64_t malloc_count;
int64_t memory_used_count;
int64_t atom_count, atom_size;
@ -419,7 +425,10 @@ typedef struct JSMemoryUsage {
} JSMemoryUsage;
void JS_ComputeMemoryUsage(JSRuntime *rt, JSMemoryUsage *s);
void JS_FillMemoryState(JSRuntime *rt, JSMemoryUsage *s);
void JS_DumpMemoryUsage(FILE *fp, const JSMemoryUsage *s, JSRuntime *rt);
void JS_DumpMyValue(JSRuntime *rt, JSValue v);
double JS_MyValueSize(JSRuntime *rt, JSValue v);
/* atom support */
#define JS_ATOM_NULL 0

File diff suppressed because it is too large Load diff

View file

@ -453,6 +453,7 @@
the standard logger in sokol_log.h instead, otherwise you won't see any warnings or
errors.
LICENSE
=======
@ -1067,6 +1068,7 @@ typedef struct {
/* sokol-audio state */
typedef struct {
bool valid;
bool setup_called;
void (*stream_cb)(float* buffer, int num_frames, int num_channels);
void (*stream_userdata_cb)(float* buffer, int num_frames, int num_channels, void* user_data);
void* user_data;
@ -2486,9 +2488,11 @@ void _saudio_backend_shutdown(void) {
// >>public
SOKOL_API_IMPL void saudio_setup(const saudio_desc* desc) {
SOKOL_ASSERT(!_saudio.valid);
SOKOL_ASSERT(!_saudio.setup_called);
SOKOL_ASSERT(desc);
SOKOL_ASSERT((desc->allocator.alloc_fn && desc->allocator.free_fn) || (!desc->allocator.alloc_fn && !desc->allocator.free_fn));
_saudio_clear(&_saudio, sizeof(_saudio));
_saudio.setup_called = true;
_saudio.desc = *desc;
_saudio.stream_cb = desc->stream_cb;
_saudio.stream_userdata_cb = desc->stream_userdata_cb;
@ -2519,6 +2523,8 @@ SOKOL_API_IMPL void saudio_setup(const saudio_desc* desc) {
}
SOKOL_API_IMPL void saudio_shutdown(void) {
SOKOL_ASSERT(_saudio.setup_called);
_saudio.setup_called = false;
if (_saudio.valid) {
_saudio_backend_shutdown();
_saudio_fifo_shutdown(&_saudio.fifo);
@ -2532,26 +2538,32 @@ SOKOL_API_IMPL bool saudio_isvalid(void) {
}
SOKOL_API_IMPL void* saudio_userdata(void) {
SOKOL_ASSERT(_saudio.setup_called);
return _saudio.desc.user_data;
}
SOKOL_API_IMPL saudio_desc saudio_query_desc(void) {
SOKOL_ASSERT(_saudio.setup_called);
return _saudio.desc;
}
SOKOL_API_IMPL int saudio_sample_rate(void) {
SOKOL_ASSERT(_saudio.setup_called);
return _saudio.sample_rate;
}
SOKOL_API_IMPL int saudio_buffer_frames(void) {
SOKOL_ASSERT(_saudio.setup_called);
return _saudio.buffer_frames;
}
SOKOL_API_IMPL int saudio_channels(void) {
SOKOL_ASSERT(_saudio.setup_called);
return _saudio.num_channels;
}
SOKOL_API_IMPL bool saudio_suspended(void) {
SOKOL_ASSERT(_saudio.setup_called);
#if defined(_SAUDIO_EMSCRIPTEN)
if (_saudio.valid) {
return 1 == saudio_js_suspended();
@ -2565,6 +2577,7 @@ SOKOL_API_IMPL bool saudio_suspended(void) {
}
SOKOL_API_IMPL int saudio_expect(void) {
SOKOL_ASSERT(_saudio.setup_called);
if (_saudio.valid) {
const int num_frames = _saudio_fifo_writable_bytes(&_saudio.fifo) / _saudio.bytes_per_frame;
return num_frames;
@ -2575,6 +2588,7 @@ SOKOL_API_IMPL int saudio_expect(void) {
}
SOKOL_API_IMPL int saudio_push(const float* frames, int num_frames) {
SOKOL_ASSERT(_saudio.setup_called);
SOKOL_ASSERT(frames && (num_frames > 0));
if (_saudio.valid) {
const int num_bytes = num_frames * _saudio.bytes_per_frame;

View file

@ -1201,6 +1201,11 @@ inline sfetch_handle_t sfetch_send(const sfetch_request_t& request) { return sfe
#define _SFETCH_HAS_THREADS (1)
#endif
#ifdef _MSC_VER
#pragma warning(push)
#pragma warning(disable:4724) // potential mod by 0
#endif
// ███████ ████████ ██████ ██ ██ ██████ ████████ ███████
// ██ ██ ██ ██ ██ ██ ██ ██ ██
// ███████ ██ ██████ ██ ██ ██ ██ ███████
@ -2806,4 +2811,9 @@ SOKOL_API_IMPL void sfetch_cancel(sfetch_handle_t h) {
item->user.cancel = true;
}
}
#ifdef _MSC_VER
#pragma warning(pop)
#endif
#endif /* SOKOL_FETCH_IMPL */

View file

@ -963,7 +963,7 @@
ON STORAGE BUFFERS
==================
Storage buffers can be used to pass large amounts of random access structured
data fromt the CPU side to the shaders. They are similar to data textures, but are
data from the CPU side to the shaders. They are similar to data textures, but are
more convenient to use both on the CPU and shader side since they can be accessed
in shaders as as a 1-dimensional array of struct items.
@ -1496,6 +1496,7 @@
.wgpu.num_bindgroup_cache_hits
.wgpu.num_bindgroup_cache_misses
.wgpu.num_bindgroup_cache_collisions
.wgpu_num_bindgroup_cache_invalidates
.wgpu.num_bindgroup_cache_vs_hash_key_mismatch
The value to pay attention to is `.wgpu.num_bindgroup_cache_collisions`,
@ -2097,13 +2098,10 @@ typedef enum sg_primitive_type {
used in the sg_sampler_desc.min_filter, sg_sampler_desc.mag_filter
and sg_sampler_desc.mipmap_filter members when creating a sampler object.
For min_filter and mag_filter the default is SG_FILTER_NEAREST.
For mipmap_filter the default is SG_FILTER_NONE.
For the default is SG_FILTER_NEAREST.
*/
typedef enum sg_filter {
_SG_FILTER_DEFAULT, // value 0 reserved for default-init
SG_FILTER_NONE, // FIXME: deprecated
SG_FILTER_NEAREST,
SG_FILTER_LINEAR,
_SG_FILTER_NUM,
@ -2892,7 +2890,7 @@ typedef struct sg_image_desc {
.min_filter: SG_FILTER_NEAREST
.mag_filter: SG_FILTER_NEAREST
.mipmap_filter SG_FILTER_NONE
.mipmap_filter SG_FILTER_NEAREST
.wrap_u: SG_WRAP_REPEAT
.wrap_v: SG_WRAP_REPEAT
.wrap_w: SG_WRAP_REPEAT (only SG_IMAGETYPE_3D)
@ -3482,6 +3480,7 @@ typedef struct sg_frame_stats_wgpu_bindings {
uint32_t num_bindgroup_cache_hits;
uint32_t num_bindgroup_cache_misses;
uint32_t num_bindgroup_cache_collisions;
uint32_t num_bindgroup_cache_invalidates;
uint32_t num_bindgroup_cache_hash_vs_key_mismatch;
} sg_frame_stats_wgpu_bindings;
@ -3592,7 +3591,7 @@ typedef struct sg_frame_stats {
_SG_LOGITEM_XMACRO(WGPU_ATTACHMENTS_CREATE_TEXTURE_VIEW_FAILED, "wgpuTextureCreateView() failed in create attachments") \
_SG_LOGITEM_XMACRO(IDENTICAL_COMMIT_LISTENER, "attempting to add identical commit listener") \
_SG_LOGITEM_XMACRO(COMMIT_LISTENER_ARRAY_FULL, "commit listener array full") \
_SG_LOGITEM_XMACRO(TRACE_HOOKS_NOT_ENABLED, "sg_install_trace_hooks() called, but SG_TRACE_HOOKS is not defined") \
_SG_LOGITEM_XMACRO(TRACE_HOOKS_NOT_ENABLED, "sg_install_trace_hooks() called, but SOKOL_TRACE_HOOKS is not defined") \
_SG_LOGITEM_XMACRO(DEALLOC_BUFFER_INVALID_STATE, "sg_dealloc_buffer(): buffer must be in ALLOC state") \
_SG_LOGITEM_XMACRO(DEALLOC_IMAGE_INVALID_STATE, "sg_dealloc_image(): image must be in alloc state") \
_SG_LOGITEM_XMACRO(DEALLOC_SAMPLER_INVALID_STATE, "sg_dealloc_sampler(): sampler must be in alloc state") \
@ -3650,8 +3649,6 @@ typedef struct sg_frame_stats {
_SG_LOGITEM_XMACRO(VALIDATE_IMAGEDESC_DYNAMIC_NO_DATA, "dynamic/stream images cannot be initialized with data") \
_SG_LOGITEM_XMACRO(VALIDATE_IMAGEDESC_COMPRESSED_IMMUTABLE, "compressed images must be immutable") \
_SG_LOGITEM_XMACRO(VALIDATE_SAMPLERDESC_CANARY, "sg_sampler_desc not initialized") \
_SG_LOGITEM_XMACRO(VALIDATE_SAMPLERDESC_MINFILTER_NONE, "sg_sampler_desc.min_filter cannot be SG_FILTER_NONE") \
_SG_LOGITEM_XMACRO(VALIDATE_SAMPLERDESC_MAGFILTER_NONE, "sg_sampler_desc.mag_filter cannot be SG_FILTER_NONE") \
_SG_LOGITEM_XMACRO(VALIDATE_SAMPLERDESC_ANISTROPIC_REQUIRES_LINEAR_FILTERING, "sg_sampler_desc.max_anisotropy > 1 requires min/mag/mipmap_filter to be SG_FILTER_LINEAR") \
_SG_LOGITEM_XMACRO(VALIDATE_SHADERDESC_CANARY, "sg_shader_desc not initialized") \
_SG_LOGITEM_XMACRO(VALIDATE_SHADERDESC_SOURCE, "shader source code required") \
@ -3887,6 +3884,12 @@ typedef enum sg_log_item {
before sg_setup() is called
.environment.d3d11.device_context
a pointer to the ID3D11DeviceContext object
.d3d11_shader_debugging
set this to true to compile shaders which are provided as HLSL source
code with debug information and without optimization, this allows
shader debugging in tools like RenderDoc, to output source code
instead of byte code from sokol-shdc, omit the `--binary` cmdline
option
WebGPU specific:
.wgpu_disable_bindgroups_cache
@ -3999,6 +4002,7 @@ typedef struct sg_desc {
int uniform_buffer_size;
int max_commit_listeners;
bool disable_validation; // disable validation layer even in debug mode, useful for tests
bool d3d11_shader_debugging; // if true, HLSL shaders are compiled with D3DCOMPILE_DEBUG | D3DCOMPILE_SKIP_OPTIMIZATION
bool mtl_force_managed_storage_mode; // for debugging: use Metal managed storage mode for resources even with UMA
bool mtl_use_command_buffer_with_retained_references; // Metal: use a managed MTLCommandBuffer which ref-counts used resources
bool wgpu_disable_bindgroups_cache; // set to true to disable the WebGPU backend BindGroup cache
@ -5783,10 +5787,20 @@ typedef struct {
uint32_t id;
} _sg_wgpu_bindgroup_handle_t;
#define _SG_WGPU_BINDGROUPSCACHE_NUM_ITEMS (1 + _SG_WGPU_MAX_BINDGROUP_ENTRIES)
typedef enum {
_SG_WGPU_BINDGROUPSCACHEITEMTYPE_NONE = 0,
_SG_WGPU_BINDGROUPSCACHEITEMTYPE_IMAGE = 0x1111111111111111,
_SG_WGPU_BINDGROUPSCACHEITEMTYPE_SAMPLER = 0x2222222222222222,
_SG_WGPU_BINDGROUPSCACHEITEMTYPE_STORAGEBUFFER = 0x3333333333333333,
_SG_WGPU_BINDGROUPSCACHEITEMTYPE_PIPELINE = 0x4444444444444444,
} _sg_wgpu_bindgroups_cache_item_type_t;
#define _SG_WGPU_BINDGROUPSCACHEKEY_NUM_ITEMS (1 + _SG_WGPU_MAX_BINDGROUP_ENTRIES)
typedef struct {
uint64_t hash;
uint32_t items[_SG_WGPU_BINDGROUPSCACHE_NUM_ITEMS];
// the format of cache key items is (_sg_wgpu_bindgroups_cache_item_type_t << 32) | handle.id,
// where the item type is a per-resource-type bit pattern
uint64_t items[_SG_WGPU_BINDGROUPSCACHEKEY_NUM_ITEMS];
} _sg_wgpu_bindgroups_cache_key_t;
typedef struct {
@ -7142,14 +7156,12 @@ _SOKOL_PRIVATE GLenum _sg_gl_blend_op(sg_blend_op op) {
_SOKOL_PRIVATE GLenum _sg_gl_min_filter(sg_filter min_f, sg_filter mipmap_f) {
if (min_f == SG_FILTER_NEAREST) {
switch (mipmap_f) {
case SG_FILTER_NONE: return GL_NEAREST;
case SG_FILTER_NEAREST: return GL_NEAREST_MIPMAP_NEAREST;
case SG_FILTER_LINEAR: return GL_NEAREST_MIPMAP_LINEAR;
default: SOKOL_UNREACHABLE; return (GLenum)0;
}
} else if (min_f == SG_FILTER_LINEAR) {
switch (mipmap_f) {
case SG_FILTER_NONE: return GL_LINEAR;
case SG_FILTER_NEAREST: return GL_LINEAR_MIPMAP_NEAREST;
case SG_FILTER_LINEAR: return GL_LINEAR_MIPMAP_LINEAR;
default: SOKOL_UNREACHABLE; return (GLenum)0;
@ -8299,40 +8311,57 @@ _SOKOL_PRIVATE sg_resource_state _sg_gl_create_image(_sg_image_t* img, const sg_
_sg_gl_cache_store_texture_sampler_binding(0);
_sg_gl_cache_bind_texture_sampler(0, img->gl.target, img->gl.tex[slot], 0);
glTexParameteri(img->gl.target, GL_TEXTURE_MAX_LEVEL, img->cmn.num_mipmaps - 1);
const int num_faces = img->cmn.type == SG_IMAGETYPE_CUBE ? 6 : 1;
int data_index = 0;
for (int face_index = 0; face_index < num_faces; face_index++) {
for (int mip_index = 0; mip_index < img->cmn.num_mipmaps; mip_index++, data_index++) {
GLenum gl_img_target = img->gl.target;
if (SG_IMAGETYPE_CUBE == img->cmn.type) {
gl_img_target = _sg_gl_cubeface_target(face_index);
}
const GLvoid* data_ptr = desc->data.subimage[face_index][mip_index].ptr;
const int mip_width = _sg_miplevel_dim(img->cmn.width, mip_index);
const int mip_height = _sg_miplevel_dim(img->cmn.height, mip_index);
// NOTE: workaround for https://issues.chromium.org/issues/355605685
// FIXME: on GLES3 and GL 4.3 (e.g. not macOS) the texture initialization
// should be rewritten to use glTexStorage + glTexSubImage
bool tex_storage_allocated = false;
#if defined(__EMSCRIPTEN__)
if (desc->data.subimage[0][0].ptr == 0) {
tex_storage_allocated = true;
if ((SG_IMAGETYPE_2D == img->cmn.type) || (SG_IMAGETYPE_CUBE == img->cmn.type)) {
if (is_compressed) {
const GLsizei data_size = (GLsizei) desc->data.subimage[face_index][mip_index].size;
glCompressedTexImage2D(gl_img_target, mip_index, gl_internal_format,
mip_width, mip_height, 0, data_size, data_ptr);
} else {
const GLenum gl_type = _sg_gl_teximage_type(img->cmn.pixel_format);
glTexImage2D(gl_img_target, mip_index, (GLint)gl_internal_format,
mip_width, mip_height, 0, gl_format, gl_type, data_ptr);
}
glTexStorage2D(img->gl.target, img->cmn.num_mipmaps, gl_internal_format, img->cmn.width, img->cmn.height);
} else if ((SG_IMAGETYPE_3D == img->cmn.type) || (SG_IMAGETYPE_ARRAY == img->cmn.type)) {
int mip_depth = img->cmn.num_slices;
if (SG_IMAGETYPE_3D == img->cmn.type) {
mip_depth = _sg_miplevel_dim(mip_depth, mip_index);
glTexStorage3D(img->gl.target, img->cmn.num_mipmaps, gl_internal_format, img->cmn.width, img->cmn.height, img->cmn.num_slices);
}
}
#endif
if (!tex_storage_allocated) {
const int num_faces = img->cmn.type == SG_IMAGETYPE_CUBE ? 6 : 1;
int data_index = 0;
for (int face_index = 0; face_index < num_faces; face_index++) {
for (int mip_index = 0; mip_index < img->cmn.num_mipmaps; mip_index++, data_index++) {
GLenum gl_img_target = img->gl.target;
if (SG_IMAGETYPE_CUBE == img->cmn.type) {
gl_img_target = _sg_gl_cubeface_target(face_index);
}
if (is_compressed) {
const GLsizei data_size = (GLsizei) desc->data.subimage[face_index][mip_index].size;
glCompressedTexImage3D(gl_img_target, mip_index, gl_internal_format,
mip_width, mip_height, mip_depth, 0, data_size, data_ptr);
} else {
const GLenum gl_type = _sg_gl_teximage_type(img->cmn.pixel_format);
glTexImage3D(gl_img_target, mip_index, (GLint)gl_internal_format,
mip_width, mip_height, mip_depth, 0, gl_format, gl_type, data_ptr);
const GLvoid* data_ptr = desc->data.subimage[face_index][mip_index].ptr;
const int mip_width = _sg_miplevel_dim(img->cmn.width, mip_index);
const int mip_height = _sg_miplevel_dim(img->cmn.height, mip_index);
if ((SG_IMAGETYPE_2D == img->cmn.type) || (SG_IMAGETYPE_CUBE == img->cmn.type)) {
if (is_compressed) {
const GLsizei data_size = (GLsizei) desc->data.subimage[face_index][mip_index].size;
glCompressedTexImage2D(gl_img_target, mip_index, gl_internal_format,
mip_width, mip_height, 0, data_size, data_ptr);
} else {
const GLenum gl_type = _sg_gl_teximage_type(img->cmn.pixel_format);
glTexImage2D(gl_img_target, mip_index, (GLint)gl_internal_format,
mip_width, mip_height, 0, gl_format, gl_type, data_ptr);
}
} else if ((SG_IMAGETYPE_3D == img->cmn.type) || (SG_IMAGETYPE_ARRAY == img->cmn.type)) {
int mip_depth = img->cmn.num_slices;
if (SG_IMAGETYPE_3D == img->cmn.type) {
mip_depth = _sg_miplevel_dim(mip_depth, mip_index);
}
if (is_compressed) {
const GLsizei data_size = (GLsizei) desc->data.subimage[face_index][mip_index].size;
glCompressedTexImage3D(gl_img_target, mip_index, gl_internal_format,
mip_width, mip_height, mip_depth, 0, data_size, data_ptr);
} else {
const GLenum gl_type = _sg_gl_teximage_type(img->cmn.pixel_format);
glTexImage3D(gl_img_target, mip_index, (GLint)gl_internal_format,
mip_width, mip_height, mip_depth, 0, gl_format, gl_type, data_ptr);
}
}
}
}
@ -10683,6 +10712,12 @@ _SOKOL_PRIVATE ID3DBlob* _sg_d3d11_compile_shader(const sg_shader_stage_desc* st
return NULL;
}
SOKOL_ASSERT(stage_desc->d3d11_target);
UINT flags1 = D3DCOMPILE_PACK_MATRIX_COLUMN_MAJOR;
if (_sg.desc.d3d11_shader_debugging) {
flags1 |= D3DCOMPILE_DEBUG | D3DCOMPILE_SKIP_OPTIMIZATION;
} else {
flags1 |= D3DCOMPILE_OPTIMIZATION_LEVEL3;
}
ID3DBlob* output = NULL;
ID3DBlob* errors_or_warnings = NULL;
HRESULT hr = _sg.d3d11.D3DCompile_func(
@ -10693,7 +10728,7 @@ _SOKOL_PRIVATE ID3DBlob* _sg_d3d11_compile_shader(const sg_shader_stage_desc* st
NULL, // pInclude
stage_desc->entry ? stage_desc->entry : "main", // pEntryPoint
stage_desc->d3d11_target, // pTarget
D3DCOMPILE_PACK_MATRIX_COLUMN_MAJOR | D3DCOMPILE_OPTIMIZATION_LEVEL3, // Flags1
flags1, // Flags1
0, // Flags2
&output, // ppCode
&errors_or_warnings); // ppErrorMsgs
@ -11437,12 +11472,15 @@ _SOKOL_PRIVATE void _sg_d3d11_append_buffer(_sg_buffer_t* buf, const sg_range* d
}
}
// see: https://learn.microsoft.com/en-us/windows/win32/direct3d11/overviews-direct3d-11-resources-subresources
// also see: https://learn.microsoft.com/en-us/windows/win32/api/d3d11/nf-d3d11-d3d11calcsubresource
_SOKOL_PRIVATE void _sg_d3d11_update_image(_sg_image_t* img, const sg_image_data* data) {
SOKOL_ASSERT(img && data);
SOKOL_ASSERT(_sg.d3d11.ctx);
SOKOL_ASSERT(img->d3d11.res);
const int num_faces = (img->cmn.type == SG_IMAGETYPE_CUBE) ? 6:1;
const int num_slices = (img->cmn.type == SG_IMAGETYPE_ARRAY) ? img->cmn.num_slices:1;
const int num_depth_slices = (img->cmn.type == SG_IMAGETYPE_3D) ? img->cmn.num_slices:1;
UINT subres_index = 0;
HRESULT hr;
D3D11_MAPPED_SUBRESOURCE d3d11_msr;
@ -11452,26 +11490,35 @@ _SOKOL_PRIVATE void _sg_d3d11_update_image(_sg_image_t* img, const sg_image_data
SOKOL_ASSERT(subres_index < (SG_MAX_MIPMAPS * SG_MAX_TEXTUREARRAY_LAYERS));
const int mip_width = _sg_miplevel_dim(img->cmn.width, mip_index);
const int mip_height = _sg_miplevel_dim(img->cmn.height, mip_index);
const int src_pitch = _sg_row_pitch(img->cmn.pixel_format, mip_width, 1);
const int src_row_pitch = _sg_row_pitch(img->cmn.pixel_format, mip_width, 1);
const int src_depth_pitch = _sg_surface_pitch(img->cmn.pixel_format, mip_width, mip_height, 1);
const sg_range* subimg_data = &(data->subimage[face_index][mip_index]);
const size_t slice_size = subimg_data->size / (size_t)num_slices;
SOKOL_ASSERT(slice_size == (size_t)(src_depth_pitch * num_depth_slices));
const size_t slice_offset = slice_size * (size_t)slice_index;
const uint8_t* slice_ptr = ((const uint8_t*)subimg_data->ptr) + slice_offset;
hr = _sg_d3d11_Map(_sg.d3d11.ctx, img->d3d11.res, subres_index, D3D11_MAP_WRITE_DISCARD, 0, &d3d11_msr);
_sg_stats_add(d3d11.num_map, 1);
if (SUCCEEDED(hr)) {
// FIXME: need to handle difference in depth-pitch for 3D textures as well!
if (src_pitch == (int)d3d11_msr.RowPitch) {
memcpy(d3d11_msr.pData, slice_ptr, slice_size);
} else {
SOKOL_ASSERT(src_pitch < (int)d3d11_msr.RowPitch);
const uint8_t* src_ptr = slice_ptr;
uint8_t* dst_ptr = (uint8_t*) d3d11_msr.pData;
for (int row_index = 0; row_index < mip_height; row_index++) {
memcpy(dst_ptr, src_ptr, (size_t)src_pitch);
src_ptr += src_pitch;
dst_ptr += d3d11_msr.RowPitch;
const uint8_t* src_ptr = slice_ptr;
uint8_t* dst_ptr = (uint8_t*)d3d11_msr.pData;
for (int depth_index = 0; depth_index < num_depth_slices; depth_index++) {
if (src_row_pitch == (int)d3d11_msr.RowPitch) {
const size_t copy_size = slice_size / (size_t)num_depth_slices;
SOKOL_ASSERT((copy_size * (size_t)num_depth_slices) == slice_size);
memcpy(dst_ptr, src_ptr, copy_size);
} else {
SOKOL_ASSERT(src_row_pitch < (int)d3d11_msr.RowPitch);
const uint8_t* src_row_ptr = src_ptr;
uint8_t* dst_row_ptr = dst_ptr;
for (int row_index = 0; row_index < mip_height; row_index++) {
memcpy(dst_row_ptr, src_row_ptr, (size_t)src_row_pitch);
src_row_ptr += src_row_pitch;
dst_row_ptr += d3d11_msr.RowPitch;
}
}
src_ptr += src_depth_pitch;
dst_ptr += d3d11_msr.DepthPitch;
}
_sg_d3d11_Unmap(_sg.d3d11.ctx, img->d3d11.res, subres_index);
_sg_stats_add(d3d11.num_unmap, 1);
@ -11853,8 +11900,6 @@ _SOKOL_PRIVATE MTLSamplerMinMagFilter _sg_mtl_minmag_filter(sg_filter f) {
_SOKOL_PRIVATE MTLSamplerMipFilter _sg_mtl_mipmap_filter(sg_filter f) {
switch (f) {
case SG_FILTER_NONE:
return MTLSamplerMipFilterNotMipmapped;
case SG_FILTER_NEAREST:
return MTLSamplerMipFilterNearest;
case SG_FILTER_LINEAR:
@ -13410,7 +13455,6 @@ _SOKOL_PRIVATE WGPUFilterMode _sg_wgpu_sampler_minmag_filter(sg_filter f) {
_SOKOL_PRIVATE WGPUMipmapFilterMode _sg_wgpu_sampler_mipmap_filter(sg_filter f) {
switch (f) {
case SG_FILTER_NONE:
case SG_FILTER_NEAREST:
return WGPUMipmapFilterMode_Nearest;
case SG_FILTER_LINEAR:
@ -14005,6 +14049,26 @@ _SOKOL_PRIVATE uint64_t _sg_wgpu_hash(const void* key, int len, uint64_t seed) {
return h;
}
_SOKOL_PRIVATE uint64_t _sg_wgpu_bindgroups_cache_item(_sg_wgpu_bindgroups_cache_item_type_t type, uint32_t id) {
return (((uint64_t)type) << 32) | id;
}
_SOKOL_PRIVATE uint64_t _sg_wgpu_bindgroups_cache_pip_item(uint32_t id) {
return _sg_wgpu_bindgroups_cache_item(_SG_WGPU_BINDGROUPSCACHEITEMTYPE_PIPELINE, id);
}
_SOKOL_PRIVATE uint64_t _sg_wgpu_bindgroups_cache_image_item(uint32_t id) {
return _sg_wgpu_bindgroups_cache_item(_SG_WGPU_BINDGROUPSCACHEITEMTYPE_IMAGE, id);
}
_SOKOL_PRIVATE uint64_t _sg_wgpu_bindgroups_cache_sampler_item(uint32_t id) {
return _sg_wgpu_bindgroups_cache_item(_SG_WGPU_BINDGROUPSCACHEITEMTYPE_SAMPLER, id);
}
_SOKOL_PRIVATE uint64_t _sg_wgpu_bindgroups_cache_sbuf_item(uint32_t id) {
return _sg_wgpu_bindgroups_cache_item(_SG_WGPU_BINDGROUPSCACHEITEMTYPE_STORAGEBUFFER, id);
}
_SOKOL_PRIVATE void _sg_wgpu_init_bindgroups_cache_key(_sg_wgpu_bindgroups_cache_key_t* key, const _sg_bindings_t* bnd) {
SOKOL_ASSERT(bnd);
SOKOL_ASSERT(bnd->pip);
@ -14016,38 +14080,39 @@ _SOKOL_PRIVATE void _sg_wgpu_init_bindgroups_cache_key(_sg_wgpu_bindgroups_cache
SOKOL_ASSERT(bnd->num_fs_sbufs <= SG_MAX_SHADERSTAGE_STORAGEBUFFERS);
_sg_clear(key->items, sizeof(key->items));
key->items[0] = bnd->pip->slot.id;
const int vs_imgs_offset = 1;
const int vs_smps_offset = vs_imgs_offset + SG_MAX_SHADERSTAGE_IMAGES;
const int vs_sbufs_offset = vs_smps_offset + SG_MAX_SHADERSTAGE_SAMPLERS;
const int fs_imgs_offset = vs_sbufs_offset + SG_MAX_SHADERSTAGE_STORAGEBUFFERS;
const int fs_smps_offset = fs_imgs_offset + SG_MAX_SHADERSTAGE_IMAGES;
const int fs_sbufs_offset = fs_smps_offset + SG_MAX_SHADERSTAGE_SAMPLERS;
SOKOL_ASSERT((fs_sbufs_offset + SG_MAX_SHADERSTAGE_STORAGEBUFFERS) == _SG_WGPU_BINDGROUPSCACHE_NUM_ITEMS);
int item_idx = 0;
key->items[item_idx++] = _sg_wgpu_bindgroups_cache_pip_item(bnd->pip->slot.id);
for (int i = 0; i < bnd->num_vs_imgs; i++) {
SOKOL_ASSERT(bnd->vs_imgs[i]);
key->items[vs_imgs_offset + i] = bnd->vs_imgs[i]->slot.id;
SOKOL_ASSERT(item_idx < _SG_WGPU_BINDGROUPSCACHEKEY_NUM_ITEMS);
key->items[item_idx++] = _sg_wgpu_bindgroups_cache_image_item(bnd->vs_imgs[i]->slot.id);
}
for (int i = 0; i < bnd->num_vs_smps; i++) {
SOKOL_ASSERT(bnd->vs_smps[i]);
key->items[vs_smps_offset + i] = bnd->vs_smps[i]->slot.id;
SOKOL_ASSERT(item_idx < _SG_WGPU_BINDGROUPSCACHEKEY_NUM_ITEMS);
key->items[item_idx++] = _sg_wgpu_bindgroups_cache_sampler_item(bnd->vs_smps[i]->slot.id);
}
for (int i = 0; i < bnd->num_vs_sbufs; i++) {
SOKOL_ASSERT(bnd->vs_sbufs[i]);
key->items[vs_sbufs_offset + i] = bnd->vs_sbufs[i]->slot.id;
SOKOL_ASSERT(item_idx < _SG_WGPU_BINDGROUPSCACHEKEY_NUM_ITEMS);
key->items[item_idx++] = _sg_wgpu_bindgroups_cache_sbuf_item(bnd->vs_sbufs[i]->slot.id);
}
for (int i = 0; i < bnd->num_fs_imgs; i++) {
SOKOL_ASSERT(bnd->fs_imgs[i]);
key->items[fs_imgs_offset + i] = bnd->fs_imgs[i]->slot.id;
SOKOL_ASSERT(item_idx < _SG_WGPU_BINDGROUPSCACHEKEY_NUM_ITEMS);
key->items[item_idx++] = _sg_wgpu_bindgroups_cache_image_item(bnd->fs_imgs[i]->slot.id);
}
for (int i = 0; i < bnd->num_fs_smps; i++) {
SOKOL_ASSERT(bnd->fs_smps[i]);
key->items[fs_smps_offset + i] = bnd->fs_smps[i]->slot.id;
SOKOL_ASSERT(item_idx < _SG_WGPU_BINDGROUPSCACHEKEY_NUM_ITEMS);
key->items[item_idx++] = _sg_wgpu_bindgroups_cache_sampler_item(bnd->fs_smps[i]->slot.id);
}
for (int i = 0; i < bnd->num_fs_sbufs; i++) {
SOKOL_ASSERT(bnd->fs_sbufs[i]);
key->items[fs_sbufs_offset + i] = bnd->fs_sbufs[i]->slot.id;
SOKOL_ASSERT(item_idx < _SG_WGPU_BINDGROUPSCACHEKEY_NUM_ITEMS);
key->items[item_idx++] = _sg_wgpu_bindgroups_cache_sbuf_item(bnd->fs_sbufs[i]->slot.id);
}
SOKOL_ASSERT(item_idx == (1 + bnd->num_vs_imgs + bnd->num_vs_smps + bnd->num_vs_sbufs + bnd->num_fs_imgs + bnd->num_fs_smps + bnd->num_fs_sbufs));
key->hash = _sg_wgpu_hash(&key->items, (int)sizeof(key->items), 0x1234567887654321);
}
@ -14199,6 +14264,33 @@ _SOKOL_PRIVATE uint32_t _sg_wgpu_bindgroups_cache_get(uint64_t hash) {
return _sg.wgpu.bindgroups_cache.items[index].id;
}
// called from wgpu resource destroy functions to also invalidate any
// bindgroups cache slot and bindgroup referencing that resource
_SOKOL_PRIVATE void _sg_wgpu_bindgroups_cache_invalidate(_sg_wgpu_bindgroups_cache_item_type_t type, uint32_t id) {
const uint64_t key_item = _sg_wgpu_bindgroups_cache_item(type, id);
SOKOL_ASSERT(_sg.wgpu.bindgroups_cache.items);
for (uint32_t cache_item_idx = 0; cache_item_idx < _sg.wgpu.bindgroups_cache.num; cache_item_idx++) {
const uint32_t bg_id = _sg.wgpu.bindgroups_cache.items[cache_item_idx].id;
if (bg_id != SG_INVALID_ID) {
_sg_wgpu_bindgroup_t* bg = _sg_wgpu_lookup_bindgroup(bg_id);
SOKOL_ASSERT(bg && (bg->slot.state == SG_RESOURCESTATE_VALID));
// check if resource is in bindgroup, if yes discard bindgroup and invalidate cache slot
bool invalidate_cache_item = false;
for (int key_item_idx = 0; key_item_idx < _SG_WGPU_BINDGROUPSCACHEKEY_NUM_ITEMS; key_item_idx++) {
if (bg->key.items[key_item_idx] == key_item) {
invalidate_cache_item = true;
break;
}
}
if (invalidate_cache_item) {
_sg_wgpu_discard_bindgroup(bg); bg = 0;
_sg_wgpu_bindgroups_cache_set(cache_item_idx, SG_INVALID_ID);
_sg_stats_add(wgpu.bindings.num_bindgroup_cache_invalidates, 1);
}
}
}
}
_SOKOL_PRIVATE void _sg_wgpu_bindings_cache_clear(void) {
memset(&_sg.wgpu.bindings_cache, 0, sizeof(_sg.wgpu.bindings_cache));
}
@ -14259,7 +14351,7 @@ _SOKOL_PRIVATE void _sg_wgpu_bindings_cache_bg_update(const _sg_wgpu_bindgroup_t
}
}
_SOKOL_PRIVATE void _sg_wgpu_set_bindings_bindgroup(_sg_wgpu_bindgroup_t* bg) {
_SOKOL_PRIVATE void _sg_wgpu_set_bindgroup(_sg_wgpu_bindgroup_t* bg) {
if (_sg_wgpu_bindings_cache_bg_dirty(bg)) {
_sg_wgpu_bindings_cache_bg_update(bg);
_sg_stats_add(wgpu.bindings.num_set_bindgroup, 1);
@ -14305,7 +14397,7 @@ _SOKOL_PRIVATE bool _sg_wgpu_apply_bindgroup(_sg_bindings_t* bnd) {
_sg_wgpu_bindgroups_cache_set(key.hash, bg->slot.id);
}
if (bg && bg->slot.state == SG_RESOURCESTATE_VALID) {
_sg_wgpu_set_bindings_bindgroup(bg);
_sg_wgpu_set_bindgroup(bg);
} else {
return false;
}
@ -14314,7 +14406,7 @@ _SOKOL_PRIVATE bool _sg_wgpu_apply_bindgroup(_sg_bindings_t* bnd) {
_sg_wgpu_bindgroup_t* bg = _sg_wgpu_create_bindgroup(bnd);
if (bg) {
if (bg->slot.state == SG_RESOURCESTATE_VALID) {
_sg_wgpu_set_bindings_bindgroup(bg);
_sg_wgpu_set_bindgroup(bg);
}
_sg_wgpu_discard_bindgroup(bg);
} else {
@ -14322,7 +14414,7 @@ _SOKOL_PRIVATE bool _sg_wgpu_apply_bindgroup(_sg_bindings_t* bnd) {
}
}
} else {
_sg_wgpu_set_bindings_bindgroup(0);
_sg_wgpu_set_bindgroup(0);
}
return true;
}
@ -14455,8 +14547,10 @@ _SOKOL_PRIVATE sg_resource_state _sg_wgpu_create_buffer(_sg_buffer_t* buf, const
_SOKOL_PRIVATE void _sg_wgpu_discard_buffer(_sg_buffer_t* buf) {
SOKOL_ASSERT(buf);
if (buf->cmn.type == SG_BUFFERTYPE_STORAGEBUFFER) {
_sg_wgpu_bindgroups_cache_invalidate(_SG_WGPU_BINDGROUPSCACHEITEMTYPE_STORAGEBUFFER, buf->slot.id);
}
if (buf->wgpu.buf) {
wgpuBufferDestroy(buf->wgpu.buf);
wgpuBufferRelease(buf->wgpu.buf);
}
}
@ -14592,12 +14686,12 @@ _SOKOL_PRIVATE sg_resource_state _sg_wgpu_create_image(_sg_image_t* img, const s
_SOKOL_PRIVATE void _sg_wgpu_discard_image(_sg_image_t* img) {
SOKOL_ASSERT(img);
_sg_wgpu_bindgroups_cache_invalidate(_SG_WGPU_BINDGROUPSCACHEITEMTYPE_IMAGE, img->slot.id);
if (img->wgpu.view) {
wgpuTextureViewRelease(img->wgpu.view);
img->wgpu.view = 0;
}
if (img->wgpu.tex) {
wgpuTextureDestroy(img->wgpu.tex);
wgpuTextureRelease(img->wgpu.tex);
img->wgpu.tex = 0;
}
@ -14638,6 +14732,7 @@ _SOKOL_PRIVATE sg_resource_state _sg_wgpu_create_sampler(_sg_sampler_t* smp, con
_SOKOL_PRIVATE void _sg_wgpu_discard_sampler(_sg_sampler_t* smp) {
SOKOL_ASSERT(smp);
_sg_wgpu_bindgroups_cache_invalidate(_SG_WGPU_BINDGROUPSCACHEITEMTYPE_SAMPLER, smp->slot.id);
if (smp->wgpu.smp) {
wgpuSamplerRelease(smp->wgpu.smp);
smp->wgpu.smp = 0;
@ -14875,6 +14970,7 @@ _SOKOL_PRIVATE sg_resource_state _sg_wgpu_create_pipeline(_sg_pipeline_t* pip, _
_SOKOL_PRIVATE void _sg_wgpu_discard_pipeline(_sg_pipeline_t* pip) {
SOKOL_ASSERT(pip);
_sg_wgpu_bindgroups_cache_invalidate(_SG_WGPU_BINDGROUPSCACHEITEMTYPE_PIPELINE, pip->slot.id);
if (pip == _sg.wgpu.cur_pipeline) {
_sg.wgpu.cur_pipeline = 0;
_sg.wgpu.cur_pipeline_id.id = SG_INVALID_ID;
@ -16219,8 +16315,6 @@ _SOKOL_PRIVATE bool _sg_validate_sampler_desc(const sg_sampler_desc* desc) {
_sg_validate_begin();
_SG_VALIDATE(desc->_start_canary == 0, VALIDATE_SAMPLERDESC_CANARY);
_SG_VALIDATE(desc->_end_canary == 0, VALIDATE_SAMPLERDESC_CANARY);
_SG_VALIDATE(desc->min_filter != SG_FILTER_NONE, VALIDATE_SAMPLERDESC_MINFILTER_NONE);
_SG_VALIDATE(desc->mag_filter != SG_FILTER_NONE, VALIDATE_SAMPLERDESC_MAGFILTER_NONE);
// restriction from WebGPU: when anisotropy > 1, all filters must be linear
if (desc->max_anisotropy > 1) {
_SG_VALIDATE((desc->min_filter == SG_FILTER_LINEAR)
@ -17040,7 +17134,7 @@ _SOKOL_PRIVATE sg_sampler_desc _sg_sampler_desc_defaults(const sg_sampler_desc*
sg_sampler_desc def = *desc;
def.min_filter = _sg_def(def.min_filter, SG_FILTER_NEAREST);
def.mag_filter = _sg_def(def.mag_filter, SG_FILTER_NEAREST);
def.mipmap_filter = _sg_def(def.mipmap_filter, SG_FILTER_NONE);
def.mipmap_filter = _sg_def(def.mipmap_filter, SG_FILTER_NEAREST);
def.wrap_u = _sg_def(def.wrap_u, SG_WRAP_REPEAT);
def.wrap_v = _sg_def(def.wrap_v, SG_WRAP_REPEAT);
def.wrap_w = _sg_def(def.wrap_w, SG_WRAP_REPEAT);

View file

@ -1661,7 +1661,6 @@ static int _sfons_render_create(void* user_ptr, int width, int height) {
_sfons_clear(&smp_desc, sizeof(smp_desc));
smp_desc.min_filter = SG_FILTER_LINEAR;
smp_desc.mag_filter = SG_FILTER_LINEAR;
smp_desc.mipmap_filter = SG_FILTER_NONE;
sfons->smp = sg_make_sampler(&smp_desc);
}

View file

@ -1287,7 +1287,6 @@ _SOKOL_PRIVATE const char* _sgimgui_pixelformat_string(sg_pixel_format fmt) {
_SOKOL_PRIVATE const char* _sgimgui_filter_string(sg_filter f) {
switch (f) {
case SG_FILTER_NONE: return "SG_FILTER_NONE";
case SG_FILTER_NEAREST: return "SG_FILTER_NEAREST";
case SG_FILTER_LINEAR: return "SG_FILTER_LINEAR";
default: return "???";
@ -4287,7 +4286,7 @@ _SOKOL_PRIVATE void _sgimgui_draw_frame_stats_panel(sgimgui_t* ctx) {
ImGuiTableFlags_SizingFixedFit |
ImGuiTableFlags_Borders;
if (igBeginTable("##frame_stats_table", 2, flags, IMVEC2(0, 0), 0)) {
igTableSetupScrollFreeze(0, 2);
igTableSetupScrollFreeze(0, 1);
igTableSetupColumn("key", ImGuiTableColumnFlags_None, 0, 0);
igTableSetupColumn("value", ImGuiTableColumnFlags_None, 0, 0);
igTableHeadersRow();
@ -4335,6 +4334,7 @@ _SOKOL_PRIVATE void _sgimgui_draw_frame_stats_panel(sgimgui_t* ctx) {
_sgimgui_frame_stats(wgpu.bindings.num_bindgroup_cache_hits);
_sgimgui_frame_stats(wgpu.bindings.num_bindgroup_cache_misses);
_sgimgui_frame_stats(wgpu.bindings.num_bindgroup_cache_collisions);
_sgimgui_frame_stats(wgpu.bindings.num_bindgroup_cache_invalidates);
_sgimgui_frame_stats(wgpu.bindings.num_bindgroup_cache_hash_vs_key_mismatch);
break;
case SG_BACKEND_METAL_MACOS:

View file

@ -372,8 +372,8 @@
the last call to sgl_draw() through sokol-gfx, and will 'rewind' the internal
vertex-, uniform- and command-buffers.
--- each sokol-gl context tracks an internal error code, to query the
current error code for the currently active context call:
--- each sokol-gl context tracks internal error states which can
be obtains via:
sgl_error_t sgl_error()
@ -381,18 +381,28 @@
sgl_error_t sgl_context_error(ctx);
...which can return the following error codes:
...this returns a struct with the following booleans:
SGL_NO_ERROR - all OK, no error occurred since last sgl_draw()
SGL_ERROR_VERTICES_FULL - internal vertex buffer is full (checked in sgl_end())
SGL_ERROR_UNIFORMS_FULL - the internal uniforms buffer is full (checked in sgl_end())
SGL_ERROR_COMMANDS_FULL - the internal command buffer is full (checked in sgl_end())
SGL_ERROR_STACK_OVERFLOW - matrix- or pipeline-stack overflow
SGL_ERROR_STACK_UNDERFLOW - matrix- or pipeline-stack underflow
SGL_ERROR_NO_CONTEXT - the active context no longer exists
.any - true if any of the below errors is true
.vertices_full - internal vertex buffer is full (checked in sgl_end())
.uniforms_full - the internal uniforms buffer is full (checked in sgl_end())
.commands_full - the internal command buffer is full (checked in sgl_end())
.stack_overflow - matrix- or pipeline-stack overflow
.stack_underflow - matrix- or pipeline-stack underflow
.no_context - the active context no longer exists
...if sokol-gl is in an error-state, sgl_draw() will skip any rendering,
and reset the error code to SGL_NO_ERROR.
...depending on the above error state, sgl_draw() may skip rendering
completely, or only draw partial geometry
--- you can get the number of recorded vertices and draw commands in the current
frame and active sokol-gl context via:
int sgl_num_vertices()
int sgl_num_commands()
...this allows you to check whether the vertex or command pools are running
full before the overflow actually happens (in this case you could also
check the error booleans in the result of sgl_error()).
RENDER LAYERS
=============
@ -762,14 +772,14 @@ typedef struct sgl_context { uint32_t id; } sgl_context;
Errors are reset each frame after calling sgl_draw(),
get the last error code with sgl_error()
*/
typedef enum sgl_error_t {
SGL_NO_ERROR = 0,
SGL_ERROR_VERTICES_FULL,
SGL_ERROR_UNIFORMS_FULL,
SGL_ERROR_COMMANDS_FULL,
SGL_ERROR_STACK_OVERFLOW,
SGL_ERROR_STACK_UNDERFLOW,
SGL_ERROR_NO_CONTEXT,
typedef struct sgl_error_t {
bool any;
bool vertices_full;
bool uniforms_full;
bool commands_full;
bool stack_overflow;
bool stack_underflow;
bool no_context;
} sgl_error_t;
/*
@ -832,6 +842,10 @@ SOKOL_GL_API_DECL void sgl_set_context(sgl_context ctx);
SOKOL_GL_API_DECL sgl_context sgl_get_context(void);
SOKOL_GL_API_DECL sgl_context sgl_default_context(void);
/* get information about recorded vertices and commands in current context */
SOKOL_GL_API_DECL int sgl_num_vertices(void);
SOKOL_GL_API_DECL int sgl_num_commands(void);
/* draw recorded commands (call inside a sokol-gfx render pass) */
SOKOL_GL_API_DECL void sgl_draw(void);
SOKOL_GL_API_DECL void sgl_context_draw(sgl_context ctx);
@ -2342,7 +2356,7 @@ typedef struct {
/* state tracking */
int base_vertex;
int vtx_count; /* number of times vtx function has been called, used for non-triangle primitives */
int quad_vtx_count; /* number of times vtx function has been called, used for non-triangle primitives */
sgl_error_t error;
bool in_begin;
int layer_id;
@ -2903,10 +2917,24 @@ static void _sgl_destroy_context(sgl_context ctx_id) {
// ██ ██ ██ ███████ ██████
//
// >>misc
static sgl_error_t _sgl_error_defaults(void) {
sgl_error_t defaults = {0};
return defaults;
}
static int _sgl_num_vertices(_sgl_context_t* ctx) {
return ctx->vertices.next;
}
static int _sgl_num_commands(_sgl_context_t* ctx) {
return ctx->commands.next;
}
static void _sgl_begin(_sgl_context_t* ctx, _sgl_primitive_type_t mode) {
ctx->in_begin = true;
ctx->base_vertex = ctx->vertices.next;
ctx->vtx_count = 0;
ctx->quad_vtx_count = 0;
ctx->cur_prim_type = mode;
}
@ -2916,7 +2944,7 @@ static void _sgl_rewind(_sgl_context_t* ctx) {
ctx->uniforms.next = 0;
ctx->commands.next = 0;
ctx->base_vertex = 0;
ctx->error = SGL_NO_ERROR;
ctx->error = _sgl_error_defaults();
ctx->layer_id = 0;
ctx->matrix_dirty = true;
}
@ -2938,7 +2966,8 @@ static _sgl_vertex_t* _sgl_next_vertex(_sgl_context_t* ctx) {
if (ctx->vertices.next < ctx->vertices.cap) {
return &ctx->vertices.ptr[ctx->vertices.next++];
} else {
ctx->error = SGL_ERROR_VERTICES_FULL;
ctx->error.vertices_full = true;
ctx->error.any = true;
return 0;
}
}
@ -2947,7 +2976,8 @@ static _sgl_uniform_t* _sgl_next_uniform(_sgl_context_t* ctx) {
if (ctx->uniforms.next < ctx->uniforms.cap) {
return &ctx->uniforms.ptr[ctx->uniforms.next++];
} else {
ctx->error = SGL_ERROR_UNIFORMS_FULL;
ctx->error.uniforms_full = true;
ctx->error.any = true;
return 0;
}
}
@ -2964,7 +2994,8 @@ static _sgl_command_t* _sgl_next_command(_sgl_context_t* ctx) {
if (ctx->commands.next < ctx->commands.cap) {
return &ctx->commands.ptr[ctx->commands.next++];
} else {
ctx->error = SGL_ERROR_COMMANDS_FULL;
ctx->error.commands_full = true;
ctx->error.any = true;
return 0;
}
}
@ -2991,7 +3022,7 @@ static void _sgl_vtx(_sgl_context_t* ctx, float x, float y, float z, float u, fl
SOKOL_ASSERT(ctx->in_begin);
_sgl_vertex_t* vtx;
/* handle non-native primitive types */
if ((ctx->cur_prim_type == SGL_PRIMITIVETYPE_QUADS) && ((ctx->vtx_count & 3) == 3)) {
if ((ctx->cur_prim_type == SGL_PRIMITIVETYPE_QUADS) && ((ctx->quad_vtx_count & 3) == 3)) {
/* for quads, before writing the last quad vertex, reuse
the first and third vertex to start the second triangle in the quad
*/
@ -3007,7 +3038,7 @@ static void _sgl_vtx(_sgl_context_t* ctx, float x, float y, float z, float u, fl
vtx->rgba = rgba;
vtx->psize = ctx->point_size;
}
ctx->vtx_count++;
ctx->quad_vtx_count++;
}
static void _sgl_identity(_sgl_matrix_t* m) {
@ -3342,7 +3373,7 @@ static bool _sgl_is_default_context(sgl_context ctx_id) {
static void _sgl_draw(_sgl_context_t* ctx, int layer_id) {
SOKOL_ASSERT(ctx);
if ((ctx->error == SGL_NO_ERROR) && (ctx->vertices.next > 0) && (ctx->commands.next > 0)) {
if ((ctx->vertices.next > 0) && (ctx->commands.next > 0)) {
sg_push_debug_group("sokol-gl");
uint32_t cur_pip_id = SG_INVALID_ID;
@ -3356,6 +3387,8 @@ static void _sgl_draw(_sgl_context_t* ctx, int layer_id) {
sg_update_buffer(ctx->vbuf, &range);
}
// render all successfully recorded commands (this may be less than the
// issued commands if we're in an error state)
for (int i = 0; i < ctx->commands.next; i++) {
const _sgl_command_t* cmd = &ctx->commands.ptr[i];
if (cmd->layer_id != layer_id) {
@ -3463,7 +3496,10 @@ SOKOL_API_IMPL sgl_error_t sgl_error(void) {
if (ctx) {
return ctx->error;
} else {
return SGL_ERROR_NO_CONTEXT;
sgl_error_t err = _sgl_error_defaults();
err.no_context = true;
err.any = true;
return err;
}
}
@ -3472,7 +3508,10 @@ SOKOL_API_IMPL sgl_error_t sgl_context_error(sgl_context ctx_id) {
if (ctx) {
return ctx->error;
} else {
return SGL_ERROR_NO_CONTEXT;
sgl_error_t err = _sgl_error_defaults();
err.no_context = true;
err.any = true;
return err;
}
}
@ -3521,6 +3560,26 @@ SOKOL_API_IMPL sgl_context sgl_default_context(void) {
return SGL_DEFAULT_CONTEXT;
}
SOKOL_API_IMPL int sgl_num_vertices(void) {
SOKOL_ASSERT(_SGL_INIT_COOKIE == _sgl.init_cookie);
_sgl_context_t* ctx = _sgl.cur_ctx;
if (ctx) {
return _sgl_num_vertices(ctx);
} else {
return 0;
}
}
SOKOL_API_IMPL int sgl_num_commands(void) {
SOKOL_ASSERT(_SGL_INIT_COOKIE == _sgl.init_cookie);
_sgl_context_t* ctx = _sgl.cur_ctx;
if (ctx) {
return _sgl_num_commands(ctx);
} else {
return 0;
}
}
SOKOL_API_IMPL sgl_pipeline sgl_make_pipeline(const sg_pipeline_desc* desc) {
SOKOL_ASSERT(_SGL_INIT_COOKIE == _sgl.init_cookie);
_sgl_context_t* ctx = _sgl.cur_ctx;
@ -3576,7 +3635,8 @@ SOKOL_API_IMPL void sgl_push_pipeline(void) {
ctx->pip_tos++;
ctx->pip_stack[ctx->pip_tos] = ctx->pip_stack[ctx->pip_tos-1];
} else {
ctx->error = SGL_ERROR_STACK_OVERFLOW;
ctx->error.stack_overflow = true;
ctx->error.any = true;
}
}
@ -3589,7 +3649,8 @@ SOKOL_API_IMPL void sgl_pop_pipeline(void) {
if (ctx->pip_tos > 0) {
ctx->pip_tos--;
} else {
ctx->error = SGL_ERROR_STACK_UNDERFLOW;
ctx->error.stack_underflow = true;
ctx->error.any = true;
}
}
@ -3778,6 +3839,7 @@ SOKOL_API_IMPL void sgl_end(void) {
SOKOL_ASSERT(ctx->in_begin);
SOKOL_ASSERT(ctx->vertices.next >= ctx->base_vertex);
ctx->in_begin = false;
bool matrix_dirty = ctx->matrix_dirty;
if (matrix_dirty) {
ctx->matrix_dirty = false;
@ -3787,6 +3849,12 @@ SOKOL_API_IMPL void sgl_end(void) {
uni->tm = *_sgl_matrix_texture(ctx);
}
}
// don't record any new commands when we're in an error state
if (ctx->error.any) {
return;
}
// check if command can be merged with current command
sg_pipeline pip = _sgl_get_pipeline(ctx->pip_stack[ctx->pip_tos], ctx->cur_prim_type);
sg_image img = ctx->texturing_enabled ? ctx->cur_img : _sgl.def_img;
@ -4205,7 +4273,8 @@ SOKOL_GL_API_DECL void sgl_push_matrix(void) {
_sgl_matrix_t* dst = _sgl_matrix(ctx);
*dst = *src;
} else {
ctx->error = SGL_ERROR_STACK_OVERFLOW;
ctx->error.stack_overflow = true;
ctx->error.any = true;
}
}
@ -4220,7 +4289,8 @@ SOKOL_GL_API_DECL void sgl_pop_matrix(void) {
if (ctx->matrix_tos[ctx->cur_matrix_mode] > 0) {
ctx->matrix_tos[ctx->cur_matrix_mode]--;
} else {
ctx->error = SGL_ERROR_STACK_UNDERFLOW;
ctx->error.stack_underflow = true;
ctx->error.any = true;
}
}

View file

@ -1844,13 +1844,13 @@ static const char* _simgui_fs_source_dummy = "";
#endif
#if !defined(SOKOL_IMGUI_NO_SOKOL_APP)
static void _simgui_set_clipboard(void* user_data, const char* text) {
(void)user_data;
static void _simgui_set_clipboard(ImGuiContext* ctx, const char* text) {
(void)ctx;
sapp_set_clipboard_string(text);
}
static const char* _simgui_get_clipboard(void* user_data) {
(void)user_data;
static const char* _simgui_get_clipboard(ImGuiContext* ctx) {
(void)ctx;
return sapp_get_clipboard_string();
}
#endif
@ -2200,6 +2200,7 @@ SOKOL_API_IMPL void simgui_setup(const simgui_desc_t* desc) {
ImGui::CreateContext();
ImGui::StyleColorsDark();
ImGuiIO* io = &ImGui::GetIO();
ImGuiPlatformIO* pio = &ImGui::GetPlatformIO();
if (!_simgui.desc.no_default_font) {
io->Fonts->AddFontDefault();
}
@ -2207,6 +2208,7 @@ SOKOL_API_IMPL void simgui_setup(const simgui_desc_t* desc) {
igCreateContext(NULL);
igStyleColorsDark(igGetStyle());
ImGuiIO* io = igGetIO();
ImGuiPlatformIO* pio = igGetPlatformIO();
if (!_simgui.desc.no_default_font) {
ImFontAtlas_AddFontDefault(io->Fonts, NULL);
}
@ -2218,8 +2220,8 @@ SOKOL_API_IMPL void simgui_setup(const simgui_desc_t* desc) {
if (!_simgui.desc.disable_set_mouse_cursor) {
io->BackendFlags |= ImGuiBackendFlags_HasMouseCursors;
}
io->SetClipboardTextFn = _simgui_set_clipboard;
io->GetClipboardTextFn = _simgui_get_clipboard;
pio->Platform_SetClipboardTextFn = _simgui_set_clipboard;
pio->Platform_GetClipboardTextFn = _simgui_get_clipboard;
#endif
io->ConfigWindowsResizeFromEdges = !_simgui.desc.disable_windows_resize_from_edges;
@ -2400,7 +2402,6 @@ SOKOL_API_IMPL void simgui_create_fonts_texture(const simgui_font_tex_desc_t* de
font_smp_desc.wrap_v = SG_WRAP_CLAMP_TO_EDGE;
font_smp_desc.min_filter = desc->min_filter;
font_smp_desc.mag_filter = desc->mag_filter;
font_smp_desc.mipmap_filter = SG_FILTER_NONE;
font_smp_desc.label = "sokol-imgui-font-sampler";
_simgui.font_smp = sg_make_sampler(&font_smp_desc);

View file

@ -236,7 +236,7 @@
.overrides = {
.min_filter = SG_FILTER_NEAREST,
.mag_filter = SG_FILTER_NEAREST,
.mipmap_filter = SG_FILTER_NONE,
.mipmap_filter = SG_FILTER_NEAREST,
.wrap_u = SG_WRAP_MIRROR,
.wrap_v = SG_WRAP_MIRROR,
.premul_alpha_enabled = ...,
@ -4573,15 +4573,19 @@ static sg_filter _sspine_as_sampler_filter(spAtlasFilter filter) {
static sg_filter _sspine_as_sampler_mipmap_filter(spAtlasFilter filter) {
switch (filter) {
case SP_ATLAS_UNKNOWN_FILTER: return _SG_FILTER_DEFAULT;
case SP_ATLAS_NEAREST: return SG_FILTER_NONE;
case SP_ATLAS_LINEAR: return SG_FILTER_NONE;
case SP_ATLAS_MIPMAP: return SG_FILTER_NEAREST;
case SP_ATLAS_MIPMAP_NEAREST_NEAREST: return SG_FILTER_NEAREST;
case SP_ATLAS_MIPMAP_LINEAR_NEAREST: return SG_FILTER_NEAREST;
case SP_ATLAS_MIPMAP_NEAREST_LINEAR: return SG_FILTER_LINEAR;
case SP_ATLAS_MIPMAP_LINEAR_LINEAR: return SG_FILTER_LINEAR;
default: return SG_FILTER_NEAREST;
case SP_ATLAS_UNKNOWN_FILTER:
return _SG_FILTER_DEFAULT;
case SP_ATLAS_NEAREST:
case SP_ATLAS_LINEAR:
case SP_ATLAS_MIPMAP:
case SP_ATLAS_MIPMAP_NEAREST_NEAREST:
case SP_ATLAS_MIPMAP_LINEAR_NEAREST:
return SG_FILTER_NEAREST;
case SP_ATLAS_MIPMAP_NEAREST_LINEAR:
case SP_ATLAS_MIPMAP_LINEAR_LINEAR:
return SG_FILTER_LINEAR;
default:
return SG_FILTER_NEAREST;
}
}

View file

@ -1,22 +1,31 @@
#include "timer.h"
#include <stdio.h>
#include "stb_ds.h"
timer *timers;
timer **timers;
timer *timer_make()
{
return NULL;
timer *t = calloc(sizeof(*t),1);
arrput(timers, t);
return t;
}
void timer_free(timer *t)
{
for (int i = 0; i < arrlen(timers); i++) {
if (timers[i] == t) {
arrdelswap(timers,i);
break;
}
}
free(t);
}
void timer_update(double dt)
{
for (int i = 0; i < arrlen(timers); i++) {
timers[i].remain -= dt;
timers[i]->remain -= dt;
}
}
}

View file

@ -5,9 +5,8 @@ typedef struct timer {
double remain;
} timer;
timer *timer_make();
void timer_free(timer *t);
void timer_update(double dt);
#endif
#endif

View file

@ -114,30 +114,37 @@ void c_event(const sapp_event *e)
char lcfmt[5];
switch (e->type) {
case SAPP_EVENTTYPE_MOUSE_MOVE:
if (gui_wantmouse()) return;
script_evalf("prosperon.mousemove([%g, %g], [%g, %g]);", e->mouse_x, e->mouse_y, e->mouse_dx, -e->mouse_dy);
break;
case SAPP_EVENTTYPE_MOUSE_SCROLL:
if (gui_wantmouse()) return;
script_evalf("prosperon.mousescroll([%g, %g]);", e->scroll_x, e->scroll_y);
break;
case SAPP_EVENTTYPE_KEY_DOWN:
if (gui_wantkeys()) return;
script_evalf("prosperon.keydown(%d, %d);", e->key_code, e->key_repeat);
break;
case SAPP_EVENTTYPE_KEY_UP:
if (gui_wantkeys()) return;
script_evalf("prosperon.keyup(%d);", e->key_code);
break;
case SAPP_EVENTTYPE_MOUSE_UP:
if (gui_wantmouse()) return;
script_evalf("prosperon.mouseup(%d);", e->mouse_button);
break;
case SAPP_EVENTTYPE_MOUSE_DOWN:
if (gui_wantmouse()) return;
script_evalf("prosperon.mousedown(%d);", e->mouse_button);
break;
case SAPP_EVENTTYPE_CHAR:
if (gui_wantkeys()) return;
if (iswcntrl(e->char_code)) break;
snprintf(lcfmt, 5, "%lc", e->char_code);
script_evalf("prosperon.textinput(`%s`);", lcfmt);
@ -176,9 +183,11 @@ void c_event(const sapp_event *e)
break;
case SAPP_EVENTTYPE_MOUSE_ENTER:
if (gui_wantmouse()) return;
script_evalf("prosperon.mouseenter();");
break;
case SAPP_EVENTTYPE_MOUSE_LEAVE:
if (gui_wantmouse()) return;
script_evalf("prosperon.mouseleave();");
break;
case SAPP_EVENTTYPE_TOUCHES_BEGAN: