work
This commit is contained in:
parent
0b8c135d11
commit
b45554afaa
@ -228,7 +228,7 @@ wasm_build :: (opts: Build_Options, trueno_opts: Trueno_Build_Options) {
|
|||||||
"emcc",
|
"emcc",
|
||||||
"src/platform_specific/main.c", "dist/main.o", "modules/sokol-jai/sokol/gfx/sokol_gfx_wasm_gl_release.a", "modules/sokol-jai/sokol/log/sokol_log_wasm_gl_release.a", "modules/sokol-jai/sokol/audio/sokol_audio_wasm_gl_release.a", "modules/sokol-jai/sokol/time/sokol_time_wasm_gl_release.a", "modules/sokol-jai/sokol/app/sokol_app_wasm_gl_release.a", "modules/sokol-jai/sokol/glue/sokol_glue_wasm_gl_release.a", "modules/sokol-jai/sokol/fetch/sokol_fetch_wasm_gl_release.a", "modules/Fontstash/wasm/fontstash.a",
|
"src/platform_specific/main.c", "dist/main.o", "modules/sokol-jai/sokol/gfx/sokol_gfx_wasm_gl_release.a", "modules/sokol-jai/sokol/log/sokol_log_wasm_gl_release.a", "modules/sokol-jai/sokol/audio/sokol_audio_wasm_gl_release.a", "modules/sokol-jai/sokol/time/sokol_time_wasm_gl_release.a", "modules/sokol-jai/sokol/app/sokol_app_wasm_gl_release.a", "modules/sokol-jai/sokol/glue/sokol_glue_wasm_gl_release.a", "modules/sokol-jai/sokol/fetch/sokol_fetch_wasm_gl_release.a", "modules/Fontstash/wasm/fontstash.a",
|
||||||
"modules/sokol-jai/sokol/stbi/stb_image.a",
|
"modules/sokol-jai/sokol/stbi/stb_image.a",
|
||||||
"modules/sokol-jai/sokol/stbv/stb_vorbis.a",
|
"modules/stb_vorbis/wasm/stb_vorbis.a",
|
||||||
"-o", "dist/index.html",
|
"-o", "dist/index.html",
|
||||||
"-sSTACK_SIZE=10MB",
|
"-sSTACK_SIZE=10MB",
|
||||||
"-sALLOW_MEMORY_GROWTH",
|
"-sALLOW_MEMORY_GROWTH",
|
||||||
|
|||||||
@ -265,6 +265,8 @@ STBVorbisError :: enum u32 {
|
|||||||
} else #if CPU == .ARM64 {
|
} else #if CPU == .ARM64 {
|
||||||
lib_stb_vorbis :: #library "switch2/stb_vorbis";
|
lib_stb_vorbis :: #library "switch2/stb_vorbis";
|
||||||
}
|
}
|
||||||
|
} else #if OS == .WASM {
|
||||||
|
lib_stb_vorbis :: #library,no_dll "wasm/stb_vorbis";
|
||||||
} else {
|
} else {
|
||||||
#assert false;
|
#assert false;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -149,6 +149,8 @@ FOOTER_TEMPLATE :: #string END
|
|||||||
} else #if CPU == .ARM64 {
|
} else #if CPU == .ARM64 {
|
||||||
%2 :: #library "switch2/%1";
|
%2 :: #library "switch2/%1";
|
||||||
}
|
}
|
||||||
|
} else #if OS == .WASM {
|
||||||
|
%2 :: #library,no_dll "wasm/%1";
|
||||||
} else {
|
} else {
|
||||||
#assert false;
|
#assert false;
|
||||||
}
|
}
|
||||||
|
|||||||
BIN
modules/stb_vorbis/wasm/stb_vorbis.a
Normal file
BIN
modules/stb_vorbis/wasm/stb_vorbis.a
Normal file
Binary file not shown.
@ -1,4 +1,4 @@
|
|||||||
master_volume 0.205032
|
master_volume 0.05216
|
||||||
music_volume 1
|
music_volume 1
|
||||||
sfx_volume 1
|
sfx_volume 1
|
||||||
fullscreen 0
|
fullscreen 1
|
||||||
|
|||||||
@ -92,6 +92,17 @@ mixer_queue_next :: (current_audio: *Audio_Data, next_task: Mixer_Play_Task) {
|
|||||||
array_add(*g_mixer.tasks, next_task);
|
array_add(*g_mixer.tasks, next_task);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
mixer_stop :: (audio: *Audio_Data) {
|
||||||
|
mixer_lock(*g_mixer);
|
||||||
|
defer mixer_unlock(*g_mixer);
|
||||||
|
for g_mixer.tasks {
|
||||||
|
if it.audio != audio continue;
|
||||||
|
if it.queued_next then free(it.queued_next);
|
||||||
|
free(it.audio_state);
|
||||||
|
remove it;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
mixer_get_samples :: (buffer: *float, frame_count: s32, channel_count: s32) {
|
mixer_get_samples :: (buffer: *float, frame_count: s32, channel_count: s32) {
|
||||||
mixer_lock(*g_mixer);
|
mixer_lock(*g_mixer);
|
||||||
defer mixer_unlock(*g_mixer);
|
defer mixer_unlock(*g_mixer);
|
||||||
|
|||||||
@ -90,6 +90,7 @@ Pass :: struct {
|
|||||||
kind : Pass_Kind;
|
kind : Pass_Kind;
|
||||||
params : View_Params;
|
params : View_Params;
|
||||||
shadow : Shadow_Params;
|
shadow : Shadow_Params;
|
||||||
|
frustum : [6]Vector4;
|
||||||
}
|
}
|
||||||
|
|
||||||
make_pass :: (scene: *Scene, view_index: int, kind: Pass_Kind) -> Pass {
|
make_pass :: (scene: *Scene, view_index: int, kind: Pass_Kind) -> Pass {
|
||||||
@ -110,6 +111,7 @@ make_pass :: (scene: *Scene, view_index: int, kind: Pass_Kind) -> Pass {
|
|||||||
camera = pass.view.camera.target + env.sun_direction * 1000;
|
camera = pass.view.camera.target + env.sun_direction * 1000;
|
||||||
view_proj = scene.shadow_view_proj[view_index];
|
view_proj = scene.shadow_view_proj[view_index];
|
||||||
}
|
}
|
||||||
|
pass.frustum = frustum_planes(view_proj);
|
||||||
_, inv_view_proj := inverse(view_proj);
|
_, inv_view_proj := inverse(view_proj);
|
||||||
pass.params.view_proj = view_proj.floats;
|
pass.params.view_proj = view_proj.floats;
|
||||||
pass.params.inv_view_proj = inv_view_proj.floats;
|
pass.params.inv_view_proj = inv_view_proj.floats;
|
||||||
@ -131,6 +133,22 @@ make_pass :: (scene: *Scene, view_index: int, kind: Pass_Kind) -> Pass {
|
|||||||
return pass;
|
return pass;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
frustum_planes :: (m: Matrix4) -> [6]Vector4 {
|
||||||
|
column :: (m: Matrix4, j: int) -> Vector4 { return .{m.coef[0][j], m.coef[1][j], m.coef[2][j], m.coef[3][j]}; }
|
||||||
|
x, y, z, w := column(m, 0), column(m, 1), column(m, 2), column(m, 3);
|
||||||
|
return .[w + x, w - x, w + y, w - y, w + z, w - z];
|
||||||
|
}
|
||||||
|
|
||||||
|
box_in_frustum :: (planes: [6]Vector4, lo: Vector3, hi: Vector3) -> bool {
|
||||||
|
for planes {
|
||||||
|
x := ifx it.x > 0 then hi.x else lo.x;
|
||||||
|
y := ifx it.y > 0 then hi.y else lo.y;
|
||||||
|
z := ifx it.z > 0 then hi.z else lo.z;
|
||||||
|
if it.x * x + it.y * y + it.z * z + it.w < 0 return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
apply_uniforms :: (slot: s32, params: *$T) {
|
apply_uniforms :: (slot: s32, params: *$T) {
|
||||||
sg_apply_uniforms(slot, *(sg_range.{ params, size_of(T) }));
|
sg_apply_uniforms(slot, *(sg_range.{ params, size_of(T) }));
|
||||||
}
|
}
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@ -92,8 +92,8 @@ layout(binding=4) uniform texture3D probe_atlas; // per brick 32^3 probes, 3 t
|
|||||||
layout(binding=2) uniform sampler probe_smp;
|
layout(binding=2) uniform sampler probe_smp;
|
||||||
|
|
||||||
const float GLOSS_GLOW = 4.0; // how much a full gloss highlight blooms
|
const float GLOSS_GLOW = 4.0; // how much a full gloss highlight blooms
|
||||||
|
const float SUN_GLOW = 1.5;
|
||||||
const float REFLECTED_GLOW_CURVE = 0.4; // below 1, reflected glow fades slower than reflected colour
|
const float REFLECTED_GLOW_CURVE = 0.4; // below 1, reflected glow fades slower than reflected colour
|
||||||
const float GLASS_PROJECTION = 2.0; // how strongly coloured sunlight paints its colour on
|
|
||||||
|
|
||||||
const int PROBE_BRICKS_PER_COLUMN = 16;
|
const int PROBE_BRICKS_PER_COLUMN = 16;
|
||||||
|
|
||||||
@ -160,20 +160,17 @@ void main() {
|
|||||||
if (v_tint > 1.5) albedo = mix(albedo, vec3(1.0, 0.2, 0.2), 0.5);
|
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);
|
else if (v_tint > 0.5) albedo = mix(albedo, vec3(0.2, 1.0, 0.2), 0.5);
|
||||||
vec3 sun = v_facing * sun_shadow(v_world, v_normal);
|
vec3 sun = v_facing * sun_shadow(v_world, v_normal);
|
||||||
vec3 lit = sun * mix(0.25, 1.0, v_ao);
|
|
||||||
vec4 probe = probe_min.w > 0.5 ? probe_light(v_world, v_normal) : vec4(0.0);
|
vec4 probe = probe_min.w > 0.5 ? probe_light(v_world, v_normal) : vec4(0.0);
|
||||||
vec3 shade = v_shade + probe.rgb;
|
vec3 shade = (v_shade + probe.rgb) * mix(0.6, 1.0, v_ao);
|
||||||
vec3 light = mix(shade * mix(0.6, 1.0, v_ao), v_sun, lit);
|
vec3 incoming = v_sun * sun * mix(0.25, 1.0, v_ao);
|
||||||
vec3 color = albedo * light;
|
vec3 color = albedo * (shade + incoming);
|
||||||
|
|
||||||
// Light through glass paints the glass colour onto what it hits, so the pattern
|
|
||||||
// shows on dark surfaces too, not only where the albedo can reflect it.
|
|
||||||
float bright = brightest(sun);
|
float bright = brightest(sun);
|
||||||
if (bright > 0.001) {
|
if (bright > 0.001) {
|
||||||
float tinted = (bright - min(sun.r, min(sun.g, sun.b))) / bright;
|
float tinted = (bright - min(sun.r, min(sun.g, sun.b))) / bright;
|
||||||
color = mix(color, sun * v_sun, min(1.0, tinted * GLASS_PROJECTION));
|
color += incoming * (1.0 - albedo) * tinted;
|
||||||
}
|
}
|
||||||
float glow = swatch.a * EMISSIVE_GLOW;
|
float glow = swatch.a * EMISSIVE_GLOW + brightest(incoming) * SUN_GLOW;
|
||||||
|
|
||||||
// Glossier is both stronger and sharper.
|
// Glossier is both stronger and sharper.
|
||||||
if (material.b > 0.0) {
|
if (material.b > 0.0) {
|
||||||
|
|||||||
@ -73,23 +73,36 @@ triles_upload :: (scene: *Scene) {
|
|||||||
triles.palette_image = sg_make_image(*desc);
|
triles.palette_image = sg_make_image(*desc);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if triles.world_version != scene.world_version world_rebuild(scene);
|
||||||
|
|
||||||
array_reset_keeping_memory(*triles.instances);
|
array_reset_keeping_memory(*triles.instances);
|
||||||
array_reset_keeping_memory(*triles.batches);
|
array_reset_keeping_memory(*triles.batches);
|
||||||
|
chunk_count := triles.world_chunks.count;
|
||||||
|
array_resize(*triles.cut_chunks, scene.views.count * chunk_count, initialize = false);
|
||||||
|
memset(triles.cut_chunks.data, 0, triles.cut_chunks.count);
|
||||||
|
|
||||||
draws : [..]Keyed(Instance);
|
draws : [..]Keyed(Instance);
|
||||||
draws.allocator = temp;
|
draws.allocator = temp;
|
||||||
for view, view_index: scene.views {
|
for view, view_index: scene.views {
|
||||||
array_reset_keeping_memory(*draws);
|
array_reset_keeping_memory(*draws);
|
||||||
if view.draw_world {
|
if view.draw_world {
|
||||||
for chunk: scene.chunks for group: chunk.groups for group.instances {
|
cut_chunks := array_view(triles.cut_chunks, view_index * chunk_count, chunk_count);
|
||||||
position := chunk.origin + Vector3.{xx it.x, xx it.y, xx it.z};
|
for view_cut_chunks(.{xx view_index}) {
|
||||||
cut := trile_cut(.{xx view_index}, group.mesh, position);
|
index := table_find_pointer(*triles.chunk_index, it);
|
||||||
array_add(*draws, .{group.mesh.id, .{position, xx it.orientation, 0, cut}});
|
if index cut_chunks[index.*] = true;
|
||||||
|
}
|
||||||
|
for batch: triles.world_batches {
|
||||||
|
if !batch.cuttable || !cut_chunks[batch.chunk] continue;
|
||||||
|
for batch.first..batch.first + batch.count - 1 {
|
||||||
|
instance := triles.world_instances[it];
|
||||||
|
instance.cut = trile_cut(.{xx view_index}, .{batch.mesh}, instance.position);
|
||||||
|
array_add(*draws, .{MAIN_ONLY_KEY | cast(s64) batch.mesh, instance});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for scene.triles {
|
for scene.triles {
|
||||||
if it.view.index != view_index continue;
|
if it.view.index != view_index continue;
|
||||||
preview : s64 = ifx it.tint != .NONE then PREVIEW_KEY else 0;
|
preview : s64 = ifx it.tint != .NONE then MAIN_ONLY_KEY else 0;
|
||||||
cut := ifx preview then 0.0 else trile_cut(it.view, it.mesh, it.position);
|
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}});
|
array_add(*draws, .{preview | it.mesh.id, .{it.position, xx it.orientation, xx it.tint, cut}});
|
||||||
}
|
}
|
||||||
@ -104,14 +117,13 @@ triles_draw_glass_shadow :: (pass: *Pass) {
|
|||||||
apply_uniforms(UB_view_params, *pass.params);
|
apply_uniforms(UB_view_params, *pass.params);
|
||||||
triles.params.show_hidden = 1;
|
triles.params.show_hidden = 1;
|
||||||
apply_uniforms(UB_trile_params, *triles.params);
|
apply_uniforms(UB_trile_params, *triles.params);
|
||||||
for triles.batches {
|
for pass_draws(pass) {
|
||||||
if it.view_index != pass.view_index || it.key & PREVIEW_KEY continue;
|
mesh := slot_get(*triles.meshes, it.mesh);
|
||||||
mesh := slot_get(*triles.meshes, xx it.key);
|
|
||||||
if !mesh || mesh.index_count == 0 continue;
|
if !mesh || mesh.index_count == 0 continue;
|
||||||
bind : sg_bindings;
|
bind : sg_bindings;
|
||||||
bind.vertex_buffers[0] = mesh.vertices;
|
bind.vertex_buffers[0] = mesh.vertices;
|
||||||
bind.index_buffer = mesh.indices;
|
bind.index_buffer = mesh.indices;
|
||||||
bind.vertex_buffers[1] = triles.stream.buffer;
|
bind.vertex_buffers[1] = it.buffer;
|
||||||
bind.vertex_buffer_offsets[1] = it.first * size_of(Instance);
|
bind.vertex_buffer_offsets[1] = it.first * size_of(Instance);
|
||||||
bind.images[IMG_palette] = triles.palette_image;
|
bind.images[IMG_palette] = triles.palette_image;
|
||||||
bind.samplers[SMP_palette_smp] = samplers.nearest_clamp;
|
bind.samplers[SMP_palette_smp] = samplers.nearest_clamp;
|
||||||
@ -137,15 +149,14 @@ triles_draw :: (pass: *Pass) {
|
|||||||
apply_uniforms(UB_trile_fs_params, *fs_params);
|
apply_uniforms(UB_trile_fs_params, *fs_params);
|
||||||
}
|
}
|
||||||
|
|
||||||
for triles.batches {
|
for pass_draws(pass) {
|
||||||
if it.view_index != pass.view_index || (pass.kind != .MAIN && it.key & PREVIEW_KEY) continue;
|
mesh := slot_get(*triles.meshes, it.mesh);
|
||||||
mesh := slot_get(*triles.meshes, xx (it.key & ~PREVIEW_KEY));
|
|
||||||
if !mesh || mesh.index_count == 0 continue;
|
if !mesh || mesh.index_count == 0 continue;
|
||||||
|
|
||||||
bind : sg_bindings;
|
bind : sg_bindings;
|
||||||
bind.vertex_buffers[0] = mesh.vertices;
|
bind.vertex_buffers[0] = mesh.vertices;
|
||||||
bind.index_buffer = mesh.indices;
|
bind.index_buffer = mesh.indices;
|
||||||
bind.vertex_buffers[1] = triles.stream.buffer;
|
bind.vertex_buffers[1] = it.buffer;
|
||||||
bind.vertex_buffer_offsets[1] = it.first * size_of(Instance);
|
bind.vertex_buffer_offsets[1] = it.first * size_of(Instance);
|
||||||
bind.images[IMG_palette] = triles.palette_image;
|
bind.images[IMG_palette] = triles.palette_image;
|
||||||
bind.samplers[SMP_palette_smp] = samplers.nearest_clamp;
|
bind.samplers[SMP_palette_smp] = samplers.nearest_clamp;
|
||||||
@ -176,7 +187,27 @@ Instance :: struct {
|
|||||||
cut : float; // 0..1 dissolved away from the camera, still casts shadows
|
cut : float; // 0..1 dissolved away from the camera, still casts shadows
|
||||||
}
|
}
|
||||||
|
|
||||||
PREVIEW_KEY :: 1 << 32;
|
MAIN_ONLY_KEY :: 1 << 32;
|
||||||
|
|
||||||
|
World_Chunk :: struct {
|
||||||
|
key : Chunk_Key;
|
||||||
|
origin : Vector3;
|
||||||
|
}
|
||||||
|
|
||||||
|
World_Batch :: struct {
|
||||||
|
mesh : u32;
|
||||||
|
chunk : s32;
|
||||||
|
cuttable : bool;
|
||||||
|
first : s32;
|
||||||
|
count : s32;
|
||||||
|
}
|
||||||
|
|
||||||
|
Trile_Draw :: struct {
|
||||||
|
mesh : u32;
|
||||||
|
buffer : sg_buffer;
|
||||||
|
first : s32;
|
||||||
|
count : s32;
|
||||||
|
}
|
||||||
|
|
||||||
triles : struct {
|
triles : struct {
|
||||||
pipeline : sg_pipeline;
|
pipeline : sg_pipeline;
|
||||||
@ -192,6 +223,94 @@ triles : struct {
|
|||||||
instances : [..]Instance;
|
instances : [..]Instance;
|
||||||
batches : [..]Batch;
|
batches : [..]Batch;
|
||||||
stream : Stream_Buffer;
|
stream : Stream_Buffer;
|
||||||
|
|
||||||
|
world_version : u64;
|
||||||
|
world_chunks : [..]World_Chunk;
|
||||||
|
chunk_index : Table(Chunk_Key, s32, chunk_key_hash, chunk_key_compare);
|
||||||
|
world_batches : [..]World_Batch;
|
||||||
|
world_instances : [..]Instance;
|
||||||
|
world_buffer : sg_buffer;
|
||||||
|
cut_chunks : [..]bool;
|
||||||
|
}
|
||||||
|
|
||||||
|
world_rebuild :: (scene: *Scene) {
|
||||||
|
triles.world_version = scene.world_version;
|
||||||
|
array_reset_keeping_memory(*triles.world_chunks);
|
||||||
|
array_reset_keeping_memory(*triles.world_batches);
|
||||||
|
array_reset_keeping_memory(*triles.world_instances);
|
||||||
|
table_reset(*triles.chunk_index);
|
||||||
|
|
||||||
|
for scene.chunks array_add(*triles.world_chunks, .{it_index, it.origin});
|
||||||
|
quick_sort(triles.world_chunks, (a: World_Chunk, b: World_Chunk) -> s64 {
|
||||||
|
if a.key.x != b.key.x return a.key.x - b.key.x;
|
||||||
|
if a.key.z != b.key.z return a.key.z - b.key.z;
|
||||||
|
return a.key.y - b.key.y;
|
||||||
|
});
|
||||||
|
|
||||||
|
Piece :: struct {
|
||||||
|
key : s64;
|
||||||
|
chunk : s32;
|
||||||
|
group : *World_Group;
|
||||||
|
}
|
||||||
|
pieces : [..]Piece;
|
||||||
|
pieces.allocator = temp;
|
||||||
|
for chunk, index: triles.world_chunks {
|
||||||
|
table_set(*triles.chunk_index, chunk.key, xx index);
|
||||||
|
scene_chunk := table_find_pointer(*scene.chunks, chunk.key);
|
||||||
|
for * scene_chunk.groups array_add(*pieces, .{(cast(s64) it.mesh.id << 32) | index, xx index, it});
|
||||||
|
}
|
||||||
|
quick_sort(pieces, (a: Piece, b: Piece) -> s64 { return a.key - b.key; });
|
||||||
|
|
||||||
|
occluders := world_occluders();
|
||||||
|
for pieces {
|
||||||
|
chunk := triles.world_chunks[it.chunk];
|
||||||
|
batch := World_Batch.{mesh = it.group.mesh.id, chunk = it.chunk, cuttable = trile_mesh_cuttable(it.group.mesh), first = xx triles.world_instances.count};
|
||||||
|
for instance: it.group.instances {
|
||||||
|
if trile_buried(*occluders, chunk.key, instance.x, instance.y, instance.z) continue;
|
||||||
|
position := chunk.origin + Vector3.{xx instance.x, xx instance.y, xx instance.z};
|
||||||
|
array_add(*triles.world_instances, .{position, xx instance.orientation, 0, 0});
|
||||||
|
}
|
||||||
|
batch.count = xx (triles.world_instances.count - batch.first);
|
||||||
|
if batch.count array_add(*triles.world_batches, batch);
|
||||||
|
}
|
||||||
|
|
||||||
|
if triles.world_buffer.id sg_destroy_buffer(triles.world_buffer);
|
||||||
|
triles.world_buffer = .{};
|
||||||
|
if triles.world_instances.count {
|
||||||
|
size := triles.world_instances.count * size_of(Instance);
|
||||||
|
triles.world_buffer = sg_make_buffer(*(sg_buffer_desc.{ data = .{ triles.world_instances.data, xx size } }));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pass_draws :: (pass: *Pass) -> []Trile_Draw {
|
||||||
|
draws : [..]Trile_Draw;
|
||||||
|
draws.allocator = temp;
|
||||||
|
main := pass.kind == .MAIN;
|
||||||
|
|
||||||
|
if pass.view.draw_world && triles.world_batches.count {
|
||||||
|
chunk_count := triles.world_chunks.count;
|
||||||
|
visible := NewArray(chunk_count, bool,, temp);
|
||||||
|
for triles.world_chunks visible[it_index] = box_in_frustum(pass.frustum, it.origin, it.origin + Vector3.{CHUNK_SIZE, CHUNK_SIZE, CHUNK_SIZE});
|
||||||
|
cut_chunks := array_view(triles.cut_chunks, pass.view_index * chunk_count, chunk_count);
|
||||||
|
|
||||||
|
run : Trile_Draw;
|
||||||
|
for triles.world_batches {
|
||||||
|
if !visible[it.chunk] || (main && it.cuttable && cut_chunks[it.chunk]) continue;
|
||||||
|
if run.count && run.mesh == it.mesh && run.first + run.count == it.first {
|
||||||
|
run.count += it.count;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if run.count array_add(*draws, run);
|
||||||
|
run = .{it.mesh, triles.world_buffer, it.first, it.count};
|
||||||
|
}
|
||||||
|
if run.count array_add(*draws, run);
|
||||||
|
}
|
||||||
|
|
||||||
|
for triles.batches {
|
||||||
|
if it.view_index != pass.view_index || (!main && it.key & MAIN_ONLY_KEY) continue;
|
||||||
|
array_add(*draws, .{xx (it.key & ~MAIN_ONLY_KEY), triles.stream.buffer, it.first, it.count});
|
||||||
|
}
|
||||||
|
return draws;
|
||||||
}
|
}
|
||||||
|
|
||||||
make_mesh :: (mesh: *Mesh, vertices: []Trile_Vertex, indices: []u16) {
|
make_mesh :: (mesh: *Mesh, vertices: []Trile_Vertex, indices: []u16) {
|
||||||
|
|||||||
@ -94,19 +94,19 @@ Ui_Vertex :: struct {
|
|||||||
trile_mesh_create :: (trile: *Trile) -> Trile_Mesh {
|
trile_mesh_create :: (trile: *Trile) -> Trile_Mesh {
|
||||||
vertices, indices := mesh_trile(trile);
|
vertices, indices := mesh_trile(trile);
|
||||||
mesh := Trile_Mesh.{renderer_trile_mesh_create(vertices, indices)};
|
mesh := Trile_Mesh.{renderer_trile_mesh_create(vertices, indices)};
|
||||||
set_mesh_cut(mesh, trile.cut);
|
set_mesh_info(mesh, trile.cut, trile_fills_cell(trile));
|
||||||
return mesh;
|
return mesh;
|
||||||
}
|
}
|
||||||
|
|
||||||
trile_mesh_update :: (mesh: Trile_Mesh, trile: *Trile) {
|
trile_mesh_update :: (mesh: Trile_Mesh, trile: *Trile) {
|
||||||
vertices, indices := mesh_trile(trile);
|
vertices, indices := mesh_trile(trile);
|
||||||
renderer_trile_mesh_update(mesh.id, vertices, indices);
|
renderer_trile_mesh_update(mesh.id, vertices, indices);
|
||||||
set_mesh_cut(mesh, trile.cut);
|
set_mesh_info(mesh, trile.cut, trile_fills_cell(trile));
|
||||||
}
|
}
|
||||||
|
|
||||||
trile_mesh_destroy :: (mesh: Trile_Mesh) {
|
trile_mesh_destroy :: (mesh: Trile_Mesh) {
|
||||||
renderer_trile_mesh_destroy(mesh.id);
|
renderer_trile_mesh_destroy(mesh.id);
|
||||||
set_mesh_cut(mesh, .NONE);
|
set_mesh_info(mesh, .NONE, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
swatch_set :: (id: u8, color: [3]u8, reflectivity: u8, gloss: u8, emittance: u8, glass: bool) {
|
swatch_set :: (id: u8, color: [3]u8, reflectivity: u8, gloss: u8, emittance: u8, glass: bool) {
|
||||||
@ -142,6 +142,7 @@ Probe :: struct {
|
|||||||
PROBES_PER_CHUNK :: CHUNK_SIZE * CHUNK_SIZE * CHUNK_SIZE;
|
PROBES_PER_CHUNK :: CHUNK_SIZE * CHUNK_SIZE * CHUNK_SIZE;
|
||||||
|
|
||||||
world_set_chunk :: (key: Chunk_Key, origin: Vector3, groups: []World_Group) {
|
world_set_chunk :: (key: Chunk_Key, origin: Vector3, groups: []World_Group) {
|
||||||
|
scene.world_version += 1;
|
||||||
chunk := find_or_add(*scene.chunks, key);
|
chunk := find_or_add(*scene.chunks, key);
|
||||||
chunk.origin = origin;
|
chunk.origin = origin;
|
||||||
for chunk.groups array_free(it.instances);
|
for chunk.groups array_free(it.instances);
|
||||||
@ -156,12 +157,14 @@ world_set_chunk :: (key: Chunk_Key, origin: Vector3, groups: []World_Group) {
|
|||||||
world_remove_chunk :: (key: Chunk_Key) {
|
world_remove_chunk :: (key: Chunk_Key) {
|
||||||
chunk := table_find_pointer(*scene.chunks, key);
|
chunk := table_find_pointer(*scene.chunks, key);
|
||||||
if !chunk return;
|
if !chunk return;
|
||||||
|
scene.world_version += 1;
|
||||||
for chunk.groups array_free(it.instances);
|
for chunk.groups array_free(it.instances);
|
||||||
array_free(chunk.groups);
|
array_free(chunk.groups);
|
||||||
table_remove(*scene.chunks, key);
|
table_remove(*scene.chunks, key);
|
||||||
}
|
}
|
||||||
|
|
||||||
world_clear :: () {
|
world_clear :: () {
|
||||||
|
scene.world_version += 1;
|
||||||
for chunk: scene.chunks {
|
for chunk: scene.chunks {
|
||||||
for chunk.groups array_free(it.instances);
|
for chunk.groups array_free(it.instances);
|
||||||
array_free(chunk.groups);
|
array_free(chunk.groups);
|
||||||
@ -221,6 +224,65 @@ trile_cut :: (view: View_Id, mesh: Trile_Mesh, position: Vector3) -> float {
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
view_cut_chunks :: (view: View_Id) -> []Chunk_Key {
|
||||||
|
keys : [..]Chunk_Key;
|
||||||
|
keys.allocator = temp;
|
||||||
|
cut := scene.view_cuts[view.index];
|
||||||
|
if !cut.enabled return keys;
|
||||||
|
|
||||||
|
for scene.chunks {
|
||||||
|
if chunk_column_distance(it.origin, cut.focus) <= CUT_RADIUS array_add(*keys, it_index);
|
||||||
|
}
|
||||||
|
unpack :: (bits: s64) -> s32 { return xx (((bits & 0xFFFFF) ^ 0x80000) - 0x80000); }
|
||||||
|
for scene.cut_fades {
|
||||||
|
if (it_index >> 60) != view.index continue;
|
||||||
|
array_add_if_unique(*keys, world_to_chunk_coord(unpack(it_index >> 40), unpack(it_index >> 20), unpack(it_index)), chunk_key_compare);
|
||||||
|
}
|
||||||
|
return keys;
|
||||||
|
}
|
||||||
|
|
||||||
|
trile_mesh_cuttable :: (mesh: Trile_Mesh) -> bool {
|
||||||
|
return mesh_cut(mesh) != .NONE;
|
||||||
|
}
|
||||||
|
|
||||||
|
OCCLUDER_WORDS :: CHUNK_SIZE * CHUNK_SIZE * CHUNK_SIZE / 64;
|
||||||
|
World_Occluders :: Table(Chunk_Key, *[OCCLUDER_WORDS]u64, chunk_key_hash, chunk_key_compare);
|
||||||
|
|
||||||
|
world_occluders :: () -> World_Occluders {
|
||||||
|
occluders : World_Occluders;
|
||||||
|
occluders.allocator = temp;
|
||||||
|
for chunk: scene.chunks {
|
||||||
|
bits := New([OCCLUDER_WORDS]u64,, temp);
|
||||||
|
for group: chunk.groups {
|
||||||
|
if !mesh_occludes(group.mesh) continue;
|
||||||
|
for group.instances {
|
||||||
|
cell := cast(s64) it.x + cast(s64) it.y * CHUNK_SIZE + cast(s64) it.z * CHUNK_SIZE * CHUNK_SIZE;
|
||||||
|
bits.*[cell >> 6] |= (cast(u64) 1) << cast(u64) (cell & 63);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
table_set(*occluders, it_index, bits);
|
||||||
|
}
|
||||||
|
return occluders;
|
||||||
|
}
|
||||||
|
|
||||||
|
trile_buried :: (occluders: *World_Occluders, key: Chunk_Key, x: s32, y: s32, z: s32) -> bool {
|
||||||
|
occluded :: (occluders: *World_Occluders, key: Chunk_Key, x: s32, y: s32, z: s32) -> bool {
|
||||||
|
c := s32.[x, y, z];
|
||||||
|
k := s32.[key.x, key.y, key.z];
|
||||||
|
for 0..2 {
|
||||||
|
if c[it] < 0 { c[it] += CHUNK_SIZE; k[it] -= 1; }
|
||||||
|
if c[it] >= CHUNK_SIZE { c[it] -= CHUNK_SIZE; k[it] += 1; }
|
||||||
|
}
|
||||||
|
bits := table_find_pointer(occluders, .{k[0], k[1], k[2]});
|
||||||
|
if !bits return false;
|
||||||
|
cell := cast(s64) c[0] + c[1] * CHUNK_SIZE + c[2] * CHUNK_SIZE * CHUNK_SIZE;
|
||||||
|
return ((bits.*.*[cell >> 6] >> cast(u64) (cell & 63)) & 1) != 0;
|
||||||
|
}
|
||||||
|
return occluded(occluders, key, x - 1, y, z) && occluded(occluders, key, x + 1, y, z)
|
||||||
|
&& occluded(occluders, key, x, y - 1, z) && occluded(occluders, key, x, y + 1, z)
|
||||||
|
&& occluded(occluders, key, x, y, z - 1) && occluded(occluders, key, x, y, z + 1);
|
||||||
|
}
|
||||||
|
|
||||||
draw_trile :: (view: View_Id, mesh: Trile_Mesh, position: Vector3, orientation: u8, tint := Trile_Tint.NONE) {
|
draw_trile :: (view: View_Id, mesh: Trile_Mesh, position: Vector3, orientation: u8, tint := Trile_Tint.NONE) {
|
||||||
array_add(*scene.triles, .{view, mesh, position, orientation, tint});
|
array_add(*scene.triles, .{view, mesh, position, orientation, tint});
|
||||||
}
|
}
|
||||||
@ -309,7 +371,9 @@ Trile_Vertex :: struct {
|
|||||||
|
|
||||||
Scene :: struct {
|
Scene :: struct {
|
||||||
chunks : Table(Chunk_Key, Scene_Chunk, chunk_key_hash, chunk_key_compare);
|
chunks : Table(Chunk_Key, Scene_Chunk, chunk_key_hash, chunk_key_compare);
|
||||||
mesh_cuts : [..]Trile_Cut; // by mesh id
|
world_version : u64;
|
||||||
|
mesh_cuts : [..]Trile_Cut; // by mesh id
|
||||||
|
mesh_occluders : [..]bool;
|
||||||
|
|
||||||
views : [..]View;
|
views : [..]View;
|
||||||
view_proj : [..]Matrix4;
|
view_proj : [..]Matrix4;
|
||||||
@ -362,9 +426,34 @@ scene_end_frame :: (width: s32, height: s32, time: float) {
|
|||||||
|
|
||||||
scene : Scene;
|
scene : Scene;
|
||||||
|
|
||||||
set_mesh_cut :: (mesh: Trile_Mesh, cut: Trile_Cut) {
|
set_mesh_info :: (mesh: Trile_Mesh, cut: Trile_Cut, fills_cell: bool) {
|
||||||
if scene.mesh_cuts.count <= mesh.id array_resize(*scene.mesh_cuts, mesh.id + 1);
|
if scene.mesh_cuts.count <= mesh.id {
|
||||||
|
array_resize(*scene.mesh_cuts, mesh.id + 1);
|
||||||
|
array_resize(*scene.mesh_occluders, mesh.id + 1);
|
||||||
|
}
|
||||||
scene.mesh_cuts[mesh.id] = cut;
|
scene.mesh_cuts[mesh.id] = cut;
|
||||||
|
scene.mesh_occluders[mesh.id] = fills_cell && cut == .NONE;
|
||||||
|
scene.world_version += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
mesh_occludes :: (mesh: Trile_Mesh) -> bool {
|
||||||
|
if mesh.id >= scene.mesh_occluders.count return false;
|
||||||
|
return scene.mesh_occluders[mesh.id];
|
||||||
|
}
|
||||||
|
|
||||||
|
trile_fills_cell :: (trile: *Trile) -> bool {
|
||||||
|
if !trile.is_opaque return false;
|
||||||
|
for x: 0..15 for y: 0..15 for z: 0..15 {
|
||||||
|
id := trile.trixels[x][y][z];
|
||||||
|
if !id || g_swatches[id].glass return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
chunk_column_distance :: (origin: Vector3, point: Vector3) -> float {
|
||||||
|
dx := max(max(origin.x - point.x, point.x - origin.x - CHUNK_SIZE), 0);
|
||||||
|
dz := max(max(origin.z - point.z, point.z - origin.z - CHUNK_SIZE), 0);
|
||||||
|
return sqrt(dx * dx + dz * dz);
|
||||||
}
|
}
|
||||||
|
|
||||||
mesh_cut :: (mesh: Trile_Mesh) -> Trile_Cut {
|
mesh_cut :: (mesh: Trile_Mesh) -> Trile_Cut {
|
||||||
@ -382,13 +471,16 @@ make_view_cut :: (view: View) -> View_Cut {
|
|||||||
to_camera := Vector2.{view.camera.position.x - view.focus.x, view.camera.position.z - view.focus.z};
|
to_camera := Vector2.{view.camera.position.x - view.focus.x, view.camera.position.z - view.focus.z};
|
||||||
if length(to_camera) > 0.0001 cut.toward_camera = normalize(to_camera);
|
if length(to_camera) > 0.0001 cut.toward_camera = normalize(to_camera);
|
||||||
|
|
||||||
for chunk: scene.chunks for group: chunk.groups {
|
for chunk: scene.chunks {
|
||||||
if mesh_cut(group.mesh) != .ROOF continue;
|
if chunk.origin.y + CHUNK_SIZE <= cut.floor || chunk_column_distance(chunk.origin, view.focus) > CUT_RADIUS continue;
|
||||||
for group.instances {
|
for group: chunk.groups {
|
||||||
p := chunk.origin + Vector3.{xx it.x, xx it.y, xx it.z};
|
if mesh_cut(group.mesh) != .ROOF continue;
|
||||||
if p.y <= cut.floor continue;
|
for group.instances {
|
||||||
if length(Vector2.{p.x + 0.5 - view.focus.x, p.z + 0.5 - view.focus.z}) > CUT_RADIUS continue;
|
p := chunk.origin + Vector3.{xx it.x, xx it.y, xx it.z};
|
||||||
cut.height = min(cut.height, p.y);
|
if p.y <= cut.floor continue;
|
||||||
|
if length(Vector2.{p.x + 0.5 - view.focus.x, p.z + 0.5 - view.focus.z}) > CUT_RADIUS continue;
|
||||||
|
cut.height = min(cut.height, p.y);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return cut;
|
return cut;
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user