add sprite and rendering to engine js

This commit is contained in:
John Alanbrook 2024-07-23 17:21:27 -05:00
parent 08725d474d
commit ef802bb6f2
9 changed files with 255 additions and 11 deletions

View file

@ -15,6 +15,24 @@ var make_point_obj = function(o, p)
var fullrect = [0,0,1,1];
var sprite_addbucket = function(sprite)
{
return;
var pp = sprite.gameobject.drawlayer.toString();
sprite_buckets[pp] ??= {};
sprite_buckets[pp][sprite.path] ??= {};
sprite_buckets[pp][sprite.path][sprite.guid] = sprite;
}
var sprite_rmbucket = function(sprite)
{
return;
var pp = sprite.gameobject.drawlayer.toString();
if (!sprite_buckets[pp]) return;
if (!sprite_buckets[pp][sprite.path]) return;
delete sprite_buckets[pp][sprite.path][sprite.guid];
}
var sprite = {
loop: true,
rect: fullrect,
@ -61,7 +79,10 @@ var sprite = {
return;
}
if (p === this.path) return;
this._p = p;
sprite_rmbucket(this);
this._p = p;
sprite_addbucket(this);
this.del_anim?.();
this.texture = game.texture(p);
@ -74,12 +95,12 @@ var sprite = {
this.play();
this.pos = this.dimensions().scale(this.anchor);
},
get path() {
return this._p;
},
kill() {
sprite_rmbucket(this);
this.del_anim?.();
this.anim = undefined;
this.gameobject = undefined;
@ -111,6 +132,7 @@ var sprite = {
height() { return this.dimensions().y; },
};
globalThis.allsprites = {};
globalThis.sprite_buckets = [];
sprite.doc = {
path: "Path to the texture.",
@ -152,8 +174,11 @@ component.sprite = function(obj) {
sp.transform = obj.transform;
sp.guid = prosperon.guid();
allsprites[sp.guid] = sp;
sprite_addbucket(sp);
if (component.sprite.make_hook) component.sprite.make_hook(sp);
return sp;
}
sprite.shade = [1,1,1,1];
Object.mixin(os.make_seg2d(), {

View file

@ -324,6 +324,25 @@ game.engine_start = function (s) {
};
render.init();
camera = make_camera();
camera.transform.pos = [0,0,-100];
camera.mode = "keep";
camera.break = "fit";
camera.size = game.size;
gamestate.camera = camera;
hudcam = make_camera();
hudcam.near = 0;
hudcam.size = camera.size;
hudcam.mode = "keep";
hudcam.break = "fit";
appcam = make_camera();
appcam.near = 0;
appcam.size = window.size;
appcam.transform.pos = [window.size.x,window.size.y,-100];
screencolor = render.screencolor();
},
process,
window.size.x,
@ -342,6 +361,141 @@ prosperon.release_mode = function()
}
prosperon.debug = true;
// 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
function camviewport()
{
var aspect = (this.viewport[2]-this.viewport[0])/(this.viewport[3]-this.viewport[1])*window.size.x/window.size.y;
var raspect = this.size.x/this.size.y;
var left = this.viewport[0]*window.size.x;
var bottom = this.viewport[1]*window.size.y;
var usemode = this.mode;
if (this.break && this.size.x > window.size.x && this.size.y > window.size.y)
usemode = this.break;
if (usemode === "fit")
if (raspect < aspect) usemode = "height";
else usemode = "width";
switch(usemode) {
case "stretch":
case "expand":
return [0, 0, window.size.x, window.size.y];
case "keep":
return [left, bottom, left+this.size.x, bottom+this.size.y];
case "height":
var ret = [left, 0, this.size.x*(window.size.y/this.size.y), window.size.y];
ret[0] = (window.size.x-(ret[2]-ret[0]))/2;
return ret;
case "width":
var ret = [0, bottom, window.size.x, this.size.y*(window.size.x/this.size.x)];
ret[1] = (window.size.y-(ret[3]-ret[1]))/2;
return ret;
}
return [0, 0, window.size.x, window.size.y];
}
// pos is pixels on the screen, lower left[0,0]
function camscreen2world(pos)
{
var view = this.screen2cam(pos);
view.x *= this.size.x;
view.y *= this.size.y;
view = view.sub([this.size.x/2, this.size.y/2]);
view = view.add(this.pos.xy);
return view;
}
camscreen2world.doc = "Convert a view position for a camera to world."
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;
}
screen2cam.doc = "Convert a screen space position in pixels to a normalized viewport position in a camera."
function make_camera()
{
var cam = world.spawn();
cam.near = 0.1;
cam.far = 1000;
cam.ortho = true;
cam.viewport = [0,0,1,1];
cam.size = window.size.slice(); // The render size of this camera in pixels
// In ortho mode, this determines how many pixels it will see
cam.mode = "stretch";
cam.screen2world = camscreen2world;
cam.screen2cam = screen2cam;
cam.mousepos = function() { return this.screen2world(input.mouse.screenpos()); }
cam.view = camviewport;
cam.offscreen = false;
return cam;
}
var camera;
var hudcam;
var appcam;
var screencolor;
prosperon.render = function()
{
render.set_camera(camera);
render.sprites();
prosperon.draw();
hudcam.size = camera.size;
hudcam.transform.pos = [hudcam.size.x/2, hudcam.size.y/2, -100];
render.set_camera(hudcam);
prosperon.hud();
render.flush_text();
render.end_pass();
/* draw the image of the game world first */
render.glue_pass();
render.viewport(...camera.view());
render.use_shader(render.postshader);
render.use_mat({diffuse:screencolor});
render.draw(shape.quad);
// Flush & render
appcam.transform.pos = [window.size.x/2, window.size.y/2, -100];
appcam.size = window.size.slice();
if (os.sys() !== 'macos')
appcam.size.y *= -1;
render.set_camera(appcam);
render.viewport(...appcam.view());
// Call gui functions
mum.style = mum.dbg_style;
prosperon.gui();
if (mum.drawinput) mum.drawinput();
prosperon.gui_dbg();
render.flush_text();
mum.style = mum.base;
render.imgui_new(window.size.x, window.size.y, 0.01);
// if (gfx_gui) render.gfx_gui();
render.imgui_end();
render.end_pass();
render.commit();
}
function process() {
var startframe = profile.now();
var dt = profile.secs(profile.now()) - frame_t;

View file

@ -354,6 +354,7 @@ dup(diff) {
this.components[key].enabled = false;
delete this.components[key];
}
delete this.components;
this.clear();
@ -367,7 +368,6 @@ dup(diff) {
}
},
make_objs(objs) {
for (var prop in objs) {
say(`spawning ${json.encode(objs[prop])}`);

View file

@ -274,3 +274,41 @@ mum.ex_hud = function()
mum.label("TOP RIGHT", {pos:game.size, anchor:[1,1]});
mum.label("BOTTOM RIGHT", {pos:[game.size.x, 0], anchor:[1,0]});
}
mum.drawinput = undefined;
var ptext = "";
var panpan = {
draw() {
mum.rectangle({pos:[0,0], anchor:[0,0], height:20, width: window.size.x, padding:[10,16], color:Color.black});
mum.label("input level: ");
mum.label(ptext, {offset:[50,0], color:Color.red});
},
inputs: {
block: true,
char(c) {
ptext += c
},
enter() {
delete mum.drawinput;
player[0].uncontrol(panpan);
},
escape() {
delete mum.drawinput;
player[0].uncontrol(panpan);
},
backspace() {
ptext = ptext.slice(0,ptext.length-1);
}
},
}
mum.textinput = function (fn, str = "") {
mum.drawinput = panpan.draw;
ptext = str;
player[0].control(panpan);
panpan.inputs.enter = function() {
fn(ptext);
delete mum.drawinput;
player[0].uncontrol(panpan);
}
}

View file

@ -108,7 +108,7 @@ var make_emitter = function()
{
var e = Object.create(emitter);
e.ssbo = render.make_textssbo();
e.shape = shape.quad;
e.shape = shape.centered_quad;
e.shader = render.make_shader("shaders/baseparticle.cg");
e.dead = [];
return e;

View file

@ -444,6 +444,8 @@ var parshader;
var spritessboshader;
var polyssboshader;
var sprite_ssbo;
render.init = function() {
textshader = render.make_shader("shaders/text_base.cg");
render.spriteshader = render.make_shader("shaders/sprite.cg");
@ -456,6 +458,7 @@ render.init = function() {
polyssboshader = render.make_shader("shaders/poly_ssbo.cg");
textssbo = render.make_textssbo();
poly_ssbo = render.make_textssbo();
sprite_ssbo = render.make_textssbo();
render.textshader = textshader;
@ -489,6 +492,30 @@ render.init = function() {
}
}
render.sprites = function(gridsize = 1)
{
var sps = Object.values(allsprites);
var sprite_buckets = {};
for (var sprite of sps) {
var pp = sprite.gameobject.drawlayer.toString();
sprite_buckets[pp] ??= {};
sprite_buckets[pp][sprite.path] ??= {};
sprite_buckets[pp][sprite.path][sprite.guid] = sprite;
}
render.use_shader(spritessboshader);
for (var bucket of Object.values(sprite_buckets)){
for (var img of Object.values(bucket)) {
var sparray = Object.values(img);
if (sparray.length === 0) continue;
var ss = sparray[0];
render.use_mat(ss);
render.make_sprite_ssbo(Object.values(sparray), sprite_ssbo);
render.draw(shape.quad, sprite_ssbo, sparray.length);
}
}
}
render.circle = function(pos, radius, color) {
check_flush();
var mat = {

View file

@ -19,15 +19,13 @@ readonly buffer ssbo {
out vec2 uv;
out vec4 color0;
vec2 pos;
uniform mat4 vp;
void main()
{
particle p = par[gl_InstanceIndex];
pos = a_pos - 0.5;
gl_Position = vp * p.model * vec4(pos, 0.0, 1.0);
uv = a_pos;
gl_Position = vp * p.model * vec4(a_pos, 0.0, 1.0);
uv = a_uv;
color0 = p.color;
}
@end

View file

@ -1533,9 +1533,9 @@ static const JSCFunctionListEntry js_physics_funcs[] = {
CGETSET_ADD(physics, collision_persistence),
};
JSC_GETSET_APPLY(transform, pos, vec3)
JSC_GETSET_APPLY(transform, scale, vec3)
JSC_GETSET_APPLY(transform, rotation, quat)
JSC_GETSET(transform, pos, vec3)
JSC_GETSET(transform, scale, vec3)
JSC_GETSET(transform, rotation, quat)
JSC_CCALL(transform_move, transform_move(js2transform(self), js2vec3(argv[0])); )
JSC_CCALL(transform_lookat,

View file

@ -56,6 +56,8 @@ HMM_Vec3 mat3_t_dir(HMM_Mat4 m, HMM_Vec3 dir)
}
HMM_Mat4 transform2mat(transform *t) {
return HMM_M4TRS(t->pos, t->rotation, t->scale);
if (t->dirty) {
t->cache = HMM_M4TRS(t->pos, t->rotation, t->scale);
t->dirty = 0;