First
This commit is contained in:
@ -12,13 +12,14 @@ egui = "0.33.3"
|
||||
egui-wgpu = "0.33.3"
|
||||
egui-winit = "0.33.3"
|
||||
env_logger = "0.11.8"
|
||||
image = "0.25.9"
|
||||
indicatif = "0.18.3"
|
||||
itertools = "0.14.0"
|
||||
pollster = "0.4.0"
|
||||
rand = "0.9.2"
|
||||
wgpu = "27.0.1"
|
||||
tiff = "0.10.3"
|
||||
wgpu = {version = "27.0.1", features = ["spirv"]}
|
||||
winit = "0.30.12"
|
||||
|
||||
[profile.release]
|
||||
opt-level = 3
|
||||
|
||||
|
||||
236879
dragon.txt
Normal file
236879
dragon.txt
Normal file
File diff suppressed because it is too large
Load Diff
587
shaders/bak.wgsl
Normal file
587
shaders/bak.wgsl
Normal file
@ -0,0 +1,587 @@
|
||||
struct PushConstants
|
||||
{
|
||||
view_projection: mat4x4<f32>,
|
||||
transform: mat4x4<f32>,
|
||||
eye_position: vec3<f32>,
|
||||
root_color: vec4<f32>,
|
||||
root_subdivided: u32,
|
||||
}
|
||||
|
||||
var<push_constant> constants: PushConstants;
|
||||
|
||||
struct VertexOutput
|
||||
{
|
||||
@builtin(position) pos: vec4<f32>,
|
||||
@location(0) eye_pos: vec3<f32>,
|
||||
@location(1) world_pos: vec3<f32>,
|
||||
@location(2) cube_pos: vec3<f32>,
|
||||
@location(3) root_color: vec4<f32>,
|
||||
@location(4) root_subdivided: u32
|
||||
}
|
||||
|
||||
@vertex
|
||||
fn vertex_main(@builtin(vertex_index) index: u32, @builtin(instance_index) instance_index: u32) -> VertexOutput
|
||||
{
|
||||
let side_length = u32(100);
|
||||
let offset = vec3<f32>(vec3<u32>(
|
||||
instance_index % side_length,
|
||||
(instance_index / side_length) % side_length,
|
||||
instance_index / (side_length * side_length)
|
||||
));
|
||||
|
||||
let cube_vertices = array<vec3<f32>, 8>(
|
||||
vec3<f32>(0., 0., 0.),
|
||||
vec3<f32>(0., 0., 1.),
|
||||
vec3<f32>(1., 0., 1.),
|
||||
vec3<f32>(1., 0., 0.),
|
||||
|
||||
vec3<f32>(0., 1., 0.),
|
||||
vec3<f32>(0., 1., 1.),
|
||||
vec3<f32>(1., 1., 1.),
|
||||
vec3<f32>(1., 1., 0.),
|
||||
);
|
||||
|
||||
let cube_faces = array<u32, 24>(
|
||||
// Bottom face
|
||||
1, 0, 2, 3,
|
||||
|
||||
// Top face
|
||||
4, 5, 7, 6,
|
||||
|
||||
// Side faces
|
||||
0, 1, 4, 5,
|
||||
1, 2, 5, 6,
|
||||
2, 3, 6, 7,
|
||||
3, 0, 7, 4,
|
||||
);
|
||||
|
||||
let quad_index = index / (3 * 2);
|
||||
let triangle_index = index % (3 * 2);
|
||||
let triangle_map = array<u32, 6>(
|
||||
0, 1, 2, 1, 3, 2
|
||||
);
|
||||
|
||||
let vertex = cube_vertices[cube_faces[quad_index * 4 + triangle_map[triangle_index]]];
|
||||
|
||||
var output: VertexOutput;
|
||||
output.pos = constants.view_projection * constants.transform * vec4<f32>(vertex + offset, 1.);
|
||||
output.eye_pos = constants.eye_position;
|
||||
output.world_pos = (constants.transform * vec4<f32>(vertex + offset, 1.)).xyz;
|
||||
output.cube_pos = offset;
|
||||
|
||||
output.root_color = constants.root_color;
|
||||
output.root_subdivided = constants.root_subdivided;
|
||||
return output;
|
||||
}
|
||||
|
||||
fn is_voxel(p: vec3<i32>) -> bool
|
||||
{
|
||||
//return true;
|
||||
return length(vec3<f32>(p) - vec3<f32>(16.)) < 16.;
|
||||
}
|
||||
|
||||
const N: u32 = 4;
|
||||
const N3: u32 = N * N * N;
|
||||
const MAX_DEPTH: u32 = 4;
|
||||
|
||||
struct StructureTile
|
||||
{
|
||||
children: array<u32, 64>,
|
||||
}
|
||||
|
||||
struct ColorTile
|
||||
{
|
||||
colors: array<vec4<f32>, 64>
|
||||
}
|
||||
|
||||
// BG
|
||||
@group(0) @binding(0) var<storage> structure_tiles : array<StructureTile>;
|
||||
@group(0) @binding(1) var<storage> color_tiles : array<ColorTile>;
|
||||
|
||||
struct TraverseResult
|
||||
{
|
||||
albedo: vec4<f32>,
|
||||
normal: vec3<f32>
|
||||
}
|
||||
|
||||
fn traverse2(max_depth: u32, root_color: vec4<f32>, root_subdivided: bool, eye_pos: vec3<f32>, ray_dir: vec3<f32>) -> TraverseResult
|
||||
{
|
||||
var normal = max_mask3(eye_pos) * sign(ray_dir);
|
||||
if max_depth == 0 || !root_subdivided
|
||||
{
|
||||
var res: TraverseResult;
|
||||
res.albedo = root_color;
|
||||
res.normal = normal;
|
||||
return res;
|
||||
//return viridis_quintic(0.);
|
||||
}
|
||||
|
||||
// var node_structs = structure_tiles[0];
|
||||
// var node_colors = color_tiles[0];
|
||||
var node_tile_idx = u32(0);
|
||||
var stack = array<u32, 4>(0, 0, 0, 0);
|
||||
|
||||
var voxel_scale = N * N * N;
|
||||
var scale_exp = 3;
|
||||
let voxel_scale_lut = array<u32, 4>(
|
||||
1,
|
||||
N,
|
||||
N * N,
|
||||
N * N * N
|
||||
);
|
||||
|
||||
let dist = 1. / ray_dir;
|
||||
let origin = eye_pos * 256.;
|
||||
var pos = origin; // In voxel space
|
||||
let offset = - origin * dist;
|
||||
let wall_offset = select(vec3(0.), vec3(1.), ray_dir > vec3(0.));
|
||||
let step = select(vec3(-1), vec3(1), ray_dir > vec3(0.));
|
||||
var voxel_pos = clamp(vec3<u32>(floor(pos)), vec3(0), vec3(N * N * N * N - 1));
|
||||
let max = 60.;
|
||||
for(var iter = 0; iter < 256; iter++)
|
||||
{
|
||||
var child_pos = voxel_pos >> vec3<u32>(scale_exp * 2);
|
||||
var local_child_pos = child_pos & vec3<u32>(3);
|
||||
var child_idx = local_child_pos.x + local_child_pos.y * N + local_child_pos.z * N * N;
|
||||
|
||||
while (structure_tiles[node_tile_idx].children[child_idx] >> 31) != 0 && (u32(4 - scale_exp) < max_depth)
|
||||
{
|
||||
stack[scale_exp] = u32(node_tile_idx);
|
||||
scale_exp -= 1;
|
||||
voxel_scale = voxel_scale_lut[scale_exp];
|
||||
|
||||
node_tile_idx = structure_tiles[node_tile_idx].children[child_idx] & 0x3fffffff;
|
||||
|
||||
child_pos = voxel_pos >> vec3<u32>(scale_exp * 2);
|
||||
local_child_pos = child_pos & vec3<u32>(3);
|
||||
child_idx = local_child_pos.x + local_child_pos.y * N + local_child_pos.z * N * N;
|
||||
}
|
||||
|
||||
if color_tiles[node_tile_idx].colors[child_idx].w != 0
|
||||
{
|
||||
var res: TraverseResult;
|
||||
res.albedo = color_tiles[node_tile_idx].colors[child_idx];
|
||||
res.normal = vec3<f32>(normal);
|
||||
return res;
|
||||
// return vec4(0.5 * (dot(vec3<f32>(normal), vec3<f32>(1., 1., 1.)) + 1.2));
|
||||
// return color_tiles[node_tile_idx].colors[child_idx] * sample_mat(vec3<i32>(child_pos));
|
||||
// let color_lut = array<vec3<f32>, 4>(
|
||||
// vec3(1., 1., 0.2),
|
||||
// vec3(0.2, 1., 0.2),
|
||||
// vec3(0.2, 1., 1.),
|
||||
// vec3(1., 0.2, 1.),
|
||||
// );
|
||||
// return vec4(color_lut[scale_exp], 1.) * sample_mat(vec3<i32>(child_pos));
|
||||
// return color_tiles[node_tile_idx].colors[child_idx] * sample_mat(vec3<i32>(child_pos));
|
||||
// return inferno_quintic(f32(iter) / max);
|
||||
}
|
||||
|
||||
// Compute intersection
|
||||
let global_voxel = vec3<f32>(child_pos * voxel_scale);
|
||||
let cell_max = global_voxel + vec3<f32>(voxel_scale) * wall_offset;
|
||||
|
||||
let t1 = fma(dist, cell_max, offset);
|
||||
|
||||
let t_far = min(t1.x, min(t1.y, t1.z));
|
||||
|
||||
// Figure out which boundary we crossed to Figure out next neighbor
|
||||
normal = select(vec3(0.), vec3<f32>(step), vec3(t_far) == t1);
|
||||
let neighbor_min = vec3<i32>(global_voxel) + select(vec3(0), step * vec3<i32>(voxel_scale), vec3(t_far) == t1);
|
||||
let neighbor_max = neighbor_min + vec3<i32>(voxel_scale);
|
||||
pos = clamp(origin + t_far * ray_dir, vec3<f32>(neighbor_min), vec3<f32>(neighbor_max) - vec3<f32>(1.));
|
||||
//return vec4(vec3<f32>(neighbor_min) / 256., 1.);
|
||||
|
||||
voxel_pos = vec3<u32>(floor(pos));
|
||||
|
||||
let diff = vec3<u32>(pos + 256) ^ vec3<u32>(global_voxel + 256);
|
||||
let diff_exp = (firstLeadingBit((diff.x | diff.y | diff.z)) >> 1);
|
||||
|
||||
|
||||
if diff_exp > u32(scale_exp)
|
||||
{
|
||||
if diff_exp > 3
|
||||
{
|
||||
discard;
|
||||
//return viridis_quintic(f32(iter) / max);
|
||||
}
|
||||
|
||||
scale_exp = i32(diff_exp);
|
||||
voxel_scale = voxel_scale_lut[scale_exp];
|
||||
node_tile_idx = stack[scale_exp];
|
||||
}
|
||||
}
|
||||
|
||||
var res: TraverseResult;
|
||||
res.albedo = vec4(1., 0., 1., 1.);
|
||||
res.normal = vec3<f32>(1., 0., 0.);
|
||||
return res;
|
||||
}
|
||||
|
||||
//@fragment
|
||||
fn traverse(max_depth: u32, root_color: vec4<f32>, root_subdivided: bool, eye_pos: vec3<f32>, ray_dir: vec3<f32>) -> vec4<f32>
|
||||
{
|
||||
if max_depth == 0 || !root_subdivided
|
||||
{
|
||||
return root_color;
|
||||
}
|
||||
let chunk_size = N * N * N * N;
|
||||
let depth_child_size_lut = array<u32, 4>(N*N*N, N*N, N, 1);
|
||||
|
||||
var stack_nodes = array<i32, 4>(0, 0, 0, 0);
|
||||
var stack_child_pos = array<vec3<i32>, 4>(vec3(0), vec3(0), vec3(0), vec3(0));
|
||||
var stack_node_offset = array<vec3<i32>, 4>(vec3(0), vec3(0), vec3(0), vec3(0));
|
||||
var stack_ptr = 0;
|
||||
var current_child_size = chunk_size / N;
|
||||
var current_child_pos = vec3(0);
|
||||
var current_node_offset = vec3(0);
|
||||
var current_depth = 1;
|
||||
|
||||
var current_tile_index = 0;
|
||||
var current_children_data = structure_tiles[current_tile_index];
|
||||
var current_children_colors = color_tiles[current_tile_index];
|
||||
|
||||
// Intersection parameters
|
||||
let dist = 1. / ray_dir;
|
||||
var offset = - (eye_pos * f32(chunk_size)) * dist;
|
||||
|
||||
// Interesect with root
|
||||
let t0 = fma(dist, vec3<f32>(0.), offset);
|
||||
let t1 = fma(dist, vec3<f32>(chunk_size), offset);
|
||||
|
||||
let tmin = min(t0, t1);
|
||||
let tmax = max(t0, t1);
|
||||
|
||||
let t_near = max(max(tmin.x, max(tmin.y, tmin.z)), 0.);
|
||||
let t_far = min(tmax.x, min(tmax.y, tmax.z));
|
||||
|
||||
let step = select(vec3(-1), vec3(1), ray_dir > vec3(0.));
|
||||
|
||||
var start_pos = (eye_pos * f32(chunk_size)) + t_near * ray_dir;
|
||||
var hit_pos = start_pos;
|
||||
offset = - start_pos * dist;
|
||||
var t = 0.;
|
||||
|
||||
current_child_pos = vec3(
|
||||
clamp(i32(floor(start_pos.x)), 0, i32(chunk_size) - 1),
|
||||
clamp(i32(floor(start_pos.y)), 0, i32(chunk_size) - 1),
|
||||
clamp(i32(floor(start_pos.z)), 0, i32(chunk_size) - 1),
|
||||
) / i32(current_child_size);
|
||||
|
||||
//return vec4<f32>(vec3<f32>(current_child_pos) / 4., 1.);
|
||||
|
||||
for(var iter = 0; iter < 300; iter++)
|
||||
{
|
||||
// Retrieve current child information
|
||||
let child_index = current_child_pos.x + current_child_pos.y * i32(N) + current_child_pos.z * i32(N * N);
|
||||
|
||||
let child_u32 = current_children_data.children[child_index];
|
||||
let child_subdivided = (child_u32 >> 31) == 1;
|
||||
let child_color = current_children_colors.colors[child_index];
|
||||
|
||||
if child_color.w != 0. // Child is solid
|
||||
&& (max_depth == u32(current_depth) || !child_subdivided)
|
||||
{
|
||||
// Sample mat
|
||||
let voxel_pos = current_node_offset + current_child_pos * i32(current_child_size);
|
||||
//return vec4(child_color.xyz, 1.);
|
||||
return child_color+ vec4<f32>(vec3<f32>(f32(iter) / 200.), 1.);
|
||||
}
|
||||
|
||||
// Advance
|
||||
// Project current child
|
||||
let global_child_pos = current_child_pos * i32(current_child_size) + current_node_offset;
|
||||
|
||||
let t0 = fma(dist, vec3<f32>(global_child_pos), offset);
|
||||
let t1 = fma(dist, vec3<f32>(global_child_pos) + vec3<f32>(current_child_size), offset);
|
||||
|
||||
let tmin = min(t0, t1);
|
||||
let tmax = max(t0, t1);
|
||||
|
||||
let t_near = max(max(tmin.x, max(tmin.y, tmin.z)), 0.);
|
||||
let t_far = min(tmax.x, min(tmax.y, tmax.z));
|
||||
|
||||
if child_subdivided
|
||||
{
|
||||
// Push operation
|
||||
|
||||
stack_nodes[stack_ptr] = current_tile_index;
|
||||
stack_child_pos[stack_ptr] = current_child_pos;
|
||||
stack_node_offset[stack_ptr] = current_node_offset;
|
||||
stack_ptr ++;
|
||||
|
||||
// Retrieve child information
|
||||
current_tile_index = i32(child_u32 & 0x3fffffff);
|
||||
current_children_data = structure_tiles[current_tile_index];
|
||||
current_children_colors = color_tiles[current_tile_index];
|
||||
|
||||
// Determine child of the child
|
||||
let hit_pos = start_pos + ray_dir * t_near;
|
||||
|
||||
let next_node_offset = current_node_offset + current_child_pos * i32(current_child_size);
|
||||
let next_child_size = current_child_size / N;
|
||||
current_child_pos =
|
||||
(clamp(vec3<i32>(floor(hit_pos)), global_child_pos, global_child_pos + vec3(i32(current_child_size - 1))) - next_node_offset) / i32(next_child_size);
|
||||
|
||||
current_child_size = next_child_size;
|
||||
current_node_offset = next_node_offset;
|
||||
current_depth ++;
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
// ADVANCE
|
||||
let advance_mask = min_mask3i32(tmax);
|
||||
let next_child = current_child_pos + advance_mask * step;
|
||||
if any(next_child < vec3(0)) || any(next_child >= vec3(i32(N)))
|
||||
{
|
||||
|
||||
let aligned_child = select(vec3(0), vec3(i32(N)), vec3(step) > vec3(0));
|
||||
let masked_aligned = advance_mask * ((aligned_child * i32(current_child_size)) + current_node_offset);
|
||||
let exiting_axis = masked_aligned.x + masked_aligned.y + masked_aligned.z + 256;
|
||||
|
||||
// HARDCODED FOR N = 4
|
||||
let ctz = countTrailingZeros(exiting_axis) / 2;
|
||||
let exiting_depth = 4 - ctz;
|
||||
|
||||
if exiting_depth == 0 // Getting out of root
|
||||
{
|
||||
return vec4(f32(iter) / 200.);
|
||||
discard;
|
||||
}
|
||||
|
||||
// Restore destination depth
|
||||
current_depth = exiting_depth;
|
||||
stack_ptr = current_depth - 1;
|
||||
|
||||
current_tile_index = stack_nodes[stack_ptr];
|
||||
current_children_data = structure_tiles[current_tile_index];
|
||||
current_children_colors = color_tiles[current_tile_index];
|
||||
|
||||
current_node_offset = stack_node_offset[stack_ptr];
|
||||
current_child_pos = stack_child_pos[stack_ptr] + step * advance_mask;
|
||||
|
||||
current_child_size = depth_child_size_lut[current_depth - 1];
|
||||
}else{
|
||||
current_child_pos = next_child;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return vec4<f32>(100., 0., 100., 100.);
|
||||
}
|
||||
|
||||
fn sample_mat(pos: vec3<i32>) -> f32
|
||||
{
|
||||
var voxel = pos;
|
||||
var div = 1;
|
||||
var overlay = 1.;
|
||||
for(var i = 1; i <= 4; i++)
|
||||
{
|
||||
let x = (voxel.x / div + voxel.y / div + voxel.z / div) % 2 == 0;
|
||||
overlay -= select(0., 1. / (f32(i) * 2.5), x);
|
||||
div *= 4;
|
||||
}
|
||||
return overlay;
|
||||
}
|
||||
|
||||
@fragment
|
||||
fn fragment_tree_main(in: VertexOutput) -> @location(0) vec4<f32>
|
||||
{
|
||||
var hit_pos = vec3(0.);
|
||||
let dir = normalize(in.world_pos.xyz - in.eye_pos);
|
||||
let chunk_size = 4 * 4 * 4 * 4;
|
||||
|
||||
let aabb = intersectAABB(in.eye_pos, dir, in.cube_pos, in.cube_pos + vec3(1.));
|
||||
let norm = -intersect_normal_AABB(in.eye_pos, dir, in.cube_pos, in.cube_pos + vec3(1.));
|
||||
hit_pos = in.eye_pos + max(aabb.x, 0.) * dir - in.cube_pos;
|
||||
|
||||
let cube_color = vec3(1.);
|
||||
|
||||
var pos = hit_pos * f32(chunk_size);
|
||||
let step = vec3<i32>(
|
||||
select(-1, 1, dir.x > 0.),
|
||||
select(-1, 1, dir.y > 0.),
|
||||
select(-1, 1, dir.z > 0.)
|
||||
);
|
||||
|
||||
var voxel = vec3<i32>(
|
||||
clamp(i32(floor(pos.x)), 0, chunk_size - 1),
|
||||
clamp(i32(floor(pos.y)), 0, chunk_size - 1),
|
||||
clamp(i32(floor(pos.z)), 0, chunk_size - 1),
|
||||
);
|
||||
|
||||
var div = 1;
|
||||
var overlay = 1.;
|
||||
for(var i = 1; i <= 4; i++)
|
||||
{
|
||||
let x = (voxel.x / div + voxel.y / div + voxel.z / div) % 2 == 0;
|
||||
overlay -= select(0., 1. / (f32(i) * 2.5), x);
|
||||
div *= 4;
|
||||
}
|
||||
overlay = 1.;
|
||||
let max_depth = 4;
|
||||
let glob_dist = aabb.x / 1.5; // Pixel footprint
|
||||
let depth = clamp(4 - u32(floor(log(glob_dist) / log(4.))), 0, 4);
|
||||
let res = traverse2(depth, in.root_color, in.root_subdivided != 0, hit_pos, dir);
|
||||
return res.albedo * (dot(-res.normal, normalize(vec3(1., 2., 3.))) + 1.2) / 2.;
|
||||
}
|
||||
|
||||
@fragment
|
||||
fn fragment_main(in: VertexOutput) -> @location(0) vec4<f32>
|
||||
{
|
||||
let chunk_size = 32;
|
||||
|
||||
let dir = normalize(in.world_pos.xyz - in.eye_pos);
|
||||
|
||||
var hit_pos = vec3(0.);
|
||||
if all(in.eye_pos > in.cube_pos) && all(in.eye_pos < (in.cube_pos + vec3(1.)))
|
||||
{
|
||||
hit_pos = in.eye_pos - in.cube_pos;
|
||||
}
|
||||
else
|
||||
{
|
||||
let aabb = intersectAABB(in.eye_pos, dir, in.cube_pos, in.cube_pos + vec3(1.));
|
||||
hit_pos = in.eye_pos + aabb.x * dir - in.cube_pos;
|
||||
}
|
||||
|
||||
let cube_color = vec3(1.);
|
||||
|
||||
var pos = hit_pos * f32(chunk_size);
|
||||
let step = vec3<i32>(
|
||||
select(-1, 1, dir.x > 0.),
|
||||
select(-1, 1, dir.y > 0.),
|
||||
select(-1, 1, dir.z > 0.)
|
||||
);
|
||||
|
||||
var voxel = vec3<i32>(
|
||||
clamp(i32(floor(pos.x)), 0, chunk_size - 1),
|
||||
clamp(i32(floor(pos.y)), 0, chunk_size - 1),
|
||||
clamp(i32(floor(pos.z)), 0, chunk_size - 1),
|
||||
);
|
||||
|
||||
let tDelta = vec3<f32>(1.) / abs(dir);
|
||||
var dist = vec3(
|
||||
select(pos.x - f32(voxel.x), f32(voxel.x) + 1. - pos.x, step.x > 0),
|
||||
select(pos.y - f32(voxel.y), f32(voxel.y) + 1. - pos.y, step.y > 0),
|
||||
select(pos.z - f32(voxel.z), f32(voxel.z) + 1. - pos.z, step.z > 0),
|
||||
);
|
||||
var tMax = dist * tDelta;
|
||||
//var tMax = (ceil(vec3<f32>(step) * pos) - vec3<f32>(step) * pos) * tDelta;
|
||||
var t = 0.;
|
||||
|
||||
|
||||
// Loop
|
||||
loop
|
||||
{
|
||||
if any(voxel >= vec3<i32>(chunk_size)) || any(voxel < vec3<i32>(0))
|
||||
{
|
||||
discard;
|
||||
//break;
|
||||
}
|
||||
// Sample
|
||||
if is_voxel(voxel)
|
||||
{
|
||||
// Compute normal
|
||||
let voxel_center = vec3<f32>(voxel) + vec3(0.5);
|
||||
let pos = pos + t * dir;
|
||||
|
||||
let norm_dir = normalize(pos - voxel_center);
|
||||
let norm_dir_max = max_mask3 (abs(norm_dir));
|
||||
let norm = sign(norm_dir * norm_dir_max);
|
||||
|
||||
let color = (1.2 + dot(norm, vec3<f32>(0., 1., 0.))) * 0.5;
|
||||
return vec4(vec3<f32>(color) * cube_color, 1.);
|
||||
return vec4(vec3<f32>(color), 1.);
|
||||
}
|
||||
|
||||
// Select which to step
|
||||
let mask = min_mask3(tMax);
|
||||
let delta = tDelta * mask;
|
||||
|
||||
let next_t_vec = tMax * mask;
|
||||
t = next_t_vec.x + next_t_vec.y + next_t_vec.z;
|
||||
|
||||
tMax += delta;
|
||||
|
||||
voxel += step * vec3<i32>(mask);
|
||||
|
||||
}
|
||||
|
||||
// Ray direction
|
||||
return vec4<f32>(1., 0., 1., 1.);
|
||||
}
|
||||
|
||||
fn min_mask3(v: vec3<f32>) -> vec3<f32>
|
||||
{
|
||||
let min = min(v.x, min(v.y, v.z));
|
||||
|
||||
return vec3<f32>
|
||||
(
|
||||
select(0., 1., v.x == min),
|
||||
select(0., 1., v.y == min),
|
||||
select(0., 1., v.z == min),
|
||||
);
|
||||
}
|
||||
|
||||
fn min_mask3i32(v: vec3<f32>) -> vec3<i32>
|
||||
{
|
||||
let min = min(v.x, min(v.y, v.z));
|
||||
|
||||
return vec3<i32>
|
||||
(
|
||||
select(0, 1, v.x == min),
|
||||
select(0, 1, v.y == min),
|
||||
select(0, 1, v.z == min),
|
||||
);
|
||||
}
|
||||
|
||||
fn max_mask3(v: vec3<f32>) -> vec3<f32>
|
||||
{
|
||||
let max = max(v.x, max(v.y, v.z));
|
||||
|
||||
return vec3<f32>
|
||||
(
|
||||
select(0., 1., v.x == max),
|
||||
select(0., 1., v.y == max),
|
||||
select(0., 1., v.z == max),
|
||||
);
|
||||
}
|
||||
|
||||
fn intersectAABB(rayOrigin: vec3<f32>, rayDir: vec3<f32>, boxMin: vec3<f32>, boxMax: vec3<f32>) -> vec2<f32> {
|
||||
let tMin = (boxMin - rayOrigin) / rayDir;
|
||||
let tMax = (boxMax - rayOrigin) / rayDir;
|
||||
let t1 = min(tMin, tMax);
|
||||
let t2 = max(tMin, tMax);
|
||||
let tNear = max(max(t1.x, t1.y), t1.z);
|
||||
let tFar = min(min(t2.x, t2.y), t2.z);
|
||||
return vec2(tNear, tFar);
|
||||
};
|
||||
|
||||
fn intersect_normal_AABB(rayOrigin: vec3<f32>, rayDir: vec3<f32>, boxMin: vec3<f32>, boxMax: vec3<f32>) -> vec3<f32> {
|
||||
let tMin = (boxMin - rayOrigin) / rayDir;
|
||||
let tMax = (boxMax - rayOrigin) / rayDir;
|
||||
let t1 = min(tMin, tMax);
|
||||
let tNear = max(max(t1.x, t1.y), t1.z);
|
||||
return select(vec3(0), sign(rayDir), vec3(tNear) == t1);
|
||||
};
|
||||
|
||||
fn inferno_quintic( xx: f32 ) -> vec4<f32>
|
||||
{
|
||||
let x = saturate(xx);
|
||||
let x1 = vec4( 1.0, x, x * x, x * x * x ); // 1 x x2 x3
|
||||
let x2 = x1 * x1.w * x; // x4 x5 x6 x7
|
||||
return vec4(saturate( vec3(
|
||||
dot( x1.xyzw, vec4( -0.027780558, 1.228188385, 0.278906882, 3.892783760 ) ) + dot( x2.xy, vec2( -8.490712758, 4.069046086 ) ),
|
||||
dot( x1.xyzw, vec4( 0.014065206, 0.015360518, 1.605395918, -4.821108251 ) ) + dot( x2.xy, vec2( 8.389314011, -4.193858954 ) ),
|
||||
dot( x1.xyzw, vec4( -0.019628385, 3.122510347, -5.893222355, 2.798380308 ) ) + dot( x2.xy, vec2( -3.608884658, 4.324996022 ) ) ) ), 1.);
|
||||
}
|
||||
fn viridis_quintic( xx: f32 ) -> vec4<f32>
|
||||
{
|
||||
let x = saturate( xx );
|
||||
let x1 = vec4( 1.0, x, x * x, x * x * x ); // 1 x x2 x3
|
||||
let x2 = x1 * x1.w * x; // x4 x5 x6 x7
|
||||
return vec4(saturate( vec3(
|
||||
dot( x1.xyzw, vec4( 0.280268003, -0.143510503, 2.225793877, -14.815088879 ) ) + dot( x2.xy, vec2( 25.212752309, -11.772589584 ) ),
|
||||
dot( x1.xyzw, vec4( -0.002117546, 1.617109353, -1.909305070, 2.701152864 ) ) + dot( x2.xy, vec2( -1.685288385, 0.178738871 ) ),
|
||||
dot( x1.xyzw, vec4( 0.300805501, 2.614650302, -12.019139090, 28.933559110 ) ) + dot( x2.xy, vec2( -33.491294770, 13.762053843 ) ) ) ), 1.);
|
||||
}
|
||||
71
shaders/chunk.wgsl
Normal file
71
shaders/chunk.wgsl
Normal file
@ -0,0 +1,71 @@
|
||||
struct PushConstants
|
||||
{
|
||||
view_projection: mat4x4<f32>,
|
||||
transform: mat4x4<f32>,
|
||||
eye_position: vec3<f32>,
|
||||
}
|
||||
|
||||
var<push_constant> constants: PushConstants;
|
||||
|
||||
struct VertexOutput
|
||||
{
|
||||
@builtin(position) pos: vec4<f32>,
|
||||
@location(0) eye_pos: vec3<f32>,
|
||||
@location(1) world_pos: vec3<f32>,
|
||||
@location(2) cube_pos: vec3<f32>,
|
||||
@location(3) root_color: vec4<f32>,
|
||||
@location(4) root_subdivided: u32
|
||||
}
|
||||
|
||||
@vertex
|
||||
fn vertex_main(@builtin(vertex_index) index: u32, @builtin(instance_index) instance_index: u32, @location(0) chunk_location: vec3<i32>) -> VertexOutput
|
||||
{
|
||||
|
||||
let cube_vertices = array<vec3<f32>, 8>(
|
||||
vec3<f32>(0., 0., 0.),
|
||||
vec3<f32>(0., 0., 1.),
|
||||
vec3<f32>(1., 0., 1.),
|
||||
vec3<f32>(1., 0., 0.),
|
||||
|
||||
vec3<f32>(0., 1., 0.),
|
||||
vec3<f32>(0., 1., 1.),
|
||||
vec3<f32>(1., 1., 1.),
|
||||
vec3<f32>(1., 1., 0.),
|
||||
);
|
||||
|
||||
let cube_faces = array<u32, 24>(
|
||||
// Bottom face
|
||||
1, 0, 2, 3,
|
||||
|
||||
// Top face
|
||||
4, 5, 7, 6,
|
||||
|
||||
// Side faces
|
||||
0, 1, 4, 5,
|
||||
1, 2, 5, 6,
|
||||
2, 3, 6, 7,
|
||||
3, 0, 7, 4,
|
||||
);
|
||||
|
||||
let quad_index = index / (3 * 2);
|
||||
let triangle_index = index % (3 * 2);
|
||||
let triangle_map = array<u32, 6>(
|
||||
0, 1, 2, 1, 3, 2
|
||||
);
|
||||
|
||||
let vertex = cube_vertices[cube_faces[quad_index * 4 + triangle_map[triangle_index]]];
|
||||
|
||||
var output: VertexOutput;
|
||||
output.pos = constants.view_projection * constants.transform * vec4<f32>(vertex + vec3<f32>(chunk_location), 1.);
|
||||
output.eye_pos = constants.eye_position;
|
||||
output.world_pos = (constants.transform * vec4<f32>(vertex + vec3<f32>(chunk_location), 1.)).xyz;
|
||||
//output.cube_pos = offset;
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
@fragment
|
||||
fn fragment_main() -> @location(0) vec4<f32>
|
||||
{
|
||||
return vec4(1.);
|
||||
}
|
||||
@ -74,457 +74,8 @@ fn vertex_main(@builtin(vertex_index) index: u32, @builtin(instance_index) insta
|
||||
return output;
|
||||
}
|
||||
|
||||
fn is_voxel(p: vec3<i32>) -> bool
|
||||
{
|
||||
//return true;
|
||||
return length(vec3<f32>(p) - vec3<f32>(16.)) < 16.;
|
||||
}
|
||||
|
||||
const N: u32 = 4;
|
||||
const N3: u32 = N * N * N;
|
||||
const MAX_DEPTH: u32 = 4;
|
||||
|
||||
struct StructureTile
|
||||
{
|
||||
children: array<u32, 64>,
|
||||
}
|
||||
|
||||
struct ColorTile
|
||||
{
|
||||
colors: array<vec4<f32>, 64>
|
||||
}
|
||||
|
||||
// BG
|
||||
@group(0) @binding(0) var<storage> structure_tiles : array<StructureTile>;
|
||||
@group(0) @binding(1) var<storage> color_tiles : array<ColorTile>;
|
||||
|
||||
fn traverse2(max_depth: u32, root_color: vec4<f32>, root_subdivided: bool, eye_pos: vec3<f32>, ray_dir: vec3<f32>) -> vec4<f32>
|
||||
{
|
||||
if max_depth == 0 || !root_subdivided
|
||||
{
|
||||
return root_color;
|
||||
return inferno_quintic(0.);
|
||||
}
|
||||
|
||||
// var node_structs = structure_tiles[0];
|
||||
// var node_colors = color_tiles[0];
|
||||
var node_tile_idx = u32(0);
|
||||
var stack = array<u32, 4>(0, 0, 0, 0);
|
||||
|
||||
var voxel_scale = N * N * N;
|
||||
var scale_exp = 3;
|
||||
let voxel_scale_lut = array<u32, 4>(
|
||||
1,
|
||||
N,
|
||||
N * N,
|
||||
N * N * N
|
||||
);
|
||||
|
||||
let dist = 1. / ray_dir;
|
||||
let origin = eye_pos * 256.;
|
||||
var pos = origin; // In voxel space
|
||||
let offset = - origin * dist;
|
||||
let wall_offset = select(vec3(0.), vec3(1.), ray_dir > vec3(0.));
|
||||
var voxel_pos = clamp(vec3<u32>(floor(pos)), vec3(0), vec3(N * N * N * N - 1));
|
||||
let max = 20.;
|
||||
for(var iter = 0; iter < 256; iter++)
|
||||
{
|
||||
var child_pos = voxel_pos >> vec3<u32>(scale_exp * 2);
|
||||
var local_child_pos = child_pos & vec3<u32>(3);
|
||||
var child_idx = local_child_pos.x + local_child_pos.y * N + local_child_pos.z * N * N;
|
||||
|
||||
while (structure_tiles[node_tile_idx].children[child_idx] >> 31) != 0 && (u32(4 - scale_exp) < max_depth)
|
||||
{
|
||||
stack[scale_exp] = u32(node_tile_idx);
|
||||
scale_exp -= 1;
|
||||
voxel_scale = voxel_scale_lut[scale_exp];
|
||||
|
||||
node_tile_idx = structure_tiles[node_tile_idx].children[child_idx] & 0x3fffffff;
|
||||
|
||||
child_pos = voxel_pos >> vec3<u32>(scale_exp * 2);
|
||||
local_child_pos = child_pos & vec3<u32>(3);
|
||||
child_idx = local_child_pos.x + local_child_pos.y * N + local_child_pos.z * N * N;
|
||||
}
|
||||
|
||||
if color_tiles[node_tile_idx].colors[child_idx].w != 0
|
||||
{
|
||||
return color_tiles[node_tile_idx].colors[child_idx] * sample_mat(vec3<i32>(voxel_pos));
|
||||
return inferno_quintic(f32(iter) / max);
|
||||
}
|
||||
|
||||
// Compute intersection
|
||||
let global_voxel = vec3<f32>(child_pos * voxel_scale);
|
||||
let cell_max = global_voxel + vec3<f32>(voxel_scale) * wall_offset;
|
||||
|
||||
let t1 = fma(dist, cell_max, offset);
|
||||
|
||||
let t_far = min(t1.x, min(t1.y, t1.z)) + 0.001;
|
||||
pos = origin + t_far * ray_dir;
|
||||
voxel_pos = vec3<u32>(floor(pos));
|
||||
|
||||
let diff = vec3<u32>(pos + 256) ^ vec3<u32>(global_voxel + 256);
|
||||
let diff_exp = (firstLeadingBit((diff.x | diff.y | diff.z)) >> 1);
|
||||
|
||||
|
||||
if diff_exp > u32(scale_exp)
|
||||
{
|
||||
if diff_exp > 3
|
||||
{
|
||||
discard;
|
||||
return inferno_quintic(f32(iter) / max);
|
||||
}
|
||||
|
||||
scale_exp = i32(diff_exp);
|
||||
voxel_scale = voxel_scale_lut[scale_exp];
|
||||
node_tile_idx = stack[scale_exp];
|
||||
}
|
||||
}
|
||||
|
||||
return vec4(1., 0., 1., 1.);
|
||||
}
|
||||
|
||||
//@fragment
|
||||
fn traverse(max_depth: u32, root_color: vec4<f32>, root_subdivided: bool, eye_pos: vec3<f32>, ray_dir: vec3<f32>) -> vec4<f32>
|
||||
{
|
||||
if max_depth == 0 || !root_subdivided
|
||||
{
|
||||
return root_color;
|
||||
}
|
||||
let chunk_size = N * N * N * N;
|
||||
let depth_child_size_lut = array<u32, 4>(N*N*N, N*N, N, 1);
|
||||
|
||||
var stack_nodes = array<i32, 4>(0, 0, 0, 0);
|
||||
var stack_child_pos = array<vec3<i32>, 4>(vec3(0), vec3(0), vec3(0), vec3(0));
|
||||
var stack_node_offset = array<vec3<i32>, 4>(vec3(0), vec3(0), vec3(0), vec3(0));
|
||||
var stack_ptr = 0;
|
||||
var current_child_size = chunk_size / N;
|
||||
var current_child_pos = vec3(0);
|
||||
var current_node_offset = vec3(0);
|
||||
var current_depth = 1;
|
||||
|
||||
var current_tile_index = 0;
|
||||
var current_children_data = structure_tiles[current_tile_index];
|
||||
var current_children_colors = color_tiles[current_tile_index];
|
||||
|
||||
// Intersection parameters
|
||||
let dist = 1. / ray_dir;
|
||||
var offset = - (eye_pos * f32(chunk_size)) * dist;
|
||||
|
||||
// Interesect with root
|
||||
let t0 = fma(dist, vec3<f32>(0.), offset);
|
||||
let t1 = fma(dist, vec3<f32>(chunk_size), offset);
|
||||
|
||||
let tmin = min(t0, t1);
|
||||
let tmax = max(t0, t1);
|
||||
|
||||
let t_near = max(max(tmin.x, max(tmin.y, tmin.z)), 0.);
|
||||
let t_far = min(tmax.x, min(tmax.y, tmax.z));
|
||||
|
||||
let step = select(vec3(-1), vec3(1), ray_dir > vec3(0.));
|
||||
|
||||
var start_pos = (eye_pos * f32(chunk_size)) + t_near * ray_dir;
|
||||
var hit_pos = start_pos;
|
||||
offset = - start_pos * dist;
|
||||
var t = 0.;
|
||||
|
||||
current_child_pos = vec3(
|
||||
clamp(i32(floor(start_pos.x)), 0, i32(chunk_size) - 1),
|
||||
clamp(i32(floor(start_pos.y)), 0, i32(chunk_size) - 1),
|
||||
clamp(i32(floor(start_pos.z)), 0, i32(chunk_size) - 1),
|
||||
) / i32(current_child_size);
|
||||
|
||||
//return vec4<f32>(vec3<f32>(current_child_pos) / 4., 1.);
|
||||
|
||||
for(var iter = 0; iter < 300; iter++)
|
||||
{
|
||||
// Retrieve current child information
|
||||
let child_index = current_child_pos.x + current_child_pos.y * i32(N) + current_child_pos.z * i32(N * N);
|
||||
|
||||
let child_u32 = current_children_data.children[child_index];
|
||||
let child_subdivided = (child_u32 >> 31) == 1;
|
||||
let child_color = current_children_colors.colors[child_index];
|
||||
|
||||
if child_color.w != 0. // Child is solid
|
||||
&& (max_depth == u32(current_depth) || !child_subdivided)
|
||||
{
|
||||
// Sample mat
|
||||
let voxel_pos = current_node_offset + current_child_pos * i32(current_child_size);
|
||||
//return vec4(child_color.xyz, 1.);
|
||||
return child_color+ vec4<f32>(vec3<f32>(f32(iter) / 200.), 1.);
|
||||
}
|
||||
|
||||
// Advance
|
||||
// Project current child
|
||||
let global_child_pos = current_child_pos * i32(current_child_size) + current_node_offset;
|
||||
|
||||
let t0 = fma(dist, vec3<f32>(global_child_pos), offset);
|
||||
let t1 = fma(dist, vec3<f32>(global_child_pos) + vec3<f32>(current_child_size), offset);
|
||||
|
||||
let tmin = min(t0, t1);
|
||||
let tmax = max(t0, t1);
|
||||
|
||||
let t_near = max(max(tmin.x, max(tmin.y, tmin.z)), 0.);
|
||||
let t_far = min(tmax.x, min(tmax.y, tmax.z));
|
||||
|
||||
if child_subdivided
|
||||
{
|
||||
// Push operation
|
||||
|
||||
stack_nodes[stack_ptr] = current_tile_index;
|
||||
stack_child_pos[stack_ptr] = current_child_pos;
|
||||
stack_node_offset[stack_ptr] = current_node_offset;
|
||||
stack_ptr ++;
|
||||
|
||||
// Retrieve child information
|
||||
current_tile_index = i32(child_u32 & 0x3fffffff);
|
||||
current_children_data = structure_tiles[current_tile_index];
|
||||
current_children_colors = color_tiles[current_tile_index];
|
||||
|
||||
// Determine child of the child
|
||||
let hit_pos = start_pos + ray_dir * t_near;
|
||||
|
||||
let next_node_offset = current_node_offset + current_child_pos * i32(current_child_size);
|
||||
let next_child_size = current_child_size / N;
|
||||
current_child_pos =
|
||||
(clamp(vec3<i32>(floor(hit_pos)), global_child_pos, global_child_pos + vec3(i32(current_child_size - 1))) - next_node_offset) / i32(next_child_size);
|
||||
|
||||
current_child_size = next_child_size;
|
||||
current_node_offset = next_node_offset;
|
||||
current_depth ++;
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
// ADVANCE
|
||||
let advance_mask = min_mask3i32(tmax);
|
||||
let next_child = current_child_pos + advance_mask * step;
|
||||
if any(next_child < vec3(0)) || any(next_child >= vec3(i32(N)))
|
||||
{
|
||||
|
||||
let aligned_child = select(vec3(0), vec3(i32(N)), vec3(step) > vec3(0));
|
||||
let masked_aligned = advance_mask * ((aligned_child * i32(current_child_size)) + current_node_offset);
|
||||
let exiting_axis = masked_aligned.x + masked_aligned.y + masked_aligned.z + 256;
|
||||
|
||||
// HARDCODED FOR N = 4
|
||||
let ctz = countTrailingZeros(exiting_axis) / 2;
|
||||
let exiting_depth = 4 - ctz;
|
||||
|
||||
if exiting_depth == 0 // Getting out of root
|
||||
{
|
||||
return vec4(f32(iter) / 200.);
|
||||
discard;
|
||||
}
|
||||
|
||||
// Restore destination depth
|
||||
current_depth = exiting_depth;
|
||||
stack_ptr = current_depth - 1;
|
||||
|
||||
current_tile_index = stack_nodes[stack_ptr];
|
||||
current_children_data = structure_tiles[current_tile_index];
|
||||
current_children_colors = color_tiles[current_tile_index];
|
||||
|
||||
current_node_offset = stack_node_offset[stack_ptr];
|
||||
current_child_pos = stack_child_pos[stack_ptr] + step * advance_mask;
|
||||
|
||||
current_child_size = depth_child_size_lut[current_depth - 1];
|
||||
}else{
|
||||
current_child_pos = next_child;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return vec4<f32>(100., 0., 100., 100.);
|
||||
}
|
||||
|
||||
fn sample_mat(pos: vec3<i32>) -> f32
|
||||
{
|
||||
var voxel = pos;
|
||||
var div = 1;
|
||||
var overlay = 1.;
|
||||
for(var i = 1; i <= 4; i++)
|
||||
{
|
||||
let x = (voxel.x / div + voxel.y / div + voxel.z / div) % 2 == 0;
|
||||
overlay -= select(0., 1. / (f32(i) * 2.5), x);
|
||||
div *= 4;
|
||||
}
|
||||
return overlay;
|
||||
}
|
||||
|
||||
@fragment
|
||||
fn fragment_tree_main(in: VertexOutput) -> @location(0) vec4<f32>
|
||||
fn fragment_main() -> @location(0) vec4<f32>
|
||||
{
|
||||
var hit_pos = vec3(0.);
|
||||
let dir = normalize(in.world_pos.xyz - in.eye_pos);
|
||||
let chunk_size = 4 * 4 * 4 * 4;
|
||||
|
||||
let aabb = intersectAABB(in.eye_pos, dir, in.cube_pos, in.cube_pos + vec3(1.));
|
||||
hit_pos = in.eye_pos + max(aabb.x, 0.) * dir - in.cube_pos;
|
||||
|
||||
let cube_color = vec3(1.);
|
||||
|
||||
var pos = hit_pos * f32(chunk_size);
|
||||
let step = vec3<i32>(
|
||||
select(-1, 1, dir.x > 0.),
|
||||
select(-1, 1, dir.y > 0.),
|
||||
select(-1, 1, dir.z > 0.)
|
||||
);
|
||||
|
||||
var voxel = vec3<i32>(
|
||||
clamp(i32(floor(pos.x)), 0, chunk_size - 1),
|
||||
clamp(i32(floor(pos.y)), 0, chunk_size - 1),
|
||||
clamp(i32(floor(pos.z)), 0, chunk_size - 1),
|
||||
);
|
||||
|
||||
var div = 1;
|
||||
var overlay = 1.;
|
||||
for(var i = 1; i <= 4; i++)
|
||||
{
|
||||
let x = (voxel.x / div + voxel.y / div + voxel.z / div) % 2 == 0;
|
||||
overlay -= select(0., 1. / (f32(i) * 2.5), x);
|
||||
div *= 4;
|
||||
}
|
||||
overlay = 1.;
|
||||
return overlay * traverse2(4, in.root_color, in.root_subdivided != 0, hit_pos, dir);
|
||||
}
|
||||
|
||||
@fragment
|
||||
fn fragment_main(in: VertexOutput) -> @location(0) vec4<f32>
|
||||
{
|
||||
let chunk_size = 32;
|
||||
|
||||
let dir = normalize(in.world_pos.xyz - in.eye_pos);
|
||||
|
||||
var hit_pos = vec3(0.);
|
||||
if all(in.eye_pos > in.cube_pos) && all(in.eye_pos < (in.cube_pos + vec3(1.)))
|
||||
{
|
||||
hit_pos = in.eye_pos - in.cube_pos;
|
||||
}
|
||||
else
|
||||
{
|
||||
let aabb = intersectAABB(in.eye_pos, dir, in.cube_pos, in.cube_pos + vec3(1.));
|
||||
hit_pos = in.eye_pos + aabb.x * dir - in.cube_pos;
|
||||
}
|
||||
|
||||
let cube_color = vec3(1.);
|
||||
|
||||
var pos = hit_pos * f32(chunk_size);
|
||||
let step = vec3<i32>(
|
||||
select(-1, 1, dir.x > 0.),
|
||||
select(-1, 1, dir.y > 0.),
|
||||
select(-1, 1, dir.z > 0.)
|
||||
);
|
||||
|
||||
var voxel = vec3<i32>(
|
||||
clamp(i32(floor(pos.x)), 0, chunk_size - 1),
|
||||
clamp(i32(floor(pos.y)), 0, chunk_size - 1),
|
||||
clamp(i32(floor(pos.z)), 0, chunk_size - 1),
|
||||
);
|
||||
|
||||
let tDelta = vec3<f32>(1.) / abs(dir);
|
||||
var dist = vec3(
|
||||
select(pos.x - f32(voxel.x), f32(voxel.x) + 1. - pos.x, step.x > 0),
|
||||
select(pos.y - f32(voxel.y), f32(voxel.y) + 1. - pos.y, step.y > 0),
|
||||
select(pos.z - f32(voxel.z), f32(voxel.z) + 1. - pos.z, step.z > 0),
|
||||
);
|
||||
var tMax = dist * tDelta;
|
||||
//var tMax = (ceil(vec3<f32>(step) * pos) - vec3<f32>(step) * pos) * tDelta;
|
||||
var t = 0.;
|
||||
|
||||
|
||||
// Loop
|
||||
loop
|
||||
{
|
||||
if any(voxel >= vec3<i32>(chunk_size)) || any(voxel < vec3<i32>(0))
|
||||
{
|
||||
discard;
|
||||
//break;
|
||||
}
|
||||
// Sample
|
||||
if is_voxel(voxel)
|
||||
{
|
||||
// Compute normal
|
||||
let voxel_center = vec3<f32>(voxel) + vec3(0.5);
|
||||
let pos = pos + t * dir;
|
||||
|
||||
let norm_dir = normalize(pos - voxel_center);
|
||||
let norm_dir_max = max_mask3 (abs(norm_dir));
|
||||
let norm = sign(norm_dir * norm_dir_max);
|
||||
|
||||
let color = (1.2 + dot(norm, vec3<f32>(0., 1., 0.))) * 0.5;
|
||||
return vec4(vec3<f32>(color) * cube_color, 1.);
|
||||
return vec4(vec3<f32>(color), 1.);
|
||||
}
|
||||
|
||||
// Select which to step
|
||||
let mask = min_mask3(tMax);
|
||||
let delta = tDelta * mask;
|
||||
|
||||
let next_t_vec = tMax * mask;
|
||||
t = next_t_vec.x + next_t_vec.y + next_t_vec.z;
|
||||
|
||||
tMax += delta;
|
||||
|
||||
voxel += step * vec3<i32>(mask);
|
||||
|
||||
}
|
||||
|
||||
// Ray direction
|
||||
return vec4<f32>(1., 0., 1., 1.);
|
||||
}
|
||||
|
||||
fn min_mask3(v: vec3<f32>) -> vec3<f32>
|
||||
{
|
||||
let min = min(v.x, min(v.y, v.z));
|
||||
|
||||
return vec3<f32>
|
||||
(
|
||||
select(0., 1., v.x == min),
|
||||
select(0., 1., v.y == min),
|
||||
select(0., 1., v.z == min),
|
||||
);
|
||||
}
|
||||
|
||||
fn min_mask3i32(v: vec3<f32>) -> vec3<i32>
|
||||
{
|
||||
let min = min(v.x, min(v.y, v.z));
|
||||
|
||||
return vec3<i32>
|
||||
(
|
||||
select(0, 1, v.x == min),
|
||||
select(0, 1, v.y == min),
|
||||
select(0, 1, v.z == min),
|
||||
);
|
||||
}
|
||||
|
||||
fn max_mask3(v: vec3<f32>) -> vec3<f32>
|
||||
{
|
||||
let max = max(v.x, max(v.y, v.z));
|
||||
|
||||
return vec3<f32>
|
||||
(
|
||||
select(0., 1., v.x == max),
|
||||
select(0., 1., v.y == max),
|
||||
select(0., 1., v.z == max),
|
||||
);
|
||||
}
|
||||
|
||||
fn intersectAABB(rayOrigin: vec3<f32>, rayDir: vec3<f32>, boxMin: vec3<f32>, boxMax: vec3<f32>) -> vec2<f32> {
|
||||
let tMin = (boxMin - rayOrigin) / rayDir;
|
||||
let tMax = (boxMax - rayOrigin) / rayDir;
|
||||
let t1 = min(tMin, tMax);
|
||||
let t2 = max(tMin, tMax);
|
||||
let tNear = max(max(t1.x, t1.y), t1.z);
|
||||
let tFar = min(min(t2.x, t2.y), t2.z);
|
||||
return vec2(tNear, tFar);
|
||||
};
|
||||
|
||||
fn inferno_quintic( xx: f32 ) -> vec4<f32>
|
||||
{
|
||||
let x = saturate(xx);
|
||||
let x1 = vec4( 1.0, x, x * x, x * x * x ); // 1 x x2 x3
|
||||
let x2 = x1 * x1.w * x; // x4 x5 x6 x7
|
||||
return vec4(saturate( vec3(
|
||||
dot( x1.xyzw, vec4( -0.027780558, 1.228188385, 0.278906882, 3.892783760 ) ) + dot( x2.xy, vec2( -8.490712758, 4.069046086 ) ),
|
||||
dot( x1.xyzw, vec4( 0.014065206, 0.015360518, 1.605395918, -4.821108251 ) ) + dot( x2.xy, vec2( 8.389314011, -4.193858954 ) ),
|
||||
dot( x1.xyzw, vec4( -0.019628385, 3.122510347, -5.893222355, 2.798380308 ) ) + dot( x2.xy, vec2( -3.608884658, 4.324996022 ) ) ) ), 1.);
|
||||
return vec4(1.);
|
||||
}
|
||||
|
||||
30
shaders/lighting.wgsl
Normal file
30
shaders/lighting.wgsl
Normal file
@ -0,0 +1,30 @@
|
||||
|
||||
@vertex
|
||||
fn vertex_main(@builtin(vertex_index) vertex_id: u32) -> @builtin(position) vec4<f32>
|
||||
{
|
||||
|
||||
let vertices = array<vec2<f32>, 3>(
|
||||
vec2(-1., -3.),
|
||||
vec2(-1., 1.),
|
||||
vec2(3., 1.),
|
||||
);
|
||||
|
||||
return vec4(vertices[vertex_id], 0., 1.);
|
||||
}
|
||||
|
||||
@group(0) @binding(0) var albedo_tex: texture_storage_2d<rgba32float, read>;
|
||||
@group(0) @binding(1) var position_tex: texture_storage_2d<rgba32float, read>;
|
||||
@group(0) @binding(2) var normal_tex: texture_storage_2d<rgba32float, read>;
|
||||
@group(0) @binding(3) var depth_tex: texture_depth_2d;
|
||||
|
||||
@fragment
|
||||
fn fragment_main(@builtin(position) screen_position: vec4<f32>) -> @location(0) vec4<f32>
|
||||
{
|
||||
let texel_position = vec2<u32>(screen_position.xy);
|
||||
|
||||
let albedo = textureLoad(albedo_tex, texel_position);
|
||||
let position = textureLoad(position_tex, texel_position);
|
||||
let normal = textureLoad(normal_tex, texel_position);
|
||||
let depth = textureLoad(depth_tex, texel_position, 0);
|
||||
return vec4(depth / 1000.);
|
||||
}
|
||||
278
shaders/voxel_rt.wgsl
Normal file
278
shaders/voxel_rt.wgsl
Normal file
@ -0,0 +1,278 @@
|
||||
struct VertexOutput
|
||||
{
|
||||
@builtin(position) pos: vec4<f32>,
|
||||
@location(0) eye_pos: vec3<f32>,
|
||||
@location(1) world_pos: vec3<f32>,
|
||||
@location(2) cube_pos: vec3<f32>,
|
||||
@location(3) root_color: vec4<f32>,
|
||||
@location(4) root_subdivided: u32
|
||||
}
|
||||
|
||||
const N: u32 = 4;
|
||||
const N3: u32 = N * N * N;
|
||||
const MAX_DEPTH: u32 = 4;
|
||||
|
||||
struct StructureTile
|
||||
{
|
||||
children: array<u32, 64>,
|
||||
}
|
||||
|
||||
struct ColorTile
|
||||
{
|
||||
colors: array<vec4<f32>, 64>
|
||||
}
|
||||
|
||||
// BG
|
||||
@group(0) @binding(0) var<storage> structure_tiles : array<StructureTile>;
|
||||
@group(0) @binding(1) var<storage> color_tiles : array<ColorTile>;
|
||||
|
||||
struct TraverseResult
|
||||
{
|
||||
albedo: vec4<f32>,
|
||||
normal: vec3<f32>,
|
||||
position: vec3<f32>,
|
||||
}
|
||||
|
||||
fn traverse(max_depth: u32, root_color: vec4<f32>, root_subdivided: bool, eye_pos: vec3<f32>, ray_dir: vec3<f32>) -> TraverseResult
|
||||
{
|
||||
var normal = max_mask3(eye_pos) * sign(ray_dir);
|
||||
if max_depth == 0 || !root_subdivided
|
||||
{
|
||||
var res: TraverseResult;
|
||||
res.albedo = root_color;
|
||||
res.normal = normal;
|
||||
res.position = eye_pos;
|
||||
return res;
|
||||
//return viridis_quintic(0.);
|
||||
}
|
||||
|
||||
// var node_structs = structure_tiles[0];
|
||||
// var node_colors = color_tiles[0];
|
||||
var node_tile_idx = u32(0);
|
||||
var stack = array<u32, 4>(0, 0, 0, 0);
|
||||
|
||||
var voxel_scale = N * N * N;
|
||||
var scale_exp = 3;
|
||||
let voxel_scale_lut = array<u32, 4>(
|
||||
1,
|
||||
N,
|
||||
N * N,
|
||||
N * N * N
|
||||
);
|
||||
|
||||
let dist = 1. / ray_dir;
|
||||
let origin = eye_pos * 256.;
|
||||
var pos = origin; // In voxel space
|
||||
let offset = - origin * dist;
|
||||
let wall_offset = select(vec3(0.), vec3(1.), ray_dir > vec3(0.));
|
||||
let step = select(vec3(-1), vec3(1), ray_dir > vec3(0.));
|
||||
var voxel_pos = clamp(vec3<u32>(floor(pos)), vec3(0), vec3(N * N * N * N - 1));
|
||||
let max = 60.;
|
||||
for(var iter = 0; iter < 256; iter++)
|
||||
{
|
||||
var child_pos = voxel_pos >> vec3<u32>(scale_exp * 2);
|
||||
var local_child_pos = child_pos & vec3<u32>(3);
|
||||
var child_idx = local_child_pos.x + local_child_pos.y * N + local_child_pos.z * N * N;
|
||||
|
||||
while (structure_tiles[node_tile_idx].children[child_idx] >> 31) != 0 && (u32(4 - scale_exp) < max_depth)
|
||||
{
|
||||
stack[scale_exp] = u32(node_tile_idx);
|
||||
scale_exp -= 1;
|
||||
voxel_scale = voxel_scale_lut[scale_exp];
|
||||
|
||||
node_tile_idx = structure_tiles[node_tile_idx].children[child_idx] & 0x3fffffff;
|
||||
|
||||
child_pos = voxel_pos >> vec3<u32>(scale_exp * 2);
|
||||
local_child_pos = child_pos & vec3<u32>(3);
|
||||
child_idx = local_child_pos.x + local_child_pos.y * N + local_child_pos.z * N * N;
|
||||
}
|
||||
|
||||
if color_tiles[node_tile_idx].colors[child_idx].w != 0
|
||||
{
|
||||
var res: TraverseResult;
|
||||
res.albedo = color_tiles[node_tile_idx].colors[child_idx];
|
||||
res.normal = vec3<f32>(normal);
|
||||
res.position = pos;
|
||||
return res;
|
||||
// return vec4(0.5 * (dot(vec3<f32>(normal), vec3<f32>(1., 1., 1.)) + 1.2));
|
||||
// return color_tiles[node_tile_idx].colors[child_idx] * sample_mat(vec3<i32>(child_pos));
|
||||
// let color_lut = array<vec3<f32>, 4>(
|
||||
// vec3(1., 1., 0.2),
|
||||
// vec3(0.2, 1., 0.2),
|
||||
// vec3(0.2, 1., 1.),
|
||||
// vec3(1., 0.2, 1.),
|
||||
// );
|
||||
// return vec4(color_lut[scale_exp], 1.) * sample_mat(vec3<i32>(child_pos));
|
||||
// return color_tiles[node_tile_idx].colors[child_idx] * sample_mat(vec3<i32>(child_pos));
|
||||
// return inferno_quintic(f32(iter) / max);
|
||||
}
|
||||
|
||||
// Compute intersection
|
||||
let global_voxel = vec3<f32>(child_pos * voxel_scale);
|
||||
let cell_max = global_voxel + vec3<f32>(voxel_scale) * wall_offset;
|
||||
|
||||
let t1 = fma(dist, cell_max, offset);
|
||||
|
||||
let t_far = min(t1.x, min(t1.y, t1.z));
|
||||
|
||||
// Figure out which boundary we crossed to Figure out next neighbor
|
||||
normal = select(vec3(0.), vec3<f32>(step), vec3(t_far) == t1);
|
||||
let neighbor_min = vec3<i32>(global_voxel) + select(vec3(0), step * vec3<i32>(voxel_scale), vec3(t_far) == t1);
|
||||
let neighbor_max = neighbor_min + vec3<i32>(voxel_scale);
|
||||
pos = clamp(origin + t_far * ray_dir, vec3<f32>(neighbor_min), vec3<f32>(neighbor_max) - vec3<f32>(1.));
|
||||
//return vec4(vec3<f32>(neighbor_min) / 256., 1.);
|
||||
|
||||
voxel_pos = vec3<u32>(floor(pos));
|
||||
|
||||
let diff = vec3<u32>(pos + 256) ^ vec3<u32>(global_voxel + 256);
|
||||
let diff_exp = (firstLeadingBit((diff.x | diff.y | diff.z)) >> 1);
|
||||
|
||||
|
||||
if diff_exp > u32(scale_exp)
|
||||
{
|
||||
if diff_exp > 3
|
||||
{
|
||||
discard;
|
||||
//return viridis_quintic(f32(iter) / max);
|
||||
}
|
||||
|
||||
scale_exp = i32(diff_exp);
|
||||
voxel_scale = voxel_scale_lut[scale_exp];
|
||||
node_tile_idx = stack[scale_exp];
|
||||
}
|
||||
}
|
||||
|
||||
var res: TraverseResult;
|
||||
res.albedo = vec4(1., 0., 1., 1.);
|
||||
res.normal = vec3<f32>(1., 0., 0.);
|
||||
return res;
|
||||
}
|
||||
|
||||
struct FragmentOutput
|
||||
{
|
||||
@location(0) albedo: vec4<f32>,
|
||||
@location(1) position: vec4<f32>,
|
||||
@location(2) normal: vec4<f32>,
|
||||
}
|
||||
|
||||
@fragment
|
||||
fn fragment_main(in: VertexOutput) -> FragmentOutput
|
||||
{
|
||||
var hit_pos = vec3(0.);
|
||||
let dir = normalize(in.world_pos.xyz - in.eye_pos);
|
||||
let chunk_size = 4 * 4 * 4 * 4;
|
||||
|
||||
let aabb = intersectAABB(in.eye_pos, dir, in.cube_pos, in.cube_pos + vec3(1.));
|
||||
let norm = -intersect_normal_AABB(in.eye_pos, dir, in.cube_pos, in.cube_pos + vec3(1.));
|
||||
hit_pos = in.eye_pos + max(aabb.x, 0.) * dir - in.cube_pos;
|
||||
|
||||
let cube_color = vec3(1.);
|
||||
|
||||
var pos = hit_pos * f32(chunk_size);
|
||||
let step = vec3<i32>(
|
||||
select(-1, 1, dir.x > 0.),
|
||||
select(-1, 1, dir.y > 0.),
|
||||
select(-1, 1, dir.z > 0.)
|
||||
);
|
||||
|
||||
var voxel = vec3<i32>(
|
||||
clamp(i32(floor(pos.x)), 0, chunk_size - 1),
|
||||
clamp(i32(floor(pos.y)), 0, chunk_size - 1),
|
||||
clamp(i32(floor(pos.z)), 0, chunk_size - 1),
|
||||
);
|
||||
|
||||
var div = 1;
|
||||
var overlay = 1.;
|
||||
for(var i = 1; i <= 4; i++)
|
||||
{
|
||||
let x = (voxel.x / div + voxel.y / div + voxel.z / div) % 2 == 0;
|
||||
overlay -= select(0., 1. / (f32(i) * 2.5), x);
|
||||
div *= 4;
|
||||
}
|
||||
overlay = 1.;
|
||||
let max_depth = 4;
|
||||
let glob_dist = aabb.x / 1.5; // Pixel footprint
|
||||
let depth = clamp(4 - u32(floor(log(glob_dist) / log(4.))), 0, 4);
|
||||
let res = traverse(depth, in.root_color, in.root_subdivided != 0, hit_pos, dir);
|
||||
|
||||
var output: FragmentOutput;
|
||||
output.albedo = res.albedo;
|
||||
output.normal= vec4(-res.normal, 1.);
|
||||
output.position = vec4(res.position, 1.);
|
||||
return output;
|
||||
}
|
||||
|
||||
fn min_mask3(v: vec3<f32>) -> vec3<f32>
|
||||
{
|
||||
let min = min(v.x, min(v.y, v.z));
|
||||
|
||||
return vec3<f32>
|
||||
(
|
||||
select(0., 1., v.x == min),
|
||||
select(0., 1., v.y == min),
|
||||
select(0., 1., v.z == min),
|
||||
);
|
||||
}
|
||||
|
||||
fn min_mask3i32(v: vec3<f32>) -> vec3<i32>
|
||||
{
|
||||
let min = min(v.x, min(v.y, v.z));
|
||||
|
||||
return vec3<i32>
|
||||
(
|
||||
select(0, 1, v.x == min),
|
||||
select(0, 1, v.y == min),
|
||||
select(0, 1, v.z == min),
|
||||
);
|
||||
}
|
||||
|
||||
fn max_mask3(v: vec3<f32>) -> vec3<f32>
|
||||
{
|
||||
let max = max(v.x, max(v.y, v.z));
|
||||
|
||||
return vec3<f32>
|
||||
(
|
||||
select(0., 1., v.x == max),
|
||||
select(0., 1., v.y == max),
|
||||
select(0., 1., v.z == max),
|
||||
);
|
||||
}
|
||||
|
||||
fn intersectAABB(rayOrigin: vec3<f32>, rayDir: vec3<f32>, boxMin: vec3<f32>, boxMax: vec3<f32>) -> vec2<f32> {
|
||||
let tMin = (boxMin - rayOrigin) / rayDir;
|
||||
let tMax = (boxMax - rayOrigin) / rayDir;
|
||||
let t1 = min(tMin, tMax);
|
||||
let t2 = max(tMin, tMax);
|
||||
let tNear = max(max(t1.x, t1.y), t1.z);
|
||||
let tFar = min(min(t2.x, t2.y), t2.z);
|
||||
return vec2(tNear, tFar);
|
||||
};
|
||||
|
||||
fn intersect_normal_AABB(rayOrigin: vec3<f32>, rayDir: vec3<f32>, boxMin: vec3<f32>, boxMax: vec3<f32>) -> vec3<f32> {
|
||||
let tMin = (boxMin - rayOrigin) / rayDir;
|
||||
let tMax = (boxMax - rayOrigin) / rayDir;
|
||||
let t1 = min(tMin, tMax);
|
||||
let tNear = max(max(t1.x, t1.y), t1.z);
|
||||
return select(vec3(0), sign(rayDir), vec3(tNear) == t1);
|
||||
};
|
||||
|
||||
fn inferno_quintic( xx: f32 ) -> vec4<f32>
|
||||
{
|
||||
let x = saturate(xx);
|
||||
let x1 = vec4( 1.0, x, x * x, x * x * x ); // 1 x x2 x3
|
||||
let x2 = x1 * x1.w * x; // x4 x5 x6 x7
|
||||
return vec4(saturate( vec3(
|
||||
dot( x1.xyzw, vec4( -0.027780558, 1.228188385, 0.278906882, 3.892783760 ) ) + dot( x2.xy, vec2( -8.490712758, 4.069046086 ) ),
|
||||
dot( x1.xyzw, vec4( 0.014065206, 0.015360518, 1.605395918, -4.821108251 ) ) + dot( x2.xy, vec2( 8.389314011, -4.193858954 ) ),
|
||||
dot( x1.xyzw, vec4( -0.019628385, 3.122510347, -5.893222355, 2.798380308 ) ) + dot( x2.xy, vec2( -3.608884658, 4.324996022 ) ) ) ), 1.);
|
||||
}
|
||||
fn viridis_quintic( xx: f32 ) -> vec4<f32>
|
||||
{
|
||||
let x = saturate( xx );
|
||||
let x1 = vec4( 1.0, x, x * x, x * x * x ); // 1 x x2 x3
|
||||
let x2 = x1 * x1.w * x; // x4 x5 x6 x7
|
||||
return vec4(saturate( vec3(
|
||||
dot( x1.xyzw, vec4( 0.280268003, -0.143510503, 2.225793877, -14.815088879 ) ) + dot( x2.xy, vec2( 25.212752309, -11.772589584 ) ),
|
||||
dot( x1.xyzw, vec4( -0.002117546, 1.617109353, -1.909305070, 2.701152864 ) ) + dot( x2.xy, vec2( -1.685288385, 0.178738871 ) ),
|
||||
dot( x1.xyzw, vec4( 0.300805501, 2.614650302, -12.019139090, 28.933559110 ) ) + dot( x2.xy, vec2( -33.491294770, 13.762053843 ) ) ) ), 1.);
|
||||
}
|
||||
166
src/hm_renderer.rs
Normal file
166
src/hm_renderer.rs
Normal file
@ -0,0 +1,166 @@
|
||||
use std::fs::File;
|
||||
use std::io::BufReader;
|
||||
use std::path::Path;
|
||||
|
||||
use itertools::Itertools;
|
||||
use tiff::decoder::Decoder;
|
||||
use wgpu::Device;
|
||||
use wgpu::RenderPass;
|
||||
use wgpu::TextureFormat;
|
||||
|
||||
use crate::state::Camera;
|
||||
use crate::voxel_renderer::VoxelRenderer;
|
||||
|
||||
pub struct HeightMapRenderer
|
||||
{
|
||||
width: u32,
|
||||
height: u32,
|
||||
|
||||
heightmap: Vec<f32>,
|
||||
height_min: f32,
|
||||
height_max: f32,
|
||||
|
||||
alive_chunks: Vec<cgmath::Vector3<i32>>,
|
||||
eye_pos: cgmath::Vector3<f32>,
|
||||
|
||||
voxel_renderer: VoxelRenderer,
|
||||
}
|
||||
|
||||
impl HeightMapRenderer
|
||||
{
|
||||
pub fn from_image(
|
||||
path: impl AsRef<Path>,
|
||||
device: &Device,
|
||||
surface_format: TextureFormat,
|
||||
) -> Self
|
||||
{
|
||||
let file = File::open(path).unwrap();
|
||||
let mut decoder = Decoder::new(BufReader::new(file)).unwrap();
|
||||
let (width, height) = decoder.dimensions().unwrap();
|
||||
|
||||
let decoded = decoder.read_image().unwrap();
|
||||
let pixels = match decoded
|
||||
{
|
||||
tiff::decoder::DecodingResult::F32(decoded_pixels) => decoded_pixels,
|
||||
_ =>
|
||||
{
|
||||
panic!("Unsuported image format.");
|
||||
}
|
||||
};
|
||||
|
||||
Self {
|
||||
width,
|
||||
height,
|
||||
|
||||
height_min: pixels.iter().copied().reduce(f32::min).unwrap_or(0.),
|
||||
height_max: pixels.iter().copied().reduce(f32::max).unwrap_or(0.),
|
||||
|
||||
heightmap: pixels,
|
||||
alive_chunks: vec![],
|
||||
eye_pos: cgmath::Vector3::new(0., 0., 0.),
|
||||
|
||||
voxel_renderer: VoxelRenderer::new(device, surface_format),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_eye_pos(&mut self, eye_pos: cgmath::Vector3<f32>, device: &Device)
|
||||
{
|
||||
self.eye_pos = eye_pos;
|
||||
// self.voxel_renderer.set_chunks(
|
||||
// device,
|
||||
// &[
|
||||
// cgmath::Vector3::new(-1, 0, -1),
|
||||
// cgmath::Vector3::new(-1, 0, 1),
|
||||
// cgmath::Vector3::new(1, 0, -1),
|
||||
// cgmath::Vector3::new(1, 0, 1),
|
||||
// ],
|
||||
// );
|
||||
// return;
|
||||
// Compute alive chunks
|
||||
|
||||
let eye_chunk = cgmath::Vector3::new(
|
||||
eye_pos.x.floor() as i32,
|
||||
eye_pos.y.floor() as i32,
|
||||
eye_pos.z.floor() as i32,
|
||||
);
|
||||
let mut alive_chunks = vec![];
|
||||
for ((x, y), z) in (-10..=10)
|
||||
.cartesian_product(-10..=10)
|
||||
.cartesian_product(-10..=10)
|
||||
{
|
||||
let chunk = eye_chunk + cgmath::Vector3::new(x, y, z);
|
||||
if chunk.x >= 0 && chunk.z >= 0
|
||||
{
|
||||
if chunk.y == -1
|
||||
{
|
||||
alive_chunks.push(chunk);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
self.alive_chunks = alive_chunks;
|
||||
if !self.alive_chunks.is_empty()
|
||||
{
|
||||
self.voxel_renderer.set_chunks(device, &self.alive_chunks);
|
||||
}
|
||||
return;
|
||||
|
||||
let mut alive_chunks = vec![];
|
||||
for ((x, y), z) in (-10..=10)
|
||||
.cartesian_product(-10..=10)
|
||||
.cartesian_product(-10..=10)
|
||||
{
|
||||
let chunk = eye_chunk + cgmath::Vector3::new(x, y, z);
|
||||
if chunk.x >= 0
|
||||
&& chunk.x < (self.width / 256) as i32
|
||||
&& chunk.z >= 0
|
||||
&& chunk.z < (self.height / 256) as i32
|
||||
{
|
||||
if chunk.y == -1
|
||||
{
|
||||
alive_chunks.push(chunk);
|
||||
println!("{}, {}, {}", chunk.x, chunk.y, chunk.z);
|
||||
}
|
||||
continue;
|
||||
|
||||
let chunk_voxel_height = y * 256;
|
||||
let submit = [
|
||||
(chunk.x, chunk.z),
|
||||
(chunk.x + 1, chunk.z),
|
||||
(chunk.x, chunk.z + 1),
|
||||
(chunk.x + 1, chunk.z + 1),
|
||||
]
|
||||
.iter()
|
||||
.map(|(chunk_x, chunk_z)| {
|
||||
let voxel_x = chunk_x * 256;
|
||||
let voxel_z = chunk_z * 256;
|
||||
|
||||
let altitude =
|
||||
self.heightmap[voxel_x as usize + voxel_z as usize * self.width as usize];
|
||||
let voxel_height =
|
||||
map(altitude, self.height_min, self.height_max, 0., 100. * 256.);
|
||||
//println!("{}", voxel_height);
|
||||
(chunk_voxel_height as f32) < voxel_height
|
||||
})
|
||||
.reduce(|a, b| a || b)
|
||||
.unwrap_or(false);
|
||||
if submit
|
||||
{
|
||||
alive_chunks.push(chunk);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn render(&mut self, render_pass: &mut RenderPass, camera: &Camera)
|
||||
{
|
||||
if !self.alive_chunks.is_empty()
|
||||
{}
|
||||
self.voxel_renderer.render(render_pass, camera);
|
||||
}
|
||||
}
|
||||
|
||||
fn map(x: f32, x_min: f32, x_max: f32, y_min: f32, y_max: f32) -> f32
|
||||
{
|
||||
((x - x_min) / (x_max - x_min)) * (y_max - y_min) + y_min
|
||||
}
|
||||
47
src/lib.rs
47
src/lib.rs
@ -2,8 +2,10 @@
|
||||
#![feature(generic_const_exprs)]
|
||||
|
||||
pub mod egui_renderer;
|
||||
pub mod hm_renderer;
|
||||
pub mod state;
|
||||
pub mod voxel;
|
||||
pub mod voxel_renderer;
|
||||
|
||||
use std::sync::Arc;
|
||||
|
||||
@ -15,7 +17,8 @@ use winit::window::Window;
|
||||
|
||||
use crate::state::State;
|
||||
|
||||
pub fn run() -> anyhow::Result<()> {
|
||||
pub fn run() -> anyhow::Result<()>
|
||||
{
|
||||
env_logger::init();
|
||||
|
||||
let event_loop = EventLoop::with_user_event().build()?;
|
||||
@ -27,12 +30,15 @@ pub fn run() -> anyhow::Result<()> {
|
||||
|
||||
// App struct
|
||||
#[derive(Default)]
|
||||
pub struct App {
|
||||
pub struct App
|
||||
{
|
||||
state: Option<State>,
|
||||
}
|
||||
|
||||
impl ApplicationHandler for App {
|
||||
fn resumed(&mut self, event_loop: &event_loop::ActiveEventLoop) {
|
||||
impl ApplicationHandler for App
|
||||
{
|
||||
fn resumed(&mut self, event_loop: &event_loop::ActiveEventLoop)
|
||||
{
|
||||
// Create window
|
||||
let window = Arc::new(
|
||||
event_loop
|
||||
@ -59,28 +65,35 @@ impl ApplicationHandler for App {
|
||||
event_loop: &event_loop::ActiveEventLoop,
|
||||
_window_id: winit::window::WindowId,
|
||||
event: winit::event::WindowEvent,
|
||||
) {
|
||||
)
|
||||
{
|
||||
let state = self.state.as_mut().unwrap();
|
||||
state.handle_event(&event);
|
||||
|
||||
match event {
|
||||
WindowEvent::CloseRequested => {
|
||||
match event
|
||||
{
|
||||
WindowEvent::CloseRequested =>
|
||||
{
|
||||
event_loop.exit();
|
||||
}
|
||||
WindowEvent::RedrawRequested => {
|
||||
WindowEvent::RedrawRequested =>
|
||||
{
|
||||
state.render();
|
||||
state.get_window().request_redraw();
|
||||
}
|
||||
|
||||
WindowEvent::Resized(size) => {
|
||||
WindowEvent::Resized(size) =>
|
||||
{
|
||||
state.resize(size);
|
||||
}
|
||||
|
||||
WindowEvent::MouseWheel { delta, .. } => {
|
||||
WindowEvent::MouseWheel { delta, .. } =>
|
||||
{
|
||||
state.mouse_wheel(delta);
|
||||
}
|
||||
|
||||
_ => {}
|
||||
_ =>
|
||||
{}
|
||||
}
|
||||
}
|
||||
|
||||
@ -89,16 +102,20 @@ impl ApplicationHandler for App {
|
||||
_event_loop: &event_loop::ActiveEventLoop,
|
||||
_device_id: winit::event::DeviceId,
|
||||
event: winit::event::DeviceEvent,
|
||||
) {
|
||||
)
|
||||
{
|
||||
let state = self.state.as_mut().unwrap();
|
||||
|
||||
#[allow(clippy::single_match)]
|
||||
match event {
|
||||
winit::event::DeviceEvent::MouseMotion { delta } => {
|
||||
match event
|
||||
{
|
||||
winit::event::DeviceEvent::MouseMotion { delta } =>
|
||||
{
|
||||
state.cursor_moved(delta.0 as f32, delta.1 as f32);
|
||||
}
|
||||
|
||||
_ => {}
|
||||
_ =>
|
||||
{}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
385
src/state.rs
385
src/state.rs
@ -1,10 +1,8 @@
|
||||
use std::collections::HashSet;
|
||||
use std::num::NonZero;
|
||||
use std::sync::Arc;
|
||||
use std::time::Instant;
|
||||
|
||||
use cgmath::EuclideanSpace;
|
||||
use cgmath::InnerSpace;
|
||||
use cgmath::Matrix4;
|
||||
use cgmath::Point3;
|
||||
use cgmath::SquareMatrix;
|
||||
@ -12,37 +10,23 @@ use cgmath::Vector3;
|
||||
use cgmath::Vector4;
|
||||
use crevice::std430::AsStd430;
|
||||
use egui_wgpu::ScreenDescriptor;
|
||||
use indicatif::ProgressIterator;
|
||||
use itertools::Itertools;
|
||||
use wgpu::Backends;
|
||||
use wgpu::BindGroup;
|
||||
use wgpu::BindGroupDescriptor;
|
||||
use wgpu::BindGroupEntry;
|
||||
use wgpu::BindGroupLayoutDescriptor;
|
||||
use wgpu::BindGroupLayoutEntry;
|
||||
use wgpu::Buffer;
|
||||
use wgpu::BufferUsages;
|
||||
use wgpu::Device;
|
||||
use wgpu::Extent3d;
|
||||
use wgpu::FeaturesWGPU;
|
||||
use wgpu::FeaturesWebGPU;
|
||||
use wgpu::PushConstantRange;
|
||||
use wgpu::RenderPipeline;
|
||||
use wgpu::ShaderStages;
|
||||
use wgpu::TextureDescriptor;
|
||||
use wgpu::TextureFormat;
|
||||
use wgpu::TextureUsages;
|
||||
use wgpu::TextureView;
|
||||
use wgpu::include_wgsl;
|
||||
use wgpu::util::DeviceExt;
|
||||
use wgpu::TextureViewDescriptor;
|
||||
use winit::event::MouseScrollDelta;
|
||||
use winit::event::WindowEvent;
|
||||
use winit::keyboard::KeyCode;
|
||||
use winit::window::Window;
|
||||
|
||||
use crate::egui_renderer::EguiState;
|
||||
use crate::voxel::Color;
|
||||
use crate::voxel::GPUStructureTile;
|
||||
use crate::voxel::NTree;
|
||||
use crate::voxel_renderer::VoxelRenderer;
|
||||
|
||||
pub struct State
|
||||
{
|
||||
@ -55,25 +39,15 @@ pub struct State
|
||||
egui_state: EguiState,
|
||||
camera: Camera,
|
||||
|
||||
// Pipelines
|
||||
rm_pipeline: RenderPipeline,
|
||||
|
||||
depth_buffer_view: TextureView,
|
||||
|
||||
// Input
|
||||
pressed_set: HashSet<KeyCode>,
|
||||
|
||||
// Frame time
|
||||
last_frame: Instant,
|
||||
|
||||
// Data
|
||||
ntree: NTree<4>,
|
||||
depth_buffer: TextureView,
|
||||
|
||||
// Tree structure
|
||||
structure_tiles_buf: Buffer,
|
||||
color_tiles_buf: Buffer,
|
||||
tree_bind_group: BindGroup,
|
||||
root_color: Color,
|
||||
voxel_renderer: VoxelRenderer,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
@ -111,12 +85,15 @@ impl State
|
||||
required_features: wgpu::Features {
|
||||
features_wgpu: FeaturesWGPU::PUSH_CONSTANTS
|
||||
| FeaturesWGPU::BUFFER_BINDING_ARRAY
|
||||
| FeaturesWGPU::STORAGE_RESOURCE_BINDING_ARRAY,
|
||||
| FeaturesWGPU::TEXTURE_BINDING_ARRAY
|
||||
| FeaturesWGPU::STORAGE_RESOURCE_BINDING_ARRAY
|
||||
| FeaturesWGPU::MULTIVIEW,
|
||||
features_webgpu: FeaturesWebGPU::empty(),
|
||||
},
|
||||
required_limits: wgpu::Limits {
|
||||
max_push_constant_size: RayMarchingPushConstants::std430_size_static() as u32,
|
||||
max_binding_array_elements_per_shader_stage: 2,
|
||||
max_binding_array_elements_per_shader_stage: 4,
|
||||
max_color_attachment_bytes_per_sample: 48,
|
||||
..wgpu::Limits::downlevel_defaults()
|
||||
},
|
||||
..Default::default()
|
||||
@ -130,193 +107,6 @@ impl State
|
||||
let cap = surface.get_capabilities(&adapter);
|
||||
let surface_format = cap.formats[0];
|
||||
|
||||
// Depth buffer
|
||||
let depth_buffer = device.create_texture(&TextureDescriptor {
|
||||
label: Some("depth_buffer"),
|
||||
size: Extent3d {
|
||||
width: size.width,
|
||||
height: size.height,
|
||||
depth_or_array_layers: 1,
|
||||
},
|
||||
mip_level_count: 1,
|
||||
sample_count: 1,
|
||||
dimension: wgpu::TextureDimension::D2,
|
||||
format: TextureFormat::Depth24PlusStencil8,
|
||||
usage: TextureUsages::RENDER_ATTACHMENT,
|
||||
view_formats: &[],
|
||||
});
|
||||
|
||||
let depth_buffer_view = depth_buffer.create_view(&Default::default());
|
||||
|
||||
// Rendering pipeline
|
||||
let cube_shader_module = device.create_shader_module(include_wgsl!("../shaders/cube.wgsl"));
|
||||
let tree_bind_group_layout = device.create_bind_group_layout(&BindGroupLayoutDescriptor {
|
||||
label: Some("ntree_bind_group_layout"),
|
||||
entries: &[
|
||||
BindGroupLayoutEntry {
|
||||
binding: 0,
|
||||
visibility: ShaderStages::FRAGMENT,
|
||||
ty: wgpu::BindingType::Buffer {
|
||||
ty: wgpu::BufferBindingType::Storage { read_only: true },
|
||||
has_dynamic_offset: false,
|
||||
min_binding_size: NonZero::new(0),
|
||||
},
|
||||
count: NonZero::new(1),
|
||||
},
|
||||
BindGroupLayoutEntry {
|
||||
binding: 1,
|
||||
visibility: ShaderStages::FRAGMENT,
|
||||
ty: wgpu::BindingType::Buffer {
|
||||
ty: wgpu::BufferBindingType::Storage { read_only: true },
|
||||
has_dynamic_offset: false,
|
||||
min_binding_size: NonZero::new(0),
|
||||
},
|
||||
count: NonZero::new(1),
|
||||
},
|
||||
],
|
||||
});
|
||||
let rm_pipeline_layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
|
||||
label: Some("Render3D Mesh Pipeline Layout"),
|
||||
bind_group_layouts: &[&tree_bind_group_layout],
|
||||
push_constant_ranges: &[PushConstantRange {
|
||||
stages: ShaderStages::VERTEX,
|
||||
range: 0..RayMarchingPushConstants::std430_size_static() as u32,
|
||||
}],
|
||||
});
|
||||
|
||||
let rm_pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor {
|
||||
label: Some("Render3D Mesh Pipeline"),
|
||||
layout: Some(&rm_pipeline_layout),
|
||||
vertex: wgpu::VertexState {
|
||||
module: &cube_shader_module,
|
||||
entry_point: Some("vertex_main"),
|
||||
buffers: &[],
|
||||
compilation_options: wgpu::PipelineCompilationOptions::default(),
|
||||
},
|
||||
fragment: Some(wgpu::FragmentState {
|
||||
module: &cube_shader_module,
|
||||
entry_point: Some("fragment_tree_main"),
|
||||
targets: &[Some(wgpu::ColorTargetState {
|
||||
format: surface_format,
|
||||
blend: Some(wgpu::BlendState::REPLACE),
|
||||
write_mask: wgpu::ColorWrites::ALL,
|
||||
})],
|
||||
compilation_options: wgpu::PipelineCompilationOptions::default(),
|
||||
}),
|
||||
primitive: wgpu::PrimitiveState {
|
||||
topology: wgpu::PrimitiveTopology::TriangleList,
|
||||
cull_mode: Some(wgpu::Face::Front),
|
||||
..Default::default()
|
||||
},
|
||||
depth_stencil: Some(wgpu::DepthStencilState {
|
||||
format: TextureFormat::Depth24PlusStencil8,
|
||||
depth_write_enabled: true,
|
||||
depth_compare: wgpu::CompareFunction::Less,
|
||||
stencil: wgpu::StencilState::default(),
|
||||
bias: wgpu::DepthBiasState::default(),
|
||||
}),
|
||||
multisample: wgpu::MultisampleState {
|
||||
count: 1,
|
||||
mask: !0,
|
||||
alpha_to_coverage_enabled: false,
|
||||
},
|
||||
multiview: None,
|
||||
cache: None,
|
||||
});
|
||||
|
||||
let mut ntree = NTree::<4>::constant(crate::voxel::Color(0., 0., 0., 0.), 4);
|
||||
|
||||
println!("Building tree");
|
||||
let width = 100;
|
||||
for ((x, y), z) in (0..(width / 2))
|
||||
.progress()
|
||||
.cartesian_product(0..width)
|
||||
.cartesian_product(0..width)
|
||||
{
|
||||
let dist =
|
||||
cgmath::Vector3::new(x as f32 - 128., y as f32 - 128., z as f32 - 128.).magnitude();
|
||||
if dist <= 128.
|
||||
{
|
||||
ntree.set(x, y, z, crate::voxel::Color(0.2, 1., 0.2, 1.));
|
||||
}
|
||||
}
|
||||
println!("Built tree");
|
||||
|
||||
// let width = 33;
|
||||
// for ((x, y), z) in (0..width)
|
||||
// .cartesian_product(0..width)
|
||||
// .cartesian_product(0..width)
|
||||
// {
|
||||
// if x == 32 || y == 32 || z == 32
|
||||
// {
|
||||
// ntree.set(x, y, z, Color(0., 1., 0., 1.));
|
||||
// }
|
||||
// else
|
||||
// {
|
||||
// ntree.set(x, y, z, Color(0., 0., 0., 0.));
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// for x in 0..256
|
||||
// {
|
||||
// ntree.set(x, 40, 40, Color(0., 0., 0., 0.));
|
||||
// ntree.set(x, 41, 40, Color(1., 0., 0., 1.));
|
||||
// ntree.set(x, 39, 40, Color(1., 0., 0., 1.));
|
||||
//
|
||||
// ntree.set(x, 40, 39, Color(0., 0., 1., 1.));
|
||||
// ntree.set(x, 40, 41, Color(0., 0., 1., 1.));
|
||||
// }
|
||||
//
|
||||
// let width = 16;
|
||||
// for ((x, y), z) in (0..width)
|
||||
// .cartesian_product(0..width)
|
||||
// .cartesian_product(0..width)
|
||||
// {
|
||||
// ntree.set(x, y, z, Color(0., 0., 0., 0.));
|
||||
// }
|
||||
// ntree.set(4, 0, 0, Color(1., 0., 0., 1.));
|
||||
|
||||
//ntree.set(0, 0, 0, Color(1., 0., 0., 1.));
|
||||
//ntree.set(255, 255, 255, Color(0., 0., 1., 1.));
|
||||
println!("Flattening");
|
||||
let (root_color, structure_tiles, color_tiles) = ntree.to_gpu_rep();
|
||||
println!("Falttened");
|
||||
|
||||
let structure_tiles_buf = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
|
||||
label: Some("structure_tiles_buf"),
|
||||
contents: unsafe {
|
||||
std::slice::from_raw_parts(
|
||||
structure_tiles.as_ptr() as *const u8,
|
||||
structure_tiles.len() * size_of::<GPUStructureTile<4>>(),
|
||||
)
|
||||
},
|
||||
usage: BufferUsages::STORAGE,
|
||||
});
|
||||
let color_tiles_buf = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
|
||||
label: Some("color_tiles_buf"),
|
||||
contents: unsafe {
|
||||
std::slice::from_raw_parts(
|
||||
color_tiles.as_ptr() as *const u8,
|
||||
color_tiles.len() * size_of::<[Color; 4 * 4 * 4]>(),
|
||||
)
|
||||
},
|
||||
usage: BufferUsages::STORAGE,
|
||||
});
|
||||
let tree_bind_group = device.create_bind_group(&BindGroupDescriptor {
|
||||
label: Some("tree_bind_group"),
|
||||
layout: &tree_bind_group_layout,
|
||||
entries: &[
|
||||
BindGroupEntry {
|
||||
binding: 0,
|
||||
resource: structure_tiles_buf.as_entire_binding(),
|
||||
},
|
||||
BindGroupEntry {
|
||||
binding: 1,
|
||||
resource: color_tiles_buf.as_entire_binding(),
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
let state = State {
|
||||
egui_state: EguiState::new(&device, surface_format, &window),
|
||||
|
||||
@ -326,7 +116,7 @@ impl State
|
||||
surface,
|
||||
surface_format,
|
||||
camera: Camera {
|
||||
eye: Point3::new(0., 0., 0.),
|
||||
eye: Point3::new(10., 0., 10.),
|
||||
up: Vector3::unit_y(),
|
||||
aspect: size.width as f32 / size.height as f32,
|
||||
fovy: 90.,
|
||||
@ -339,20 +129,18 @@ impl State
|
||||
speed: 0.005,
|
||||
},
|
||||
|
||||
rm_pipeline,
|
||||
|
||||
depth_buffer_view,
|
||||
|
||||
pressed_set: HashSet::new(),
|
||||
last_frame: Instant::now(),
|
||||
|
||||
ntree,
|
||||
|
||||
root_color,
|
||||
depth_buffer: create_gbuffer(
|
||||
&device,
|
||||
size.width,
|
||||
size.height,
|
||||
TextureFormat::Depth24PlusStencil8,
|
||||
TextureUsages::RENDER_ATTACHMENT,
|
||||
),
|
||||
voxel_renderer: VoxelRenderer::new(&device, surface_format),
|
||||
device,
|
||||
structure_tiles_buf,
|
||||
color_tiles_buf,
|
||||
tree_bind_group,
|
||||
};
|
||||
|
||||
// Configure surface for the first time
|
||||
@ -386,25 +174,14 @@ impl State
|
||||
{
|
||||
self.size = new_size;
|
||||
|
||||
// reconfigure the surface
|
||||
let depth_buffer = self.device.create_texture(&TextureDescriptor {
|
||||
label: Some("depth_buffer"),
|
||||
size: Extent3d {
|
||||
width: new_size.width,
|
||||
height: new_size.height,
|
||||
depth_or_array_layers: 1,
|
||||
},
|
||||
mip_level_count: 1,
|
||||
sample_count: 1,
|
||||
dimension: wgpu::TextureDimension::D2,
|
||||
format: TextureFormat::Depth24PlusStencil8,
|
||||
usage: TextureUsages::RENDER_ATTACHMENT,
|
||||
view_formats: &[],
|
||||
});
|
||||
|
||||
self.depth_buffer_view = depth_buffer.create_view(&Default::default());
|
||||
|
||||
self.camera.aspect = new_size.width as f32 / new_size.height as f32;
|
||||
self.depth_buffer = create_gbuffer(
|
||||
&self.device,
|
||||
new_size.width,
|
||||
new_size.height,
|
||||
TextureFormat::Depth24PlusStencil8,
|
||||
TextureUsages::RENDER_ATTACHMENT,
|
||||
);
|
||||
self.configure_surface();
|
||||
}
|
||||
|
||||
@ -439,7 +216,7 @@ impl State
|
||||
.surface
|
||||
.get_current_texture()
|
||||
.expect("failed to acquire next swapchain texture");
|
||||
let texture_view = surface_texture
|
||||
let surface_view = surface_texture
|
||||
.texture
|
||||
.create_view(&wgpu::TextureViewDescriptor {
|
||||
// Without add_srgb_suffix() the image we will be working with
|
||||
@ -447,60 +224,34 @@ impl State
|
||||
format: Some(self.surface_format.add_srgb_suffix()),
|
||||
..Default::default()
|
||||
});
|
||||
|
||||
// Renders a GREEN screen
|
||||
let mut encoder = self.device.create_command_encoder(&Default::default());
|
||||
// Create the renderpass which will clear the screen.
|
||||
let mut renderpass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
|
||||
label: None,
|
||||
color_attachments: &[Some(wgpu::RenderPassColorAttachment {
|
||||
view: &texture_view,
|
||||
depth_slice: None,
|
||||
resolve_target: None,
|
||||
ops: wgpu::Operations {
|
||||
load: wgpu::LoadOp::Clear(wgpu::Color::BLACK),
|
||||
store: wgpu::StoreOp::Store,
|
||||
},
|
||||
})],
|
||||
depth_stencil_attachment: Some(wgpu::RenderPassDepthStencilAttachment {
|
||||
view: &self.depth_buffer_view,
|
||||
depth_ops: Some(wgpu::Operations {
|
||||
load: wgpu::LoadOp::Clear(1.),
|
||||
store: wgpu::StoreOp::Discard,
|
||||
|
||||
{
|
||||
let mut hm_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
|
||||
label: None,
|
||||
color_attachments: &[Some(wgpu::RenderPassColorAttachment {
|
||||
view: &surface_view,
|
||||
depth_slice: None,
|
||||
resolve_target: None,
|
||||
ops: wgpu::Operations {
|
||||
load: wgpu::LoadOp::Clear(wgpu::Color::BLACK),
|
||||
store: wgpu::StoreOp::Store,
|
||||
},
|
||||
})],
|
||||
depth_stencil_attachment: Some(wgpu::RenderPassDepthStencilAttachment {
|
||||
view: &self.depth_buffer,
|
||||
depth_ops: Some(wgpu::Operations {
|
||||
load: wgpu::LoadOp::Clear(1.),
|
||||
store: wgpu::StoreOp::Discard,
|
||||
}),
|
||||
stencil_ops: None,
|
||||
}),
|
||||
stencil_ops: None,
|
||||
}),
|
||||
timestamp_writes: None,
|
||||
occlusion_query_set: None,
|
||||
});
|
||||
timestamp_writes: None,
|
||||
occlusion_query_set: None,
|
||||
});
|
||||
}
|
||||
|
||||
renderpass.set_pipeline(&self.rm_pipeline);
|
||||
|
||||
//renderpass.set_vertex_buffer(0, self.positions_buffer.slice(..));
|
||||
renderpass.set_bind_group(0, Some(&self.tree_bind_group), &[]);
|
||||
renderpass.set_push_constants(
|
||||
ShaderStages::VERTEX,
|
||||
0,
|
||||
RayMarchingPushConstants {
|
||||
view_projection: self.camera.view_proj(),
|
||||
transform: Matrix4::identity(),
|
||||
eye_position: self.camera.eye.to_vec(),
|
||||
root_color: Vector4::new(
|
||||
self.root_color.0,
|
||||
self.root_color.1,
|
||||
self.root_color.2,
|
||||
self.root_color.3,
|
||||
),
|
||||
root_subdivided: 1,
|
||||
}
|
||||
.as_std430()
|
||||
.as_bytes(),
|
||||
);
|
||||
renderpass.draw(0..(6 * 2 * 3), 0..1);
|
||||
|
||||
// End the renderpass.
|
||||
drop(renderpass);
|
||||
// Egui
|
||||
// Egui Pass
|
||||
{
|
||||
let screen_descriptor = ScreenDescriptor {
|
||||
size_in_pixels: [self.size.width, self.size.height],
|
||||
@ -519,6 +270,10 @@ impl State
|
||||
"Frame rate: {:.2} fps",
|
||||
1. / (Instant::now() - self.last_frame).as_secs_f32()
|
||||
));
|
||||
ui.label(format!(
|
||||
"Camera pos {:.1},{:.1},{:.1}",
|
||||
self.camera.eye.x, self.camera.eye.y, self.camera.eye.z,
|
||||
))
|
||||
},
|
||||
);
|
||||
|
||||
@ -527,7 +282,7 @@ impl State
|
||||
&self.queue,
|
||||
&mut encoder,
|
||||
&self.window,
|
||||
&texture_view,
|
||||
&surface_view,
|
||||
screen_descriptor,
|
||||
);
|
||||
}
|
||||
@ -589,6 +344,32 @@ impl State
|
||||
}
|
||||
}
|
||||
|
||||
pub fn create_gbuffer(
|
||||
device: &Device,
|
||||
width: u32,
|
||||
height: u32,
|
||||
format: TextureFormat,
|
||||
usage: TextureUsages,
|
||||
) -> TextureView
|
||||
{
|
||||
device
|
||||
.create_texture(&TextureDescriptor {
|
||||
label: Some("gbuffer"),
|
||||
size: Extent3d {
|
||||
width,
|
||||
height,
|
||||
depth_or_array_layers: 1,
|
||||
},
|
||||
mip_level_count: 1,
|
||||
sample_count: 1,
|
||||
dimension: wgpu::TextureDimension::D2,
|
||||
format,
|
||||
usage,
|
||||
view_formats: &[format],
|
||||
})
|
||||
.create_view(&TextureViewDescriptor::default())
|
||||
}
|
||||
|
||||
#[derive(AsStd430)]
|
||||
pub struct RayMarchingPushConstants
|
||||
{
|
||||
|
||||
318
src/voxel.rs
318
src/voxel.rs
@ -1,9 +1,13 @@
|
||||
use std::collections::HashMap;
|
||||
use std::collections::VecDeque;
|
||||
use std::hash::Hash;
|
||||
use std::usize;
|
||||
use std::vec;
|
||||
|
||||
use itertools::Itertools;
|
||||
use itertools::kmerge;
|
||||
|
||||
use crate::voxel;
|
||||
|
||||
#[derive(Clone, Copy, PartialEq, Debug)]
|
||||
pub struct Color(pub f32, pub f32, pub f32, pub f32);
|
||||
@ -74,6 +78,205 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
pub fn from_arrays_old(chunk: &[Color], depth: u32) -> Self
|
||||
{
|
||||
let chunk_width = N.pow(depth);
|
||||
let mut structure = HashMap::with_capacity(chunk_width * chunk_width * chunk_width);
|
||||
println!("Inserting voxels");
|
||||
for ((x, y), z) in (0..chunk_width)
|
||||
.cartesian_product(0..chunk_width)
|
||||
.cartesian_product(0..chunk_width)
|
||||
{
|
||||
structure.insert(
|
||||
NTreeLocator::<N>::from_depth_coords(x, y, z, depth as usize),
|
||||
NTreeNode::<N> {
|
||||
color: chunk[x + y * chunk_width + z * chunk_width * chunk_width],
|
||||
subdivided: false,
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
println!("Starting bottom up");
|
||||
let mut current_size = 1;
|
||||
for d in (0..depth).rev()
|
||||
{
|
||||
println!("depth: {d}");
|
||||
current_size *= N;
|
||||
for ((x, y), z) in (0..(chunk_width / current_size))
|
||||
.cartesian_product(0..(chunk_width / current_size))
|
||||
.cartesian_product(0..(chunk_width / current_size))
|
||||
{
|
||||
let loc = NTreeLocator::<N>::from_depth_coords(x, y, z, d as usize);
|
||||
let children = (0..N)
|
||||
.cartesian_product(0..N)
|
||||
.cartesian_product(0..N)
|
||||
.map(|((cx, cy), cz)| structure.get(&loc.get_child(cx, cy, cz)).unwrap())
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
let (can_merge, _) = children.iter().fold((true, None), |acc, child| match acc
|
||||
{
|
||||
(_, None) => (true, Some(child.color)),
|
||||
(b, Some(color)) =>
|
||||
{
|
||||
(b && !child.subdivided && color == child.color, Some(color))
|
||||
}
|
||||
});
|
||||
|
||||
let node;
|
||||
if can_merge
|
||||
{
|
||||
node = NTreeNode::<N> {
|
||||
color: children[0].color,
|
||||
subdivided: false,
|
||||
};
|
||||
|
||||
//Remove merged childrenn
|
||||
(0..N)
|
||||
.cartesian_product(0..N)
|
||||
.cartesian_product(0..N)
|
||||
.for_each(|((cx, cy), cz)| {
|
||||
structure.remove(&loc.get_child(cx, cy, cz)).unwrap();
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
node = NTreeNode::<N> {
|
||||
color: Color::average(
|
||||
children
|
||||
.iter()
|
||||
.map(|x| x.color)
|
||||
.collect::<Vec<_>>()
|
||||
.as_slice(),
|
||||
),
|
||||
subdivided: true,
|
||||
};
|
||||
}
|
||||
structure.insert(loc, node);
|
||||
}
|
||||
}
|
||||
|
||||
Self { structure, depth }
|
||||
}
|
||||
|
||||
pub fn from_arrays(chunk: &[Color], depth: usize) -> Self
|
||||
{
|
||||
let width = N.pow(depth as u32);
|
||||
let mut structure = HashMap::new();
|
||||
let mut taken = vec![0; chunk.len()]; // Whether or not the current voxel has been added
|
||||
|
||||
// Voxel insertion/combination pass
|
||||
for (y, z) in (0..width).cartesian_product(0..width)
|
||||
{
|
||||
let mut x = 0;
|
||||
while x < width
|
||||
{
|
||||
if taken[x + y * width + z * width * width] != 0
|
||||
{
|
||||
x += taken[x + y * width + z * width * width];
|
||||
continue;
|
||||
}
|
||||
|
||||
let max_combination_depth = trailing_zeroes::<N>(x)
|
||||
.unwrap_or(depth)
|
||||
.min(trailing_zeroes::<N>(y).unwrap_or(depth))
|
||||
.min(trailing_zeroes::<N>(z).unwrap_or(depth));
|
||||
|
||||
let prev_color = chunk[x + y * width + z * width * width];
|
||||
let mut locator = NTreeLocator::<N>::from_depth_coords(x, y, z, depth);
|
||||
//let mut insertion_depth = depth;
|
||||
let mut block_width = 1;
|
||||
'depth_loop: for depth in 1..=max_combination_depth
|
||||
{
|
||||
let combination_width = N.pow(depth as u32);
|
||||
for ((sx, sy), sz) in (0..combination_width)
|
||||
.cartesian_product(0..combination_width)
|
||||
.cartesian_product(0..combination_width)
|
||||
{
|
||||
let voxel_color =
|
||||
chunk[(x + sx) + (y + sy) * width + (z + sz) * width * width];
|
||||
let voxel_taken =
|
||||
taken[(x + sx) + (y + sy) * width + (z + sz) * width * width];
|
||||
if prev_color != voxel_color || voxel_taken != 0
|
||||
{
|
||||
// Cannot merge further
|
||||
break 'depth_loop;
|
||||
}
|
||||
}
|
||||
|
||||
// At this point, voxel in combination_width^3 block can be merged
|
||||
block_width = combination_width;
|
||||
//insertion_depth -= 1;
|
||||
locator = locator.get_parent();
|
||||
}
|
||||
|
||||
structure.insert(
|
||||
locator,
|
||||
NTreeNode {
|
||||
color: prev_color,
|
||||
subdivided: false,
|
||||
},
|
||||
);
|
||||
|
||||
// Mark as taken
|
||||
for ((sx, sy), sz) in (0..block_width)
|
||||
.cartesian_product(0..block_width)
|
||||
.cartesian_product(0..block_width)
|
||||
{
|
||||
taken[(x + sx) + (y + sy) * width + (z + sz) * width * width] = block_width;
|
||||
}
|
||||
|
||||
x += block_width;
|
||||
}
|
||||
}
|
||||
|
||||
// Increase pass
|
||||
for d in (0..=(depth - 1)).rev()
|
||||
{
|
||||
// Iterate on blocks of depth d
|
||||
let block_count = N.pow(d as u32); // Number of such blocks along axis
|
||||
let block_width = width / block_count; // Number of such blocks along axis
|
||||
for ((bx, by), bz) in (0..(block_count))
|
||||
.cartesian_product(0..(block_count))
|
||||
.cartesian_product(0..(block_count))
|
||||
{
|
||||
// Get how was the origin voxel merged
|
||||
let merged_width = taken[(bx * block_width)
|
||||
+ (by * block_width) * width
|
||||
+ (bz * block_width) * width * width];
|
||||
|
||||
if merged_width >= block_width
|
||||
{
|
||||
// Current voxels have been merged in bigger or equal block
|
||||
continue;
|
||||
}
|
||||
|
||||
let locator = NTreeLocator::<N>::from_depth_coords(bx, by, bz, d);
|
||||
|
||||
// Otherwise, merge
|
||||
|
||||
// Children CANNOT be merged, Otherwise they would have been already
|
||||
// Compute average color
|
||||
|
||||
let colors = locator
|
||||
.iter_children()
|
||||
.map(|loc| structure.get(&loc).unwrap().color)
|
||||
.collect::<Vec<_>>();
|
||||
structure.insert(
|
||||
locator,
|
||||
NTreeNode::<N> {
|
||||
color: Color::average(&colors),
|
||||
subdivided: true,
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Self {
|
||||
structure,
|
||||
depth: depth as u32,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set(&mut self, x: usize, y: usize, z: usize, color: Color)
|
||||
{
|
||||
let mut local_x = x;
|
||||
@ -323,38 +526,51 @@ impl Color
|
||||
Color(r_a + r_b, g_a + g_b, b_a + b_b, a_a + a_b)
|
||||
})
|
||||
.unwrap_or(Color(0., 0., 0., 0.));
|
||||
let len = colors.len().max(1) as f32;
|
||||
//let len = colors.len().max(1) as f32;
|
||||
let len = colors.iter().map(|x| x.3).sum::<f32>();
|
||||
|
||||
Color(sum.0 / len, sum.1 / len, sum.2 / len, sum.3 / len)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Hash, PartialEq, Eq)]
|
||||
pub struct NTreeLocator<const N: usize>(usize);
|
||||
#[derive(Clone, Copy, Hash, PartialEq, Eq, Debug)]
|
||||
pub struct NTreeLocator<const N: usize>(usize, usize, usize);
|
||||
|
||||
impl<const N: usize> NTreeLocator<N>
|
||||
{
|
||||
pub fn root() -> Self
|
||||
{
|
||||
Self(1)
|
||||
Self(1, 1, 1)
|
||||
}
|
||||
|
||||
pub fn get_child(&self, child_x: usize, child_y: usize, child_z: usize) -> Self
|
||||
{
|
||||
assert!(child_x < N && child_y < N && child_z < N);
|
||||
let mut new_loc = self.0;
|
||||
let mut new_loc_x = self.0;
|
||||
let mut new_loc_y = self.1;
|
||||
let mut new_loc_z = self.2;
|
||||
|
||||
// Shift to left three times
|
||||
new_loc *= N;
|
||||
new_loc += child_x;
|
||||
new_loc_x *= N;
|
||||
new_loc_x += child_x;
|
||||
|
||||
new_loc *= N;
|
||||
new_loc += child_y;
|
||||
new_loc_y *= N;
|
||||
new_loc_y += child_y;
|
||||
|
||||
new_loc *= N;
|
||||
new_loc += child_z;
|
||||
new_loc_z *= N;
|
||||
new_loc_z += child_z;
|
||||
|
||||
Self(new_loc)
|
||||
Self(new_loc_x, new_loc_y, new_loc_z)
|
||||
}
|
||||
|
||||
pub fn from_depth_coords(x: usize, y: usize, z: usize, depth: usize) -> Self
|
||||
{
|
||||
if depth == 0
|
||||
{
|
||||
return Self::root();
|
||||
}
|
||||
let off = N.pow(depth as u32);
|
||||
Self(off + x, off + y, off + z)
|
||||
}
|
||||
|
||||
pub fn iter_children(&self) -> impl Iterator<Item = NTreeLocator<N>>
|
||||
@ -373,7 +589,7 @@ impl<const N: usize> NTreeLocator<N>
|
||||
}
|
||||
else
|
||||
{
|
||||
Self(self.0 / (N * N * N))
|
||||
Self(self.0 / N, self.1 / N, self.2 / N)
|
||||
}
|
||||
}
|
||||
|
||||
@ -397,15 +613,33 @@ impl<const N: usize> NTreeLocator<N>
|
||||
}
|
||||
}
|
||||
|
||||
fn trailing_zeroes<const N: usize>(mut n: usize) -> Option<usize>
|
||||
{
|
||||
if n == 0
|
||||
{
|
||||
return None;
|
||||
}
|
||||
let mut i = 0;
|
||||
while n.is_multiple_of(N)
|
||||
// n % N == 0
|
||||
{
|
||||
i += 1;
|
||||
n /= N;
|
||||
}
|
||||
Some(i)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test
|
||||
{
|
||||
|
||||
use crate::voxel::trailing_zeroes;
|
||||
use itertools::Itertools;
|
||||
use rand::Rng;
|
||||
|
||||
use crate::voxel::Color;
|
||||
use crate::voxel::NTree;
|
||||
use crate::voxel::NTreeLocator;
|
||||
|
||||
#[test]
|
||||
pub fn constant()
|
||||
@ -453,6 +687,34 @@ mod test
|
||||
println!("Total nodes {}", ntree.structure.len());
|
||||
}
|
||||
|
||||
#[test]
|
||||
pub fn full_insert_bottom_up()
|
||||
{
|
||||
const DEPTH: u32 = 4;
|
||||
const N: usize = 3;
|
||||
const WIDTH: usize = N.pow(DEPTH);
|
||||
|
||||
let mut rng = rand::rng();
|
||||
let mut storage = vec![Color(0., 0., 0., 0.); WIDTH * WIDTH * WIDTH];
|
||||
for ((x, y), z) in (0..WIDTH)
|
||||
.cartesian_product(0..WIDTH)
|
||||
.cartesian_product(0..WIDTH)
|
||||
{
|
||||
let color = Color(rng.random(), rng.random(), rng.random(), rng.random());
|
||||
storage[x + WIDTH * y + WIDTH * WIDTH * z] = color;
|
||||
}
|
||||
let ntree = NTree::<N>::from_arrays(&storage, DEPTH as usize);
|
||||
|
||||
for ((x, y), z) in (0..WIDTH)
|
||||
.cartesian_product(0..WIDTH)
|
||||
.cartesian_product(0..WIDTH)
|
||||
{
|
||||
let color = ntree.get(x, y, z);
|
||||
assert_eq!(storage[x + WIDTH * y + WIDTH * WIDTH * z], color);
|
||||
}
|
||||
println!("Total nodes {}", ntree.structure.len());
|
||||
}
|
||||
|
||||
#[test]
|
||||
pub fn full_insert_const()
|
||||
{
|
||||
@ -545,4 +807,34 @@ mod test
|
||||
drop(_a);
|
||||
drop(_b);
|
||||
}
|
||||
|
||||
#[test]
|
||||
pub fn tree_locator_from_depth_coords()
|
||||
{
|
||||
const N: usize = 4;
|
||||
assert_eq!(
|
||||
NTreeLocator::<N>::root(),
|
||||
NTreeLocator::<N>::from_depth_coords(1, 2, 3, 0)
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
NTreeLocator::<N>::root().get_child(1, 2, 3),
|
||||
NTreeLocator::<N>::from_depth_coords(1, 2, 3, 1)
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
NTreeLocator::<N>::root()
|
||||
.get_child(1, 1, 1)
|
||||
.get_child(1, 2, 3),
|
||||
NTreeLocator::<N>::from_depth_coords(4 + 1, 4 + 2, 4 + 3, 2)
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn trailing_zeroes_base10()
|
||||
{
|
||||
let n = 2139800000;
|
||||
assert_eq!(trailing_zeroes::<10>(n), Some(5));
|
||||
assert_eq!(trailing_zeroes::<10>(0), None);
|
||||
}
|
||||
}
|
||||
|
||||
153
src/voxel_renderer.rs
Normal file
153
src/voxel_renderer.rs
Normal file
@ -0,0 +1,153 @@
|
||||
use cgmath::EuclideanSpace;
|
||||
use cgmath::SquareMatrix;
|
||||
use crevice::std430::AsStd430;
|
||||
use wgpu::Buffer;
|
||||
use wgpu::BufferDescriptor;
|
||||
use wgpu::BufferUsages;
|
||||
use wgpu::Device;
|
||||
use wgpu::PushConstantRange;
|
||||
use wgpu::RenderPass;
|
||||
use wgpu::RenderPipeline;
|
||||
use wgpu::ShaderStages;
|
||||
use wgpu::TextureFormat;
|
||||
use wgpu::VertexAttribute;
|
||||
use wgpu::VertexBufferLayout;
|
||||
use wgpu::VertexFormat;
|
||||
use wgpu::include_wgsl;
|
||||
use wgpu::util::BufferInitDescriptor;
|
||||
use wgpu::util::DeviceExt;
|
||||
|
||||
use crate::state::Camera;
|
||||
|
||||
pub struct VoxelRenderer
|
||||
{
|
||||
chunk_instances_capacity: u32,
|
||||
chunk_instances: Buffer,
|
||||
chunk_pipeline: RenderPipeline,
|
||||
}
|
||||
|
||||
impl VoxelRenderer
|
||||
{
|
||||
pub fn new(device: &Device, surface_format: TextureFormat) -> Self
|
||||
{
|
||||
let chunk_shader_module =
|
||||
device.create_shader_module(include_wgsl!("../shaders/chunk.wgsl"));
|
||||
let chunk_pipeline_layout =
|
||||
device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
|
||||
label: Some("Render3D Mesh Pipeline Layout"),
|
||||
bind_group_layouts: &[],
|
||||
push_constant_ranges: &[PushConstantRange {
|
||||
stages: ShaderStages::VERTEX,
|
||||
range: 0..ChunkPushConstants::std430_size_static() as u32,
|
||||
}],
|
||||
});
|
||||
|
||||
let chunk_pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor {
|
||||
label: Some("chunk_pipeline"),
|
||||
layout: Some(&chunk_pipeline_layout),
|
||||
vertex: wgpu::VertexState {
|
||||
module: &chunk_shader_module,
|
||||
entry_point: Some("vertex_main"),
|
||||
buffers: &[VertexBufferLayout {
|
||||
array_stride: size_of::<i32>() as u64 * 3,
|
||||
step_mode: wgpu::VertexStepMode::Instance,
|
||||
attributes: &[VertexAttribute {
|
||||
format: VertexFormat::Sint32x3,
|
||||
offset: 0,
|
||||
shader_location: 0,
|
||||
}],
|
||||
}],
|
||||
compilation_options: wgpu::PipelineCompilationOptions::default(),
|
||||
},
|
||||
fragment: Some(wgpu::FragmentState {
|
||||
module: &chunk_shader_module,
|
||||
entry_point: Some("fragment_main"),
|
||||
targets: &[Some(wgpu::ColorTargetState {
|
||||
format: surface_format,
|
||||
blend: None,
|
||||
write_mask: wgpu::ColorWrites::ALL,
|
||||
})],
|
||||
compilation_options: wgpu::PipelineCompilationOptions::default(),
|
||||
}),
|
||||
primitive: wgpu::PrimitiveState {
|
||||
topology: wgpu::PrimitiveTopology::TriangleList,
|
||||
..Default::default()
|
||||
},
|
||||
depth_stencil: Some(wgpu::DepthStencilState {
|
||||
format: TextureFormat::Depth24Plus,
|
||||
depth_write_enabled: true,
|
||||
depth_compare: wgpu::CompareFunction::Less,
|
||||
stencil: wgpu::StencilState::default(),
|
||||
bias: wgpu::DepthBiasState::default(),
|
||||
}),
|
||||
multisample: wgpu::MultisampleState {
|
||||
count: 1,
|
||||
mask: !0,
|
||||
alpha_to_coverage_enabled: false,
|
||||
},
|
||||
multiview: None,
|
||||
|
||||
cache: None,
|
||||
});
|
||||
|
||||
Self {
|
||||
chunk_instances_capacity: 1,
|
||||
chunk_instances: device.create_buffer(&BufferDescriptor {
|
||||
label: Some("chunk_instances_buffer"),
|
||||
size: size_of::<ChunkInstance>() as u64,
|
||||
usage: BufferUsages::VERTEX,
|
||||
mapped_at_creation: false,
|
||||
}),
|
||||
|
||||
chunk_pipeline,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_chunks(&mut self, device: &Device, chunks: &[cgmath::Vector3<i32>])
|
||||
{
|
||||
self.chunk_instances = device.create_buffer_init(&BufferInitDescriptor {
|
||||
label: Some("chunk_instances_buffer"),
|
||||
contents: unsafe {
|
||||
std::slice::from_raw_parts(
|
||||
chunks.as_ptr() as *const u8,
|
||||
std::mem::size_of_val(chunks),
|
||||
)
|
||||
},
|
||||
usage: BufferUsages::VERTEX,
|
||||
});
|
||||
self.chunk_instances_capacity = chunks.len() as u32;
|
||||
}
|
||||
|
||||
pub fn render(&mut self, render_pass: &mut RenderPass, camera: &Camera)
|
||||
{
|
||||
render_pass.set_pipeline(&self.chunk_pipeline);
|
||||
|
||||
//renderpass.set_vertex_buffer(0, self.positions_buffer.slice(..));
|
||||
render_pass.set_push_constants(
|
||||
ShaderStages::VERTEX,
|
||||
0,
|
||||
ChunkPushConstants {
|
||||
view_projection: camera.view_proj(),
|
||||
transform: cgmath::Matrix4::identity(),
|
||||
eye_position: camera.eye.to_vec(),
|
||||
}
|
||||
.as_std430()
|
||||
.as_bytes(),
|
||||
);
|
||||
render_pass.set_vertex_buffer(0, self.chunk_instances.slice(..));
|
||||
render_pass.draw(0..(6 * 2 * 3), 0..(self.chunk_instances_capacity));
|
||||
}
|
||||
}
|
||||
|
||||
struct ChunkInstance
|
||||
{
|
||||
location: cgmath::Vector3<i32>,
|
||||
}
|
||||
|
||||
#[derive(crevice::std430::AsStd430)]
|
||||
pub struct ChunkPushConstants
|
||||
{
|
||||
view_projection: cgmath::Matrix4<f32>,
|
||||
transform: cgmath::Matrix4<f32>,
|
||||
eye_position: cgmath::Vector3<f32>,
|
||||
}
|
||||
Reference in New Issue
Block a user