improve sprite render speed; particle rendering
This commit is contained in:
@@ -12,21 +12,6 @@ function make_point_obj(o, p) {
|
||||
};
|
||||
};
|
||||
|
||||
function sprite_addbucket(sprite) {
|
||||
if (!sprite.image) return;
|
||||
var layer = sprite.z_value();
|
||||
sprite_buckets[layer] ??= {};
|
||||
sprite_buckets[layer][sprite.image.texture] ??= [];
|
||||
sprite_buckets[layer][sprite.image.texture].push(sprite);
|
||||
sprite._oldlayer = layer;
|
||||
sprite._oldtex = sprite.image.texture;
|
||||
};
|
||||
|
||||
function sprite_rmbucket(sprite) {
|
||||
if (sprite._oldlayer && sprite._oldtex) sprite_buckets[sprite._oldlayer][sprite._oldtex].remove(sprite);
|
||||
// else for (var layer of Object.values(sprite_buckets)) for (var path of Object.values(layer)) path.remove(sprite);
|
||||
};
|
||||
|
||||
/* an anim is simply an array of images */
|
||||
/* an anim set is like this
|
||||
frog = {
|
||||
@@ -40,16 +25,22 @@ var sprite = {
|
||||
image: undefined,
|
||||
get diffuse() { return this.image; },
|
||||
set diffuse(x) {},
|
||||
z_value() {return 100000 + this.gameobject.drawlayer * 1000 - this.gameobject.pos.y;},
|
||||
anim_speed: 1,
|
||||
play(str, loop = true, reverse = false, fn) {
|
||||
if (!this.animset) {
|
||||
// console.warn(`Sprite has no animset when trying to play ${str}`);
|
||||
return parseq.imm();
|
||||
fn?.();
|
||||
return;
|
||||
// return parseq.imm();
|
||||
}
|
||||
|
||||
if (typeof str === 'string')
|
||||
if (typeof str === 'string') {
|
||||
if (!this.animset[str]) {
|
||||
fn?.();
|
||||
return;
|
||||
}
|
||||
this.anim = this.animset[str];
|
||||
}
|
||||
|
||||
var playing = this.anim;
|
||||
|
||||
@@ -140,7 +131,6 @@ var sprite = {
|
||||
return this._p;
|
||||
},
|
||||
kill: function kill() {
|
||||
sprite_rmbucket(this);
|
||||
this.del_anim?.();
|
||||
this.anim = undefined;
|
||||
this.gameobject = undefined;
|
||||
@@ -148,11 +138,7 @@ var sprite = {
|
||||
},
|
||||
anchor: [0, 0],
|
||||
sync: function sync() {
|
||||
var layer = this.z_value();
|
||||
if (layer === this._oldlayer && this.image.texture === this._oldtex) return;
|
||||
|
||||
sprite_rmbucket(this);
|
||||
sprite_addbucket(this);
|
||||
this.layer = this.gameobject.drawlayer;
|
||||
},
|
||||
pick() {
|
||||
return this;
|
||||
@@ -249,7 +235,6 @@ component.sprite = function (obj) {
|
||||
sp.transform.parent = obj.transform;
|
||||
sp.guid = prosperon.guid();
|
||||
allsprites.push(sp);
|
||||
sprite_addbucket(sp);
|
||||
return sp;
|
||||
};
|
||||
|
||||
|
||||
@@ -74,6 +74,9 @@ emitter.step = function step(dt) {
|
||||
this.particles.remove(p);
|
||||
}
|
||||
}
|
||||
|
||||
for (var p of this.particles)
|
||||
p.transform.clean();
|
||||
};
|
||||
|
||||
emitter.burst = function (count, t) {
|
||||
@@ -84,8 +87,6 @@ var emitters = [];
|
||||
|
||||
var make_emitter = function () {
|
||||
var e = Object.create(emitter);
|
||||
// e.shape = shape.centered_quad;
|
||||
// e.shader = "shaders/baseparticle.cg";
|
||||
e.particles = [];
|
||||
e.dead = [];
|
||||
emitters.push(e);
|
||||
@@ -96,31 +97,6 @@ function update_emitters(dt) {
|
||||
for (var e of emitters) e.step(dt);
|
||||
}
|
||||
|
||||
var arr = [];
|
||||
function draw_emitters() {
|
||||
var buckets = {};
|
||||
var base = 0;
|
||||
for (var e of emitters) {
|
||||
var bucket = buckets[e.diffuse.path];
|
||||
if (!bucket)
|
||||
buckets[e.diffuse.path] = [e];
|
||||
else
|
||||
bucket.push(e);
|
||||
}
|
||||
|
||||
for (var path in buckets) {
|
||||
arr.length = 0;
|
||||
var bucket = buckets[path];
|
||||
for (var e of bucket) {
|
||||
if (e.particles.length === 0) continue;
|
||||
for (var p of e.particles) arr.push(p);
|
||||
}
|
||||
var sprite_mesh = os.make_sprite_mesh(arr);
|
||||
render.geometry(bucket[0], sprite_mesh);
|
||||
base += arr.length;
|
||||
}
|
||||
}
|
||||
|
||||
function stat_emitters()
|
||||
{
|
||||
var stat = {};
|
||||
@@ -131,4 +107,6 @@ function stat_emitters()
|
||||
return stat;
|
||||
}
|
||||
|
||||
return { make_emitter, update_emitters, draw_emitters, stat_emitters };
|
||||
function all_emitters() { return emitters; }
|
||||
|
||||
return { make_emitter, update_emitters, stat_emitters, all_emitters };
|
||||
|
||||
@@ -496,8 +496,9 @@ try{
|
||||
|
||||
var buffers = [];
|
||||
buffers = buffers.concat(queue_sprite_mesh(render_queue));
|
||||
for (var q of render_queue)
|
||||
if (q.type === 'geometry') buffers = buffers.concat([q.mesh.pos, q.mesh.color,q.mesh.uv,q.mesh.indices]);
|
||||
var unique_meshes = [...new Set(render_queue.map(x => x.mesh))];
|
||||
for (var q of unique_meshes)
|
||||
buffers = buffers.concat([q.pos, q.color,q.uv,q.indices]);
|
||||
|
||||
buffers = buffers.concat(queue_sprite_mesh(hud_queue));
|
||||
for (var q of hud_queue)
|
||||
@@ -743,25 +744,63 @@ render.draw_hud = true;
|
||||
render.draw_gui = true;
|
||||
render.draw_gizmos = true;
|
||||
|
||||
function insertion_sort(arr, cmp)
|
||||
{
|
||||
for (let i = 1; i < arr.length; i++) {
|
||||
let key = arr[i]
|
||||
let j = i - 1
|
||||
while (j >= 0 && cmp(arr[j], key) > 0)
|
||||
arr[j + 1] = arr[j--]
|
||||
arr[j + 1] = key
|
||||
}
|
||||
return arr
|
||||
}
|
||||
|
||||
function sprites_to_queue(sprites, ysort = false)
|
||||
{
|
||||
var sprites = allsprites;
|
||||
for (var i = 0; i < sprites.length; i++)
|
||||
sprites[i].transform.clean();
|
||||
// var sprites = os.cull_sprites(allsprites, prosperon.camera);
|
||||
|
||||
os.insertion_sort(sprites,render._main.sort_sprite)
|
||||
// sprites.sort(render._main.sort_sprite)
|
||||
|
||||
var mesh = render._main.make_sprite_mesh(sprites);
|
||||
|
||||
var queue = [];
|
||||
for (var l in sprites) {
|
||||
var layer = sprites[l]
|
||||
for (var image in layer) {
|
||||
var sparr = layer[image]
|
||||
if (sparr.length === 0) continue;
|
||||
var mesh = render._main.make_sprite_mesh(sparr);
|
||||
queue.push({
|
||||
type: 'geometry',
|
||||
var idx = 0;
|
||||
var image;
|
||||
var first_index = 0;
|
||||
var count = 0;
|
||||
|
||||
for (var i = 0; i < sprites.length; i++) {
|
||||
var spr = sprites[i];
|
||||
if (spr.image !== image) {
|
||||
if (count > 0) queue.push({
|
||||
type:'geometry',
|
||||
mesh,
|
||||
pipeline:sprite_pipeline,
|
||||
image:sparr[0].image,
|
||||
first_index:0,
|
||||
num_indices:mesh.num_indices
|
||||
image,
|
||||
first_index,
|
||||
num_indices:count*6
|
||||
});
|
||||
}
|
||||
|
||||
image = spr.image;
|
||||
first_index = i*6;
|
||||
count = 1;
|
||||
} else count++
|
||||
}
|
||||
|
||||
if (count > 0) queue.push({
|
||||
type:'geometry',
|
||||
mesh,
|
||||
pipeline:sprite_pipeline,
|
||||
image,
|
||||
first_index,
|
||||
num_indices: count*6
|
||||
});
|
||||
|
||||
return queue;
|
||||
}
|
||||
|
||||
@@ -853,6 +892,20 @@ render.rectangle = function render_rectangle(rect, color = Color.white, pipeline
|
||||
});
|
||||
};
|
||||
|
||||
render.particles = function render_particles(emitter, pipeline = sprite_pipeline)
|
||||
{
|
||||
var mesh = render._main.make_sprite_mesh(emitter.particles);
|
||||
if (mesh.num_indices === 0) return;
|
||||
current_queue.push({
|
||||
type:'geometry',
|
||||
mesh,
|
||||
image:emitter.diffuse,
|
||||
pipeline,
|
||||
first_index:0,
|
||||
num_indices:mesh.num_indices
|
||||
});
|
||||
}
|
||||
|
||||
render.text = function text(text, rect, font = prosperon.font, size = 0, color = Color.white, wrap = 0, pipeline = sprite_pipeline) {
|
||||
if (typeof font === 'string')
|
||||
font = render.get_font(font)
|
||||
@@ -1275,7 +1328,7 @@ try {
|
||||
} catch(e) { console.error(e); }
|
||||
}
|
||||
|
||||
var waittime = 1/60;
|
||||
var waittime = 1/240;
|
||||
var last_frame_time = 0;
|
||||
// Ran once per frame
|
||||
prosperon.process = function process() {
|
||||
@@ -1320,6 +1373,8 @@ try {
|
||||
|
||||
current_queue = render_queue;
|
||||
try { prosperon.draw(); } catch(e) { console.error(e) }
|
||||
for (var e of all_emitters())
|
||||
render.particles(e);
|
||||
current_queue = hud_queue;
|
||||
try { prosperon.hud(); } catch(e) { console.error(e) }
|
||||
try { imgui_fn(); } catch(e) { console.error(e) }
|
||||
|
||||
@@ -271,7 +271,7 @@ Cmdline.register_order(
|
||||
render._main = prosperon.window.make_gpu(false, driver);
|
||||
render._main.window = prosperon.window;
|
||||
render._main.claim_window(prosperon.window);
|
||||
render._main.set_swapchain("sdr", "immediate");
|
||||
render._main.set_swapchain("sdr", "mailbox");
|
||||
var tt = game.texture('moon');
|
||||
tt.texture.__proto__.toString = function() { return os.value_id(this); }
|
||||
|
||||
|
||||
391
source/jsffi.c
391
source/jsffi.c
@@ -58,6 +58,9 @@ static JSAtom src_atom;
|
||||
static JSAtom count_atom;
|
||||
static JSAtom num_indices_atom;
|
||||
static JSAtom transform_atom;
|
||||
static JSAtom image_atom;
|
||||
static JSAtom layer_atom;
|
||||
static JSAtom parent_atom;
|
||||
|
||||
// GPU ATOMS
|
||||
static JSAtom cw_atom;
|
||||
@@ -240,6 +243,11 @@ JSValue VALUE##__##PROP##__v = JS_GetPropertyStr(JS,VALUE,#PROP); \
|
||||
TARGET = js2##TYPE(JS, VALUE##__##PROP##__v); \
|
||||
JS_FreeValue(JS,VALUE##__##PROP##__v); }\
|
||||
|
||||
#define JS_GETATOM(JS, TARGET, VALUE, ATOM, TYPE) {\
|
||||
JSValue VALUE##__##PROP##__v = JS_GetProperty(JS,VALUE,ATOM); \
|
||||
TARGET = js2##TYPE(JS, VALUE##__##PROP##__v); \
|
||||
JS_FreeValue(JS,VALUE##__##PROP##__v); }\
|
||||
|
||||
int JS_GETBOOL(JSContext *js, JSValue v, const char *prop)
|
||||
{
|
||||
JSValue __v = JS_GetPropertyStr(js,v,prop);
|
||||
@@ -1142,16 +1150,40 @@ int js_arrlen(JSContext *js,JSValue v) {
|
||||
return len;
|
||||
}
|
||||
|
||||
static inline int js_transform_dirty_chain(JSContext *js, JSValue v)
|
||||
{
|
||||
transform *t = js2transform(js, v);
|
||||
if (!t) return 0; // no transform => assume not dirty
|
||||
|
||||
if (t->dirty) return 1;
|
||||
|
||||
JSValue parentVal = JS_GetProperty(js, v, parent_atom);
|
||||
if (JS_IsObject(parentVal)) {
|
||||
int r = js_transform_dirty_chain(js, parentVal);
|
||||
JS_FreeValue(js, parentVal);
|
||||
return r;
|
||||
}
|
||||
|
||||
JS_FreeValue(js, parentVal);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline HMM_Mat3 js2transform_mat3(JSContext *js, JSValue v)
|
||||
{
|
||||
transform *T = js2transform(js,v);
|
||||
transform *P = js2transform(js,js_getpropertystr(js,v,"parent"));
|
||||
if (!js_transform_dirty_chain(js,v)) return T->gcache3;
|
||||
if (!T) return HMM_M3D(1);
|
||||
transform *P;
|
||||
JS_GETATOM(js,P,v,parent_atom,transform);
|
||||
if (P) {
|
||||
HMM_Mat3 pm = transform2mat3(P);
|
||||
HMM_Mat3 tm = transform2mat3(T);
|
||||
return HMM_MulM3(pm,tm);
|
||||
}
|
||||
return transform2mat3(T);
|
||||
T->gcache3 = HMM_MulM3(pm,tm);
|
||||
|
||||
} else
|
||||
T->gcache3 = transform2mat3(T);
|
||||
|
||||
return T->gcache3;
|
||||
}
|
||||
|
||||
// Unpacks a typed array javascript object. If it has a gpu property, returns it, too. Otherwise, if requested, makes one.
|
||||
@@ -1218,6 +1250,7 @@ double js2angle(JSContext *js,JSValue v) {
|
||||
typedef HMM_Vec4 colorf;
|
||||
|
||||
colorf js2color(JSContext *js,JSValue v) {
|
||||
if (JS_IsUndefined(v)) return (colorf){1,1,1,1};
|
||||
JSValue c[4];
|
||||
for (int i = 0; i < 4; i++) c[i] = JS_GetPropertyUint32(js,v,i);
|
||||
float a = JS_IsUndefined(c[3]) ? 1.0 : js2number(js,c[3]);
|
||||
@@ -1407,6 +1440,7 @@ int js_print_exception(JSContext *js, JSValue v)
|
||||
}
|
||||
|
||||
rect js2rect(JSContext *js,JSValue v) {
|
||||
if (JS_IsUndefined(v)) return (rect){0,0,1,1};
|
||||
rect rect;
|
||||
rect.w = js_getnum(js,v,width_atom);
|
||||
rect.h = js_getnum(js,v,height_atom);
|
||||
@@ -3294,7 +3328,8 @@ JSC_CCALL(renderer_make_sprite_mesh,
|
||||
// Calculate the base index for the current quad
|
||||
size_t base = i * 4;
|
||||
|
||||
HMM_Mat3 trmat = js2transform_mat3(js,jstransform);
|
||||
|
||||
HMM_Mat3 trmat = tr->gcache3;
|
||||
|
||||
HMM_Vec3 base_quad[4] = {
|
||||
{0.0,0.0,1.0},
|
||||
@@ -3902,6 +3937,147 @@ static HMM_Vec3 base_quad[4] = {
|
||||
{1.0,1.0,1.0}
|
||||
};
|
||||
|
||||
static inline void add_quad(text_vert **verts, rect *restrict src, rect *restrict dst)
|
||||
{
|
||||
text_vert v = (text_vert){
|
||||
.pos = (HMM_Vec2){dst->x, dst->y},
|
||||
.uv = (HMM_Vec2){src->x,src->y},
|
||||
.color = (HMM_Vec4){1,1,1,1}
|
||||
};
|
||||
arrput(*verts, v);
|
||||
|
||||
v = (text_vert){
|
||||
.pos = (HMM_Vec2){dst->x+dst->w, dst->y},
|
||||
.uv = (HMM_Vec2){src->x+src->w,src->y},
|
||||
.color = (HMM_Vec4){1,1,1,1}
|
||||
};
|
||||
arrput(*verts, v);
|
||||
|
||||
v = (text_vert){
|
||||
.pos = (HMM_Vec2){dst->x, dst->y+dst->h},
|
||||
.uv = (HMM_Vec2){src->x,src->y+src->h},
|
||||
.color = (HMM_Vec4){1,1,1,1}
|
||||
};
|
||||
arrput(*verts, v);
|
||||
|
||||
v = (text_vert){
|
||||
.pos = (HMM_Vec2){dst->x+dst->w, dst->y+dst->h},
|
||||
.uv = (HMM_Vec2){src->x+src->w,src->y+src->h},
|
||||
.color = (HMM_Vec4){1,1,1,1}
|
||||
};
|
||||
arrput(*verts, v);
|
||||
}
|
||||
|
||||
typedef struct {
|
||||
text_vert vert[4];
|
||||
} quad;
|
||||
|
||||
typedef struct {
|
||||
quad quad;
|
||||
JSValue image;
|
||||
int layer;
|
||||
JSContext *js;
|
||||
JSValue sprite;
|
||||
} sprite;
|
||||
|
||||
int sort_sprite(const sprite *a, const sprite *b)
|
||||
{
|
||||
if (a->layer != b->layer) return a->layer - b->layer;
|
||||
|
||||
if (a->quad.vert[0].pos.y != b->quad.vert[0].pos.y) return b->quad.vert[0].pos.y - a->quad.vert[0].pos.y;
|
||||
|
||||
// if (!JS_SameValue(a->js, a->image, b->image)) return JS_VALUE_GET_PTR(a->image) < JS_VALUE_GET_PTR(b->image) ? -1 : 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
JSC_CCALL(gpu_sort_sprite,
|
||||
JSValue a = argv[0];
|
||||
JSValue b = argv[1];
|
||||
|
||||
int alayer, blayer;
|
||||
JS_GETATOM(js,alayer,a,layer_atom,number)
|
||||
JS_GETATOM(js,blayer,b,layer_atom,number)
|
||||
if (alayer != blayer) return number2js(js,alayer - blayer);
|
||||
|
||||
transform *atr, *btr;
|
||||
JS_GETATOM(js,atr,a,transform_atom,transform)
|
||||
JS_GETATOM(js,btr,b,transform_atom,transform);
|
||||
if (atr->gcache3.Columns[2].y != btr->gcache3.Columns[2].y) return number2js(js,btr->gcache3.Columns[2].y - atr->gcache3.Columns[2].y);
|
||||
|
||||
JSValue aimg,bimg;
|
||||
aimg = JS_GetProperty(js,a,image_atom);
|
||||
bimg = JS_GetProperty(js,b,image_atom);
|
||||
JS_FreeValue(js,aimg);
|
||||
JS_FreeValue(js,bimg);
|
||||
if (!JS_SameValue(js,aimg,bimg)) return number2js(js,JS_VALUE_GET_PTR(aimg) < JS_VALUE_GET_PTR(bimg) ? -1 : 1);
|
||||
return number2js(js,0);
|
||||
)
|
||||
|
||||
JSC_CCALL(gpu_make_sprite_queue,
|
||||
size_t quads = js_arrlen(js, argv[0]);
|
||||
|
||||
// Reserve an array of 'sprites' if needed, but watch for out-of-bounds
|
||||
sprite *sprites = NULL;
|
||||
arrsetlen(sprites, quads);
|
||||
|
||||
for (int i = 0; i < quads; i++) {
|
||||
JSValue sub = JS_GetPropertyUint32(js, argv[0], i);
|
||||
rect src;
|
||||
HMM_Vec4 color;
|
||||
|
||||
JS_GETATOM(js, src, sub, src_atom, rect)
|
||||
JS_GETATOM(js, color, sub, color_atom, color)
|
||||
|
||||
JSValue jstransform = JS_GetPropertyStr(js, sub, "transform");
|
||||
|
||||
quad sprite_quad;
|
||||
transform *t;
|
||||
JS_GETATOM(js,t,sub,transform_atom,transform)
|
||||
HMM_Mat3 trmat = t->gcache3;
|
||||
|
||||
// Transform the base_quad's 4 points into sprite_quad
|
||||
for (int j = 0; j < 4; j++)
|
||||
sprite_quad.vert[j].pos = HMM_MulM3V3(trmat, base_quad[j]).xy;
|
||||
|
||||
// Use [0..3] for uv and color
|
||||
sprite_quad.vert[0].uv = (HMM_Vec2){ src.x, src.y + src.h };
|
||||
sprite_quad.vert[1].uv = (HMM_Vec2){ src.x+src.w, src.y + src.h };
|
||||
sprite_quad.vert[2].uv = (HMM_Vec2){ src.x, src.y };
|
||||
sprite_quad.vert[3].uv = (HMM_Vec2){ src.x+src.w, src.y };
|
||||
|
||||
sprite_quad.vert[0].color = color;
|
||||
sprite_quad.vert[1].color = color;
|
||||
sprite_quad.vert[2].color = color;
|
||||
sprite_quad.vert[3].color = color;
|
||||
|
||||
// If you need to store into a bigger array:
|
||||
// sprites[i].quad = sprite_quad;
|
||||
// etc.
|
||||
sprite sp;
|
||||
sp.quad = sprite_quad;
|
||||
sp.image = JS_GetPropertyStr(js,sub,"image");
|
||||
sp.js = js;
|
||||
JS_GETPROP(js,sp.layer,sub,layer,number)
|
||||
sprites[i] = sp;
|
||||
sprites[i].sprite = JS_DupValue(js,sub);
|
||||
|
||||
JS_FreeValue(js, sub);
|
||||
JS_FreeValue(js, jstransform);
|
||||
JS_FreeValue(js, sp.image);
|
||||
}
|
||||
|
||||
qsort(sprites, arrlen(sprites),sizeof(sprite),sort_sprite);
|
||||
|
||||
|
||||
ret = JS_NewArray(js);
|
||||
|
||||
for (int i = 0; i < quads; i++)
|
||||
JS_SetPropertyUint32(js,ret,i,sprites[i].sprite);
|
||||
|
||||
arrfree(sprites);
|
||||
)
|
||||
|
||||
JSC_CCALL(gpu_make_sprite_mesh,
|
||||
size_t quads = js_arrlen(js, argv[0]);
|
||||
size_t verts = quads*4;
|
||||
@@ -3914,25 +4090,17 @@ JSC_CCALL(gpu_make_sprite_mesh,
|
||||
|
||||
for (int i = 0; i < quads; i++) {
|
||||
JSValue sub = JS_GetPropertyUint32(js,argv[0],i);
|
||||
JSValue jstransform = JS_GetProperty(js,sub,transform_atom);
|
||||
transform *tr = js2transform(js,jstransform);
|
||||
JSValue jssrc = JS_GetProperty(js,sub,src_atom);
|
||||
JSValue jscolor = JS_GetProperty(js,sub,color_atom);
|
||||
|
||||
transform *tr;
|
||||
rect src;
|
||||
if (JS_IsUndefined(jssrc))
|
||||
src = (rect){0,0,1,1};
|
||||
else
|
||||
src = js2rect(js,jssrc);
|
||||
|
||||
HMM_Vec4 color;
|
||||
if (JS_IsUndefined(jscolor))
|
||||
color = (HMM_Vec4){1,1,1,1};
|
||||
else
|
||||
color = js2vec4(js,jscolor);
|
||||
JS_GETATOM(js,src,sub,src_atom,rect)
|
||||
JS_GETATOM(js,color,sub,color_atom,color)
|
||||
JS_GETATOM(js,tr,sub,transform_atom,transform)
|
||||
JS_FreeValue(js,sub);
|
||||
|
||||
size_t base = i*4;
|
||||
HMM_Mat3 trmat = js2transform_mat3(js,jstransform);
|
||||
HMM_Mat3 trmat = tr->gcache3;
|
||||
// HMM_Mat3 trmat = transform2mat3(tr);
|
||||
for (int j = 0; j < 4; j++)
|
||||
posdata[base+j] = HMM_MulM3V3(trmat, base_quad[j]).xy;
|
||||
|
||||
@@ -3945,11 +4113,6 @@ JSC_CCALL(gpu_make_sprite_mesh,
|
||||
colordata[base+1] = color;
|
||||
colordata[base+2] = color;
|
||||
colordata[base+3] = color;
|
||||
|
||||
JS_FreeValue(js,jstransform);
|
||||
JS_FreeValue(js,sub);
|
||||
JS_FreeValue(js,jscolor);
|
||||
JS_FreeValue(js,jssrc);
|
||||
}
|
||||
|
||||
// Check old mesh
|
||||
@@ -4314,37 +4477,6 @@ JSC_CCALL(gpu_compute_pipeline,
|
||||
return SDL_GPUComputePipeline2js(js,pipeline);
|
||||
)
|
||||
|
||||
static inline void add_quad(text_vert **verts, rect *restrict src, rect *restrict dst)
|
||||
{
|
||||
text_vert v = (text_vert){
|
||||
.pos = (HMM_Vec2){dst->x, dst->y},
|
||||
.uv = (HMM_Vec2){src->x,src->y},
|
||||
.color = (HMM_Vec4){1,1,1,1}
|
||||
};
|
||||
arrput(*verts, v);
|
||||
|
||||
v = (text_vert){
|
||||
.pos = (HMM_Vec2){dst->x+dst->w, dst->y},
|
||||
.uv = (HMM_Vec2){src->x+src->w,src->y},
|
||||
.color = (HMM_Vec4){1,1,1,1}
|
||||
};
|
||||
arrput(*verts, v);
|
||||
|
||||
v = (text_vert){
|
||||
.pos = (HMM_Vec2){dst->x, dst->y+dst->h},
|
||||
.uv = (HMM_Vec2){src->x,src->y+src->h},
|
||||
.color = (HMM_Vec4){1,1,1,1}
|
||||
};
|
||||
arrput(*verts, v);
|
||||
|
||||
v = (text_vert){
|
||||
.pos = (HMM_Vec2){dst->x+dst->w, dst->y+dst->h},
|
||||
.uv = (HMM_Vec2){src->x+src->w,src->y+src->h},
|
||||
.color = (HMM_Vec4){1,1,1,1}
|
||||
};
|
||||
arrput(*verts, v);
|
||||
}
|
||||
|
||||
static inline void tile_region(text_vert **verts, rect src_uv, rect dst, float tex_w, float tex_h, bool tile_x, bool tile_y)
|
||||
{
|
||||
// Convert the incoming UV rect into pixel coords
|
||||
@@ -4652,10 +4784,12 @@ static const JSCFunctionListEntry js_SDL_GPUDevice_funcs[] = {
|
||||
MIST_FUNC_DEF(gpu, make_pipeline, 1), // loads pipeline state into an object
|
||||
MIST_FUNC_DEF(gpu,compute_pipeline,1),
|
||||
MIST_FUNC_DEF(gpu, set_swapchain, 2),
|
||||
MIST_FUNC_DEF(gpu,sort_sprite,2),
|
||||
MIST_FUNC_DEF(gpu, make_sampler,1),
|
||||
MIST_FUNC_DEF(gpu, load_texture, 2),
|
||||
MIST_FUNC_DEF(gpu, texture, 1),
|
||||
MIST_FUNC_DEF(gpu, make_sprite_mesh, 2),
|
||||
MIST_FUNC_DEF(gpu, make_sprite_queue, 1),
|
||||
MIST_FUNC_DEF(gpu, make_quad, 0),
|
||||
MIST_FUNC_DEF(gpu, driver, 0),
|
||||
MIST_FUNC_DEF(gpu, make_shader, 1),
|
||||
@@ -4900,6 +5034,7 @@ JSC_CCALL(cmd_push_compute_uniform_data,
|
||||
)
|
||||
|
||||
JSC_CCALL(cmd_submit,
|
||||
Uint64 start = SDL_GetTicksNS();
|
||||
SDL_GPUCommandBuffer *cmds = js2SDL_GPUCommandBuffer(js,self);
|
||||
SDL_GPUFence *fence = SDL_SubmitGPUCommandBufferAndAcquireFence(cmds);
|
||||
return SDL_GPUFence2js(js,fence);
|
||||
@@ -4922,10 +5057,9 @@ JSC_CCALL(cmd_hud,
|
||||
SDL_PushGPUVertexUniformData(cmds, js2number(js,argv[1]), &data, sizeof(data));
|
||||
)
|
||||
|
||||
JSC_CCALL(cmd_camera,
|
||||
SDL_GPUCommandBuffer *cmds = js2SDL_GPUCommandBuffer(js, self);
|
||||
JSValue camera = argv[0];
|
||||
|
||||
shader_globals camera_globals(JSContext *js, JSValue camera)
|
||||
{
|
||||
shader_globals data = {0};
|
||||
HMM_Vec2 size;
|
||||
transform *transform;
|
||||
double fov;
|
||||
@@ -4945,8 +5079,6 @@ JSC_CCALL(cmd_camera,
|
||||
HMM_Mat4 proj;
|
||||
HMM_Mat4 view;
|
||||
|
||||
shader_globals data = {0};
|
||||
|
||||
if (ortho) {
|
||||
proj = HMM_Orthographic_RH_NO(
|
||||
-size.x*0.5, 0.5*size.x,
|
||||
@@ -4977,6 +5109,12 @@ JSC_CCALL(cmd_camera,
|
||||
data.viewport_size = (HMM_Vec2){0.5,0.5};
|
||||
data.viewport_offset = (HMM_Vec2){0,0};
|
||||
data.time = SDL_GetTicksNS() / 1000000000.0f;
|
||||
return data;
|
||||
}
|
||||
|
||||
JSC_CCALL(cmd_camera,
|
||||
SDL_GPUCommandBuffer *cmds = js2SDL_GPUCommandBuffer(js, self);
|
||||
shader_globals data = camera_globals(js, argv[0]);
|
||||
SDL_PushGPUVertexUniformData(cmds, js2number(js,argv[1]), &data, sizeof(data));
|
||||
)
|
||||
|
||||
@@ -5626,9 +5764,9 @@ static const JSCFunctionListEntry js_io_funcs[] = {
|
||||
MIST_FUNC_DEF(io, gamemode, 2),
|
||||
};
|
||||
|
||||
JSC_GETSET(transform, pos, vec3)
|
||||
JSC_GETSET(transform, scale, vec3f)
|
||||
JSC_GETSET(transform, rotation, quat)
|
||||
JSC_GETSET_APPLY(transform, pos, vec3)
|
||||
JSC_GETSET_APPLY(transform, scale, vec3f)
|
||||
JSC_GETSET_APPLY(transform, rotation, quat)
|
||||
JSC_CCALL(transform_move,
|
||||
transform *t = js2transform(js,self);
|
||||
transform_move(t, js2vec3(js,argv[0]));
|
||||
@@ -5647,7 +5785,7 @@ JSC_CCALL(transform_rotate,
|
||||
transform *t = js2transform(js,self);
|
||||
HMM_Quat rot = HMM_QFromAxisAngle_RH(axis, js2angle(js,argv[1]));
|
||||
t->rotation = HMM_MulQ(t->rotation,rot);
|
||||
t->dirty = true;
|
||||
transform_apply(t);
|
||||
)
|
||||
|
||||
JSC_CCALL(transform_angle,
|
||||
@@ -5672,6 +5810,7 @@ JSC_CCALL(transform_phys2d,
|
||||
transform_move(t, (HMM_Vec3){v.x*dt,v.y*dt,0});
|
||||
HMM_Quat rot = HMM_QFromAxisAngle_RH((HMM_Vec3){0,0,1}, av*dt);
|
||||
t->rotation = HMM_MulQ(t->rotation, rot);
|
||||
transform_apply(t);
|
||||
)
|
||||
|
||||
JSC_CCALL(transform_unit,
|
||||
@@ -5679,6 +5818,7 @@ JSC_CCALL(transform_unit,
|
||||
t->pos = v3zero;
|
||||
t->rotation = QUAT1;
|
||||
t->scale = v3one;
|
||||
transform_apply(t);
|
||||
)
|
||||
|
||||
JSC_CCALL(transform_trs,
|
||||
@@ -5686,6 +5826,7 @@ JSC_CCALL(transform_trs,
|
||||
t->pos = JS_IsUndefined(argv[0]) ? v3zero : js2vec3(js,argv[0]);
|
||||
t->rotation = JS_IsUndefined(argv[1]) ? QUAT1 : js2quat(js,argv[1]);
|
||||
t->scale = JS_IsUndefined(argv[2]) ? v3one : js2vec3(js,argv[2]);
|
||||
transform_apply(t);
|
||||
)
|
||||
|
||||
JSC_CCALL(transform_rect,
|
||||
@@ -5694,6 +5835,7 @@ JSC_CCALL(transform_rect,
|
||||
t->pos = (HMM_Vec3){r.x,r.y,0};
|
||||
t->scale = (HMM_Vec3){r.w,r.h,1};
|
||||
t->rotation = QUAT1;
|
||||
transform_apply(t);
|
||||
)
|
||||
|
||||
JSC_CCALL(transform_array,
|
||||
@@ -5704,10 +5846,35 @@ JSC_CCALL(transform_array,
|
||||
JS_SetPropertyUint32(js,ret,i, number2js(js,m.em[i]));
|
||||
)
|
||||
|
||||
/*static JSValue js_transform_get_parent(JSContext *js, JSValueConst self)
|
||||
{
|
||||
return JS_GetProperty(js,self,parent_atom);
|
||||
}
|
||||
|
||||
static JSValue js_transform_set_parent(JSContext *js, JSValueConst self, JSValue v)
|
||||
{
|
||||
transform *t = js2transform(js,self);
|
||||
if (t->parent) return JS_UNDEFINED;
|
||||
transform *p = js2transform(js,v);
|
||||
if (!p) {
|
||||
JS_SetProperty(js,self,parent_atom,JS_UNDEFINED);
|
||||
t->parent = NULL;
|
||||
}
|
||||
|
||||
JS_SetProperty(js,self,parent_atom,JS_DupValue(js,v));
|
||||
t->parent = p;
|
||||
}
|
||||
*/
|
||||
|
||||
JSC_CCALL(transform_clean,
|
||||
js2transform_mat3(js,self);
|
||||
)
|
||||
|
||||
static const JSCFunctionListEntry js_transform_funcs[] = {
|
||||
CGETSET_ADD(transform, pos),
|
||||
CGETSET_ADD(transform, scale),
|
||||
CGETSET_ADD(transform, rotation),
|
||||
// CGETSET_ADD(transform, parent),
|
||||
MIST_FUNC_DEF(transform, trs, 3),
|
||||
MIST_FUNC_DEF(transform, phys2d, 3),
|
||||
MIST_FUNC_DEF(transform, move, 1),
|
||||
@@ -5718,6 +5885,7 @@ static const JSCFunctionListEntry js_transform_funcs[] = {
|
||||
MIST_FUNC_DEF(transform, unit, 0),
|
||||
MIST_FUNC_DEF(transform, rect, 1),
|
||||
MIST_FUNC_DEF(transform, array, 0),
|
||||
MIST_FUNC_DEF(transform, clean, 0),
|
||||
};
|
||||
|
||||
JSC_CCALL(datastream_time, return number2js(js,plm_get_time(js2datastream(js,self)->plm)); )
|
||||
@@ -6737,6 +6905,37 @@ JSC_CCALL(os_battery_seconds,
|
||||
return number2js(js,seconds);
|
||||
)
|
||||
|
||||
JSC_CCALL(os_insertion_sort,
|
||||
JSValue arr = argv[0];
|
||||
JSValue cmp = argv[1];
|
||||
int len = js_arrlen(js, arr);
|
||||
|
||||
for (int i = 1; i < len; i++) {
|
||||
JSValue key = JS_GetPropertyUint32(js, arr, i);
|
||||
int j = i - 1;
|
||||
|
||||
while (j >= 0) {
|
||||
JSValue arr_j = JS_GetPropertyUint32(js, arr, j);
|
||||
|
||||
JSValue ret = JS_Call(js, cmp, JS_UNDEFINED, 2, (JSValue[]){ arr_j, key });
|
||||
double c = js2number(js, ret);
|
||||
JS_FreeValue(js, ret);
|
||||
|
||||
if (c > 0) {
|
||||
JS_SetPropertyUint32(js, arr, j + 1, arr_j);
|
||||
j--;
|
||||
} else {
|
||||
JS_FreeValue(js, arr_j);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
JS_SetPropertyUint32(js, arr, j + 1, key);
|
||||
}
|
||||
|
||||
ret = JS_DupValue(js,arr);
|
||||
)
|
||||
|
||||
JSC_CCALL(os_power_state,
|
||||
SDL_PowerState state = SDL_GetPowerInfo(NULL, NULL);
|
||||
switch(state) {
|
||||
@@ -6750,6 +6949,60 @@ JSC_CCALL(os_power_state,
|
||||
return JS_UNDEFINED;
|
||||
)
|
||||
|
||||
JSC_CCALL(os_cull_sprite,
|
||||
JSValue sprite = argv[0];
|
||||
JSValue camera = argv[1];
|
||||
)
|
||||
|
||||
JSC_CCALL(os_cull_sprites,
|
||||
ret = JS_NewArray(js);
|
||||
int n = 0;
|
||||
|
||||
JSValue sprites = argv[0];
|
||||
shader_globals info = camera_globals(js,argv[1]);
|
||||
|
||||
int len = js_arrlen(js,sprites);
|
||||
|
||||
HMM_Vec2 corners[4] = {
|
||||
(HMM_Vec2){0,0},
|
||||
(HMM_Vec2){1,0},
|
||||
(HMM_Vec2){0,1},
|
||||
(HMM_Vec2){1,1},
|
||||
};
|
||||
|
||||
for (int i = 0; i < len; i++) {
|
||||
JSValue sub = JS_GetPropertyUint32(js,sprites, i);
|
||||
transform *t;
|
||||
JS_GETATOM(js,t,sub,transform_atom,transform)
|
||||
HMM_Mat3 trmat = t->gcache3;
|
||||
int outside = 0;
|
||||
|
||||
for (int j = 0; j < 4; j++) {
|
||||
Uint64 start = SDL_GetTicksNS();
|
||||
HMM_Vec3 corner = HMM_MulM3V3(trmat, (HMM_Vec3){corners[j].x, corners[j].y, 1.0});
|
||||
HMM_Vec4 clip = HMM_MulM4V4(info.world_to_projection, (HMM_Vec4){corner.x,corner.y,corner.z,1.0});
|
||||
if (clip.w <= 0.0) {
|
||||
outside++;
|
||||
continue;
|
||||
}
|
||||
|
||||
float nx = clip.x/clip.w;
|
||||
float ny = clip.y/clip.w;
|
||||
float nz = clip.z/clip.w;
|
||||
|
||||
if (nx < -1 || nx > 1) outside++;
|
||||
else if (ny < -1 || ny > 1) outside++;
|
||||
else if (nz < -1 || nz > 1) outside++;
|
||||
}
|
||||
|
||||
if (outside != 4) {
|
||||
JS_SetPropertyUint32(js,ret,n,JS_DupValue(js,sub));
|
||||
n++;
|
||||
}
|
||||
JS_FreeValue(js,sub);
|
||||
}
|
||||
)
|
||||
|
||||
static const JSCFunctionListEntry js_os_funcs[] = {
|
||||
MIST_FUNC_DEF(os, turbulence, 4),
|
||||
MIST_FUNC_DEF(os, model_buffer, 1),
|
||||
@@ -6810,6 +7063,8 @@ static const JSCFunctionListEntry js_os_funcs[] = {
|
||||
MIST_FUNC_DEF(os, battery_voltage, 0),
|
||||
MIST_FUNC_DEF(os, battery_seconds, 0),
|
||||
MIST_FUNC_DEF(os, power_state, 0),
|
||||
MIST_FUNC_DEF(os, insertion_sort, 2),
|
||||
MIST_FUNC_DEF(os, cull_sprites, 2),
|
||||
};
|
||||
|
||||
#define JSSTATIC(NAME, PARENT) \
|
||||
@@ -6886,7 +7141,6 @@ void ffi_load(JSContext *js) {
|
||||
QJSCLASSPREP_FUNCS(SDL_GPUBuffer)
|
||||
// QJSCLASSPREP_FUNCS(SDL_GPUTransferBuffer)
|
||||
|
||||
|
||||
QJSGLOBALCLASS(os);
|
||||
|
||||
QJSCLASSPREP_FUNCS(transform);
|
||||
@@ -6953,6 +7207,8 @@ void ffi_load(JSContext *js) {
|
||||
dst_atom = JS_NewAtom(js, "dst");
|
||||
count_atom = JS_NewAtom(js, "count");
|
||||
transform_atom = JS_NewAtom(js,"transform");
|
||||
image_atom = JS_NewAtom(js,"image");
|
||||
layer_atom = JS_NewAtom(js,"layer");
|
||||
|
||||
cw_atom = JS_NewAtom(js,"cw");
|
||||
ccw_atom = JS_NewAtom(js,"ccw");
|
||||
@@ -7017,6 +7273,7 @@ void ffi_load(JSContext *js) {
|
||||
index_atom = JS_NewAtom(js, "index");
|
||||
indirect_atom = JS_NewAtom(js, "indirect");
|
||||
num_indices_atom = JS_NewAtom(js,"num_indices");
|
||||
parent_atom = JS_NewAtom(js,"parent");
|
||||
|
||||
fill_event_atoms(js);
|
||||
|
||||
|
||||
@@ -2,9 +2,12 @@
|
||||
#define SPRITE_H
|
||||
|
||||
#include "HandmadeMath.h"
|
||||
#include "script.h"
|
||||
|
||||
typedef struct {
|
||||
HMM_Mat2 affine;
|
||||
HMM_Mat3 affine;
|
||||
JSValue image;
|
||||
int layer;
|
||||
} sprite;
|
||||
|
||||
#endif
|
||||
|
||||
@@ -8,16 +8,15 @@ transform *make_transform()
|
||||
|
||||
t->scale = (HMM_Vec3){1,1,1};
|
||||
t->rotation = (HMM_Quat){0,0,0,1};
|
||||
t->dirty = 1;
|
||||
transform_apply(t);
|
||||
return t;
|
||||
}
|
||||
|
||||
void transform_free(JSRuntime *rt, transform *t) { free(t); }
|
||||
void transform_apply(transform *t) { t->dirty = 1; }
|
||||
void transform_move(transform *t, HMM_Vec3 v)
|
||||
{
|
||||
t->pos = HMM_AddV3(t->pos, v);
|
||||
t->dirty = 1;
|
||||
transform_apply(t);
|
||||
}
|
||||
|
||||
HMM_Vec3 transform_direction(transform *t, HMM_Vec3 dir)
|
||||
@@ -53,14 +52,34 @@ HMM_Vec3 mat3_t_dir(HMM_Mat4 m, HMM_Vec3 dir)
|
||||
return mat3_t_pos(m, dir);
|
||||
}
|
||||
|
||||
static inline void transform_clean(transform *t)
|
||||
{
|
||||
if (!t->dirty) return;
|
||||
t->dirty = 0;
|
||||
t->cache = HMM_M4TRS(t->pos,t->rotation,t->scale);
|
||||
t->cache3 = HMM_M3TRS(t->pos.xy,t->rotation.x,t->scale.xy);
|
||||
}
|
||||
|
||||
HMM_Mat4 transform2mat(transform *t)
|
||||
{
|
||||
return HMM_M4TRS(t->pos, t->rotation, t->scale);
|
||||
transform_clean(t);
|
||||
return t->cache;
|
||||
}
|
||||
|
||||
HMM_Mat3 transform2mat3(transform *t)
|
||||
{
|
||||
return HMM_M3TRS(t->pos.xy, t->rotation.x, t->scale.xy);
|
||||
transform_clean(t);
|
||||
return t->cache3;
|
||||
}
|
||||
|
||||
HMM_Mat3 transform2mat3_global(transform *t)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void transform_apply(transform *t)
|
||||
{
|
||||
t->dirty = 1;
|
||||
}
|
||||
|
||||
HMM_Quat angle2rotation(float angle)
|
||||
|
||||
@@ -9,13 +9,19 @@ typedef struct transform {
|
||||
HMM_Vec3 scale;
|
||||
HMM_Quat rotation;
|
||||
HMM_Mat4 cache;
|
||||
HMM_Mat3 cache3;
|
||||
HMM_Mat3 gcache3;
|
||||
HMM_Mat4 gcache;
|
||||
int dirty;
|
||||
struct transform *parent;
|
||||
struct transform *children;
|
||||
} transform;
|
||||
|
||||
transform *make_transform();
|
||||
void transform_apply(transform *t);
|
||||
void transform_free(JSRuntime *rt,transform *t);
|
||||
|
||||
void transform_apply(transform *t);
|
||||
|
||||
#define VEC2_FMT "[%g,%g]"
|
||||
#define VEC2_MEMS(s) (s).x, (s).y
|
||||
|
||||
@@ -40,6 +46,9 @@ HMM_Vec3 mat3_t_dir(HMM_Mat4 m, HMM_Vec3 dir);
|
||||
|
||||
HMM_Mat4 transform2mat(transform *t);
|
||||
HMM_Mat3 transform2mat3(transform *t);
|
||||
|
||||
HMM_Mat3 transform2mat3_global(transform *t);
|
||||
|
||||
transform mat2transform(HMM_Mat4 m);
|
||||
|
||||
HMM_Quat angle2rotation(float angle);
|
||||
|
||||
Reference in New Issue
Block a user