port more rendering to the new system

This commit is contained in:
Tuomas Katajisto 2026-10-02 23:41:45 +03:00
parent 9ebeea8772
commit ff7787a4e0
24 changed files with 4040 additions and 512 deletions

View File

@ -1139,7 +1139,7 @@ draw_editor_billboards :: (view: View_Id) {
draw_level_editor :: () {
curworld := get_current_world();
if !curworld.valid then return;
view := add_view(.{camera = get_level_editor_camera(), environment = *curworld.world.environment});
view := add_view(.{camera = get_level_editor_camera(), environment = world_environment(*curworld.world)});
draw_entities(view, *curworld.world);
draw_live_particles(view);
if show_trile_preview && !trile_preview_disabled {

View File

@ -61,6 +61,7 @@ draw_material_studio_ui :: (theme: *GR.Overall_Theme) {
r.y += r.h;
GR.slider(r, *swatch.emittance, 0, 127, 1, *theme.slider_theme);
r.y += r.h * 2;
swatch_changed(ms_selected);
r.h = row_h;
if GR.button(r, "Save", *theme.button_theme) {
@ -73,6 +74,7 @@ draw_material_studio_ui :: (theme: *GR.Overall_Theme) {
GR.label(r, "In use, can't delete", *t_label_left(theme));
} else if GR.button(r, "Delete", *theme.button_theme) {
swatch.* = .{};
swatch_changed(ms_selected);
ms_selected = 0;
}
}

View File

@ -386,7 +386,7 @@ draw_trile_editor :: () {
// A trile is drawn in the light of the world it will sit in.
environment := New(Environment,, temp);
curworld := get_current_world();
if curworld.valid environment = *curworld.world.environment;
if curworld.valid environment = world_environment(*curworld.world);
view := add_view(.{camera = get_trile_editor_camera(), environment = environment, draw_world = false});
mesh, ok := get_trile_mesh(editor_current_trile.name);

View File

@ -0,0 +1,59 @@
billboards_init :: () {
desc := pipeline_desc(billboard_shader_desc(sg_query_backend()), .VIEW, depth_write = true);
desc.layout.buffers[0].step_func = .PER_INSTANCE;
desc.layout.attrs[ATTR_billboard_center] = .{ format = .FLOAT3 };
desc.layout.attrs[ATTR_billboard_size] = .{ format = .FLOAT2 };
desc.layout.attrs[ATTR_billboard_uv_rect] = .{ format = .FLOAT4 };
desc.layout.attrs[ATTR_billboard_face] = .{ format = .FLOAT3 };
billboards.pipeline = sg_make_pipeline(*desc);
}
billboards_upload :: (scene: *Scene) {
array_reset_keeping_memory(*billboards.instances);
array_reset_keeping_memory(*billboards.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, .{it.sheet.id, .{it.position, it.size, it.uv_rect, it.face}});
}
batch_instances(view_index, draws, *billboards.batches, *billboards.instances);
}
stream_upload(*billboards.stream, billboards.instances);
}
billboards_draw :: (pass: *Pass) {
sg_apply_pipeline(billboards.pipeline);
apply_uniforms(UB_view_params, *pass.params);
for billboards.batches {
if it.view_index != pass.view_index continue;
bind : sg_bindings;
bind.vertex_buffers[0] = billboards.stream.buffer;
bind.vertex_buffer_offsets[0] = it.first * size_of(Instance);
bind.images[IMG_sprite] = texture_image(xx it.key);
bind.samplers[SMP_sprite_smp] = samplers.nearest_clamp;
sg_apply_bindings(*bind);
sg_draw(0, 6, it.count);
}
}
#scope_file
Instance :: struct {
center : Vector3;
size : Vector2;
uv_rect : Vector4;
face : Vector3;
}
billboards : struct {
pipeline : sg_pipeline;
instances : [..]Instance;
batches : [..]Batch;
stream : Stream_Buffer;
}

135
src/renderer_sokol/gpu.jai Normal file
View File

@ -0,0 +1,135 @@
VIEW_COLOR_FORMAT :: sg_pixel_format.RGBA8;
VIEW_DEPTH_FORMAT :: sg_pixel_format.DEPTH;
ALPHA_BLEND :: sg_blend_state.{
enabled = true,
src_factor_rgb = .SRC_ALPHA,
dst_factor_rgb = .ONE_MINUS_SRC_ALPHA,
};
Pipeline_Target :: enum {
SCREEN;
VIEW;
}
samplers : struct {
nearest_clamp : sg_sampler;
linear_clamp : sg_sampler;
linear_repeat : sg_sampler;
}
samplers_init :: () {
samplers.nearest_clamp = sg_make_sampler(*(sg_sampler_desc.{ min_filter = .NEAREST, mag_filter = .NEAREST, wrap_u = .CLAMP_TO_EDGE, wrap_v = .CLAMP_TO_EDGE }));
samplers.linear_clamp = sg_make_sampler(*(sg_sampler_desc.{ min_filter = .LINEAR, mag_filter = .LINEAR, wrap_u = .CLAMP_TO_EDGE, wrap_v = .CLAMP_TO_EDGE }));
samplers.linear_repeat = sg_make_sampler(*(sg_sampler_desc.{ min_filter = .LINEAR, mag_filter = .LINEAR, wrap_u = .REPEAT, wrap_v = .REPEAT }));
}
// Vertex layout and anything else specific is filled in by the caller.
pipeline_desc :: (shader: sg_shader_desc, target: Pipeline_Target, blend := false, depth_write := false) -> sg_pipeline_desc {
shader_desc := shader;
desc : sg_pipeline_desc;
desc.shader = sg_make_shader(*shader_desc);
if blend desc.colors[0].blend = ALPHA_BLEND;
if target == .VIEW {
desc.colors[0].pixel_format = VIEW_COLOR_FORMAT;
desc.depth.pixel_format = VIEW_DEPTH_FORMAT;
desc.depth.compare = .LESS_EQUAL;
desc.depth.write_enabled = depth_write;
desc.sample_count = 1;
}
return desc;
}
Pass :: struct {
scene : *Scene;
view_index : int;
view : View;
reflection : bool;
params : View_Params;
}
make_pass :: (scene: *Scene, view_index: int, reflection: bool) -> Pass {
pass := Pass.{ scene = scene, view_index = view_index, view = scene.views[view_index], reflection = reflection };
height := ifx pass.view.environment then pass.view.environment.reflection_height else 0;
camera := pass.view.camera.position;
view_proj := scene.view_proj[view_index];
pass.params.clip = .[0, 0, 0, 1];
if reflection {
camera.y = 2 * height - camera.y;
view_proj = scene.reflection_view_proj[view_index];
pass.params.clip = .[0, 1, 0, -height];
}
pass.params.view_proj = view_proj.floats;
pass.params.camera = .[camera.x, camera.y, camera.z, scene.time];
return pass;
}
apply_uniforms :: (slot: s32, params: *$T) {
sg_apply_uniforms(slot, *(sg_range.{ params, size_of(T) }));
}
Slots :: struct (T: Type) {
items : [..]T;
free : [..]u32;
}
// Ids start at 1 so that 0 can mean none.
slot_add :: (slots: *Slots($T)) -> u32, *T {
if slots.free.count {
id := pop(*slots.free);
return id, *slots.items[id - 1];
}
item := array_add(*slots.items);
return xx slots.items.count, item;
}
slot_get :: (slots: *Slots($T), id: u32) -> *T {
if id == 0 || id > slots.items.count return null;
return *slots.items[id - 1];
}
slot_remove :: (slots: *Slots($T), id: u32) {
slots.items[id - 1] = .{};
array_add(*slots.free, id);
}
Stream_Buffer :: struct {
buffer : sg_buffer;
size : int;
}
stream_upload :: (stream: *Stream_Buffer, data: []$T) {
if data.count == 0 return;
size := data.count * size_of(T);
if size > stream.size {
if stream.size sg_destroy_buffer(stream.buffer);
stream.size = max(size, stream.size * 2);
stream.buffer = sg_make_buffer(*(sg_buffer_desc.{ size = xx stream.size, usage = .STREAM }));
}
sg_update_buffer(stream.buffer, *(sg_range.{ data.data, xx size }));
}
Keyed :: struct (Instance: Type) {
key : s64;
instance : Instance;
}
Batch :: struct {
view_index : s32;
key : s64;
first : s32;
count : s32;
}
// Sorts the view's draws by key and appends one batch per run of equal keys.
batch_instances :: (view_index: int, draws: []Keyed($I), batches: *[..]Batch, instances: *[..]I) {
quick_sort(draws, (a: Keyed(I), b: Keyed(I)) -> s64 { return a.key - b.key; });
for draws {
if it_index == 0 || draws[it_index - 1].key != it.key {
array_add(batches, .{view_index = xx view_index, key = it.key, first = xx instances.count});
}
array_add(instances, it.instance);
batches.*[batches.count - 1].count += 1;
}
}

View File

@ -1,10 +1,16 @@
#import,dir "../../modules/sokol-jai/sokol/gfx"(DEBUG = !FLAG_RELEASE_BUILD);
#import,dir "../../modules/sokol-jai/sokol/glue"(DEBUG = !FLAG_RELEASE_BUILD);
#import "Sort";
#load "shaders/jai/ui.jai";
#load "shaders/jai/shaders.jai";
#load "gpu.jai";
#load "ui.jai";
#load "triles.jai";
#load "views.jai";
#load "water.jai";
#load "billboards.jai";
renderer_init :: () {
renderer_init :: (orientations: [ORIENTATION_COUNT]Matrix3) {
sg_setup(*(sg_desc.{
environment = cast,force(sg_environment) sglue_environment(),
logger = .{ func = slog_func },
@ -13,7 +19,12 @@ renderer_init :: () {
white := u8.[255, 255, 255, 255];
renderer.white = make_image(1, 1, white);
samplers_init();
ui_init();
triles_init(orientations);
views_init();
water_init();
billboards_init();
}
renderer_shutdown :: () {
@ -21,50 +32,55 @@ renderer_shutdown :: () {
}
renderer_texture_create :: (width: s32, height: s32, pixels: []u8) -> u32 {
slot : *Renderer_Texture;
index : int;
if renderer.free_textures.count {
index = pop(*renderer.free_textures);
slot = *renderer.textures[index];
} else {
index = renderer.textures.count;
slot = array_add(*renderer.textures);
}
slot.width = width;
slot.height = height;
slot.image = make_image(width, height, pixels);
return xx (index + 1);
id, texture := slot_add(*renderer.textures);
texture.width = width;
texture.height = height;
texture.image = make_image(width, height, pixels);
return id;
}
// Immutable images are recreated rather than updated, so a texture can be
// updated any number of times in a frame.
renderer_texture_update :: (id: u32, pixels: []u8) {
slot := *renderer.textures[id - 1];
sg_destroy_image(slot.image);
slot.image = make_image(slot.width, slot.height, pixels);
texture := slot_get(*renderer.textures, id);
sg_destroy_image(texture.image);
texture.image = make_image(texture.width, texture.height, pixels);
}
renderer_texture_destroy :: (id: u32) {
slot := *renderer.textures[id - 1];
sg_destroy_image(slot.image);
slot.* = .{};
array_add(*renderer.free_textures, xx (id - 1));
sg_destroy_image(slot_get(*renderer.textures, id).image);
slot_remove(*renderer.textures, id);
}
renderer_render :: (scene: *Scene, width: s32, height: s32) {
triles_upload(scene);
billboards_upload(scene);
ui_upload(scene);
pass_action : sg_pass_action;
pass_action.colors[0] = .{ load_action = .CLEAR, clear_value = .{0, 0, 0, 1} };
sg_begin_pass(*(sg_pass.{ action = pass_action, swapchain = cast,force(sg_swapchain) sglue_swapchain() }));
for view, view_index: scene.views {
for reflection: bool.[true, false] {
pass := make_pass(scene, view_index, reflection);
view_begin_pass(view_index, width, height, reflection);
triles_draw(*pass);
billboards_draw(*pass);
water_draw(*pass);
sg_end_pass();
}
}
action : sg_pass_action;
action.colors[0] = .{ load_action = .CLEAR, clear_value = .{0, 0, 0, 1} };
sg_begin_pass(*(sg_pass.{ action = action, swapchain = cast,force(sg_swapchain) sglue_swapchain() }));
for scene.views view_composite(it_index);
ui_draw(scene, width, height);
sg_end_pass();
sg_commit();
}
texture_image :: (id: u32) -> sg_image {
if id == 0 return renderer.white;
return renderer.textures[id - 1].image;
texture := slot_get(*renderer.textures, id);
if !texture || !texture.image.id return renderer.white;
return texture.image;
}
#scope_file
@ -76,17 +92,12 @@ Renderer_Texture :: struct {
}
renderer : struct {
textures : [..]Renderer_Texture;
free_textures : [..]s32;
white : sg_image;
textures : Slots(Renderer_Texture);
white : sg_image;
}
make_image :: (width: s32, height: s32, pixels: []u8) -> sg_image {
desc := sg_image_desc.{
width = width,
height = height,
pixel_format = .RGBA8,
};
desc := sg_image_desc.{ width = width, height = height, pixel_format = .RGBA8 };
desc.data.subimage[0][0] = .{ pixels.data, xx pixels.count };
return sg_make_image(*desc);
}

View File

@ -0,0 +1,43 @@
@vs vs_billboard
@include_block view
@include_block quad
in vec3 center; // bottom middle
in vec2 size;
in vec4 uv_rect;
in vec3 face;
out float v_clip;
out vec2 v_uv;
void main() {
vec2 c = quad_corner(gl_VertexIndex);
vec3 look = face;
if (face.x < -10.0) look = vec3(center.x - camera.x, 0.0, center.z - camera.z);
if (dot(look.xz, look.xz) < 0.0001) look = vec3(0.0, 0.0, 1.0);
vec3 right = normalize(cross(vec3(0.0, 1.0, 0.0), look));
vec3 world = center + right * ((c.x - 0.5) * size.x) + vec3(0.0, c.y * size.y, 0.0);
gl_Position = view_proj * vec4(world, 1.0);
v_clip = dot(vec4(world, 1.0), clip);
v_uv = vec2(uv_rect.x + c.x * uv_rect.z, uv_rect.y + (1.0 - c.y) * uv_rect.w);
}
@end
@fs fs_billboard
in float v_clip;
in vec2 v_uv;
out vec4 frag_color;
layout(binding=0) uniform texture2D sprite;
layout(binding=0) uniform sampler sprite_smp;
void main() {
if (v_clip < 0.0) discard;
vec4 sampled = texture(sampler2D(sprite, sprite_smp), v_uv);
if (sampled.a < 0.01) discard;
frag_color = vec4(sampled.rgb, 1.0);
}
@end
@program billboard vs_billboard fs_billboard

View File

@ -1,22 +1,23 @@
compile_sokol_shaders :: () -> bool {
dir :: "src/renderer_sokol/shaders";
shdc :: #ifx OS == .MACOS then "sokol-shdc-mac" else "sokol-shdc";
dir :: "src/renderer_sokol/shaders";
input :: "src/renderer_sokol/shaders/shaders.glsl";
output :: "src/renderer_sokol/shaders/jai/shaders.jai";
shdc :: #ifx OS == .MACOS then "src/renderer_sokol/shaders/sokol-shdc-mac" else "src/renderer_sokol/shaders/sokol-shdc";
out_time, _, out_exists := file_modtime_and_size(output);
stale := !out_exists;
for file_list(dir) {
if !ends_with(it, ".glsl") continue;
name := path_strip_extension(path_filename(it));
out := tprint("%/jai/%.jai", dir, name);
src_time := file_modtime_and_size(it);
out_time, _, out_exists := file_modtime_and_size(out);
if out_exists && compare_apollo_times(out_time, src_time) >= 0 continue;
if compare_apollo_times(src_time, out_time) > 0 stale = true;
}
if !stale return true;
result, output, error := run_command(tprint("%/%", dir, shdc), "-i", it, "-o", out,
"-l", "glsl430:glsl300es:metal_macos", "-f", "sokol_jai", capture_and_return_output = true);
if result.exit_code != 0 {
log_error("sokol-shdc failed on %:\n%%", it, output, error);
return false;
}
result, out, error := run_command(shdc, "-i", input, "-o", output,
"-l", "glsl430:glsl300es:metal_macos", "-f", "sokol_jai", capture_and_return_output = true);
if result.exit_code != 0 {
log_error("sokol-shdc failed:\n%%", out, error);
return false;
}
return true;
}

View File

@ -0,0 +1,14 @@
@block view
layout(binding=0) uniform view_params {
mat4 view_proj;
vec4 camera; // xyz = position, w = time
vec4 clip; // plane; geometry on its negative side is dropped
};
@end
@block quad
vec2 quad_corner(int vertex_index) {
const vec2 corners[6] = vec2[6](vec2(0, 0), vec2(1, 0), vec2(1, 1), vec2(0, 0), vec2(1, 1), vec2(0, 1));
return corners[vertex_index];
}
@end

View File

@ -0,0 +1,27 @@
@vs vs_composite
layout(binding=0) uniform composite_vs_params {
float flip_y;
};
out vec2 v_uv;
void main() {
vec2 p = vec2((gl_VertexIndex << 1) & 2, gl_VertexIndex & 2);
v_uv = vec2(p.x, mix(p.y, 1.0 - p.y, flip_y));
gl_Position = vec4(p * 2.0 - 1.0, 0.0, 1.0);
}
@end
@fs fs_composite
in vec2 v_uv;
out vec4 frag_color;
layout(binding=0) uniform texture2D view_color;
layout(binding=0) uniform sampler composite_smp;
void main() {
frag_color = texture(sampler2D(view_color, composite_smp), v_uv);
}
@end
@program composite vs_composite fs_composite

File diff suppressed because it is too large Load Diff

View File

@ -1,422 +0,0 @@
/*
#version:1# (machine generated, don't edit!)
Generated by sokol-shdc (https://github.com/floooh/sokol-tools)
Cmdline:
sokol-shdc -i src/renderer_sokol/shaders/ui.glsl -o src/renderer_sokol/shaders/jai/ui.jai -l glsl430:glsl300es:metal_macos -f sokol_jai
Overview:
=========
Shader program: 'ui':
Get shader desc: ui_shader_desc(sg_query_backend())
Vertex Shader: vs_ui
Fragment Shader: fs_ui
Attributes:
ATTR_ui_position => 0
ATTR_ui_uv => 1
ATTR_ui_color0 => 2
Bindings:
Uniform block 'ui_vs_params':
Jai struct: Ui_Vs_Params
Bind slot: UB_ui_vs_params => 0
Image 'tex':
Image type: ._2D
Sample type: .FLOAT
Multisampled: false
Bind slot: IMG_tex => 0
Sampler 'smp':
Type: .FILTERING
Bind slot: SMP_smp => 0
*/
ATTR_ui_position :: 0;
ATTR_ui_uv :: 1;
ATTR_ui_color0 :: 2;
UB_ui_vs_params :: 0;
IMG_tex :: 0;
SMP_smp :: 0;
Ui_Vs_Params :: struct {
screen_size: [2]float;
_: [8]u8;
};
/*
#version 430
uniform vec4 ui_vs_params[1];
layout(location = 0) in vec2 position;
layout(location = 0) out vec2 v_uv;
layout(location = 1) in vec2 uv;
layout(location = 1) out vec4 v_color;
layout(location = 2) in vec4 color0;
void main()
{
vec2 _26 = ((position / ui_vs_params[0].xy) * 2.0) - vec2(1.0);
gl_Position = vec4(_26.x, -_26.y, 0.0, 1.0);
v_uv = uv;
v_color = color0;
}
*/
vs_ui_source_glsl430 := u8.[
0x23,0x76,0x65,0x72,0x73,0x69,0x6f,0x6e,0x20,0x34,0x33,0x30,0x0a,0x0a,0x75,0x6e,
0x69,0x66,0x6f,0x72,0x6d,0x20,0x76,0x65,0x63,0x34,0x20,0x75,0x69,0x5f,0x76,0x73,
0x5f,0x70,0x61,0x72,0x61,0x6d,0x73,0x5b,0x31,0x5d,0x3b,0x0a,0x6c,0x61,0x79,0x6f,
0x75,0x74,0x28,0x6c,0x6f,0x63,0x61,0x74,0x69,0x6f,0x6e,0x20,0x3d,0x20,0x30,0x29,
0x20,0x69,0x6e,0x20,0x76,0x65,0x63,0x32,0x20,0x70,0x6f,0x73,0x69,0x74,0x69,0x6f,
0x6e,0x3b,0x0a,0x6c,0x61,0x79,0x6f,0x75,0x74,0x28,0x6c,0x6f,0x63,0x61,0x74,0x69,
0x6f,0x6e,0x20,0x3d,0x20,0x30,0x29,0x20,0x6f,0x75,0x74,0x20,0x76,0x65,0x63,0x32,
0x20,0x76,0x5f,0x75,0x76,0x3b,0x0a,0x6c,0x61,0x79,0x6f,0x75,0x74,0x28,0x6c,0x6f,
0x63,0x61,0x74,0x69,0x6f,0x6e,0x20,0x3d,0x20,0x31,0x29,0x20,0x69,0x6e,0x20,0x76,
0x65,0x63,0x32,0x20,0x75,0x76,0x3b,0x0a,0x6c,0x61,0x79,0x6f,0x75,0x74,0x28,0x6c,
0x6f,0x63,0x61,0x74,0x69,0x6f,0x6e,0x20,0x3d,0x20,0x31,0x29,0x20,0x6f,0x75,0x74,
0x20,0x76,0x65,0x63,0x34,0x20,0x76,0x5f,0x63,0x6f,0x6c,0x6f,0x72,0x3b,0x0a,0x6c,
0x61,0x79,0x6f,0x75,0x74,0x28,0x6c,0x6f,0x63,0x61,0x74,0x69,0x6f,0x6e,0x20,0x3d,
0x20,0x32,0x29,0x20,0x69,0x6e,0x20,0x76,0x65,0x63,0x34,0x20,0x63,0x6f,0x6c,0x6f,
0x72,0x30,0x3b,0x0a,0x0a,0x76,0x6f,0x69,0x64,0x20,0x6d,0x61,0x69,0x6e,0x28,0x29,
0x0a,0x7b,0x0a,0x20,0x20,0x20,0x20,0x76,0x65,0x63,0x32,0x20,0x5f,0x32,0x36,0x20,
0x3d,0x20,0x28,0x28,0x70,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x20,0x2f,0x20,0x75,
0x69,0x5f,0x76,0x73,0x5f,0x70,0x61,0x72,0x61,0x6d,0x73,0x5b,0x30,0x5d,0x2e,0x78,
0x79,0x29,0x20,0x2a,0x20,0x32,0x2e,0x30,0x29,0x20,0x2d,0x20,0x76,0x65,0x63,0x32,
0x28,0x31,0x2e,0x30,0x29,0x3b,0x0a,0x20,0x20,0x20,0x20,0x67,0x6c,0x5f,0x50,0x6f,
0x73,0x69,0x74,0x69,0x6f,0x6e,0x20,0x3d,0x20,0x76,0x65,0x63,0x34,0x28,0x5f,0x32,
0x36,0x2e,0x78,0x2c,0x20,0x2d,0x5f,0x32,0x36,0x2e,0x79,0x2c,0x20,0x30,0x2e,0x30,
0x2c,0x20,0x31,0x2e,0x30,0x29,0x3b,0x0a,0x20,0x20,0x20,0x20,0x76,0x5f,0x75,0x76,
0x20,0x3d,0x20,0x75,0x76,0x3b,0x0a,0x20,0x20,0x20,0x20,0x76,0x5f,0x63,0x6f,0x6c,
0x6f,0x72,0x20,0x3d,0x20,0x63,0x6f,0x6c,0x6f,0x72,0x30,0x3b,0x0a,0x7d,0x0a,0x0a,
0x00,
];
/*
#version 430
layout(binding = 16) uniform sampler2D tex_smp;
layout(location = 0) out vec4 frag_color;
layout(location = 0) in vec2 v_uv;
layout(location = 1) in vec4 v_color;
void main()
{
frag_color = texture(tex_smp, v_uv) * v_color;
}
*/
fs_ui_source_glsl430 := u8.[
0x23,0x76,0x65,0x72,0x73,0x69,0x6f,0x6e,0x20,0x34,0x33,0x30,0x0a,0x0a,0x6c,0x61,
0x79,0x6f,0x75,0x74,0x28,0x62,0x69,0x6e,0x64,0x69,0x6e,0x67,0x20,0x3d,0x20,0x31,
0x36,0x29,0x20,0x75,0x6e,0x69,0x66,0x6f,0x72,0x6d,0x20,0x73,0x61,0x6d,0x70,0x6c,
0x65,0x72,0x32,0x44,0x20,0x74,0x65,0x78,0x5f,0x73,0x6d,0x70,0x3b,0x0a,0x0a,0x6c,
0x61,0x79,0x6f,0x75,0x74,0x28,0x6c,0x6f,0x63,0x61,0x74,0x69,0x6f,0x6e,0x20,0x3d,
0x20,0x30,0x29,0x20,0x6f,0x75,0x74,0x20,0x76,0x65,0x63,0x34,0x20,0x66,0x72,0x61,
0x67,0x5f,0x63,0x6f,0x6c,0x6f,0x72,0x3b,0x0a,0x6c,0x61,0x79,0x6f,0x75,0x74,0x28,
0x6c,0x6f,0x63,0x61,0x74,0x69,0x6f,0x6e,0x20,0x3d,0x20,0x30,0x29,0x20,0x69,0x6e,
0x20,0x76,0x65,0x63,0x32,0x20,0x76,0x5f,0x75,0x76,0x3b,0x0a,0x6c,0x61,0x79,0x6f,
0x75,0x74,0x28,0x6c,0x6f,0x63,0x61,0x74,0x69,0x6f,0x6e,0x20,0x3d,0x20,0x31,0x29,
0x20,0x69,0x6e,0x20,0x76,0x65,0x63,0x34,0x20,0x76,0x5f,0x63,0x6f,0x6c,0x6f,0x72,
0x3b,0x0a,0x0a,0x76,0x6f,0x69,0x64,0x20,0x6d,0x61,0x69,0x6e,0x28,0x29,0x0a,0x7b,
0x0a,0x20,0x20,0x20,0x20,0x66,0x72,0x61,0x67,0x5f,0x63,0x6f,0x6c,0x6f,0x72,0x20,
0x3d,0x20,0x74,0x65,0x78,0x74,0x75,0x72,0x65,0x28,0x74,0x65,0x78,0x5f,0x73,0x6d,
0x70,0x2c,0x20,0x76,0x5f,0x75,0x76,0x29,0x20,0x2a,0x20,0x76,0x5f,0x63,0x6f,0x6c,
0x6f,0x72,0x3b,0x0a,0x7d,0x0a,0x0a,0x00,
];
/*
#version 300 es
uniform vec4 ui_vs_params[1];
layout(location = 0) in vec2 position;
out vec2 v_uv;
layout(location = 1) in vec2 uv;
out vec4 v_color;
layout(location = 2) in vec4 color0;
void main()
{
vec2 _26 = ((position / ui_vs_params[0].xy) * 2.0) - vec2(1.0);
gl_Position = vec4(_26.x, -_26.y, 0.0, 1.0);
v_uv = uv;
v_color = color0;
}
*/
vs_ui_source_glsl300es := u8.[
0x23,0x76,0x65,0x72,0x73,0x69,0x6f,0x6e,0x20,0x33,0x30,0x30,0x20,0x65,0x73,0x0a,
0x0a,0x75,0x6e,0x69,0x66,0x6f,0x72,0x6d,0x20,0x76,0x65,0x63,0x34,0x20,0x75,0x69,
0x5f,0x76,0x73,0x5f,0x70,0x61,0x72,0x61,0x6d,0x73,0x5b,0x31,0x5d,0x3b,0x0a,0x6c,
0x61,0x79,0x6f,0x75,0x74,0x28,0x6c,0x6f,0x63,0x61,0x74,0x69,0x6f,0x6e,0x20,0x3d,
0x20,0x30,0x29,0x20,0x69,0x6e,0x20,0x76,0x65,0x63,0x32,0x20,0x70,0x6f,0x73,0x69,
0x74,0x69,0x6f,0x6e,0x3b,0x0a,0x6f,0x75,0x74,0x20,0x76,0x65,0x63,0x32,0x20,0x76,
0x5f,0x75,0x76,0x3b,0x0a,0x6c,0x61,0x79,0x6f,0x75,0x74,0x28,0x6c,0x6f,0x63,0x61,
0x74,0x69,0x6f,0x6e,0x20,0x3d,0x20,0x31,0x29,0x20,0x69,0x6e,0x20,0x76,0x65,0x63,
0x32,0x20,0x75,0x76,0x3b,0x0a,0x6f,0x75,0x74,0x20,0x76,0x65,0x63,0x34,0x20,0x76,
0x5f,0x63,0x6f,0x6c,0x6f,0x72,0x3b,0x0a,0x6c,0x61,0x79,0x6f,0x75,0x74,0x28,0x6c,
0x6f,0x63,0x61,0x74,0x69,0x6f,0x6e,0x20,0x3d,0x20,0x32,0x29,0x20,0x69,0x6e,0x20,
0x76,0x65,0x63,0x34,0x20,0x63,0x6f,0x6c,0x6f,0x72,0x30,0x3b,0x0a,0x0a,0x76,0x6f,
0x69,0x64,0x20,0x6d,0x61,0x69,0x6e,0x28,0x29,0x0a,0x7b,0x0a,0x20,0x20,0x20,0x20,
0x76,0x65,0x63,0x32,0x20,0x5f,0x32,0x36,0x20,0x3d,0x20,0x28,0x28,0x70,0x6f,0x73,
0x69,0x74,0x69,0x6f,0x6e,0x20,0x2f,0x20,0x75,0x69,0x5f,0x76,0x73,0x5f,0x70,0x61,
0x72,0x61,0x6d,0x73,0x5b,0x30,0x5d,0x2e,0x78,0x79,0x29,0x20,0x2a,0x20,0x32,0x2e,
0x30,0x29,0x20,0x2d,0x20,0x76,0x65,0x63,0x32,0x28,0x31,0x2e,0x30,0x29,0x3b,0x0a,
0x20,0x20,0x20,0x20,0x67,0x6c,0x5f,0x50,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x20,
0x3d,0x20,0x76,0x65,0x63,0x34,0x28,0x5f,0x32,0x36,0x2e,0x78,0x2c,0x20,0x2d,0x5f,
0x32,0x36,0x2e,0x79,0x2c,0x20,0x30,0x2e,0x30,0x2c,0x20,0x31,0x2e,0x30,0x29,0x3b,
0x0a,0x20,0x20,0x20,0x20,0x76,0x5f,0x75,0x76,0x20,0x3d,0x20,0x75,0x76,0x3b,0x0a,
0x20,0x20,0x20,0x20,0x76,0x5f,0x63,0x6f,0x6c,0x6f,0x72,0x20,0x3d,0x20,0x63,0x6f,
0x6c,0x6f,0x72,0x30,0x3b,0x0a,0x7d,0x0a,0x0a,0x00,
];
/*
#version 300 es
precision mediump float;
precision highp int;
uniform highp sampler2D tex_smp;
layout(location = 0) out highp vec4 frag_color;
in highp vec2 v_uv;
in highp vec4 v_color;
void main()
{
frag_color = texture(tex_smp, v_uv) * v_color;
}
*/
fs_ui_source_glsl300es := u8.[
0x23,0x76,0x65,0x72,0x73,0x69,0x6f,0x6e,0x20,0x33,0x30,0x30,0x20,0x65,0x73,0x0a,
0x70,0x72,0x65,0x63,0x69,0x73,0x69,0x6f,0x6e,0x20,0x6d,0x65,0x64,0x69,0x75,0x6d,
0x70,0x20,0x66,0x6c,0x6f,0x61,0x74,0x3b,0x0a,0x70,0x72,0x65,0x63,0x69,0x73,0x69,
0x6f,0x6e,0x20,0x68,0x69,0x67,0x68,0x70,0x20,0x69,0x6e,0x74,0x3b,0x0a,0x0a,0x75,
0x6e,0x69,0x66,0x6f,0x72,0x6d,0x20,0x68,0x69,0x67,0x68,0x70,0x20,0x73,0x61,0x6d,
0x70,0x6c,0x65,0x72,0x32,0x44,0x20,0x74,0x65,0x78,0x5f,0x73,0x6d,0x70,0x3b,0x0a,
0x0a,0x6c,0x61,0x79,0x6f,0x75,0x74,0x28,0x6c,0x6f,0x63,0x61,0x74,0x69,0x6f,0x6e,
0x20,0x3d,0x20,0x30,0x29,0x20,0x6f,0x75,0x74,0x20,0x68,0x69,0x67,0x68,0x70,0x20,
0x76,0x65,0x63,0x34,0x20,0x66,0x72,0x61,0x67,0x5f,0x63,0x6f,0x6c,0x6f,0x72,0x3b,
0x0a,0x69,0x6e,0x20,0x68,0x69,0x67,0x68,0x70,0x20,0x76,0x65,0x63,0x32,0x20,0x76,
0x5f,0x75,0x76,0x3b,0x0a,0x69,0x6e,0x20,0x68,0x69,0x67,0x68,0x70,0x20,0x76,0x65,
0x63,0x34,0x20,0x76,0x5f,0x63,0x6f,0x6c,0x6f,0x72,0x3b,0x0a,0x0a,0x76,0x6f,0x69,
0x64,0x20,0x6d,0x61,0x69,0x6e,0x28,0x29,0x0a,0x7b,0x0a,0x20,0x20,0x20,0x20,0x66,
0x72,0x61,0x67,0x5f,0x63,0x6f,0x6c,0x6f,0x72,0x20,0x3d,0x20,0x74,0x65,0x78,0x74,
0x75,0x72,0x65,0x28,0x74,0x65,0x78,0x5f,0x73,0x6d,0x70,0x2c,0x20,0x76,0x5f,0x75,
0x76,0x29,0x20,0x2a,0x20,0x76,0x5f,0x63,0x6f,0x6c,0x6f,0x72,0x3b,0x0a,0x7d,0x0a,
0x0a,0x00,
];
/*
#include <metal_stdlib>
#include <simd/simd.h>
using namespace metal;
struct ui_vs_params
{
float2 screen_size;
};
struct main0_out
{
float2 v_uv [[user(locn0)]];
float4 v_color [[user(locn1)]];
float4 gl_Position [[position]];
};
struct main0_in
{
float2 position [[attribute(0)]];
float2 uv [[attribute(1)]];
float4 color0 [[attribute(2)]];
};
vertex main0_out main0(main0_in in [[stage_in]], constant ui_vs_params& _15 [[buffer(0)]])
{
main0_out out = {};
float2 _26 = ((in.position / _15.screen_size) * 2.0) - float2(1.0);
out.gl_Position = float4(_26.x, -_26.y, 0.0, 1.0);
out.v_uv = in.uv;
out.v_color = in.color0;
return out;
}
*/
vs_ui_source_metal_macos := u8.[
0x23,0x69,0x6e,0x63,0x6c,0x75,0x64,0x65,0x20,0x3c,0x6d,0x65,0x74,0x61,0x6c,0x5f,
0x73,0x74,0x64,0x6c,0x69,0x62,0x3e,0x0a,0x23,0x69,0x6e,0x63,0x6c,0x75,0x64,0x65,
0x20,0x3c,0x73,0x69,0x6d,0x64,0x2f,0x73,0x69,0x6d,0x64,0x2e,0x68,0x3e,0x0a,0x0a,
0x75,0x73,0x69,0x6e,0x67,0x20,0x6e,0x61,0x6d,0x65,0x73,0x70,0x61,0x63,0x65,0x20,
0x6d,0x65,0x74,0x61,0x6c,0x3b,0x0a,0x0a,0x73,0x74,0x72,0x75,0x63,0x74,0x20,0x75,
0x69,0x5f,0x76,0x73,0x5f,0x70,0x61,0x72,0x61,0x6d,0x73,0x0a,0x7b,0x0a,0x20,0x20,
0x20,0x20,0x66,0x6c,0x6f,0x61,0x74,0x32,0x20,0x73,0x63,0x72,0x65,0x65,0x6e,0x5f,
0x73,0x69,0x7a,0x65,0x3b,0x0a,0x7d,0x3b,0x0a,0x0a,0x73,0x74,0x72,0x75,0x63,0x74,
0x20,0x6d,0x61,0x69,0x6e,0x30,0x5f,0x6f,0x75,0x74,0x0a,0x7b,0x0a,0x20,0x20,0x20,
0x20,0x66,0x6c,0x6f,0x61,0x74,0x32,0x20,0x76,0x5f,0x75,0x76,0x20,0x5b,0x5b,0x75,
0x73,0x65,0x72,0x28,0x6c,0x6f,0x63,0x6e,0x30,0x29,0x5d,0x5d,0x3b,0x0a,0x20,0x20,
0x20,0x20,0x66,0x6c,0x6f,0x61,0x74,0x34,0x20,0x76,0x5f,0x63,0x6f,0x6c,0x6f,0x72,
0x20,0x5b,0x5b,0x75,0x73,0x65,0x72,0x28,0x6c,0x6f,0x63,0x6e,0x31,0x29,0x5d,0x5d,
0x3b,0x0a,0x20,0x20,0x20,0x20,0x66,0x6c,0x6f,0x61,0x74,0x34,0x20,0x67,0x6c,0x5f,
0x50,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x20,0x5b,0x5b,0x70,0x6f,0x73,0x69,0x74,
0x69,0x6f,0x6e,0x5d,0x5d,0x3b,0x0a,0x7d,0x3b,0x0a,0x0a,0x73,0x74,0x72,0x75,0x63,
0x74,0x20,0x6d,0x61,0x69,0x6e,0x30,0x5f,0x69,0x6e,0x0a,0x7b,0x0a,0x20,0x20,0x20,
0x20,0x66,0x6c,0x6f,0x61,0x74,0x32,0x20,0x70,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,
0x20,0x5b,0x5b,0x61,0x74,0x74,0x72,0x69,0x62,0x75,0x74,0x65,0x28,0x30,0x29,0x5d,
0x5d,0x3b,0x0a,0x20,0x20,0x20,0x20,0x66,0x6c,0x6f,0x61,0x74,0x32,0x20,0x75,0x76,
0x20,0x5b,0x5b,0x61,0x74,0x74,0x72,0x69,0x62,0x75,0x74,0x65,0x28,0x31,0x29,0x5d,
0x5d,0x3b,0x0a,0x20,0x20,0x20,0x20,0x66,0x6c,0x6f,0x61,0x74,0x34,0x20,0x63,0x6f,
0x6c,0x6f,0x72,0x30,0x20,0x5b,0x5b,0x61,0x74,0x74,0x72,0x69,0x62,0x75,0x74,0x65,
0x28,0x32,0x29,0x5d,0x5d,0x3b,0x0a,0x7d,0x3b,0x0a,0x0a,0x76,0x65,0x72,0x74,0x65,
0x78,0x20,0x6d,0x61,0x69,0x6e,0x30,0x5f,0x6f,0x75,0x74,0x20,0x6d,0x61,0x69,0x6e,
0x30,0x28,0x6d,0x61,0x69,0x6e,0x30,0x5f,0x69,0x6e,0x20,0x69,0x6e,0x20,0x5b,0x5b,
0x73,0x74,0x61,0x67,0x65,0x5f,0x69,0x6e,0x5d,0x5d,0x2c,0x20,0x63,0x6f,0x6e,0x73,
0x74,0x61,0x6e,0x74,0x20,0x75,0x69,0x5f,0x76,0x73,0x5f,0x70,0x61,0x72,0x61,0x6d,
0x73,0x26,0x20,0x5f,0x31,0x35,0x20,0x5b,0x5b,0x62,0x75,0x66,0x66,0x65,0x72,0x28,
0x30,0x29,0x5d,0x5d,0x29,0x0a,0x7b,0x0a,0x20,0x20,0x20,0x20,0x6d,0x61,0x69,0x6e,
0x30,0x5f,0x6f,0x75,0x74,0x20,0x6f,0x75,0x74,0x20,0x3d,0x20,0x7b,0x7d,0x3b,0x0a,
0x20,0x20,0x20,0x20,0x66,0x6c,0x6f,0x61,0x74,0x32,0x20,0x5f,0x32,0x36,0x20,0x3d,
0x20,0x28,0x28,0x69,0x6e,0x2e,0x70,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x20,0x2f,
0x20,0x5f,0x31,0x35,0x2e,0x73,0x63,0x72,0x65,0x65,0x6e,0x5f,0x73,0x69,0x7a,0x65,
0x29,0x20,0x2a,0x20,0x32,0x2e,0x30,0x29,0x20,0x2d,0x20,0x66,0x6c,0x6f,0x61,0x74,
0x32,0x28,0x31,0x2e,0x30,0x29,0x3b,0x0a,0x20,0x20,0x20,0x20,0x6f,0x75,0x74,0x2e,
0x67,0x6c,0x5f,0x50,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x20,0x3d,0x20,0x66,0x6c,
0x6f,0x61,0x74,0x34,0x28,0x5f,0x32,0x36,0x2e,0x78,0x2c,0x20,0x2d,0x5f,0x32,0x36,
0x2e,0x79,0x2c,0x20,0x30,0x2e,0x30,0x2c,0x20,0x31,0x2e,0x30,0x29,0x3b,0x0a,0x20,
0x20,0x20,0x20,0x6f,0x75,0x74,0x2e,0x76,0x5f,0x75,0x76,0x20,0x3d,0x20,0x69,0x6e,
0x2e,0x75,0x76,0x3b,0x0a,0x20,0x20,0x20,0x20,0x6f,0x75,0x74,0x2e,0x76,0x5f,0x63,
0x6f,0x6c,0x6f,0x72,0x20,0x3d,0x20,0x69,0x6e,0x2e,0x63,0x6f,0x6c,0x6f,0x72,0x30,
0x3b,0x0a,0x20,0x20,0x20,0x20,0x72,0x65,0x74,0x75,0x72,0x6e,0x20,0x6f,0x75,0x74,
0x3b,0x0a,0x7d,0x0a,0x0a,0x00,
];
/*
#include <metal_stdlib>
#include <simd/simd.h>
using namespace metal;
struct main0_out
{
float4 frag_color [[color(0)]];
};
struct main0_in
{
float2 v_uv [[user(locn0)]];
float4 v_color [[user(locn1)]];
};
fragment main0_out main0(main0_in in [[stage_in]], texture2d<float> tex [[texture(0)]], sampler smp [[sampler(0)]])
{
main0_out out = {};
out.frag_color = tex.sample(smp, in.v_uv) * in.v_color;
return out;
}
*/
fs_ui_source_metal_macos := u8.[
0x23,0x69,0x6e,0x63,0x6c,0x75,0x64,0x65,0x20,0x3c,0x6d,0x65,0x74,0x61,0x6c,0x5f,
0x73,0x74,0x64,0x6c,0x69,0x62,0x3e,0x0a,0x23,0x69,0x6e,0x63,0x6c,0x75,0x64,0x65,
0x20,0x3c,0x73,0x69,0x6d,0x64,0x2f,0x73,0x69,0x6d,0x64,0x2e,0x68,0x3e,0x0a,0x0a,
0x75,0x73,0x69,0x6e,0x67,0x20,0x6e,0x61,0x6d,0x65,0x73,0x70,0x61,0x63,0x65,0x20,
0x6d,0x65,0x74,0x61,0x6c,0x3b,0x0a,0x0a,0x73,0x74,0x72,0x75,0x63,0x74,0x20,0x6d,
0x61,0x69,0x6e,0x30,0x5f,0x6f,0x75,0x74,0x0a,0x7b,0x0a,0x20,0x20,0x20,0x20,0x66,
0x6c,0x6f,0x61,0x74,0x34,0x20,0x66,0x72,0x61,0x67,0x5f,0x63,0x6f,0x6c,0x6f,0x72,
0x20,0x5b,0x5b,0x63,0x6f,0x6c,0x6f,0x72,0x28,0x30,0x29,0x5d,0x5d,0x3b,0x0a,0x7d,
0x3b,0x0a,0x0a,0x73,0x74,0x72,0x75,0x63,0x74,0x20,0x6d,0x61,0x69,0x6e,0x30,0x5f,
0x69,0x6e,0x0a,0x7b,0x0a,0x20,0x20,0x20,0x20,0x66,0x6c,0x6f,0x61,0x74,0x32,0x20,
0x76,0x5f,0x75,0x76,0x20,0x5b,0x5b,0x75,0x73,0x65,0x72,0x28,0x6c,0x6f,0x63,0x6e,
0x30,0x29,0x5d,0x5d,0x3b,0x0a,0x20,0x20,0x20,0x20,0x66,0x6c,0x6f,0x61,0x74,0x34,
0x20,0x76,0x5f,0x63,0x6f,0x6c,0x6f,0x72,0x20,0x5b,0x5b,0x75,0x73,0x65,0x72,0x28,
0x6c,0x6f,0x63,0x6e,0x31,0x29,0x5d,0x5d,0x3b,0x0a,0x7d,0x3b,0x0a,0x0a,0x66,0x72,
0x61,0x67,0x6d,0x65,0x6e,0x74,0x20,0x6d,0x61,0x69,0x6e,0x30,0x5f,0x6f,0x75,0x74,
0x20,0x6d,0x61,0x69,0x6e,0x30,0x28,0x6d,0x61,0x69,0x6e,0x30,0x5f,0x69,0x6e,0x20,
0x69,0x6e,0x20,0x5b,0x5b,0x73,0x74,0x61,0x67,0x65,0x5f,0x69,0x6e,0x5d,0x5d,0x2c,
0x20,0x74,0x65,0x78,0x74,0x75,0x72,0x65,0x32,0x64,0x3c,0x66,0x6c,0x6f,0x61,0x74,
0x3e,0x20,0x74,0x65,0x78,0x20,0x5b,0x5b,0x74,0x65,0x78,0x74,0x75,0x72,0x65,0x28,
0x30,0x29,0x5d,0x5d,0x2c,0x20,0x73,0x61,0x6d,0x70,0x6c,0x65,0x72,0x20,0x73,0x6d,
0x70,0x20,0x5b,0x5b,0x73,0x61,0x6d,0x70,0x6c,0x65,0x72,0x28,0x30,0x29,0x5d,0x5d,
0x29,0x0a,0x7b,0x0a,0x20,0x20,0x20,0x20,0x6d,0x61,0x69,0x6e,0x30,0x5f,0x6f,0x75,
0x74,0x20,0x6f,0x75,0x74,0x20,0x3d,0x20,0x7b,0x7d,0x3b,0x0a,0x20,0x20,0x20,0x20,
0x6f,0x75,0x74,0x2e,0x66,0x72,0x61,0x67,0x5f,0x63,0x6f,0x6c,0x6f,0x72,0x20,0x3d,
0x20,0x74,0x65,0x78,0x2e,0x73,0x61,0x6d,0x70,0x6c,0x65,0x28,0x73,0x6d,0x70,0x2c,
0x20,0x69,0x6e,0x2e,0x76,0x5f,0x75,0x76,0x29,0x20,0x2a,0x20,0x69,0x6e,0x2e,0x76,
0x5f,0x63,0x6f,0x6c,0x6f,0x72,0x3b,0x0a,0x20,0x20,0x20,0x20,0x72,0x65,0x74,0x75,
0x72,0x6e,0x20,0x6f,0x75,0x74,0x3b,0x0a,0x7d,0x0a,0x0a,0x00,
];
ui_shader_desc :: (backend: sg_backend) -> sg_shader_desc {
desc: sg_shader_desc;
desc.label = "ui_shader";
if backend == {
case .GLCORE;
desc.vertex_func.source = xx *vs_ui_source_glsl430;
desc.vertex_func.entry = "main";
desc.fragment_func.source = xx *fs_ui_source_glsl430;
desc.fragment_func.entry = "main";
desc.attrs[0].base_type = .FLOAT;
desc.attrs[0].glsl_name = "position";
desc.attrs[1].base_type = .FLOAT;
desc.attrs[1].glsl_name = "uv";
desc.attrs[2].base_type = .FLOAT;
desc.attrs[2].glsl_name = "color0";
desc.uniform_blocks[0].stage = .VERTEX;
desc.uniform_blocks[0].layout = .STD140;
desc.uniform_blocks[0].size = 16;
desc.uniform_blocks[0].glsl_uniforms[0].type = .FLOAT4;
desc.uniform_blocks[0].glsl_uniforms[0].array_count = 1;
desc.uniform_blocks[0].glsl_uniforms[0].glsl_name = "ui_vs_params";
desc.images[0].stage = .FRAGMENT;
desc.images[0].multisampled = false;
desc.images[0].image_type = ._2D;
desc.images[0].sample_type = .FLOAT;
desc.samplers[0].stage = .FRAGMENT;
desc.samplers[0].sampler_type = .FILTERING;
desc.image_sampler_pairs[0].stage = .FRAGMENT;
desc.image_sampler_pairs[0].image_slot = 0;
desc.image_sampler_pairs[0].sampler_slot = 0;
desc.image_sampler_pairs[0].glsl_name = "tex_smp";
case .GLES3;
desc.vertex_func.source = xx *vs_ui_source_glsl300es;
desc.vertex_func.entry = "main";
desc.fragment_func.source = xx *fs_ui_source_glsl300es;
desc.fragment_func.entry = "main";
desc.attrs[0].base_type = .FLOAT;
desc.attrs[0].glsl_name = "position";
desc.attrs[1].base_type = .FLOAT;
desc.attrs[1].glsl_name = "uv";
desc.attrs[2].base_type = .FLOAT;
desc.attrs[2].glsl_name = "color0";
desc.uniform_blocks[0].stage = .VERTEX;
desc.uniform_blocks[0].layout = .STD140;
desc.uniform_blocks[0].size = 16;
desc.uniform_blocks[0].glsl_uniforms[0].type = .FLOAT4;
desc.uniform_blocks[0].glsl_uniforms[0].array_count = 1;
desc.uniform_blocks[0].glsl_uniforms[0].glsl_name = "ui_vs_params";
desc.images[0].stage = .FRAGMENT;
desc.images[0].multisampled = false;
desc.images[0].image_type = ._2D;
desc.images[0].sample_type = .FLOAT;
desc.samplers[0].stage = .FRAGMENT;
desc.samplers[0].sampler_type = .FILTERING;
desc.image_sampler_pairs[0].stage = .FRAGMENT;
desc.image_sampler_pairs[0].image_slot = 0;
desc.image_sampler_pairs[0].sampler_slot = 0;
desc.image_sampler_pairs[0].glsl_name = "tex_smp";
case .METAL_MACOS;
desc.vertex_func.source = xx *vs_ui_source_metal_macos;
desc.vertex_func.entry = "main0";
desc.fragment_func.source = xx *fs_ui_source_metal_macos;
desc.fragment_func.entry = "main0";
desc.attrs[0].base_type = .FLOAT;
desc.attrs[1].base_type = .FLOAT;
desc.attrs[2].base_type = .FLOAT;
desc.uniform_blocks[0].stage = .VERTEX;
desc.uniform_blocks[0].layout = .STD140;
desc.uniform_blocks[0].size = 16;
desc.uniform_blocks[0].msl_buffer_n = 0;
desc.images[0].stage = .FRAGMENT;
desc.images[0].multisampled = false;
desc.images[0].image_type = ._2D;
desc.images[0].sample_type = .FLOAT;
desc.images[0].msl_texture_n = 0;
desc.samplers[0].stage = .FRAGMENT;
desc.samplers[0].sampler_type = .FILTERING;
desc.samplers[0].msl_sampler_n = 0;
desc.image_sampler_pairs[0].stage = .FRAGMENT;
desc.image_sampler_pairs[0].image_slot = 0;
desc.image_sampler_pairs[0].sampler_slot = 0;
}
return desc;
}

View File

@ -0,0 +1,6 @@
@include common.glsl
@include ui.glsl
@include composite.glsl
@include trile.glsl
@include water.glsl
@include billboard.glsl

View File

@ -0,0 +1,48 @@
@vs vs_trile
@include_block view
layout(binding=1) uniform trile_params {
vec4 orientations[72];
};
in vec3 position;
in float swatch;
in vec4 instance; // xyz = position, w = orientation
in float tint;
out float v_clip;
out float v_swatch;
out float v_tint;
void main() {
int ori = int(instance.w + 0.5) * 3;
vec3 p = position - 0.5;
vec3 world = vec3(dot(orientations[ori].xyz, p),
dot(orientations[ori + 1].xyz, p),
dot(orientations[ori + 2].xyz, p)) + 0.5 + instance.xyz;
gl_Position = view_proj * vec4(world, 1.0);
v_clip = dot(vec4(world, 1.0), clip);
v_swatch = swatch;
v_tint = tint;
}
@end
@fs fs_trile
in float v_clip;
in float v_swatch;
in float v_tint;
out vec4 frag_color;
layout(binding=0) uniform texture2D palette;
layout(binding=0) uniform sampler palette_smp;
void main() {
if (v_clip < 0.0) discard;
vec3 albedo = texelFetch(sampler2D(palette, palette_smp), ivec2(int(v_swatch + 0.5), 0), 0).rgb;
if (v_tint > 1.5) albedo = mix(albedo, vec3(1.0, 0.2, 0.2), 0.5);
else if (v_tint > 0.5) albedo = mix(albedo, vec3(0.2, 1.0, 0.2), 0.5);
frag_color = vec4(albedo, 1.0);
}
@end
@program trile vs_trile fs_trile

View File

@ -0,0 +1,64 @@
@vs vs_water
@include_block view
@include_block quad
layout(binding=1) uniform water_vs_params {
float height;
float half_size;
};
out vec3 v_to_camera;
out vec4 v_normal_uv;
void main() {
vec2 xz = camera.xz + (quad_corner(gl_VertexIndex) * 2.0 - 1.0) * half_size;
vec3 world = vec3(xz.x, height, xz.y);
gl_Position = view_proj * vec4(world, 1.0);
vec2 flow = camera.w * vec2(-0.005, -0.012);
v_normal_uv = vec4(xz * 0.4 + flow * 1.5, xz * 0.1 + flow * 1.7);
v_to_camera = camera.xyz - world;
}
@end
@fs fs_water
in vec3 v_to_camera;
in vec4 v_normal_uv;
out vec4 frag_color;
layout(binding=2) uniform water_fs_params {
vec3 color;
float surface;
float reflectivity;
float distortion;
};
layout(binding=0) uniform texture2D reflection;
layout(binding=1) uniform texture2D normals;
layout(binding=0) uniform sampler reflection_smp;
layout(binding=1) uniform sampler normals_smp;
void main() {
if (surface < 0.5) {
frag_color = vec4(color, 1.0);
return;
}
vec3 n1 = texture(sampler2D(normals, normals_smp), v_normal_uv.xy).xzy * 2.0 - 1.0;
vec3 n2 = texture(sampler2D(normals, normals_smp), v_normal_uv.zw).xzy * 2.0 - 1.0;
vec3 normal = normalize(n1 + n2);
float facing = max(dot(normalize(v_to_camera), normal), 0.0);
float fresnel = min(1.0, 0.02 + 0.98 * pow(1.0 - facing, 5.0) + 0.45);
vec2 uv = gl_FragCoord.xy / vec2(textureSize(sampler2D(reflection, reflection_smp), 0));
uv.y = 1.0 - uv.y;
vec3 reflected = texture(sampler2D(reflection, reflection_smp), uv + normal.xz * distortion).rgb;
vec3 surface_color = mix(color, reflected, mix(fresnel, 1.0, reflectivity));
float alpha = mix(mix(0.3, 0.5, fresnel), 1.0, reflectivity);
frag_color = vec4(surface_color, alpha);
}
@end
@program water vs_water fs_water

View File

@ -0,0 +1,133 @@
triles_init :: (orientations: [ORIENTATION_COUNT]Matrix3) {
desc := pipeline_desc(trile_shader_desc(sg_query_backend()), .VIEW, depth_write = true);
desc.layout.buffers[1].step_func = .PER_INSTANCE;
desc.layout.attrs[ATTR_trile_position] = .{ format = .FLOAT3 };
desc.layout.attrs[ATTR_trile_swatch] = .{ format = .FLOAT };
desc.layout.attrs[ATTR_trile_instance] = .{ format = .FLOAT4, buffer_index = 1 };
desc.layout.attrs[ATTR_trile_tint] = .{ format = .FLOAT, buffer_index = 1 };
desc.cull_mode = .BACK;
desc.face_winding = .CCW;
triles.pipeline = sg_make_pipeline(*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) -> u32 {
id, mesh := slot_add(*triles.meshes);
make_mesh(mesh, vertices);
return id;
}
renderer_trile_mesh_update :: (id: u32, vertices: []Trile_Vertex) {
mesh := slot_get(*triles.meshes, id);
free_mesh(mesh);
make_mesh(mesh, vertices);
}
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, roughness: u8, metallic: u8, emittance: u8) {
for 0..2 triles.palette[id][it] = color[it];
triles.palette[id][3] = 255;
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 = 1, 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};
array_add(*draws, .{group.mesh.id, .{position, xx it.orientation, 0}});
}
}
for scene.triles {
if it.view.index != view_index continue;
preview : s64 = ifx it.tint != .NONE then PREVIEW_KEY else 0;
array_add(*draws, .{preview | it.mesh.id, .{it.position, xx it.orientation, xx it.tint}});
}
batch_instances(view_index, draws, *triles.batches, *triles.instances);
}
stream_upload(*triles.stream, triles.instances);
}
triles_draw :: (pass: *Pass) {
sg_apply_pipeline(triles.pipeline);
apply_uniforms(UB_view_params, *pass.params);
apply_uniforms(UB_trile_params, *triles.params);
for triles.batches {
if it.view_index != pass.view_index || (pass.reflection && it.key & PREVIEW_KEY) continue;
mesh := slot_get(*triles.meshes, xx (it.key & ~PREVIEW_KEY));
if !mesh || mesh.vertex_count == 0 continue;
bind : sg_bindings;
bind.vertex_buffers[0] = mesh.vertices;
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.vertex_count, it.count);
}
}
#scope_file
Mesh :: struct {
vertices : sg_buffer;
vertex_count : s32;
}
Instance :: struct {
position : Vector3;
orientation : float;
tint : float;
}
PREVIEW_KEY :: 1 << 32;
triles : struct {
pipeline : sg_pipeline;
params : Trile_Params;
meshes : Slots(Mesh);
palette : [256][4]u8;
palette_image : sg_image;
palette_dirty : bool;
instances : [..]Instance;
batches : [..]Batch;
stream : Stream_Buffer;
}
make_mesh :: (mesh: *Mesh, vertices: []Trile_Vertex) {
mesh.vertex_count = xx vertices.count;
if vertices.count {
mesh.vertices = sg_make_buffer(*(sg_buffer_desc.{ data = .{ vertices.data, xx (vertices.count * size_of(Trile_Vertex)) } }));
}
}
free_mesh :: (mesh: *Mesh) {
if mesh.vertex_count sg_destroy_buffer(mesh.vertices);
mesh.* = .{};
}

View File

@ -1,38 +1,15 @@
ui_init :: () {
shader_desc := ui_shader_desc(sg_query_backend());
desc : sg_pipeline_desc;
desc.shader = sg_make_shader(*shader_desc);
desc := pipeline_desc(ui_shader_desc(sg_query_backend()), .SCREEN, blend = true);
desc.layout.attrs[ATTR_ui_position] = .{ format = .FLOAT2 };
desc.layout.attrs[ATTR_ui_uv] = .{ format = .FLOAT2 };
desc.layout.attrs[ATTR_ui_color0] = .{ format = .FLOAT4 };
desc.colors[0].blend = .{
enabled = true,
src_factor_rgb = .SRC_ALPHA,
dst_factor_rgb = .ONE_MINUS_SRC_ALPHA,
};
ui.pipeline = sg_make_pipeline(*desc);
ui.sampler = sg_make_sampler(*(sg_sampler_desc.{
wrap_u = .CLAMP_TO_EDGE,
wrap_v = .CLAMP_TO_EDGE,
min_filter = .NEAREST,
mag_filter = .NEAREST,
}));
}
ui_upload :: (scene: *Scene) {
array_reset_keeping_memory(*ui.vertices);
for scene.ui array_add(*ui.vertices, ..it.vertices);
if ui.vertices.count == 0 return;
size := ui.vertices.count * size_of(Ui_Vertex);
if size > ui.buffer_size {
if ui.buffer_size sg_destroy_buffer(ui.buffer);
ui.buffer_size = max(size, ui.buffer_size * 2);
ui.buffer = sg_make_buffer(*(sg_buffer_desc.{ size = xx ui.buffer_size, usage = .STREAM }));
}
sg_update_buffer(ui.buffer, *(sg_range.{ ui.vertices.data, xx size }));
stream_upload(*ui.stream, ui.vertices);
}
ui_draw :: (scene: *Scene, w: s32, h: s32) {
@ -40,11 +17,11 @@ ui_draw :: (scene: *Scene, w: s32, h: s32) {
sg_apply_pipeline(ui.pipeline);
params := Ui_Vs_Params.{ screen_size = .[xx w, xx h] };
sg_apply_uniforms(UB_ui_vs_params, *(sg_range.{ *params, size_of(Ui_Vs_Params) }));
apply_uniforms(UB_ui_vs_params, *params);
bind : sg_bindings;
bind.vertex_buffers[0] = ui.buffer;
bind.samplers[SMP_smp] = ui.sampler;
bind.vertex_buffers[0] = ui.stream.buffer;
bind.samplers[SMP_smp] = samplers.nearest_clamp;
first : s32;
for scene.ui {
@ -66,8 +43,6 @@ ui_draw :: (scene: *Scene, w: s32, h: s32) {
ui : struct {
pipeline : sg_pipeline;
sampler : sg_sampler;
buffer : sg_buffer;
buffer_size : int;
stream : Stream_Buffer;
vertices : [..]Ui_Vertex;
}

View File

@ -0,0 +1,72 @@
views_init :: () {
desc := pipeline_desc(composite_shader_desc(sg_query_backend()), .SCREEN);
views.composite = sg_make_pipeline(*desc);
}
view_begin_pass :: (index: int, width: s32, height: s32, reflection: bool) {
if width != views.width || height != views.height {
for views.targets {
destroy_target(it.main);
destroy_target(it.reflection);
}
array_reset_keeping_memory(*views.targets);
views.width = width;
views.height = height;
}
while views.targets.count <= index array_add(*views.targets, .{make_target(width, height), make_target(width, height)});
targets := views.targets[index];
action : sg_pass_action;
action.colors[0] = .{ load_action = .CLEAR, clear_value = .{0, 0, 0, 1} };
action.depth = .{ load_action = .CLEAR, clear_value = 1 };
target := ifx reflection then targets.reflection else targets.main;
sg_begin_pass(*(sg_pass.{ action = action, attachments = target.attachments }));
}
view_reflection_image :: (index: int) -> sg_image {
return views.targets[index].reflection.color;
}
view_composite :: (index: int) {
sg_apply_pipeline(views.composite);
params := Composite_Vs_Params.{ flip_y = ifx sg_query_features().origin_top_left then 1.0 else 0.0 };
apply_uniforms(UB_composite_vs_params, *params);
bind : sg_bindings;
bind.images[IMG_view_color] = views.targets[index].main.color;
bind.samplers[SMP_composite_smp] = samplers.nearest_clamp;
sg_apply_bindings(*bind);
sg_draw(0, 3, 1);
}
#scope_file
View_Target :: struct {
color : sg_image;
depth : sg_image;
attachments : sg_attachments;
}
views : struct {
composite : sg_pipeline;
targets : [..]struct { main, reflection: View_Target; };
width : s32;
height : s32;
}
make_target :: (width: s32, height: s32) -> View_Target {
t : View_Target;
t.color = sg_make_image(*(sg_image_desc.{ render_target = true, width = width, height = height, pixel_format = VIEW_COLOR_FORMAT, sample_count = 1 }));
t.depth = sg_make_image(*(sg_image_desc.{ render_target = true, width = width, height = height, pixel_format = VIEW_DEPTH_FORMAT, sample_count = 1 }));
desc : sg_attachments_desc;
desc.colors[0].image = t.color;
desc.depth_stencil.image = t.depth;
t.attachments = sg_make_attachments(*desc);
return t;
}
destroy_target :: (t: View_Target) {
sg_destroy_attachments(t.attachments);
sg_destroy_image(t.color);
sg_destroy_image(t.depth);
}

View File

@ -0,0 +1,42 @@
WATER_HALF_SIZE :: 1000.0;
water_init :: () {
desc := pipeline_desc(water_shader_desc(sg_query_backend()), .VIEW, blend = true);
water.pipeline = sg_make_pipeline(*desc);
}
water_draw :: (pass: *Pass) {
env := pass.view.environment;
if pass.reflection || !pass.view.draw_world || !env || !env.water return;
sg_apply_pipeline(water.pipeline);
apply_uniforms(UB_view_params, *pass.params);
bind : sg_bindings;
bind.images[IMG_reflection] = view_reflection_image(pass.view_index);
bind.images[IMG_normals] = texture_image(env.water_normals.id);
bind.samplers[SMP_reflection_smp] = samplers.linear_clamp;
bind.samplers[SMP_normals_smp] = samplers.linear_repeat;
sg_apply_bindings(*bind);
draw_plane :: (height: float, color: Vector3, surface: bool) #expand {
vs := Water_Vs_Params.{ height = height, half_size = WATER_HALF_SIZE };
fs := Water_Fs_Params.{
color = color.component,
surface = xx surface,
reflectivity = env.water_reflectivity,
distortion = env.water_distortion,
};
apply_uniforms(UB_water_vs_params, *vs);
apply_uniforms(UB_water_fs_params, *fs);
sg_draw(0, 6, 1);
}
draw_plane(0, env.deep_color, false);
draw_plane(env.reflection_height, env.water_color, true);
}
#scope_file
water : struct {
pipeline : sg_pipeline;
}

View File

@ -2,6 +2,7 @@
// scene.jai and text.jai.
#load "scene.jai";
#load "trile_mesher.jai";
#load "text.jai";
#load "camera.jai";
#load "debug_draw.jai";
@ -16,7 +17,7 @@
render :: () {
text_end_frame();
w, h := get_window_size();
scene_end_frame(w, h);
scene_end_frame(w, h, xx get_time());
g_debug_line_count = 0;
g_overlay_line_count = 0;
}

View File

@ -9,6 +9,13 @@ Font_Face :: struct { id: s32; }
View_Id :: struct { index: s32; }
Environment :: struct {
reflection_height : float;
water : bool;
water_color : Vector3 = .{1, 1, 1};
deep_color : Vector3 = .{1, 1, 1};
water_reflectivity : float;
water_distortion : float = 0.007;
water_normals : Texture;
}
View :: struct {
@ -46,14 +53,19 @@ Ui_Vertex :: struct {
}
trile_mesh_create :: (trile: *Trile) -> Trile_Mesh {
scene.trile_mesh_count += 1;
return .{scene.trile_mesh_count};
return .{renderer_trile_mesh_create(mesh_trile(trile))};
}
trile_mesh_update :: (mesh: Trile_Mesh, trile: *Trile) {
renderer_trile_mesh_update(mesh.id, mesh_trile(trile));
}
trile_mesh_destroy :: (mesh: Trile_Mesh) {
renderer_trile_mesh_destroy(mesh.id);
}
swatch_set :: (id: u8, color: [3]u8, roughness: u8, metallic: u8, emittance: u8) {
renderer_swatch_set(id, color, roughness, metallic, emittance);
}
// Pixels are RGBA8.
@ -74,8 +86,9 @@ World_Group :: struct {
instances : []Trile_Instance;
}
world_set_chunk :: (key: Chunk_Key, groups: []World_Group) {
world_set_chunk :: (key: Chunk_Key, origin: Vector3, groups: []World_Group) {
chunk := find_or_add(*scene.chunks, key);
chunk.origin = origin;
for chunk.groups array_free(it.instances);
array_reset_keeping_memory(*chunk.groups);
for groups {
@ -103,6 +116,13 @@ world_clear :: () {
add_view :: (view: View) -> View_Id {
array_add(*scene.views, view);
camera := view.camera;
array_add(*scene.view_proj, create_viewproj(*camera));
height := ifx view.environment then view.environment.reflection_height else 0;
camera.position.y = 2 * height - camera.position.y;
camera.target.y = 2 * height - camera.target.y;
array_add(*scene.reflection_view_proj, create_viewproj(*camera));
return .{xx (scene.views.count - 1)};
}
@ -133,6 +153,7 @@ ui_scissor_clear :: () {
}
Scene_Chunk :: struct {
origin : Vector3;
groups : [..]World_Group;
}
@ -168,12 +189,18 @@ Scene_Ui :: struct {
clear_scissor : bool;
}
Scene :: struct {
trile_mesh_count : u32;
Trile_Vertex :: struct {
position : Vector3;
swatch : float;
}
Scene :: struct {
chunks : Table(Chunk_Key, Scene_Chunk, chunk_key_hash, chunk_key_compare);
views : [..]View;
view_proj : [..]Matrix4;
reflection_view_proj : [..]Matrix4;
time : float;
triles : [..]Scene_Trile;
billboards : [..]Scene_Billboard;
particles : [..]Scene_Particles;
@ -181,16 +208,24 @@ Scene :: struct {
}
scene_init :: () {
renderer_init();
orientations : [ORIENTATION_COUNT]Matrix3;
for *orientations {
m := orientation_matrix(xx it_index).m;
for r: 0..2 for c: 0..2 it.coef[r][c] = xx m[r][c];
}
renderer_init(orientations);
}
scene_shutdown :: () {
renderer_shutdown();
}
scene_end_frame :: (width: s32, height: s32) {
scene_end_frame :: (width: s32, height: s32, time: float) {
scene.time = time;
renderer_render(*scene, width, height);
array_reset_keeping_memory(*scene.views);
array_reset_keeping_memory(*scene.view_proj);
array_reset_keeping_memory(*scene.reflection_view_proj);
array_reset_keeping_memory(*scene.triles);
array_reset_keeping_memory(*scene.billboards);
array_reset_keeping_memory(*scene.particles);

View File

@ -0,0 +1,57 @@
mesh_trile :: (trile: *Trile) -> []Trile_Vertex {
vertices : [..]Trile_Vertex;
vertices.allocator = temp;
for axis: 0..2 for sign: s32.[1, -1] {
u_axis := (axis + 1) % 3;
v_axis := (axis + 2) % 3;
for depth: 0..15 {
mask : [16][16]u8;
for u: 0..15 for v: 0..15 {
c : [3]s32;
c[axis] = xx depth; c[u_axis] = xx u; c[v_axis] = xx v;
swatch := trixel(trile, c);
c[axis] += sign;
if trixel(trile, c) == 0 mask[u][v] = swatch;
}
for u0: 0..15 for v0: 0..15 {
swatch := mask[u0][v0];
if !swatch continue;
v1 := v0 + 1;
while v1 < 16 && mask[u0][v1] == swatch v1 += 1;
u1 := u0 + 1;
while u1 < 16 && row_matches(*mask, u1, v0, v1, swatch) u1 += 1;
for u: u0..u1-1 for v: v0..v1-1 mask[u][v] = 0;
plane := cast(float)(depth + ifx sign > 0 then 1 else 0);
corner :: (axis: int, u_axis: int, v_axis: int, plane: float, u: int, v: int) -> Vector3 #expand {
p : Vector3;
p.component[axis] = plane * TRIXEL_SIZE;
p.component[u_axis] = u * TRIXEL_SIZE;
p.component[v_axis] = v * TRIXEL_SIZE;
return p;
}
p0 := corner(axis, u_axis, v_axis, plane, u0, v0);
p1 := corner(axis, u_axis, v_axis, plane, u1, v0);
p2 := corner(axis, u_axis, v_axis, plane, u1, v1);
p3 := corner(axis, u_axis, v_axis, plane, u0, v1);
if sign < 0 p1, p3 = p3, p1;
for Vector3.[p0, p1, p2, p0, p2, p3] array_add(*vertices, .{it, xx swatch});
}
}
}
return vertices;
}
#scope_file
row_matches :: (mask: *[16][16]u8, u: int, v0: int, v1: int, swatch: u8) -> bool {
for v: v0..v1-1 if mask.*[u][v] != swatch return false;
return true;
}
trixel :: (trile: *Trile, c: [3]s32) -> u8 {
for c if it < 0 || it > 15 return 0;
return trile.trixels[c[0]][c[1]][c[2]];
}

View File

@ -158,12 +158,18 @@ swatch_add :: (name: string, color: [3]u8, roughness: u8 = 4, metallic: u8 = 0,
for 1..MAX_SWATCHES-1 {
if g_swatches[it].used continue;
g_swatches[it] = .{used = true, name = copy_string(name), color = color, roughness = roughness, metallic = metallic, emittance = emittance};
swatch_changed(xx it);
return cast(u8) it, true;
}
log_error("All % swatches are in use", MAX_SWATCHES - 1);
return 0, false;
}
swatch_changed :: (id: u8) {
s := g_swatches[id];
swatch_set(id, s.color, s.roughness, s.metallic, s.emittance);
}
swatch_color :: (id: u8) -> Vector3 {
c := g_swatches[id].color;
return .{c[0] / 255.0, c[1] / 255.0, c[2] / 255.0};
@ -212,6 +218,7 @@ lswatches :: () {
if it.id < 1 || it.id >= MAX_SWATCHES continue;
g_swatches[it.id] = .{used = true, name = sprint("%", it.name), color = it.color, roughness = it.roughness, metallic = it.metallic, emittance = it.emittance};
}
for 0..MAX_SWATCHES-1 swatch_changed(xx it);
} @Command
Palette_Entry :: struct {

View File

@ -251,7 +251,8 @@ upload_chunk :: (chunk: *Chunk) {
mesh, ok := get_trile_mesh(it.trile_name);
if ok array_add(*groups, .{mesh, it.instances});
}
world_set_chunk(chunk.coord, groups);
origin := Vector3.{xx (chunk.coord.x * CHUNK_SIZE), xx (chunk.coord.y * CHUNK_SIZE), xx (chunk.coord.z * CHUNK_SIZE)};
world_set_chunk(chunk.coord, origin, groups);
}
get_note_locations :: (note : string) -> []Vector3 {
@ -720,6 +721,19 @@ World_Config :: struct {
INDOOR_REFLECTION_PLANE :: 1.0;
world_environment :: (world: *World) -> *Environment {
env := *world.environment;
conf := *world.conf;
env.reflection_height = effective_plane_height(conf);
env.water = !conf.indoor;
env.water_color = conf.waterColor;
env.deep_color = conf.deepColor;
env.water_reflectivity = conf.waterReflectivity;
env.water_distortion = conf.waterDistortion;
env.water_normals = get_texture_from_pack("core", "utiltex/water_small.png");
return env;
}
// The planar reflection height: the water surface outdoors, the floor indoors.
effective_plane_height :: (wc: *World_Config) -> float {
if wc.indoor return INDOOR_REFLECTION_PLANE;