833 lines
20 KiB
Plaintext
833 lines
20 KiB
Plaintext
var prosperon = {}
|
||
|
||
// This file is hard coded for the SDL GPU case
|
||
|
||
var video = use('sdl_video')
|
||
var surface = use('surface')
|
||
var sdl_gpu = use('sdl_gpu')
|
||
var io = use('io')
|
||
var geometry = use('geometry')
|
||
var blob = use('blob')
|
||
|
||
var os = use('os')
|
||
|
||
var win_size = {width:500,height:500}
|
||
|
||
function makeOrthoMetal(l,r,b,t,n,f){
|
||
return [
|
||
2/(r-l), 0, 0, 0,
|
||
0, 2/(t-b), 0, 0,
|
||
0, 0, 1/(f-n), 0,
|
||
-(r+l)/(r-l), -(t+b)/(t-b), -n/(f-n), 1
|
||
]
|
||
}
|
||
|
||
function make_camera_pblob(camera) {
|
||
def zoom = camera.zoom;
|
||
// Use surface dimensions if rendering to a surface, otherwise window dimensions
|
||
def cw = camera.surface ? camera.surface.width : win_size.width;
|
||
def ch = camera.surface ? camera.surface.height : win_size.height;
|
||
// how big is the world window?
|
||
def world_w = cw / zoom;
|
||
def world_h = ch / zoom;
|
||
|
||
// compute world‐space bounds so that camera.pos lands at the anchor
|
||
// anchor.x = 0 → origin at left; 1 → origin at right
|
||
def l = camera.pos[0] - camera.anchor[0] * world_w;
|
||
def b = camera.pos[1] - camera.anchor[1] * world_h;
|
||
def r = l + world_w;
|
||
def t = b + world_h;
|
||
|
||
// now build the Metal/Vulkan‐style ortho (z ∈ [0,1])
|
||
def mat = makeOrthoMetal(l, r, b, t, 0, 1);
|
||
return geometry.array_blob(mat);
|
||
}
|
||
|
||
|
||
var driver = "vulkan"
|
||
switch(os.platform()) {
|
||
case "Linux":
|
||
driver = "vulkan"
|
||
break
|
||
case "Windows":
|
||
// driver = "direct3d12"
|
||
driver = "vulkan"
|
||
break
|
||
case "macOS":
|
||
driver = "metal"
|
||
break
|
||
}
|
||
|
||
var default_sampler = {
|
||
min_filter: "linear",
|
||
mag_filter: "linear",
|
||
mipmap: "nearest",
|
||
u: "repeat",
|
||
v: "repeat",
|
||
w: "repeat",
|
||
mip_bias: 0,
|
||
max_anisotropy: 0,
|
||
compare_op: "none",
|
||
min_lod: 0,
|
||
max_lod: 10,
|
||
anisotropy: false,
|
||
compare: false
|
||
};
|
||
|
||
var main_color = {
|
||
type:"2d",
|
||
format: "rgba8",
|
||
layers: 1,
|
||
mip_levels: 1,
|
||
samples: 0,
|
||
sampler:true,
|
||
color_target:true
|
||
};
|
||
|
||
var main_depth = {
|
||
type: "2d",
|
||
format: "d32 float s8",
|
||
layers:1,
|
||
mip_levels:1,
|
||
samples:0,
|
||
sampler:true,
|
||
depth_target:true
|
||
};
|
||
|
||
var default_window = {
|
||
// Basic properties
|
||
title: "Prosperon Window",
|
||
width: 640,
|
||
height: 360,
|
||
|
||
// Position - can be numbers or "centered"
|
||
x: null, // SDL_WINDOWPOS_null by default
|
||
y: null, // SDL_WINDOWPOS_null by default
|
||
|
||
// Window behavior flags
|
||
resizable: true,
|
||
fullscreen: false,
|
||
hidden: false,
|
||
borderless: false,
|
||
alwaysOnTop: false,
|
||
minimized: false,
|
||
maximized: false,
|
||
|
||
// Input grabbing
|
||
mouseGrabbed: false,
|
||
keyboardGrabbed: false,
|
||
|
||
// Display properties
|
||
highPixelDensity: false,
|
||
transparent: false,
|
||
opacity: 1.0, // 0.0 to 1.0
|
||
|
||
// Focus behavior
|
||
notFocusable: false,
|
||
|
||
// Special window types (mutually exclusive)
|
||
utility: false, // Utility window (not in taskbar)
|
||
tooltip: false, // Tooltip window (requires parent)
|
||
popupMenu: false, // Popup menu window (requires parent)
|
||
|
||
// Graphics API flags (var SDL choose if not specified)
|
||
opengl: false, // Force OpenGL context
|
||
vulkan: false, // Force Vulkan context
|
||
metal: false, // Force Metal context (macOS)
|
||
|
||
// Advanced properties
|
||
parent: null, // Parent window for tooltips/popups/modal
|
||
modal: false, // Modal to parent window (requires parent)
|
||
externalGraphicsContext: false, // Use external graphics context
|
||
|
||
// Input handling
|
||
textInput: true, // Enable text input on creation
|
||
}
|
||
|
||
var win_config = arg[0] || {}
|
||
win_config.__proto__ = default_window
|
||
|
||
win_config.metal = true
|
||
|
||
var window = new video.window(win_config)
|
||
var device = new sdl_gpu.gpu({
|
||
shaders_msl:true,
|
||
shaders_metallib:true,
|
||
name: "metal"
|
||
})
|
||
device.claim_window(window)
|
||
device.set_swapchain(window, 'sdr', 'vsync')
|
||
|
||
var shader_type = device.shader_format()[0]
|
||
shader_type = 'msl'
|
||
|
||
var sampler_cache = {}
|
||
|
||
function canonicalize_sampler(desc) {
|
||
if (desc == true)
|
||
return json.encode(default_sampler)
|
||
|
||
var sampler_obj = {}
|
||
sampler_obj.__proto__ = default_sampler
|
||
|
||
if (typeof desc == 'object') {
|
||
for (var key in desc) {
|
||
if (desc.hasOwnProperty(key)) {
|
||
sampler_obj[key] = desc[key]
|
||
}
|
||
}
|
||
}
|
||
|
||
var keys = Object.keys(sampler_obj).sort()
|
||
var canonical = {}
|
||
for (var i = 0; i < keys.length; i++)
|
||
canonical[keys[i]] = sampler_obj[keys[i]]
|
||
|
||
return json.encode(canonical)
|
||
}
|
||
|
||
function get_sampler(desc) {
|
||
var key = canonicalize_sampler(desc)
|
||
|
||
if (!sampler_cache[key]) {
|
||
var sampler_config = json.decode(key)
|
||
sampler_cache[key] = new sdl_gpu.sampler(device, sampler_config)
|
||
}
|
||
|
||
return sampler_cache[key]
|
||
}
|
||
|
||
var std_sampler = get_sampler(true)
|
||
|
||
// Shader and pipeline cache
|
||
var shader_cache = {}
|
||
var pipeline_cache = {}
|
||
|
||
function upload(copypass, buffer, toblob)
|
||
{
|
||
stone(toblob)
|
||
var trans = new sdl_gpu.transfer_buffer(device, {
|
||
size: toblob.length/8,
|
||
usage:"upload"
|
||
})
|
||
|
||
trans.copy_blob(device, toblob)
|
||
|
||
copypass.upload_to_buffer({
|
||
transfer_buffer: trans,
|
||
offset:0
|
||
}, {
|
||
buffer: buffer,
|
||
offset: 0,
|
||
size: toblob.length/8
|
||
})
|
||
}
|
||
|
||
function make_shader(sh_file)
|
||
{
|
||
var file = `shaders/${shader_type}/${sh_file}.${shader_type}`
|
||
if (shader_cache[file]) return shader_cache[file]
|
||
var refl = json.decode(io.slurp(`shaders/reflection/${sh_file}.json`))
|
||
|
||
var shader = {
|
||
code: io.slurpbytes(file),
|
||
format: shader_type,
|
||
stage: sh_file.endsWith("vert") ? "vertex" : "fragment",
|
||
num_samplers: refl.separate_samplers ? refl.separate_samplers.length : 0,
|
||
num_textures: 0,
|
||
num_storage_buffers: refl.separate_storage_buffers ? refl.separate_storage_buffers.length : 0,
|
||
num_uniform_buffers: refl.ubos ? refl.ubos.length : 0,
|
||
entrypoint: shader_type == "msl" ? "main0" : "main"
|
||
}
|
||
|
||
shader[GPU] = new sdl_gpu.shader(device, shader)
|
||
shader.reflection = refl;
|
||
shader_cache[file] = shader
|
||
shader.file = sh_file
|
||
return shader
|
||
}
|
||
|
||
var usepipe
|
||
function load_pipeline(config)
|
||
{
|
||
if (usepipe) return usepipe
|
||
config.vertex = make_shader(config.vertex)[GPU]
|
||
config.fragment = make_shader(config.fragment)[GPU]
|
||
usepipe = new sdl_gpu.graphics_pipeline(device, config)
|
||
log.console(usepipe)
|
||
return usepipe
|
||
}
|
||
|
||
// Initialize ImGui with the window and renderer
|
||
//imgui.init(window, renderer)
|
||
//imgui.newframe()
|
||
|
||
var io = use('io');
|
||
var rasterize = use('rasterize');
|
||
var time = use('time')
|
||
var tilemap = use('tilemap')
|
||
|
||
var res = use('resources')
|
||
var input = use('input')
|
||
|
||
var graphics = use('graphics')
|
||
|
||
var camera = {}
|
||
|
||
prosperon.scissor = function(rect) {
|
||
device.scissor(rect)
|
||
}
|
||
|
||
// Pipeline component definitions
|
||
var default_depth_state = {
|
||
compare: "always", // never/less/equal/less_equal/greater/not_equal/greater_equal/always
|
||
test: false,
|
||
write: false,
|
||
bias: 0,
|
||
bias_slope_scale: 0,
|
||
bias_clamp: 0
|
||
}
|
||
|
||
var default_stencil_state = {
|
||
compare: "always", // never/less/equal/less_equal/greater/neq/greq/always
|
||
fail: "keep", // keep/zero/replace/incr_clamp/decr_clamp/invert/incr_wrap/decr_wrap
|
||
depth_fail: "keep",
|
||
pass: "keep"
|
||
}
|
||
|
||
var disabled_blend_state = {
|
||
enabled: false,
|
||
src_rgb: "zero",
|
||
dst_rgb: "zero",
|
||
op_rgb: "add",
|
||
src_alpha: "one",
|
||
dst_alpha: "zero",
|
||
op_alpha: "add"
|
||
}
|
||
|
||
var alpha_blend_state = {
|
||
enabled: true,
|
||
src_rgb: "src_alpha",
|
||
dst_rgb: "one_minus_src_alpha",
|
||
op_rgb: "add",
|
||
src_alpha: "one",
|
||
dst_alpha: "one_minus_src_alpha",
|
||
op_alpha: "add"
|
||
}
|
||
|
||
var default_multisample_state = {
|
||
count: 1,
|
||
mask: 0xFFFFFFFF,
|
||
domask: false
|
||
}
|
||
|
||
// Helper function to create pipeline config
|
||
function create_pipeline_config(options) {
|
||
var config = {
|
||
vertex: options.vertex,
|
||
fragment: options.fragment,
|
||
primitive: options.primitive || "triangle",
|
||
fill: options.fill ?? true,
|
||
depth: options.depth || default_depth_state,
|
||
stencil: {
|
||
enabled: options.stencil_enabled ?? false,
|
||
front: options.stencil_front || default_stencil_state,
|
||
back: options.stencil_back || default_stencil_state,
|
||
test: options.stencil_test ?? false,
|
||
compare_mask: options.stencil_compare_mask ?? 0,
|
||
write_mask: options.stencil_write_mask ?? 0
|
||
},
|
||
blend: options.blend || disabled_blend_state,
|
||
cull: options.cull || "none",
|
||
face: options.face || "cw",
|
||
alpha_to_coverage: options.alpha_to_coverage ?? false,
|
||
multisample: options.multisample || default_multisample_state,
|
||
label: options.label || "pipeline",
|
||
target: options.target || {}
|
||
}
|
||
|
||
// Ensure target has required properties
|
||
if (!config.target.color_targets) {
|
||
config.target.color_targets = [{
|
||
format: "rgba8",
|
||
blend: config.blend
|
||
}]
|
||
}
|
||
|
||
return config
|
||
}
|
||
|
||
var gameactor
|
||
|
||
var images = {}
|
||
|
||
var renderer_commands = []
|
||
|
||
///// input /////
|
||
var input_cb
|
||
var input_rate = 1/60
|
||
function poll_input() {
|
||
var evs = input.get_events()
|
||
|
||
// Filter and transform events
|
||
if (Array.isArray(evs)) {
|
||
var filteredEvents = []
|
||
// var wantMouse = imgui.wantmouse()
|
||
// var wantKeys = imgui.wantkeys()
|
||
var wantMouse = false
|
||
var wantKeys = false
|
||
|
||
for (var i = 0; i < evs.length; i++) {
|
||
var event = evs[i]
|
||
var shouldInclude = true
|
||
|
||
// Filter mouse events if ImGui wants mouse input
|
||
if (wantMouse && (event.type == 'mouse_motion' ||
|
||
event.type == 'mouse_button_down' ||
|
||
event.type == 'mouse_button_up' ||
|
||
event.type == 'mouse_wheel')) {
|
||
shouldInclude = false
|
||
}
|
||
|
||
// Filter keyboard events if ImGui wants keyboard input
|
||
if (wantKeys && (event.type == 'key_down' ||
|
||
event.type == 'key_up' ||
|
||
event.type == 'text_input' ||
|
||
event.type == 'text_editing')) {
|
||
shouldInclude = false
|
||
}
|
||
|
||
if (shouldInclude) {
|
||
// Transform mouse coordinates from window to renderer coordinates
|
||
if (event.pos && (event.type == 'mouse_motion' ||
|
||
event.type == 'mouse_button_down' ||
|
||
event.type == 'mouse_button_up' ||
|
||
event.type == 'mouse_wheel')) {
|
||
// Convert window coordinates to renderer logical coordinates
|
||
// var logicalPos = renderer.coordsFromWindow(event.pos)
|
||
// event.pos = logicalPos
|
||
}
|
||
// Handle drop events which also have position
|
||
if (event.pos && (event.type == 'drop_file' ||
|
||
event.type == 'drop_text' ||
|
||
event.type == 'drop_position')) {
|
||
// var logicalPos = renderer.coordsFromWindow(event.pos)
|
||
// event.pos = logicalPos
|
||
}
|
||
|
||
// Handle window events
|
||
if (event.type == 'window_pixel_size_changed') {
|
||
win_size.width = event.width
|
||
win_size.height = event.height
|
||
}
|
||
|
||
if (event.type == 'quit')
|
||
$_.stop()
|
||
|
||
if (event.type.includes('key')) {
|
||
if (event.key)
|
||
event.key = input.keyname(event.key)
|
||
}
|
||
|
||
if (event.type.startsWith('mouse_') && event.pos && event.pos.y)
|
||
event.pos.y = -event.pos.y + win_size.height
|
||
// event.pos.y = -event.pos.y + logical.height
|
||
|
||
filteredEvents.push(event)
|
||
}
|
||
}
|
||
|
||
evs = filteredEvents
|
||
}
|
||
|
||
input_cb(evs)
|
||
$_.delay(poll_input, input_rate)
|
||
}
|
||
|
||
prosperon.input = function(fn)
|
||
{
|
||
input_cb = fn
|
||
poll_input()
|
||
}
|
||
|
||
var sprite_pipeline = {
|
||
vertex: "sprite.vert",
|
||
fragment: "sprite.frag",
|
||
cull: "none",
|
||
target: {
|
||
color_targets: [
|
||
{format: device.swapchain_format(window), blend:alpha_blend_state}
|
||
],
|
||
},
|
||
vertex_buffer_descriptions: [ { slot:0, input_rate: "vertex", instance_step_rate: 0,
|
||
pitch: 8},
|
||
{slot:1, input_rate:"vertex", instance_step_rate: 0, pitch: 8},
|
||
{slot:2, input_rate:"vertex", instance_step_rate: 0, pitch: 16}
|
||
],
|
||
vertex_attributes: [
|
||
{ location: 0, buffer_slot: 0, format: "float2", offset: 0},
|
||
{ location: 1, buffer_slot: 1, format: "float2", offset: 0},
|
||
{ location: 2, buffer_slot: 2, format: "float4", offset: 0}
|
||
],
|
||
primitive: "triangle",
|
||
blend: alpha_blend_state
|
||
}
|
||
|
||
var pipey = load_pipeline(sprite_pipeline)
|
||
|
||
var GPU = Symbol()
|
||
|
||
var cur_cam
|
||
var cmd_fns = {}
|
||
cmd_fns.camera = function(cmd)
|
||
{
|
||
if (cmd.camera.surface && !cmd.camera.surface[GPU]) {
|
||
cmd.camera.surface[GPU] = new sdl_gpu.texture(device, cmd.camera.surface)
|
||
// Store the sampler description on the texture for later use
|
||
if (cmd.camera.surface.sampler != null) {
|
||
cmd.camera.surface[GPU].sampler_desc = cmd.camera.surface.sampler
|
||
}
|
||
}
|
||
draw_queue.push(cmd)
|
||
}
|
||
|
||
var new_tex = []
|
||
|
||
function get_img_gpu(surface)
|
||
{
|
||
if (!surface) return
|
||
|
||
var full_mip = Math.floor(Math.log2(Math.max(surface.width,surface.height))) + 1
|
||
var gpu = new sdl_gpu.texture(device, {
|
||
width: surface.width,
|
||
height: surface.height,
|
||
layers: 1,
|
||
mip_levels: full_mip,
|
||
samples: 0,
|
||
type: "2d",
|
||
format: "rgba8",
|
||
sampler: surface.sampler != null ? surface.sampler : true,
|
||
color_target: true
|
||
})
|
||
|
||
// Store the sampler description on the texture for later use
|
||
if (surface.sampler != null) {
|
||
gpu.sampler_desc = surface.sampler
|
||
}
|
||
|
||
var tbuf = new sdl_gpu.transfer_buffer(device, {
|
||
size: surface.pixels.length/8,
|
||
usage: "upload"
|
||
})
|
||
|
||
tbuf.copy_blob(device, surface.pixels)
|
||
|
||
copy_pass.upload_to_texture({
|
||
transfer_buffer: tbuf,
|
||
offset: 0,
|
||
pixels_per_row: surface.width,
|
||
rows_per_layer: surface.height,
|
||
}, {
|
||
texture: gpu,
|
||
mip_level: 0,
|
||
layer: 0,
|
||
x: 0, y: 0, z: 0,
|
||
w: surface.width,
|
||
h: surface.height,
|
||
d: 1
|
||
}, false);
|
||
|
||
new_tex.push(gpu)
|
||
|
||
return gpu
|
||
}
|
||
|
||
var pos_blob
|
||
var uv_blob
|
||
var color_blob
|
||
var index_blob
|
||
|
||
var draw_queue = []
|
||
var index_count = 0
|
||
var vertex_count = 0
|
||
|
||
function render_geom(geom, img)
|
||
{
|
||
if (!img[GPU]) {
|
||
if (img.surface)
|
||
img[GPU] = get_img_gpu(img.surface)
|
||
else
|
||
img[GPU] = get_img_gpu(img.cpu)
|
||
|
||
if (!img[GPU]) return
|
||
}
|
||
|
||
pos_blob.write_blob(geom.xy)
|
||
uv_blob.write_blob(geom.uv)
|
||
color_blob.write_blob(geom.color)
|
||
index_blob.write_blob(geom.indices)
|
||
|
||
draw_queue.push({
|
||
pipeline:pipey,
|
||
texture: img[GPU],
|
||
num_indices: geom.num_indices,
|
||
first_index: index_count,
|
||
vertex_offset: vertex_count
|
||
})
|
||
|
||
vertex_count += (geom.xy.length/8) / 8
|
||
index_count += geom.num_indices
|
||
}
|
||
|
||
cmd_fns.draw_image = function(cmd)
|
||
{
|
||
var img
|
||
if (typeof cmd.image == 'string')
|
||
img = graphics.texture(cmd.image)
|
||
else
|
||
img = cmd.image
|
||
|
||
cmd.rect.width ??= img.width
|
||
cmd.rect.height ??= img.height
|
||
|
||
var geom = geometry.make_rect_quad(cmd.rect)
|
||
geom.indices = geometry.make_quad_indices(1)
|
||
geom.num_indices = 6
|
||
|
||
render_geom(geom, img)
|
||
}
|
||
|
||
cmd_fns.draw_text = function(cmd)
|
||
{
|
||
if (!cmd.text || !cmd.pos) return
|
||
|
||
var font = graphics.get_font(cmd.font)
|
||
if (!font[GPU])
|
||
font[GPU] = get_img_gpu(font.surface)
|
||
var mesh = graphics.make_text_buffer(
|
||
cmd.text,
|
||
cmd.pos,
|
||
[cmd.material.color.r, cmd.material.color.g, cmd.material.color.b, cmd.material.color.a],
|
||
cmd.wrap || 0,
|
||
font
|
||
)
|
||
|
||
render_geom(mesh, font)
|
||
}
|
||
|
||
cmd_fns.tilemap = function(cmd)
|
||
{
|
||
var geometryCommands = cmd.tilemap.draw()
|
||
|
||
for (var geomCmd of geometryCommands) {
|
||
var img = graphics.texture(geomCmd.image)
|
||
if (!img) continue
|
||
|
||
render_geom(geomCmd.geometry, img)
|
||
}
|
||
}
|
||
|
||
cmd_fns.geometry = function(cmd)
|
||
{
|
||
if (typeof cmd.image == 'object') {
|
||
render_geom(cmd.geometry, cmd.image)
|
||
return
|
||
}
|
||
var img = graphics.texture(cmd.image)
|
||
if (!img) return
|
||
|
||
render_geom(cmd.geometry, img)
|
||
}
|
||
|
||
cmd_fns.draw_slice9 = function(cmd)
|
||
{
|
||
var img = graphics.texture(cmd.image)
|
||
if (!img) return
|
||
|
||
// Use the gpu_slice9 function from geometry module to generate the mesh
|
||
var slice_info = {
|
||
tile_top: true,
|
||
tile_bottom: true,
|
||
tile_left: true,
|
||
tile_right: true,
|
||
tile_center_x: true,
|
||
tile_center_y: true
|
||
}
|
||
|
||
// Convert single slice value to LRTB object if needed
|
||
var slice_lrtb = cmd.slice
|
||
if (typeof cmd.slice == 'number') {
|
||
slice_lrtb = {
|
||
l: cmd.slice,
|
||
r: cmd.slice,
|
||
t: cmd.slice,
|
||
b: cmd.slice
|
||
}
|
||
}
|
||
|
||
var mesh = geometry.slice9(img, cmd.rect, slice_lrtb, slice_info)
|
||
|
||
render_geom(mesh, img)
|
||
}
|
||
|
||
var copy_pass
|
||
|
||
prosperon.create_batch = function create_batch(draw_cmds, done) {
|
||
pos_blob = new blob
|
||
uv_blob = new blob
|
||
color_blob = new blob
|
||
index_blob = new blob
|
||
draw_queue = []
|
||
index_count = 0
|
||
vertex_count = 0
|
||
new_tex = []
|
||
|
||
var render_queue = device.acquire_cmd_buffer()
|
||
copy_pass = render_queue.copy_pass()
|
||
|
||
for (var cmd of draw_cmds)
|
||
if (cmd_fns[cmd.cmd])
|
||
cmd_fns[cmd.cmd](cmd)
|
||
|
||
var pos_buffer = new sdl_gpu.buffer(device,{ vertex:true, size:pos_blob.length/8});
|
||
var uv_buffer = new sdl_gpu.buffer(device,{ vertex:true, size:uv_blob.length/8});
|
||
var color_buffer = new sdl_gpu.buffer(device,{ vertex:true, size:color_blob.length/8});
|
||
var index_buffer = new sdl_gpu.buffer(device,{ index:true, size:index_blob.length/8});
|
||
|
||
upload(copy_pass, pos_buffer, pos_blob)
|
||
upload(copy_pass, uv_buffer, uv_blob)
|
||
upload(copy_pass, color_buffer, color_blob)
|
||
upload(copy_pass, index_buffer, index_blob)
|
||
|
||
copy_pass.end();
|
||
|
||
for (var g of new_tex)
|
||
render_queue.generate_mipmaps(g)
|
||
|
||
var render_pass
|
||
var render_target
|
||
|
||
for (var cmd of draw_queue) {
|
||
if (cmd.camera) {
|
||
if (!cmd.camera.surface && render_target != "swap") {
|
||
if (render_pass)
|
||
render_pass.end()
|
||
render_target = "swap"
|
||
render_pass = render_queue.swapchain_pass(window)
|
||
} else if (cmd.camera.surface && render_target != cmd.camera.surface) {
|
||
if (render_pass)
|
||
render_pass.end()
|
||
render_target = cmd.camera.surface
|
||
render_pass = render_queue.render_pass({
|
||
color_targets: [{
|
||
texture: cmd.camera.surface[GPU],
|
||
mip_level: 0,
|
||
layer: 0,
|
||
load: "clear",
|
||
clear_color: cmd.camera.background,
|
||
store: "store",
|
||
}]
|
||
})
|
||
}
|
||
render_pass.bind_pipeline(pipey)
|
||
render_pass.bind_buffers(0, [
|
||
{ buffer: pos_buffer, offset: 0 },
|
||
{ buffer: uv_buffer, offset: 0 },
|
||
{ buffer: color_buffer, offset: 0 }
|
||
])
|
||
|
||
render_pass.bind_index_buffer(
|
||
{ buffer: index_buffer, offset: 0 }, // the binding itself is in bytes
|
||
16 // 16 = Uint32 indices
|
||
);
|
||
var vpW, vpH
|
||
|
||
if (render_target == "swap") {
|
||
vpW = win_size.width
|
||
vpH = win_size.height
|
||
} else {
|
||
vpW = render_target.width
|
||
vpH = render_target.height
|
||
}
|
||
|
||
render_pass.viewport({
|
||
x: cmd.camera.viewport.x*vpW,
|
||
y: cmd.camera.viewport.y * vpH,
|
||
width: cmd.camera.viewport.width * vpW,
|
||
height: cmd.camera.viewport.height * vpH
|
||
})
|
||
|
||
cur_cam = make_camera_pblob(cmd.camera)
|
||
render_queue.push_vertex_uniform_data(0, cur_cam)
|
||
continue
|
||
}
|
||
// Use texture's sampler if it has one, otherwise use standard sampler
|
||
var sampler_to_use = std_sampler
|
||
if (cmd.texture && cmd.texture.sampler_desc) {
|
||
sampler_to_use = get_sampler(cmd.texture.sampler_desc)
|
||
}
|
||
render_pass.bind_samplers(false, 0, [{texture:cmd.texture, sampler: sampler_to_use}])
|
||
|
||
render_pass.draw_indexed(
|
||
cmd.num_indices,
|
||
1,
|
||
cmd.first_index,
|
||
cmd.vertex_offset,
|
||
0
|
||
)
|
||
}
|
||
|
||
render_pass.end()
|
||
render_queue.submit()
|
||
|
||
if (done) done()
|
||
}
|
||
|
||
////////// dmon hot reload ////////
|
||
function poll_file_changes() {
|
||
dmon.poll(e => {
|
||
if (e.action == 'modify' || e.action == 'create') {
|
||
// Check if it's an image file
|
||
var ext = e.file.split('.').pop().toLowerCase()
|
||
var imageExts = ['png', 'jpg', 'jpeg', 'gif', 'bmp', 'tga', 'webp', 'qoi', 'ase', 'aseprite']
|
||
|
||
if (imageExts.includes(ext)) {
|
||
// Try to find the full path for this image
|
||
var possiblePaths = [
|
||
e.file,
|
||
e.root + e.file,
|
||
res.find_image(e.file.split('/').pop().split('.')[0])
|
||
].filter(p => p)
|
||
|
||
for (var path of possiblePaths) {
|
||
graphics.tex_hotreload(path)
|
||
}
|
||
}
|
||
}
|
||
})
|
||
|
||
// Schedule next poll in 0.5 seconds
|
||
$_.delay(poll_file_changes, 0.5)
|
||
}
|
||
|
||
var dmon = use('dmon')
|
||
prosperon.dmon = function()
|
||
{
|
||
dmon.watch('.')
|
||
poll_file_changes()
|
||
}
|
||
|
||
var window_cmds = {
|
||
size(size) {
|
||
window.size = size
|
||
},
|
||
}
|
||
|
||
prosperon.set_window = function(config)
|
||
{
|
||
for (var c in config)
|
||
if (window_cmds[c]) window_cmds[c](config[c])
|
||
}
|
||
|
||
return prosperon
|