207 lines
8.4 KiB
Plaintext
207 lines
8.4 KiB
Plaintext
triles_init :: (orientations: [ORIENTATION_COUNT]Matrix3) {
|
|
desc := pipeline_desc(trile_shader_desc(sg_query_backend()), .VIEW_OPAQUE, depth_write = true);
|
|
desc.layout.buffers[1].step_func = .PER_INSTANCE;
|
|
desc.layout.attrs[ATTR_trile_position] = .{ format = .FLOAT3 };
|
|
desc.layout.attrs[ATTR_trile_data_n] = .{ format = .UBYTE4N };
|
|
desc.layout.attrs[ATTR_trile_instance] = .{ format = .FLOAT4, buffer_index = 1 };
|
|
desc.layout.attrs[ATTR_trile_tint] = .{ format = .FLOAT, buffer_index = 1 };
|
|
desc.layout.attrs[ATTR_trile_cut] = .{ format = .FLOAT, buffer_index = 1 };
|
|
desc.index_type = .UINT16;
|
|
desc.cull_mode = .BACK;
|
|
desc.face_winding = .CCW;
|
|
triles.pipeline = sg_make_pipeline(*desc);
|
|
|
|
// Back faces only, so lit faces don't shadow themselves.
|
|
shadow_desc := pipeline_desc(trile_shadow_shader_desc(sg_query_backend()), .SHADOW, depth_write = true);
|
|
shadow_desc.layout = desc.layout;
|
|
shadow_desc.index_type = .UINT16;
|
|
shadow_desc.cull_mode = .FRONT;
|
|
shadow_desc.face_winding = .CCW;
|
|
triles.shadow_pipeline = sg_make_pipeline(*shadow_desc);
|
|
|
|
glass_desc := pipeline_desc(trile_glass_shadow_shader_desc(sg_query_backend()), .SHADOW, MULTIPLY_BLEND);
|
|
glass_desc.colors[0].write_mask = .RGB;
|
|
glass_desc.layout = desc.layout;
|
|
glass_desc.index_type = .UINT16;
|
|
glass_desc.cull_mode = .FRONT;
|
|
glass_desc.face_winding = .CCW;
|
|
triles.glass_shadow_pipeline = sg_make_pipeline(*glass_desc);
|
|
|
|
for orientations {
|
|
for r: 0..2 for c: 0..2 triles.params.orientations[it_index * 3 + r][c] = it.coef[r][c];
|
|
}
|
|
triles.palette_dirty = true;
|
|
}
|
|
|
|
renderer_trile_mesh_create :: (vertices: []Trile_Vertex, indices: []u16) -> u32 {
|
|
id, mesh := slot_add(*triles.meshes);
|
|
make_mesh(mesh, vertices, indices);
|
|
return id;
|
|
}
|
|
|
|
renderer_trile_mesh_update :: (id: u32, vertices: []Trile_Vertex, indices: []u16) {
|
|
mesh := slot_get(*triles.meshes, id);
|
|
free_mesh(mesh);
|
|
make_mesh(mesh, vertices, indices);
|
|
}
|
|
|
|
renderer_trile_mesh_destroy :: (id: u32) {
|
|
free_mesh(slot_get(*triles.meshes, id));
|
|
slot_remove(*triles.meshes, id);
|
|
}
|
|
|
|
renderer_swatch_set :: (id: u8, color: [3]u8, reflectivity: u8, gloss: u8, emittance: u8, glass: bool) {
|
|
for 0..2 triles.palette[0][id][it] = color[it];
|
|
triles.palette[0][id][3] = xx (min(cast(int) emittance, 127) * 255 / 127); // emittance is 0..127
|
|
triles.palette[1][id][0] = xx ifx glass then 255 else 0;
|
|
triles.palette[1][id][1] = reflectivity;
|
|
triles.palette[1][id][2] = gloss;
|
|
triles.palette_dirty = true;
|
|
}
|
|
|
|
triles_upload :: (scene: *Scene) {
|
|
if triles.palette_dirty {
|
|
triles.palette_dirty = false;
|
|
if triles.palette_image.id sg_destroy_image(triles.palette_image);
|
|
desc := sg_image_desc.{ width = 256, height = 2, pixel_format = .RGBA8 };
|
|
desc.data.subimage[0][0] = .{ triles.palette.data, size_of(type_of(triles.palette)) };
|
|
triles.palette_image = sg_make_image(*desc);
|
|
}
|
|
|
|
array_reset_keeping_memory(*triles.instances);
|
|
array_reset_keeping_memory(*triles.batches);
|
|
|
|
draws : [..]Keyed(Instance);
|
|
draws.allocator = temp;
|
|
for view, view_index: scene.views {
|
|
array_reset_keeping_memory(*draws);
|
|
if view.draw_world {
|
|
for chunk: scene.chunks for group: chunk.groups for group.instances {
|
|
position := chunk.origin + Vector3.{xx it.x, xx it.y, xx it.z};
|
|
cut := trile_cut(.{xx view_index}, group.mesh, position);
|
|
array_add(*draws, .{group.mesh.id, .{position, xx it.orientation, 0, cut}});
|
|
}
|
|
}
|
|
for scene.triles {
|
|
if it.view.index != view_index continue;
|
|
preview : s64 = ifx it.tint != .NONE then PREVIEW_KEY else 0;
|
|
cut := ifx preview then 0.0 else trile_cut(it.view, it.mesh, it.position);
|
|
array_add(*draws, .{preview | it.mesh.id, .{it.position, xx it.orientation, xx it.tint, cut}});
|
|
}
|
|
batch_instances(view_index, draws, *triles.batches, *triles.instances);
|
|
}
|
|
stream_upload(*triles.stream, triles.instances);
|
|
}
|
|
|
|
// After every opaque caster, so glass behind something solid doesn't tint.
|
|
triles_draw_glass_shadow :: (pass: *Pass) {
|
|
sg_apply_pipeline(triles.glass_shadow_pipeline);
|
|
apply_uniforms(UB_view_params, *pass.params);
|
|
triles.params.show_hidden = 1;
|
|
apply_uniforms(UB_trile_params, *triles.params);
|
|
for triles.batches {
|
|
if it.view_index != pass.view_index || it.key & PREVIEW_KEY continue;
|
|
mesh := slot_get(*triles.meshes, xx it.key);
|
|
if !mesh || mesh.index_count == 0 continue;
|
|
bind : sg_bindings;
|
|
bind.vertex_buffers[0] = mesh.vertices;
|
|
bind.index_buffer = mesh.indices;
|
|
bind.vertex_buffers[1] = triles.stream.buffer;
|
|
bind.vertex_buffer_offsets[1] = it.first * size_of(Instance);
|
|
bind.images[IMG_palette] = triles.palette_image;
|
|
bind.samplers[SMP_palette_smp] = samplers.nearest_clamp;
|
|
sg_apply_bindings(*bind);
|
|
sg_draw(0, mesh.index_count, it.count);
|
|
}
|
|
}
|
|
|
|
triles_draw :: (pass: *Pass) {
|
|
// Cut triles are only hidden from the camera itself, not from shadows or reflections.
|
|
triles.params.show_hidden = xx (pass.kind != .MAIN);
|
|
sg_apply_pipeline(ifx pass.kind == .SHADOW then triles.shadow_pipeline else triles.pipeline);
|
|
apply_uniforms(UB_view_params, *pass.params);
|
|
apply_uniforms(UB_trile_params, *triles.params);
|
|
if pass.kind != .SHADOW {
|
|
apply_uniforms(UB_shadow_params, *pass.shadow);
|
|
fs_params := Trile_Fs_Params.{
|
|
sun_dir = pass.params.sun_direction,
|
|
use_reflection = xx (pass.kind == .MAIN),
|
|
reflection_height = view_environment(pass.view).reflection_height,
|
|
};
|
|
probes_set_params(*fs_params);
|
|
apply_uniforms(UB_trile_fs_params, *fs_params);
|
|
}
|
|
|
|
for triles.batches {
|
|
if it.view_index != pass.view_index || (pass.kind != .MAIN && it.key & PREVIEW_KEY) continue;
|
|
mesh := slot_get(*triles.meshes, xx (it.key & ~PREVIEW_KEY));
|
|
if !mesh || mesh.index_count == 0 continue;
|
|
|
|
bind : sg_bindings;
|
|
bind.vertex_buffers[0] = mesh.vertices;
|
|
bind.index_buffer = mesh.indices;
|
|
bind.vertex_buffers[1] = triles.stream.buffer;
|
|
bind.vertex_buffer_offsets[1] = it.first * size_of(Instance);
|
|
bind.images[IMG_palette] = triles.palette_image;
|
|
bind.samplers[SMP_palette_smp] = samplers.nearest_clamp;
|
|
if pass.kind != .SHADOW {
|
|
bind_shadow(pass, *bind);
|
|
bind.images[IMG_trile_reflection] = ifx pass.kind == .MAIN then view_reflection_image(pass.view_index) else texture_image(0);
|
|
bind.images[IMG_trile_reflection_glow] = ifx pass.kind == .MAIN then view_reflection_glow_image(pass.view_index) else texture_image(0);
|
|
bind.samplers[SMP_trile_reflection_smp] = samplers.linear_clamp;
|
|
probes_bind(*bind);
|
|
}
|
|
sg_apply_bindings(*bind);
|
|
sg_draw(0, mesh.index_count, it.count);
|
|
}
|
|
}
|
|
|
|
#scope_file
|
|
|
|
Mesh :: struct {
|
|
vertices : sg_buffer;
|
|
indices : sg_buffer;
|
|
index_count : s32;
|
|
}
|
|
|
|
Instance :: struct {
|
|
position : Vector3;
|
|
orientation : float;
|
|
tint : float;
|
|
cut : float; // 0..1 dissolved away from the camera, still casts shadows
|
|
}
|
|
|
|
PREVIEW_KEY :: 1 << 32;
|
|
|
|
triles : struct {
|
|
pipeline : sg_pipeline;
|
|
shadow_pipeline : sg_pipeline;
|
|
glass_shadow_pipeline : sg_pipeline;
|
|
params : Trile_Params;
|
|
meshes : Slots(Mesh);
|
|
|
|
palette : [2][256][4]u8; // row 0: colour, emittance; row 1: glass, reflectivity, gloss
|
|
palette_image : sg_image;
|
|
palette_dirty : bool;
|
|
|
|
instances : [..]Instance;
|
|
batches : [..]Batch;
|
|
stream : Stream_Buffer;
|
|
}
|
|
|
|
make_mesh :: (mesh: *Mesh, vertices: []Trile_Vertex, indices: []u16) {
|
|
mesh.index_count = xx indices.count;
|
|
if indices.count {
|
|
mesh.vertices = sg_make_buffer(*(sg_buffer_desc.{ data = .{ vertices.data, xx (vertices.count * size_of(Trile_Vertex)) } }));
|
|
mesh.indices = sg_make_buffer(*(sg_buffer_desc.{ type = .INDEXBUFFER, data = .{ indices.data, xx (indices.count * size_of(u16)) } }));
|
|
}
|
|
}
|
|
|
|
free_mesh :: (mesh: *Mesh) {
|
|
if mesh.index_count {
|
|
sg_destroy_buffer(mesh.vertices);
|
|
sg_destroy_buffer(mesh.indices);
|
|
}
|
|
mesh.* = .{};
|
|
}
|