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
} ,
* /
2024-02-29 13:54:33 -06:00
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-02-29 13:54:33 -06:00
} ;
2024-07-11 14:25:45 -05:00
var cur = { } ;
2024-09-27 10:19:05 -05:00
cur . images = [ ] ;
2024-10-02 11:56:33 -05:00
cur . samplers = [ ] ;
2024-07-11 14:25:45 -05:00
2024-10-03 09:31:06 -05:00
/ * A p i p e l i n e d e f i n e s t h e c h a r a c t e r i s t i c s o f t h e i n c o m i n g d r a w .
{
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 ) ;
2024-10-08 02:46:59 -05:00
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 ) ;
2024-08-02 20:52:50 -05:00
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 ;
2024-08-02 20:52:50 -05:00
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 ;
2024-10-02 11:56:33 -05:00
cur . samplers . length = 0 ;
2024-07-11 14:25:45 -05:00
if ( ! cur . shader . fs . images ) return ;
2024-10-02 11:56:33 -05:00
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" ) ) ;
2024-10-02 11:56:33 -05:00
}
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 ;
2024-05-15 14:16:05 -05:00
var shaderlang = {
2024-09-26 11:36:09 -05:00
macos : "metal_macos" ,
windows : "hlsl5" ,
linux : "glsl430" ,
web : "wgsl" ,
ios : "metal_ios" ,
} ;
2024-05-15 14:16:05 -05:00
var attr _map = {
a _pos : 0 ,
2024-09-26 11:36:09 -05:00
a _uv : 1 ,
2024-05-15 14:16:05 -05:00
a _norm : 2 ,
2024-08-09 14:39:31 -05:00
a _joint : 3 ,
2024-05-15 14:16:05 -05:00
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-05-15 14:16:05 -05:00
2024-09-26 11:36:09 -05:00
render . poly _prim = function poly _prim ( verts ) {
2024-05-15 14:16:05 -05:00
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 ;
2024-05-15 14:16:05 -05:00
for ( var i = 2 ; i < verts . length ; i ++ ) {
index . push ( 0 ) ;
2024-09-26 11:36:09 -05:00
index . push ( i - 1 ) ;
2024-05-15 14:16:05 -05:00
index . push ( i ) ;
}
2024-09-26 11:36:09 -05:00
2024-05-15 14:16:05 -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-05-15 14:16:05 -05:00
} ;
2024-09-26 11:36:09 -05:00
} ;
2024-05-15 14:16:05 -05:00
2024-08-02 20:52:50 -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 ) ;
} ,
} ;
2024-08-02 20:52:50 -05:00
function set _global _uni ( uni , stage ) {
2024-09-26 11:36:09 -05:00
uni _globals [ uni . name ] ? . ( stage , uni . slot ) ;
2024-05-15 14:16:05 -05:00
}
2024-07-14 16:09:50 -05:00
2024-07-22 15:40:58 -05:00
var shader _cache = { } ;
2024-08-07 16:55:08 -05:00
var shader _times = { } ;
2024-08-02 20:52:50 -05:00
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-08-02 20:52:50 -05:00
}
2024-07-25 17:53:53 -05:00
2024-09-26 11:36:09 -05:00
render . hotreload = function ( ) {
2024-08-07 16:55:08 -05:00
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-05-15 14:16:05 -05:00
}
2024-09-26 11:36:09 -05:00
} ;
2024-08-07 16:55:08 -05:00
2024-09-26 11:36:09 -05:00
function create _shader _obj ( file ) {
2024-05-15 14:16:05 -05:00
var files = [ file ] ;
2024-08-07 16:55:08 -05:00
var out = ".prosperon/tmp.shader" ;
var shader = io . slurp ( file ) ;
2024-05-30 17:12:32 -05:00
2024-05-15 14:16:05 -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
2024-05-15 14:16:05 -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-05-15 14:16:05 -05:00
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-05-15 14:16:05 -05:00
}
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-15 14:16:05 -05:00
}
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-08-07 16:55:08 -05:00
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 ;
2024-08-07 16:55:08 -05:00
compiled . source = shader ;
2024-09-26 11:36:09 -05:00
2024-08-07 16:55:08 -05:00
return compiled ;
}
2024-10-03 17:36:29 -05:00
function make _shader ( shader , pipe ) {
2024-08-07 16:55:08 -05:00
if ( shader _cache [ shader ] ) return shader _cache [ shader ] ;
2024-09-26 11:36:09 -05:00
2024-08-07 16:55:08 -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
2024-08-07 16:55:08 -05:00
var shaderobj = json . decode ( io . slurp ( writejson ) ) ;
var obj = shaderobj [ os . sys ( ) ] ;
2024-10-03 17:36:29 -05:00
2024-08-07 16:55:08 -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
2024-08-07 16:55:08 -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 ( ) ] ;
2024-05-15 14:16:05 -05:00
2024-08-07 16:55:08 -05:00
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
2024-05-15 14:16:05 -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-05-15 14:16:05 -05:00
} ;
2024-08-02 20:52:50 -05:00
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-08-02 20:52:50 -05:00
}
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 ) ;
2024-05-15 14:16:05 -05:00
for ( var p in shader . vs . unimap ) {
2024-08-02 20:52:50 -05:00
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 } ` ) ;
2024-05-15 14:16:05 -05:00
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 ;
}
2024-05-15 14:16:05 -05:00
shader _unisize [ s . size ] ( 0 , s . slot , material [ p ] ) ;
}
2024-09-26 11:36:09 -05:00
2024-05-15 14:16:05 -05:00
for ( var p in shader . fs . unimap ) {
2024-08-02 20:52:50 -05:00
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 } ` ) ;
2024-05-15 14:16:05 -05:00
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
2024-10-02 20:14:45 -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-05-15 14:16:05 -05:00
}
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 ) {
2024-10-08 02:46:59 -05:00
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 ;
2024-10-02 11:56:33 -05:00
cur . bind . samplers = cur . samplers ;
2024-09-27 10:19:05 -05:00
render . setbind ( cur . bind ) ;
return ;
2024-10-08 02:46:59 -05:00
}
2024-10-02 11:56:33 -05:00
/ * i f ( b c a c h e . h a s ( c u r . s h a d e r ) & & b c a c h e . g e t ( c u r . s h a d e r ) . h a s ( m e s h ) ) {
2024-09-27 10:19:05 -05:00
cur . bind = bcache . get ( cur . shader ) . get ( mesh ) ;
cur . bind . images = cur . images ;
2024-10-02 11:56:33 -05:00
cur . bind . samplers = cur . samplers ;
2024-09-27 10:19:05 -05:00
if ( ssbo )
cur . bind . ssbo = [ ssbo ] ;
render . setbind ( cur . bind ) ;
return ;
2024-10-02 11:56:33 -05:00
} * /
var bind = { } ; / * i f ( ! b c a c h e . h a s ( c u r . s h a d e r ) ) b c a c h e . s e t ( c u r . s h a d e r , n e w W e a k M a p ( ) ) ;
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 ;
2024-05-15 14:16:05 -05:00
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 ) {
2024-05-15 14:16:05 -05:00
bind . index = mesh . index ;
bind . count = mesh . count ;
2024-09-26 11:36:09 -05:00
} else bind . count = mesh . verts ;
2024-05-15 14:16:05 -05:00
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 ;
2024-10-02 11:56:33 -05:00
bind . samplers = cur . samplers ;
2024-07-11 14:25:45 -05:00
render . setbind ( cur . bind ) ;
2024-09-26 11:36:09 -05:00
2024-05-15 14:16:05 -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 ( ) {
2024-08-02 20:52:50 -05:00
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 ) ;
2024-08-02 20:52:50 -05:00
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
2024-08-07 16:55:08 -05:00
render . circle = function render _circle ( pos , radius , color , inner _radius = 1 ) {
2024-10-06 17:18:18 -05:00
check _flush ( ) ;
2024-08-07 16:55:08 -05:00
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 ,
2024-08-07 16:55:08 -05:00
inner _r : inner _radius ,
2024-06-07 00:43:15 -05:00
coord : pos ,
2024-08-07 16:55:08 -05:00
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
} ;
2024-08-07 16:55:08 -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
2024-08-02 20:52:50 -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 ) ;
2024-08-02 20:52:50 -05:00
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 ( ) {
2024-08-02 20:52:50 -05:00
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 ) {
2024-08-02 20:52:50 -05:00
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 = [ ] ;
2024-08-02 20:52:50 -05:00
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 ( ) {
2024-08-02 20:52:50 -05:00
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 ,
2024-08-02 20:52:50 -05:00
} ;
poly _cache . push ( e ) ;
return e ;
}
2024-09-26 11:36:09 -05:00
var e = poly _cache [ poly _idx - 1 ] ;
2024-08-02 20:52:50 -05:00
e . transform . unit ( ) ;
return e ;
}
2024-09-26 11:36:09 -05:00
function flush _poly ( ) {
2024-08-02 20:52:50 -05:00
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 ) ;
2024-08-02 20:52:50 -05:00
poly _idx = 0 ;
2024-07-23 12:02:46 -05:00
}
2024-08-02 20:52:50 -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 ;
}
2024-08-02 20:52:50 -05:00
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
2024-08-02 20:52:50 -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
2024-08-02 20:52:50 -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
} ;
2024-08-02 20:52:50 -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 ] ;
2024-08-02 20:52:50 -05:00
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 ] ;
2024-08-02 20:52:50 -05:00
poly . color = color ;
2024-10-06 17:18:18 -05:00
queued _shader = shader ;
queued _pipe = pipe ;
2024-10-08 02:46:59 -05:00
check _flush ( flush _poly ) ;
2024-05-16 08:21:13 -05:00
} ;
2024-09-26 11:36:09 -05:00
2024-08-02 20:52:50 -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 ] ;
2024-08-02 20:52:50 -05:00
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-02 20:14:45 -05:00
{
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 ) ;
2024-10-02 20:14:45 -05:00
var t = os . make _transform ( ) ;
2024-10-17 17:23:33 -05:00
t . trs ( pos , undefined , scale ) ;
2024-10-02 20:14:45 -05:00
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
2024-10-02 20:14:45 -05:00
} ) ;
render . draw ( shape . quad ) ;
}
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-17 17:23:33 -05:00
var size = [ rect . width ? rect . width : tex . width , rect . height ? rect . height : tex . height ] ;
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 ] ) ) ;
e . transform . trs ( pos , undefined , size . div ( [ tex . width , tex . height ] ) ) ;
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
// pos is the lower left corner, scale is the width and height
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-17 17:23:33 -05:00
2024-10-14 20:07:32 -05:00
var tex = image . texture ;
2024-10-17 17:23:33 -05:00
var size = [ rect . width ? rect . width : tex . width , rect . height ? rect . height : tex . height ] ;
2024-07-09 01:03:39 -05:00
var t = os . make _transform ( ) ;
t . pos = pos ;
2024-10-17 17:23:33 -05:00
t . scale = size . div ( [ tex . width , tex . height ] ) ;
slice = clay . normalizeSpacing ( slice ) ;
var border = [ slice . l / tex . width , slice . b / tex . height , slice . r / tex . width , slice . t / tex . height ] ;
2024-07-11 14:25:45 -05:00
render . use _shader ( slice9shader ) ;
2024-08-02 20:52:50 -05:00
set _model ( 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 ,
rect : [ 0 , 0 , 1 , 1 ] ,
2024-07-09 16:43:09 -05:00
border : border ,
2024-10-17 17:23:33 -05:00
scale : [ size . x / tex . width , size . y / tex . height ] ,
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
2024-08-02 20:52:50 -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 ) {
2024-08-02 20:52:50 -05:00
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-07-25 17:53:53 -05:00
2024-09-26 11:36:09 -05:00
var left = this . viewport [ 0 ] * window . size . x ;
var bottom = this . viewport [ 1 ] * window . size . y ;
2024-07-25 17:53:53 -05:00
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 ;
2024-07-25 17:53:53 -05:00
if ( usemode === "fit" )
if ( raspect < aspect ) usemode = "height" ;
else usemode = "width" ;
2024-09-26 11:36:09 -05:00
switch ( usemode ) {
2024-07-25 17:53:53 -05:00
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
} ;
2024-07-25 17:53:53 -05:00
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
}
2024-07-25 17:53:53 -05:00
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 ;
2024-07-25 17:53:53 -05:00
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 ;
2024-07-25 17:53:53 -05:00
return ret ;
}
2024-10-17 17:23:33 -05:00
return {
x : 0 ,
y : 0 ,
width : window . size . x ,
height : window . size . y
} ;
2024-07-25 17:53:53 -05:00
}
// pos is pixels on the screen, lower left[0,0]
2024-09-26 11:36:09 -05:00
function camscreen2world ( pos ) {
2024-07-25 17:53:53 -05:00
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 ) ;
2024-07-25 17:53:53 -05:00
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-07-25 17:53:53 -05:00
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 ( ) ;
2024-07-25 17:53:53 -05:00
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-07-25 17:53:53 -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-07-25 17:53:53 -05:00
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 ( ) {
2024-07-25 17:53:53 -05:00
var cam = world . spawn ( ) ;
2024-10-17 17:23:33 -05:00
cam . near = 1 ;
cam . far = - 1000 ;
2024-07-25 17:53:53 -05:00
cam . ortho = true ;
2024-10-17 17:23:33 -05:00
cam . viewport = [ 0 , 0 , 1 , 1 ] ; // normalized screen coordinates of where to draw
2024-07-25 17:53:53 -05:00
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 ;
2024-07-25 17:53:53 -05:00
cam . view = camviewport ;
return cam ;
2024-09-26 11:36:09 -05:00
} ;
2024-07-25 17:53:53 -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
2024-10-02 11:56:33 -05:00
// figure out the highest resolution we can render at that's an integer
2024-10-17 17:23:33 -05:00
/ * v a r b a s e s i z e = p r o s p e r o n . c a m e r a . s i z e . s l i c e ( ) ;
2024-10-02 11:56:33 -05:00
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 ( ) ;
2024-07-25 17:53:53 -05:00
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-07-25 17:53:53 -05:00
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" ) ;
2024-07-25 17:53:53 -05:00
render . end _pass ( ) ;
2024-09-26 19:28:54 -05:00
2024-07-25 17:53:53 -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
} ;
2024-07-25 17:53:53 -05:00
2024-08-02 20:52:50 -05:00
prosperon . process = function process ( ) {
2024-10-02 09:55:32 -05:00
profile . report ( "frame" ) ;
2024-07-25 17:53:53 -05:00
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" ) ;
2024-07-25 17:53:53 -05:00
prosperon . appupdate ( dt ) ;
2024-10-02 09:55:32 -05:00
profile . endreport ( "app update" ) ;
2024-07-25 17:53:53 -05:00
2024-10-02 09:55:32 -05:00
profile . report ( "input" ) ;
2024-07-25 17:53:53 -05:00
input . procdown ( ) ;
2024-10-02 09:55:32 -05:00
profile . endreport ( "input" ) ;
2024-07-25 17:53:53 -05:00
if ( sim . mode === "play" || sim . mode === "step" ) {
2024-10-02 09:55:32 -05:00
profile . report ( "update" ) ;
2024-07-25 17:53:53 -05:00
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" ) ;
2024-07-25 17:53:53 -05:00
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-07-25 17:53:53 -05:00
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" ) ;
2024-07-25 17:53:53 -05:00
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-07-25 17:53:53 -05:00
}
2024-10-02 09:55:32 -05:00
profile . report ( "render" ) ;
2024-07-25 17:53:53 -05:00
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-07-25 17:53:53 -05:00
2024-09-26 11:36:09 -05:00
return { render } ;