prosperon/scripts/render.js

1454 lines
37 KiB
JavaScript
Raw Normal View History

2024-10-17 17:23:33 -05:00
var unit_transform = os.make_transform();
2024-10-05 08:43:51 -05:00
/*
Anatomy of rendering an image
render.image(path)
Path can be a file like "toad"
If this is a gif, this would display the entire range of the animation
It can be a frame of animation, like "frog.0"
If it's an aseprite, it can have multiple animations, like "frog.walk.0"
file^ frame^ idx
render.image("frog.walk.0",
game.image("frog.walk.0") ==> retrieve
image = {
texture: "spritesheet.png",
rect: [x,y,w,h],
time: 100
},
frames: {
toad: {
x: 4,
y: 5,
w: 10,
h: 10
},
frog: {
walk: [
{ texture: spritesheet.png, x: 10, y:10, w:6,h:6, time: 100 },
{ texture: spritesheet.png, x:16,y:10,w:6,h:6,time:100} <--- two frame walk animation
],
},
},
}
texture frog {
texture: {"frog.png"}, <--- this is the actual thing to send to the gpu
x:0,
y:0,
w:10,
h:10
},
*/
render.doc = {
doc: "Functions for rendering modes.",
normal: "Final render with all lighting.",
2024-09-26 11:36:09 -05:00
wireframe: "Show only wireframes of models.",
};
2024-07-11 14:25:45 -05:00
var cur = {};
2024-09-27 10:19:05 -05:00
cur.images = [];
cur.samplers = [];
2024-07-11 14:25:45 -05:00
2024-10-03 09:31:06 -05:00
/* A pipeline defines the characteristics of the incoming draw.
{
shader <--- the shader determines the vertex layout,
depth
compare always (never/less/equal/less_equal/greater/not_equal/greater_equal/always)
write false
bias 0
bias_slope_scale 0
bias_clamp 0
stencil
enabled false
front/back
compare always
fail_op keep (zero/replace/incr_clamp/decr_clamp/invert/incr_wrap/decr_wrap)
depth_fail_op keep
pass_op keep
read true
write false
ref 0 (0-255)
color
write_mask rgba
blend
enabled false
src_factor_rgb one
dst_factor_rgb zero
op_rgb add
src_factor_alpha one
dst_factor_alpha zero
op_alpha add
cull none
face_winding cw
alpha_to_coverage false
label ""
*/
var blendop = {
add: 1,
subtract: 2,
reverse_subtract: 3
};
var blendfactor = {
zero: 1,
one: 2,
src_color: 3,
one_minus_src_color: 4,
src_alpha: 5,
one_minus_src_alpha: 6,
dst_color: 7,
one_minus_dst_color: 8,
dst_alpha: 9,
one_minus_dst_alpha: 10,
src_alpha_saturated: 11,
blend_color: 12,
one_minus_blend_color: 13,
blend_alpha: 14,
one_minus_blend_alpha: 15
};
var colormask = {
none: 0x10,
r: 0x1,
g: 0x2,
rg: 0x3,
b: 0x4,
rb: 0x5,
gb: 0x6,
rgb: 0x7,
a: 0x8,
ra: 0x9,
ga: 0xA,
rga: 0xB,
ba: 0xC,
rba: 0xD,
gba: 0xE,
rgba: 0xF
};
var primitive_map = {
point: 1,
line: 2,
linestrip: 3,
triangle: 4,
trianglestrip: 5,
};
var cull_map = {
none: 1,
front: 2,
back: 3,
};
var stencilop = {
keep: 1,
zero: 2,
replace: 3,
incr_clamp: 4,
decr_clamp: 5,
invert: 6,
incr_wrap: 7,
decr_wrap: 8
};
var face_map = {
ccw: 1,
cw: 2,
};
var compare = {
never: 1,
less: 2,
equal: 3,
less_equal: 4,
greater: 5,
not_equal: 6,
greater_equal: 7,
always: 8
};
var base_pipeline = {
primitive: primitive_map.triangle,
depth: {
compare: compare.always,
write: false,
bias: 0,
bias_slope_scale: 0,
bias_clamp: 0
},
stencil: {
2024-10-03 23:35:40 -05:00
enabled: true,
2024-10-03 09:31:06 -05:00
front: {
2024-10-03 23:35:40 -05:00
compare: compare.equal,
2024-10-03 09:31:06 -05:00
fail_op: stencilop.keep,
depth_fail_op: stencilop.keep,
pass_op: stencilop.keep
},
back: {
2024-10-03 23:35:40 -05:00
compare: compare.equal,
2024-10-03 09:31:06 -05:00
fail_op: stencilop.keep,
depth_fail_op: stencilop.keep,
pass_op: stencilop.keep
},
read: true,
write: false,
ref: 0
},
write_mask: colormask.rgba,
blend: {
enabled: false,
src_factor_rgb: blendfactor.one,
dst_factor_rgb: blendfactor.zero,
op_rgb: blendop.add,
src_factor_alpha: blendfactor.one,
dst_factor_alpha: blendfactor.zero,
op_alpha: blendop.add,
},
cull: cull_map.none,
face: face_map.cw,
alpha_to_coverage: false,
label: "scripted pipeline"
}
render.base_pipeline = base_pipeline;
render.colormask = colormask;
render.primitive_map = primitive_map;
render.cull_map = cull_map;
render.stencilop = stencilop;
render.face_map = face_map;
render.compare = compare;
render.blendfactor = blendfactor;
2024-10-03 23:35:40 -05:00
var pipe_shaders = new WeakMap();
// Uses the shader with the specified pipeline. If none specified, uses the base pipeline
2024-10-17 17:23:33 -05:00
render.use_shader = function use_shader(shader, pipeline = base_pipeline) {
2024-09-26 11:36:09 -05:00
if (typeof shader === "string") shader = make_shader(shader);
if (cur.shader === shader) return;
2024-10-03 23:35:40 -05:00
if (!pipe_shaders.has(shader)) pipe_shaders.set(shader, new WeakMap());
var shader_pipelines = pipe_shaders.get(shader);
if (!shader_pipelines.has(pipeline)) {
var new_pipeline = render.make_pipeline(shader,pipeline);
shader_pipelines.set(pipeline, new_pipeline);
}
var use_pipeline = shader_pipelines.get(pipeline);
if (cur.shader === shader && cur.pipeline === use_pipeline) return;
2024-07-11 14:25:45 -05:00
cur.shader = shader;
cur.bind = undefined;
cur.mesh = undefined;
2024-09-27 10:19:05 -05:00
cur.ssbo = undefined;
2024-10-03 23:35:40 -05:00
cur.images = [];
cur.pipeline = use_pipeline;
// Grab or create a pipeline obj that utilizes the specific shader and pipeline
render.setpipeline(use_pipeline);
shader_globals(cur.shader);
2024-09-26 11:36:09 -05:00
};
2024-07-11 14:25:45 -05:00
2024-10-03 23:35:40 -05:00
render.use_pipeline = function use_pipeline(pipeline) {
}
2024-09-26 11:36:09 -05:00
render.use_mat = function use_mat(mat) {
2024-07-11 14:25:45 -05:00
if (!cur.shader) return;
if (cur.mat === mat) return;
shader_apply_material(cur.shader, mat, cur.mat);
2024-09-26 11:36:09 -05:00
2024-07-11 14:25:45 -05:00
cur.mat = mat;
2024-09-27 10:19:05 -05:00
cur.images.length = 0;
cur.samplers.length = 0;
2024-07-11 14:25:45 -05:00
if (!cur.shader.fs.images) return;
for (var img of cur.shader.fs.images) {
2024-09-26 11:36:09 -05:00
if (mat[img.name]) cur.images.push(mat[img.name]);
else cur.images.push(game.texture("icons/no_tex.gif"));
}
for (var smp of cur.shader.fs.samplers) {
var std = smp.sampler_type === "nonfiltering";
cur.samplers.push(std);
}
2024-09-26 11:36:09 -05:00
};
2024-07-11 14:25:45 -05:00
var models_array = [];
2024-09-26 11:36:09 -05:00
function set_model(t) {
if (cur.shader.vs.unimap.model) render.setunim4(0, cur.shader.vs.unimap.model.slot, t);
2024-07-11 14:25:45 -05:00
}
2024-08-08 17:32:58 -05:00
render.set_model = set_model;
var shaderlang = {
2024-09-26 11:36:09 -05:00
macos: "metal_macos",
windows: "hlsl5",
linux: "glsl430",
web: "wgsl",
ios: "metal_ios",
};
var attr_map = {
a_pos: 0,
2024-09-26 11:36:09 -05:00
a_uv: 1,
a_norm: 2,
2024-08-09 14:39:31 -05:00
a_joint: 3,
a_weight: 4,
a_color: 5,
a_tan: 6,
a_angle: 7,
a_wh: 8,
a_st: 9,
a_ppos: 10,
2024-09-26 11:36:09 -05:00
a_scale: 11,
};
2024-09-26 11:36:09 -05:00
render.poly_prim = function poly_prim(verts) {
var index = [];
if (verts.length < 1) return undefined;
2024-09-26 11:36:09 -05:00
for (var i = 0; i < verts.length; i++) verts[i][2] = 0;
for (var i = 2; i < verts.length; i++) {
index.push(0);
2024-09-26 11:36:09 -05:00
index.push(i - 1);
index.push(i);
}
2024-09-26 11:36:09 -05:00
return {
pos: os.make_buffer(verts.flat()),
verts: verts.length,
index: os.make_buffer(index, 1),
2024-09-26 11:36:09 -05:00
count: index.length,
};
2024-09-26 11:36:09 -05:00
};
var uni_globals = {
2024-09-26 11:36:09 -05:00
time(stage, slot) {
render.setuniv(stage, slot, profile.secs(profile.now()));
},
projection(stage, slot) {
render.setuniproj(stage, slot);
},
view(stage, slot) {
render.setuniview(stage, slot);
},
vp(stage, slot) {
render.setunivp(stage, slot);
},
};
function set_global_uni(uni, stage) {
2024-09-26 11:36:09 -05:00
uni_globals[uni.name]?.(stage, uni.slot);
}
2024-07-14 16:09:50 -05:00
2024-07-22 15:40:58 -05:00
var shader_cache = {};
var shader_times = {};
2024-09-26 11:36:09 -05:00
function strip_shader_inputs(shader) {
for (var a of shader.vs.inputs) a.name = a.name.slice(2);
}
2024-09-26 11:36:09 -05:00
render.hotreload = function () {
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()];
var old = shader_cache[i];
Object.assign(shader_cache[i], obj);
cur.bind = undefined;
cur.mesh = undefined;
}
2024-09-26 11:36:09 -05:00
};
2024-09-26 11:36:09 -05:00
function create_shader_obj(file) {
var files = [file];
var out = ".prosperon/tmp.shader";
var shader = io.slurp(file);
2024-05-30 17:12:32 -05:00
var incs = shader.match(/#include <.*>/g);
if (incs)
2024-09-26 11:36:09 -05:00
for (var inc of incs) {
var filez = inc.match(/#include <(.*)>/)[1];
var macro = io.slurp(filez);
if (!macro) {
filez = `shaders/${filez}`;
macro = io.slurp(filez);
}
shader = shader.replace(inc, macro);
files.push(filez);
2024-06-07 00:43:15 -05:00
}
2024-09-26 11:36:09 -05:00
shader = shader.replace(/uniform\s+(\w+)\s+(\w+);/g, "uniform _$2 { $1 $2; };");
shader = shader.replace(/(texture2D|sampler) /g, "uniform $1 ");
io.slurpwrite(out, shader);
2024-05-16 14:50:18 -05:00
var compiled = {};
// shader file is created, now cross compile to all targets
for (var platform in shaderlang) {
var backend = shaderlang[platform];
var ret = os.system(`sokol-shdc -f bare_yaml --slang=${backend} -i ${out} -o ${out}`);
if (ret) {
console.error(`error compiling shader ${file}. No compilation found for ${platform}:${backend}, and no cross compiler available.`);
return;
}
2024-09-26 11:36:09 -05:00
/* Take YAML and create the shader object */
var yamlfile = `${out}_reflection.yaml`;
var jjson = yaml.tojson(io.slurp(yamlfile));
var obj = json.decode(jjson);
io.rm(yamlfile);
2024-05-16 14:50:18 -05:00
2024-09-26 11:36:09 -05:00
obj = obj.shaders[0].programs[0];
function add_code(stage) {
stage.code = io.slurp(stage.path);
2024-06-03 16:45:08 -05:00
2024-09-26 11:36:09 -05:00
io.rm(stage.path);
delete stage.path;
}
2024-09-26 11:36:09 -05:00
add_code(obj.vs);
if (!obj.fs && obj.vs.fs) {
obj.fs = obj.vs.fs;
delete obj.vs.fs;
}
2024-05-16 14:50:18 -05:00
2024-09-26 11:36:09 -05:00
add_code(obj.fs);
2024-10-03 09:31:06 -05:00
obj.indexed = true;
2024-09-26 11:36:09 -05:00
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) {
var uniname = uni.struct_name[0] == "_" ? uni.struct_name.slice(1) : uni.struct_name;
unimap[uniname] = {
name: uniname,
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.name = file;
2024-09-26 11:36:09 -05:00
strip_shader_inputs(obj);
compiled[platform] = obj;
2024-05-16 14:50:18 -05:00
}
compiled.files = files;
compiled.source = shader;
2024-09-26 11:36:09 -05:00
return compiled;
}
2024-10-03 17:36:29 -05:00
function make_shader(shader, pipe) {
if (shader_cache[shader]) return shader_cache[shader];
2024-09-26 11:36:09 -05:00
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`;
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;
}
}
2024-10-02 09:55:32 -05:00
var shaderobj = json.decode(io.slurp(writejson));
var obj = shaderobj[os.sys()];
2024-10-03 17:36:29 -05:00
shader_cache[file] = obj;
shader_times[file] = io.mod(file);
return obj;
}
2024-10-02 09:55:32 -05:00
profile.report(`shader_${file}`);
2024-09-26 11:36:09 -05:00
var compiled = create_shader_obj(file);
io.slurpwrite(writejson, json.encode(compiled));
2024-05-16 14:50:18 -05:00
var obj = compiled[os.sys()];
shader_cache[file] = obj;
shader_times[file] = io.mod(file);
2024-10-02 09:55:32 -05:00
profile.endreport(`shader_${file}`);
2024-09-26 11:36:09 -05:00
return obj;
}
var shader_unisize = {
4: render.setuniv,
8: render.setuniv2,
12: render.setuniv3,
2024-09-26 11:36:09 -05:00
16: render.setuniv4,
};
2024-09-26 11:36:09 -05:00
function shader_globals(shader) {
for (var p in shader.vs.unimap) set_global_uni(shader.vs.unimap[p], 0);
for (var p in shader.fs.unimap) set_global_uni(shader.fs.unimap[p], 1);
}
2024-09-26 11:36:09 -05:00
function shader_apply_material(shader, material = {}, old = {}) {
2024-10-17 17:23:33 -05:00
render.setpipeline(cur.pipeline);
for (var p in shader.vs.unimap) {
if (!(p in material)) continue;
2024-07-11 14:25:45 -05:00
if (material[p] === old[p]) continue;
assert(p in material, `shader ${shader.name} has no uniform for ${p}`);
var s = shader.vs.unimap[p];
2024-09-26 11:36:09 -05:00
if (p === "bones") {
2024-08-09 14:39:31 -05:00
render.setunibones(0, s.slot, material[p]);
continue;
}
shader_unisize[s.size](0, s.slot, material[p]);
}
2024-09-26 11:36:09 -05:00
for (var p in shader.fs.unimap) {
if (!(p in material)) continue;
2024-07-11 14:25:45 -05:00
if (material[p] === old[p]) continue;
2024-09-26 11:36:09 -05:00
assert(p in material, `shader ${shader.name} has no uniform for ${p}`);
var s = shader.fs.unimap[p];
shader_unisize[s.size](1, s.slot, material[p]);
}
2024-09-26 11:36:09 -05:00
2024-06-19 18:52:41 -05:00
if (!material.diffuse) return;
2024-07-11 14:25:45 -05:00
if (material.diffuse === old.diffuse) return;
2024-06-19 18:52:41 -05:00
if ("diffuse_texel" in shader.fs.unimap) render.setuniv2(1, shader.fs.unimap.diffuse_texel.slot, [1,1].div([material.diffuse.width, material.diffuse.height]));
2024-09-26 11:36:09 -05:00
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]);
}
2024-09-27 10:19:05 -05:00
// Creates a binding object for a given mesh and shader
var bcache = new WeakMap();
2024-09-26 11:36:09 -05:00
function sg_bind(mesh, ssbo) {
if (cur.bind && cur.mesh === mesh && cur.ssbo === ssbo) {
2024-09-27 10:19:05 -05:00
cur.bind.ssbo = [ssbo];
cur.bind.images = cur.images;
cur.bind.samplers = cur.samplers;
2024-09-27 10:19:05 -05:00
render.setbind(cur.bind);
return;
}
/* if (bcache.has(cur.shader) && bcache.get(cur.shader).has(mesh)) {
2024-09-27 10:19:05 -05:00
cur.bind = bcache.get(cur.shader).get(mesh);
cur.bind.images = cur.images;
cur.bind.samplers = cur.samplers;
2024-09-27 10:19:05 -05:00
if (ssbo)
cur.bind.ssbo = [ssbo];
render.setbind(cur.bind);
return;
}*/
var bind = {};/* if (!bcache.has(cur.shader)) bcache.set(cur.shader, new WeakMap());
if (!bcache.get(cur.shader).has(mesh)) bcache.get(cur.shader).set(mesh, bind);*/
2024-09-27 10:19:05 -05:00
cur.mesh = mesh;
cur.ssbo = ssbo;
cur.bind = bind;
bind.attrib = [];
2024-07-11 14:25:45 -05:00
if (cur.shader.vs.inputs)
2024-09-26 11:36:09 -05:00
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. ${json.encode(mesh)}`);
return undefined;
} else bind.attrib.push(mesh[a.name]);
}
2024-07-11 14:25:45 -05:00
if (cur.shader.indexed) {
bind.index = mesh.index;
bind.count = mesh.count;
2024-09-26 11:36:09 -05:00
} else bind.count = mesh.verts;
bind.ssbo = [];
2024-09-26 11:36:09 -05:00
if (cur.shader.vs.storage_buffers) for (var b of cur.shader.vs.storage_buffers) bind.ssbo.push(ssbo);
2024-07-11 14:25:45 -05:00
bind.images = cur.images;
bind.samplers = cur.samplers;
2024-07-11 14:25:45 -05:00
render.setbind(cur.bind);
2024-09-26 11:36:09 -05:00
return bind;
}
2024-05-16 08:21:13 -05:00
render.device = {
2024-09-26 11:36:09 -05:00
pc: [1920, 1080],
macbook_m2: [2560, 1664, 13.6],
ds_top: [400, 240, 3.53],
ds_bottom: [320, 240, 3.02],
playdate: [400, 240, 2.7],
switch: [1280, 720, 6.2],
switch_lite: [1280, 720, 5.5],
switch_oled: [1280, 720, 7],
dsi: [256, 192, 3.268],
ds: [256, 192, 3],
dsixl: [256, 192, 4.2],
ipad_air_m2: [2360, 1640, 11.97],
2024-05-16 08:21:13 -05:00
iphone_se: [1334, 750, 4.7],
2024-09-26 11:36:09 -05:00
iphone_12_pro: [2532, 1170, 6.06],
iphone_15: [2556, 1179, 6.1],
gba: [240, 160, 2.9],
gameboy: [160, 144, 2.48],
gbc: [160, 144, 2.28],
steamdeck: [1280, 800, 7],
vita: [960, 544, 5],
psp: [480, 272, 4.3],
imac_m3: [4480, 2520, 23.5],
macbook_pro_m3: [3024, 1964, 14.2],
ps1: [320, 240, 5],
ps2: [640, 480],
snes: [256, 224],
gamecube: [640, 480],
n64: [320, 240],
c64: [320, 200],
macintosh: [512, 342, 9],
gamegear: [160, 144, 3.2],
2024-05-16 08:21:13 -05:00
};
render.device.doc = `Device resolutions given as [x,y,inches diagonal].`;
2024-06-07 00:43:15 -05:00
var textshader;
var circleshader;
var polyshader;
2024-07-09 01:03:39 -05:00
var slice9shader;
2024-07-18 12:39:58 -05:00
var parshader;
2024-07-22 19:07:02 -05:00
var spritessboshader;
2024-07-23 12:02:46 -05:00
var polyssboshader;
2024-07-23 17:21:27 -05:00
var sprite_ssbo;
2024-09-26 11:36:09 -05:00
render.init = function () {
textshader = make_shader("shaders/text_base.cg");
render.spriteshader = make_shader("shaders/sprite.cg");
spritessboshader = make_shader("shaders/sprite_ssbo.cg");
2024-10-03 17:36:29 -05:00
var postpipe = Object.create(base_pipeline);
postpipe.cull = cull_map.none;
2024-10-03 23:35:40 -05:00
postpipe.primitive = primitive_map.triangle;
2024-10-03 17:36:29 -05:00
render.postshader = make_shader("shaders/simplepost.cg", postpipe);
slice9shader = make_shader("shaders/9slice.cg");
circleshader = make_shader("shaders/circle.cg");
polyshader = make_shader("shaders/poly.cg");
parshader = make_shader("shaders/baseparticle.cg");
polyssboshader = make_shader("shaders/poly_ssbo.cg");
2024-07-23 12:02:46 -05:00
poly_ssbo = render.make_textssbo();
2024-07-23 17:21:27 -05:00
sprite_ssbo = render.make_textssbo();
2024-09-26 11:36:09 -05:00
2024-06-07 00:43:15 -05:00
render.textshader = textshader;
2024-09-26 11:36:09 -05:00
os.make_circle2d().draw = function () {
render.circle(this.body().transform().pos, this.radius, [1, 1, 0, 1]);
};
var disabled = [148 / 255, 148 / 255, 148 / 255, 1];
var sleep = [1, 140 / 255, 228 / 255, 1];
var dynamic = [1, 70 / 255, 46 / 255, 1];
var kinematic = [1, 194 / 255, 64 / 255, 1];
var static_color = [73 / 255, 209 / 255, 80 / 255, 1];
os.make_poly2d().draw = function () {
2024-06-07 09:49:13 -05:00
var body = this.body();
2024-09-26 11:36:09 -05:00
var color = body.sleeping() ? [0, 0.3, 0, 0.4] : [0, 1, 0, 0.4];
2024-06-07 09:49:13 -05:00
var t = body.transform();
render.poly(this.points, color, body.transform());
color.a = 1;
render.line(this.points.wrapped(1), color, 1, body.transform());
2024-09-26 11:36:09 -05:00
};
os.make_seg2d().draw = function () {
render.line([this.a(), this.b()], [1, 0, 1, 1], Math.max(this.radius / 2, 1), this.body().transform());
};
joint.pin().draw = function () {
2024-06-07 09:49:13 -05:00
var a = this.bodyA();
var b = this.bodyB();
2024-09-26 11:36:09 -05:00
render.line([a.transform().pos.xy, b.transform().pos.xy], [0, 1, 1, 1], 1);
};
};
2024-06-07 00:43:15 -05:00
2024-08-25 14:23:22 -05:00
render.draw_sprites = true;
render.draw_particles = true;
render.draw_hud = true;
render.draw_gui = true;
2024-08-26 11:13:26 -05:00
render.draw_gizmos = true;
2024-08-25 14:23:22 -05:00
2024-09-26 23:12:30 -05:00
render.buckets = [];
2024-09-27 10:19:05 -05:00
render.sprites = function render_sprites() {
2024-10-17 17:23:33 -05:00
profile.report("sprites");
2024-10-02 09:55:32 -05:00
profile.report("drawing");
2024-07-23 17:21:27 -05:00
render.use_shader(spritessboshader);
2024-09-26 23:12:30 -05:00
var buckets = component.sprite_buckets();
for (var l in buckets) {
var layer = buckets[l];
for (var img in layer) {
var sparray = layer[img];
2024-09-26 11:36:09 -05:00
if (sparray.length === 0) continue;
var ss = sparray[0];
2024-09-29 06:10:42 -05:00
ss.baseinstance = render.make_sprite_ssbo(sparray, sprite_ssbo);
render.use_mat(ss);
2024-09-26 11:36:09 -05:00
render.draw(shape.quad, sprite_ssbo, sparray.length);
2024-07-23 17:21:27 -05:00
}
}
2024-10-02 09:55:32 -05:00
profile.endreport("drawing");
2024-10-17 17:23:33 -05:00
profile.endreport("sprites");
2024-09-26 11:36:09 -05:00
};
2024-07-23 17:21:27 -05:00
render.circle = function render_circle(pos, radius, color, inner_radius = 1) {
2024-10-06 17:18:18 -05:00
check_flush();
2024-09-26 11:36:09 -05:00
if (inner_radius >= 1) inner_radius = inner_radius / radius;
else if (inner_radius < 0) inner_radius = 1.0;
2024-06-07 00:43:15 -05:00
var mat = {
radius: radius,
inner_r: inner_radius,
2024-06-07 00:43:15 -05:00
coord: pos,
shade: color,
2024-06-07 00:43:15 -05:00
};
2024-07-11 14:25:45 -05:00
render.use_shader(circleshader);
render.use_mat(mat);
render.draw(shape.quad);
2024-09-26 11:36:09 -05:00
};
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).";
2024-06-07 00:43:15 -05:00
render.poly = function render_poly(points, color, transform) {
2024-06-07 00:43:15 -05:00
var buffer = render.poly_prim(points);
2024-09-26 11:36:09 -05:00
var mat = { shade: color };
2024-07-11 14:25:45 -05:00
render.use_shader(polyshader);
set_model(transform);
2024-07-11 14:25:45 -05:00
render.use_mat(mat);
render.draw(buffer);
2024-09-26 11:36:09 -05:00
};
2024-06-07 00:43:15 -05:00
2024-07-23 12:02:46 -05:00
var nextflush = undefined;
2024-09-26 11:36:09 -05:00
function flush() {
nextflush?.();
nextflush = undefined;
}
2024-09-23 18:17:46 -05:00
// If flush_fn was already on deck, it does not flush. Otherwise, flushes and then sets the flush fn
2024-09-26 11:36:09 -05:00
function check_flush(flush_fn) {
if (!nextflush) nextflush = flush_fn;
2024-07-23 12:02:46 -05:00
else if (nextflush !== flush_fn) {
nextflush();
nextflush = flush_fn;
}
2024-07-23 12:02:46 -05:00
}
2024-09-13 10:27:01 -05:00
render.flush = check_flush;
2024-09-29 06:10:42 -05:00
render.forceflush = function()
{
if (nextflush) nextflush();
2024-10-04 00:45:44 -05:00
nextflush = undefined;
2024-10-17 17:23:33 -05:00
cur.shader = undefined;
2024-09-29 06:10:42 -05:00
}
2024-09-13 10:27:01 -05:00
2024-07-23 12:02:46 -05:00
var poly_cache = [];
var poly_idx = 0;
2024-07-23 12:02:46 -05:00
var poly_ssbo;
2024-09-26 11:36:09 -05:00
function poly_e() {
var e;
poly_idx++;
if (poly_idx > poly_cache.length) {
e = {
2024-09-26 11:36:09 -05:00
transform: os.make_transform(),
color: Color.white,
};
poly_cache.push(e);
return e;
}
2024-09-26 11:36:09 -05:00
var e = poly_cache[poly_idx - 1];
e.transform.unit();
return e;
}
2024-09-26 11:36:09 -05:00
function flush_poly() {
if (poly_idx === 0) return;
2024-10-06 17:18:18 -05:00
render.use_shader(queued_shader, queued_pipe);
2024-09-29 06:10:42 -05:00
var base = render.make_particle_ssbo(poly_cache.slice(0, poly_idx), poly_ssbo);
render.use_mat({baseinstance:base});
2024-08-24 18:40:29 -05:00
render.draw(shape.centered_quad, poly_ssbo, poly_idx);
poly_idx = 0;
2024-07-23 12:02:46 -05:00
}
render.line = function render_line(points, color = Color.white, thickness = 1) {
2024-09-26 11:36:09 -05:00
for (var i = 0; i < points.length - 1; i++) {
2024-08-28 16:38:31 -05:00
var a = points[i];
2024-09-26 11:36:09 -05:00
var b = points[i + 1];
2024-08-28 16:38:31 -05:00
var poly = poly_e();
2024-09-26 11:36:09 -05:00
var dist = vector.distance(a, b);
poly.transform.move(vector.midpoint(a, b));
2024-10-17 17:23:33 -05:00
poly.transform.rotate([0, 0, 1], vector.angle([b.x - a.x, b.y - a.y]));
2024-08-28 16:38:31 -05:00
poly.transform.scale = [dist, thickness, 1];
poly.color = color;
}
check_flush(flush_poly);
2024-09-26 11:36:09 -05:00
};
2024-06-07 00:43:15 -05:00
2024-05-16 08:21:13 -05:00
/* All draw in screen space */
2024-09-26 11:36:09 -05:00
render.point = function (pos, size, color = Color.blue) {
render.circle(pos, size, size, color);
2024-05-16 08:21:13 -05:00
};
2024-09-26 11:36:09 -05:00
render.cross = function render_cross(pos, size, color = Color.red, thickness = 1) {
2024-09-26 11:36:09 -05:00
var a = [pos.add([0, size]), pos.add([0, -size])];
var b = [pos.add([size, 0]), pos.add([-size, 0])];
render.line(a, color, thickness);
render.line(b, color, thickness);
2024-05-21 09:33:17 -05:00
};
2024-09-26 11:36:09 -05:00
render.arrow = function render_arrow(start, end, color = Color.red, wingspan = 4, wingangle = 10) {
2024-05-16 08:21:13 -05:00
var dir = end.sub(start).normalized();
2024-09-26 11:36:09 -05:00
var wing1 = [Vector.rotate(dir, wingangle).scale(wingspan).add(end), end];
var wing2 = [Vector.rotate(dir, -wingangle).scale(wingspan).add(end), end];
render.line([start, end], color);
render.line(wing1, color);
render.line(wing2, color);
2024-05-16 08:21:13 -05:00
};
render.coordinate = function render_coordinate(pos, size, color) {
2024-09-26 11:36:09 -05:00
render.text(JSON.stringify(pos.map(p => Math.round(p))), pos, size, color);
2024-05-16 08:21:13 -05:00
render.point(pos, 2, color);
2024-09-26 11:36:09 -05:00
};
2024-05-16 08:21:13 -05:00
2024-10-06 17:18:18 -05:00
var queued_shader;
var queued_pipe;
2024-10-17 17:23:33 -05:00
render.rectangle = function render_rectangle(rect, color, shader = polyssboshader, pipe = base_pipeline) {
2024-07-23 14:30:41 -05:00
var transform = os.make_transform();
2024-10-17 17:23:33 -05:00
var wh = [rect.width, rect.height];
var poly = poly_e();
2024-10-17 17:23:33 -05:00
var pos = [rect.x,rect.y].add([rect.width,rect.height].scale(0.5));
pos = pos.sub([rect.width,rect.height].scale([rect.anchor_x,rect.anchor_y]));
poly.transform.move(pos);
2024-09-26 11:36:09 -05:00
poly.transform.scale = [wh.x, wh.y, 1];
poly.color = color;
2024-10-06 17:18:18 -05:00
queued_shader = shader;
queued_pipe = pipe;
check_flush(flush_poly);
2024-05-16 08:21:13 -05:00
};
2024-09-26 11:36:09 -05:00
render.box = function render_box(pos, wh, color = Color.white) {
var poly = poly_e();
poly.transform.move(pos);
2024-09-26 11:36:09 -05:00
poly.transform.scale = [wh.x, wh.y, 1];
poly.color = color;
2024-09-26 11:36:09 -05:00
check_flush(flush_poly);
2024-05-16 08:21:13 -05:00
};
2024-09-26 11:36:09 -05:00
render.window = function render_window(pos, wh, color) {
render.box(pos.add(wh.scale(0.5)), wh, color);
};
2024-05-16 08:21:13 -05:00
2024-10-17 17:23:33 -05:00
render.text = function (str, rect, font = cur_font, size = 0, color = Color.white, wrap = -1, ) {
2024-10-16 07:53:05 -05:00
if (typeof font === 'string')
font = render.get_font(font);
2024-10-14 20:07:32 -05:00
if (!font) return;
2024-10-17 17:23:33 -05:00
var pos = [rect.x,rect.y];
pos.y -= font.descent;
if (rect.anchor_y)
pos.y -= rect.anchor_y*(font.ascent-font.descent);
2024-10-14 20:07:32 -05:00
gui.text(str, pos, size, color, wrap, font); // this puts text into buffer
cur_font = font;
2024-07-23 12:02:46 -05:00
check_flush(render.flush_text);
2024-05-16 08:21:13 -05:00
};
2024-10-16 07:53:05 -05:00
var tttsize = render.text_size;
render.text_size = function(str, font)
{
if (typeof font === 'string')
font = render.get_font(font);
return tttsize(str,font);
}
2024-09-23 18:17:46 -05:00
var lasttex = undefined;
var img_cache = [];
var img_idx = 0;
2024-09-26 11:36:09 -05:00
function flush_img() {
2024-09-23 18:17:46 -05:00
if (img_idx === 0) return;
render.use_shader(spritessboshader);
2024-09-29 06:10:42 -05:00
var startidx = render.make_sprite_ssbo(img_cache.slice(0, img_idx), sprite_ssbo);
2024-10-06 17:18:18 -05:00
render.use_mat({baseinstance:startidx});
2024-09-27 14:30:15 -05:00
cur.images = [lasttex];
2024-09-29 06:10:42 -05:00
render.draw(shape.quad, sprite_ssbo, img_idx);
2024-09-23 18:17:46 -05:00
lasttex = undefined;
img_idx = 0;
}
2024-09-26 11:36:09 -05:00
function img_e() {
2024-09-23 18:17:46 -05:00
img_idx++;
if (img_idx > img_cache.length) {
2024-09-26 19:28:54 -05:00
var e = {
2024-09-23 18:17:46 -05:00
transform: os.make_transform(),
shade: Color.white,
};
img_cache.push(e);
return e;
}
2024-09-26 19:28:54 -05:00
return img_cache[img_idx - 1];
2024-09-23 18:17:46 -05:00
}
2024-10-03 23:35:40 -05:00
var stencil_write = {
compare: compare.always,
fail_op: stencilop.replace,
depth_fail_op:stencilop.replace,
pass_op: stencilop.replace
};
2024-10-04 00:45:44 -05:00
var stencil_writer = function stencil_writer(ref)
2024-10-03 23:35:40 -05:00
{
var pipe = Object.create(base_pipeline);
Object.assign(pipe, {
stencil: {
enabled: true,
front: stencil_write,
back: stencil_write,
write:true,
read:true,
ref:ref
},
write_mask: colormask.none
});
2024-10-04 00:45:44 -05:00
return pipe;
}.hashify();
2024-10-03 23:35:40 -05:00
2024-10-06 17:18:18 -05:00
render.stencil_writer = stencil_writer;
2024-10-03 23:35:40 -05:00
// objects by default draw where the stencil buffer is 0
2024-10-04 00:45:44 -05:00
render.fillmask = function(ref)
2024-10-03 23:35:40 -05:00
{
2024-10-04 00:45:44 -05:00
render.forceflush();
var pipe = stencil_writer(ref);
2024-10-03 23:35:40 -05:00
render.use_shader('shaders/screenfill.cg', pipe);
render.draw(shape.quad);
2024-10-04 00:45:44 -05:00
}
var stencil_invert = {
compare: compare.always,
fail_op: stencilop.invert,
depth_fail_op: stencilop.invert,
pass_op: stencilop.invert
};
var stencil_inverter = Object.create(base_pipeline);
Object.assign(stencil_inverter, {
stencil: {
enabled: true,
front: stencil_invert,
back:stencil_invert,
write:true,
read:true,
ref: 0
},
write_mask: colormask.none
});
render.invertmask = function()
{
render.forceflush();
render.use_shader('shaders/screenfill.cg', stencil_inverter);
render.draw(shape.quad);
}
2024-10-03 23:35:40 -05:00
2024-10-17 17:23:33 -05:00
render.mask = function mask(image, pos, scale, rotation = 0, ref = 1)
{
2024-10-17 17:23:33 -05:00
if (typeof image === 'string')
image = game.texture(image);
var tex = image.texture;
if (scale) scale = sacle.div([tex.width,tex.height]);
else scale = vector.v3one;
2024-10-05 08:43:51 -05:00
2024-10-04 00:45:44 -05:00
var pipe = stencil_writer(ref);
render.use_shader('shaders/sprite.cg', pipe);
var t = os.make_transform();
2024-10-17 17:23:33 -05:00
t.trs(pos, undefined,scale);
set_model(t);
render.use_mat({
2024-10-17 17:23:33 -05:00
diffuse:image.texture,
rect: image.rect,
2024-10-04 00:45:44 -05:00
shade: Color.white
});
render.draw(shape.quad);
}
2024-10-19 18:37:27 -05:00
function calc_image_size(img)
{
return [img.texture.width*img.rect.width, img.texture.height*img.rect.height];
}
2024-10-17 17:23:33 -05:00
render.image = function image(image, rect = [0,0], rotation = 0, color = Color.white) {
2024-10-05 08:43:51 -05:00
if (typeof image === "string")
image = game.texture(image);
var tex = image.texture;
2024-09-11 12:25:42 -05:00
if (!tex) return;
2024-09-23 18:17:46 -05:00
2024-10-19 18:37:27 -05:00
var image_size = calc_image_size(image);
2024-10-18 12:51:21 -05:00
var size = [rect.width ? rect.width : image_size.x, rect.height ? rect.height : image_size.y];
2024-10-17 17:23:33 -05:00
2024-09-23 18:17:46 -05:00
if (!lasttex) {
2024-09-26 11:36:09 -05:00
check_flush(flush_img);
2024-09-23 18:17:46 -05:00
lasttex = tex;
}
2024-09-26 11:36:09 -05:00
2024-09-23 18:17:46 -05:00
if (lasttex !== tex) {
flush_img();
lasttex = tex;
}
2024-09-26 11:36:09 -05:00
2024-09-23 18:17:46 -05:00
var e = img_e();
2024-10-17 17:23:33 -05:00
var pos = [rect.x,rect.y].sub(size.scale([rect.anchor_x, rect.anchor_y]));
2024-10-19 18:37:27 -05:00
e.transform.trs(pos, undefined, size);
2024-10-05 08:43:51 -05:00
e.image = image;
2024-10-06 17:18:18 -05:00
e.shade = color;
2024-09-23 18:17:46 -05:00
return;
2024-09-26 11:36:09 -05:00
};
2024-07-15 15:54:18 -05:00
2024-10-19 18:37:27 -05:00
var slice9_t = os.make_transform();
2024-07-15 15:54:18 -05:00
// pos is the lower left corner, scale is the width and height
2024-10-19 18:37:27 -05:00
// slice is given in pixels
2024-10-17 17:23:33 -05:00
render.slice9 = function (image, rect = [0,0], slice = 0, color = Color.white) {
2024-10-14 20:07:32 -05:00
if (typeof image === 'string')
image = game.texture(image);
2024-10-19 18:37:27 -05:00
2024-10-14 20:07:32 -05:00
var tex = image.texture;
2024-10-19 18:37:27 -05:00
var image_size = calc_image_size(image);
var size = [rect.width ? rect.width : image_size.x, rect.height ? rect.height : image_size.y];
slice9_t.trs([rect.x,rect.y].sub(size.scale([rect.anchor_x, rect.anchor_y])), undefined, size);
2024-10-17 17:23:33 -05:00
slice = clay.normalizeSpacing(slice);
2024-10-19 18:37:27 -05:00
var border = [slice.l/image_size.x, slice.b/image_size.y, slice.r/image_size.x, slice.t/image_size.y];
2024-07-11 14:25:45 -05:00
render.use_shader(slice9shader);
2024-10-19 18:37:27 -05:00
set_model(slice9_t);
2024-07-11 14:25:45 -05:00
render.use_mat({
2024-07-09 13:48:15 -05:00
shade: color,
2024-09-26 11:36:09 -05:00
diffuse: tex,
2024-10-19 18:37:27 -05:00
win_tex_scale: size.div(image_size),
rect: [image.rect.x, image.rect.y,image.rect.width,image.rect.height],
frag_rect: [image.rect.x, image.rect.y,image.rect.width,image.rect.height],
2024-07-09 16:43:09 -05:00
border: border,
2024-07-09 01:03:39 -05:00
});
2024-07-11 14:25:45 -05:00
render.draw(shape.quad);
2024-09-26 11:36:09 -05:00
};
2024-07-09 01:03:39 -05:00
2024-09-26 11:36:09 -05:00
function endframe() {
2024-09-06 22:47:04 -05:00
tdraw = 0;
}
var textssbos = [];
var tdraw = 0;
2024-10-14 20:07:32 -05:00
var cur_font = undefined;
2024-07-18 17:09:35 -05:00
2024-09-26 11:36:09 -05:00
render.flush_text = function () {
2024-07-09 01:03:39 -05:00
if (!render.textshader) return;
2024-09-06 22:47:04 -05:00
tdraw++;
2024-09-26 11:36:09 -05:00
if (textssbos.length < tdraw) textssbos.push(render.make_textssbo());
var textssbo = textssbos[tdraw - 1];
2024-09-06 22:47:04 -05:00
var amt = render.flushtext(textssbo); // load from buffer into ssbo
2024-09-26 11:36:09 -05:00
2024-09-06 22:47:04 -05:00
if (amt === 0) {
tdraw--;
2024-09-26 11:36:09 -05:00
return;
}
2024-07-11 14:25:45 -05:00
render.use_shader(render.textshader);
2024-10-14 20:07:32 -05:00
render.use_mat({ text: cur_font.texture });
2024-07-18 17:09:35 -05:00
render.draw(shape.quad, textssbo, amt);
2024-09-26 11:36:09 -05:00
};
2024-05-16 08:21:13 -05:00
var fontcache = {};
2024-09-26 11:36:09 -05:00
2024-10-14 20:07:32 -05:00
render.get_font = function(path,size)
{
2024-10-16 07:53:05 -05:00
var parts = path.split('.');
if (!isNaN(parts[1])) {
path = parts[0];
size = Number(parts[1]);
}
path = Resources.find_font(path);
var fontstr = `${path}.${size}`;
2024-10-14 20:07:32 -05:00
if (!fontcache[fontstr]) fontcache[fontstr] = os.make_font(path,size);
return fontcache[fontstr];
}
2024-05-16 08:21:13 -05:00
render.doc = "Draw shapes in screen space.";
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.";
2024-09-26 11:36:09 -05:00
render.draw = function render_draw(mesh, ssbo, inst = 1, e_start = 0) {
sg_bind(mesh, ssbo);
2024-10-02 09:55:32 -05:00
profile.report("gpu_draw");
2024-09-07 00:11:34 -05:00
render.spdraw(e_start, cur.bind.count, inst);
2024-10-02 09:55:32 -05:00
profile.endreport("gpu_draw");
2024-09-26 11:36:09 -05:00
};
2024-05-16 08:21:13 -05:00
2024-10-17 17:23:33 -05:00
// Camera viewport is a rectangle with the bottom left corner defined as x,y. Units are pixels on the window.
2024-09-26 11:36:09 -05:00
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;
2024-09-26 11:36:09 -05:00
var left = this.viewport[0] * window.size.x;
var bottom = this.viewport[1] * window.size.y;
var usemode = this.mode;
2024-09-26 11:36:09 -05:00
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";
2024-09-26 11:36:09 -05:00
switch (usemode) {
case "stretch":
case "expand":
2024-10-17 17:23:33 -05:00
return {
x: 0,
y: 0,
width: window.size.x,
height: window.size.y
};
case "keep":
2024-10-17 17:23:33 -05:00
return {
x: left,
y: bottom,
width:left+this.size.x,
height:bottom+this.size.y
}
case "height":
2024-10-17 17:23:33 -05:00
var ret = {
x:left,
y:0,
width:this.size.x*(window.size.y/this.size.y),
height:window.size.y
};
ret.x = (window.size.x - (ret.width-ret.x))/2;
return ret;
case "width":
2024-10-17 17:23:33 -05:00
var ret = {
x:0,
y:bottom,
width:window.size.x,
height:this.size.y*(window.size.x/this.size.x)
};
ret.y = (window.size.y - (ret.height-ret.y))/2;
return ret;
}
2024-10-17 17:23:33 -05:00
return {
x:0,
y:0,
width:window.size.x,
height:window.size.y
};
}
// pos is pixels on the screen, lower left[0,0]
2024-09-26 11:36:09 -05:00
function camscreen2world(pos) {
var view = this.screen2cam(pos);
view.x *= this.size.x;
view.y *= this.size.y;
view = view.add(this.pos.xy);
2024-10-17 17:23:33 -05:00
view = view.scale(this.transform.scale);
return view;
}
2024-08-22 13:31:00 -05:00
// 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
2024-10-15 17:15:50 -05:00
// screen coordinates, pixels, 0,0 at the top left of the window and [w,h] at the top right of the screen
// hud coordinates, same as screen coordinates but the top left is 0,0
2024-08-22 13:31:00 -05:00
2024-09-26 11:36:09 -05:00
camscreen2world.doc = "Convert a view position for a camera to world.";
2024-08-22 13:31:00 -05:00
// return camera coordinates given a screen position
2024-09-26 11:36:09 -05:00
function screen2cam(pos) {
2024-10-17 17:23:33 -05:00
var winsize = window.size.slice();
var viewport = this.view();
2024-10-17 17:23:33 -05:00
var viewpos = pos.sub([viewport.x,viewport.y]);
viewpos = viewpos.div([viewport.width,viewport.height]);
viewpos.y += 1;
return viewpos;
2024-08-22 13:31:00 -05:00
}
2024-09-26 11:36:09 -05:00
function camextents() {
var half = this.size; //.scale(0.5);
2024-08-22 13:31:00 -05:00
return {
2024-09-26 11:36:09 -05:00
l: this.pos.x - half.x,
r: this.pos.x + half.x,
t: this.pos.y + half.y,
b: this.pos.y - half.y,
2024-08-22 13:31:00 -05:00
};
}
2024-09-26 11:36:09 -05:00
screen2cam.doc = "Convert a screen space position in pixels to a normalized viewport position in a camera.";
2024-09-26 11:36:09 -05:00
prosperon.gizmos = function () {
2024-08-27 14:58:08 -05:00
game.all_objects(o => {
if (o.gizmo) render.image(game.texture(o.gizmo), o.pos);
});
2024-09-26 11:36:09 -05:00
};
2024-08-27 14:58:08 -05:00
2024-09-26 11:36:09 -05:00
prosperon.make_camera = function () {
var cam = world.spawn();
2024-10-17 17:23:33 -05:00
cam.near = 1;
cam.far = -1000;
cam.ortho = true;
2024-10-17 17:23:33 -05:00
cam.viewport = [0, 0, 1, 1]; // normalized screen coordinates of where to draw
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;
2024-08-22 13:31:00 -05:00
cam.extents = camextents;
cam.view = camviewport;
return cam;
2024-09-26 11:36:09 -05:00
};
var screencolor;
2024-09-26 11:36:09 -05:00
globalThis.imtoggle = function (name, obj, field) {
2024-09-26 19:28:54 -05:00
var changed = false;
var old = obj[field];
2024-09-26 11:36:09 -05:00
obj[field] = imgui.checkbox(name, obj[field]);
2024-09-26 19:28:54 -05:00
if (old !== obj[field]) return true;
return false;
2024-09-26 11:36:09 -05:00
};
2024-09-03 14:08:46 -05:00
var replstr = "";
2024-09-26 11:36:09 -05:00
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");
};
2024-09-03 14:08:46 -05:00
2024-09-26 11:36:09 -05:00
var imgui_fn = function () {
2024-09-03 14:08:46 -05:00
render.imgui_new(window.size.x, window.size.y, 0.01);
2024-09-26 11:36:09 -05:00
if (debug.console)
debug.console = imgui.window("console", _ => {
imgui.text(console.transcript);
replstr = imgui.textinput(undefined, replstr);
imgui.button("submit", _ => {
eval(replstr);
replstr = "";
});
});
2024-09-03 14:08:46 -05:00
2024-09-26 11:36:09 -05:00
imgui.mainmenubar(_ => {
2024-09-03 14:08:46 -05:00
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", _ => {
2024-09-26 11:36:09 -05:00
imtoggle("Profiler", debug, "showprofiler");
imtoggle("Terminal out", debug, "termout");
imtoggle("Meta [f7]", debug, "meta");
imtoggle("Cheats [f8]", debug, "cheat");
imtoggle("Console [f9]", debug, "console");
2024-09-03 14:08:46 -05:00
});
imgui.sokol_gfx();
imgui.menu("Graphics", _ => {
2024-09-26 11:36:09 -05:00
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");
2024-09-03 14:08:46 -05:00
imgui.menu("Window", _ => {
window.fullscreen = imgui.checkbox("fullscreen", window.fullscreen);
2024-09-26 11:36:09 -05:00
// window.vsync = imgui.checkbox("vsync", window.vsync);
2024-09-03 14:08:46 -05:00
imgui.menu("MSAA", _ => {
2024-09-26 11:36:09 -05:00
for (var msaa of gamestate.msaa) imgui.button(msaa + "x", _ => (window.sample_count = msaa));
2024-09-03 14:08:46 -05:00
});
imgui.menu("Resolution", _ => {
2024-09-26 11:36:09 -05:00
for (var res of gamestate.resolutions) imgui.button(res, _ => (window.resolution = res));
2024-09-03 14:08:46 -05:00
});
});
});
prosperon.menu_hook?.();
});
2024-09-26 11:36:09 -05:00
2024-09-03 14:08:46 -05:00
prosperon.imgui();
render.imgui_end();
2024-09-26 11:36:09 -05:00
};
2024-09-03 14:08:46 -05:00
// figure out the highest resolution we can render at that's an integer
2024-10-17 17:23:33 -05:00
/* var basesize = prosperon.camera.size.slice();
var baseview = prosperon.camera.view();
var wh = [baseview[2]-baseview[0], baseview[3]-baseview[1]];
var mult = 1;
var trysize = basesize.scale(mult);
while (trysize.x <= wh.x && trysize.y <= wh.y) {
mult++;
trysize = basesize.scale(mult);
}
if (Math.abs(wh.x - basesize.scale(mult-1).x) < Math.abs(wh.x - trysize.x))
mult--;
prosperon.window_render(basesize.scale(mult));
2024-10-17 17:23:33 -05:00
*/
prosperon.render = function () {
render.glue_pass();
render.set_view(prosperon.camera.transform);
render.set_projection_ortho({
l:-prosperon.camera.size.x/2,
r:prosperon.camera.size.x/2,
b:-prosperon.camera.size.y/2,
t:prosperon.camera.size.y/2
}, prosperon.camera.near,prosperon.camera.far);
render.viewport(prosperon.camera.view(), false);
2024-08-25 14:23:22 -05:00
if (render.draw_sprites) render.sprites();
if (render.draw_particles) draw_emitters();
prosperon.draw();
2024-10-06 17:18:18 -05:00
render.fillmask(0);
2024-10-17 17:23:33 -05:00
render.forceflush();
2024-10-03 17:36:29 -05:00
2024-10-17 17:23:33 -05:00
render.set_projection_ortho({
l:0,
r:prosperon.camera.size.x,
b:-prosperon.camera.size.y,
t:0
},-1,1);
render.set_view(unit_transform);
if (render.draw_hud) prosperon.hud();
render.forceflush();
2024-09-09 18:55:07 -05:00
2024-10-17 17:23:33 -05:00
render.set_projection_ortho({
l:0,
r:window.size.x,
b:-window.size.y,
t:0
},-1,1);
render.viewport({
t:0,
height:window.size.y,
width:window.size.x,
l:0
}, false);
prosperon.app();
2024-10-19 18:37:27 -05:00
render.forceflush();
2024-10-02 09:55:32 -05:00
profile.report("imgui");
2024-09-26 11:36:09 -05:00
if (debug.show) imgui_fn();
2024-10-02 09:55:32 -05:00
profile.endreport("imgui");
render.end_pass();
2024-09-26 19:28:54 -05:00
render.commit();
2024-09-29 06:10:42 -05:00
profile.report_frame(profile.secs(profile.now()) - frame_t);
2024-09-26 11:36:09 -05:00
2024-09-06 22:47:04 -05:00
endframe();
2024-09-26 11:36:09 -05:00
};
prosperon.process = function process() {
2024-10-02 09:55:32 -05:00
profile.report("frame");
var dt = profile.secs(profile.now()) - frame_t;
frame_t = profile.secs(profile.now());
2024-09-24 17:07:32 -05:00
var sst = profile.now();
2024-09-26 11:36:09 -05:00
2024-08-06 14:23:21 -05:00
/* debugging: check for gc */
profile.print_gc();
var cycles = os.check_cycles();
if (cycles) say(cycles);
2024-10-02 09:55:32 -05:00
profile.report("app update");
prosperon.appupdate(dt);
2024-10-02 09:55:32 -05:00
profile.endreport("app update");
2024-10-02 09:55:32 -05:00
profile.report("input");
input.procdown();
2024-10-02 09:55:32 -05:00
profile.endreport("input");
if (sim.mode === "play" || sim.mode === "step") {
2024-10-02 09:55:32 -05:00
profile.report("update");
prosperon.update(dt * game.timescale);
2024-08-25 14:23:22 -05:00
update_emitters(dt * game.timescale);
2024-09-30 04:36:53 -05:00
os.update_timers(dt * game.timescale);
2024-10-02 09:55:32 -05:00
profile.endreport("update");
if (sim.mode === "step") sim.pause();
2024-09-24 17:07:32 -05:00
}
2024-09-26 11:36:09 -05:00
profile.pushdata(profile.data.cpu.scripts, profile.now() - sst);
2024-09-24 17:07:32 -05:00
sst = profile.now();
2024-09-24 17:07:32 -05:00
if (sim.mode === "play" || sim.mode === "step") {
2024-10-02 09:55:32 -05:00
profile.report("physics");
physlag += dt;
while (physlag > physics.delta) {
physlag -= physics.delta;
prosperon.phys2d_step(physics.delta * game.timescale);
prosperon.physupdate(physics.delta * game.timescale);
}
2024-10-02 09:55:32 -05:00
profile.endreport("physics");
2024-09-26 11:36:09 -05:00
profile.pushdata(profile.data.cpu.physics, profile.now() - sst);
2024-09-24 17:07:32 -05:00
sst = profile.now();
}
2024-10-02 09:55:32 -05:00
profile.report("render");
prosperon.render();
2024-10-02 09:55:32 -05:00
profile.endreport("render");
2024-09-26 11:36:09 -05:00
profile.pushdata(profile.data.cpu.render, profile.now() - sst);
2024-10-02 09:55:32 -05:00
profile.endreport('frame');
2024-09-24 14:12:59 -05:00
profile.capture_data();
2024-09-26 11:36:09 -05:00
};
2024-09-26 11:36:09 -05:00
return { render };