improve sprite render speed; particle rendering

This commit is contained in:
2025-01-13 15:35:04 -06:00
parent 653748363f
commit dab13a1f54
8 changed files with 449 additions and 143 deletions

View File

@@ -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;
};

View File

@@ -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 };

View File

@@ -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) }

View File

@@ -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); }

View File

@@ -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);

View File

@@ -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

View File

@@ -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)

View File

@@ -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);