From cadf10b3a9a6e1230e3ec67a3b8fd84f619cb43b Mon Sep 17 00:00:00 2001 From: John Alanbrook Date: Wed, 15 May 2024 14:16:05 -0500 Subject: [PATCH] Move rendering functions into prosperon --- scripts/components.js | 5 + scripts/engine.js | 48 ++++--- scripts/entity.js | 2 + scripts/render.js | 296 ++++++++++++++++++++++++++++++++++++++++++ source/engine/jsffi.c | 27 ++-- 5 files changed, 343 insertions(+), 35 deletions(-) diff --git a/scripts/components.js b/scripts/components.js index fb6fda0..35e3c27 100644 --- a/scripts/components.js +++ b/scripts/components.js @@ -110,6 +110,7 @@ var sprite = { 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]; f = (f+1)%playing.frames.length; if (f === 0) { self.anim_done?.(); @@ -133,6 +134,8 @@ var sprite = { this._p = p; this.del_anim?.(); this.texture = game.texture(p); + this.diffuse = this.texture; + this.rect = [0,0,1,1]; var anim = SpriteAnim.make(p); if (!anim) return; @@ -140,6 +143,7 @@ var sprite = { this.play(); this.pos = this.dimensions().scale(this.anchor); + }, get path() { return this._p; @@ -179,6 +183,7 @@ var sprite = { globalThis.allsprites = {}; sprite.make = function(go) { + var sp = Object.create(sprite); sp.go = go; sp.gameobject = go; sp.guid = prosperon.guid(); diff --git a/scripts/engine.js b/scripts/engine.js index 7032c4e..a178a72 100644 --- a/scripts/engine.js +++ b/scripts/engine.js @@ -296,6 +296,31 @@ game.engine_start = function(s) { Object.readonly(window.__proto__, 'high_dpi'); Object.readonly(window.__proto__, 'sample_count'); s(); + + shape.quad = { + pos:os.make_buffer([ + 0,0,0, + 1,0,0, + 0,1,0, + 1,1,0 + ],0), + verts: 4, + uv: os.make_buffer([0,1,1,1,0,0,1,0],2), + index: os.make_buffer([0,1,2,2,1,3], 1), + count: 6 + }; + + shape.triangle = { + pos: os.make_buffer([ + 0,0,0, + 0.5,1,0, + 1,0,0] + ,0), + uv: os.make_buffer([0,0,0.5,1,1,0],2), + verts: 3, + count: 3, + index: os.make_buffer([0,2,1],1), + }; }, process); } @@ -327,30 +352,11 @@ function process() } } var st = profile.now(); - if (!game.camera) - prosperon.window_render(world.transform, 1); - else - prosperon.window_render(game.camera.transform, game.camera.zoom); - - //render.set_camera(); - - /*os.sprite_pipe(); - allsprites.forEach(function(x) { - render.set_sprite_tex(x.texture); - x.draw(x.go); - render.sprite_flush(); - }); - render.sprite_flush();*/ - prosperon.draw(); // draw calls - debug.draw(); // calls needed debugs - - prosperon.hook3d?.(); - + prosperon.window_render(); + prosperon.draw(); prosperon.gui(); prosperon.screengui(); - prosperon.hookend?.(); - //render.end_pass(); profile.addreport(profcache, "render frame", st); frames.push(profile.secs(profile.now()-startframe)); if (frames.length > 20) frames.shift(); diff --git a/scripts/entity.js b/scripts/entity.js index 32d602e..eacd819 100644 --- a/scripts/entity.js +++ b/scripts/entity.js @@ -44,6 +44,8 @@ var entity = { return p; }, + drawlayer: 0, + full_path() { return this.path_from(world); }, clear() { diff --git a/scripts/render.js b/scripts/render.js index 3dad097..11f54c4 100644 --- a/scripts/render.js +++ b/scripts/render.js @@ -144,4 +144,300 @@ 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."; +var shaderlang = { + macos: "metal_macos", + windows: "hlsl5", + linux: "glsl330", + web: "wgsl", + ios: "metal_ios", +} + +var attr_map = { + a_pos: 0, + a_uv: 1, + a_norm: 2, + a_bone: 3, + a_weight: 4, + a_color: 5, + a_tan: 6, + a_angle: 7, + a_wh: 8, + a_st: 9, + a_ppos: 10, + a_scale: 11 +} + +var blend_map = { + mix: true, + none: false +} + +var primitive_map = { + point: 1, + line: 2, + linestrip: 3, + triangle: 4, + trianglestrip: 5 +} + +var cull_map = { + none: 1, + front: 2, + back: 3 +} + +var depth_map = { + off: false, + on: true +} + +var face_map = { + cw: 2, + ccw: 1 +} + +render.poly_prim = function(verts) +{ + var index = []; + if (verts.length < 1) return undefined; + + for (var i = 0; i < verts.length; i++) + verts[i][2] = 0; + + for (var i = 2; i < verts.length; i++) { + index.push(0); + index.push(i-1); + index.push(i); + } + + return { + pos: os.make_buffer(verts.flat()), + verts: verts.length, + index: os.make_buffer(index, 1), + count: index.length + }; +} + +function shader_directive(shader, name, map) +{ + var reg = new RegExp(`#${name}.*`, 'g'); + var mat = shader.match(reg); + if (!mat) return 0; + + reg = new RegExp(`#${name}\s*`, 'g'); + var ff = mat.map(d=>d.replace(reg,''))[0].trim(); + + return map[ff]; +} + +function global_uni(uni, stage) +{ + switch(uni.name) { + case "time": + render.setuniv(stage, uni.slot, profile.secs(profile.now())); + return true; + case "projection": + render.setuniproj(stage, uni.slot); + return true; + case "view": + render.setuniview(stage, uni.slot); + return true; + case "vp": + render.setunivp(stage, uni.slot); + return true; + } + + return false; +} + +render.make_shader = function(shader) +{ + var file = shader; + shader = io.slurp(shader); + var writejson = `${file.name()}_c.json`; + var st = profile.now(); + + 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.report(st, `CACHE make shader from ${file}`); + var shaderobj = json.decode(io.slurp(writejson)); + shaderobj.pipe = render.pipeline(shaderobj); + return shaderobj; + } + + var out = `${file.name()}.shader`; + + var files = [file]; + + var incs = shader.match(/#include <.*>/g); + if (incs) + for (var inc of incs) { + var filez = inc.match(/#include <(.*)>/)[1]; + var macro = io.slurp(filez); + shader = shader.replace(inc, macro); + files.push(filez); + } + + var blend = shader_directive(shader, 'blend', blend_map); + var primitive = shader_directive(shader, 'primitive', primitive_map); + var cull = shader_directive(shader, 'cull', cull_map); + var depth = shader_directive(shader, 'depth', depth_map); + var face = shader_directive(shader, 'face', face_map); + + 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); + var backend = shaderlang[os.sys()]; + var ret = os.system(`sokol-shdc -b -f bare_yaml --slang=${backend} -i ${out} -o ${out}`); + if (ret) { + console.info(`error compiling shader`); + return; + } + io.rm(out); + + /* Take YAML and create the shader object */ + var yamlfile = `${out}_reflection.yaml`; + console.info(`slurping ${yamlfile}`); + var obj = json.decode(yaml.tojson(io.slurp(yamlfile))); + io.rm(yamlfile); + + obj = obj.shaders[0].programs[0]; + function add_code(stage) { + console.info(json.encode(stage)); + stage.code = io.slurp(stage.path); + io.rm(stage.path); + delete stage.path; + } + console.info(json.encode(obj)); + add_code(obj.vs); + if (!obj.fs) + if (obj.vs.fs) { + obj.fs = obj.vs.fs; + delete obj.vs.fs; + } + add_code(obj.fs); + + obj.blend = blend; + obj.cull = cull; + obj.primitive = primitive; + obj.depth = depth; + obj.face = face; + + if (obj.vs.inputs) + for (var i of obj.vs.inputs) { + if (!(i.name in attr_map)) + i.mat = -1; + else + i.mat = attr_map[i.name]; + } + + function make_unimap(stage) { + if (!stage.uniform_blocks) return {}; + var unimap = {}; + for (var uni of stage.uniform_blocks) { + if (uni.struct_name[0] == "_") + uni.struct_name = uni.struct_name.slice(1); + + unimap[uni.struct_name] = { + name: uni.struct_name, + slot: Number(uni.slot), + size: Number(uni.size) + }; + } + + return unimap; + } + + obj.vs.unimap = make_unimap(obj.vs); + obj.fs.unimap = make_unimap(obj.fs); + + obj.files = files; + + obj.name = file; + + io.slurpwrite(writejson, json.encode(obj)); + profile.report(st, `make shader from ${file}`); + + console.info(`pipeline for ${file}`); + obj.pipe = render.pipeline(obj); + + return obj; +} + +var shader_unisize = { + 4: render.setuniv, + 8: render.setuniv2, + 12: render.setuniv3, + 16: render.setuniv4 +}; + +render.shader_apply_material = function(shader, material = {}) +{ + for (var p in shader.vs.unimap) { + if (global_uni(shader.vs.unimap[p], 0)) continue; + if (!(p in material)) continue; + var s = shader.vs.unimap[p]; + shader_unisize[s.size](0, s.slot, material[p]); + } + + for (var p in shader.fs.unimap) { + if (global_uni(shader.fs.unimap[p], 1)) continue; + if (!(p in material)) continue; + var s = shader.fs.unimap[p]; + shader_unisize[s.size](1, s.slot, material[p]); + } + + if ("diffuse_size" in shader.fs.unimap) + render.setuniv2(1, shader.fs.unimap.diffuse_size.slot, [material.diffuse.width, material.diffuse.height]); + + if ("diffuse_size" in shader.vs.unimap) + render.setuniv2(0, shader.vs.unimap.diffuse_size.slot, [material.diffuse.width, material.diffuse.height]); +} + +render.sg_bind = function(shader, mesh = {}, material = {}, ssbo) +{ + var bind = {}; + bind.attrib = []; + if (shader.vs.inputs) + for (var a of shader.vs.inputs) { + if (!(a.name in mesh)) { + if (!(a.name.slice(2) in mesh)) { + console.error(`cannot draw shader ${shader.name}; there is no attrib ${a.name} in the given mesh.`); + return undefined; + } else + bind.attrib.push(mesh[a.name.slice(2)]); + } else + bind.attrib.push(mesh[a.name]); + } + bind.images = []; + if (shader.fs.images) + for (var img of shader.fs.images) { + if (material[img.name]) + bind.images.push(material[img.name]); + else + bind.images.push(game.texture("icons/no_tex.gif")); + } + + if (mesh.index) { + bind.index = mesh.index; + bind.count = mesh.count; + } else + bind.count = mesh.verts; + + bind.ssbo = []; + if (shader.vs.storage_buffers) + for (var b of shader.vs.storage_buffers) + bind.ssbo.push(ssbo); + + return bind; +} + return {render}; diff --git a/source/engine/jsffi.c b/source/engine/jsffi.c index a0c6dc4..31540d4 100644 --- a/source/engine/jsffi.c +++ b/source/engine/jsffi.c @@ -79,8 +79,6 @@ QJSCLASS(texture) QJSCLASS(font) QJSCLASS(warp_gravity) QJSCLASS(warp_damp) -QJSCLASS(material) -QJSCLASS(model) QJSCLASS(window) QJSCLASS(constraint) QJSCLASS(sg_buffer) @@ -1367,14 +1365,6 @@ static const JSCFunctionListEntry js_physics_funcs[] = { MIST_FUNC_DEF(physics, make_gravity, 0), }; -JSC_CCALL(model_draw_go, - model_draw_go(js2model(this), js2gameobject(argv[0])) -); - -static const JSCFunctionListEntry js_model_funcs[] = { - MIST_FUNC_DEF(model, draw_go, 1) -}; - static const JSCFunctionListEntry js_emitter_funcs[] = { CGETSET_ADD(emitter, life), CGETSET_ADD(emitter, life_var), @@ -1891,8 +1881,15 @@ JSValue primitive2js(primitive *p) PRIMCHECK(p,v,index) js_setpropstr(v, "count", number2js(p->idx_count)); - printf("new primitive with count %d\n", p->idx_count); - + return v; +} + +JSValue material2js(material *m) +{ + JSValue v = JS_NewObject(js); + if (m->diffuse) + js_setpropstr(v, "diffuse", texture2js(m->diffuse)); + return v; } @@ -1901,7 +1898,10 @@ JSC_SCALL(os_make_model, if (arrlen(m->meshes) != 1) return JSUNDEF; mesh *me = m->meshes+0; - return primitive2js(me->primitives+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_CCALL(os_make_emitter, emitter *e = make_emitter(); @@ -2122,7 +2122,6 @@ void ffi_load() { QJSCLASSPREP_FUNCS(font); QJSCLASSPREP_FUNCS(constraint); QJSCLASSPREP_FUNCS(window); - QJSCLASSPREP_FUNCS(model); QJSCLASSPREP_FUNCS(datastream); QJSGLOBALCLASS(nota);