221 lines
7.8 KiB
Plaintext
221 lines
7.8 KiB
Plaintext
//------------------------------------------------------------------------------
|
|
// offscreen/main.jai
|
|
//
|
|
// Render to an offscreen rendertarget texture, and use this texture
|
|
// for rendering to the display.
|
|
//------------------------------------------------------------------------------
|
|
#import "Basic";
|
|
#import,dir "../../sokol/log"(USE_GL=USE_GL);
|
|
#import,dir "../../sokol/gfx"(USE_GL=USE_GL);
|
|
#import,dir "../../sokol/app"(USE_GL=USE_GL);
|
|
#import,dir "../../sokol/glue"(USE_GL=USE_GL);
|
|
#import,dir "../../sokol/shape"(USE_GL=USE_GL);
|
|
#load "../math.jai";
|
|
#load "./shader.jai";
|
|
|
|
OFFSCREEN_SAMPLE_COUNT :: 1;
|
|
|
|
state: struct {
|
|
offscreen: struct {
|
|
pass_action: sg_pass_action;
|
|
attachments: sg_attachments;
|
|
pip: sg_pipeline;
|
|
bind: sg_bindings;
|
|
};
|
|
default: struct {
|
|
pass_action: sg_pass_action;
|
|
pip: sg_pipeline;
|
|
bind: sg_bindings;
|
|
};
|
|
donut: sshape_element_range_t;
|
|
sphere: sshape_element_range_t;
|
|
rx, ry: float;
|
|
vertices: [4000]sshape_vertex_t;
|
|
indices: [24000]u16;
|
|
}
|
|
|
|
init :: () #c_call {
|
|
push_context,defer_pop;
|
|
|
|
sg_setup(*(sg_desc.{
|
|
environment = xx,force sglue_environment(),
|
|
logger = .{ func = slog_func },
|
|
}));
|
|
|
|
// default pass action: clear to blue-ish
|
|
state.default.pass_action = .{
|
|
colors[0] = .{ load_action = .CLEAR, clear_value = .{ 0.25, 0.45, 0.65, 1.0 } },
|
|
};
|
|
|
|
// offscreen pass action: clear to grey
|
|
state.offscreen.pass_action = .{
|
|
colors[0] = .{ load_action = .CLEAR, clear_value = .{ 0.25, 0.25, 0.25, 1.0 } },
|
|
};
|
|
|
|
// a render pass with one color- and one depth-attachment image
|
|
img_desc := sg_image_desc.{
|
|
render_target = true,
|
|
width = 256,
|
|
height = 256,
|
|
pixel_format = .RGBA8,
|
|
sample_count = OFFSCREEN_SAMPLE_COUNT,
|
|
};
|
|
color_img := sg_make_image(*img_desc);
|
|
img_desc.pixel_format = .DEPTH;
|
|
depth_img := sg_make_image(*img_desc);
|
|
state.offscreen.attachments = sg_make_attachments(*(sg_attachments_desc.{
|
|
colors[0] = .{ image = color_img },
|
|
depth_stencil = .{
|
|
image = depth_img,
|
|
},
|
|
}));
|
|
|
|
// a donut shape which is rendered into the offscreen render target, and
|
|
// a sphere shape which is rendered into the default framebuffer
|
|
buf := sshape_buffer_t.{
|
|
vertices = .{ buffer = .{ ptr = *state.vertices, size = size_of(type_of(state.vertices)) } },
|
|
indices = .{ buffer = .{ ptr = *state.indices, size = size_of(type_of(state.indices)) } },
|
|
};
|
|
|
|
buf = sshape_build_torus(*buf, *(sshape_torus_t.{
|
|
radius = 0.5,
|
|
ring_radius = 0.3,
|
|
sides = 20,
|
|
rings = 30,
|
|
}));
|
|
state.donut = sshape_element_range(*buf);
|
|
|
|
buf = sshape_build_sphere(*buf, *(sshape_sphere_t.{
|
|
radius = 0.5,
|
|
slices = 72,
|
|
stacks = 40,
|
|
}));
|
|
state.sphere = sshape_element_range(*buf);
|
|
|
|
vbuf := sg_make_buffer(xx,force *sshape_vertex_buffer_desc(*buf));
|
|
ibuf := sg_make_buffer(xx,force *sshape_index_buffer_desc(*buf));
|
|
|
|
// pipeline-state-object for offscreen-rendered donut, don't need texture coord here
|
|
offscreen_pip_desc := sg_pipeline_desc.{
|
|
shader = sg_make_shader(*offscreen_shader_desc(sg_query_backend())),
|
|
index_type = .UINT16,
|
|
cull_mode = .BACK,
|
|
sample_count = OFFSCREEN_SAMPLE_COUNT,
|
|
depth = .{
|
|
pixel_format = .DEPTH,
|
|
compare = .LESS_EQUAL,
|
|
write_enabled = true,
|
|
},
|
|
colors[0] = .{ pixel_format = .RGBA8 },
|
|
};
|
|
offscreen_pip_desc.layout.buffers[0] = xx,force sshape_vertex_buffer_layout_state();
|
|
offscreen_pip_desc.layout.attrs[ATTR_offscreen_position] = xx,force sshape_position_vertex_attr_state();
|
|
offscreen_pip_desc.layout.attrs[ATTR_offscreen_normal] = xx,force sshape_normal_vertex_attr_state();
|
|
state.offscreen.pip = sg_make_pipeline(*offscreen_pip_desc);
|
|
|
|
// and another pipeline-state-object for the default pass
|
|
default_pip_desc := sg_pipeline_desc.{
|
|
shader = sg_make_shader(*default_shader_desc(sg_query_backend())),
|
|
index_type = .UINT16,
|
|
cull_mode = .BACK,
|
|
depth = .{
|
|
compare = .LESS_EQUAL,
|
|
write_enabled = true,
|
|
},
|
|
};
|
|
default_pip_desc.layout.buffers[0] = xx,force sshape_vertex_buffer_layout_state();
|
|
default_pip_desc.layout.attrs[ATTR_default_position] = xx,force sshape_position_vertex_attr_state();
|
|
default_pip_desc.layout.attrs[ATTR_default_normal] = xx,force sshape_normal_vertex_attr_state();
|
|
default_pip_desc.layout.attrs[ATTR_default_texcoord0] = xx,force sshape_texcoord_vertex_attr_state();
|
|
state.default.pip = sg_make_pipeline(*default_pip_desc);
|
|
|
|
// a sampler object for sampling the render target as texture
|
|
smp := sg_make_sampler(*(sg_sampler_desc.{
|
|
min_filter = .LINEAR,
|
|
mag_filter = .LINEAR,
|
|
wrap_u = .REPEAT,
|
|
wrap_v = .REPEAT,
|
|
}));
|
|
|
|
// the resource bindings for rendering a non-textured cube into offscreen render target
|
|
state.offscreen.bind = .{
|
|
vertex_buffers[0] = vbuf,
|
|
index_buffer = ibuf,
|
|
};
|
|
|
|
// resource bindings to render a textured shape, using the offscreen render target as texture
|
|
state.default.bind = .{
|
|
vertex_buffers[0] = vbuf,
|
|
index_buffer = ibuf,
|
|
};
|
|
state.default.bind.images[IMG_tex] = color_img;
|
|
state.default.bind.samplers[SMP_smp] = smp;
|
|
}
|
|
|
|
frame :: () #c_call {
|
|
push_context,defer_pop;
|
|
|
|
t := cast(float) (sapp_frame_duration() * 60.0);
|
|
state.rx += 1.0 * t;
|
|
state.ry += 2.0 * t;
|
|
|
|
// the offscreen pass, rendering an rotating, untextured donut into a render target image
|
|
vs_params := Vs_Params.{
|
|
mvp = compute_mvp(rx = state.rx, ry = state.ry, aspect = 1.0, eye_dist = 2.5),
|
|
};
|
|
sg_begin_pass(*(sg_pass.{ action = state.offscreen.pass_action, attachments = state.offscreen.attachments }));
|
|
sg_apply_pipeline(state.offscreen.pip);
|
|
sg_apply_bindings(*state.offscreen.bind);
|
|
sg_apply_uniforms(UB_vs_params, *(sg_range.{ ptr = *vs_params, size = size_of(type_of(vs_params)) }));
|
|
sg_draw(state.donut.base_element, state.donut.num_elements, 1);
|
|
sg_end_pass();
|
|
|
|
// and the default-pass, rendering a rotating textured sphere which uses the
|
|
// previously rendered offscreen render-target as texture
|
|
vs_params = Vs_Params.{
|
|
mvp = compute_mvp(
|
|
rx = -state.rx * 0.25,
|
|
ry = state.ry * 0.25,
|
|
aspect = sapp_widthf() / sapp_heightf(),
|
|
eye_dist = 2.0,
|
|
),
|
|
};
|
|
sg_begin_pass(*(sg_pass.{ action = state.default.pass_action, swapchain = xx,force sglue_swapchain() }));
|
|
sg_apply_pipeline(state.default.pip);
|
|
sg_apply_bindings(*state.default.bind);
|
|
sg_apply_uniforms(UB_vs_params, *(sg_range.{ ptr = *vs_params, size = size_of(type_of(vs_params)) }));
|
|
sg_draw(state.sphere.base_element, state.sphere.num_elements, 1);
|
|
sg_end_pass();
|
|
|
|
sg_commit();
|
|
}
|
|
|
|
compute_mvp :: (rx: float, ry: float, aspect: float, eye_dist: float) -> Matrix4 {
|
|
proj := persp_mat4(fov = 45.0, aspect = aspect, near = 0.01, far = 10.0);
|
|
view := lookat_mat4(eye = .{ 0.0, 0.0, eye_dist }, center = .{}, up = .{ 0.0, 1.0, 0.0 });
|
|
view_proj := multiply_mat4(proj, view);
|
|
rxm := rotate_mat4(rx, .{ 1.0, 0.0, 0.0 });
|
|
rym := rotate_mat4(ry, .{ 0.0, 1.0, 0.0 });
|
|
model := multiply_mat4(rym, rxm);
|
|
mvp := multiply_mat4(view_proj, model);
|
|
return mvp;
|
|
}
|
|
|
|
cleanup :: () #c_call {
|
|
sg_shutdown();
|
|
}
|
|
|
|
main :: () {
|
|
sapp_run(*(sapp_desc.{
|
|
init_cb = init,
|
|
frame_cb = frame,
|
|
cleanup_cb = cleanup,
|
|
width = 800,
|
|
height = 600,
|
|
sample_count = 4,
|
|
window_title = "offscreen",
|
|
icon = .{ sokol_default = true },
|
|
logger = .{ func = slog_func },
|
|
}));
|
|
}
|