trueno/src/rendering/scene.jai
2026-10-03 00:01:24 +03:00

241 lines
6.5 KiB
Plaintext

// The new scene API. The idea is that what to draw is defined trough this,
// and the renderer can be nicely separated and tested.
//
// I'm also planning to maybe add a WGPU backend at some point or something.
Trile_Mesh :: struct { id: u32; }
Texture :: struct { id: u32; }
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 {
camera : Camera;
environment : *Environment;
draw_world : bool = true;
}
Trile_Tint :: enum u8 {
NONE;
ADD_PREVIEW;
DELETE_PREVIEW;
}
// This direction means that the billboard should face the camera.
BILLBOARD_FACE_CAMERA :: Vector3.{-100, -100, -100};
Blend_Mode :: enum u8 {
ALPHA;
ADDITIVE;
}
Particle_Instance :: struct {
position : Vector3;
size : float;
uv_rect : Vector4; // x, y, w, h, 0..1
color : Vector4;
}
// Pixels from top left.
Ui_Vertex :: struct {
position : Vector2;
uv : Vector2;
color : Vector4;
}
trile_mesh_create :: (trile: *Trile) -> Trile_Mesh {
vertices, indices := mesh_trile(trile);
return .{renderer_trile_mesh_create(vertices, indices)};
}
trile_mesh_update :: (mesh: Trile_Mesh, trile: *Trile) {
vertices, indices := mesh_trile(trile);
renderer_trile_mesh_update(mesh.id, vertices, indices);
}
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.
texture_create :: (width: s32, height: s32, pixels: []u8) -> Texture {
return .{renderer_texture_create(width, height, pixels)};
}
texture_update :: (texture: Texture, pixels: []u8) {
renderer_texture_update(texture.id, pixels);
}
texture_destroy :: (texture: Texture) {
renderer_texture_destroy(texture.id);
}
World_Group :: struct {
mesh : Trile_Mesh;
instances : []Trile_Instance;
}
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 {
group := array_add(*chunk.groups);
group.mesh = it.mesh;
group.instances = array_copy(it.instances);
}
}
world_remove_chunk :: (key: Chunk_Key) {
chunk := table_find_pointer(*scene.chunks, key);
if !chunk return;
for chunk.groups array_free(it.instances);
array_free(chunk.groups);
table_remove(*scene.chunks, key);
}
world_clear :: () {
for chunk: scene.chunks {
for chunk.groups array_free(it.instances);
array_free(chunk.groups);
}
table_reset(*scene.chunks);
}
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)};
}
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});
}
// uv_rect is x, y, w, h in the sheet, normalized; a negative w mirrors it.
draw_billboard :: (view: View_Id, sheet: Texture, uv_rect: Vector4, size: Vector2, position: Vector3, face := BILLBOARD_FACE_CAMERA) {
array_add(*scene.billboards, .{view, sheet, uv_rect, size, position, face});
}
draw_particles :: (view: View_Id, sheet: Texture, blend: Blend_Mode, particles: []Particle_Instance) {
array_add(*scene.particles, .{view, sheet, blend, array_copy(particles,, temp)});
}
// Untextured triangles pass a zero texture.
ui_triangles :: (vertices: []Ui_Vertex, texture: Texture) {
array_add(*scene.ui, .{texture = texture, vertices = array_copy(vertices,, temp)});
}
ui_scissor :: (x: s32, y: s32, w: s32, h: s32) {
array_add(*scene.ui, .{scissor = .{x, y, w, h}, set_scissor = true});
}
ui_scissor_clear :: () {
array_add(*scene.ui, .{clear_scissor = true});
}
Scene_Chunk :: struct {
origin : Vector3;
groups : [..]World_Group;
}
Scene_Trile :: struct {
view : View_Id;
mesh : Trile_Mesh;
position : Vector3;
orientation : u8;
tint : Trile_Tint;
}
Scene_Billboard :: struct {
view : View_Id;
sheet : Texture;
uv_rect : Vector4;
size : Vector2;
position : Vector3;
face : Vector3;
}
Scene_Particles :: struct {
view : View_Id;
sheet : Texture;
blend : Blend_Mode;
particles : []Particle_Instance;
}
Scene_Ui :: struct {
texture : Texture;
vertices : []Ui_Vertex;
scissor : Ui_Rect;
set_scissor : bool;
clear_scissor : bool;
}
Trile_Vertex :: struct {
position : Vector3;
swatch : float;
ao : float; // 0 = fully occluded, 1 = open
}
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;
ui : [..]Scene_Ui;
}
scene_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, 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);
array_reset_keeping_memory(*scene.ui);
}
#scope_file
scene : Scene;