trueno/src/renderer_sokol/sprites.jai

102 lines
4.1 KiB
Plaintext

sprites_init :: () {
// Billboards and particles share the vertex shader, so they share attribute slots.
make :: (shader: sg_shader_desc, target: Pipeline_Target, blend := sg_blend_state.{}) -> sg_pipeline {
desc := pipeline_desc(shader, target, blend, depth_write = target != .VIEW_TRANSPARENT);
desc.layout.buffers[0].step_func = .PER_INSTANCE;
desc.layout.attrs[ATTR_sprite_center] = .{ format = .FLOAT3 };
desc.layout.attrs[ATTR_sprite_size] = .{ format = .FLOAT2 };
desc.layout.attrs[ATTR_sprite_uv_rect] = .{ format = .FLOAT4 };
desc.layout.attrs[ATTR_sprite_face] = .{ format = .FLOAT3 };
desc.layout.attrs[ATTR_sprite_color] = .{ format = .FLOAT4 };
desc.layout.attrs[ATTR_sprite_glow] = .{ format = .FLOAT };
return sg_make_pipeline(*desc);
}
backend := sg_query_backend();
sprites.pipelines[Kind.BILLBOARD] = make(sprite_shader_desc(backend), .VIEW_OPAQUE);
sprites.pipelines[Kind.PARTICLE_ALPHA] = make(particle_shader_desc(backend), .VIEW_TRANSPARENT, ALPHA_BLEND);
sprites.pipelines[Kind.PARTICLE_ADDITIVE] = make(particle_shader_desc(backend), .VIEW_TRANSPARENT, ADDITIVE_BLEND);
sprites.shadow_pipeline = make(sprite_shadow_shader_desc(backend), .SHADOW);
}
sprites_upload :: (scene: *Scene) {
array_reset_keeping_memory(*sprites.instances);
array_reset_keeping_memory(*sprites.batches);
draws : [..]Keyed(Instance);
draws.allocator = temp;
for view_index: 0..scene.views.count-1 {
array_reset_keeping_memory(*draws);
for scene.billboards {
if it.view.index != view_index continue;
array_add(*draws, .{key(.BILLBOARD, it.sheet), .{it.position, it.size, it.uv_rect, it.face, .{1, 1, 1, 1}, 0}});
}
for batch: scene.particles {
if batch.view.index != view_index continue;
kind := ifx batch.blend == .ADDITIVE then Kind.PARTICLE_ADDITIVE else .PARTICLE_ALPHA;
for batch.particles {
array_add(*draws, .{key(kind, batch.sheet), .{it.position, .{it.size, it.size}, it.uv_rect, BILLBOARD_FACE_CAMERA, it.color, it.glow}});
}
}
batch_instances(view_index, draws, *sprites.batches, *sprites.instances);
}
stream_upload(*sprites.stream, sprites.instances);
}
// Opaque sprites go before water, transparent ones after it. Only billboards cast shadows.
sprites_draw :: (pass: *Pass, transparent: bool) {
shadow := pass.kind == .SHADOW;
current : Kind = xx -1;
for sprites.batches {
if it.view_index != pass.view_index continue;
kind := cast(Kind) (it.key >> 32);
if (kind != .BILLBOARD) != transparent continue;
if kind != current {
current = kind;
sg_apply_pipeline(ifx shadow then sprites.shadow_pipeline else sprites.pipelines[kind]);
apply_uniforms(UB_view_params, *pass.params);
params := Sprite_Params.{ lit = xx (kind == .BILLBOARD) };
apply_uniforms(UB_sprite_params, *params);
if kind == .BILLBOARD && !shadow apply_uniforms(UB_shadow_params, *pass.shadow);
}
bind : sg_bindings;
bind.vertex_buffers[0] = sprites.stream.buffer;
bind.vertex_buffer_offsets[0] = it.first * size_of(Instance);
bind.images[IMG_sprite] = texture_image(xx (it.key & 0xFFFF_FFFF));
bind.samplers[SMP_sprite_smp] = samplers.nearest_clamp;
if kind == .BILLBOARD && !shadow bind_shadow(pass, *bind);
sg_apply_bindings(*bind);
sg_draw(0, 6, it.count);
}
}
#scope_file
Kind :: enum {
BILLBOARD;
PARTICLE_ALPHA;
PARTICLE_ADDITIVE;
}
Instance :: struct {
center : Vector3;
size : Vector2;
uv_rect : Vector4;
face : Vector3;
color : Vector4;
glow : float;
}
sprites : struct {
pipelines : [#run enum_highest_value(Kind) + 1]sg_pipeline;
shadow_pipeline : sg_pipeline;
instances : [..]Instance;
batches : [..]Batch;
stream : Stream_Buffer;
}
key :: (kind: Kind, sheet: Texture) -> s64 {
return (cast(s64) kind << 32) | sheet.id;
}