From 9716656841413e866cb85112a852769b42068374 Mon Sep 17 00:00:00 2001 From: Robert Swain Date: Sun, 2 May 2021 16:14:49 +0200 Subject: [PATCH 01/10] Add merger that supports ambient occlusion evaluation --- .../building_blocks_mesh/src/greedy_quads.rs | 188 +++++++++++++++++- 1 file changed, 187 insertions(+), 1 deletion(-) diff --git a/crates/building_blocks_mesh/src/greedy_quads.rs b/crates/building_blocks_mesh/src/greedy_quads.rs index bb476d72..31219389 100644 --- a/crates/building_blocks_mesh/src/greedy_quads.rs +++ b/crates/building_blocks_mesh/src/greedy_quads.rs @@ -459,5 +459,191 @@ impl VoxelMerger { quad_width } } +pub struct VoxelAOMerger { + marker: std::marker::PhantomData, +} + +impl MergeStrategy for VoxelAOMerger +where + T: MergeVoxel + IsEmpty + IsOpaque, +{ + type Voxel = T; + + fn find_quad( + min_stride: Stride, + min_value: &T, + mut max_width: i32, + max_height: i32, + face_strides: &FaceStrides, + voxels: &A, + visited: &Array3x1, + ) -> (i32, i32) + where + A: Get, + { + // Greedily search for the biggest visible quad where all merge values are the same. + let quad_value = min_value.voxel_merge_value(); + + // Calculate minimum U edge AO values + let vaos = [ + get_ao_at_vert(voxels, face_strides, min_stride), + get_ao_at_vert(voxels, face_strides, min_stride + face_strides.v_stride), + ]; + + // Start by finding the widest quad in the U direction. + let mut row_start_stride = min_stride; + let quad_width = Self::get_row_width( + voxels, + visited, + &quad_value, + &vaos, + face_strides.visibility_offset, + row_start_stride, + face_strides.u_stride, + face_strides, + max_width, + ); + + max_width = max_width.min(quad_width); + let mut quad_height = 1; -// TODO: implement a MergeStrategy for voxels with an ambient occlusion value at each vertex + // If the 0th row minimum U edge ambient occlusion values match, we can merge in V + if vaos[0] == vaos[1] { + // Now see how tall we can make the quad in the V direction without changing the width. + row_start_stride += face_strides.v_stride; + while quad_height < max_height { + // Check if minimum U edge AO values match, if so we can merge vertically, otherwise we're done + let row_vaos = [ + get_ao_at_vert(voxels, face_strides, row_start_stride), + get_ao_at_vert( + voxels, + face_strides, + row_start_stride + face_strides.v_stride, + ), + ]; + if row_vaos[0] != row_vaos[1] { + break; + } + let row_width = Self::get_row_width( + voxels, + visited, + &quad_value, + &row_vaos, + face_strides.visibility_offset, + row_start_stride, + face_strides.u_stride, + face_strides, + max_width, + ); + if row_width < quad_width { + break; + } + quad_height += 1; + row_start_stride += face_strides.v_stride; + } + } + + (quad_width, quad_height) + } +} + +impl VoxelAOMerger { + fn get_row_width( + voxels: &A, + visited: &Array3x1, + quad_merge_voxel_value: &T::VoxelValue, + vaos: &[i32], + visibility_offset: Stride, + start_stride: Stride, + delta_stride: Stride, + face_strides: &FaceStrides, + max_width: i32, + ) -> i32 + where + A: Get, + T: IsEmpty + IsOpaque + MergeVoxel, + { + let mut quad_width = 0; + let mut row_stride = start_stride; + while quad_width < max_width { + if visited.get(row_stride) { + // Already have a quad for this voxel face. + break; + } + + let voxel = voxels.get(row_stride); + + if !face_needs_mesh(&voxel, row_stride, visibility_offset, voxels, visited) { + break; + } + + if !voxel.voxel_merge_value().eq(quad_merge_voxel_value) { + // Voxel needs to be non-empty and match the quad merge value. + break; + } + + quad_width += 1; + + let next_vaos = [ + get_ao_at_vert(voxels, face_strides, row_stride + face_strides.u_stride), + get_ao_at_vert( + voxels, + face_strides, + row_stride + face_strides.u_stride + face_strides.v_stride, + ), + ]; + if vaos[0] != next_vaos[0] || vaos[1] != next_vaos[1] { + // Ambient occlusion values must be constant across the row + // They are not so we cannot proceed to the next quad + break; + } + + row_stride += delta_stride; + } + + quad_width + } +} + +fn get_ao_at_vert(voxels: &A, face_strides: &FaceStrides, stride: Stride) -> i32 +where + A: Get, + T: IsEmpty + IsOpaque + MergeVoxel, +{ + let top0_loc = stride - face_strides.u_stride; + let top1_loc = stride - face_strides.n_stride; + let top2_loc = stride; + let top3_loc = stride - face_strides.n_stride - face_strides.u_stride; + + let bot0_loc = stride - face_strides.u_stride - face_strides.v_stride; + let bot1_loc = stride - face_strides.n_stride - face_strides.v_stride; + let bot2_loc = stride - face_strides.v_stride; + let bot3_loc = stride - face_strides.n_stride - face_strides.u_stride - face_strides.v_stride; + + let top0 = !voxels.get(top0_loc).is_empty(); + let top1 = !voxels.get(top1_loc).is_empty(); + let top2 = !voxels.get(top2_loc).is_empty(); + let top3 = !voxels.get(top3_loc).is_empty(); + let bot0 = !voxels.get(bot0_loc).is_empty(); + let bot1 = !voxels.get(bot1_loc).is_empty(); + let bot2 = !voxels.get(bot2_loc).is_empty(); + let bot3 = !voxels.get(bot3_loc).is_empty(); + + let (side0, side1, corner) = if !top0 && bot0 { + (top2, top3, top1) + } else if !top1 && bot1 { + (top2, top3, top0) + } else if !top2 && bot2 { + (top0, top1, top3) + } else if !top3 && bot3 { + (top0, top1, top2) + } else { + return 0; + }; + + if side0 && side1 { + return 3; + } else { + return side0 as i32 + side1 as i32 + corner as i32; + } +} From d68d4d456637f5a12194f92ae565bda2b2541f1b Mon Sep 17 00:00:00 2001 From: Robert Swain Date: Sun, 2 May 2021 16:57:49 +0200 Subject: [PATCH 02/10] Add support for swapping the quad diagonal This is due to the anisotropic interpolation between vertices if all diagonals are oriented the same way, as noted at the end of this article: https://0fps.net/2013/07/03/ambient-occlusion-for-minecraft-like-worlds/ --- crates/building_blocks_mesh/src/quad.rs | 25 +++++++++++++++++-------- 1 file changed, 17 insertions(+), 8 deletions(-) diff --git a/crates/building_blocks_mesh/src/quad.rs b/crates/building_blocks_mesh/src/quad.rs index eb379e6b..0dc06838 100644 --- a/crates/building_blocks_mesh/src/quad.rs +++ b/crates/building_blocks_mesh/src/quad.rs @@ -110,9 +110,10 @@ impl OrientedCubeFace { } /// Returns the 6 vertex indices for the quad in order to make two triangles in a mesh. Winding order depends on both the - /// sign of the surface normal and the permutation of the UVs. - pub fn quad_mesh_indices(&self, start: u32) -> [u32; 6] { - quad_indices(start, self.n_sign * self.permutation.sign() > 0) + /// sign of the surface normal and the permutation of the UVs. Swapping the diagonal can be used for altering the triangle + /// orientation to achieve more isotropic interpolation of vertex values such as ambient occlusion. + pub fn quad_mesh_indices(&self, start: u32, swap_diagonal: bool) -> [u32; 6] { + quad_indices(start, self.n_sign * self.permutation.sign() > 0, swap_diagonal) } /// Returns the UV coordinates of the 4 corners of the quad. Returns vertices in the same order as @@ -178,7 +179,7 @@ impl OrientedCubeFace { .extend_from_slice(&self.quad_mesh_positions(quad, voxel_size)); mesh.normals.extend_from_slice(&self.quad_mesh_normals()); mesh.indices - .extend_from_slice(&self.quad_mesh_indices(start_index)); + .extend_from_slice(&self.quad_mesh_indices(start_index, false)); } /// Extends `mesh` with the given `quad` that belongs to this face. @@ -199,17 +200,25 @@ impl OrientedCubeFace { mesh.tex_coords .extend_from_slice(&self.tex_coords(u_flip_face, flip_v, quad)); mesh.indices - .extend_from_slice(&self.quad_mesh_indices(start_index)); + .extend_from_slice(&self.quad_mesh_indices(start_index, false)); } } /// Returns the vertex indices for a single quad (two triangles). The triangles may have either clockwise or counter-clockwise /// winding. `start` is the first index. -fn quad_indices(start: u32, counter_clockwise: bool) -> [u32; 6] { +fn quad_indices(start: u32, counter_clockwise: bool, swap_diagonal: bool) -> [u32; 6] { if counter_clockwise { - [start, start + 1, start + 2, start + 1, start + 3, start + 2] + if swap_diagonal { + [start, start + 1, start + 2, start + 1, start + 3, start + 2] + } else { + [start, start + 3, start + 2, start, start + 1, start + 3] + } } else { - [start, start + 2, start + 1, start + 1, start + 2, start + 3] + if swap_diagonal { + [start, start + 2, start + 3, start + 1, start, start + 3] + } else { + [start, start + 2, start + 1, start + 1, start + 2, start + 3] + } } } From c1215f3f10d18ee26ba006b4612b4353d509e92d Mon Sep 17 00:00:00 2001 From: Robert Swain Date: Mon, 3 May 2021 20:05:49 +0200 Subject: [PATCH 03/10] Fix logic --- crates/building_blocks_mesh/src/greedy_quads.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/crates/building_blocks_mesh/src/greedy_quads.rs b/crates/building_blocks_mesh/src/greedy_quads.rs index 31219389..feb40ba6 100644 --- a/crates/building_blocks_mesh/src/greedy_quads.rs +++ b/crates/building_blocks_mesh/src/greedy_quads.rs @@ -521,7 +521,8 @@ where row_start_stride + face_strides.v_stride, ), ]; - if row_vaos[0] != row_vaos[1] { + if vaos[0] != row_vaos[0] || vaos[1] != row_vaos[1] { + // AO values not constant along V break; } let row_width = Self::get_row_width( From 2fa8b39ea9c2c2b9e92c2fcae59adc591708db1d Mon Sep 17 00:00:00 2001 From: Robert Swain Date: Mon, 3 May 2021 20:10:23 +0200 Subject: [PATCH 04/10] Match values returned by Mikola Lysenko's function in the article --- crates/building_blocks_mesh/src/greedy_quads.rs | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/crates/building_blocks_mesh/src/greedy_quads.rs b/crates/building_blocks_mesh/src/greedy_quads.rs index feb40ba6..a1375550 100644 --- a/crates/building_blocks_mesh/src/greedy_quads.rs +++ b/crates/building_blocks_mesh/src/greedy_quads.rs @@ -630,7 +630,7 @@ where let bot2 = !voxels.get(bot2_loc).is_empty(); let bot3 = !voxels.get(bot3_loc).is_empty(); - let (side0, side1, corner) = if !top0 && bot0 { + let (side1, side2, corner) = if !top0 && bot0 { (top2, top3, top1) } else if !top1 && bot1 { (top2, top3, top0) @@ -639,7 +639,14 @@ where } else if !top3 && bot3 { (top0, top1, top2) } else { + return 3; + }; + + if side1 && side2 { return 0; + } + return 3 - (side1 as i32 + side2 as i32 + corner as i32); +} }; if side0 && side1 { From 747316f021b8ef1bed058c78da5db0858a673c46 Mon Sep 17 00:00:00 2001 From: Robert Swain Date: Mon, 3 May 2021 21:13:11 +0200 Subject: [PATCH 05/10] Add ambient occlusion example --- Cargo.toml | 6 + .../ambient_occlusion.frag | 423 ++++++++++++++++ .../ambient_occlusion.vert | 68 +++ .../blocky_ambient_occlusion.rs | 468 ++++++++++++++++++ 4 files changed, 965 insertions(+) create mode 100644 examples/blocky_ambient_occlusion/ambient_occlusion.frag create mode 100644 examples/blocky_ambient_occlusion/ambient_occlusion.vert create mode 100644 examples/blocky_ambient_occlusion/blocky_ambient_occlusion.rs diff --git a/Cargo.toml b/Cargo.toml index e8bf7cc2..8d3ad698 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -62,6 +62,7 @@ building_blocks_mesh = { path = "crates/building_blocks_mesh", version = "0.6.0" building_blocks_search = { path = "crates/building_blocks_search", version = "0.6.0", default-features = false, optional = true } [dev-dependencies] +bevy_fly_camera = "0.7" simdnoise = "3.1" # Common code for tests and examples. @@ -104,3 +105,8 @@ required-features = ["mesh"] name = "lod_terrain" path = "examples/lod_terrain/lod_terrain.rs" required-features = ["glam", "mesh"] + +[[example]] +name = "blocky_ambient_occlusion" +path = "examples/blocky_ambient_occlusion/blocky_ambient_occlusion.rs" +required-features = ["mesh"] diff --git a/examples/blocky_ambient_occlusion/ambient_occlusion.frag b/examples/blocky_ambient_occlusion/ambient_occlusion.frag new file mode 100644 index 00000000..12c0f7d6 --- /dev/null +++ b/examples/blocky_ambient_occlusion/ambient_occlusion.frag @@ -0,0 +1,423 @@ +// MIT License + +// Copyright (c) 2020 Carter Anderson +// Copyright (c) 2021 Robert Swain + +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: + +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. + +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +// Default bevy PBR shaders with added vertex attribute for texture layer +// and using an array texture for the base colour +// +// NOTE: These are from bevy v0.5.0 exactly and must be updated when bevy is +// updated! + +// From the Filament design doc +// https://google.github.io/filament/Filament.html#table_symbols +// Symbol Definition +// v View unit vector +// l Incident light unit vector +// n Surface normal unit vector +// h Half unit vector between l and v +// f BRDF +// f_d Diffuse component of a BRDF +// f_r Specular component of a BRDF +// α Roughness, remapped from using input perceptualRoughness +// σ Diffuse reflectance +// Ω Spherical domain +// f0 Reflectance at normal incidence +// f90 Reflectance at grazing angle +// χ+(a) Heaviside function (1 if a>0 and 0 otherwise) +// nior Index of refraction (IOR) of an interface +// ⟨n⋅l⟩ Dot product clamped to [0..1] +// ⟨a⟩ Saturated value (clamped to [0..1]) + +// The Bidirectional Reflectance Distribution Function (BRDF) describes the surface response of a standard material +// and consists of two components, the diffuse component (f_d) and the specular component (f_r): +// f(v,l) = f_d(v,l) + f_r(v,l) +// +// The form of the microfacet model is the same for diffuse and specular +// f_r(v,l) = f_d(v,l) = 1 / { |n⋅v||n⋅l| } ∫_Ω D(m,α) G(v,l,m) f_m(v,l,m) (v⋅m) (l⋅m) dm +// +// In which: +// D, also called the Normal Distribution Function (NDF) models the distribution of the microfacets +// G models the visibility (or occlusion or shadow-masking) of the microfacets +// f_m is the microfacet BRDF and differs between specular and diffuse components +// +// The above integration needs to be approximated. + +#version 450 + +const int MAX_LIGHTS = 10; + +struct Light { + mat4 proj; + vec4 pos; + vec4 color; +}; + +layout(location = 0) in vec3 v_WorldPosition; +layout(location = 1) in vec3 v_WorldNormal; +layout(location = 2) in vec2 v_Uv; + +#ifdef STANDARDMATERIAL_NORMAL_MAP +layout(location = 3) in vec4 v_WorldTangent; +#endif + +layout(location = 4) in float v_AO; + +layout(location = 0) out vec4 o_Target; + +layout(set = 0, binding = 0) uniform CameraViewProj { + mat4 ViewProj; +}; +layout(std140, set = 0, binding = 1) uniform CameraPosition { + vec4 CameraPos; +}; + +layout(std140, set = 1, binding = 0) uniform Lights { + vec4 AmbientColor; + uvec4 NumLights; + Light SceneLights[MAX_LIGHTS]; +}; + +layout(set = 3, binding = 0) uniform StandardMaterial_base_color { + vec4 base_color; +}; + +#ifdef STANDARDMATERIAL_BASE_COLOR_TEXTURE +layout(set = 3, binding = 1) uniform texture2D StandardMaterial_base_color_texture; +layout(set = 3, + binding = 2) uniform sampler StandardMaterial_base_color_texture_sampler; +#endif + +#ifndef STANDARDMATERIAL_UNLIT + +layout(set = 3, binding = 3) uniform StandardMaterial_roughness { + float perceptual_roughness; +}; + +layout(set = 3, binding = 4) uniform StandardMaterial_metallic { + float metallic; +}; + +# ifdef STANDARDMATERIAL_METALLIC_ROUGHNESS_TEXTURE +layout(set = 3, binding = 5) uniform texture2D StandardMaterial_metallic_roughness_texture; +layout(set = 3, + binding = 6) uniform sampler StandardMaterial_metallic_roughness_texture_sampler; +# endif + +layout(set = 3, binding = 7) uniform StandardMaterial_reflectance { + float reflectance; +}; + +# ifdef STANDARDMATERIAL_NORMAL_MAP +layout(set = 3, binding = 8) uniform texture2D StandardMaterial_normal_map; +layout(set = 3, + binding = 9) uniform sampler StandardMaterial_normal_map_sampler; +# endif + +# if defined(STANDARDMATERIAL_OCCLUSION_TEXTURE) +layout(set = 3, binding = 10) uniform texture2D StandardMaterial_occlusion_texture; +layout(set = 3, + binding = 11) uniform sampler StandardMaterial_occlusion_texture_sampler; +# endif + +layout(set = 3, binding = 12) uniform StandardMaterial_emissive { + vec4 emissive; +}; + +# if defined(STANDARDMATERIAL_EMISSIVE_TEXTURE) +layout(set = 3, binding = 13) uniform texture2D StandardMaterial_emissive_texture; +layout(set = 3, + binding = 14) uniform sampler StandardMaterial_emissive_texture_sampler; +# endif + +# define saturate(x) clamp(x, 0.0, 1.0) +const float PI = 3.141592653589793; + +float pow5(float x) { + float x2 = x * x; + return x2 * x2 * x; +} + +// distanceAttenuation is simply the square falloff of light intensity +// combined with a smooth attenuation at the edge of the light radius +// +// light radius is a non-physical construct for efficiency purposes, +// because otherwise every light affects every fragment in the scene +float getDistanceAttenuation(const vec3 posToLight, float inverseRadiusSquared) { + float distanceSquare = dot(posToLight, posToLight); + float factor = distanceSquare * inverseRadiusSquared; + float smoothFactor = saturate(1.0 - factor * factor); + float attenuation = smoothFactor * smoothFactor; + return attenuation * 1.0 / max(distanceSquare, 1e-4); +} + +// Normal distribution function (specular D) +// Based on https://google.github.io/filament/Filament.html#citation-walter07 + +// D_GGX(h,α) = α^2 / { π ((n⋅h)^2 (α2−1) + 1)^2 } + +// Simple implementation, has precision problems when using fp16 instead of fp32 +// see https://google.github.io/filament/Filament.html#listing_speculardfp16 +float D_GGX(float roughness, float NoH, const vec3 h) { + float oneMinusNoHSquared = 1.0 - NoH * NoH; + float a = NoH * roughness; + float k = roughness / (oneMinusNoHSquared + a * a); + float d = k * k * (1.0 / PI); + return d; +} + +// Visibility function (Specular G) +// V(v,l,a) = G(v,l,α) / { 4 (n⋅v) (n⋅l) } +// such that f_r becomes +// f_r(v,l) = D(h,α) V(v,l,α) F(v,h,f0) +// where +// V(v,l,α) = 0.5 / { n⋅l sqrt((n⋅v)^2 (1−α2) + α2) + n⋅v sqrt((n⋅l)^2 (1−α2) + α2) } +// Note the two sqrt's, that may be slow on mobile, see https://google.github.io/filament/Filament.html#listing_approximatedspecularv +float V_SmithGGXCorrelated(float roughness, float NoV, float NoL) { + float a2 = roughness * roughness; + float lambdaV = NoL * sqrt((NoV - a2 * NoV) * NoV + a2); + float lambdaL = NoV * sqrt((NoL - a2 * NoL) * NoL + a2); + float v = 0.5 / (lambdaV + lambdaL); + return v; +} + +// Fresnel function +// see https://google.github.io/filament/Filament.html#citation-schlick94 +// F_Schlick(v,h,f_0,f_90) = f_0 + (f_90 − f_0) (1 − v⋅h)^5 +vec3 F_Schlick(const vec3 f0, float f90, float VoH) { + // not using mix to keep the vec3 and float versions identical + return f0 + (f90 - f0) * pow5(1.0 - VoH); +} + +float F_Schlick(float f0, float f90, float VoH) { + // not using mix to keep the vec3 and float versions identical + return f0 + (f90 - f0) * pow5(1.0 - VoH); +} + +vec3 fresnel(vec3 f0, float LoH) { + // f_90 suitable for ambient occlusion + // see https://google.github.io/filament/Filament.html#lighting/occlusion + float f90 = saturate(dot(f0, vec3(50.0 * 0.33))); + return F_Schlick(f0, f90, LoH); +} + +// Specular BRDF +// https://google.github.io/filament/Filament.html#materialsystem/specularbrdf + +// Cook-Torrance approximation of the microfacet model integration using Fresnel law F to model f_m +// f_r(v,l) = { D(h,α) G(v,l,α) F(v,h,f0) } / { 4 (n⋅v) (n⋅l) } +vec3 specular(vec3 f0, float roughness, const vec3 h, float NoV, float NoL, + float NoH, float LoH) { + float D = D_GGX(roughness, NoH, h); + float V = V_SmithGGXCorrelated(roughness, NoV, NoL); + vec3 F = fresnel(f0, LoH); + + return (D * V) * F; +} + +// Diffuse BRDF +// https://google.github.io/filament/Filament.html#materialsystem/diffusebrdf +// fd(v,l) = σ/π * 1 / { |n⋅v||n⋅l| } ∫Ω D(m,α) G(v,l,m) (v⋅m) (l⋅m) dm + +// simplest approximation +// float Fd_Lambert() { +// return 1.0 / PI; +// } +// +// vec3 Fd = diffuseColor * Fd_Lambert(); + +// Disney approximation +// See https://google.github.io/filament/Filament.html#citation-burley12 +// minimal quality difference +float Fd_Burley(float roughness, float NoV, float NoL, float LoH) { + float f90 = 0.5 + 2.0 * roughness * LoH * LoH; + float lightScatter = F_Schlick(1.0, f90, NoL); + float viewScatter = F_Schlick(1.0, f90, NoV); + return lightScatter * viewScatter * (1.0 / PI); +} + +// From https://www.unrealengine.com/en-US/blog/physically-based-shading-on-mobile +vec3 EnvBRDFApprox(vec3 f0, float perceptual_roughness, float NoV) { + const vec4 c0 = { -1, -0.0275, -0.572, 0.022 }; + const vec4 c1 = { 1, 0.0425, 1.04, -0.04 }; + vec4 r = perceptual_roughness * c0 + c1; + float a004 = min(r.x * r.x, exp2(-9.28 * NoV)) * r.x + r.y; + vec2 AB = vec2(-1.04, 1.04) * a004 + r.zw; + return f0 * AB.x + AB.y; +} + +float perceptualRoughnessToRoughness(float perceptualRoughness) { + // clamp perceptual roughness to prevent precision problems + // According to Filament design 0.089 is recommended for mobile + // Filament uses 0.045 for non-mobile + float clampedPerceptualRoughness = clamp(perceptualRoughness, 0.089, 1.0); + return clampedPerceptualRoughness * clampedPerceptualRoughness; +} + +// from https://64.github.io/tonemapping/ +// reinhard on RGB oversaturates colors +vec3 reinhard(vec3 color) { + return color / (1.0 + color); +} + +vec3 reinhard_extended(vec3 color, float max_white) { + vec3 numerator = color * (1.0f + (color / vec3(max_white * max_white))); + return numerator / (1.0 + color); +} + +// luminance coefficients from Rec. 709. +// https://en.wikipedia.org/wiki/Rec._709 +float luminance(vec3 v) { + return dot(v, vec3(0.2126, 0.7152, 0.0722)); +} + +vec3 change_luminance(vec3 c_in, float l_out) { + float l_in = luminance(c_in); + return c_in * (l_out / l_in); +} + +vec3 reinhard_luminance(vec3 color) { + float l_old = luminance(color); + float l_new = l_old / (1.0f + l_old); + return change_luminance(color, l_new); +} + +vec3 reinhard_extended_luminance(vec3 color, float max_white_l) { + float l_old = luminance(color); + float numerator = l_old * (1.0f + (l_old / (max_white_l * max_white_l))); + float l_new = numerator / (1.0f + l_old); + return change_luminance(color, l_new); +} + +#endif + +void main() { + vec4 output_color = base_color; +#ifdef STANDARDMATERIAL_BASE_COLOR_TEXTURE + output_color *= texture(sampler2D(StandardMaterial_base_color_texture, + StandardMaterial_base_color_texture_sampler), + v_Uv); +#endif + +#ifndef STANDARDMATERIAL_UNLIT + // calculate non-linear roughness from linear perceptualRoughness +# ifdef STANDARDMATERIAL_METALLIC_ROUGHNESS_TEXTURE + vec4 metallic_roughness = texture(sampler2D(StandardMaterial_metallic_roughness_texture, StandardMaterial_metallic_roughness_texture_sampler), v_Uv); + // Sampling from GLTF standard channels for now + float metallic = metallic * metallic_roughness.b; + float perceptual_roughness = perceptual_roughness * metallic_roughness.g; +# endif + + float roughness = perceptualRoughnessToRoughness(perceptual_roughness); + + vec3 N = normalize(v_WorldNormal); + +# ifdef STANDARDMATERIAL_NORMAL_MAP + vec3 T = normalize(v_WorldTangent.xyz); + vec3 B = cross(N, T) * v_WorldTangent.w; +# endif + +# ifdef STANDARDMATERIAL_DOUBLE_SIDED + N = gl_FrontFacing ? N : -N; +# ifdef STANDARDMATERIAL_NORMAL_MAP + T = gl_FrontFacing ? T : -T; + B = gl_FrontFacing ? B : -B; +# endif +# endif + +# ifdef STANDARDMATERIAL_NORMAL_MAP + mat3 TBN = mat3(T, B, N); + N = TBN * normalize(texture(sampler2D(StandardMaterial_normal_map, StandardMaterial_normal_map_sampler), v_Uv).rgb * 2.0 - 1.0); +# endif + +# ifdef STANDARDMATERIAL_OCCLUSION_TEXTURE + float occlusion = texture(sampler2D(StandardMaterial_occlusion_texture, StandardMaterial_occlusion_texture_sampler), v_Uv).r; +# else + float occlusion = 1.0; +# endif + +# ifdef STANDARDMATERIAL_EMISSIVE_TEXTURE + vec4 emissive = emissive; + // TODO use .a for exposure compensation in HDR + emissive.rgb *= texture(sampler2D(StandardMaterial_emissive_texture, StandardMaterial_emissive_texture_sampler), v_Uv).rgb; +# endif + + vec3 V = normalize(CameraPos.xyz - v_WorldPosition.xyz); + // Neubelt and Pettineo 2013, "Crafting a Next-gen Material Pipeline for The Order: 1886" + float NdotV = max(dot(N, V), 1e-4); + + // Remapping [0,1] reflectance to F0 + // See https://google.github.io/filament/Filament.html#materialsystem/parameterization/remapping + vec3 F0 = 0.16 * reflectance * reflectance * (1.0 - metallic) + output_color.rgb * metallic; + + // Diffuse strength inversely related to metallicity + vec3 diffuseColor = output_color.rgb * (1.0 - metallic); + + // accumulate color + vec3 light_accum = vec3(0.0); + for (int i = 0; i < int(NumLights.x) && i < MAX_LIGHTS; ++i) { + Light light = SceneLights[i]; + + vec3 lightDir = light.pos.xyz - v_WorldPosition.xyz; + vec3 L = normalize(lightDir); + + float rangeAttenuation = + getDistanceAttenuation(lightDir, light.pos.w); + + vec3 H = normalize(L + V); + float NoL = saturate(dot(N, L)); + float NoH = saturate(dot(N, H)); + float LoH = saturate(dot(L, H)); + + vec3 specular = specular(F0, roughness, H, NdotV, NoL, NoH, LoH); + vec3 diffuse = diffuseColor * Fd_Burley(roughness, NdotV, NoL, LoH); + + // Lout = f(v,l) Φ / { 4 π d^2 }⟨n⋅l⟩ + // where + // f(v,l) = (f_d(v,l) + f_r(v,l)) * light_color + // Φ is light intensity + + // our rangeAttentuation = 1 / d^2 multiplied with an attenuation factor for smoothing at the edge of the non-physical maximum light radius + // It's not 100% clear where the 1/4π goes in the derivation, but we follow the filament shader and leave it out + + // See https://google.github.io/filament/Filament.html#mjx-eqn-pointLightLuminanceEquation + // TODO compensate for energy loss https://google.github.io/filament/Filament.html#materialsystem/improvingthebrdfs/energylossinspecularreflectance + // light.color.rgb is premultiplied with light.intensity on the CPU + light_accum += + ((diffuse + specular) * light.color.rgb) * (rangeAttenuation * NoL); + } + + vec3 diffuse_ambient = EnvBRDFApprox(diffuseColor, 1.0, NdotV); + vec3 specular_ambient = EnvBRDFApprox(F0, perceptual_roughness, NdotV); + + output_color.rgb = light_accum; + output_color.rgb += (diffuse_ambient + specular_ambient) * AmbientColor.xyz * occlusion * v_AO; + output_color.rgb += emissive.rgb * output_color.a; + + // tone_mapping + output_color.rgb = reinhard_luminance(output_color.rgb); + // Gamma correction. + // Not needed with sRGB buffer + // output_color.rgb = pow(output_color.rgb, vec3(1.0 / 2.2)); +#endif + + o_Target = output_color; +} diff --git a/examples/blocky_ambient_occlusion/ambient_occlusion.vert b/examples/blocky_ambient_occlusion/ambient_occlusion.vert new file mode 100644 index 00000000..eea034fe --- /dev/null +++ b/examples/blocky_ambient_occlusion/ambient_occlusion.vert @@ -0,0 +1,68 @@ +// MIT License + +// Copyright (c) 2020 Carter Anderson +// Copyright (c) 2021 Robert Swain + +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: + +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. + +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +// Default bevy PBR shaders with added vertex attribute for texture layer +// and using an array texture for the base colour + +#version 450 + +layout(location = 0) in vec3 Vertex_Position; +layout(location = 1) in vec3 Vertex_Normal; +layout(location = 2) in vec2 Vertex_Uv; + +#ifdef STANDARDMATERIAL_NORMAL_MAP +layout(location = 3) in vec4 Vertex_Tangent; +#endif + +layout(location = 4) in float Vertex_AO; + +layout(location = 0) out vec3 v_WorldPosition; +layout(location = 1) out vec3 v_WorldNormal; +layout(location = 2) out vec2 v_Uv; + +layout(set = 0, binding = 0) uniform CameraViewProj { + mat4 ViewProj; +}; + +#ifdef STANDARDMATERIAL_NORMAL_MAP +layout(location = 3) out vec4 v_WorldTangent; +#endif + +layout(location = 4) out float v_AO; + +layout(set = 2, binding = 0) uniform Transform { + mat4 Model; +}; + +void main() { + vec4 world_position = Model * vec4(Vertex_Position, 1.0); + v_WorldPosition = world_position.xyz; + v_WorldNormal = mat3(Model) * Vertex_Normal; + v_Uv = Vertex_Uv; +#ifdef STANDARDMATERIAL_NORMAL_MAP + v_WorldTangent = vec4(mat3(Model) * Vertex_Tangent.xyz, Vertex_Tangent.w); +#endif + gl_Position = ViewProj * world_position; + + v_AO = Vertex_AO * 1.0 / 3.0; +} diff --git a/examples/blocky_ambient_occlusion/blocky_ambient_occlusion.rs b/examples/blocky_ambient_occlusion/blocky_ambient_occlusion.rs new file mode 100644 index 00000000..9bdaa3f2 --- /dev/null +++ b/examples/blocky_ambient_occlusion/blocky_ambient_occlusion.rs @@ -0,0 +1,468 @@ +use bevy::{ + input::system::exit_on_esc_system, + pbr::AmbientLight, + prelude::{shape, *}, + render::{ + mesh::Indices, + pipeline::{PipelineDescriptor, PrimitiveTopology, RenderPipeline}, + shader::{ShaderStage, ShaderStages}, + wireframe::{WireframeConfig, WireframePlugin}, + }, + wgpu::{WgpuFeature, WgpuFeatures, WgpuOptions}, +}; +use bevy_fly_camera::{FlyCamera, FlyCameraPlugin}; +use building_blocks::core::prelude::*; +use building_blocks::mesh::{ + greedy_quads_with_merge_strategy, GreedyQuadsBuffer, IsOpaque, MergeVoxel, PosNormTexMesh, + VoxelAOMerger, RIGHT_HANDED_Y_UP_CONFIG, +}; +use building_blocks::storage::{access_traits::{Get, GetMut}, Array3x1, IsEmpty}; + +fn main() { + App::build() + .insert_resource(WgpuOptions { + features: WgpuFeatures { + // The Wireframe requires NonFillPolygonMode feature + features: vec![WgpuFeature::NonFillPolygonMode], + }, + ..Default::default() + }) + .add_plugins(DefaultPlugins) + .add_plugin(WireframePlugin) + .insert_resource(WireframeConfig { + global: true, + ..Default::default() + }) + .add_plugin(FlyCameraPlugin) + .add_system(exit_on_esc_system.system()) + .add_startup_system(setup.system()) + .add_system(toggle_fly_camera.system()) + .run(); +} + +/// Basic voxel type with one byte of texture layers +#[derive(Default, Clone, Copy)] +struct Voxel(bool); + +impl MergeVoxel for Voxel { + type VoxelValue = bool; + + fn voxel_merge_value(&self) -> Self::VoxelValue { + self.0 + } +} + +impl IsOpaque for Voxel { + fn is_opaque(&self) -> bool { + true + } +} + +impl IsEmpty for Voxel { + fn is_empty(&self) -> bool { + !self.0 + } +} + +fn setup( + mut commands: Commands, + mut pipelines: ResMut>, + mut shaders: ResMut>, + mut meshes: ResMut>, + mut materials: ResMut>, +) { + let voxels = set_up_voxels(); + + let mut greedy_buffer = + GreedyQuadsBuffer::new(*voxels.extent(), RIGHT_HANDED_Y_UP_CONFIG.quad_groups()); + greedy_quads_with_merge_strategy::<_, _, VoxelAOMerger>( + &voxels, + voxels.extent(), + &mut greedy_buffer, + ); + + let flip_v = true; + let voxel_size = 1.0; + let mut mesh_buf = PosNormTexMesh::default(); + let mut ambient_occlusions = Vec::new(); + for group in greedy_buffer.quad_groups.iter() { + for quad in group.quads.iter() { + let mut ao_values = [0f32; 4]; + for (i, vertex) in group.face.quad_corners(quad).iter().enumerate() { + ao_values[i] = get_ao_at_vert_pos(*vertex, &voxels, voxels.extent()) as f32; + } + ambient_occlusions.extend_from_slice(&ao_values); + group.face.add_quad_to_pos_norm_tex_mesh( + RIGHT_HANDED_Y_UP_CONFIG.u_flip_face, + flip_v, + quad, + voxel_size, + &mut mesh_buf, + ); + } + } + + // Create the bevy mesh + let mut render_mesh = Mesh::new(PrimitiveTopology::TriangleList); + + let PosNormTexMesh { + positions, + normals, + tex_coords, + indices, + } = mesh_buf; + + render_mesh.set_attribute(Mesh::ATTRIBUTE_POSITION, positions); + render_mesh.set_attribute(Mesh::ATTRIBUTE_NORMAL, normals); + render_mesh.set_attribute(Mesh::ATTRIBUTE_UV_0, tex_coords); + render_mesh.set_attribute("Vertex_AO", ambient_occlusions); + render_mesh.set_indices(Some(Indices::U32(indices))); + + // Configure the custom PBR shader with ambient occlusion technique + let pipeline = pipelines.add(PipelineDescriptor::default_config(ShaderStages { + vertex: shaders.add(Shader::from_glsl( + ShaderStage::Vertex, + include_str!("ambient_occlusion.vert"), + )), + fragment: Some(shaders.add(Shader::from_glsl( + ShaderStage::Fragment, + include_str!("ambient_occlusion.frag"), + ))), + })); + commands.spawn_bundle(PbrBundle { + mesh: meshes.add(render_mesh), + material: materials.add(StandardMaterial { + base_color: Color::SEA_GREEN, + ..Default::default() + }), + render_pipelines: RenderPipelines::from_pipelines(vec![RenderPipeline::new(pipeline)]), + ..Default::default() + }); + + // Axes + let axis_mesh = meshes.add(shape::Cube { size: 1.0 }.into()); + let axis_length = 50.0; + commands.spawn_bundle(PbrBundle { + mesh: axis_mesh.clone(), + material: materials.add(StandardMaterial { + base_color: Color::RED, + ..Default::default() + }), + transform: Transform::from_matrix(Mat4::from_scale_rotation_translation( + Vec3::new(axis_length, 0.1, 0.1), + Quat::IDENTITY, + Vec3::new(0.5 * axis_length, 0.0, 0.0), + )), + ..Default::default() + }); + commands.spawn_bundle(PbrBundle { + mesh: axis_mesh.clone(), + material: materials.add(StandardMaterial { + base_color: Color::GREEN, + ..Default::default() + }), + transform: Transform::from_matrix(Mat4::from_scale_rotation_translation( + Vec3::new(0.1, axis_length, 0.1), + Quat::IDENTITY, + Vec3::new(0.0, 0.5 * axis_length, 0.0), + )), + ..Default::default() + }); + commands.spawn_bundle(PbrBundle { + mesh: axis_mesh, + material: materials.add(StandardMaterial { + base_color: Color::BLUE, + ..Default::default() + }), + transform: Transform::from_matrix(Mat4::from_scale_rotation_translation( + Vec3::new(0.1, 0.1, axis_length), + Quat::IDENTITY, + Vec3::new(0.0, 0.0, 0.5 * axis_length), + )), + ..Default::default() + }); + + // NOTE: Ambient occlusion only applies to diffuse and specular scattering of ambient light + // other light sources will easily overpower it so this demo only uses ambient light for the + // sake of focusing on the ambient occlusion + commands.insert_resource(AmbientLight { + color: Color::WHITE, + brightness: 0.6, + }); + let xy_offset = Vec3::new(0.1 * axis_length, 0.1 * axis_length, 0.0); + commands + .spawn_bundle(PerspectiveCameraBundle { + transform: Transform::from_matrix(Mat4::face_toward( + Vec3::new(0.0, 0.0, 0.5 * axis_length) + xy_offset, + xy_offset, + Vec3::Y, + )), + ..Default::default() + }) + .insert(FlyCamera::default()); +} + +fn set_up_voxels() -> Array3x1 { + let interior_extent = Extent3i::from_min_and_shape(PointN([0; 3]), PointN([64; 3])); + let full_extent = interior_extent.padded(1); + let mut voxels = Array3x1::fill(full_extent, Voxel::default()); + + let row_step = PointN([0, 5, 0]); + let col_step = PointN([5, 0, 0]); + let mut offset = PointN([1, 1, 1]); + + // . + *voxels.get_mut(offset + PointN([1, 1, 1])) = Voxel(true); + offset += row_step; + + // . . . + // . . . + *voxels.get_mut(offset + PointN([0, 2, 1])) = Voxel(true); + *voxels.get_mut(offset + PointN([1, 1, 1])) = Voxel(true); + offset += col_step; + + *voxels.get_mut(offset + PointN([2, 2, 1])) = Voxel(true); + *voxels.get_mut(offset + PointN([1, 1, 1])) = Voxel(true); + offset += col_step; + + *voxels.get_mut(offset + PointN([1, 2, 0])) = Voxel(true); + *voxels.get_mut(offset + PointN([1, 1, 1])) = Voxel(true); + *offset.x_mut() = 1; + offset += row_step; + + // .. .. + // . . + *voxels.get_mut(offset + PointN([0, 2, 0])) = Voxel(true); + *voxels.get_mut(offset + PointN([0, 2, 1])) = Voxel(true); + *voxels.get_mut(offset + PointN([1, 1, 1])) = Voxel(true); + offset += col_step; + + *voxels.get_mut(offset + PointN([0, 2, 0])) = Voxel(true); + *voxels.get_mut(offset + PointN([1, 2, 0])) = Voxel(true); + *voxels.get_mut(offset + PointN([1, 1, 1])) = Voxel(true); + *offset.x_mut() = 1; + offset += row_step; + + // . .. + // .. .. + *voxels.get_mut(offset + PointN([1, 2, 0])) = Voxel(true); + *voxels.get_mut(offset + PointN([0, 2, 1])) = Voxel(true); + *voxels.get_mut(offset + PointN([1, 1, 1])) = Voxel(true); + offset += col_step; + + *voxels.get_mut(offset + PointN([0, 2, 0])) = Voxel(true); + *voxels.get_mut(offset + PointN([1, 2, 0])) = Voxel(true); + *voxels.get_mut(offset + PointN([0, 2, 1])) = Voxel(true); + *voxels.get_mut(offset + PointN([1, 1, 1])) = Voxel(true); + *offset.x_mut() = 1; + offset += row_step; + + // Looks like a 2-seater sofa and we want to check that the faces of the seat and backrests are NOT merged + // as the ambient occlusion values on the edges are different than in the middle + // Left armrest + *voxels.get_mut(offset + PointN([0, 1, 1])) = Voxel(true); + // Seat + *voxels.get_mut(offset + PointN([1, 0, 1])) = Voxel(true); + *voxels.get_mut(offset + PointN([2, 0, 1])) = Voxel(true); + // Back + *voxels.get_mut(offset + PointN([1, 1, 0])) = Voxel(true); + *voxels.get_mut(offset + PointN([2, 1, 0])) = Voxel(true); + // Right armrest + *voxels.get_mut(offset + PointN([3, 1, 1])) = Voxel(true); + offset += PointN([6, 0, 0]); + + // Looks like a 4-seater sofa and we want to check that the faces of the seat and backrests are + // merged for the center two seats but not the left/right seats as the ambient occlusion values + // on the edges are different than in the middle + // Left armrest + *voxels.get_mut(offset + PointN([0, 1, 1])) = Voxel(true); + // Seat + *voxels.get_mut(offset + PointN([1, 0, 1])) = Voxel(true); + *voxels.get_mut(offset + PointN([2, 0, 1])) = Voxel(true); + *voxels.get_mut(offset + PointN([3, 0, 1])) = Voxel(true); + *voxels.get_mut(offset + PointN([4, 0, 1])) = Voxel(true); + // Back + *voxels.get_mut(offset + PointN([1, 1, 0])) = Voxel(true); + *voxels.get_mut(offset + PointN([2, 1, 0])) = Voxel(true); + *voxels.get_mut(offset + PointN([3, 1, 0])) = Voxel(true); + *voxels.get_mut(offset + PointN([4, 1, 0])) = Voxel(true); + // Right armrest + *voxels.get_mut(offset + PointN([5, 1, 1])) = Voxel(true); + offset += PointN([8, 0, 0]); + + // Looks like a 2-seater sofa and we want to check that the faces of the seat and backrests are NOT merged + // as the ambient occlusion values on the edges are different than in the middle + // Seat + *voxels.get_mut(offset + PointN([0, 0, 1])) = Voxel(true); + *voxels.get_mut(offset + PointN([1, 0, 1])) = Voxel(true); + // Back + *voxels.get_mut(offset + PointN([0, 1, 0])) = Voxel(true); + *voxels.get_mut(offset + PointN([1, 1, 0])) = Voxel(true); + offset += PointN([4, 0, 0]); + + // Looks like a 4-seater sofa and we want to check that the faces of the seat and backrests are + // merged for the center two seats but not the left/right seats as the ambient occlusion values + // on the edges are different than in the middle + // Seat + *voxels.get_mut(offset + PointN([0, 0, 1])) = Voxel(true); + *voxels.get_mut(offset + PointN([1, 0, 1])) = Voxel(true); + *voxels.get_mut(offset + PointN([2, 0, 1])) = Voxel(true); + *voxels.get_mut(offset + PointN([3, 0, 1])) = Voxel(true); + // Back + *voxels.get_mut(offset + PointN([0, 1, 0])) = Voxel(true); + *voxels.get_mut(offset + PointN([1, 1, 0])) = Voxel(true); + *voxels.get_mut(offset + PointN([2, 1, 0])) = Voxel(true); + *voxels.get_mut(offset + PointN([3, 1, 0])) = Voxel(true); + *offset.x_mut() = 1; + offset += row_step; + + // Looks like a 2-seater sofa and we want to check that the faces of the seat and backrests are NOT merged + // as the ambient occlusion values on the edges are different than in the middle + // Left armrest + *voxels.get_mut(offset + PointN([0, 1, 1])) = Voxel(true); + // Seat + *voxels.get_mut(offset + PointN([1, 0, 1])) = Voxel(true); + *voxels.get_mut(offset + PointN([2, 0, 1])) = Voxel(true); + *voxels.get_mut(offset + PointN([1, 0, 2])) = Voxel(true); + *voxels.get_mut(offset + PointN([2, 0, 2])) = Voxel(true); + *voxels.get_mut(offset + PointN([1, 0, 3])) = Voxel(true); + *voxels.get_mut(offset + PointN([2, 0, 3])) = Voxel(true); + // Back + *voxels.get_mut(offset + PointN([1, 1, 0])) = Voxel(true); + *voxels.get_mut(offset + PointN([2, 1, 0])) = Voxel(true); + *voxels.get_mut(offset + PointN([1, 2, 0])) = Voxel(true); + *voxels.get_mut(offset + PointN([2, 2, 0])) = Voxel(true); + *voxels.get_mut(offset + PointN([1, 3, 0])) = Voxel(true); + *voxels.get_mut(offset + PointN([2, 3, 0])) = Voxel(true); + // Right armrest + *voxels.get_mut(offset + PointN([3, 1, 1])) = Voxel(true); + offset += PointN([6, 0, 0]); + + // Looks like a 4-seater sofa and we want to check that the faces of the seat and backrests are + // merged for the center two seats but not the left/right seats as the ambient occlusion values + // on the edges are different than in the middle + // Left armrest + *voxels.get_mut(offset + PointN([0, 1, 1])) = Voxel(true); + // Seat + *voxels.get_mut(offset + PointN([1, 0, 1])) = Voxel(true); + *voxels.get_mut(offset + PointN([2, 0, 1])) = Voxel(true); + *voxels.get_mut(offset + PointN([3, 0, 1])) = Voxel(true); + *voxels.get_mut(offset + PointN([4, 0, 1])) = Voxel(true); + *voxels.get_mut(offset + PointN([1, 0, 2])) = Voxel(true); + *voxels.get_mut(offset + PointN([2, 0, 2])) = Voxel(true); + *voxels.get_mut(offset + PointN([3, 0, 2])) = Voxel(true); + *voxels.get_mut(offset + PointN([4, 0, 2])) = Voxel(true); + *voxels.get_mut(offset + PointN([1, 0, 3])) = Voxel(true); + *voxels.get_mut(offset + PointN([2, 0, 3])) = Voxel(true); + *voxels.get_mut(offset + PointN([3, 0, 3])) = Voxel(true); + *voxels.get_mut(offset + PointN([4, 0, 3])) = Voxel(true); + // Back + *voxels.get_mut(offset + PointN([1, 1, 0])) = Voxel(true); + *voxels.get_mut(offset + PointN([2, 1, 0])) = Voxel(true); + *voxels.get_mut(offset + PointN([3, 1, 0])) = Voxel(true); + *voxels.get_mut(offset + PointN([4, 1, 0])) = Voxel(true); + *voxels.get_mut(offset + PointN([1, 2, 0])) = Voxel(true); + *voxels.get_mut(offset + PointN([2, 2, 0])) = Voxel(true); + *voxels.get_mut(offset + PointN([3, 2, 0])) = Voxel(true); + *voxels.get_mut(offset + PointN([4, 2, 0])) = Voxel(true); + *voxels.get_mut(offset + PointN([1, 3, 0])) = Voxel(true); + *voxels.get_mut(offset + PointN([2, 3, 0])) = Voxel(true); + *voxels.get_mut(offset + PointN([3, 3, 0])) = Voxel(true); + *voxels.get_mut(offset + PointN([4, 3, 0])) = Voxel(true); + // Right armrest + *voxels.get_mut(offset + PointN([5, 1, 1])) = Voxel(true); + offset += PointN([8, 0, 0]); + + // Looks like a 2-seater sofa and we want to check that the faces of the seat and backrests are NOT merged + // as the ambient occlusion values on the edges are different than in the middle + // Seat + *voxels.get_mut(offset + PointN([0, 0, 1])) = Voxel(true); + *voxels.get_mut(offset + PointN([1, 0, 1])) = Voxel(true); + *voxels.get_mut(offset + PointN([0, 0, 2])) = Voxel(true); + *voxels.get_mut(offset + PointN([1, 0, 2])) = Voxel(true); + // Back + *voxels.get_mut(offset + PointN([0, 1, 0])) = Voxel(true); + *voxels.get_mut(offset + PointN([1, 1, 0])) = Voxel(true); + *voxels.get_mut(offset + PointN([0, 2, 0])) = Voxel(true); + *voxels.get_mut(offset + PointN([1, 2, 0])) = Voxel(true); + offset += PointN([4, 0, 0]); + + // Looks like a 4-seater sofa and we want to check that the faces of the seat and backrests are + // merged for the center two seats but not the left/right seats as the ambient occlusion values + // on the edges are different than in the middle + // Seat + *voxels.get_mut(offset + PointN([0, 0, 1])) = Voxel(true); + *voxels.get_mut(offset + PointN([1, 0, 1])) = Voxel(true); + *voxels.get_mut(offset + PointN([2, 0, 1])) = Voxel(true); + *voxels.get_mut(offset + PointN([3, 0, 1])) = Voxel(true); + *voxels.get_mut(offset + PointN([0, 0, 2])) = Voxel(true); + *voxels.get_mut(offset + PointN([1, 0, 2])) = Voxel(true); + *voxels.get_mut(offset + PointN([2, 0, 2])) = Voxel(true); + *voxels.get_mut(offset + PointN([3, 0, 2])) = Voxel(true); + // Back + *voxels.get_mut(offset + PointN([0, 1, 0])) = Voxel(true); + *voxels.get_mut(offset + PointN([1, 1, 0])) = Voxel(true); + *voxels.get_mut(offset + PointN([2, 1, 0])) = Voxel(true); + *voxels.get_mut(offset + PointN([3, 1, 0])) = Voxel(true); + *voxels.get_mut(offset + PointN([0, 2, 0])) = Voxel(true); + *voxels.get_mut(offset + PointN([1, 2, 0])) = Voxel(true); + *voxels.get_mut(offset + PointN([2, 2, 0])) = Voxel(true); + *voxels.get_mut(offset + PointN([3, 2, 0])) = Voxel(true); + *offset.x_mut() = 1; + offset += row_step; + + voxels +} + +fn get_ao_at_vert_pos(v: Point3i, padded_chunk: &A, padded_chunk_extent: &Extent3i) -> i32 +where + A: Get, + T: IsEmpty + IsOpaque + MergeVoxel, +{ + let loc: Point3i = v; + + let top0_loc = PointN([loc.x() - 1, loc.y(), loc.z()]); + let top1_loc: Point3i = PointN([loc.x(), loc.y(), loc.z() - 1]); + let top2_loc: Point3i = PointN([loc.x(), loc.y(), loc.z()]); + let top3_loc: Point3i = PointN([loc.x() - 1, loc.y(), loc.z() - 1]); + + let bot0_loc: Point3i = PointN([loc.x() - 1, loc.y() - 1, loc.z()]); + let bot1_loc: Point3i = PointN([loc.x(), loc.y() - 1, loc.z() - 1]); + let bot2_loc: Point3i = PointN([loc.x(), loc.y() - 1, loc.z()]); + let bot3_loc: Point3i = PointN([loc.x() - 1, loc.y() - 1, loc.z() - 1]); + + let top0 = padded_chunk_extent.contains(top0_loc) && !padded_chunk.get(top0_loc).is_empty(); + let top1 = padded_chunk_extent.contains(top1_loc) && !padded_chunk.get(top1_loc).is_empty(); + let top2 = padded_chunk_extent.contains(top2_loc) && !padded_chunk.get(top2_loc).is_empty(); + let top3 = padded_chunk_extent.contains(top3_loc) && !padded_chunk.get(top3_loc).is_empty(); + let bot0 = padded_chunk_extent.contains(bot0_loc) && !padded_chunk.get(bot0_loc).is_empty(); + let bot1 = padded_chunk_extent.contains(bot1_loc) && !padded_chunk.get(bot1_loc).is_empty(); + let bot2 = padded_chunk_extent.contains(bot2_loc) && !padded_chunk.get(bot2_loc).is_empty(); + let bot3 = padded_chunk_extent.contains(bot3_loc) && !padded_chunk.get(bot3_loc).is_empty(); + + let (side1, side2, corner) = if !top0 && bot0 { + (top2, top3, top1) + } else if !top1 && bot1 { + (top2, top3, top0) + } else if !top2 && bot2 { + (top0, top1, top3) + } else if !top3 && bot3 { + (top0, top1, top2) + } else { + return 3; + }; + + if side1 && side2 { + return 0; + } + + return 3 - (side1 as i32 + side2 as i32 + corner as i32); +} + +fn toggle_fly_camera(keyboard_input: Res>, mut fly_camera: Query<&mut FlyCamera>) { + if keyboard_input.just_pressed(KeyCode::C) { + for mut fc in fly_camera.iter_mut() { + fc.enabled = !fc.enabled; + } + } +} From 1b77f6b5cbfd26a1ed0f3f5b31ea534a1edf5263 Mon Sep 17 00:00:00 2001 From: Robert Swain Date: Thu, 6 May 2021 20:39:42 +0200 Subject: [PATCH 06/10] Make it go. OMG so many lines of tests... This is the worst code I've written in years. --- .../building_blocks_mesh/src/greedy_quads.rs | 783 +++++++- .../src/array/coords.rs | 2 +- .../ambient_occlusion.vert | 11 +- .../blocky_ambient_occlusion.rs | 1697 +++++++++++++++-- 4 files changed, 2288 insertions(+), 205 deletions(-) diff --git a/crates/building_blocks_mesh/src/greedy_quads.rs b/crates/building_blocks_mesh/src/greedy_quads.rs index a1375550..ca55dc5f 100644 --- a/crates/building_blocks_mesh/src/greedy_quads.rs +++ b/crates/building_blocks_mesh/src/greedy_quads.rs @@ -64,6 +64,35 @@ pub const RIGHT_HANDED_Y_UP_CONFIG: QuadCoordinateConfig = QuadCoordinateConfig u_flip_face: Axis3::X, }; +pub fn oriented_cube_face_to_cube_face(oriented_cube_face: OrientedCubeFace) -> CubeFace { + match oriented_cube_face.permutation { + Axis3Permutation::Zxy => { + if oriented_cube_face.n_sign > 0 { + CubeFace::Front + } else { + CubeFace::Back + } + } + Axis3Permutation::Yzx => { + if oriented_cube_face.n_sign > 0 { + CubeFace::Top + } else { + CubeFace::Bottom + } + } + Axis3Permutation::Xzy => { + if oriented_cube_face.n_sign > 0 { + CubeFace::Right + } else { + CubeFace::Left + } + } + _ => { + unreachable!(); + } + } +} + impl QuadCoordinateConfig { pub fn quad_groups(self) -> [QuadGroup; 6] { let [f0, f1, f2, f3, f4, f5] = self.faces; @@ -128,6 +157,7 @@ pub fn greedy_quads(voxels: &A, extent: &Extent3i, output: &mut GreedyQuad where A: IndexedArray<[i32; 3]> + ForEach<[i32; 3], (Point3i, Stride), Item = T> + + Get + Get, T: IsEmpty + IsOpaque + MergeVoxel, { @@ -142,6 +172,7 @@ pub fn greedy_quads_with_merge_strategy( ) where A: IndexedArray<[i32; 3]> + ForEach<[i32; 3], (Point3i, Stride), Item = T> + + Get + Get, T: IsEmpty + IsOpaque, Merger: MergeStrategy, @@ -167,6 +198,7 @@ fn greedy_quads_for_group( ) where A: IndexedArray<[i32; 3]> + ForEach<[i32; 3], (Point3i, Stride), Item = T> + + Get + Get, T: IsEmpty + IsOpaque, Merger: MergeStrategy, @@ -186,6 +218,8 @@ fn greedy_quads_for_group( }, } = quad_group; + println!("n: {:?}, u: {:?}, v: {:?}, n_sign: {}", n, u, v, n_sign); + let [n_axis, u_axis, v_axis] = permutation.axes(); let i_n = n_axis.index(); let i_u = u_axis.index(); @@ -276,27 +310,44 @@ fn face_needs_mesh( visited: &Array3x1, ) -> bool where - A: Get, + A: Get + IndexedArray<[i32; 3]>, T: IsEmpty + IsOpaque, { if voxel.is_empty() || visited.get(voxel_stride) { + // println!( + // "face_needs_mesh: false -> voxel.is_empty(): {}, visited.get(voxel_stride): {}", + // voxel.is_empty(), + // visited.get(voxel_stride) + // ); return false; } let adjacent_voxel = voxels.get(voxel_stride + visibility_offset); if adjacent_voxel.is_empty() { + println!( + "face_needs_mesh: true -> adjacent_voxel.is_empty(): {:?}", + stride_to_point(voxels.extent(), voxel_stride + visibility_offset) + ); // Must be visible, opaque or transparent. return true; } if adjacent_voxel.is_opaque() { + println!( + "face_needs_mesh: true -> adjacent_voxel.is_opaque(): {:?}", + stride_to_point(voxels.extent(), voxel_stride + visibility_offset) + ); // Fully occluded. return false; } // TODO: If the face lies between two transparent voxels, we choose not to mesh it. We might need to extend the IsOpaque // trait with different levels of transparency to support this. + println!( + "face_needs_mesh: {} -> voxel.is_opaque()", + voxel.is_opaque() + ); voxel.is_opaque() } @@ -337,10 +388,13 @@ pub trait MergeStrategy { visited: &Array3x1, ) -> (i32, i32) where - A: IndexedArray<[i32; 3]> + Get, + A: IndexedArray<[i32; 3]> + + Get + + Get, Self::Voxel: IsEmpty + IsOpaque; } +#[derive(Debug)] pub struct FaceStrides { pub n_stride: Stride, pub u_stride: Stride, @@ -443,9 +497,9 @@ impl VoxelMerger { let voxel = voxels.get(row_stride); - if !face_needs_mesh(&voxel, row_stride, visibility_offset, voxels, visited) { - break; - } + // if !face_needs_mesh(&voxel, row_stride, visibility_offset, voxels, visited) { + // break; + // } if !voxel.voxel_merge_value().eq(quad_merge_voxel_value) { // Voxel needs to be non-empty and match the quad merge value. @@ -479,19 +533,52 @@ where visited: &Array3x1, ) -> (i32, i32) where - A: Get, + A: IndexedArray<[i32; 3]> + Get + Get, { + println!(">>> find_quad"); // Greedily search for the biggest visible quad where all merge values are the same. let quad_value = min_value.voxel_merge_value(); - // Calculate minimum U edge AO values - let vaos = [ - get_ao_at_vert(voxels, face_strides, min_stride), - get_ao_at_vert(voxels, face_strides, min_stride + face_strides.v_stride), - ]; + println!("face_strides: {:?}", face_strides); + let cube_face = face_strides_to_cube_face(face_strides); + println!("cube_face: {:?}", cube_face); + println!( + "find_quad for {:?}", + stride_to_point(voxels.extent(), min_stride) + ); // Start by finding the widest quad in the U direction. let mut row_start_stride = min_stride; + let vaos = [ + get_ao_at_vert( + voxels, + face_strides, + row_start_stride, + cube_face, + FaceVertex::TopLeft, + ), + get_ao_at_vert( + voxels, + face_strides, + row_start_stride, + cube_face, + FaceVertex::BottomLeft, + ), + get_ao_at_vert( + voxels, + face_strides, + row_start_stride, + cube_face, + FaceVertex::TopRight, + ), + get_ao_at_vert( + voxels, + face_strides, + row_start_stride, + cube_face, + FaceVertex::BottomRight, + ), + ]; let quad_width = Self::get_row_width( voxels, visited, @@ -501,28 +588,60 @@ where row_start_stride, face_strides.u_stride, face_strides, + cube_face, max_width, ); max_width = max_width.min(quad_width); let mut quad_height = 1; - // If the 0th row minimum U edge ambient occlusion values match, we can merge in V - if vaos[0] == vaos[1] { + // If the 0th row ambient occlusion values are constant along V, we can merge in V + if vaos[0] == vaos[1] && vaos[2] == vaos[3] { // Now see how tall we can make the quad in the V direction without changing the width. row_start_stride += face_strides.v_stride; while quad_height < max_height { + let p = stride_to_point(voxels.extent(), row_start_stride); + println!("quad_height: {}, p: {:?}", quad_height, p); // Check if minimum U edge AO values match, if so we can merge vertically, otherwise we're done let row_vaos = [ - get_ao_at_vert(voxels, face_strides, row_start_stride), get_ao_at_vert( voxels, face_strides, - row_start_stride + face_strides.v_stride, + row_start_stride, + cube_face, + FaceVertex::TopLeft, + ), + get_ao_at_vert( + voxels, + face_strides, + row_start_stride, + cube_face, + FaceVertex::BottomLeft, + ), + get_ao_at_vert( + voxels, + face_strides, + row_start_stride, + cube_face, + FaceVertex::TopRight, + ), + get_ao_at_vert( + voxels, + face_strides, + row_start_stride, + cube_face, + FaceVertex::BottomRight, ), ]; - if vaos[0] != row_vaos[0] || vaos[1] != row_vaos[1] { + + if vaos[1] != row_vaos[0] + || vaos[3] != row_vaos[2] + || row_vaos[0] != row_vaos[1] + || row_vaos[0] != row_vaos[2] + || row_vaos[2] != row_vaos[3] + { // AO values not constant along V + println!("AO values not constant along V"); break; } let row_width = Self::get_row_width( @@ -534,9 +653,11 @@ where row_start_stride, face_strides.u_stride, face_strides, + cube_face, max_width, ); if row_width < quad_width { + println!("row_width {} < quad_width {}", row_width, quad_width); break; } quad_height += 1; @@ -544,6 +665,8 @@ where } } + println!("quad_width: {}, quad_height: {}", quad_width, quad_height); + println!("<<< find_quad"); (quad_width, quad_height) } } @@ -558,100 +681,634 @@ impl VoxelAOMerger { start_stride: Stride, delta_stride: Stride, face_strides: &FaceStrides, + cube_face: CubeFace, max_width: i32, ) -> i32 where - A: Get, + A: IndexedArray<[i32; 3]> + Get + Get, T: IsEmpty + IsOpaque + MergeVoxel, { + println!(">>> get_row_width"); + println!( + "start stride: {:?} = {:?}", + stride_to_point(voxels.extent(), start_stride), + vaos + ); let mut quad_width = 0; let mut row_stride = start_stride; while quad_width < max_width { + let padded_extent = *voxels.extent(); + let p = stride_to_point(&padded_extent, row_stride) + padded_extent.minimum; + println!("p: {:?}", p); + if visited.get(row_stride) { // Already have a quad for this voxel face. + println!("<<< Visited: {:?}", p); break; } let voxel = voxels.get(row_stride); if !face_needs_mesh(&voxel, row_stride, visibility_offset, voxels, visited) { + println!("Face does not need mesh: {:?}", p); break; } if !voxel.voxel_merge_value().eq(quad_merge_voxel_value) { // Voxel needs to be non-empty and match the quad merge value. + println!("Voxel value differs: {:?}", p); break; } - quad_width += 1; - let next_vaos = [ - get_ao_at_vert(voxels, face_strides, row_stride + face_strides.u_stride), get_ao_at_vert( voxels, face_strides, - row_stride + face_strides.u_stride + face_strides.v_stride, + row_stride, + cube_face, + FaceVertex::TopLeft, + ), + get_ao_at_vert( + voxels, + face_strides, + row_stride, + cube_face, + FaceVertex::BottomLeft, + ), + get_ao_at_vert( + voxels, + face_strides, + row_stride, + cube_face, + FaceVertex::TopRight, + ), + get_ao_at_vert( + voxels, + face_strides, + row_stride, + cube_face, + FaceVertex::BottomRight, ), ]; - if vaos[0] != next_vaos[0] || vaos[1] != next_vaos[1] { + if vaos[2] != next_vaos[0] + || vaos[3] != next_vaos[1] + || next_vaos[0] != next_vaos[2] + || next_vaos[1] != next_vaos[3] + { + println!( + "AO values differ: {:?} {:?}", + p, face_strides.visibility_offset + ); // Ambient occlusion values must be constant across the row // They are not so we cannot proceed to the next quad + if quad_width == 0 { + quad_width = 1; + } break; } + quad_width += 1; row_stride += delta_stride; } + println!("quad_width: {}", quad_width); + println!("<<< get_row_width"); quad_width } } -fn get_ao_at_vert(voxels: &A, face_strides: &FaceStrides, stride: Stride) -> i32 +fn stride_to_point(extent: &Extent3i, stride: Stride) -> Point3i { + let mut rem = stride.0 as i32; + + let xy_area = extent.shape.x() * extent.shape.y(); + let z = rem / xy_area; + rem %= xy_area; + + let y = rem / extent.shape.x(); + rem %= extent.shape.x(); + + let x = rem; + + PointN([x, y, z]) + extent.minimum +} + +#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] +pub enum CubeFace { + Top, + Bottom, + Left, + Right, + Back, + Front, +} + +#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] +pub enum FaceVertex { + TopLeft, + TopRight, + BottomLeft, + BottomRight, +} + +fn face_strides_to_cube_face(face_strides: &FaceStrides) -> CubeFace { + let (ns, us, vs, vis) = ( + face_strides.n_stride, + face_strides.u_stride, + face_strides.v_stride, + face_strides.visibility_offset, + ); + let n_sign = if vis == ns { 1 } else { -1 }; + let (xs, ys, zs) = if ns >= us && us >= vs { + (vs, us, ns) + } else if ns >= vs && vs >= us { + (us, vs, ns) + } else if us >= ns && ns >= vs { + (vs, ns, us) + } else if us >= vs && vs >= ns { + (ns, vs, us) + } else if vs >= ns && ns >= us { + (us, ns, vs) + } else if vs >= us && us >= ns { + (ns, us, vs) + } else { + unreachable!(); + }; + if n_sign > 0 { + if ns == xs { + CubeFace::Right + } else if ns == ys { + CubeFace::Top + } else { + CubeFace::Front + } + } else { + if ns == xs { + CubeFace::Left + } else if ns == ys { + CubeFace::Bottom + } else { + CubeFace::Back + } + } +} + +pub fn get_ao_at_vert( + voxels: &A, + face_strides: &FaceStrides, + stride: Stride, + cube_face: CubeFace, + face_vertex: FaceVertex, +) -> i32 where - A: Get, + A: IndexedArray<[i32; 3]> + Get + Get, T: IsEmpty + IsOpaque + MergeVoxel, { - let top0_loc = stride - face_strides.u_stride; - let top1_loc = stride - face_strides.n_stride; - let top2_loc = stride; - let top3_loc = stride - face_strides.n_stride - face_strides.u_stride; - - let bot0_loc = stride - face_strides.u_stride - face_strides.v_stride; - let bot1_loc = stride - face_strides.n_stride - face_strides.v_stride; - let bot2_loc = stride - face_strides.v_stride; - let bot3_loc = stride - face_strides.n_stride - face_strides.u_stride - face_strides.v_stride; - - let top0 = !voxels.get(top0_loc).is_empty(); - let top1 = !voxels.get(top1_loc).is_empty(); - let top2 = !voxels.get(top2_loc).is_empty(); - let top3 = !voxels.get(top3_loc).is_empty(); - let bot0 = !voxels.get(bot0_loc).is_empty(); - let bot1 = !voxels.get(bot1_loc).is_empty(); - let bot2 = !voxels.get(bot2_loc).is_empty(); - let bot3 = !voxels.get(bot3_loc).is_empty(); - - let (side1, side2, corner) = if !top0 && bot0 { - (top2, top3, top1) - } else if !top1 && bot1 { - (top2, top3, top0) - } else if !top2 && bot2 { - (top0, top1, top3) - } else if !top3 && bot3 { - (top0, top1, top2) + let (ns, us, vs, vis) = ( + face_strides.n_stride, + face_strides.u_stride, + face_strides.v_stride, + face_strides.visibility_offset, + ); + let (side1_s, side2_s, corner_s) = match cube_face { + CubeFace::Top => match face_vertex { + FaceVertex::TopLeft => (stride + vis - vs, stride + vis - us, stride + vis - us - vs), + FaceVertex::TopRight => (stride + vis - us, stride + vis + vs, stride + vis - us + vs), + FaceVertex::BottomLeft => { + (stride + vis + us, stride + vis - vs, stride + vis + us - vs) + } + FaceVertex::BottomRight => { + (stride + vis + vs, stride + vis + us, stride + vis + us + vs) + } + }, + CubeFace::Bottom => match face_vertex { + FaceVertex::TopLeft => (stride + vis + vs, stride + vis - us, stride + vis - us + vs), + FaceVertex::TopRight => (stride + vis - us, stride + vis - vs, stride + vis - us - vs), + FaceVertex::BottomLeft => { + (stride + vis + us, stride + vis + vs, stride + vis + us + vs) + } + FaceVertex::BottomRight => { + (stride + vis - vs, stride + vis + us, stride + vis + us - vs) + } + }, + CubeFace::Left => match face_vertex { + FaceVertex::TopLeft => (stride + vis + vs, stride + vis - us, stride + vis - us + vs), + FaceVertex::TopRight => (stride + vis + us, stride + vis + vs, stride + vis + us + vs), + FaceVertex::BottomLeft => { + (stride + vis - us, stride + vis - vs, stride + vis - us - vs) + } + FaceVertex::BottomRight => { + (stride + vis - vs, stride + vis + us, stride + vis + us - vs) + } + }, + CubeFace::Right => match face_vertex { + FaceVertex::TopLeft => (stride + vis + us, stride + vis + vs, stride + vis + us + vs), + FaceVertex::TopRight => (stride + vis + vs, stride + vis - us, stride + vis - us + vs), + FaceVertex::BottomLeft => { + (stride + vis - vs, stride + vis + us, stride + vis + us - vs) + } + FaceVertex::BottomRight => { + (stride + vis - us, stride + vis - vs, stride + vis - us - vs) + } + }, + CubeFace::Back => match face_vertex { + FaceVertex::TopLeft => (stride + vis + us, stride + vis + vs, stride + vis + us + vs), + FaceVertex::TopRight => (stride + vis + vs, stride + vis - us, stride + vis - us + vs), + FaceVertex::BottomLeft => { + (stride + vis - vs, stride + vis + us, stride + vis + us - vs) + } + FaceVertex::BottomRight => { + (stride + vis - us, stride + vis - vs, stride + vis - us - vs) + } + }, + CubeFace::Front => match face_vertex { + FaceVertex::TopLeft => (stride + vis - us, stride + vis + vs, stride + vis - us + vs), + FaceVertex::TopRight => (stride + vis + vs, stride + vis + us, stride + vis + us + vs), + FaceVertex::BottomLeft => { + (stride + vis - vs, stride + vis - us, stride + vis - us - vs) + } + FaceVertex::BottomRight => { + (stride + vis + us, stride + vis - vs, stride + vis + us - vs) + } + }, + }; + // println!( + // "side1: {:?}, side2: {:?}, corner: {:?}", + // stride_to_point(voxels.extent(), side1_s), + // stride_to_point(voxels.extent(), side2_s), + // stride_to_point(voxels.extent(), corner_s) + // ); + + let (side1, side2, corner) = ( + !voxels.get(side1_s).is_empty(), + !voxels.get(side2_s).is_empty(), + !voxels.get(corner_s).is_empty() && voxels.get(corner_s).is_opaque(), + ); + // println!("side1: {}, side2: {}, corner: {}", side1, side2, corner); + let ao_value = if side1 && side2 { + 0 } else { - return 3; + 3 - (side1 as i32 + side2 as i32 + corner as i32) }; - if side1 && side2 { - return 0; - } - return 3 - (side1 as i32 + side2 as i32 + corner as i32); -} - }; + println!( + "AO value for {:?} {:?} {:?} = {}", + stride_to_point(voxels.extent(), stride), + cube_face, + face_vertex, + ao_value + ); - if side0 && side1 { - return 3; - } else { - return side0 as i32 + side1 as i32 + corner as i32; - } + ao_value } + +// // ████████╗███████╗███████╗████████╗ +// // ╚══██╔══╝██╔════╝██╔════╝╚══██╔══╝ +// // ██║ █████╗ ███████╗ ██║ +// // ██║ ██╔══╝ ╚════██║ ██║ +// // ██║ ███████╗███████║ ██║ +// // ╚═╝ ╚══════╝╚══════╝ ╚═╝ + +// #[cfg(test)] +// mod tests { +// use std::collections::HashSet; + +// use super::*; + +// #[derive(Clone)] +// struct Voxel(u8); + +// impl IsEmpty for Voxel { +// fn is_empty(&self) -> bool { +// self.0 == 0 +// } +// } + +// impl IsOpaque for Voxel { +// fn is_opaque(&self) -> bool { +// true +// } +// } + +// impl MergeVoxel for Voxel { +// type VoxelValue = u8; + +// fn voxel_merge_value(&self) -> Self::VoxelValue { +// self.0 +// } +// } + +// #[test] +// fn ambient_occlusion_one_voxel() { +// let extent = Extent3i::from_min_and_shape(PointN([0, 0, 0]), PointN([4, 4, 4])); +// let mut array = Array3x1::fill(extent, Voxel(0)); +// *array.get_mut(PointN([1, 1, 1])) = Voxel(1); + +// let mut output = GreedyQuadsBuffer::new(extent, RIGHT_HANDED_Y_UP_CONFIG.quad_groups()); +// greedy_quads_with_merge_strategy::<_, _, VoxelAOMerger>( +// &array, +// &extent, +// &mut output, +// ); + +// for group in output.quad_groups.iter() { +// for quad in group.quads.iter() { +// let mut ambient_occlusions = [0f32; 4]; +// for (i, vertex) in group.face.quad_corners(quad).iter().enumerate() { +// ambient_occlusions[i] = get_ao_at_vert_pos(*vertex, &array, &extent) as f32; +// } +// let positions = group.face.quad_mesh_positions(quad, 1.0); +// for (i, position) in positions.iter().enumerate() { +// println!("position: {:?} = {}", position, ambient_occlusions[i]); +// assert!(ambient_occlusions[i] == 0.0f32); +// } +// } +// } +// } + +// #[test] +// fn ambient_occlusion_two_voxels_a() { +// let extent = Extent3i::from_min_and_shape(PointN([0, 0, 0]), PointN([4, 4, 4])); +// let mut array = Array3x1::fill(extent, Voxel(0)); +// *array.get_mut(PointN([1, 1, 1])) = Voxel(1); +// *array.get_mut(PointN([0, 2, 1])) = Voxel(1); + +// let mut output = GreedyQuadsBuffer::new(extent, RIGHT_HANDED_Y_UP_CONFIG.quad_groups()); +// greedy_quads_with_merge_strategy::<_, _, VoxelAOMerger>( +// &array, +// &extent, +// &mut output, +// ); + +// for group in output.quad_groups.iter() { +// for quad in group.quads.iter() { +// let mut ambient_occlusions = [0f32; 4]; +// for (i, vertex) in group.face.quad_corners(quad).iter().enumerate() { +// ambient_occlusions[i] = get_ao_at_vert_pos(*vertex, &array, &extent) as f32; +// } +// let positions = group.face.quad_mesh_positions(quad, 1.0); +// for (i, position) in positions.iter().enumerate() { +// println!("position: {:?} = {}", position, ambient_occlusions[i]); +// assert!( +// ambient_occlusions[i] == 0.0 +// || ((*position == [1.0, 2.0, 1.0] || *position == [1.0, 2.0, 2.0]) +// && ambient_occlusions[i] == 1.0) +// ); +// } +// } +// } +// } + +// #[test] +// fn ambient_occlusion_two_voxels_b() { +// let extent = Extent3i::from_min_and_shape(PointN([0, 0, 0]), PointN([4, 4, 4])); +// let mut array = Array3x1::fill(extent, Voxel(0)); +// *array.get_mut(PointN([1, 1, 1])) = Voxel(1); +// *array.get_mut(PointN([0, 2, 0])) = Voxel(1); + +// let mut output = GreedyQuadsBuffer::new(extent, RIGHT_HANDED_Y_UP_CONFIG.quad_groups()); +// greedy_quads_with_merge_strategy::<_, _, VoxelAOMerger>( +// &array, +// &extent, +// &mut output, +// ); + +// for group in output.quad_groups.iter() { +// for quad in group.quads.iter() { +// let mut ambient_occlusions = [0f32; 4]; +// for (i, vertex) in group.face.quad_corners(quad).iter().enumerate() { +// ambient_occlusions[i] = get_ao_at_vert_pos(*vertex, &array, &extent) as f32; +// } +// let positions = group.face.quad_mesh_positions(quad, 1.0); +// for (i, position) in positions.iter().enumerate() { +// println!("position: {:?} = {}", position, ambient_occlusions[i]); +// assert!( +// ambient_occlusions[i] == 0.0 +// || (*position == [1.0, 2.0, 1.0] && ambient_occlusions[i] == 1.0) +// ); +// } +// } +// } +// } + +// #[test] +// fn ambient_occlusion_two_voxels_c() { +// let extent = Extent3i::from_min_and_shape(PointN([0, 0, 0]), PointN([4, 4, 4])); +// let mut array = Array3x1::fill(extent, Voxel(0)); +// *array.get_mut(PointN([1, 1, 1])) = Voxel(1); +// *array.get_mut(PointN([1, 2, 0])) = Voxel(1); + +// let mut output = GreedyQuadsBuffer::new(extent, RIGHT_HANDED_Y_UP_CONFIG.quad_groups()); +// greedy_quads_with_merge_strategy::<_, _, VoxelAOMerger>( +// &array, +// &extent, +// &mut output, +// ); + +// for group in output.quad_groups.iter() { +// for quad in group.quads.iter() { +// let mut ambient_occlusions = [0f32; 4]; +// for (i, vertex) in group.face.quad_corners(quad).iter().enumerate() { +// ambient_occlusions[i] = get_ao_at_vert_pos(*vertex, &array, &extent) as f32; +// } +// let positions = group.face.quad_mesh_positions(quad, 1.0); +// for (i, position) in positions.iter().enumerate() { +// println!("position: {:?} = {}", position, ambient_occlusions[i]); +// assert!( +// ambient_occlusions[i] == 0.0 +// || ((*position == [1.0, 2.0, 1.0] || *position == [2.0, 2.0, 1.0]) +// && ambient_occlusions[i] == 1.0) +// ); +// } +// } +// } +// } + +// #[test] +// fn ambient_occlusion_three_voxels_a() { +// let extent = Extent3i::from_min_and_shape(PointN([0, 0, 0]), PointN([4, 4, 4])); +// let mut array = Array3x1::fill(extent, Voxel(0)); +// *array.get_mut(PointN([1, 1, 1])) = Voxel(1); +// *array.get_mut(PointN([0, 2, 0])) = Voxel(1); +// *array.get_mut(PointN([0, 2, 1])) = Voxel(1); + +// let mut output = GreedyQuadsBuffer::new(extent, RIGHT_HANDED_Y_UP_CONFIG.quad_groups()); +// greedy_quads_with_merge_strategy::<_, _, VoxelAOMerger>( +// &array, +// &extent, +// &mut output, +// ); + +// for group in output.quad_groups.iter() { +// for quad in group.quads.iter() { +// let mut ambient_occlusions = [0f32; 4]; +// for (i, vertex) in group.face.quad_corners(quad).iter().enumerate() { +// ambient_occlusions[i] = get_ao_at_vert_pos(*vertex, &array, &extent) as f32; +// } +// let positions = group.face.quad_mesh_positions(quad, 1.0); +// for (i, position) in positions.iter().enumerate() { +// println!("position: {:?} = {}", position, ambient_occlusions[i]); +// assert!( +// ambient_occlusions[i] == 0.0 +// || (*position == [1.0, 2.0, 1.0] && ambient_occlusions[i] == 2.0) +// || (*position == [1.0, 2.0, 2.0] && ambient_occlusions[i] == 1.0) +// ); +// } +// } +// } +// } + +// #[test] +// fn ambient_occlusion_three_voxels_b() { +// let extent = Extent3i::from_min_and_shape(PointN([0, 0, 0]), PointN([4, 4, 4])); +// let mut array = Array3x1::fill(extent, Voxel(0)); +// *array.get_mut(PointN([1, 1, 1])) = Voxel(1); +// *array.get_mut(PointN([0, 2, 0])) = Voxel(1); +// *array.get_mut(PointN([1, 2, 0])) = Voxel(1); + +// let mut output = GreedyQuadsBuffer::new(extent, RIGHT_HANDED_Y_UP_CONFIG.quad_groups()); +// greedy_quads_with_merge_strategy::<_, _, VoxelAOMerger>( +// &array, +// &extent, +// &mut output, +// ); + +// for group in output.quad_groups.iter() { +// for quad in group.quads.iter() { +// let mut ambient_occlusions = [0f32; 4]; +// for (i, vertex) in group.face.quad_corners(quad).iter().enumerate() { +// ambient_occlusions[i] = get_ao_at_vert_pos(*vertex, &array, &extent) as f32; +// } +// let positions = group.face.quad_mesh_positions(quad, 1.0); +// for (i, position) in positions.iter().enumerate() { +// println!("position: {:?} = {}", position, ambient_occlusions[i]); +// assert!( +// ambient_occlusions[i] == 0.0 +// || (*position == [1.0, 2.0, 1.0] && ambient_occlusions[i] == 2.0) +// || (*position == [2.0, 2.0, 1.0] && ambient_occlusions[i] == 1.0) +// ); +// } +// } +// } +// } + +// #[test] +// fn ambient_occlusion_three_voxels_c() { +// let extent = Extent3i::from_min_and_shape(PointN([0, 0, 0]), PointN([4, 4, 4])); +// let mut array = Array3x1::fill(extent, Voxel(0)); +// *array.get_mut(PointN([1, 1, 1])) = Voxel(1); +// *array.get_mut(PointN([1, 2, 0])) = Voxel(1); +// *array.get_mut(PointN([0, 2, 1])) = Voxel(1); + +// let mut output = GreedyQuadsBuffer::new(extent, RIGHT_HANDED_Y_UP_CONFIG.quad_groups()); +// greedy_quads_with_merge_strategy::<_, _, VoxelAOMerger>( +// &array, +// &extent, +// &mut output, +// ); + +// for group in output.quad_groups.iter() { +// for quad in group.quads.iter() { +// let mut ambient_occlusions = [0f32; 4]; +// for (i, vertex) in group.face.quad_corners(quad).iter().enumerate() { +// ambient_occlusions[i] = get_ao_at_vert_pos(*vertex, &array, &extent) as f32; +// } +// let positions = group.face.quad_mesh_positions(quad, 1.0); +// for (i, position) in positions.iter().enumerate() { +// println!("position: {:?} = {}", position, ambient_occlusions[i]); +// assert!( +// ambient_occlusions[i] == 0.0 +// || (*position == [1.0, 2.0, 1.0] && ambient_occlusions[i] == 3.0) +// || ((*position == [1.0, 2.0, 2.0] +// || *position == [2.0, 2.0, 1.0] +// || *position == [1.0, 3.0, 1.0]) +// && ambient_occlusions[i] == 1.0) +// ); +// } +// } +// } +// } + +// #[test] +// fn ambient_occlusion_four_voxels() { +// let extent = Extent3i::from_min_and_shape(PointN([0, 0, 0]), PointN([4, 4, 4])); +// let mut array = Array3x1::fill(extent, Voxel(0)); +// *array.get_mut(PointN([1, 1, 1])) = Voxel(1); +// *array.get_mut(PointN([1, 2, 0])) = Voxel(1); +// *array.get_mut(PointN([0, 2, 1])) = Voxel(1); +// *array.get_mut(PointN([0, 2, 0])) = Voxel(1); + +// let mut output = GreedyQuadsBuffer::new(extent, RIGHT_HANDED_Y_UP_CONFIG.quad_groups()); +// greedy_quads_with_merge_strategy::<_, _, VoxelAOMerger>( +// &array, +// &extent, +// &mut output, +// ); + +// for group in output.quad_groups.iter() { +// for quad in group.quads.iter() { +// let mut ambient_occlusions = [0f32; 4]; +// for (i, vertex) in group.face.quad_corners(quad).iter().enumerate() { +// ambient_occlusions[i] = get_ao_at_vert_pos(*vertex, &array, &extent) as f32; +// } +// let positions = group.face.quad_mesh_positions(quad, 1.0); +// for (i, position) in positions.iter().enumerate() { +// println!("position: {:?} = {}", position, ambient_occlusions[i]); +// assert!( +// ambient_occlusions[i] == 0.0 +// || (*position == [1.0, 2.0, 1.0] && ambient_occlusions[i] == 3.0) +// || ((*position == [1.0, 2.0, 2.0] +// || *position == [2.0, 2.0, 1.0] +// || *position == [1.0, 3.0, 1.0]) +// && ambient_occlusions[i] == 1.0) +// ); +// } +// } +// } +// } + +// #[test] +// fn ambient_occlusion_merge_corners() { +// let extent = Extent3i::from_min_and_shape(PointN([0, 0, 0]), PointN([8, 8, 8])); +// let mut array = Array3x1::fill(extent, Voxel(0)); +// // Looks like a 2-seater sofa and we want to check that the faces of the seat and backrests are NOT merged +// // as the ambient occlusion values on the edges are different than in the middle +// // Left armrest +// *array.get_mut(PointN([1, 2, 2])) = Voxel(1); +// // Right armrest +// *array.get_mut(PointN([4, 2, 2])) = Voxel(1); +// // Seat +// *array.get_mut(PointN([2, 1, 2])) = Voxel(1); +// *array.get_mut(PointN([3, 1, 2])) = Voxel(1); +// // Back +// *array.get_mut(PointN([2, 2, 1])) = Voxel(1); +// *array.get_mut(PointN([3, 2, 1])) = Voxel(1); + +// let mut output = GreedyQuadsBuffer::new(extent, RIGHT_HANDED_Y_UP_CONFIG.quad_groups()); +// greedy_quads_with_merge_strategy::<_, _, VoxelAOMerger>( +// &array, +// &extent, +// &mut output, +// ); + +// let mut positions_set = HashSet::new(); +// for group in output.quad_groups.iter() { +// for quad in group.quads.iter() { +// let positions = group.face.quad_mesh_positions(quad, 1.0); +// for position in positions.iter() { +// println!("position: {:?}", position); +// positions_set.insert(PointN([ +// position[0] as i32, +// position[1] as i32, +// position[2] as i32, +// ])); +// } +// } +// } +// // Front center of seat +// assert!(positions_set.contains(&PointN([3, 2, 3]))); +// // Back center of seat +// assert!(positions_set.contains(&PointN([3, 2, 2]))); +// // Center of back +// assert!(positions_set.contains(&PointN([3, 3, 2]))); +// } +// } diff --git a/crates/building_blocks_storage/src/array/coords.rs b/crates/building_blocks_storage/src/array/coords.rs index 4bb96ac0..3b7b88e1 100644 --- a/crates/building_blocks_storage/src/array/coords.rs +++ b/crates/building_blocks_storage/src/array/coords.rs @@ -61,7 +61,7 @@ impl Deref for Local { } /// The most efficient coordinates for slice-backed lattice maps. A single number that translates directly to a slice offset. -#[derive(Clone, Copy, Debug, Default, Eq, Hash, PartialEq)] +#[derive(Clone, Copy, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)] pub struct Stride(pub usize); impl Zero for Stride { diff --git a/examples/blocky_ambient_occlusion/ambient_occlusion.vert b/examples/blocky_ambient_occlusion/ambient_occlusion.vert index eea034fe..79460127 100644 --- a/examples/blocky_ambient_occlusion/ambient_occlusion.vert +++ b/examples/blocky_ambient_occlusion/ambient_occlusion.vert @@ -64,5 +64,14 @@ void main() { #endif gl_Position = ViewProj * world_position; - v_AO = Vertex_AO * 1.0 / 3.0; + vec4 ao_curve = vec4(0.0, 0.2, 0.4, 1.0); + if (Vertex_AO == 0.0) { + v_AO = ao_curve.x; + } else if (Vertex_AO == 1.0) { + v_AO = ao_curve.y; + } else if (Vertex_AO == 2.0) { + v_AO = ao_curve.z; + } else { + v_AO = ao_curve.w; + } } diff --git a/examples/blocky_ambient_occlusion/blocky_ambient_occlusion.rs b/examples/blocky_ambient_occlusion/blocky_ambient_occlusion.rs index 9bdaa3f2..82cee4c3 100644 --- a/examples/blocky_ambient_occlusion/blocky_ambient_occlusion.rs +++ b/examples/blocky_ambient_occlusion/blocky_ambient_occlusion.rs @@ -1,3 +1,5 @@ +use std::collections::HashMap; + use bevy::{ input::system::exit_on_esc_system, pbr::AmbientLight, @@ -16,7 +18,15 @@ use building_blocks::mesh::{ greedy_quads_with_merge_strategy, GreedyQuadsBuffer, IsOpaque, MergeVoxel, PosNormTexMesh, VoxelAOMerger, RIGHT_HANDED_Y_UP_CONFIG, }; -use building_blocks::storage::{access_traits::{Get, GetMut}, Array3x1, IsEmpty}; +use building_blocks::storage::{ + access_traits::{Get, GetMut}, + Array3x1, IsEmpty, +}; +use building_blocks_mesh::{ + get_ao_at_vert, oriented_cube_face_to_cube_face, CubeFace, FaceStrides, FaceVertex, + OrientedCubeFace, QuadGroup, UnorientedQuad, +}; +use building_blocks_storage::{IndexedArray, Local, Stride}; fn main() { App::build() @@ -64,6 +74,147 @@ impl IsEmpty for Voxel { } } +fn cube_face_to_quad_corners_as_face_vertices(cube_face: CubeFace) -> [FaceVertex; 4] { + match cube_face { + CubeFace::Top => [ + FaceVertex::TopLeft, + FaceVertex::BottomLeft, + FaceVertex::TopRight, + FaceVertex::BottomRight, + ], + CubeFace::Bottom => [ + FaceVertex::TopRight, + FaceVertex::BottomRight, + FaceVertex::TopLeft, + FaceVertex::BottomLeft, + ], + CubeFace::Left => [ + FaceVertex::BottomLeft, + FaceVertex::BottomRight, + FaceVertex::TopLeft, + FaceVertex::TopRight, + ], + CubeFace::Right => [ + FaceVertex::BottomRight, + FaceVertex::BottomLeft, + FaceVertex::TopRight, + FaceVertex::TopLeft, + ], + CubeFace::Back => [ + FaceVertex::BottomRight, + FaceVertex::BottomLeft, + FaceVertex::TopRight, + FaceVertex::TopLeft, + ], + CubeFace::Front => [ + FaceVertex::BottomLeft, + FaceVertex::BottomRight, + FaceVertex::TopLeft, + FaceVertex::TopRight, + ], + } +} + +#[derive(Debug, Clone)] +struct MeshBuf { + pub positions: Vec<[f32; 3]>, + pub normals: Vec<[f32; 3]>, + pub tex_coords: Vec<[f32; 2]>, + pub ambient_occlusion: Vec, + pub indices: Vec, + pub extent: Extent3i, +} + +impl Default for MeshBuf { + fn default() -> Self { + Self { + positions: Vec::new(), + normals: Vec::new(), + tex_coords: Vec::new(), + ambient_occlusion: Vec::new(), + indices: Vec::new(), + extent: Extent3i::from_min_and_shape(PointN([0, 0, 0]), PointN([0, 0, 0])), + } + } +} + +impl MeshBuf { + fn add_quad( + &mut self, + face: &OrientedCubeFace, + quad: &UnorientedQuad, + voxel_size: f32, + u_flip_face: Axis3, + ambient_occlusions: &[f32; 4], + ) { + let start_index = self.positions.len() as u32; + self.positions + .extend_from_slice(&face.quad_mesh_positions(quad, voxel_size)); + self.normals.extend_from_slice(&face.quad_mesh_normals()); + + let flip_v = true; + self.tex_coords + .extend_from_slice(&face.tex_coords(u_flip_face, flip_v, quad)); + + self.ambient_occlusion.extend_from_slice(ambient_occlusions); + self.indices.extend_from_slice(&face.quad_mesh_indices( + start_index, + false, // FIXME: Fix flipping! It will need to be different logic for different faces!!! + //ambient_occlusions[0] + ambient_occlusions[3] + //> ambient_occlusions[1] + ambient_occlusions[2], + )); + } +} + +fn cube_face_to_face_strides(voxels: &A, cube_face: CubeFace) -> FaceStrides +where + A: IndexedArray<[i32; 3]>, +{ + let (xs, ys, zs) = ( + voxels.stride_from_local_point(Local(PointN([1, 0, 0]))), + voxels.stride_from_local_point(Local(PointN([0, 1, 0]))), + voxels.stride_from_local_point(Local(PointN([0, 0, 1]))), + ); + match cube_face { + CubeFace::Top => FaceStrides { + n_stride: ys, + u_stride: zs, + v_stride: xs, + visibility_offset: ys, + }, + CubeFace::Bottom => FaceStrides { + n_stride: ys, + u_stride: zs, + v_stride: xs, + visibility_offset: Stride(0) - ys, + }, + CubeFace::Left => FaceStrides { + n_stride: xs, + u_stride: zs, + v_stride: ys, + visibility_offset: Stride(0) - xs, + }, + CubeFace::Right => FaceStrides { + n_stride: xs, + u_stride: zs, + v_stride: ys, + visibility_offset: xs, + }, + CubeFace::Back => FaceStrides { + n_stride: zs, + u_stride: xs, + v_stride: ys, + visibility_offset: Stride(0) - zs, + }, + CubeFace::Front => FaceStrides { + n_stride: zs, + u_stride: xs, + v_stride: ys, + visibility_offset: zs, + }, + } +} + fn setup( mut commands: Commands, mut pipelines: ResMut>, @@ -83,21 +234,54 @@ fn setup( let flip_v = true; let voxel_size = 1.0; - let mut mesh_buf = PosNormTexMesh::default(); - let mut ambient_occlusions = Vec::new(); + let mut mesh_buf = MeshBuf::default(); for group in greedy_buffer.quad_groups.iter() { + let cube_face = oriented_cube_face_to_cube_face(group.face); + let face_strides = cube_face_to_face_strides(&voxels, cube_face); for quad in group.quads.iter() { - let mut ao_values = [0f32; 4]; - for (i, vertex) in group.face.quad_corners(quad).iter().enumerate() { - ao_values[i] = get_ao_at_vert_pos(*vertex, &voxels, voxels.extent()) as f32; - } - ambient_occlusions.extend_from_slice(&ao_values); - group.face.add_quad_to_pos_norm_tex_mesh( - RIGHT_HANDED_Y_UP_CONFIG.u_flip_face, - flip_v, + let stride = + voxels.stride_from_local_point(Local(quad.minimum - voxels.extent().minimum)); + println!("quad corners: {:?}", group.face.quad_corners(quad)); + let corners = cube_face_to_quad_corners_as_face_vertices( + oriented_cube_face_to_cube_face(group.face), + ); + // FIXME - THESE HAVE TO USE THE QUAD WIDTH AND QUAD HEIGHT!!! + let ao_values = [ + get_ao_at_vert( + &voxels, + &face_strides, + stride, + cube_face, + corners[0], // FIXME: SHOULD BE UV 0,0 + ) as f32, + get_ao_at_vert( + &voxels, + &face_strides, + stride, + cube_face, + corners[1], // FIXME: SHOULD BE UV 1,0 + ) as f32, + get_ao_at_vert( + &voxels, + &face_strides, + stride, + cube_face, + corners[2], // FIXME: SHOULD BE UV 0,1 + ) as f32, + get_ao_at_vert( + &voxels, + &face_strides, + stride, + cube_face, + corners[3], // FIXME: SHOULD BE UV 1,1 + ) as f32, + ]; + mesh_buf.add_quad( + &group.face, quad, voxel_size, - &mut mesh_buf, + RIGHT_HANDED_Y_UP_CONFIG.u_flip_face, + &ao_values, ); } } @@ -105,17 +289,19 @@ fn setup( // Create the bevy mesh let mut render_mesh = Mesh::new(PrimitiveTopology::TriangleList); - let PosNormTexMesh { + let MeshBuf { positions, normals, tex_coords, indices, + ambient_occlusion, + .. } = mesh_buf; render_mesh.set_attribute(Mesh::ATTRIBUTE_POSITION, positions); render_mesh.set_attribute(Mesh::ATTRIBUTE_NORMAL, normals); render_mesh.set_attribute(Mesh::ATTRIBUTE_UV_0, tex_coords); - render_mesh.set_attribute("Vertex_AO", ambient_occlusions); + render_mesh.set_attribute("Vertex_AO", ambient_occlusion); render_mesh.set_indices(Some(Indices::U32(indices))); // Configure the custom PBR shader with ambient occlusion technique @@ -202,7 +388,50 @@ fn setup( .insert(FlyCamera::default()); } +const CUBE_FACES: [CubeFace; 6] = [ + CubeFace::Top, + CubeFace::Front, + CubeFace::Right, + CubeFace::Bottom, + CubeFace::Back, + CubeFace::Left, +]; + +const FACE_VERTICES: [FaceVertex; 4] = [ + FaceVertex::TopLeft, + FaceVertex::TopRight, + FaceVertex::BottomRight, + FaceVertex::BottomLeft, +]; + +fn populate_and_verify( + voxels: &mut Array3x1, + positions: &[Point3i], + offset: Point3i, + vertex_aos: &HashMap<(Point3i, CubeFace, FaceVertex), i32>, +) { + for position in positions { + *voxels.get_mut(offset + *position) = Voxel(true); + } + for position in positions { + let stride = + voxels.stride_from_local_point(Local(offset + *position - voxels.extent().minimum)); + for cube_face in &CUBE_FACES { + let face_strides = cube_face_to_face_strides(&*voxels, *cube_face); + for face_vertex in &FACE_VERTICES { + assert!( + get_ao_at_vert(&*voxels, &face_strides, stride, *cube_face, *face_vertex) + == *vertex_aos + .get(&(*position, *cube_face, *face_vertex)) + .unwrap_or(&3) + ); + } + } + } +} + fn set_up_voxels() -> Array3x1 { + println!(">>> set_up_voxels"); let interior_extent = Extent3i::from_min_and_shape(PointN([0; 3]), PointN([64; 3])); let full_extent = interior_extent.padded(1); let mut voxels = Array3x1::fill(full_extent, Voxel::default()); @@ -212,130 +441,1362 @@ fn set_up_voxels() -> Array3x1 { let mut offset = PointN([1, 1, 1]); // . - *voxels.get_mut(offset + PointN([1, 1, 1])) = Voxel(true); + let positions = [PointN([1, 1, 1])]; + populate_and_verify(&mut voxels, &positions, offset, &HashMap::new()); offset += row_step; // . . . // . . . - *voxels.get_mut(offset + PointN([0, 2, 1])) = Voxel(true); - *voxels.get_mut(offset + PointN([1, 1, 1])) = Voxel(true); + let positions = [PointN([1, 1, 1]), PointN([0, 2, 1])]; + let vertex_aos: HashMap<(Point3i, CubeFace, FaceVertex), i32> = [ + ((PointN([1, 1, 1]), CubeFace::Top, FaceVertex::TopLeft), 2), + ( + (PointN([1, 1, 1]), CubeFace::Top, FaceVertex::BottomLeft), + 2, + ), + ((PointN([1, 1, 1]), CubeFace::Left, FaceVertex::TopLeft), 2), + ((PointN([1, 1, 1]), CubeFace::Left, FaceVertex::TopRight), 2), + ( + (PointN([0, 2, 1]), CubeFace::Right, FaceVertex::BottomRight), + 2, + ), + ( + (PointN([0, 2, 1]), CubeFace::Right, FaceVertex::BottomLeft), + 2, + ), + ( + (PointN([0, 2, 1]), CubeFace::Bottom, FaceVertex::TopLeft), + 2, + ), + ( + (PointN([0, 2, 1]), CubeFace::Bottom, FaceVertex::BottomLeft), + 2, + ), + ] + .iter() + .cloned() + .collect(); + populate_and_verify(&mut voxels, &positions, offset, &vertex_aos); offset += col_step; - *voxels.get_mut(offset + PointN([2, 2, 1])) = Voxel(true); - *voxels.get_mut(offset + PointN([1, 1, 1])) = Voxel(true); + let positions = [PointN([1, 1, 1]), PointN([0, 2, 0])]; + let vertex_aos: HashMap<(Point3i, CubeFace, FaceVertex), i32> = [ + ((PointN([1, 1, 1]), CubeFace::Top, FaceVertex::TopLeft), 2), + ((PointN([1, 1, 1]), CubeFace::Back, FaceVertex::TopRight), 2), + ((PointN([1, 1, 1]), CubeFace::Left, FaceVertex::TopLeft), 2), + ( + (PointN([0, 2, 0]), CubeFace::Front, FaceVertex::BottomRight), + 2, + ), + ( + (PointN([0, 2, 0]), CubeFace::Right, FaceVertex::BottomLeft), + 2, + ), + ( + (PointN([0, 2, 0]), CubeFace::Bottom, FaceVertex::BottomLeft), + 2, + ), + ] + .iter() + .cloned() + .collect(); + populate_and_verify(&mut voxels, &positions, offset, &vertex_aos); offset += col_step; - *voxels.get_mut(offset + PointN([1, 2, 0])) = Voxel(true); - *voxels.get_mut(offset + PointN([1, 1, 1])) = Voxel(true); + let positions = [PointN([1, 1, 1]), PointN([1, 2, 0])]; + let vertex_aos: HashMap<(Point3i, CubeFace, FaceVertex), i32> = [ + ((PointN([1, 1, 1]), CubeFace::Top, FaceVertex::TopLeft), 2), + ((PointN([1, 1, 1]), CubeFace::Top, FaceVertex::TopRight), 2), + ((PointN([1, 1, 1]), CubeFace::Back, FaceVertex::TopLeft), 2), + ((PointN([1, 1, 1]), CubeFace::Back, FaceVertex::TopRight), 2), + ( + (PointN([1, 2, 0]), CubeFace::Front, FaceVertex::BottomRight), + 2, + ), + ( + (PointN([1, 2, 0]), CubeFace::Front, FaceVertex::BottomLeft), + 2, + ), + ( + (PointN([1, 2, 0]), CubeFace::Bottom, FaceVertex::BottomRight), + 2, + ), + ( + (PointN([1, 2, 0]), CubeFace::Bottom, FaceVertex::BottomLeft), + 2, + ), + ] + .iter() + .cloned() + .collect(); + populate_and_verify(&mut voxels, &positions, offset, &vertex_aos); *offset.x_mut() = 1; offset += row_step; // .. .. // . . - *voxels.get_mut(offset + PointN([0, 2, 0])) = Voxel(true); - *voxels.get_mut(offset + PointN([0, 2, 1])) = Voxel(true); - *voxels.get_mut(offset + PointN([1, 1, 1])) = Voxel(true); + let positions = [PointN([1, 1, 1]), PointN([0, 2, 0]), PointN([0, 2, 1])]; + let vertex_aos: HashMap<(Point3i, CubeFace, FaceVertex), i32> = [ + ((PointN([1, 1, 1]), CubeFace::Top, FaceVertex::TopLeft), 1), + ( + (PointN([1, 1, 1]), CubeFace::Top, FaceVertex::BottomLeft), + 2, + ), + ((PointN([1, 1, 1]), CubeFace::Back, FaceVertex::TopRight), 2), + ((PointN([1, 1, 1]), CubeFace::Left, FaceVertex::TopRight), 2), + ((PointN([1, 1, 1]), CubeFace::Left, FaceVertex::TopLeft), 1), + ( + (PointN([0, 2, 0]), CubeFace::Front, FaceVertex::BottomRight), + 2, + ), // NOTE: This is a hidden face + ( + (PointN([0, 2, 0]), CubeFace::Right, FaceVertex::BottomLeft), + 2, + ), + ( + (PointN([0, 2, 0]), CubeFace::Bottom, FaceVertex::BottomLeft), + 2, + ), + ( + (PointN([0, 2, 1]), CubeFace::Right, FaceVertex::BottomRight), + 2, + ), + ( + (PointN([0, 2, 1]), CubeFace::Right, FaceVertex::BottomLeft), + 2, + ), + ( + (PointN([0, 2, 1]), CubeFace::Bottom, FaceVertex::TopLeft), + 2, + ), + ( + (PointN([0, 2, 1]), CubeFace::Bottom, FaceVertex::BottomLeft), + 2, + ), + ] + .iter() + .cloned() + .collect(); + populate_and_verify(&mut voxels, &positions, offset, &vertex_aos); offset += col_step; - *voxels.get_mut(offset + PointN([0, 2, 0])) = Voxel(true); - *voxels.get_mut(offset + PointN([1, 2, 0])) = Voxel(true); - *voxels.get_mut(offset + PointN([1, 1, 1])) = Voxel(true); + let positions = [PointN([1, 1, 1]), PointN([0, 2, 0]), PointN([1, 2, 0])]; + let vertex_aos: HashMap<(Point3i, CubeFace, FaceVertex), i32> = [ + ((PointN([1, 1, 1]), CubeFace::Top, FaceVertex::TopLeft), 1), + ((PointN([1, 1, 1]), CubeFace::Top, FaceVertex::TopRight), 2), + ((PointN([1, 1, 1]), CubeFace::Back, FaceVertex::TopLeft), 2), + ((PointN([1, 1, 1]), CubeFace::Back, FaceVertex::TopRight), 1), + ((PointN([1, 1, 1]), CubeFace::Left, FaceVertex::TopLeft), 2), + ( + (PointN([0, 2, 0]), CubeFace::Front, FaceVertex::BottomRight), + 2, + ), + ( + (PointN([0, 2, 0]), CubeFace::Right, FaceVertex::BottomLeft), + 2, + ), // NOTE: This is a hidden face + ( + (PointN([0, 2, 0]), CubeFace::Bottom, FaceVertex::BottomLeft), + 2, + ), + ( + (PointN([1, 2, 0]), CubeFace::Front, FaceVertex::BottomRight), + 2, + ), + ( + (PointN([1, 2, 0]), CubeFace::Front, FaceVertex::BottomLeft), + 2, + ), + ( + (PointN([1, 2, 0]), CubeFace::Bottom, FaceVertex::BottomRight), + 2, + ), + ( + (PointN([1, 2, 0]), CubeFace::Bottom, FaceVertex::BottomLeft), + 2, + ), + ] + .iter() + .cloned() + .collect(); + populate_and_verify(&mut voxels, &positions, offset, &vertex_aos); *offset.x_mut() = 1; offset += row_step; // . .. // .. .. - *voxels.get_mut(offset + PointN([1, 2, 0])) = Voxel(true); - *voxels.get_mut(offset + PointN([0, 2, 1])) = Voxel(true); - *voxels.get_mut(offset + PointN([1, 1, 1])) = Voxel(true); + let positions = [PointN([1, 1, 1]), PointN([0, 2, 1]), PointN([1, 2, 0])]; + let vertex_aos: HashMap<(Point3i, CubeFace, FaceVertex), i32> = [ + ((PointN([1, 1, 1]), CubeFace::Top, FaceVertex::TopLeft), 0), + ((PointN([1, 1, 1]), CubeFace::Top, FaceVertex::TopRight), 2), + ( + (PointN([1, 1, 1]), CubeFace::Top, FaceVertex::BottomLeft), + 2, + ), + ((PointN([1, 1, 1]), CubeFace::Back, FaceVertex::TopLeft), 2), + ((PointN([1, 1, 1]), CubeFace::Back, FaceVertex::TopRight), 2), + ((PointN([1, 1, 1]), CubeFace::Left, FaceVertex::TopLeft), 2), + ((PointN([1, 1, 1]), CubeFace::Left, FaceVertex::TopRight), 2), + ( + (PointN([0, 2, 1]), CubeFace::Right, FaceVertex::TopRight), + 2, + ), + ( + (PointN([0, 2, 1]), CubeFace::Right, FaceVertex::BottomRight), + 0, + ), + ( + (PointN([0, 2, 1]), CubeFace::Right, FaceVertex::BottomLeft), + 2, + ), + ( + (PointN([0, 2, 1]), CubeFace::Bottom, FaceVertex::TopLeft), + 2, + ), + ( + (PointN([0, 2, 1]), CubeFace::Bottom, FaceVertex::BottomLeft), + 2, + ), + ((PointN([0, 2, 1]), CubeFace::Back, FaceVertex::TopLeft), 2), + ( + (PointN([0, 2, 1]), CubeFace::Back, FaceVertex::BottomLeft), + 2, + ), + ((PointN([1, 2, 0]), CubeFace::Front, FaceVertex::TopLeft), 2), + ( + (PointN([1, 2, 0]), CubeFace::Front, FaceVertex::BottomRight), + 2, + ), + ( + (PointN([1, 2, 0]), CubeFace::Front, FaceVertex::BottomLeft), + 0, + ), + ( + (PointN([1, 2, 0]), CubeFace::Bottom, FaceVertex::BottomRight), + 2, + ), + ( + (PointN([1, 2, 0]), CubeFace::Bottom, FaceVertex::BottomLeft), + 2, + ), + ((PointN([1, 2, 0]), CubeFace::Left, FaceVertex::TopRight), 2), + ( + (PointN([1, 2, 0]), CubeFace::Left, FaceVertex::BottomRight), + 2, + ), + ] + .iter() + .cloned() + .collect(); + populate_and_verify(&mut voxels, &positions, offset, &vertex_aos); offset += col_step; - *voxels.get_mut(offset + PointN([0, 2, 0])) = Voxel(true); - *voxels.get_mut(offset + PointN([1, 2, 0])) = Voxel(true); - *voxels.get_mut(offset + PointN([0, 2, 1])) = Voxel(true); - *voxels.get_mut(offset + PointN([1, 1, 1])) = Voxel(true); + let positions = [ + PointN([1, 1, 1]), + PointN([0, 2, 0]), + PointN([0, 2, 1]), + PointN([1, 2, 0]), + ]; + let vertex_aos: HashMap<(Point3i, CubeFace, FaceVertex), i32> = [ + ((PointN([1, 1, 1]), CubeFace::Top, FaceVertex::TopLeft), 0), + ((PointN([1, 1, 1]), CubeFace::Top, FaceVertex::TopRight), 2), + ( + (PointN([1, 1, 1]), CubeFace::Top, FaceVertex::BottomLeft), + 2, + ), + ((PointN([1, 1, 1]), CubeFace::Back, FaceVertex::TopLeft), 2), + ((PointN([1, 1, 1]), CubeFace::Back, FaceVertex::TopRight), 1), + ((PointN([1, 1, 1]), CubeFace::Left, FaceVertex::TopLeft), 1), + ((PointN([1, 1, 1]), CubeFace::Left, FaceVertex::TopRight), 2), + ( + (PointN([0, 2, 0]), CubeFace::Front, FaceVertex::BottomRight), + 2, + ), // NOTE: This face is hidden. + ( + (PointN([0, 2, 0]), CubeFace::Right, FaceVertex::BottomLeft), + 2, + ), // NOTE: This face is hidden. + ( + (PointN([0, 2, 0]), CubeFace::Bottom, FaceVertex::BottomLeft), + 2, + ), + ( + (PointN([0, 2, 1]), CubeFace::Right, FaceVertex::TopRight), + 2, + ), + ( + (PointN([0, 2, 1]), CubeFace::Right, FaceVertex::BottomRight), + 0, + ), + ( + (PointN([0, 2, 1]), CubeFace::Right, FaceVertex::BottomLeft), + 2, + ), + ( + (PointN([0, 2, 1]), CubeFace::Bottom, FaceVertex::TopLeft), + 2, + ), + ( + (PointN([0, 2, 1]), CubeFace::Bottom, FaceVertex::BottomLeft), + 2, + ), + ((PointN([0, 2, 1]), CubeFace::Back, FaceVertex::TopLeft), 2), // NOTE: This face is hidden. + ( + (PointN([0, 2, 1]), CubeFace::Back, FaceVertex::BottomLeft), + 2, + ), // NOTE: This face is hidden. + ((PointN([1, 2, 0]), CubeFace::Front, FaceVertex::TopLeft), 2), + ( + (PointN([1, 2, 0]), CubeFace::Front, FaceVertex::BottomRight), + 2, + ), + ( + (PointN([1, 2, 0]), CubeFace::Front, FaceVertex::BottomLeft), + 0, + ), + ( + (PointN([1, 2, 0]), CubeFace::Bottom, FaceVertex::BottomRight), + 2, + ), + ( + (PointN([1, 2, 0]), CubeFace::Bottom, FaceVertex::BottomLeft), + 2, + ), + ((PointN([1, 2, 0]), CubeFace::Left, FaceVertex::TopRight), 2), // NOTE: This face is hidden. + ( + (PointN([1, 2, 0]), CubeFace::Left, FaceVertex::BottomRight), + 2, + ), // NOTE: This face is hidden. + ] + .iter() + .cloned() + .collect(); + populate_and_verify(&mut voxels, &positions, offset, &vertex_aos); *offset.x_mut() = 1; offset += row_step; // Looks like a 2-seater sofa and we want to check that the faces of the seat and backrests are NOT merged // as the ambient occlusion values on the edges are different than in the middle - // Left armrest - *voxels.get_mut(offset + PointN([0, 1, 1])) = Voxel(true); - // Seat - *voxels.get_mut(offset + PointN([1, 0, 1])) = Voxel(true); - *voxels.get_mut(offset + PointN([2, 0, 1])) = Voxel(true); - // Back - *voxels.get_mut(offset + PointN([1, 1, 0])) = Voxel(true); - *voxels.get_mut(offset + PointN([2, 1, 0])) = Voxel(true); - // Right armrest - *voxels.get_mut(offset + PointN([3, 1, 1])) = Voxel(true); + let positions = [ + // Left armrest + PointN([0, 1, 1]), + // Seat + PointN([1, 0, 1]), + PointN([2, 0, 1]), + // Back + PointN([1, 1, 0]), + PointN([2, 1, 0]), + // Right armrest + PointN([3, 1, 1]), + ]; + let vertex_aos: HashMap<(Point3i, CubeFace, FaceVertex), i32> = [ + // Left armrest + ( + (PointN([0, 1, 1]), CubeFace::Right, FaceVertex::TopRight), + 2, + ), + ( + (PointN([0, 1, 1]), CubeFace::Right, FaceVertex::BottomRight), + 0, + ), + ( + (PointN([0, 1, 1]), CubeFace::Right, FaceVertex::BottomLeft), + 2, + ), + ( + (PointN([0, 1, 1]), CubeFace::Bottom, FaceVertex::TopLeft), + 2, + ), + ( + (PointN([0, 1, 1]), CubeFace::Bottom, FaceVertex::BottomLeft), + 2, + ), + ((PointN([0, 1, 1]), CubeFace::Back, FaceVertex::TopLeft), 2), + ( + (PointN([0, 1, 1]), CubeFace::Back, FaceVertex::BottomLeft), + 2, + ), + // Left seat + ((PointN([1, 0, 1]), CubeFace::Top, FaceVertex::TopLeft), 0), + ((PointN([1, 0, 1]), CubeFace::Top, FaceVertex::TopRight), 1), + ( + (PointN([1, 0, 1]), CubeFace::Top, FaceVertex::BottomLeft), + 2, + ), + ( + (PointN([1, 0, 1]), CubeFace::Right, FaceVertex::TopRight), + 2, + ), // NOTE: This face is hidden. + ((PointN([1, 0, 1]), CubeFace::Back, FaceVertex::TopLeft), 1), + ((PointN([1, 0, 1]), CubeFace::Back, FaceVertex::TopRight), 2), + ((PointN([1, 0, 1]), CubeFace::Left, FaceVertex::TopLeft), 2), + ((PointN([1, 0, 1]), CubeFace::Left, FaceVertex::TopRight), 2), + // Right seat + ((PointN([2, 0, 1]), CubeFace::Top, FaceVertex::TopLeft), 1), + ((PointN([2, 0, 1]), CubeFace::Top, FaceVertex::TopRight), 0), + ( + (PointN([2, 0, 1]), CubeFace::Top, FaceVertex::BottomRight), + 2, + ), + ((PointN([2, 0, 1]), CubeFace::Right, FaceVertex::TopLeft), 2), + ( + (PointN([2, 0, 1]), CubeFace::Right, FaceVertex::TopRight), + 2, + ), + ((PointN([2, 0, 1]), CubeFace::Back, FaceVertex::TopLeft), 2), + ((PointN([2, 0, 1]), CubeFace::Back, FaceVertex::TopRight), 1), + ((PointN([2, 0, 1]), CubeFace::Left, FaceVertex::TopLeft), 2), // NOTE: This face is hidden. + // Left back + ((PointN([1, 1, 0]), CubeFace::Front, FaceVertex::TopLeft), 2), + ( + (PointN([1, 1, 0]), CubeFace::Front, FaceVertex::BottomRight), + 1, + ), + ( + (PointN([1, 1, 0]), CubeFace::Front, FaceVertex::BottomLeft), + 0, + ), + ( + (PointN([1, 1, 0]), CubeFace::Right, FaceVertex::BottomLeft), + 2, + ), // NOTE: This face is hidden. + ( + (PointN([1, 1, 0]), CubeFace::Bottom, FaceVertex::BottomRight), + 2, + ), + ( + (PointN([1, 1, 0]), CubeFace::Bottom, FaceVertex::BottomLeft), + 1, + ), + ((PointN([1, 1, 0]), CubeFace::Left, FaceVertex::TopRight), 2), + ( + (PointN([1, 1, 0]), CubeFace::Left, FaceVertex::BottomRight), + 2, + ), + // Right back + ( + (PointN([2, 1, 0]), CubeFace::Front, FaceVertex::TopRight), + 2, + ), + ( + (PointN([2, 1, 0]), CubeFace::Front, FaceVertex::BottomRight), + 0, + ), + ( + (PointN([2, 1, 0]), CubeFace::Front, FaceVertex::BottomLeft), + 1, + ), + ((PointN([2, 1, 0]), CubeFace::Right, FaceVertex::TopLeft), 2), + ( + (PointN([2, 1, 0]), CubeFace::Right, FaceVertex::BottomLeft), + 2, + ), + ( + (PointN([2, 1, 0]), CubeFace::Bottom, FaceVertex::BottomRight), + 1, + ), + ( + (PointN([2, 1, 0]), CubeFace::Bottom, FaceVertex::BottomLeft), + 2, + ), + ( + (PointN([2, 1, 0]), CubeFace::Left, FaceVertex::BottomRight), + 2, + ), // NOTE: This face is hidden. + // Right arm rest + ( + (PointN([3, 1, 1]), CubeFace::Bottom, FaceVertex::TopRight), + 2, + ), + ( + (PointN([3, 1, 1]), CubeFace::Bottom, FaceVertex::BottomRight), + 2, + ), + ((PointN([3, 1, 1]), CubeFace::Back, FaceVertex::TopRight), 2), + ( + (PointN([3, 1, 1]), CubeFace::Back, FaceVertex::BottomRight), + 2, + ), + ((PointN([3, 1, 1]), CubeFace::Left, FaceVertex::TopLeft), 2), + ( + (PointN([3, 1, 1]), CubeFace::Left, FaceVertex::BottomRight), + 2, + ), + ( + (PointN([3, 1, 1]), CubeFace::Left, FaceVertex::BottomLeft), + 0, + ), + ] + .iter() + .cloned() + .collect(); + populate_and_verify(&mut voxels, &positions, offset, &vertex_aos); offset += PointN([6, 0, 0]); // Looks like a 4-seater sofa and we want to check that the faces of the seat and backrests are // merged for the center two seats but not the left/right seats as the ambient occlusion values // on the edges are different than in the middle - // Left armrest - *voxels.get_mut(offset + PointN([0, 1, 1])) = Voxel(true); - // Seat - *voxels.get_mut(offset + PointN([1, 0, 1])) = Voxel(true); - *voxels.get_mut(offset + PointN([2, 0, 1])) = Voxel(true); - *voxels.get_mut(offset + PointN([3, 0, 1])) = Voxel(true); - *voxels.get_mut(offset + PointN([4, 0, 1])) = Voxel(true); - // Back - *voxels.get_mut(offset + PointN([1, 1, 0])) = Voxel(true); - *voxels.get_mut(offset + PointN([2, 1, 0])) = Voxel(true); - *voxels.get_mut(offset + PointN([3, 1, 0])) = Voxel(true); - *voxels.get_mut(offset + PointN([4, 1, 0])) = Voxel(true); - // Right armrest - *voxels.get_mut(offset + PointN([5, 1, 1])) = Voxel(true); + let positions = [ + // Left armrest + PointN([0, 1, 1]), + // Seat + PointN([1, 0, 1]), + PointN([2, 0, 1]), + PointN([3, 0, 1]), + PointN([4, 0, 1]), + // Back + PointN([1, 1, 0]), + PointN([2, 1, 0]), + PointN([3, 1, 0]), + PointN([4, 1, 0]), + // Right armrest + PointN([5, 1, 1]), + ]; + let vertex_aos: HashMap<(Point3i, CubeFace, FaceVertex), i32> = [ + // Left armrest + ( + (PointN([0, 1, 1]), CubeFace::Right, FaceVertex::TopRight), + 2, + ), + ( + (PointN([0, 1, 1]), CubeFace::Right, FaceVertex::BottomRight), + 0, + ), + ( + (PointN([0, 1, 1]), CubeFace::Right, FaceVertex::BottomLeft), + 2, + ), + ( + (PointN([0, 1, 1]), CubeFace::Bottom, FaceVertex::TopLeft), + 2, + ), + ( + (PointN([0, 1, 1]), CubeFace::Bottom, FaceVertex::BottomLeft), + 2, + ), + ((PointN([0, 1, 1]), CubeFace::Back, FaceVertex::TopLeft), 2), + ( + (PointN([0, 1, 1]), CubeFace::Back, FaceVertex::BottomLeft), + 2, + ), + // Left seat + ((PointN([1, 0, 1]), CubeFace::Top, FaceVertex::TopLeft), 0), + ((PointN([1, 0, 1]), CubeFace::Top, FaceVertex::TopRight), 1), + ( + (PointN([1, 0, 1]), CubeFace::Top, FaceVertex::BottomLeft), + 2, + ), + ( + (PointN([1, 0, 1]), CubeFace::Right, FaceVertex::TopRight), + 2, + ), // NOTE: This face is hidden. + ((PointN([1, 0, 1]), CubeFace::Back, FaceVertex::TopLeft), 1), + ((PointN([1, 0, 1]), CubeFace::Back, FaceVertex::TopRight), 2), + ((PointN([1, 0, 1]), CubeFace::Left, FaceVertex::TopLeft), 2), + ((PointN([1, 0, 1]), CubeFace::Left, FaceVertex::TopRight), 2), + // Second seat + ((PointN([2, 0, 1]), CubeFace::Top, FaceVertex::TopLeft), 1), + ((PointN([2, 0, 1]), CubeFace::Top, FaceVertex::TopRight), 1), + ( + (PointN([2, 0, 1]), CubeFace::Right, FaceVertex::TopRight), // NOTE: This face is hidden. + 2, + ), + ((PointN([2, 0, 1]), CubeFace::Back, FaceVertex::TopLeft), 1), + ((PointN([2, 0, 1]), CubeFace::Back, FaceVertex::TopRight), 1), + ((PointN([2, 0, 1]), CubeFace::Left, FaceVertex::TopLeft), 2), // NOTE: This face is hidden. + // Third seat + ((PointN([3, 0, 1]), CubeFace::Top, FaceVertex::TopLeft), 1), + ((PointN([3, 0, 1]), CubeFace::Top, FaceVertex::TopRight), 1), + ( + (PointN([3, 0, 1]), CubeFace::Right, FaceVertex::TopRight), // NOTE: This face is hidden. + 2, + ), + ((PointN([3, 0, 1]), CubeFace::Back, FaceVertex::TopLeft), 1), + ((PointN([3, 0, 1]), CubeFace::Back, FaceVertex::TopRight), 1), + ((PointN([3, 0, 1]), CubeFace::Left, FaceVertex::TopLeft), 2), // NOTE: This face is hidden. + // Right seat + ((PointN([4, 0, 1]), CubeFace::Top, FaceVertex::TopLeft), 1), + ((PointN([4, 0, 1]), CubeFace::Top, FaceVertex::TopRight), 0), + ( + (PointN([4, 0, 1]), CubeFace::Top, FaceVertex::BottomRight), + 2, + ), + ((PointN([4, 0, 1]), CubeFace::Right, FaceVertex::TopLeft), 2), + ( + (PointN([4, 0, 1]), CubeFace::Right, FaceVertex::TopRight), + 2, + ), + ((PointN([4, 0, 1]), CubeFace::Back, FaceVertex::TopLeft), 2), + ((PointN([4, 0, 1]), CubeFace::Back, FaceVertex::TopRight), 1), + ((PointN([4, 0, 1]), CubeFace::Left, FaceVertex::TopLeft), 2), // NOTE: This face is hidden. + // Left back + ((PointN([1, 1, 0]), CubeFace::Front, FaceVertex::TopLeft), 2), + ( + (PointN([1, 1, 0]), CubeFace::Front, FaceVertex::BottomRight), + 1, + ), + ( + (PointN([1, 1, 0]), CubeFace::Front, FaceVertex::BottomLeft), + 0, + ), + ( + (PointN([1, 1, 0]), CubeFace::Right, FaceVertex::BottomLeft), + 2, + ), // NOTE: This face is hidden. + ( + (PointN([1, 1, 0]), CubeFace::Bottom, FaceVertex::BottomRight), + 2, + ), + ( + (PointN([1, 1, 0]), CubeFace::Bottom, FaceVertex::BottomLeft), + 1, + ), + ((PointN([1, 1, 0]), CubeFace::Left, FaceVertex::TopRight), 2), + ( + (PointN([1, 1, 0]), CubeFace::Left, FaceVertex::BottomRight), + 2, + ), + // Second back + ( + (PointN([2, 1, 0]), CubeFace::Front, FaceVertex::BottomRight), + 1, + ), + ( + (PointN([2, 1, 0]), CubeFace::Front, FaceVertex::BottomLeft), + 1, + ), + ( + (PointN([2, 1, 0]), CubeFace::Right, FaceVertex::BottomLeft), + 2, + ), // NOTE: This face is hidden. + ( + (PointN([2, 1, 0]), CubeFace::Bottom, FaceVertex::BottomRight), + 1, + ), + ( + (PointN([2, 1, 0]), CubeFace::Bottom, FaceVertex::BottomLeft), + 1, + ), + ( + (PointN([2, 1, 0]), CubeFace::Left, FaceVertex::BottomRight), + 2, + ), // NOTE: This face is hidden. + // Third back + ( + (PointN([3, 1, 0]), CubeFace::Front, FaceVertex::BottomRight), + 1, + ), + ( + (PointN([3, 1, 0]), CubeFace::Front, FaceVertex::BottomLeft), + 1, + ), + ( + (PointN([3, 1, 0]), CubeFace::Right, FaceVertex::BottomLeft), + 2, + ), // NOTE: This face is hidden. + ( + (PointN([3, 1, 0]), CubeFace::Bottom, FaceVertex::BottomRight), + 1, + ), + ( + (PointN([3, 1, 0]), CubeFace::Bottom, FaceVertex::BottomLeft), + 1, + ), + ( + (PointN([3, 1, 0]), CubeFace::Left, FaceVertex::BottomRight), + 2, + ), // NOTE: This face is hidden. + // Right back + ( + (PointN([4, 1, 0]), CubeFace::Front, FaceVertex::TopRight), + 2, + ), + ( + (PointN([4, 1, 0]), CubeFace::Front, FaceVertex::BottomRight), + 0, + ), + ( + (PointN([4, 1, 0]), CubeFace::Front, FaceVertex::BottomLeft), + 1, + ), + ((PointN([4, 1, 0]), CubeFace::Right, FaceVertex::TopLeft), 2), + ( + (PointN([4, 1, 0]), CubeFace::Right, FaceVertex::BottomLeft), + 2, + ), + ( + (PointN([4, 1, 0]), CubeFace::Bottom, FaceVertex::BottomRight), + 1, + ), + ( + (PointN([4, 1, 0]), CubeFace::Bottom, FaceVertex::BottomLeft), + 2, + ), + ( + (PointN([4, 1, 0]), CubeFace::Left, FaceVertex::BottomRight), + 2, + ), // NOTE: This face is hidden. + // Right arm rest + ( + (PointN([5, 1, 1]), CubeFace::Bottom, FaceVertex::TopRight), + 2, + ), + ( + (PointN([5, 1, 1]), CubeFace::Bottom, FaceVertex::BottomRight), + 2, + ), + ((PointN([5, 1, 1]), CubeFace::Back, FaceVertex::TopRight), 2), + ( + (PointN([5, 1, 1]), CubeFace::Back, FaceVertex::BottomRight), + 2, + ), + ((PointN([5, 1, 1]), CubeFace::Left, FaceVertex::TopLeft), 2), + ( + (PointN([5, 1, 1]), CubeFace::Left, FaceVertex::BottomRight), + 2, + ), + ( + (PointN([5, 1, 1]), CubeFace::Left, FaceVertex::BottomLeft), + 0, + ), + ] + .iter() + .cloned() + .collect(); + populate_and_verify(&mut voxels, &positions, offset, &vertex_aos); offset += PointN([8, 0, 0]); - // Looks like a 2-seater sofa and we want to check that the faces of the seat and backrests are NOT merged + // Looks like a 2-seater sofa without arm rests and we want to check that the faces of the seat and backrests are NOT merged // as the ambient occlusion values on the edges are different than in the middle - // Seat - *voxels.get_mut(offset + PointN([0, 0, 1])) = Voxel(true); - *voxels.get_mut(offset + PointN([1, 0, 1])) = Voxel(true); - // Back - *voxels.get_mut(offset + PointN([0, 1, 0])) = Voxel(true); - *voxels.get_mut(offset + PointN([1, 1, 0])) = Voxel(true); + let positions = [ + // Seat + PointN([0, 0, 1]), + PointN([1, 0, 1]), + // Back + PointN([0, 1, 0]), + PointN([1, 1, 0]), + ]; + let vertex_aos: HashMap<(Point3i, CubeFace, FaceVertex), i32> = [ + // Left seat + ((PointN([0, 0, 1]), CubeFace::Top, FaceVertex::TopLeft), 2), + ((PointN([0, 0, 1]), CubeFace::Top, FaceVertex::TopRight), 1), + ( + (PointN([0, 0, 1]), CubeFace::Right, FaceVertex::TopRight), + 2, + ), // NOTE: This face is hidden. + ((PointN([0, 0, 1]), CubeFace::Back, FaceVertex::TopLeft), 1), + ((PointN([0, 0, 1]), CubeFace::Back, FaceVertex::TopRight), 2), + // Right seat + ((PointN([1, 0, 1]), CubeFace::Top, FaceVertex::TopLeft), 1), + ((PointN([1, 0, 1]), CubeFace::Top, FaceVertex::TopRight), 2), + ((PointN([1, 0, 1]), CubeFace::Back, FaceVertex::TopLeft), 2), + ((PointN([1, 0, 1]), CubeFace::Back, FaceVertex::TopRight), 1), + ((PointN([1, 0, 1]), CubeFace::Left, FaceVertex::TopLeft), 2), // NOTE: This face is hidden. + // Left back + ( + (PointN([0, 1, 0]), CubeFace::Front, FaceVertex::BottomRight), + 1, + ), + ( + (PointN([0, 1, 0]), CubeFace::Front, FaceVertex::BottomLeft), + 2, + ), + ( + (PointN([0, 1, 0]), CubeFace::Right, FaceVertex::BottomLeft), + 2, + ), // NOTE: This face is hidden. + ( + (PointN([0, 1, 0]), CubeFace::Bottom, FaceVertex::BottomRight), + 2, + ), + ( + (PointN([0, 1, 0]), CubeFace::Bottom, FaceVertex::BottomLeft), + 1, + ), + // Right back + ( + (PointN([1, 1, 0]), CubeFace::Front, FaceVertex::BottomRight), + 2, + ), + ( + (PointN([1, 1, 0]), CubeFace::Front, FaceVertex::BottomLeft), + 1, + ), + ( + (PointN([1, 1, 0]), CubeFace::Bottom, FaceVertex::BottomRight), + 1, + ), + ( + (PointN([1, 1, 0]), CubeFace::Bottom, FaceVertex::BottomLeft), + 2, + ), + ( + (PointN([1, 1, 0]), CubeFace::Left, FaceVertex::BottomRight), + 2, + ), // NOTE: This face is hidden. + ] + .iter() + .cloned() + .collect(); + populate_and_verify(&mut voxels, &positions, offset, &vertex_aos); offset += PointN([4, 0, 0]); - // Looks like a 4-seater sofa and we want to check that the faces of the seat and backrests are + // Looks like a 4-seater sofa without arm rests and we want to check that the faces of the seat and backrests are // merged for the center two seats but not the left/right seats as the ambient occlusion values // on the edges are different than in the middle - // Seat - *voxels.get_mut(offset + PointN([0, 0, 1])) = Voxel(true); - *voxels.get_mut(offset + PointN([1, 0, 1])) = Voxel(true); - *voxels.get_mut(offset + PointN([2, 0, 1])) = Voxel(true); - *voxels.get_mut(offset + PointN([3, 0, 1])) = Voxel(true); - // Back - *voxels.get_mut(offset + PointN([0, 1, 0])) = Voxel(true); - *voxels.get_mut(offset + PointN([1, 1, 0])) = Voxel(true); - *voxels.get_mut(offset + PointN([2, 1, 0])) = Voxel(true); - *voxels.get_mut(offset + PointN([3, 1, 0])) = Voxel(true); + let positions = [ + // Seat + PointN([0, 0, 1]), + PointN([1, 0, 1]), + PointN([2, 0, 1]), + PointN([3, 0, 1]), + // Back + PointN([0, 1, 0]), + PointN([1, 1, 0]), + PointN([2, 1, 0]), + PointN([3, 1, 0]), + ]; + let vertex_aos: HashMap<(Point3i, CubeFace, FaceVertex), i32> = [ + // Left seat + ((PointN([0, 0, 1]), CubeFace::Top, FaceVertex::TopLeft), 2), + ((PointN([0, 0, 1]), CubeFace::Top, FaceVertex::TopRight), 1), + ( + (PointN([0, 0, 1]), CubeFace::Right, FaceVertex::TopRight), + 2, + ), // NOTE: This face is hidden. + ((PointN([0, 0, 1]), CubeFace::Back, FaceVertex::TopLeft), 1), + ((PointN([0, 0, 1]), CubeFace::Back, FaceVertex::TopRight), 2), + // Second seat + ((PointN([1, 0, 1]), CubeFace::Top, FaceVertex::TopLeft), 1), + ((PointN([1, 0, 1]), CubeFace::Top, FaceVertex::TopRight), 1), + ( + (PointN([1, 0, 1]), CubeFace::Right, FaceVertex::TopRight), // NOTE: This face is hidden. + 2, + ), + ((PointN([1, 0, 1]), CubeFace::Back, FaceVertex::TopLeft), 1), + ((PointN([1, 0, 1]), CubeFace::Back, FaceVertex::TopRight), 1), + ((PointN([1, 0, 1]), CubeFace::Left, FaceVertex::TopLeft), 2), // NOTE: This face is hidden. + // Third seat + ((PointN([2, 0, 1]), CubeFace::Top, FaceVertex::TopLeft), 1), + ((PointN([2, 0, 1]), CubeFace::Top, FaceVertex::TopRight), 1), + ( + (PointN([2, 0, 1]), CubeFace::Right, FaceVertex::TopRight), // NOTE: This face is hidden. + 2, + ), + ((PointN([2, 0, 1]), CubeFace::Back, FaceVertex::TopLeft), 1), + ((PointN([2, 0, 1]), CubeFace::Back, FaceVertex::TopRight), 1), + ((PointN([2, 0, 1]), CubeFace::Left, FaceVertex::TopLeft), 2), // NOTE: This face is hidden. + // Right seat + ((PointN([3, 0, 1]), CubeFace::Top, FaceVertex::TopLeft), 1), + ((PointN([3, 0, 1]), CubeFace::Top, FaceVertex::TopRight), 2), + ((PointN([3, 0, 1]), CubeFace::Back, FaceVertex::TopLeft), 2), + ((PointN([3, 0, 1]), CubeFace::Back, FaceVertex::TopRight), 1), + ((PointN([3, 0, 1]), CubeFace::Left, FaceVertex::TopLeft), 2), // NOTE: This face is hidden. + // Left back + ( + (PointN([0, 1, 0]), CubeFace::Front, FaceVertex::BottomRight), + 1, + ), + ( + (PointN([0, 1, 0]), CubeFace::Front, FaceVertex::BottomLeft), + 2, + ), + ( + (PointN([0, 1, 0]), CubeFace::Right, FaceVertex::BottomLeft), + 2, + ), // NOTE: This face is hidden. + ( + (PointN([0, 1, 0]), CubeFace::Bottom, FaceVertex::BottomRight), + 2, + ), + ( + (PointN([0, 1, 0]), CubeFace::Bottom, FaceVertex::BottomLeft), + 1, + ), + // Second back + ( + (PointN([1, 1, 0]), CubeFace::Front, FaceVertex::BottomRight), + 1, + ), + ( + (PointN([1, 1, 0]), CubeFace::Front, FaceVertex::BottomLeft), + 1, + ), + ( + (PointN([1, 1, 0]), CubeFace::Right, FaceVertex::BottomLeft), + 2, + ), // NOTE: This face is hidden. + ( + (PointN([1, 1, 0]), CubeFace::Bottom, FaceVertex::BottomRight), + 1, + ), + ( + (PointN([1, 1, 0]), CubeFace::Bottom, FaceVertex::BottomLeft), + 1, + ), + ( + (PointN([1, 1, 0]), CubeFace::Left, FaceVertex::BottomRight), + 2, + ), // NOTE: This face is hidden. + // Third back + ( + (PointN([2, 1, 0]), CubeFace::Front, FaceVertex::BottomRight), + 1, + ), + ( + (PointN([2, 1, 0]), CubeFace::Front, FaceVertex::BottomLeft), + 1, + ), + ( + (PointN([2, 1, 0]), CubeFace::Right, FaceVertex::BottomLeft), + 2, + ), // NOTE: This face is hidden. + ( + (PointN([2, 1, 0]), CubeFace::Bottom, FaceVertex::BottomRight), + 1, + ), + ( + (PointN([2, 1, 0]), CubeFace::Bottom, FaceVertex::BottomLeft), + 1, + ), + ( + (PointN([2, 1, 0]), CubeFace::Left, FaceVertex::BottomRight), + 2, + ), // NOTE: This face is hidden. + // Right back + ( + (PointN([3, 1, 0]), CubeFace::Front, FaceVertex::BottomRight), + 2, + ), + ( + (PointN([3, 1, 0]), CubeFace::Front, FaceVertex::BottomLeft), + 1, + ), + ( + (PointN([3, 1, 0]), CubeFace::Bottom, FaceVertex::BottomRight), + 1, + ), + ( + (PointN([3, 1, 0]), CubeFace::Bottom, FaceVertex::BottomLeft), + 2, + ), + ( + (PointN([3, 1, 0]), CubeFace::Left, FaceVertex::BottomRight), + 2, + ), // NOTE: This face is hidden. + ] + .iter() + .cloned() + .collect(); + populate_and_verify(&mut voxels, &positions, offset, &vertex_aos); *offset.x_mut() = 1; offset += row_step; - // Looks like a 2-seater sofa and we want to check that the faces of the seat and backrests are NOT merged + // Looks like a 2-seater sofa with high back and large seat and we want to check that the faces of the seat and backrests are NOT merged // as the ambient occlusion values on the edges are different than in the middle - // Left armrest - *voxels.get_mut(offset + PointN([0, 1, 1])) = Voxel(true); - // Seat - *voxels.get_mut(offset + PointN([1, 0, 1])) = Voxel(true); - *voxels.get_mut(offset + PointN([2, 0, 1])) = Voxel(true); - *voxels.get_mut(offset + PointN([1, 0, 2])) = Voxel(true); - *voxels.get_mut(offset + PointN([2, 0, 2])) = Voxel(true); - *voxels.get_mut(offset + PointN([1, 0, 3])) = Voxel(true); - *voxels.get_mut(offset + PointN([2, 0, 3])) = Voxel(true); - // Back - *voxels.get_mut(offset + PointN([1, 1, 0])) = Voxel(true); - *voxels.get_mut(offset + PointN([2, 1, 0])) = Voxel(true); - *voxels.get_mut(offset + PointN([1, 2, 0])) = Voxel(true); - *voxels.get_mut(offset + PointN([2, 2, 0])) = Voxel(true); - *voxels.get_mut(offset + PointN([1, 3, 0])) = Voxel(true); - *voxels.get_mut(offset + PointN([2, 3, 0])) = Voxel(true); - // Right armrest - *voxels.get_mut(offset + PointN([3, 1, 1])) = Voxel(true); + let positions = [ + // Left armrest + PointN([0, 1, 1]), + // Seat + PointN([1, 0, 1]), + PointN([2, 0, 1]), + PointN([1, 0, 2]), + PointN([2, 0, 2]), + PointN([1, 0, 3]), + PointN([2, 0, 3]), + // Back + PointN([1, 1, 0]), + PointN([2, 1, 0]), + PointN([1, 2, 0]), + PointN([2, 2, 0]), + PointN([1, 3, 0]), + PointN([2, 3, 0]), + // Right armrest + PointN([3, 1, 1]), + ]; + let vertex_aos: HashMap<(Point3i, CubeFace, FaceVertex), i32> = [ + // Left armrest + ((PointN([0, 1, 1]), CubeFace::Top, FaceVertex::TopRight), 2), + ( + (PointN([0, 1, 1]), CubeFace::Front, FaceVertex::BottomRight), + 2, + ), + ( + (PointN([0, 1, 1]), CubeFace::Right, FaceVertex::TopRight), + 1, + ), + ( + (PointN([0, 1, 1]), CubeFace::Right, FaceVertex::BottomRight), + 0, + ), + ( + (PointN([0, 1, 1]), CubeFace::Right, FaceVertex::BottomLeft), + 1, + ), + ( + (PointN([0, 1, 1]), CubeFace::Bottom, FaceVertex::TopLeft), + 2, + ), + ( + (PointN([0, 1, 1]), CubeFace::Bottom, FaceVertex::BottomLeft), + 1, + ), + ((PointN([0, 1, 1]), CubeFace::Back, FaceVertex::TopLeft), 1), + ( + (PointN([0, 1, 1]), CubeFace::Back, FaceVertex::BottomLeft), + 2, + ), + // Left seat + ((PointN([1, 0, 1]), CubeFace::Top, FaceVertex::TopLeft), 0), + ((PointN([1, 0, 1]), CubeFace::Top, FaceVertex::TopRight), 1), + ( + (PointN([1, 0, 1]), CubeFace::Top, FaceVertex::BottomLeft), + 2, + ), + ( + (PointN([1, 0, 1]), CubeFace::Front, FaceVertex::TopRight), + 2, + ), // NOTE: This face is hidden. + ( + (PointN([1, 0, 1]), CubeFace::Front, FaceVertex::BottomRight), + 2, + ), // NOTE: This face is hidden. + ((PointN([1, 0, 1]), CubeFace::Right, FaceVertex::TopLeft), 2), // NOTE: This face is hidden. + ( + (PointN([1, 0, 1]), CubeFace::Right, FaceVertex::TopRight), + 2, + ), // NOTE: This face is hidden. + ( + (PointN([1, 0, 1]), CubeFace::Right, FaceVertex::BottomLeft), + 2, + ), // NOTE: This face is hidden. + ((PointN([1, 0, 1]), CubeFace::Back, FaceVertex::TopLeft), 1), + ((PointN([1, 0, 1]), CubeFace::Back, FaceVertex::TopRight), 2), + ((PointN([1, 0, 1]), CubeFace::Left, FaceVertex::TopLeft), 2), + ((PointN([1, 0, 1]), CubeFace::Left, FaceVertex::TopRight), 2), + // Right seat + ((PointN([2, 0, 1]), CubeFace::Top, FaceVertex::TopLeft), 1), + ((PointN([2, 0, 1]), CubeFace::Top, FaceVertex::TopRight), 0), + ( + (PointN([2, 0, 1]), CubeFace::Top, FaceVertex::BottomRight), + 2, + ), + ((PointN([2, 0, 1]), CubeFace::Front, FaceVertex::TopLeft), 2), + ( + (PointN([2, 0, 1]), CubeFace::Front, FaceVertex::BottomLeft), + 2, + ), + ((PointN([2, 0, 1]), CubeFace::Right, FaceVertex::TopLeft), 2), + ( + (PointN([2, 0, 1]), CubeFace::Right, FaceVertex::TopRight), + 2, + ), + ((PointN([2, 0, 1]), CubeFace::Back, FaceVertex::TopLeft), 2), + ((PointN([2, 0, 1]), CubeFace::Back, FaceVertex::TopRight), 1), + ((PointN([2, 0, 1]), CubeFace::Left, FaceVertex::TopLeft), 2), // NOTE: This face is hidden. + ((PointN([2, 0, 1]), CubeFace::Left, FaceVertex::TopRight), 2), // NOTE: This face is hidden. + ( + (PointN([2, 0, 1]), CubeFace::Left, FaceVertex::BottomRight), + 2, + ), // NOTE: This face is hidden. + // Left seat first extension forward + ((PointN([1, 0, 2]), CubeFace::Top, FaceVertex::TopLeft), 2), + ( + (PointN([1, 0, 2]), CubeFace::Front, FaceVertex::TopRight), + 2, + ), // NOTE: This face is hidden. + ( + (PointN([1, 0, 2]), CubeFace::Front, FaceVertex::BottomRight), + 2, + ), // NOTE: This face is hidden. + ((PointN([1, 0, 2]), CubeFace::Right, FaceVertex::TopLeft), 2), // NOTE: This face is hidden. + ( + (PointN([1, 0, 2]), CubeFace::Right, FaceVertex::TopRight), + 2, + ), // NOTE: This face is hidden. + ( + (PointN([1, 0, 2]), CubeFace::Right, FaceVertex::BottomRight), + 2, + ), // NOTE: This face is hidden. + ( + (PointN([1, 0, 2]), CubeFace::Right, FaceVertex::BottomLeft), + 2, + ), // NOTE: This face is hidden. + ((PointN([1, 0, 2]), CubeFace::Back, FaceVertex::TopLeft), 2), + ((PointN([1, 0, 2]), CubeFace::Back, FaceVertex::TopRight), 2), + ( + (PointN([1, 0, 2]), CubeFace::Back, FaceVertex::BottomLeft), + 2, + ), + ((PointN([1, 0, 2]), CubeFace::Left, FaceVertex::TopLeft), 2), + // Right seat first extension forward + ((PointN([2, 0, 2]), CubeFace::Top, FaceVertex::TopRight), 2), + ((PointN([2, 0, 2]), CubeFace::Front, FaceVertex::TopLeft), 2), // NOTE: This face is hidden. + ( + (PointN([2, 0, 2]), CubeFace::Front, FaceVertex::BottomLeft), + 2, + ), // NOTE: This face is hidden. + ( + (PointN([2, 0, 2]), CubeFace::Right, FaceVertex::TopRight), + 2, + ), // NOTE: This face is hidden. + ((PointN([2, 0, 2]), CubeFace::Back, FaceVertex::TopLeft), 2), + ((PointN([2, 0, 2]), CubeFace::Back, FaceVertex::TopRight), 2), + ( + (PointN([2, 0, 2]), CubeFace::Back, FaceVertex::BottomRight), + 2, + ), + ((PointN([2, 0, 2]), CubeFace::Left, FaceVertex::TopLeft), 2), + ((PointN([2, 0, 2]), CubeFace::Left, FaceVertex::TopRight), 2), + ( + (PointN([2, 0, 2]), CubeFace::Left, FaceVertex::BottomRight), + 2, + ), + ( + (PointN([2, 0, 2]), CubeFace::Left, FaceVertex::BottomLeft), + 2, + ), + // Left seat second extension forward + ( + (PointN([1, 0, 3]), CubeFace::Right, FaceVertex::TopRight), + 2, + ), // NOTE: This face is hidden. + ( + (PointN([1, 0, 3]), CubeFace::Right, FaceVertex::BottomRight), + 2, + ), // NOTE: This face is hidden. + ((PointN([1, 0, 3]), CubeFace::Back, FaceVertex::TopLeft), 2), + ( + (PointN([1, 0, 3]), CubeFace::Back, FaceVertex::BottomLeft), + 2, + ), + // Right seat second extension forward + ((PointN([2, 0, 3]), CubeFace::Back, FaceVertex::TopRight), 2), + ( + (PointN([2, 0, 3]), CubeFace::Back, FaceVertex::BottomRight), + 2, + ), + ((PointN([2, 0, 3]), CubeFace::Left, FaceVertex::TopLeft), 2), + ( + (PointN([2, 0, 3]), CubeFace::Left, FaceVertex::BottomLeft), + 2, + ), + // Left back + ((PointN([1, 1, 0]), CubeFace::Top, FaceVertex::TopRight), 2), // NOTE: This face is hidden. + ( + (PointN([1, 1, 0]), CubeFace::Top, FaceVertex::BottomRight), + 2, + ), // NOTE: This face is hidden. + ((PointN([1, 1, 0]), CubeFace::Front, FaceVertex::TopLeft), 2), + ( + (PointN([1, 1, 0]), CubeFace::Front, FaceVertex::BottomRight), + 1, + ), + ( + (PointN([1, 1, 0]), CubeFace::Front, FaceVertex::BottomLeft), + 0, + ), + ((PointN([1, 1, 0]), CubeFace::Right, FaceVertex::TopLeft), 2), // NOTE: This face is hidden. + ( + (PointN([1, 1, 0]), CubeFace::Right, FaceVertex::TopRight), + 2, + ), // NOTE: This face is hidden. + ( + (PointN([1, 1, 0]), CubeFace::Right, FaceVertex::BottomLeft), + 2, + ), // NOTE: This face is hidden. + ( + (PointN([1, 1, 0]), CubeFace::Bottom, FaceVertex::BottomRight), + 2, + ), + ( + (PointN([1, 1, 0]), CubeFace::Bottom, FaceVertex::BottomLeft), + 1, + ), + ((PointN([1, 1, 0]), CubeFace::Left, FaceVertex::TopRight), 2), + ( + (PointN([1, 1, 0]), CubeFace::Left, FaceVertex::BottomRight), + 2, + ), + // Right back + ((PointN([2, 1, 0]), CubeFace::Top, FaceVertex::TopLeft), 2), + ( + (PointN([2, 1, 0]), CubeFace::Top, FaceVertex::BottomLeft), + 2, + ), + ( + (PointN([2, 1, 0]), CubeFace::Front, FaceVertex::TopRight), + 2, + ), + ( + (PointN([2, 1, 0]), CubeFace::Front, FaceVertex::BottomRight), + 0, + ), + ( + (PointN([2, 1, 0]), CubeFace::Front, FaceVertex::BottomLeft), + 1, + ), + ((PointN([2, 1, 0]), CubeFace::Right, FaceVertex::TopLeft), 2), + ( + (PointN([2, 1, 0]), CubeFace::Right, FaceVertex::BottomLeft), + 2, + ), + ( + (PointN([2, 1, 0]), CubeFace::Bottom, FaceVertex::BottomRight), + 1, + ), + ( + (PointN([2, 1, 0]), CubeFace::Bottom, FaceVertex::BottomLeft), + 2, + ), + ((PointN([2, 1, 0]), CubeFace::Left, FaceVertex::TopLeft), 2), // NOTE: This face is hidden. + ((PointN([2, 1, 0]), CubeFace::Left, FaceVertex::TopRight), 2), // NOTE: This face is hidden. + ( + (PointN([2, 1, 0]), CubeFace::Left, FaceVertex::BottomRight), + 2, + ), // NOTE: This face is hidden. + // Left back first extension upward + ((PointN([1, 2, 0]), CubeFace::Top, FaceVertex::TopRight), 2), // NOTE: This face is hidden. + ( + (PointN([1, 2, 0]), CubeFace::Top, FaceVertex::BottomRight), + 2, + ), // NOTE: This face is hidden. + ( + (PointN([1, 2, 0]), CubeFace::Front, FaceVertex::BottomLeft), + 2, + ), + ((PointN([1, 2, 0]), CubeFace::Right, FaceVertex::TopLeft), 2), // NOTE: This face is hidden. + ( + (PointN([1, 2, 0]), CubeFace::Right, FaceVertex::TopRight), + 2, + ), // NOTE: This face is hidden. + ( + (PointN([1, 2, 0]), CubeFace::Right, FaceVertex::BottomRight), + 2, + ), // NOTE: This face is hidden. + ( + (PointN([1, 2, 0]), CubeFace::Right, FaceVertex::BottomLeft), + 2, + ), // NOTE: This face is hidden. + ( + (PointN([1, 2, 0]), CubeFace::Bottom, FaceVertex::TopLeft), + 2, + ), + ( + (PointN([1, 2, 0]), CubeFace::Bottom, FaceVertex::BottomRight), + 2, + ), + ( + (PointN([1, 2, 0]), CubeFace::Bottom, FaceVertex::BottomLeft), + 2, + ), + ( + (PointN([1, 2, 0]), CubeFace::Left, FaceVertex::BottomRight), + 2, + ), + // Right back first extension upward + ((PointN([2, 2, 0]), CubeFace::Top, FaceVertex::TopLeft), 2), + ( + (PointN([2, 2, 0]), CubeFace::Top, FaceVertex::BottomLeft), + 2, + ), + ( + (PointN([2, 2, 0]), CubeFace::Front, FaceVertex::BottomRight), + 2, + ), + ( + (PointN([2, 2, 0]), CubeFace::Right, FaceVertex::BottomLeft), + 2, + ), + ( + (PointN([2, 2, 0]), CubeFace::Bottom, FaceVertex::TopRight), + 2, + ), + ( + (PointN([2, 2, 0]), CubeFace::Bottom, FaceVertex::BottomRight), + 2, + ), + ( + (PointN([2, 2, 0]), CubeFace::Bottom, FaceVertex::BottomLeft), + 2, + ), + ((PointN([2, 2, 0]), CubeFace::Left, FaceVertex::TopLeft), 2), // NOTE: This face is hidden. + ((PointN([2, 2, 0]), CubeFace::Left, FaceVertex::TopRight), 2), // NOTE: This face is hidden. + ( + (PointN([2, 2, 0]), CubeFace::Left, FaceVertex::BottomRight), + 2, + ), // NOTE: This face is hidden. + ( + (PointN([2, 2, 0]), CubeFace::Left, FaceVertex::BottomLeft), + 2, + ), // NOTE: This face is hidden. + // Left back second extension upward + ( + (PointN([1, 3, 0]), CubeFace::Right, FaceVertex::BottomRight), + 2, + ), // NOTE: This face is hidden. + ( + (PointN([1, 3, 0]), CubeFace::Right, FaceVertex::BottomLeft), + 2, + ), // NOTE: This face is hidden. + ( + (PointN([1, 3, 0]), CubeFace::Bottom, FaceVertex::TopLeft), + 2, + ), + ( + (PointN([1, 3, 0]), CubeFace::Bottom, FaceVertex::BottomLeft), + 2, + ), + // Right back second extension upward + ( + (PointN([2, 3, 0]), CubeFace::Left, FaceVertex::BottomRight), + 2, + ), // NOTE: This face is hidden. + ( + (PointN([2, 3, 0]), CubeFace::Left, FaceVertex::BottomLeft), + 2, + ), // NOTE: This face is hidden. + ( + (PointN([2, 3, 0]), CubeFace::Bottom, FaceVertex::TopRight), + 2, + ), + ( + (PointN([2, 3, 0]), CubeFace::Bottom, FaceVertex::BottomRight), + 2, + ), + // Right arm rest + ((PointN([3, 1, 1]), CubeFace::Top, FaceVertex::TopLeft), 2), + ( + (PointN([3, 1, 1]), CubeFace::Front, FaceVertex::BottomLeft), + 2, + ), + ( + (PointN([3, 1, 1]), CubeFace::Bottom, FaceVertex::TopRight), + 2, + ), + ( + (PointN([3, 1, 1]), CubeFace::Bottom, FaceVertex::BottomRight), + 1, + ), + ((PointN([3, 1, 1]), CubeFace::Back, FaceVertex::TopRight), 1), + ( + (PointN([3, 1, 1]), CubeFace::Back, FaceVertex::BottomRight), + 2, + ), + ((PointN([3, 1, 1]), CubeFace::Left, FaceVertex::TopLeft), 1), + ( + (PointN([3, 1, 1]), CubeFace::Left, FaceVertex::BottomRight), + 1, + ), + ( + (PointN([3, 1, 1]), CubeFace::Left, FaceVertex::BottomLeft), + 0, + ), + ] + .iter() + .cloned() + .collect(); + populate_and_verify(&mut voxels, &positions, offset, &vertex_aos); offset += PointN([6, 0, 0]); // Looks like a 4-seater sofa and we want to check that the faces of the seat and backrests are @@ -411,54 +1872,10 @@ fn set_up_voxels() -> Array3x1 { *offset.x_mut() = 1; offset += row_step; + println!("<<< set_up_voxels"); voxels } -fn get_ao_at_vert_pos(v: Point3i, padded_chunk: &A, padded_chunk_extent: &Extent3i) -> i32 -where - A: Get, - T: IsEmpty + IsOpaque + MergeVoxel, -{ - let loc: Point3i = v; - - let top0_loc = PointN([loc.x() - 1, loc.y(), loc.z()]); - let top1_loc: Point3i = PointN([loc.x(), loc.y(), loc.z() - 1]); - let top2_loc: Point3i = PointN([loc.x(), loc.y(), loc.z()]); - let top3_loc: Point3i = PointN([loc.x() - 1, loc.y(), loc.z() - 1]); - - let bot0_loc: Point3i = PointN([loc.x() - 1, loc.y() - 1, loc.z()]); - let bot1_loc: Point3i = PointN([loc.x(), loc.y() - 1, loc.z() - 1]); - let bot2_loc: Point3i = PointN([loc.x(), loc.y() - 1, loc.z()]); - let bot3_loc: Point3i = PointN([loc.x() - 1, loc.y() - 1, loc.z() - 1]); - - let top0 = padded_chunk_extent.contains(top0_loc) && !padded_chunk.get(top0_loc).is_empty(); - let top1 = padded_chunk_extent.contains(top1_loc) && !padded_chunk.get(top1_loc).is_empty(); - let top2 = padded_chunk_extent.contains(top2_loc) && !padded_chunk.get(top2_loc).is_empty(); - let top3 = padded_chunk_extent.contains(top3_loc) && !padded_chunk.get(top3_loc).is_empty(); - let bot0 = padded_chunk_extent.contains(bot0_loc) && !padded_chunk.get(bot0_loc).is_empty(); - let bot1 = padded_chunk_extent.contains(bot1_loc) && !padded_chunk.get(bot1_loc).is_empty(); - let bot2 = padded_chunk_extent.contains(bot2_loc) && !padded_chunk.get(bot2_loc).is_empty(); - let bot3 = padded_chunk_extent.contains(bot3_loc) && !padded_chunk.get(bot3_loc).is_empty(); - - let (side1, side2, corner) = if !top0 && bot0 { - (top2, top3, top1) - } else if !top1 && bot1 { - (top2, top3, top0) - } else if !top2 && bot2 { - (top0, top1, top3) - } else if !top3 && bot3 { - (top0, top1, top2) - } else { - return 3; - }; - - if side1 && side2 { - return 0; - } - - return 3 - (side1 as i32 + side2 as i32 + corner as i32); -} - fn toggle_fly_camera(keyboard_input: Res>, mut fly_camera: Query<&mut FlyCamera>) { if keyboard_input.just_pressed(KeyCode::C) { for mut fc in fly_camera.iter_mut() { From 528b71f9fd1f1567cd8f0de09b0fbf51bdc66f19 Mon Sep 17 00:00:00 2001 From: Robert Swain Date: Fri, 7 May 2021 14:45:04 +0200 Subject: [PATCH 07/10] Remove unnecessary Get trait constraint --- crates/building_blocks_mesh/src/greedy_quads.rs | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/crates/building_blocks_mesh/src/greedy_quads.rs b/crates/building_blocks_mesh/src/greedy_quads.rs index ca55dc5f..6ab585d8 100644 --- a/crates/building_blocks_mesh/src/greedy_quads.rs +++ b/crates/building_blocks_mesh/src/greedy_quads.rs @@ -157,7 +157,6 @@ pub fn greedy_quads(voxels: &A, extent: &Extent3i, output: &mut GreedyQuad where A: IndexedArray<[i32; 3]> + ForEach<[i32; 3], (Point3i, Stride), Item = T> - + Get + Get, T: IsEmpty + IsOpaque + MergeVoxel, { @@ -172,7 +171,6 @@ pub fn greedy_quads_with_merge_strategy( ) where A: IndexedArray<[i32; 3]> + ForEach<[i32; 3], (Point3i, Stride), Item = T> - + Get + Get, T: IsEmpty + IsOpaque, Merger: MergeStrategy, @@ -198,7 +196,6 @@ fn greedy_quads_for_group( ) where A: IndexedArray<[i32; 3]> + ForEach<[i32; 3], (Point3i, Stride), Item = T> - + Get + Get, T: IsEmpty + IsOpaque, Merger: MergeStrategy, @@ -388,9 +385,7 @@ pub trait MergeStrategy { visited: &Array3x1, ) -> (i32, i32) where - A: IndexedArray<[i32; 3]> - + Get - + Get, + A: IndexedArray<[i32; 3]> + Get, Self::Voxel: IsEmpty + IsOpaque; } @@ -533,7 +528,7 @@ where visited: &Array3x1, ) -> (i32, i32) where - A: IndexedArray<[i32; 3]> + Get + Get, + A: IndexedArray<[i32; 3]> + Get, { println!(">>> find_quad"); // Greedily search for the biggest visible quad where all merge values are the same. @@ -685,7 +680,7 @@ impl VoxelAOMerger { max_width: i32, ) -> i32 where - A: IndexedArray<[i32; 3]> + Get + Get, + A: IndexedArray<[i32; 3]> + Get, T: IsEmpty + IsOpaque + MergeVoxel, { println!(">>> get_row_width"); @@ -860,7 +855,7 @@ pub fn get_ao_at_vert( face_vertex: FaceVertex, ) -> i32 where - A: IndexedArray<[i32; 3]> + Get + Get, + A: IndexedArray<[i32; 3]> + Get, T: IsEmpty + IsOpaque + MergeVoxel, { let (ns, us, vs, vis) = ( From 915f0744ccf89cbdf215bc35104a46ae616282cc Mon Sep 17 00:00:00 2001 From: Robert Swain Date: Fri, 7 May 2021 20:18:45 +0200 Subject: [PATCH 08/10] Add IndexedArray trait constraint to the basic voxel merger --- crates/building_blocks_mesh/src/greedy_quads.rs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/crates/building_blocks_mesh/src/greedy_quads.rs b/crates/building_blocks_mesh/src/greedy_quads.rs index 6ab585d8..702df54f 100644 --- a/crates/building_blocks_mesh/src/greedy_quads.rs +++ b/crates/building_blocks_mesh/src/greedy_quads.rs @@ -426,7 +426,7 @@ where visited: &Array3x1, ) -> (i32, i32) where - A: Get, + A: Get + IndexedArray<[i32; 3]>, { // Greedily search for the biggest visible quad where all merge values are the same. let quad_value = min_value.voxel_merge_value(); @@ -479,7 +479,7 @@ impl VoxelMerger { max_width: i32, ) -> i32 where - A: Get, + A: Get + IndexedArray<[i32; 3]>, T: IsEmpty + IsOpaque + MergeVoxel, { let mut quad_width = 0; @@ -492,9 +492,9 @@ impl VoxelMerger { let voxel = voxels.get(row_stride); - // if !face_needs_mesh(&voxel, row_stride, visibility_offset, voxels, visited) { - // break; - // } + if !face_needs_mesh(&voxel, row_stride, visibility_offset, voxels, visited) { + break; + } if !voxel.voxel_merge_value().eq(quad_merge_voxel_value) { // Voxel needs to be non-empty and match the quad merge value. From 5f9f9057e638d1452b10b561326111bce62c4507 Mon Sep 17 00:00:00 2001 From: Robert Swain Date: Sat, 8 May 2021 13:07:22 +0200 Subject: [PATCH 09/10] Rework axis orientations and get greedy quads working again --- .../building_blocks_mesh/src/greedy_quads.rs | 258 +++++++----------- crates/building_blocks_mesh/src/quad.rs | 92 +++---- .../blocky_ambient_occlusion.rs | 135 +++++---- 3 files changed, 199 insertions(+), 286 deletions(-) diff --git a/crates/building_blocks_mesh/src/greedy_quads.rs b/crates/building_blocks_mesh/src/greedy_quads.rs index 702df54f..c817fc50 100644 --- a/crates/building_blocks_mesh/src/greedy_quads.rs +++ b/crates/building_blocks_mesh/src/greedy_quads.rs @@ -50,46 +50,54 @@ pub struct QuadCoordinateConfig { pub u_flip_face: Axis3, } -pub const RIGHT_HANDED_Y_UP_CONFIG: QuadCoordinateConfig = QuadCoordinateConfig { - // Y is always in the V direction when it's not the normal. When Y is the normal, right-handedness determines that - // we must use Yzx permutations. - faces: [ - OrientedCubeFace::new(-1, Axis3Permutation::Xzy), - OrientedCubeFace::new(-1, Axis3Permutation::Yzx), - OrientedCubeFace::new(-1, Axis3Permutation::Zxy), - OrientedCubeFace::new(1, Axis3Permutation::Xzy), - OrientedCubeFace::new(1, Axis3Permutation::Yzx), - OrientedCubeFace::new(1, Axis3Permutation::Zxy), - ], - u_flip_face: Axis3::X, -}; +pub fn right_handed_y_up_config() -> QuadCoordinateConfig { + QuadCoordinateConfig { + faces: [ + // Front + OrientedCubeFace::new(PointN([1, 1, 1]), Axis3Permutation::Zxy), + // Top + OrientedCubeFace::new(PointN([1, 1, -1]), Axis3Permutation::Yxz), + // Right + OrientedCubeFace::new(PointN([1, -1, 1]), Axis3Permutation::Xzy), + // Back + OrientedCubeFace::new(PointN([-1, -1, 1]), Axis3Permutation::Zxy), + // Bottom + OrientedCubeFace::new(PointN([-1, 1, 1]), Axis3Permutation::Yxz), + // Left + OrientedCubeFace::new(PointN([-1, 1, 1]), Axis3Permutation::Xzy), + ], + u_flip_face: Axis3::X, + } +} -pub fn oriented_cube_face_to_cube_face(oriented_cube_face: OrientedCubeFace) -> CubeFace { - match oriented_cube_face.permutation { - Axis3Permutation::Zxy => { - if oriented_cube_face.n_sign > 0 { - CubeFace::Front - } else { - CubeFace::Back - } - } - Axis3Permutation::Yzx => { - if oriented_cube_face.n_sign > 0 { - CubeFace::Top - } else { - CubeFace::Bottom - } - } - Axis3Permutation::Xzy => { - if oriented_cube_face.n_sign > 0 { - CubeFace::Right - } else { - CubeFace::Left - } - } - _ => { - unreachable!(); - } +pub fn oriented_cube_face_to_face_strides( + voxels: &A, + oriented_cube_face: &OrientedCubeFace, +) -> FaceStrides +where + A: IndexedArray<[i32; 3]>, +{ + let OrientedCubeFace { n, u, v, .. } = oriented_cube_face; + let n_stride = voxels.stride_from_local_point(Local(*n)); + let u_stride = voxels.stride_from_local_point(Local(*u)); + let v_stride = voxels.stride_from_local_point(Local(*v)); + let max_stride = Stride(voxels.extent().shape.volume() as usize); + FaceStrides { + n_stride, + u_stride, + v_stride, + // The offset to the voxel sharing this cube face. + visibility_offset: n_stride, + u_offset: if u_stride > max_stride { + Stride(0) - u_stride + } else { + Stride(0) + }, + v_offset: if v_stride > max_stride { + Stride(0) - v_stride + } else { + Stride(0) + }, } } @@ -201,57 +209,43 @@ fn greedy_quads_for_group( Merger: MergeStrategy, { visited.reset_values(false); + println!("ORIENTED CUBE FACE: {:?}", quad_group.face); - let QuadGroup { - quads, - face: - OrientedCubeFace { - n_sign, - permutation, - n, - u, - v, - .. - }, - } = quad_group; - - println!("n: {:?}, u: {:?}, v: {:?}, n_sign: {}", n, u, v, n_sign); - - let [n_axis, u_axis, v_axis] = permutation.axes(); + let (n, u, v) = (quad_group.face.n, quad_group.face.u, quad_group.face.v); + let [n_axis, u_axis, v_axis] = quad_group.face.permutation.axes(); let i_n = n_axis.index(); let i_u = u_axis.index(); let i_v = v_axis.index(); let num_slices = interior.shape.at(i_n); - let slice_shape = *n + *u * interior.shape.at(i_u) + *v * interior.shape.at(i_v); - let mut slice_extent = Extent3i::from_min_and_shape(interior.minimum, slice_shape); + let (positive_n, positive_u, positive_v) = (n * n, u * u, v * v); + let slice_shape = + positive_n + positive_u * interior.shape.at(i_u) + positive_v * interior.shape.at(i_v); + let mut slice_extent = Extent3i::from_min_and_shape( + interior.minimum + + if n != positive_n { + (num_slices - 1) * positive_n + } else { + PointN([0, 0, 0]) + }, + slice_shape, + ); - let n_stride = voxels.stride_from_local_point(Local(*n)); - let u_stride = voxels.stride_from_local_point(Local(*u)); - let v_stride = voxels.stride_from_local_point(Local(*v)); - let face_strides = FaceStrides { - n_stride, - u_stride, - v_stride, - // The offset to the voxel sharing this cube face. - visibility_offset: if *n_sign > 0 { - n_stride - } else { - Stride(0) - n_stride - }, - }; + let face_strides = oriented_cube_face_to_face_strides(voxels, &quad_group.face); for _ in 0..num_slices { let slice_ub = slice_extent.least_upper_bound(); let u_ub = slice_ub.at(i_u); let v_ub = slice_ub.at(i_v); + // FIXME: The min needs to start from the correct point according to the u and v directions + // otherwise for negative u / v, no merging will happen along those directions voxels.for_each( &slice_extent, - |(quad_min, quad_min_stride): (Point3i, Stride), quad_min_voxel| { + |(voxel_min, voxel_min_stride): (Point3i, Stride), voxel| { if !face_needs_mesh( - &quad_min_voxel, - quad_min_stride, + &voxel, + voxel_min_stride, face_strides.visibility_offset, voxels, visited, @@ -261,12 +255,12 @@ fn greedy_quads_for_group( // We have at least one face that needs a mesh. We'll try to expand that face into the biggest quad we can find. // These are the boundaries on quad width and height so it is contained in the slice. - let max_width = u_ub - quad_min.at(i_u); - let max_height = v_ub - quad_min.at(i_v); + let max_width = u_ub - voxel_min.at(i_u); + let max_height = v_ub - voxel_min.at(i_v); let (quad_width, quad_height) = Merger::find_quad( - quad_min_stride, - &quad_min_voxel, + voxel_min_stride, + &voxel, max_width, max_height, &face_strides, @@ -279,12 +273,26 @@ fn greedy_quads_for_group( debug_assert!(quad_height <= max_height); // Mark the quad as visited. - let quad_extent = - Extent3i::from_min_and_shape(quad_min, *n + *u * quad_width + *v * quad_height); + let extent_minimum = voxel_min + + if u != positive_u { + u * (quad_width - 1) + } else { + PointN([0, 0, 0]) + } + + if v != positive_v { + v * (quad_height - 1) + } else { + PointN([0, 0, 0]) + }; + let quad_extent = Extent3i::from_min_and_shape( + extent_minimum, + positive_n + positive_u * quad_width + positive_v * quad_height, + ); + println!("MARKING {:?} VISITED", quad_extent); visited.fill_extent(&quad_extent, true); - quads.push(UnorientedQuad { - minimum: quad_min, + quad_group.quads.push(UnorientedQuad { + minimum: voxel_min, width: quad_width, height: quad_height, }); @@ -292,7 +300,7 @@ fn greedy_quads_for_group( ); // Move to the next slice. - slice_extent += *n; + slice_extent += n; } } @@ -395,6 +403,8 @@ pub struct FaceStrides { pub u_stride: Stride, pub v_stride: Stride, pub visibility_offset: Stride, + pub u_offset: Stride, + pub v_offset: Stride, } /// A per-voxel value used for merging quads. @@ -799,10 +809,10 @@ pub enum CubeFace { #[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] pub enum FaceVertex { - TopLeft, - TopRight, BottomLeft, BottomRight, + TopRight, + TopLeft, } fn face_strides_to_cube_face(face_strides: &FaceStrides) -> CubeFace { @@ -858,81 +868,21 @@ where A: IndexedArray<[i32; 3]> + Get, T: IsEmpty + IsOpaque + MergeVoxel, { - let (ns, us, vs, vis) = ( + let (n, u, v, vis, uoff, voff) = ( face_strides.n_stride, face_strides.u_stride, face_strides.v_stride, face_strides.visibility_offset, + face_strides.u_offset, + face_strides.v_offset, ); - let (side1_s, side2_s, corner_s) = match cube_face { - CubeFace::Top => match face_vertex { - FaceVertex::TopLeft => (stride + vis - vs, stride + vis - us, stride + vis - us - vs), - FaceVertex::TopRight => (stride + vis - us, stride + vis + vs, stride + vis - us + vs), - FaceVertex::BottomLeft => { - (stride + vis + us, stride + vis - vs, stride + vis + us - vs) - } - FaceVertex::BottomRight => { - (stride + vis + vs, stride + vis + us, stride + vis + us + vs) - } - }, - CubeFace::Bottom => match face_vertex { - FaceVertex::TopLeft => (stride + vis + vs, stride + vis - us, stride + vis - us + vs), - FaceVertex::TopRight => (stride + vis - us, stride + vis - vs, stride + vis - us - vs), - FaceVertex::BottomLeft => { - (stride + vis + us, stride + vis + vs, stride + vis + us + vs) - } - FaceVertex::BottomRight => { - (stride + vis - vs, stride + vis + us, stride + vis + us - vs) - } - }, - CubeFace::Left => match face_vertex { - FaceVertex::TopLeft => (stride + vis + vs, stride + vis - us, stride + vis - us + vs), - FaceVertex::TopRight => (stride + vis + us, stride + vis + vs, stride + vis + us + vs), - FaceVertex::BottomLeft => { - (stride + vis - us, stride + vis - vs, stride + vis - us - vs) - } - FaceVertex::BottomRight => { - (stride + vis - vs, stride + vis + us, stride + vis + us - vs) - } - }, - CubeFace::Right => match face_vertex { - FaceVertex::TopLeft => (stride + vis + us, stride + vis + vs, stride + vis + us + vs), - FaceVertex::TopRight => (stride + vis + vs, stride + vis - us, stride + vis - us + vs), - FaceVertex::BottomLeft => { - (stride + vis - vs, stride + vis + us, stride + vis + us - vs) - } - FaceVertex::BottomRight => { - (stride + vis - us, stride + vis - vs, stride + vis - us - vs) - } - }, - CubeFace::Back => match face_vertex { - FaceVertex::TopLeft => (stride + vis + us, stride + vis + vs, stride + vis + us + vs), - FaceVertex::TopRight => (stride + vis + vs, stride + vis - us, stride + vis - us + vs), - FaceVertex::BottomLeft => { - (stride + vis - vs, stride + vis + us, stride + vis + us - vs) - } - FaceVertex::BottomRight => { - (stride + vis - us, stride + vis - vs, stride + vis - us - vs) - } - }, - CubeFace::Front => match face_vertex { - FaceVertex::TopLeft => (stride + vis - us, stride + vis + vs, stride + vis - us + vs), - FaceVertex::TopRight => (stride + vis + vs, stride + vis + us, stride + vis + us + vs), - FaceVertex::BottomLeft => { - (stride + vis - vs, stride + vis - us, stride + vis - us - vs) - } - FaceVertex::BottomRight => { - (stride + vis + us, stride + vis - vs, stride + vis + us - vs) - } - }, + let s = stride + vis + uoff + voff; + let (side1_s, side2_s, corner_s) = match face_vertex { + FaceVertex::BottomLeft => (s - v, s - u, s - u - v), + FaceVertex::BottomRight => (s + u, s - v, s + u - v), + FaceVertex::TopRight => (s + v, s + u, s + u + v), + FaceVertex::TopLeft => (s - u, s + v, s - u + v), }; - // println!( - // "side1: {:?}, side2: {:?}, corner: {:?}", - // stride_to_point(voxels.extent(), side1_s), - // stride_to_point(voxels.extent(), side2_s), - // stride_to_point(voxels.extent(), corner_s) - // ); - let (side1, side2, corner) = ( !voxels.get(side1_s).is_empty(), !voxels.get(side2_s).is_empty(), diff --git a/crates/building_blocks_mesh/src/quad.rs b/crates/building_blocks_mesh/src/quad.rs index 0dc06838..aa08000e 100644 --- a/crates/building_blocks_mesh/src/quad.rs +++ b/crates/building_blocks_mesh/src/quad.rs @@ -8,37 +8,33 @@ use building_blocks_core::{ /// Metadata that's used to aid in the geometric calculations for one of the 6 possible cube faces. #[derive(Clone, Copy, Debug, Eq, PartialEq)] pub struct OrientedCubeFace { - /// Determines the orientation of the plane. - pub n_sign: i32, - /// Determines the {N, U, V} <--> {X, Y, Z} relation. pub permutation: Axis3Permutation, - /// First in the `permutation` of +X, +Y, and +Z. + /// Direction of the face normal pub n: Point3i, - /// Second in the `permutation` of +X, +Y, and +Z. + /// Direction of the face-space right-pointing U axis pub u: Point3i, - /// Third in the `permutation` of +X, +Y, and +Z. + /// Direction of the face-space up-pointing V axis pub v: Point3i, } impl OrientedCubeFace { - pub const fn new(n_sign: i32, permutation: Axis3Permutation) -> Self { + pub fn new(axis_signs: Point3i, permutation: Axis3Permutation) -> Self { let [n_axis, u_axis, v_axis] = permutation.axes(); Self { - n_sign, permutation, - n: n_axis.get_unit_vector(), - u: u_axis.get_unit_vector(), - v: v_axis.get_unit_vector(), + n: axis_signs.x() * n_axis.get_unit_vector(), + u: axis_signs.y() * u_axis.get_unit_vector(), + v: axis_signs.z() * v_axis.get_unit_vector(), } } /// A cube face, using axes with an even permutation. pub fn canonical(normal: SignedAxis3) -> Self { Self::new( - normal.sign, + PointN([1, 1, 1]), Axis3Permutation::even_with_normal_axis(normal.axis), ) } @@ -56,7 +52,7 @@ impl OrientedCubeFace { } pub fn signed_normal(&self) -> Point3i { - self.n * self.n_sign + self.n } pub fn mesh_normal(&self) -> Point3f { @@ -82,11 +78,18 @@ impl OrientedCubeFace { let w_vec = self.u * quad.width; let h_vec = self.v * quad.height; - let minu_minv = if self.n_sign > 0 { - quad.minimum + self.n - } else { - quad.minimum - }; + let minu_minv = quad.minimum + + if self.n.x() == 1 { + self.n - self.u + } else if self.n.y() == 1 { + self.n - self.v + } else if self.n.z() == 1 { + self.n + } else if self.n.z() == -1 { + -self.u + } else { + PointN([0, 0, 0]) + }; let maxu_minv = minu_minv + w_vec; let minu_maxv = minu_minv + h_vec; let maxu_maxv = minu_minv + w_vec + h_vec; @@ -113,14 +116,16 @@ impl OrientedCubeFace { /// sign of the surface normal and the permutation of the UVs. Swapping the diagonal can be used for altering the triangle /// orientation to achieve more isotropic interpolation of vertex values such as ambient occlusion. pub fn quad_mesh_indices(&self, start: u32, swap_diagonal: bool) -> [u32; 6] { - quad_indices(start, self.n_sign * self.permutation.sign() > 0, swap_diagonal) + quad_indices( + start, + true, + swap_diagonal, + ) } /// Returns the UV coordinates of the 4 corners of the quad. Returns vertices in the same order as /// `OrientedCubeFace::quad_corners`. /// - /// `u_flip_face` should correspond to the field on `QuadCoordinateConfig`. See the docs there for more info. - /// /// This is just one way of assigning UVs to voxel quads. It assumes that each material has a single tile texture with /// wrapping coordinates, and each voxel face should show the entire texture. It also assumes a particular orientation for /// the texture. This should be sufficient for minecraft-style meshing. @@ -128,43 +133,14 @@ impl OrientedCubeFace { /// If you need to use a texture atlas, you must calculate your own coordinates from the `Quad`. pub fn tex_coords( &self, - u_flip_face: Axis3, - flip_v: bool, quad: &UnorientedQuad, ) -> [[f32; 2]; 4] { - let face_normal_axis = self.permutation.axes()[0]; - let flip_u = if self.n_sign < 0 { - u_flip_face != face_normal_axis - } else { - u_flip_face == face_normal_axis - }; - - match (flip_u, flip_v) { - (false, false) => [ - [0.0, 0.0], - [quad.width as f32, 0.0], - [0.0, quad.height as f32], - [quad.width as f32, quad.height as f32], - ], - (true, false) => [ - [quad.width as f32, 0.0], - [0.0, 0.0], - [quad.width as f32, quad.height as f32], - [0.0, quad.height as f32], - ], - (false, true) => [ - [0.0, quad.height as f32], - [quad.width as f32, quad.height as f32], - [0.0, 0.0], - [quad.width as f32, 0.0], - ], - (true, true) => [ - [quad.width as f32, quad.height as f32], - [0.0, quad.height as f32], - [quad.width as f32, 0.0], - [0.0, 0.0], - ], - } + [ + [0.0, 0.0], + [quad.width as f32, 0.0], + [0.0, quad.height as f32], + [quad.width as f32, quad.height as f32], + ] } /// Extends `mesh` with the given `quad` that belongs to this face. @@ -187,8 +163,6 @@ impl OrientedCubeFace { /// The texture coordinates come from `Quad::tex_coords`. pub fn add_quad_to_pos_norm_tex_mesh( &self, - u_flip_face: Axis3, - flip_v: bool, quad: &UnorientedQuad, voxel_size: f32, mesh: &mut PosNormTexMesh, @@ -198,7 +172,7 @@ impl OrientedCubeFace { .extend_from_slice(&self.quad_mesh_positions(quad, voxel_size)); mesh.normals.extend_from_slice(&self.quad_mesh_normals()); mesh.tex_coords - .extend_from_slice(&self.tex_coords(u_flip_face, flip_v, quad)); + .extend_from_slice(&self.tex_coords(quad)); mesh.indices .extend_from_slice(&self.quad_mesh_indices(start_index, false)); } diff --git a/examples/blocky_ambient_occlusion/blocky_ambient_occlusion.rs b/examples/blocky_ambient_occlusion/blocky_ambient_occlusion.rs index 82cee4c3..548f360a 100644 --- a/examples/blocky_ambient_occlusion/blocky_ambient_occlusion.rs +++ b/examples/blocky_ambient_occlusion/blocky_ambient_occlusion.rs @@ -15,16 +15,17 @@ use bevy::{ use bevy_fly_camera::{FlyCamera, FlyCameraPlugin}; use building_blocks::core::prelude::*; use building_blocks::mesh::{ - greedy_quads_with_merge_strategy, GreedyQuadsBuffer, IsOpaque, MergeVoxel, PosNormTexMesh, - VoxelAOMerger, RIGHT_HANDED_Y_UP_CONFIG, + greedy_quads_with_merge_strategy, right_handed_y_up_config, GreedyQuadsBuffer, IsOpaque, + MergeVoxel, PosNormTexMesh, VoxelAOMerger, }; use building_blocks::storage::{ access_traits::{Get, GetMut}, Array3x1, IsEmpty, }; +use building_blocks_core::Axis3Permutation; use building_blocks_mesh::{ - get_ao_at_vert, oriented_cube_face_to_cube_face, CubeFace, FaceStrides, FaceVertex, - OrientedCubeFace, QuadGroup, UnorientedQuad, + get_ao_at_vert, greedy_quads, oriented_cube_face_to_face_strides, CubeFace, FaceStrides, + FaceVertex, OrientedCubeFace, QuadGroup, UnorientedQuad, }; use building_blocks_storage::{IndexedArray, Local, Stride}; @@ -144,7 +145,6 @@ impl MeshBuf { face: &OrientedCubeFace, quad: &UnorientedQuad, voxel_size: f32, - u_flip_face: Axis3, ambient_occlusions: &[f32; 4], ) { let start_index = self.positions.len() as u32; @@ -152,9 +152,8 @@ impl MeshBuf { .extend_from_slice(&face.quad_mesh_positions(quad, voxel_size)); self.normals.extend_from_slice(&face.quad_mesh_normals()); - let flip_v = true; self.tex_coords - .extend_from_slice(&face.tex_coords(u_flip_face, flip_v, quad)); + .extend_from_slice(&face.tex_coords(quad)); self.ambient_occlusion.extend_from_slice(ambient_occlusions); self.indices.extend_from_slice(&face.quad_mesh_indices( @@ -166,52 +165,38 @@ impl MeshBuf { } } -fn cube_face_to_face_strides(voxels: &A, cube_face: CubeFace) -> FaceStrides -where - A: IndexedArray<[i32; 3]>, -{ - let (xs, ys, zs) = ( - voxels.stride_from_local_point(Local(PointN([1, 0, 0]))), - voxels.stride_from_local_point(Local(PointN([0, 1, 0]))), - voxels.stride_from_local_point(Local(PointN([0, 0, 1]))), - ); - match cube_face { - CubeFace::Top => FaceStrides { - n_stride: ys, - u_stride: zs, - v_stride: xs, - visibility_offset: ys, - }, - CubeFace::Bottom => FaceStrides { - n_stride: ys, - u_stride: zs, - v_stride: xs, - visibility_offset: Stride(0) - ys, - }, - CubeFace::Left => FaceStrides { - n_stride: xs, - u_stride: zs, - v_stride: ys, - visibility_offset: Stride(0) - xs, - }, - CubeFace::Right => FaceStrides { - n_stride: xs, - u_stride: zs, - v_stride: ys, - visibility_offset: xs, - }, - CubeFace::Back => FaceStrides { - n_stride: zs, - u_stride: xs, - v_stride: ys, - visibility_offset: Stride(0) - zs, - }, - CubeFace::Front => FaceStrides { - n_stride: zs, - u_stride: xs, - v_stride: ys, - visibility_offset: zs, - }, +fn oriented_cube_face_to_cube_face(oriented_cube_face: OrientedCubeFace) -> CubeFace { + match oriented_cube_face.permutation { + Axis3Permutation::Zxy => { + if oriented_cube_face.n.z() == 1 { + CubeFace::Front + } else if oriented_cube_face.n.z() == -1 { + CubeFace::Back + } else { + unreachable!(); + } + } + Axis3Permutation::Xzy => { + if oriented_cube_face.n.x() == 1 { + CubeFace::Top + } else if oriented_cube_face.n.x() == -1 { + CubeFace::Bottom + } else { + unreachable!(); + } + } + Axis3Permutation::Yxz => { + if oriented_cube_face.n.y() == 1 { + CubeFace::Top + } else if oriented_cube_face.n.y() == -1 { + CubeFace::Bottom + } else { + unreachable!(); + } + } + _ => { + unreachable!(); + } } } @@ -224,27 +209,26 @@ fn setup( ) { let voxels = set_up_voxels(); + let quad_coordinate_config = right_handed_y_up_config(); + let mut greedy_buffer = - GreedyQuadsBuffer::new(*voxels.extent(), RIGHT_HANDED_Y_UP_CONFIG.quad_groups()); - greedy_quads_with_merge_strategy::<_, _, VoxelAOMerger>( - &voxels, - voxels.extent(), - &mut greedy_buffer, - ); + GreedyQuadsBuffer::new(*voxels.extent(), quad_coordinate_config.quad_groups()); + greedy_quads(&voxels, voxels.extent(), &mut greedy_buffer); + // greedy_quads_with_merge_strategy::<_, _, VoxelAOMerger>( + // &voxels, + // voxels.extent(), + // &mut greedy_buffer, + // ); - let flip_v = true; let voxel_size = 1.0; let mut mesh_buf = MeshBuf::default(); for group in greedy_buffer.quad_groups.iter() { let cube_face = oriented_cube_face_to_cube_face(group.face); - let face_strides = cube_face_to_face_strides(&voxels, cube_face); + let face_strides = oriented_cube_face_to_face_strides(&voxels, &group.face); for quad in group.quads.iter() { let stride = voxels.stride_from_local_point(Local(quad.minimum - voxels.extent().minimum)); println!("quad corners: {:?}", group.face.quad_corners(quad)); - let corners = cube_face_to_quad_corners_as_face_vertices( - oriented_cube_face_to_cube_face(group.face), - ); // FIXME - THESE HAVE TO USE THE QUAD WIDTH AND QUAD HEIGHT!!! let ao_values = [ get_ao_at_vert( @@ -252,35 +236,34 @@ fn setup( &face_strides, stride, cube_face, - corners[0], // FIXME: SHOULD BE UV 0,0 + FaceVertex::BottomLeft, ) as f32, get_ao_at_vert( &voxels, &face_strides, stride, cube_face, - corners[1], // FIXME: SHOULD BE UV 1,0 + FaceVertex::BottomRight, ) as f32, get_ao_at_vert( &voxels, &face_strides, stride, cube_face, - corners[2], // FIXME: SHOULD BE UV 0,1 + FaceVertex::TopLeft, ) as f32, get_ao_at_vert( &voxels, &face_strides, stride, cube_face, - corners[3], // FIXME: SHOULD BE UV 1,1 + FaceVertex::TopRight, ) as f32, ]; mesh_buf.add_quad( &group.face, quad, voxel_size, - RIGHT_HANDED_Y_UP_CONFIG.u_flip_face, &ao_values, ); } @@ -389,11 +372,11 @@ fn setup( } const CUBE_FACES: [CubeFace; 6] = [ - CubeFace::Top, CubeFace::Front, + CubeFace::Top, CubeFace::Right, - CubeFace::Bottom, CubeFace::Back, + CubeFace::Bottom, CubeFace::Left, ]; @@ -409,15 +392,20 @@ fn populate_and_verify( positions: &[Point3i], offset: Point3i, vertex_aos: &HashMap<(Point3i, CubeFace, FaceVertex), i32>, + verify: bool, ) { + let quad_coordinate_config = right_handed_y_up_config(); for position in positions { *voxels.get_mut(offset + *position) = Voxel(true); } + if verify { for position in positions { let stride = voxels.stride_from_local_point(Local(offset + *position - voxels.extent().minimum)); - for cube_face in &CUBE_FACES { - let face_strides = cube_face_to_face_strides(&*voxels, *cube_face); + for (cube_face, oriented_cube_face) in + CUBE_FACES.iter().zip(quad_coordinate_config.faces.iter()) + { + let face_strides = oriented_cube_face_to_face_strides(&*voxels, oriented_cube_face); for face_vertex in &FACE_VERTICES { assert!( get_ao_at_vert(&*voxels, &face_strides, stride, *cube_face, *face_vertex) @@ -429,6 +417,7 @@ fn populate_and_verify( } } } +} fn set_up_voxels() -> Array3x1 { println!(">>> set_up_voxels"); From 167000ce5decefd498a14b6cd5e6bf40093dcb9b Mon Sep 17 00:00:00 2001 From: Robert Swain Date: Sat, 8 May 2021 13:07:38 +0200 Subject: [PATCH 10/10] Make test selection and verification more manageable --- .../blocky_ambient_occlusion.rs | 2854 +++++++++-------- 1 file changed, 1451 insertions(+), 1403 deletions(-) diff --git a/examples/blocky_ambient_occlusion/blocky_ambient_occlusion.rs b/examples/blocky_ambient_occlusion/blocky_ambient_occlusion.rs index 548f360a..b5c64e00 100644 --- a/examples/blocky_ambient_occlusion/blocky_ambient_occlusion.rs +++ b/examples/blocky_ambient_occlusion/blocky_ambient_occlusion.rs @@ -399,25 +399,25 @@ fn populate_and_verify( *voxels.get_mut(offset + *position) = Voxel(true); } if verify { - for position in positions { - let stride = - voxels.stride_from_local_point(Local(offset + *position - voxels.extent().minimum)); + for position in positions { + let stride = + voxels.stride_from_local_point(Local(offset + *position - voxels.extent().minimum)); for (cube_face, oriented_cube_face) in CUBE_FACES.iter().zip(quad_coordinate_config.faces.iter()) { let face_strides = oriented_cube_face_to_face_strides(&*voxels, oriented_cube_face); - for face_vertex in &FACE_VERTICES { - assert!( - get_ao_at_vert(&*voxels, &face_strides, stride, *cube_face, *face_vertex) - == *vertex_aos - .get(&(*position, *cube_face, *face_vertex)) - .unwrap_or(&3) - ); + for face_vertex in &FACE_VERTICES { + assert!( + get_ao_at_vert(&*voxels, &face_strides, stride, *cube_face, *face_vertex) + == *vertex_aos + .get(&(*position, *cube_face, *face_vertex)) + .unwrap_or(&3) + ); + } } } } } -} fn set_up_voxels() -> Array3x1 { println!(">>> set_up_voxels"); @@ -429,1437 +429,1485 @@ fn set_up_voxels() -> Array3x1 { let col_step = PointN([5, 0, 0]); let mut offset = PointN([1, 1, 1]); + let tests = 0xffff; // 1 << 4; + let mut shift = 0; + let verify = false; // . - let positions = [PointN([1, 1, 1])]; - populate_and_verify(&mut voxels, &positions, offset, &HashMap::new()); - offset += row_step; + if (tests >> shift) & 1 == 1 { + let positions = [PointN([1, 1, 1])]; + populate_and_verify(&mut voxels, &positions, offset, &HashMap::new(), verify); + offset += row_step; + } + shift += 1; // . . . // . . . - let positions = [PointN([1, 1, 1]), PointN([0, 2, 1])]; - let vertex_aos: HashMap<(Point3i, CubeFace, FaceVertex), i32> = [ - ((PointN([1, 1, 1]), CubeFace::Top, FaceVertex::TopLeft), 2), - ( - (PointN([1, 1, 1]), CubeFace::Top, FaceVertex::BottomLeft), - 2, - ), - ((PointN([1, 1, 1]), CubeFace::Left, FaceVertex::TopLeft), 2), - ((PointN([1, 1, 1]), CubeFace::Left, FaceVertex::TopRight), 2), - ( - (PointN([0, 2, 1]), CubeFace::Right, FaceVertex::BottomRight), - 2, - ), - ( - (PointN([0, 2, 1]), CubeFace::Right, FaceVertex::BottomLeft), - 2, - ), - ( - (PointN([0, 2, 1]), CubeFace::Bottom, FaceVertex::TopLeft), - 2, - ), - ( - (PointN([0, 2, 1]), CubeFace::Bottom, FaceVertex::BottomLeft), - 2, - ), - ] - .iter() - .cloned() - .collect(); - populate_and_verify(&mut voxels, &positions, offset, &vertex_aos); - offset += col_step; + if (tests >> shift) & 1 == 1 { + let positions = [PointN([1, 1, 1]), PointN([0, 2, 1])]; + let vertex_aos: HashMap<(Point3i, CubeFace, FaceVertex), i32> = [ + ((PointN([1, 1, 1]), CubeFace::Top, FaceVertex::TopLeft), 2), + ( + (PointN([1, 1, 1]), CubeFace::Top, FaceVertex::BottomLeft), + 2, + ), + ((PointN([1, 1, 1]), CubeFace::Left, FaceVertex::TopLeft), 2), + ((PointN([1, 1, 1]), CubeFace::Left, FaceVertex::TopRight), 2), + ( + (PointN([0, 2, 1]), CubeFace::Right, FaceVertex::BottomRight), + 2, + ), + ( + (PointN([0, 2, 1]), CubeFace::Right, FaceVertex::BottomLeft), + 2, + ), + ( + (PointN([0, 2, 1]), CubeFace::Bottom, FaceVertex::TopLeft), + 2, + ), + ( + (PointN([0, 2, 1]), CubeFace::Bottom, FaceVertex::BottomLeft), + 2, + ), + ] + .iter() + .cloned() + .collect(); + populate_and_verify(&mut voxels, &positions, offset, &vertex_aos, verify); + offset += col_step; + } + shift += 1; - let positions = [PointN([1, 1, 1]), PointN([0, 2, 0])]; - let vertex_aos: HashMap<(Point3i, CubeFace, FaceVertex), i32> = [ - ((PointN([1, 1, 1]), CubeFace::Top, FaceVertex::TopLeft), 2), - ((PointN([1, 1, 1]), CubeFace::Back, FaceVertex::TopRight), 2), - ((PointN([1, 1, 1]), CubeFace::Left, FaceVertex::TopLeft), 2), - ( - (PointN([0, 2, 0]), CubeFace::Front, FaceVertex::BottomRight), - 2, - ), - ( - (PointN([0, 2, 0]), CubeFace::Right, FaceVertex::BottomLeft), - 2, - ), - ( - (PointN([0, 2, 0]), CubeFace::Bottom, FaceVertex::BottomLeft), - 2, - ), - ] - .iter() - .cloned() - .collect(); - populate_and_verify(&mut voxels, &positions, offset, &vertex_aos); - offset += col_step; + if (tests >> shift) & 1 == 1 { + let positions = [PointN([1, 1, 1]), PointN([0, 2, 0])]; + let vertex_aos: HashMap<(Point3i, CubeFace, FaceVertex), i32> = [ + ((PointN([1, 1, 1]), CubeFace::Top, FaceVertex::TopLeft), 2), + ((PointN([1, 1, 1]), CubeFace::Back, FaceVertex::TopRight), 2), + ((PointN([1, 1, 1]), CubeFace::Left, FaceVertex::TopLeft), 2), + ( + (PointN([0, 2, 0]), CubeFace::Front, FaceVertex::BottomRight), + 2, + ), + ( + (PointN([0, 2, 0]), CubeFace::Right, FaceVertex::BottomLeft), + 2, + ), + ( + (PointN([0, 2, 0]), CubeFace::Bottom, FaceVertex::BottomLeft), + 2, + ), + ] + .iter() + .cloned() + .collect(); + populate_and_verify(&mut voxels, &positions, offset, &vertex_aos, verify); + offset += col_step; + } + shift += 1; - let positions = [PointN([1, 1, 1]), PointN([1, 2, 0])]; - let vertex_aos: HashMap<(Point3i, CubeFace, FaceVertex), i32> = [ - ((PointN([1, 1, 1]), CubeFace::Top, FaceVertex::TopLeft), 2), - ((PointN([1, 1, 1]), CubeFace::Top, FaceVertex::TopRight), 2), - ((PointN([1, 1, 1]), CubeFace::Back, FaceVertex::TopLeft), 2), - ((PointN([1, 1, 1]), CubeFace::Back, FaceVertex::TopRight), 2), - ( - (PointN([1, 2, 0]), CubeFace::Front, FaceVertex::BottomRight), - 2, - ), - ( - (PointN([1, 2, 0]), CubeFace::Front, FaceVertex::BottomLeft), - 2, - ), - ( - (PointN([1, 2, 0]), CubeFace::Bottom, FaceVertex::BottomRight), - 2, - ), - ( - (PointN([1, 2, 0]), CubeFace::Bottom, FaceVertex::BottomLeft), - 2, - ), - ] - .iter() - .cloned() - .collect(); - populate_and_verify(&mut voxels, &positions, offset, &vertex_aos); - *offset.x_mut() = 1; - offset += row_step; + if (tests >> shift) & 1 == 1 { + let positions = [PointN([1, 1, 1]), PointN([1, 2, 0])]; + let vertex_aos: HashMap<(Point3i, CubeFace, FaceVertex), i32> = [ + ((PointN([1, 1, 1]), CubeFace::Top, FaceVertex::TopLeft), 2), + ((PointN([1, 1, 1]), CubeFace::Top, FaceVertex::TopRight), 2), + ((PointN([1, 1, 1]), CubeFace::Back, FaceVertex::TopLeft), 2), + ((PointN([1, 1, 1]), CubeFace::Back, FaceVertex::TopRight), 2), + ( + (PointN([1, 2, 0]), CubeFace::Front, FaceVertex::BottomRight), + 2, + ), + ( + (PointN([1, 2, 0]), CubeFace::Front, FaceVertex::BottomLeft), + 2, + ), + ( + (PointN([1, 2, 0]), CubeFace::Bottom, FaceVertex::BottomRight), + 2, + ), + ( + (PointN([1, 2, 0]), CubeFace::Bottom, FaceVertex::BottomLeft), + 2, + ), + ] + .iter() + .cloned() + .collect(); + populate_and_verify(&mut voxels, &positions, offset, &vertex_aos, verify); + *offset.x_mut() = 1; + offset += row_step; + } + shift += 1; // .. .. // . . - let positions = [PointN([1, 1, 1]), PointN([0, 2, 0]), PointN([0, 2, 1])]; - let vertex_aos: HashMap<(Point3i, CubeFace, FaceVertex), i32> = [ - ((PointN([1, 1, 1]), CubeFace::Top, FaceVertex::TopLeft), 1), - ( - (PointN([1, 1, 1]), CubeFace::Top, FaceVertex::BottomLeft), - 2, - ), - ((PointN([1, 1, 1]), CubeFace::Back, FaceVertex::TopRight), 2), - ((PointN([1, 1, 1]), CubeFace::Left, FaceVertex::TopRight), 2), - ((PointN([1, 1, 1]), CubeFace::Left, FaceVertex::TopLeft), 1), - ( - (PointN([0, 2, 0]), CubeFace::Front, FaceVertex::BottomRight), - 2, - ), // NOTE: This is a hidden face - ( - (PointN([0, 2, 0]), CubeFace::Right, FaceVertex::BottomLeft), - 2, - ), - ( - (PointN([0, 2, 0]), CubeFace::Bottom, FaceVertex::BottomLeft), - 2, - ), - ( - (PointN([0, 2, 1]), CubeFace::Right, FaceVertex::BottomRight), - 2, - ), - ( - (PointN([0, 2, 1]), CubeFace::Right, FaceVertex::BottomLeft), - 2, - ), - ( - (PointN([0, 2, 1]), CubeFace::Bottom, FaceVertex::TopLeft), - 2, - ), - ( - (PointN([0, 2, 1]), CubeFace::Bottom, FaceVertex::BottomLeft), - 2, - ), - ] - .iter() - .cloned() - .collect(); - populate_and_verify(&mut voxels, &positions, offset, &vertex_aos); - offset += col_step; + if (tests >> shift) & 1 == 1 { + let positions = [PointN([1, 1, 1]), PointN([0, 2, 0]), PointN([0, 2, 1])]; + let vertex_aos: HashMap<(Point3i, CubeFace, FaceVertex), i32> = [ + ((PointN([1, 1, 1]), CubeFace::Top, FaceVertex::TopLeft), 1), + ( + (PointN([1, 1, 1]), CubeFace::Top, FaceVertex::BottomLeft), + 2, + ), + ((PointN([1, 1, 1]), CubeFace::Back, FaceVertex::TopRight), 2), + ((PointN([1, 1, 1]), CubeFace::Left, FaceVertex::TopRight), 2), + ((PointN([1, 1, 1]), CubeFace::Left, FaceVertex::TopLeft), 1), + ( + (PointN([0, 2, 0]), CubeFace::Front, FaceVertex::BottomRight), + 2, + ), // NOTE: This is a hidden face + ( + (PointN([0, 2, 0]), CubeFace::Right, FaceVertex::BottomLeft), + 2, + ), + ( + (PointN([0, 2, 0]), CubeFace::Bottom, FaceVertex::BottomLeft), + 2, + ), + ( + (PointN([0, 2, 1]), CubeFace::Right, FaceVertex::BottomRight), + 2, + ), + ( + (PointN([0, 2, 1]), CubeFace::Right, FaceVertex::BottomLeft), + 2, + ), + ( + (PointN([0, 2, 1]), CubeFace::Bottom, FaceVertex::TopLeft), + 2, + ), + ( + (PointN([0, 2, 1]), CubeFace::Bottom, FaceVertex::BottomLeft), + 2, + ), + ] + .iter() + .cloned() + .collect(); + populate_and_verify(&mut voxels, &positions, offset, &vertex_aos, verify); + offset += col_step; + } + shift += 1; - let positions = [PointN([1, 1, 1]), PointN([0, 2, 0]), PointN([1, 2, 0])]; - let vertex_aos: HashMap<(Point3i, CubeFace, FaceVertex), i32> = [ - ((PointN([1, 1, 1]), CubeFace::Top, FaceVertex::TopLeft), 1), - ((PointN([1, 1, 1]), CubeFace::Top, FaceVertex::TopRight), 2), - ((PointN([1, 1, 1]), CubeFace::Back, FaceVertex::TopLeft), 2), - ((PointN([1, 1, 1]), CubeFace::Back, FaceVertex::TopRight), 1), - ((PointN([1, 1, 1]), CubeFace::Left, FaceVertex::TopLeft), 2), - ( - (PointN([0, 2, 0]), CubeFace::Front, FaceVertex::BottomRight), - 2, - ), - ( - (PointN([0, 2, 0]), CubeFace::Right, FaceVertex::BottomLeft), - 2, - ), // NOTE: This is a hidden face - ( - (PointN([0, 2, 0]), CubeFace::Bottom, FaceVertex::BottomLeft), - 2, - ), - ( - (PointN([1, 2, 0]), CubeFace::Front, FaceVertex::BottomRight), - 2, - ), - ( - (PointN([1, 2, 0]), CubeFace::Front, FaceVertex::BottomLeft), - 2, - ), - ( - (PointN([1, 2, 0]), CubeFace::Bottom, FaceVertex::BottomRight), - 2, - ), - ( - (PointN([1, 2, 0]), CubeFace::Bottom, FaceVertex::BottomLeft), - 2, - ), - ] - .iter() - .cloned() - .collect(); - populate_and_verify(&mut voxels, &positions, offset, &vertex_aos); - *offset.x_mut() = 1; - offset += row_step; + if (tests >> shift) & 1 == 1 { + let positions = [PointN([1, 1, 1]), PointN([0, 2, 0]), PointN([1, 2, 0])]; + let vertex_aos: HashMap<(Point3i, CubeFace, FaceVertex), i32> = [ + ((PointN([1, 1, 1]), CubeFace::Top, FaceVertex::TopLeft), 1), + ((PointN([1, 1, 1]), CubeFace::Top, FaceVertex::TopRight), 2), + ((PointN([1, 1, 1]), CubeFace::Back, FaceVertex::TopLeft), 2), + ((PointN([1, 1, 1]), CubeFace::Back, FaceVertex::TopRight), 1), + ((PointN([1, 1, 1]), CubeFace::Left, FaceVertex::TopLeft), 2), + ( + (PointN([0, 2, 0]), CubeFace::Front, FaceVertex::BottomRight), + 2, + ), + ( + (PointN([0, 2, 0]), CubeFace::Right, FaceVertex::BottomLeft), + 2, + ), // NOTE: This is a hidden face + ( + (PointN([0, 2, 0]), CubeFace::Bottom, FaceVertex::BottomLeft), + 2, + ), + ( + (PointN([1, 2, 0]), CubeFace::Front, FaceVertex::BottomRight), + 2, + ), + ( + (PointN([1, 2, 0]), CubeFace::Front, FaceVertex::BottomLeft), + 2, + ), + ( + (PointN([1, 2, 0]), CubeFace::Bottom, FaceVertex::BottomRight), + 2, + ), + ( + (PointN([1, 2, 0]), CubeFace::Bottom, FaceVertex::BottomLeft), + 2, + ), + ] + .iter() + .cloned() + .collect(); + populate_and_verify(&mut voxels, &positions, offset, &vertex_aos, verify); + *offset.x_mut() = 1; + offset += row_step; + } + shift += 1; // . .. // .. .. - let positions = [PointN([1, 1, 1]), PointN([0, 2, 1]), PointN([1, 2, 0])]; - let vertex_aos: HashMap<(Point3i, CubeFace, FaceVertex), i32> = [ - ((PointN([1, 1, 1]), CubeFace::Top, FaceVertex::TopLeft), 0), - ((PointN([1, 1, 1]), CubeFace::Top, FaceVertex::TopRight), 2), - ( - (PointN([1, 1, 1]), CubeFace::Top, FaceVertex::BottomLeft), - 2, - ), - ((PointN([1, 1, 1]), CubeFace::Back, FaceVertex::TopLeft), 2), - ((PointN([1, 1, 1]), CubeFace::Back, FaceVertex::TopRight), 2), - ((PointN([1, 1, 1]), CubeFace::Left, FaceVertex::TopLeft), 2), - ((PointN([1, 1, 1]), CubeFace::Left, FaceVertex::TopRight), 2), - ( - (PointN([0, 2, 1]), CubeFace::Right, FaceVertex::TopRight), - 2, - ), - ( - (PointN([0, 2, 1]), CubeFace::Right, FaceVertex::BottomRight), - 0, - ), - ( - (PointN([0, 2, 1]), CubeFace::Right, FaceVertex::BottomLeft), - 2, - ), - ( - (PointN([0, 2, 1]), CubeFace::Bottom, FaceVertex::TopLeft), - 2, - ), - ( - (PointN([0, 2, 1]), CubeFace::Bottom, FaceVertex::BottomLeft), - 2, - ), - ((PointN([0, 2, 1]), CubeFace::Back, FaceVertex::TopLeft), 2), - ( - (PointN([0, 2, 1]), CubeFace::Back, FaceVertex::BottomLeft), - 2, - ), - ((PointN([1, 2, 0]), CubeFace::Front, FaceVertex::TopLeft), 2), - ( - (PointN([1, 2, 0]), CubeFace::Front, FaceVertex::BottomRight), - 2, - ), - ( - (PointN([1, 2, 0]), CubeFace::Front, FaceVertex::BottomLeft), - 0, - ), - ( - (PointN([1, 2, 0]), CubeFace::Bottom, FaceVertex::BottomRight), - 2, - ), - ( - (PointN([1, 2, 0]), CubeFace::Bottom, FaceVertex::BottomLeft), - 2, - ), - ((PointN([1, 2, 0]), CubeFace::Left, FaceVertex::TopRight), 2), - ( - (PointN([1, 2, 0]), CubeFace::Left, FaceVertex::BottomRight), - 2, - ), - ] - .iter() - .cloned() - .collect(); - populate_and_verify(&mut voxels, &positions, offset, &vertex_aos); - offset += col_step; + if (tests >> shift) & 1 == 1 { + let positions = [PointN([1, 1, 1]), PointN([0, 2, 1]), PointN([1, 2, 0])]; + let vertex_aos: HashMap<(Point3i, CubeFace, FaceVertex), i32> = [ + ((PointN([1, 1, 1]), CubeFace::Top, FaceVertex::TopLeft), 0), + ((PointN([1, 1, 1]), CubeFace::Top, FaceVertex::TopRight), 2), + ( + (PointN([1, 1, 1]), CubeFace::Top, FaceVertex::BottomLeft), + 2, + ), + ((PointN([1, 1, 1]), CubeFace::Back, FaceVertex::TopLeft), 2), + ((PointN([1, 1, 1]), CubeFace::Back, FaceVertex::TopRight), 2), + ((PointN([1, 1, 1]), CubeFace::Left, FaceVertex::TopLeft), 2), + ((PointN([1, 1, 1]), CubeFace::Left, FaceVertex::TopRight), 2), + ( + (PointN([0, 2, 1]), CubeFace::Right, FaceVertex::TopRight), + 2, + ), + ( + (PointN([0, 2, 1]), CubeFace::Right, FaceVertex::BottomRight), + 0, + ), + ( + (PointN([0, 2, 1]), CubeFace::Right, FaceVertex::BottomLeft), + 2, + ), + ( + (PointN([0, 2, 1]), CubeFace::Bottom, FaceVertex::TopLeft), + 2, + ), + ( + (PointN([0, 2, 1]), CubeFace::Bottom, FaceVertex::BottomLeft), + 2, + ), + ((PointN([0, 2, 1]), CubeFace::Back, FaceVertex::TopLeft), 2), + ( + (PointN([0, 2, 1]), CubeFace::Back, FaceVertex::BottomLeft), + 2, + ), + ((PointN([1, 2, 0]), CubeFace::Front, FaceVertex::TopLeft), 2), + ( + (PointN([1, 2, 0]), CubeFace::Front, FaceVertex::BottomRight), + 2, + ), + ( + (PointN([1, 2, 0]), CubeFace::Front, FaceVertex::BottomLeft), + 0, + ), + ( + (PointN([1, 2, 0]), CubeFace::Bottom, FaceVertex::BottomRight), + 2, + ), + ( + (PointN([1, 2, 0]), CubeFace::Bottom, FaceVertex::BottomLeft), + 2, + ), + ((PointN([1, 2, 0]), CubeFace::Left, FaceVertex::TopRight), 2), + ( + (PointN([1, 2, 0]), CubeFace::Left, FaceVertex::BottomRight), + 2, + ), + ] + .iter() + .cloned() + .collect(); + populate_and_verify(&mut voxels, &positions, offset, &vertex_aos, verify); + offset += col_step; + } + shift += 1; - let positions = [ - PointN([1, 1, 1]), - PointN([0, 2, 0]), - PointN([0, 2, 1]), - PointN([1, 2, 0]), - ]; - let vertex_aos: HashMap<(Point3i, CubeFace, FaceVertex), i32> = [ - ((PointN([1, 1, 1]), CubeFace::Top, FaceVertex::TopLeft), 0), - ((PointN([1, 1, 1]), CubeFace::Top, FaceVertex::TopRight), 2), - ( - (PointN([1, 1, 1]), CubeFace::Top, FaceVertex::BottomLeft), - 2, - ), - ((PointN([1, 1, 1]), CubeFace::Back, FaceVertex::TopLeft), 2), - ((PointN([1, 1, 1]), CubeFace::Back, FaceVertex::TopRight), 1), - ((PointN([1, 1, 1]), CubeFace::Left, FaceVertex::TopLeft), 1), - ((PointN([1, 1, 1]), CubeFace::Left, FaceVertex::TopRight), 2), - ( - (PointN([0, 2, 0]), CubeFace::Front, FaceVertex::BottomRight), - 2, - ), // NOTE: This face is hidden. - ( - (PointN([0, 2, 0]), CubeFace::Right, FaceVertex::BottomLeft), - 2, - ), // NOTE: This face is hidden. - ( - (PointN([0, 2, 0]), CubeFace::Bottom, FaceVertex::BottomLeft), - 2, - ), - ( - (PointN([0, 2, 1]), CubeFace::Right, FaceVertex::TopRight), - 2, - ), - ( - (PointN([0, 2, 1]), CubeFace::Right, FaceVertex::BottomRight), - 0, - ), - ( - (PointN([0, 2, 1]), CubeFace::Right, FaceVertex::BottomLeft), - 2, - ), - ( - (PointN([0, 2, 1]), CubeFace::Bottom, FaceVertex::TopLeft), - 2, - ), - ( - (PointN([0, 2, 1]), CubeFace::Bottom, FaceVertex::BottomLeft), - 2, - ), - ((PointN([0, 2, 1]), CubeFace::Back, FaceVertex::TopLeft), 2), // NOTE: This face is hidden. - ( - (PointN([0, 2, 1]), CubeFace::Back, FaceVertex::BottomLeft), - 2, - ), // NOTE: This face is hidden. - ((PointN([1, 2, 0]), CubeFace::Front, FaceVertex::TopLeft), 2), - ( - (PointN([1, 2, 0]), CubeFace::Front, FaceVertex::BottomRight), - 2, - ), - ( - (PointN([1, 2, 0]), CubeFace::Front, FaceVertex::BottomLeft), - 0, - ), - ( - (PointN([1, 2, 0]), CubeFace::Bottom, FaceVertex::BottomRight), - 2, - ), - ( - (PointN([1, 2, 0]), CubeFace::Bottom, FaceVertex::BottomLeft), - 2, - ), - ((PointN([1, 2, 0]), CubeFace::Left, FaceVertex::TopRight), 2), // NOTE: This face is hidden. - ( - (PointN([1, 2, 0]), CubeFace::Left, FaceVertex::BottomRight), - 2, - ), // NOTE: This face is hidden. - ] - .iter() - .cloned() - .collect(); - populate_and_verify(&mut voxels, &positions, offset, &vertex_aos); - *offset.x_mut() = 1; - offset += row_step; + if (tests >> shift) & 1 == 1 { + let positions = [ + PointN([1, 1, 1]), + PointN([0, 2, 0]), + PointN([0, 2, 1]), + PointN([1, 2, 0]), + ]; + let vertex_aos: HashMap<(Point3i, CubeFace, FaceVertex), i32> = [ + ((PointN([1, 1, 1]), CubeFace::Top, FaceVertex::TopLeft), 0), + ((PointN([1, 1, 1]), CubeFace::Top, FaceVertex::TopRight), 2), + ( + (PointN([1, 1, 1]), CubeFace::Top, FaceVertex::BottomLeft), + 2, + ), + ((PointN([1, 1, 1]), CubeFace::Back, FaceVertex::TopLeft), 2), + ((PointN([1, 1, 1]), CubeFace::Back, FaceVertex::TopRight), 1), + ((PointN([1, 1, 1]), CubeFace::Left, FaceVertex::TopLeft), 1), + ((PointN([1, 1, 1]), CubeFace::Left, FaceVertex::TopRight), 2), + ( + (PointN([0, 2, 0]), CubeFace::Front, FaceVertex::BottomRight), + 2, + ), // NOTE: This face is hidden. + ( + (PointN([0, 2, 0]), CubeFace::Right, FaceVertex::BottomLeft), + 2, + ), // NOTE: This face is hidden. + ( + (PointN([0, 2, 0]), CubeFace::Bottom, FaceVertex::BottomLeft), + 2, + ), + ( + (PointN([0, 2, 1]), CubeFace::Right, FaceVertex::TopRight), + 2, + ), + ( + (PointN([0, 2, 1]), CubeFace::Right, FaceVertex::BottomRight), + 0, + ), + ( + (PointN([0, 2, 1]), CubeFace::Right, FaceVertex::BottomLeft), + 2, + ), + ( + (PointN([0, 2, 1]), CubeFace::Bottom, FaceVertex::TopLeft), + 2, + ), + ( + (PointN([0, 2, 1]), CubeFace::Bottom, FaceVertex::BottomLeft), + 2, + ), + ((PointN([0, 2, 1]), CubeFace::Back, FaceVertex::TopLeft), 2), // NOTE: This face is hidden. + ( + (PointN([0, 2, 1]), CubeFace::Back, FaceVertex::BottomLeft), + 2, + ), // NOTE: This face is hidden. + ((PointN([1, 2, 0]), CubeFace::Front, FaceVertex::TopLeft), 2), + ( + (PointN([1, 2, 0]), CubeFace::Front, FaceVertex::BottomRight), + 2, + ), + ( + (PointN([1, 2, 0]), CubeFace::Front, FaceVertex::BottomLeft), + 0, + ), + ( + (PointN([1, 2, 0]), CubeFace::Bottom, FaceVertex::BottomRight), + 2, + ), + ( + (PointN([1, 2, 0]), CubeFace::Bottom, FaceVertex::BottomLeft), + 2, + ), + ((PointN([1, 2, 0]), CubeFace::Left, FaceVertex::TopRight), 2), // NOTE: This face is hidden. + ( + (PointN([1, 2, 0]), CubeFace::Left, FaceVertex::BottomRight), + 2, + ), // NOTE: This face is hidden. + ] + .iter() + .cloned() + .collect(); + populate_and_verify(&mut voxels, &positions, offset, &vertex_aos, verify); + *offset.x_mut() = 1; + offset += row_step; + } + shift += 1; // Looks like a 2-seater sofa and we want to check that the faces of the seat and backrests are NOT merged // as the ambient occlusion values on the edges are different than in the middle - let positions = [ - // Left armrest - PointN([0, 1, 1]), - // Seat - PointN([1, 0, 1]), - PointN([2, 0, 1]), - // Back - PointN([1, 1, 0]), - PointN([2, 1, 0]), - // Right armrest - PointN([3, 1, 1]), - ]; - let vertex_aos: HashMap<(Point3i, CubeFace, FaceVertex), i32> = [ - // Left armrest - ( - (PointN([0, 1, 1]), CubeFace::Right, FaceVertex::TopRight), - 2, - ), - ( - (PointN([0, 1, 1]), CubeFace::Right, FaceVertex::BottomRight), - 0, - ), - ( - (PointN([0, 1, 1]), CubeFace::Right, FaceVertex::BottomLeft), - 2, - ), - ( - (PointN([0, 1, 1]), CubeFace::Bottom, FaceVertex::TopLeft), - 2, - ), - ( - (PointN([0, 1, 1]), CubeFace::Bottom, FaceVertex::BottomLeft), - 2, - ), - ((PointN([0, 1, 1]), CubeFace::Back, FaceVertex::TopLeft), 2), - ( - (PointN([0, 1, 1]), CubeFace::Back, FaceVertex::BottomLeft), - 2, - ), - // Left seat - ((PointN([1, 0, 1]), CubeFace::Top, FaceVertex::TopLeft), 0), - ((PointN([1, 0, 1]), CubeFace::Top, FaceVertex::TopRight), 1), - ( - (PointN([1, 0, 1]), CubeFace::Top, FaceVertex::BottomLeft), - 2, - ), - ( - (PointN([1, 0, 1]), CubeFace::Right, FaceVertex::TopRight), - 2, - ), // NOTE: This face is hidden. - ((PointN([1, 0, 1]), CubeFace::Back, FaceVertex::TopLeft), 1), - ((PointN([1, 0, 1]), CubeFace::Back, FaceVertex::TopRight), 2), - ((PointN([1, 0, 1]), CubeFace::Left, FaceVertex::TopLeft), 2), - ((PointN([1, 0, 1]), CubeFace::Left, FaceVertex::TopRight), 2), - // Right seat - ((PointN([2, 0, 1]), CubeFace::Top, FaceVertex::TopLeft), 1), - ((PointN([2, 0, 1]), CubeFace::Top, FaceVertex::TopRight), 0), - ( - (PointN([2, 0, 1]), CubeFace::Top, FaceVertex::BottomRight), - 2, - ), - ((PointN([2, 0, 1]), CubeFace::Right, FaceVertex::TopLeft), 2), - ( - (PointN([2, 0, 1]), CubeFace::Right, FaceVertex::TopRight), - 2, - ), - ((PointN([2, 0, 1]), CubeFace::Back, FaceVertex::TopLeft), 2), - ((PointN([2, 0, 1]), CubeFace::Back, FaceVertex::TopRight), 1), - ((PointN([2, 0, 1]), CubeFace::Left, FaceVertex::TopLeft), 2), // NOTE: This face is hidden. - // Left back - ((PointN([1, 1, 0]), CubeFace::Front, FaceVertex::TopLeft), 2), - ( - (PointN([1, 1, 0]), CubeFace::Front, FaceVertex::BottomRight), - 1, - ), - ( - (PointN([1, 1, 0]), CubeFace::Front, FaceVertex::BottomLeft), - 0, - ), - ( - (PointN([1, 1, 0]), CubeFace::Right, FaceVertex::BottomLeft), - 2, - ), // NOTE: This face is hidden. - ( - (PointN([1, 1, 0]), CubeFace::Bottom, FaceVertex::BottomRight), - 2, - ), - ( - (PointN([1, 1, 0]), CubeFace::Bottom, FaceVertex::BottomLeft), - 1, - ), - ((PointN([1, 1, 0]), CubeFace::Left, FaceVertex::TopRight), 2), - ( - (PointN([1, 1, 0]), CubeFace::Left, FaceVertex::BottomRight), - 2, - ), - // Right back - ( - (PointN([2, 1, 0]), CubeFace::Front, FaceVertex::TopRight), - 2, - ), - ( - (PointN([2, 1, 0]), CubeFace::Front, FaceVertex::BottomRight), - 0, - ), - ( - (PointN([2, 1, 0]), CubeFace::Front, FaceVertex::BottomLeft), - 1, - ), - ((PointN([2, 1, 0]), CubeFace::Right, FaceVertex::TopLeft), 2), - ( - (PointN([2, 1, 0]), CubeFace::Right, FaceVertex::BottomLeft), - 2, - ), - ( - (PointN([2, 1, 0]), CubeFace::Bottom, FaceVertex::BottomRight), - 1, - ), - ( - (PointN([2, 1, 0]), CubeFace::Bottom, FaceVertex::BottomLeft), - 2, - ), - ( - (PointN([2, 1, 0]), CubeFace::Left, FaceVertex::BottomRight), - 2, - ), // NOTE: This face is hidden. - // Right arm rest - ( - (PointN([3, 1, 1]), CubeFace::Bottom, FaceVertex::TopRight), - 2, - ), - ( - (PointN([3, 1, 1]), CubeFace::Bottom, FaceVertex::BottomRight), - 2, - ), - ((PointN([3, 1, 1]), CubeFace::Back, FaceVertex::TopRight), 2), - ( - (PointN([3, 1, 1]), CubeFace::Back, FaceVertex::BottomRight), - 2, - ), - ((PointN([3, 1, 1]), CubeFace::Left, FaceVertex::TopLeft), 2), - ( - (PointN([3, 1, 1]), CubeFace::Left, FaceVertex::BottomRight), - 2, - ), - ( - (PointN([3, 1, 1]), CubeFace::Left, FaceVertex::BottomLeft), - 0, - ), - ] - .iter() - .cloned() - .collect(); - populate_and_verify(&mut voxels, &positions, offset, &vertex_aos); - offset += PointN([6, 0, 0]); + if (tests >> shift) & 1 == 1 { + let positions = [ + // Left armrest + PointN([0, 1, 1]), + // Seat + PointN([1, 0, 1]), + PointN([2, 0, 1]), + // Back + PointN([1, 1, 0]), + PointN([2, 1, 0]), + // Right armrest + PointN([3, 1, 1]), + ]; + let vertex_aos: HashMap<(Point3i, CubeFace, FaceVertex), i32> = [ + // Left armrest + ( + (PointN([0, 1, 1]), CubeFace::Right, FaceVertex::TopRight), + 2, + ), + ( + (PointN([0, 1, 1]), CubeFace::Right, FaceVertex::BottomRight), + 0, + ), + ( + (PointN([0, 1, 1]), CubeFace::Right, FaceVertex::BottomLeft), + 2, + ), + ( + (PointN([0, 1, 1]), CubeFace::Bottom, FaceVertex::TopLeft), + 2, + ), + ( + (PointN([0, 1, 1]), CubeFace::Bottom, FaceVertex::BottomLeft), + 2, + ), + ((PointN([0, 1, 1]), CubeFace::Back, FaceVertex::TopLeft), 2), + ( + (PointN([0, 1, 1]), CubeFace::Back, FaceVertex::BottomLeft), + 2, + ), + // Left seat + ((PointN([1, 0, 1]), CubeFace::Top, FaceVertex::TopLeft), 0), + ((PointN([1, 0, 1]), CubeFace::Top, FaceVertex::TopRight), 1), + ( + (PointN([1, 0, 1]), CubeFace::Top, FaceVertex::BottomLeft), + 2, + ), + ( + (PointN([1, 0, 1]), CubeFace::Right, FaceVertex::TopRight), + 2, + ), // NOTE: This face is hidden. + ((PointN([1, 0, 1]), CubeFace::Back, FaceVertex::TopLeft), 1), + ((PointN([1, 0, 1]), CubeFace::Back, FaceVertex::TopRight), 2), + ((PointN([1, 0, 1]), CubeFace::Left, FaceVertex::TopLeft), 2), + ((PointN([1, 0, 1]), CubeFace::Left, FaceVertex::TopRight), 2), + // Right seat + ((PointN([2, 0, 1]), CubeFace::Top, FaceVertex::TopLeft), 1), + ((PointN([2, 0, 1]), CubeFace::Top, FaceVertex::TopRight), 0), + ( + (PointN([2, 0, 1]), CubeFace::Top, FaceVertex::BottomRight), + 2, + ), + ((PointN([2, 0, 1]), CubeFace::Right, FaceVertex::TopLeft), 2), + ( + (PointN([2, 0, 1]), CubeFace::Right, FaceVertex::TopRight), + 2, + ), + ((PointN([2, 0, 1]), CubeFace::Back, FaceVertex::TopLeft), 2), + ((PointN([2, 0, 1]), CubeFace::Back, FaceVertex::TopRight), 1), + ((PointN([2, 0, 1]), CubeFace::Left, FaceVertex::TopLeft), 2), // NOTE: This face is hidden. + // Left back + ((PointN([1, 1, 0]), CubeFace::Front, FaceVertex::TopLeft), 2), + ( + (PointN([1, 1, 0]), CubeFace::Front, FaceVertex::BottomRight), + 1, + ), + ( + (PointN([1, 1, 0]), CubeFace::Front, FaceVertex::BottomLeft), + 0, + ), + ( + (PointN([1, 1, 0]), CubeFace::Right, FaceVertex::BottomLeft), + 2, + ), // NOTE: This face is hidden. + ( + (PointN([1, 1, 0]), CubeFace::Bottom, FaceVertex::BottomRight), + 2, + ), + ( + (PointN([1, 1, 0]), CubeFace::Bottom, FaceVertex::BottomLeft), + 1, + ), + ((PointN([1, 1, 0]), CubeFace::Left, FaceVertex::TopRight), 2), + ( + (PointN([1, 1, 0]), CubeFace::Left, FaceVertex::BottomRight), + 2, + ), + // Right back + ( + (PointN([2, 1, 0]), CubeFace::Front, FaceVertex::TopRight), + 2, + ), + ( + (PointN([2, 1, 0]), CubeFace::Front, FaceVertex::BottomRight), + 0, + ), + ( + (PointN([2, 1, 0]), CubeFace::Front, FaceVertex::BottomLeft), + 1, + ), + ((PointN([2, 1, 0]), CubeFace::Right, FaceVertex::TopLeft), 2), + ( + (PointN([2, 1, 0]), CubeFace::Right, FaceVertex::BottomLeft), + 2, + ), + ( + (PointN([2, 1, 0]), CubeFace::Bottom, FaceVertex::BottomRight), + 1, + ), + ( + (PointN([2, 1, 0]), CubeFace::Bottom, FaceVertex::BottomLeft), + 2, + ), + ( + (PointN([2, 1, 0]), CubeFace::Left, FaceVertex::BottomRight), + 2, + ), // NOTE: This face is hidden. + // Right arm rest + ( + (PointN([3, 1, 1]), CubeFace::Bottom, FaceVertex::TopRight), + 2, + ), + ( + (PointN([3, 1, 1]), CubeFace::Bottom, FaceVertex::BottomRight), + 2, + ), + ((PointN([3, 1, 1]), CubeFace::Back, FaceVertex::TopRight), 2), + ( + (PointN([3, 1, 1]), CubeFace::Back, FaceVertex::BottomRight), + 2, + ), + ((PointN([3, 1, 1]), CubeFace::Left, FaceVertex::TopLeft), 2), + ( + (PointN([3, 1, 1]), CubeFace::Left, FaceVertex::BottomRight), + 2, + ), + ( + (PointN([3, 1, 1]), CubeFace::Left, FaceVertex::BottomLeft), + 0, + ), + ] + .iter() + .cloned() + .collect(); + populate_and_verify(&mut voxels, &positions, offset, &vertex_aos, verify); + offset += PointN([6, 0, 0]); + } + shift += 1; // Looks like a 4-seater sofa and we want to check that the faces of the seat and backrests are // merged for the center two seats but not the left/right seats as the ambient occlusion values // on the edges are different than in the middle - let positions = [ - // Left armrest - PointN([0, 1, 1]), - // Seat - PointN([1, 0, 1]), - PointN([2, 0, 1]), - PointN([3, 0, 1]), - PointN([4, 0, 1]), - // Back - PointN([1, 1, 0]), - PointN([2, 1, 0]), - PointN([3, 1, 0]), - PointN([4, 1, 0]), - // Right armrest - PointN([5, 1, 1]), - ]; - let vertex_aos: HashMap<(Point3i, CubeFace, FaceVertex), i32> = [ - // Left armrest - ( - (PointN([0, 1, 1]), CubeFace::Right, FaceVertex::TopRight), - 2, - ), - ( - (PointN([0, 1, 1]), CubeFace::Right, FaceVertex::BottomRight), - 0, - ), - ( - (PointN([0, 1, 1]), CubeFace::Right, FaceVertex::BottomLeft), - 2, - ), - ( - (PointN([0, 1, 1]), CubeFace::Bottom, FaceVertex::TopLeft), - 2, - ), - ( - (PointN([0, 1, 1]), CubeFace::Bottom, FaceVertex::BottomLeft), - 2, - ), - ((PointN([0, 1, 1]), CubeFace::Back, FaceVertex::TopLeft), 2), - ( - (PointN([0, 1, 1]), CubeFace::Back, FaceVertex::BottomLeft), - 2, - ), - // Left seat - ((PointN([1, 0, 1]), CubeFace::Top, FaceVertex::TopLeft), 0), - ((PointN([1, 0, 1]), CubeFace::Top, FaceVertex::TopRight), 1), - ( - (PointN([1, 0, 1]), CubeFace::Top, FaceVertex::BottomLeft), - 2, - ), - ( - (PointN([1, 0, 1]), CubeFace::Right, FaceVertex::TopRight), - 2, - ), // NOTE: This face is hidden. - ((PointN([1, 0, 1]), CubeFace::Back, FaceVertex::TopLeft), 1), - ((PointN([1, 0, 1]), CubeFace::Back, FaceVertex::TopRight), 2), - ((PointN([1, 0, 1]), CubeFace::Left, FaceVertex::TopLeft), 2), - ((PointN([1, 0, 1]), CubeFace::Left, FaceVertex::TopRight), 2), - // Second seat - ((PointN([2, 0, 1]), CubeFace::Top, FaceVertex::TopLeft), 1), - ((PointN([2, 0, 1]), CubeFace::Top, FaceVertex::TopRight), 1), - ( - (PointN([2, 0, 1]), CubeFace::Right, FaceVertex::TopRight), // NOTE: This face is hidden. - 2, - ), - ((PointN([2, 0, 1]), CubeFace::Back, FaceVertex::TopLeft), 1), - ((PointN([2, 0, 1]), CubeFace::Back, FaceVertex::TopRight), 1), - ((PointN([2, 0, 1]), CubeFace::Left, FaceVertex::TopLeft), 2), // NOTE: This face is hidden. - // Third seat - ((PointN([3, 0, 1]), CubeFace::Top, FaceVertex::TopLeft), 1), - ((PointN([3, 0, 1]), CubeFace::Top, FaceVertex::TopRight), 1), - ( - (PointN([3, 0, 1]), CubeFace::Right, FaceVertex::TopRight), // NOTE: This face is hidden. - 2, - ), - ((PointN([3, 0, 1]), CubeFace::Back, FaceVertex::TopLeft), 1), - ((PointN([3, 0, 1]), CubeFace::Back, FaceVertex::TopRight), 1), - ((PointN([3, 0, 1]), CubeFace::Left, FaceVertex::TopLeft), 2), // NOTE: This face is hidden. - // Right seat - ((PointN([4, 0, 1]), CubeFace::Top, FaceVertex::TopLeft), 1), - ((PointN([4, 0, 1]), CubeFace::Top, FaceVertex::TopRight), 0), - ( - (PointN([4, 0, 1]), CubeFace::Top, FaceVertex::BottomRight), - 2, - ), - ((PointN([4, 0, 1]), CubeFace::Right, FaceVertex::TopLeft), 2), - ( - (PointN([4, 0, 1]), CubeFace::Right, FaceVertex::TopRight), - 2, - ), - ((PointN([4, 0, 1]), CubeFace::Back, FaceVertex::TopLeft), 2), - ((PointN([4, 0, 1]), CubeFace::Back, FaceVertex::TopRight), 1), - ((PointN([4, 0, 1]), CubeFace::Left, FaceVertex::TopLeft), 2), // NOTE: This face is hidden. - // Left back - ((PointN([1, 1, 0]), CubeFace::Front, FaceVertex::TopLeft), 2), - ( - (PointN([1, 1, 0]), CubeFace::Front, FaceVertex::BottomRight), - 1, - ), - ( - (PointN([1, 1, 0]), CubeFace::Front, FaceVertex::BottomLeft), - 0, - ), - ( - (PointN([1, 1, 0]), CubeFace::Right, FaceVertex::BottomLeft), - 2, - ), // NOTE: This face is hidden. - ( - (PointN([1, 1, 0]), CubeFace::Bottom, FaceVertex::BottomRight), - 2, - ), - ( - (PointN([1, 1, 0]), CubeFace::Bottom, FaceVertex::BottomLeft), - 1, - ), - ((PointN([1, 1, 0]), CubeFace::Left, FaceVertex::TopRight), 2), - ( - (PointN([1, 1, 0]), CubeFace::Left, FaceVertex::BottomRight), - 2, - ), - // Second back - ( - (PointN([2, 1, 0]), CubeFace::Front, FaceVertex::BottomRight), - 1, - ), - ( - (PointN([2, 1, 0]), CubeFace::Front, FaceVertex::BottomLeft), - 1, - ), - ( - (PointN([2, 1, 0]), CubeFace::Right, FaceVertex::BottomLeft), - 2, - ), // NOTE: This face is hidden. - ( - (PointN([2, 1, 0]), CubeFace::Bottom, FaceVertex::BottomRight), - 1, - ), - ( - (PointN([2, 1, 0]), CubeFace::Bottom, FaceVertex::BottomLeft), - 1, - ), - ( - (PointN([2, 1, 0]), CubeFace::Left, FaceVertex::BottomRight), - 2, - ), // NOTE: This face is hidden. - // Third back - ( - (PointN([3, 1, 0]), CubeFace::Front, FaceVertex::BottomRight), - 1, - ), - ( - (PointN([3, 1, 0]), CubeFace::Front, FaceVertex::BottomLeft), - 1, - ), - ( - (PointN([3, 1, 0]), CubeFace::Right, FaceVertex::BottomLeft), - 2, - ), // NOTE: This face is hidden. - ( - (PointN([3, 1, 0]), CubeFace::Bottom, FaceVertex::BottomRight), - 1, - ), - ( - (PointN([3, 1, 0]), CubeFace::Bottom, FaceVertex::BottomLeft), - 1, - ), - ( - (PointN([3, 1, 0]), CubeFace::Left, FaceVertex::BottomRight), - 2, - ), // NOTE: This face is hidden. - // Right back - ( - (PointN([4, 1, 0]), CubeFace::Front, FaceVertex::TopRight), - 2, - ), - ( - (PointN([4, 1, 0]), CubeFace::Front, FaceVertex::BottomRight), - 0, - ), - ( - (PointN([4, 1, 0]), CubeFace::Front, FaceVertex::BottomLeft), - 1, - ), - ((PointN([4, 1, 0]), CubeFace::Right, FaceVertex::TopLeft), 2), - ( - (PointN([4, 1, 0]), CubeFace::Right, FaceVertex::BottomLeft), - 2, - ), - ( - (PointN([4, 1, 0]), CubeFace::Bottom, FaceVertex::BottomRight), - 1, - ), - ( - (PointN([4, 1, 0]), CubeFace::Bottom, FaceVertex::BottomLeft), - 2, - ), - ( - (PointN([4, 1, 0]), CubeFace::Left, FaceVertex::BottomRight), - 2, - ), // NOTE: This face is hidden. - // Right arm rest - ( - (PointN([5, 1, 1]), CubeFace::Bottom, FaceVertex::TopRight), - 2, - ), - ( - (PointN([5, 1, 1]), CubeFace::Bottom, FaceVertex::BottomRight), - 2, - ), - ((PointN([5, 1, 1]), CubeFace::Back, FaceVertex::TopRight), 2), - ( - (PointN([5, 1, 1]), CubeFace::Back, FaceVertex::BottomRight), - 2, - ), - ((PointN([5, 1, 1]), CubeFace::Left, FaceVertex::TopLeft), 2), - ( - (PointN([5, 1, 1]), CubeFace::Left, FaceVertex::BottomRight), - 2, - ), - ( - (PointN([5, 1, 1]), CubeFace::Left, FaceVertex::BottomLeft), - 0, - ), - ] - .iter() - .cloned() - .collect(); - populate_and_verify(&mut voxels, &positions, offset, &vertex_aos); - offset += PointN([8, 0, 0]); + if (tests >> shift) & 1 == 1 { + let positions = [ + // Left armrest + PointN([0, 1, 1]), + // Seat + PointN([1, 0, 1]), + PointN([2, 0, 1]), + PointN([3, 0, 1]), + PointN([4, 0, 1]), + // Back + PointN([1, 1, 0]), + PointN([2, 1, 0]), + PointN([3, 1, 0]), + PointN([4, 1, 0]), + // Right armrest + PointN([5, 1, 1]), + ]; + let vertex_aos: HashMap<(Point3i, CubeFace, FaceVertex), i32> = [ + // Left armrest + ( + (PointN([0, 1, 1]), CubeFace::Right, FaceVertex::TopRight), + 2, + ), + ( + (PointN([0, 1, 1]), CubeFace::Right, FaceVertex::BottomRight), + 0, + ), + ( + (PointN([0, 1, 1]), CubeFace::Right, FaceVertex::BottomLeft), + 2, + ), + ( + (PointN([0, 1, 1]), CubeFace::Bottom, FaceVertex::TopLeft), + 2, + ), + ( + (PointN([0, 1, 1]), CubeFace::Bottom, FaceVertex::BottomLeft), + 2, + ), + ((PointN([0, 1, 1]), CubeFace::Back, FaceVertex::TopLeft), 2), + ( + (PointN([0, 1, 1]), CubeFace::Back, FaceVertex::BottomLeft), + 2, + ), + // Left seat + ((PointN([1, 0, 1]), CubeFace::Top, FaceVertex::TopLeft), 0), + ((PointN([1, 0, 1]), CubeFace::Top, FaceVertex::TopRight), 1), + ( + (PointN([1, 0, 1]), CubeFace::Top, FaceVertex::BottomLeft), + 2, + ), + ( + (PointN([1, 0, 1]), CubeFace::Right, FaceVertex::TopRight), + 2, + ), // NOTE: This face is hidden. + ((PointN([1, 0, 1]), CubeFace::Back, FaceVertex::TopLeft), 1), + ((PointN([1, 0, 1]), CubeFace::Back, FaceVertex::TopRight), 2), + ((PointN([1, 0, 1]), CubeFace::Left, FaceVertex::TopLeft), 2), + ((PointN([1, 0, 1]), CubeFace::Left, FaceVertex::TopRight), 2), + // Second seat + ((PointN([2, 0, 1]), CubeFace::Top, FaceVertex::TopLeft), 1), + ((PointN([2, 0, 1]), CubeFace::Top, FaceVertex::TopRight), 1), + ( + (PointN([2, 0, 1]), CubeFace::Right, FaceVertex::TopRight), // NOTE: This face is hidden. + 2, + ), + ((PointN([2, 0, 1]), CubeFace::Back, FaceVertex::TopLeft), 1), + ((PointN([2, 0, 1]), CubeFace::Back, FaceVertex::TopRight), 1), + ((PointN([2, 0, 1]), CubeFace::Left, FaceVertex::TopLeft), 2), // NOTE: This face is hidden. + // Third seat + ((PointN([3, 0, 1]), CubeFace::Top, FaceVertex::TopLeft), 1), + ((PointN([3, 0, 1]), CubeFace::Top, FaceVertex::TopRight), 1), + ( + (PointN([3, 0, 1]), CubeFace::Right, FaceVertex::TopRight), // NOTE: This face is hidden. + 2, + ), + ((PointN([3, 0, 1]), CubeFace::Back, FaceVertex::TopLeft), 1), + ((PointN([3, 0, 1]), CubeFace::Back, FaceVertex::TopRight), 1), + ((PointN([3, 0, 1]), CubeFace::Left, FaceVertex::TopLeft), 2), // NOTE: This face is hidden. + // Right seat + ((PointN([4, 0, 1]), CubeFace::Top, FaceVertex::TopLeft), 1), + ((PointN([4, 0, 1]), CubeFace::Top, FaceVertex::TopRight), 0), + ( + (PointN([4, 0, 1]), CubeFace::Top, FaceVertex::BottomRight), + 2, + ), + ((PointN([4, 0, 1]), CubeFace::Right, FaceVertex::TopLeft), 2), + ( + (PointN([4, 0, 1]), CubeFace::Right, FaceVertex::TopRight), + 2, + ), + ((PointN([4, 0, 1]), CubeFace::Back, FaceVertex::TopLeft), 2), + ((PointN([4, 0, 1]), CubeFace::Back, FaceVertex::TopRight), 1), + ((PointN([4, 0, 1]), CubeFace::Left, FaceVertex::TopLeft), 2), // NOTE: This face is hidden. + // Left back + ((PointN([1, 1, 0]), CubeFace::Front, FaceVertex::TopLeft), 2), + ( + (PointN([1, 1, 0]), CubeFace::Front, FaceVertex::BottomRight), + 1, + ), + ( + (PointN([1, 1, 0]), CubeFace::Front, FaceVertex::BottomLeft), + 0, + ), + ( + (PointN([1, 1, 0]), CubeFace::Right, FaceVertex::BottomLeft), + 2, + ), // NOTE: This face is hidden. + ( + (PointN([1, 1, 0]), CubeFace::Bottom, FaceVertex::BottomRight), + 2, + ), + ( + (PointN([1, 1, 0]), CubeFace::Bottom, FaceVertex::BottomLeft), + 1, + ), + ((PointN([1, 1, 0]), CubeFace::Left, FaceVertex::TopRight), 2), + ( + (PointN([1, 1, 0]), CubeFace::Left, FaceVertex::BottomRight), + 2, + ), + // Second back + ( + (PointN([2, 1, 0]), CubeFace::Front, FaceVertex::BottomRight), + 1, + ), + ( + (PointN([2, 1, 0]), CubeFace::Front, FaceVertex::BottomLeft), + 1, + ), + ( + (PointN([2, 1, 0]), CubeFace::Right, FaceVertex::BottomLeft), + 2, + ), // NOTE: This face is hidden. + ( + (PointN([2, 1, 0]), CubeFace::Bottom, FaceVertex::BottomRight), + 1, + ), + ( + (PointN([2, 1, 0]), CubeFace::Bottom, FaceVertex::BottomLeft), + 1, + ), + ( + (PointN([2, 1, 0]), CubeFace::Left, FaceVertex::BottomRight), + 2, + ), // NOTE: This face is hidden. + // Third back + ( + (PointN([3, 1, 0]), CubeFace::Front, FaceVertex::BottomRight), + 1, + ), + ( + (PointN([3, 1, 0]), CubeFace::Front, FaceVertex::BottomLeft), + 1, + ), + ( + (PointN([3, 1, 0]), CubeFace::Right, FaceVertex::BottomLeft), + 2, + ), // NOTE: This face is hidden. + ( + (PointN([3, 1, 0]), CubeFace::Bottom, FaceVertex::BottomRight), + 1, + ), + ( + (PointN([3, 1, 0]), CubeFace::Bottom, FaceVertex::BottomLeft), + 1, + ), + ( + (PointN([3, 1, 0]), CubeFace::Left, FaceVertex::BottomRight), + 2, + ), // NOTE: This face is hidden. + // Right back + ( + (PointN([4, 1, 0]), CubeFace::Front, FaceVertex::TopRight), + 2, + ), + ( + (PointN([4, 1, 0]), CubeFace::Front, FaceVertex::BottomRight), + 0, + ), + ( + (PointN([4, 1, 0]), CubeFace::Front, FaceVertex::BottomLeft), + 1, + ), + ((PointN([4, 1, 0]), CubeFace::Right, FaceVertex::TopLeft), 2), + ( + (PointN([4, 1, 0]), CubeFace::Right, FaceVertex::BottomLeft), + 2, + ), + ( + (PointN([4, 1, 0]), CubeFace::Bottom, FaceVertex::BottomRight), + 1, + ), + ( + (PointN([4, 1, 0]), CubeFace::Bottom, FaceVertex::BottomLeft), + 2, + ), + ( + (PointN([4, 1, 0]), CubeFace::Left, FaceVertex::BottomRight), + 2, + ), // NOTE: This face is hidden. + // Right arm rest + ( + (PointN([5, 1, 1]), CubeFace::Bottom, FaceVertex::TopRight), + 2, + ), + ( + (PointN([5, 1, 1]), CubeFace::Bottom, FaceVertex::BottomRight), + 2, + ), + ((PointN([5, 1, 1]), CubeFace::Back, FaceVertex::TopRight), 2), + ( + (PointN([5, 1, 1]), CubeFace::Back, FaceVertex::BottomRight), + 2, + ), + ((PointN([5, 1, 1]), CubeFace::Left, FaceVertex::TopLeft), 2), + ( + (PointN([5, 1, 1]), CubeFace::Left, FaceVertex::BottomRight), + 2, + ), + ( + (PointN([5, 1, 1]), CubeFace::Left, FaceVertex::BottomLeft), + 0, + ), + ] + .iter() + .cloned() + .collect(); + populate_and_verify(&mut voxels, &positions, offset, &vertex_aos, verify); + offset += PointN([8, 0, 0]); + } + shift += 1; // Looks like a 2-seater sofa without arm rests and we want to check that the faces of the seat and backrests are NOT merged // as the ambient occlusion values on the edges are different than in the middle - let positions = [ - // Seat - PointN([0, 0, 1]), - PointN([1, 0, 1]), - // Back - PointN([0, 1, 0]), - PointN([1, 1, 0]), - ]; - let vertex_aos: HashMap<(Point3i, CubeFace, FaceVertex), i32> = [ - // Left seat - ((PointN([0, 0, 1]), CubeFace::Top, FaceVertex::TopLeft), 2), - ((PointN([0, 0, 1]), CubeFace::Top, FaceVertex::TopRight), 1), - ( - (PointN([0, 0, 1]), CubeFace::Right, FaceVertex::TopRight), - 2, - ), // NOTE: This face is hidden. - ((PointN([0, 0, 1]), CubeFace::Back, FaceVertex::TopLeft), 1), - ((PointN([0, 0, 1]), CubeFace::Back, FaceVertex::TopRight), 2), - // Right seat - ((PointN([1, 0, 1]), CubeFace::Top, FaceVertex::TopLeft), 1), - ((PointN([1, 0, 1]), CubeFace::Top, FaceVertex::TopRight), 2), - ((PointN([1, 0, 1]), CubeFace::Back, FaceVertex::TopLeft), 2), - ((PointN([1, 0, 1]), CubeFace::Back, FaceVertex::TopRight), 1), - ((PointN([1, 0, 1]), CubeFace::Left, FaceVertex::TopLeft), 2), // NOTE: This face is hidden. - // Left back - ( - (PointN([0, 1, 0]), CubeFace::Front, FaceVertex::BottomRight), - 1, - ), - ( - (PointN([0, 1, 0]), CubeFace::Front, FaceVertex::BottomLeft), - 2, - ), - ( - (PointN([0, 1, 0]), CubeFace::Right, FaceVertex::BottomLeft), - 2, - ), // NOTE: This face is hidden. - ( - (PointN([0, 1, 0]), CubeFace::Bottom, FaceVertex::BottomRight), - 2, - ), - ( - (PointN([0, 1, 0]), CubeFace::Bottom, FaceVertex::BottomLeft), - 1, - ), - // Right back - ( - (PointN([1, 1, 0]), CubeFace::Front, FaceVertex::BottomRight), - 2, - ), - ( - (PointN([1, 1, 0]), CubeFace::Front, FaceVertex::BottomLeft), - 1, - ), - ( - (PointN([1, 1, 0]), CubeFace::Bottom, FaceVertex::BottomRight), - 1, - ), - ( - (PointN([1, 1, 0]), CubeFace::Bottom, FaceVertex::BottomLeft), - 2, - ), - ( - (PointN([1, 1, 0]), CubeFace::Left, FaceVertex::BottomRight), - 2, - ), // NOTE: This face is hidden. - ] - .iter() - .cloned() - .collect(); - populate_and_verify(&mut voxels, &positions, offset, &vertex_aos); - offset += PointN([4, 0, 0]); + if (tests >> shift) & 1 == 1 { + let positions = [ + // Seat + PointN([0, 0, 1]), + PointN([1, 0, 1]), + // Back + PointN([0, 1, 0]), + PointN([1, 1, 0]), + ]; + let vertex_aos: HashMap<(Point3i, CubeFace, FaceVertex), i32> = [ + // Left seat + ((PointN([0, 0, 1]), CubeFace::Top, FaceVertex::TopLeft), 2), + ((PointN([0, 0, 1]), CubeFace::Top, FaceVertex::TopRight), 1), + ( + (PointN([0, 0, 1]), CubeFace::Right, FaceVertex::TopRight), + 2, + ), // NOTE: This face is hidden. + ((PointN([0, 0, 1]), CubeFace::Back, FaceVertex::TopLeft), 1), + ((PointN([0, 0, 1]), CubeFace::Back, FaceVertex::TopRight), 2), + // Right seat + ((PointN([1, 0, 1]), CubeFace::Top, FaceVertex::TopLeft), 1), + ((PointN([1, 0, 1]), CubeFace::Top, FaceVertex::TopRight), 2), + ((PointN([1, 0, 1]), CubeFace::Back, FaceVertex::TopLeft), 2), + ((PointN([1, 0, 1]), CubeFace::Back, FaceVertex::TopRight), 1), + ((PointN([1, 0, 1]), CubeFace::Left, FaceVertex::TopLeft), 2), // NOTE: This face is hidden. + // Left back + ( + (PointN([0, 1, 0]), CubeFace::Front, FaceVertex::BottomRight), + 1, + ), + ( + (PointN([0, 1, 0]), CubeFace::Front, FaceVertex::BottomLeft), + 2, + ), + ( + (PointN([0, 1, 0]), CubeFace::Right, FaceVertex::BottomLeft), + 2, + ), // NOTE: This face is hidden. + ( + (PointN([0, 1, 0]), CubeFace::Bottom, FaceVertex::BottomRight), + 2, + ), + ( + (PointN([0, 1, 0]), CubeFace::Bottom, FaceVertex::BottomLeft), + 1, + ), + // Right back + ( + (PointN([1, 1, 0]), CubeFace::Front, FaceVertex::BottomRight), + 2, + ), + ( + (PointN([1, 1, 0]), CubeFace::Front, FaceVertex::BottomLeft), + 1, + ), + ( + (PointN([1, 1, 0]), CubeFace::Bottom, FaceVertex::BottomRight), + 1, + ), + ( + (PointN([1, 1, 0]), CubeFace::Bottom, FaceVertex::BottomLeft), + 2, + ), + ( + (PointN([1, 1, 0]), CubeFace::Left, FaceVertex::BottomRight), + 2, + ), // NOTE: This face is hidden. + ] + .iter() + .cloned() + .collect(); + populate_and_verify(&mut voxels, &positions, offset, &vertex_aos, verify); + offset += PointN([4, 0, 0]); + } + shift += 1; // Looks like a 4-seater sofa without arm rests and we want to check that the faces of the seat and backrests are // merged for the center two seats but not the left/right seats as the ambient occlusion values // on the edges are different than in the middle - let positions = [ - // Seat - PointN([0, 0, 1]), - PointN([1, 0, 1]), - PointN([2, 0, 1]), - PointN([3, 0, 1]), - // Back - PointN([0, 1, 0]), - PointN([1, 1, 0]), - PointN([2, 1, 0]), - PointN([3, 1, 0]), - ]; - let vertex_aos: HashMap<(Point3i, CubeFace, FaceVertex), i32> = [ - // Left seat - ((PointN([0, 0, 1]), CubeFace::Top, FaceVertex::TopLeft), 2), - ((PointN([0, 0, 1]), CubeFace::Top, FaceVertex::TopRight), 1), - ( - (PointN([0, 0, 1]), CubeFace::Right, FaceVertex::TopRight), - 2, - ), // NOTE: This face is hidden. - ((PointN([0, 0, 1]), CubeFace::Back, FaceVertex::TopLeft), 1), - ((PointN([0, 0, 1]), CubeFace::Back, FaceVertex::TopRight), 2), - // Second seat - ((PointN([1, 0, 1]), CubeFace::Top, FaceVertex::TopLeft), 1), - ((PointN([1, 0, 1]), CubeFace::Top, FaceVertex::TopRight), 1), - ( - (PointN([1, 0, 1]), CubeFace::Right, FaceVertex::TopRight), // NOTE: This face is hidden. - 2, - ), - ((PointN([1, 0, 1]), CubeFace::Back, FaceVertex::TopLeft), 1), - ((PointN([1, 0, 1]), CubeFace::Back, FaceVertex::TopRight), 1), - ((PointN([1, 0, 1]), CubeFace::Left, FaceVertex::TopLeft), 2), // NOTE: This face is hidden. - // Third seat - ((PointN([2, 0, 1]), CubeFace::Top, FaceVertex::TopLeft), 1), - ((PointN([2, 0, 1]), CubeFace::Top, FaceVertex::TopRight), 1), - ( - (PointN([2, 0, 1]), CubeFace::Right, FaceVertex::TopRight), // NOTE: This face is hidden. - 2, - ), - ((PointN([2, 0, 1]), CubeFace::Back, FaceVertex::TopLeft), 1), - ((PointN([2, 0, 1]), CubeFace::Back, FaceVertex::TopRight), 1), - ((PointN([2, 0, 1]), CubeFace::Left, FaceVertex::TopLeft), 2), // NOTE: This face is hidden. - // Right seat - ((PointN([3, 0, 1]), CubeFace::Top, FaceVertex::TopLeft), 1), - ((PointN([3, 0, 1]), CubeFace::Top, FaceVertex::TopRight), 2), - ((PointN([3, 0, 1]), CubeFace::Back, FaceVertex::TopLeft), 2), - ((PointN([3, 0, 1]), CubeFace::Back, FaceVertex::TopRight), 1), - ((PointN([3, 0, 1]), CubeFace::Left, FaceVertex::TopLeft), 2), // NOTE: This face is hidden. - // Left back - ( - (PointN([0, 1, 0]), CubeFace::Front, FaceVertex::BottomRight), - 1, - ), - ( - (PointN([0, 1, 0]), CubeFace::Front, FaceVertex::BottomLeft), - 2, - ), - ( - (PointN([0, 1, 0]), CubeFace::Right, FaceVertex::BottomLeft), - 2, - ), // NOTE: This face is hidden. - ( - (PointN([0, 1, 0]), CubeFace::Bottom, FaceVertex::BottomRight), - 2, - ), - ( - (PointN([0, 1, 0]), CubeFace::Bottom, FaceVertex::BottomLeft), - 1, - ), - // Second back - ( - (PointN([1, 1, 0]), CubeFace::Front, FaceVertex::BottomRight), - 1, - ), - ( - (PointN([1, 1, 0]), CubeFace::Front, FaceVertex::BottomLeft), - 1, - ), - ( - (PointN([1, 1, 0]), CubeFace::Right, FaceVertex::BottomLeft), - 2, - ), // NOTE: This face is hidden. - ( - (PointN([1, 1, 0]), CubeFace::Bottom, FaceVertex::BottomRight), - 1, - ), - ( - (PointN([1, 1, 0]), CubeFace::Bottom, FaceVertex::BottomLeft), - 1, - ), - ( - (PointN([1, 1, 0]), CubeFace::Left, FaceVertex::BottomRight), - 2, - ), // NOTE: This face is hidden. - // Third back - ( - (PointN([2, 1, 0]), CubeFace::Front, FaceVertex::BottomRight), - 1, - ), - ( - (PointN([2, 1, 0]), CubeFace::Front, FaceVertex::BottomLeft), - 1, - ), - ( - (PointN([2, 1, 0]), CubeFace::Right, FaceVertex::BottomLeft), - 2, - ), // NOTE: This face is hidden. - ( - (PointN([2, 1, 0]), CubeFace::Bottom, FaceVertex::BottomRight), - 1, - ), - ( - (PointN([2, 1, 0]), CubeFace::Bottom, FaceVertex::BottomLeft), - 1, - ), - ( - (PointN([2, 1, 0]), CubeFace::Left, FaceVertex::BottomRight), - 2, - ), // NOTE: This face is hidden. - // Right back - ( - (PointN([3, 1, 0]), CubeFace::Front, FaceVertex::BottomRight), - 2, - ), - ( - (PointN([3, 1, 0]), CubeFace::Front, FaceVertex::BottomLeft), - 1, - ), - ( - (PointN([3, 1, 0]), CubeFace::Bottom, FaceVertex::BottomRight), - 1, - ), - ( - (PointN([3, 1, 0]), CubeFace::Bottom, FaceVertex::BottomLeft), - 2, - ), - ( - (PointN([3, 1, 0]), CubeFace::Left, FaceVertex::BottomRight), - 2, - ), // NOTE: This face is hidden. - ] - .iter() - .cloned() - .collect(); - populate_and_verify(&mut voxels, &positions, offset, &vertex_aos); - *offset.x_mut() = 1; - offset += row_step; + if (tests >> shift) & 1 == 1 { + let positions = [ + // Seat + PointN([0, 0, 1]), + PointN([1, 0, 1]), + PointN([2, 0, 1]), + PointN([3, 0, 1]), + // Back + PointN([0, 1, 0]), + PointN([1, 1, 0]), + PointN([2, 1, 0]), + PointN([3, 1, 0]), + ]; + let vertex_aos: HashMap<(Point3i, CubeFace, FaceVertex), i32> = [ + // Left seat + ((PointN([0, 0, 1]), CubeFace::Top, FaceVertex::TopLeft), 2), + ((PointN([0, 0, 1]), CubeFace::Top, FaceVertex::TopRight), 1), + ( + (PointN([0, 0, 1]), CubeFace::Right, FaceVertex::TopRight), + 2, + ), // NOTE: This face is hidden. + ((PointN([0, 0, 1]), CubeFace::Back, FaceVertex::TopLeft), 1), + ((PointN([0, 0, 1]), CubeFace::Back, FaceVertex::TopRight), 2), + // Second seat + ((PointN([1, 0, 1]), CubeFace::Top, FaceVertex::TopLeft), 1), + ((PointN([1, 0, 1]), CubeFace::Top, FaceVertex::TopRight), 1), + ( + (PointN([1, 0, 1]), CubeFace::Right, FaceVertex::TopRight), // NOTE: This face is hidden. + 2, + ), + ((PointN([1, 0, 1]), CubeFace::Back, FaceVertex::TopLeft), 1), + ((PointN([1, 0, 1]), CubeFace::Back, FaceVertex::TopRight), 1), + ((PointN([1, 0, 1]), CubeFace::Left, FaceVertex::TopLeft), 2), // NOTE: This face is hidden. + // Third seat + ((PointN([2, 0, 1]), CubeFace::Top, FaceVertex::TopLeft), 1), + ((PointN([2, 0, 1]), CubeFace::Top, FaceVertex::TopRight), 1), + ( + (PointN([2, 0, 1]), CubeFace::Right, FaceVertex::TopRight), // NOTE: This face is hidden. + 2, + ), + ((PointN([2, 0, 1]), CubeFace::Back, FaceVertex::TopLeft), 1), + ((PointN([2, 0, 1]), CubeFace::Back, FaceVertex::TopRight), 1), + ((PointN([2, 0, 1]), CubeFace::Left, FaceVertex::TopLeft), 2), // NOTE: This face is hidden. + // Right seat + ((PointN([3, 0, 1]), CubeFace::Top, FaceVertex::TopLeft), 1), + ((PointN([3, 0, 1]), CubeFace::Top, FaceVertex::TopRight), 2), + ((PointN([3, 0, 1]), CubeFace::Back, FaceVertex::TopLeft), 2), + ((PointN([3, 0, 1]), CubeFace::Back, FaceVertex::TopRight), 1), + ((PointN([3, 0, 1]), CubeFace::Left, FaceVertex::TopLeft), 2), // NOTE: This face is hidden. + // Left back + ( + (PointN([0, 1, 0]), CubeFace::Front, FaceVertex::BottomRight), + 1, + ), + ( + (PointN([0, 1, 0]), CubeFace::Front, FaceVertex::BottomLeft), + 2, + ), + ( + (PointN([0, 1, 0]), CubeFace::Right, FaceVertex::BottomLeft), + 2, + ), // NOTE: This face is hidden. + ( + (PointN([0, 1, 0]), CubeFace::Bottom, FaceVertex::BottomRight), + 2, + ), + ( + (PointN([0, 1, 0]), CubeFace::Bottom, FaceVertex::BottomLeft), + 1, + ), + // Second back + ( + (PointN([1, 1, 0]), CubeFace::Front, FaceVertex::BottomRight), + 1, + ), + ( + (PointN([1, 1, 0]), CubeFace::Front, FaceVertex::BottomLeft), + 1, + ), + ( + (PointN([1, 1, 0]), CubeFace::Right, FaceVertex::BottomLeft), + 2, + ), // NOTE: This face is hidden. + ( + (PointN([1, 1, 0]), CubeFace::Bottom, FaceVertex::BottomRight), + 1, + ), + ( + (PointN([1, 1, 0]), CubeFace::Bottom, FaceVertex::BottomLeft), + 1, + ), + ( + (PointN([1, 1, 0]), CubeFace::Left, FaceVertex::BottomRight), + 2, + ), // NOTE: This face is hidden. + // Third back + ( + (PointN([2, 1, 0]), CubeFace::Front, FaceVertex::BottomRight), + 1, + ), + ( + (PointN([2, 1, 0]), CubeFace::Front, FaceVertex::BottomLeft), + 1, + ), + ( + (PointN([2, 1, 0]), CubeFace::Right, FaceVertex::BottomLeft), + 2, + ), // NOTE: This face is hidden. + ( + (PointN([2, 1, 0]), CubeFace::Bottom, FaceVertex::BottomRight), + 1, + ), + ( + (PointN([2, 1, 0]), CubeFace::Bottom, FaceVertex::BottomLeft), + 1, + ), + ( + (PointN([2, 1, 0]), CubeFace::Left, FaceVertex::BottomRight), + 2, + ), // NOTE: This face is hidden. + // Right back + ( + (PointN([3, 1, 0]), CubeFace::Front, FaceVertex::BottomRight), + 2, + ), + ( + (PointN([3, 1, 0]), CubeFace::Front, FaceVertex::BottomLeft), + 1, + ), + ( + (PointN([3, 1, 0]), CubeFace::Bottom, FaceVertex::BottomRight), + 1, + ), + ( + (PointN([3, 1, 0]), CubeFace::Bottom, FaceVertex::BottomLeft), + 2, + ), + ( + (PointN([3, 1, 0]), CubeFace::Left, FaceVertex::BottomRight), + 2, + ), // NOTE: This face is hidden. + ] + .iter() + .cloned() + .collect(); + populate_and_verify(&mut voxels, &positions, offset, &vertex_aos, verify); + *offset.x_mut() = 1; + offset += row_step; + } + shift += 1; // Looks like a 2-seater sofa with high back and large seat and we want to check that the faces of the seat and backrests are NOT merged // as the ambient occlusion values on the edges are different than in the middle - let positions = [ - // Left armrest - PointN([0, 1, 1]), - // Seat - PointN([1, 0, 1]), - PointN([2, 0, 1]), - PointN([1, 0, 2]), - PointN([2, 0, 2]), - PointN([1, 0, 3]), - PointN([2, 0, 3]), - // Back - PointN([1, 1, 0]), - PointN([2, 1, 0]), - PointN([1, 2, 0]), - PointN([2, 2, 0]), - PointN([1, 3, 0]), - PointN([2, 3, 0]), - // Right armrest - PointN([3, 1, 1]), - ]; - let vertex_aos: HashMap<(Point3i, CubeFace, FaceVertex), i32> = [ - // Left armrest - ((PointN([0, 1, 1]), CubeFace::Top, FaceVertex::TopRight), 2), - ( - (PointN([0, 1, 1]), CubeFace::Front, FaceVertex::BottomRight), - 2, - ), - ( - (PointN([0, 1, 1]), CubeFace::Right, FaceVertex::TopRight), - 1, - ), - ( - (PointN([0, 1, 1]), CubeFace::Right, FaceVertex::BottomRight), - 0, - ), - ( - (PointN([0, 1, 1]), CubeFace::Right, FaceVertex::BottomLeft), - 1, - ), - ( - (PointN([0, 1, 1]), CubeFace::Bottom, FaceVertex::TopLeft), - 2, - ), - ( - (PointN([0, 1, 1]), CubeFace::Bottom, FaceVertex::BottomLeft), - 1, - ), - ((PointN([0, 1, 1]), CubeFace::Back, FaceVertex::TopLeft), 1), - ( - (PointN([0, 1, 1]), CubeFace::Back, FaceVertex::BottomLeft), - 2, - ), - // Left seat - ((PointN([1, 0, 1]), CubeFace::Top, FaceVertex::TopLeft), 0), - ((PointN([1, 0, 1]), CubeFace::Top, FaceVertex::TopRight), 1), - ( - (PointN([1, 0, 1]), CubeFace::Top, FaceVertex::BottomLeft), - 2, - ), - ( - (PointN([1, 0, 1]), CubeFace::Front, FaceVertex::TopRight), - 2, - ), // NOTE: This face is hidden. - ( - (PointN([1, 0, 1]), CubeFace::Front, FaceVertex::BottomRight), - 2, - ), // NOTE: This face is hidden. - ((PointN([1, 0, 1]), CubeFace::Right, FaceVertex::TopLeft), 2), // NOTE: This face is hidden. - ( - (PointN([1, 0, 1]), CubeFace::Right, FaceVertex::TopRight), - 2, - ), // NOTE: This face is hidden. - ( - (PointN([1, 0, 1]), CubeFace::Right, FaceVertex::BottomLeft), - 2, - ), // NOTE: This face is hidden. - ((PointN([1, 0, 1]), CubeFace::Back, FaceVertex::TopLeft), 1), - ((PointN([1, 0, 1]), CubeFace::Back, FaceVertex::TopRight), 2), - ((PointN([1, 0, 1]), CubeFace::Left, FaceVertex::TopLeft), 2), - ((PointN([1, 0, 1]), CubeFace::Left, FaceVertex::TopRight), 2), - // Right seat - ((PointN([2, 0, 1]), CubeFace::Top, FaceVertex::TopLeft), 1), - ((PointN([2, 0, 1]), CubeFace::Top, FaceVertex::TopRight), 0), - ( - (PointN([2, 0, 1]), CubeFace::Top, FaceVertex::BottomRight), - 2, - ), - ((PointN([2, 0, 1]), CubeFace::Front, FaceVertex::TopLeft), 2), - ( - (PointN([2, 0, 1]), CubeFace::Front, FaceVertex::BottomLeft), - 2, - ), - ((PointN([2, 0, 1]), CubeFace::Right, FaceVertex::TopLeft), 2), - ( - (PointN([2, 0, 1]), CubeFace::Right, FaceVertex::TopRight), - 2, - ), - ((PointN([2, 0, 1]), CubeFace::Back, FaceVertex::TopLeft), 2), - ((PointN([2, 0, 1]), CubeFace::Back, FaceVertex::TopRight), 1), - ((PointN([2, 0, 1]), CubeFace::Left, FaceVertex::TopLeft), 2), // NOTE: This face is hidden. - ((PointN([2, 0, 1]), CubeFace::Left, FaceVertex::TopRight), 2), // NOTE: This face is hidden. - ( - (PointN([2, 0, 1]), CubeFace::Left, FaceVertex::BottomRight), - 2, - ), // NOTE: This face is hidden. - // Left seat first extension forward - ((PointN([1, 0, 2]), CubeFace::Top, FaceVertex::TopLeft), 2), - ( - (PointN([1, 0, 2]), CubeFace::Front, FaceVertex::TopRight), - 2, - ), // NOTE: This face is hidden. - ( - (PointN([1, 0, 2]), CubeFace::Front, FaceVertex::BottomRight), - 2, - ), // NOTE: This face is hidden. - ((PointN([1, 0, 2]), CubeFace::Right, FaceVertex::TopLeft), 2), // NOTE: This face is hidden. - ( - (PointN([1, 0, 2]), CubeFace::Right, FaceVertex::TopRight), - 2, - ), // NOTE: This face is hidden. - ( - (PointN([1, 0, 2]), CubeFace::Right, FaceVertex::BottomRight), - 2, - ), // NOTE: This face is hidden. - ( - (PointN([1, 0, 2]), CubeFace::Right, FaceVertex::BottomLeft), - 2, - ), // NOTE: This face is hidden. - ((PointN([1, 0, 2]), CubeFace::Back, FaceVertex::TopLeft), 2), - ((PointN([1, 0, 2]), CubeFace::Back, FaceVertex::TopRight), 2), - ( - (PointN([1, 0, 2]), CubeFace::Back, FaceVertex::BottomLeft), - 2, - ), - ((PointN([1, 0, 2]), CubeFace::Left, FaceVertex::TopLeft), 2), - // Right seat first extension forward - ((PointN([2, 0, 2]), CubeFace::Top, FaceVertex::TopRight), 2), - ((PointN([2, 0, 2]), CubeFace::Front, FaceVertex::TopLeft), 2), // NOTE: This face is hidden. - ( - (PointN([2, 0, 2]), CubeFace::Front, FaceVertex::BottomLeft), - 2, - ), // NOTE: This face is hidden. - ( - (PointN([2, 0, 2]), CubeFace::Right, FaceVertex::TopRight), - 2, - ), // NOTE: This face is hidden. - ((PointN([2, 0, 2]), CubeFace::Back, FaceVertex::TopLeft), 2), - ((PointN([2, 0, 2]), CubeFace::Back, FaceVertex::TopRight), 2), - ( - (PointN([2, 0, 2]), CubeFace::Back, FaceVertex::BottomRight), - 2, - ), - ((PointN([2, 0, 2]), CubeFace::Left, FaceVertex::TopLeft), 2), - ((PointN([2, 0, 2]), CubeFace::Left, FaceVertex::TopRight), 2), - ( - (PointN([2, 0, 2]), CubeFace::Left, FaceVertex::BottomRight), - 2, - ), - ( - (PointN([2, 0, 2]), CubeFace::Left, FaceVertex::BottomLeft), - 2, - ), - // Left seat second extension forward - ( - (PointN([1, 0, 3]), CubeFace::Right, FaceVertex::TopRight), - 2, - ), // NOTE: This face is hidden. - ( - (PointN([1, 0, 3]), CubeFace::Right, FaceVertex::BottomRight), - 2, - ), // NOTE: This face is hidden. - ((PointN([1, 0, 3]), CubeFace::Back, FaceVertex::TopLeft), 2), - ( - (PointN([1, 0, 3]), CubeFace::Back, FaceVertex::BottomLeft), - 2, - ), - // Right seat second extension forward - ((PointN([2, 0, 3]), CubeFace::Back, FaceVertex::TopRight), 2), - ( - (PointN([2, 0, 3]), CubeFace::Back, FaceVertex::BottomRight), - 2, - ), - ((PointN([2, 0, 3]), CubeFace::Left, FaceVertex::TopLeft), 2), - ( - (PointN([2, 0, 3]), CubeFace::Left, FaceVertex::BottomLeft), - 2, - ), - // Left back - ((PointN([1, 1, 0]), CubeFace::Top, FaceVertex::TopRight), 2), // NOTE: This face is hidden. - ( - (PointN([1, 1, 0]), CubeFace::Top, FaceVertex::BottomRight), - 2, - ), // NOTE: This face is hidden. - ((PointN([1, 1, 0]), CubeFace::Front, FaceVertex::TopLeft), 2), - ( - (PointN([1, 1, 0]), CubeFace::Front, FaceVertex::BottomRight), - 1, - ), - ( - (PointN([1, 1, 0]), CubeFace::Front, FaceVertex::BottomLeft), - 0, - ), - ((PointN([1, 1, 0]), CubeFace::Right, FaceVertex::TopLeft), 2), // NOTE: This face is hidden. - ( - (PointN([1, 1, 0]), CubeFace::Right, FaceVertex::TopRight), - 2, - ), // NOTE: This face is hidden. - ( - (PointN([1, 1, 0]), CubeFace::Right, FaceVertex::BottomLeft), - 2, - ), // NOTE: This face is hidden. - ( - (PointN([1, 1, 0]), CubeFace::Bottom, FaceVertex::BottomRight), - 2, - ), - ( - (PointN([1, 1, 0]), CubeFace::Bottom, FaceVertex::BottomLeft), - 1, - ), - ((PointN([1, 1, 0]), CubeFace::Left, FaceVertex::TopRight), 2), - ( - (PointN([1, 1, 0]), CubeFace::Left, FaceVertex::BottomRight), - 2, - ), - // Right back - ((PointN([2, 1, 0]), CubeFace::Top, FaceVertex::TopLeft), 2), - ( - (PointN([2, 1, 0]), CubeFace::Top, FaceVertex::BottomLeft), - 2, - ), - ( - (PointN([2, 1, 0]), CubeFace::Front, FaceVertex::TopRight), - 2, - ), - ( - (PointN([2, 1, 0]), CubeFace::Front, FaceVertex::BottomRight), - 0, - ), - ( - (PointN([2, 1, 0]), CubeFace::Front, FaceVertex::BottomLeft), - 1, - ), - ((PointN([2, 1, 0]), CubeFace::Right, FaceVertex::TopLeft), 2), - ( - (PointN([2, 1, 0]), CubeFace::Right, FaceVertex::BottomLeft), - 2, - ), - ( - (PointN([2, 1, 0]), CubeFace::Bottom, FaceVertex::BottomRight), - 1, - ), - ( - (PointN([2, 1, 0]), CubeFace::Bottom, FaceVertex::BottomLeft), - 2, - ), - ((PointN([2, 1, 0]), CubeFace::Left, FaceVertex::TopLeft), 2), // NOTE: This face is hidden. - ((PointN([2, 1, 0]), CubeFace::Left, FaceVertex::TopRight), 2), // NOTE: This face is hidden. - ( - (PointN([2, 1, 0]), CubeFace::Left, FaceVertex::BottomRight), - 2, - ), // NOTE: This face is hidden. - // Left back first extension upward - ((PointN([1, 2, 0]), CubeFace::Top, FaceVertex::TopRight), 2), // NOTE: This face is hidden. - ( - (PointN([1, 2, 0]), CubeFace::Top, FaceVertex::BottomRight), - 2, - ), // NOTE: This face is hidden. - ( - (PointN([1, 2, 0]), CubeFace::Front, FaceVertex::BottomLeft), - 2, - ), - ((PointN([1, 2, 0]), CubeFace::Right, FaceVertex::TopLeft), 2), // NOTE: This face is hidden. - ( - (PointN([1, 2, 0]), CubeFace::Right, FaceVertex::TopRight), - 2, - ), // NOTE: This face is hidden. - ( - (PointN([1, 2, 0]), CubeFace::Right, FaceVertex::BottomRight), - 2, - ), // NOTE: This face is hidden. - ( - (PointN([1, 2, 0]), CubeFace::Right, FaceVertex::BottomLeft), - 2, - ), // NOTE: This face is hidden. - ( - (PointN([1, 2, 0]), CubeFace::Bottom, FaceVertex::TopLeft), - 2, - ), - ( - (PointN([1, 2, 0]), CubeFace::Bottom, FaceVertex::BottomRight), - 2, - ), - ( - (PointN([1, 2, 0]), CubeFace::Bottom, FaceVertex::BottomLeft), - 2, - ), - ( - (PointN([1, 2, 0]), CubeFace::Left, FaceVertex::BottomRight), - 2, - ), - // Right back first extension upward - ((PointN([2, 2, 0]), CubeFace::Top, FaceVertex::TopLeft), 2), - ( - (PointN([2, 2, 0]), CubeFace::Top, FaceVertex::BottomLeft), - 2, - ), - ( - (PointN([2, 2, 0]), CubeFace::Front, FaceVertex::BottomRight), - 2, - ), - ( - (PointN([2, 2, 0]), CubeFace::Right, FaceVertex::BottomLeft), - 2, - ), - ( - (PointN([2, 2, 0]), CubeFace::Bottom, FaceVertex::TopRight), - 2, - ), - ( - (PointN([2, 2, 0]), CubeFace::Bottom, FaceVertex::BottomRight), - 2, - ), - ( - (PointN([2, 2, 0]), CubeFace::Bottom, FaceVertex::BottomLeft), - 2, - ), - ((PointN([2, 2, 0]), CubeFace::Left, FaceVertex::TopLeft), 2), // NOTE: This face is hidden. - ((PointN([2, 2, 0]), CubeFace::Left, FaceVertex::TopRight), 2), // NOTE: This face is hidden. - ( - (PointN([2, 2, 0]), CubeFace::Left, FaceVertex::BottomRight), - 2, - ), // NOTE: This face is hidden. - ( - (PointN([2, 2, 0]), CubeFace::Left, FaceVertex::BottomLeft), - 2, - ), // NOTE: This face is hidden. - // Left back second extension upward - ( - (PointN([1, 3, 0]), CubeFace::Right, FaceVertex::BottomRight), - 2, - ), // NOTE: This face is hidden. - ( - (PointN([1, 3, 0]), CubeFace::Right, FaceVertex::BottomLeft), - 2, - ), // NOTE: This face is hidden. - ( - (PointN([1, 3, 0]), CubeFace::Bottom, FaceVertex::TopLeft), - 2, - ), - ( - (PointN([1, 3, 0]), CubeFace::Bottom, FaceVertex::BottomLeft), - 2, - ), - // Right back second extension upward - ( - (PointN([2, 3, 0]), CubeFace::Left, FaceVertex::BottomRight), - 2, - ), // NOTE: This face is hidden. - ( - (PointN([2, 3, 0]), CubeFace::Left, FaceVertex::BottomLeft), - 2, - ), // NOTE: This face is hidden. - ( - (PointN([2, 3, 0]), CubeFace::Bottom, FaceVertex::TopRight), - 2, - ), - ( - (PointN([2, 3, 0]), CubeFace::Bottom, FaceVertex::BottomRight), - 2, - ), - // Right arm rest - ((PointN([3, 1, 1]), CubeFace::Top, FaceVertex::TopLeft), 2), - ( - (PointN([3, 1, 1]), CubeFace::Front, FaceVertex::BottomLeft), - 2, - ), - ( - (PointN([3, 1, 1]), CubeFace::Bottom, FaceVertex::TopRight), - 2, - ), - ( - (PointN([3, 1, 1]), CubeFace::Bottom, FaceVertex::BottomRight), - 1, - ), - ((PointN([3, 1, 1]), CubeFace::Back, FaceVertex::TopRight), 1), - ( - (PointN([3, 1, 1]), CubeFace::Back, FaceVertex::BottomRight), - 2, - ), - ((PointN([3, 1, 1]), CubeFace::Left, FaceVertex::TopLeft), 1), - ( - (PointN([3, 1, 1]), CubeFace::Left, FaceVertex::BottomRight), - 1, - ), - ( - (PointN([3, 1, 1]), CubeFace::Left, FaceVertex::BottomLeft), - 0, - ), - ] - .iter() - .cloned() - .collect(); - populate_and_verify(&mut voxels, &positions, offset, &vertex_aos); - offset += PointN([6, 0, 0]); + if (tests >> shift) & 1 == 1 { + let positions = [ + // Left armrest + PointN([0, 1, 1]), + // Seat + PointN([1, 0, 1]), + PointN([2, 0, 1]), + PointN([1, 0, 2]), + PointN([2, 0, 2]), + PointN([1, 0, 3]), + PointN([2, 0, 3]), + // Back + PointN([1, 1, 0]), + PointN([2, 1, 0]), + PointN([1, 2, 0]), + PointN([2, 2, 0]), + PointN([1, 3, 0]), + PointN([2, 3, 0]), + // Right armrest + PointN([3, 1, 1]), + ]; + let vertex_aos: HashMap<(Point3i, CubeFace, FaceVertex), i32> = [ + // Left armrest + ((PointN([0, 1, 1]), CubeFace::Top, FaceVertex::TopRight), 2), + ( + (PointN([0, 1, 1]), CubeFace::Front, FaceVertex::BottomRight), + 2, + ), + ( + (PointN([0, 1, 1]), CubeFace::Right, FaceVertex::TopRight), + 1, + ), + ( + (PointN([0, 1, 1]), CubeFace::Right, FaceVertex::BottomRight), + 0, + ), + ( + (PointN([0, 1, 1]), CubeFace::Right, FaceVertex::BottomLeft), + 1, + ), + ( + (PointN([0, 1, 1]), CubeFace::Bottom, FaceVertex::TopLeft), + 2, + ), + ( + (PointN([0, 1, 1]), CubeFace::Bottom, FaceVertex::BottomLeft), + 1, + ), + ((PointN([0, 1, 1]), CubeFace::Back, FaceVertex::TopLeft), 1), + ( + (PointN([0, 1, 1]), CubeFace::Back, FaceVertex::BottomLeft), + 2, + ), + // Left seat + ((PointN([1, 0, 1]), CubeFace::Top, FaceVertex::TopLeft), 0), + ((PointN([1, 0, 1]), CubeFace::Top, FaceVertex::TopRight), 1), + ( + (PointN([1, 0, 1]), CubeFace::Top, FaceVertex::BottomLeft), + 2, + ), + ( + (PointN([1, 0, 1]), CubeFace::Front, FaceVertex::TopRight), + 2, + ), // NOTE: This face is hidden. + ( + (PointN([1, 0, 1]), CubeFace::Front, FaceVertex::BottomRight), + 2, + ), // NOTE: This face is hidden. + ((PointN([1, 0, 1]), CubeFace::Right, FaceVertex::TopLeft), 2), // NOTE: This face is hidden. + ( + (PointN([1, 0, 1]), CubeFace::Right, FaceVertex::TopRight), + 2, + ), // NOTE: This face is hidden. + ( + (PointN([1, 0, 1]), CubeFace::Right, FaceVertex::BottomLeft), + 2, + ), // NOTE: This face is hidden. + ((PointN([1, 0, 1]), CubeFace::Back, FaceVertex::TopLeft), 1), + ((PointN([1, 0, 1]), CubeFace::Back, FaceVertex::TopRight), 2), + ((PointN([1, 0, 1]), CubeFace::Left, FaceVertex::TopLeft), 2), + ((PointN([1, 0, 1]), CubeFace::Left, FaceVertex::TopRight), 2), + // Right seat + ((PointN([2, 0, 1]), CubeFace::Top, FaceVertex::TopLeft), 1), + ((PointN([2, 0, 1]), CubeFace::Top, FaceVertex::TopRight), 0), + ( + (PointN([2, 0, 1]), CubeFace::Top, FaceVertex::BottomRight), + 2, + ), + ((PointN([2, 0, 1]), CubeFace::Front, FaceVertex::TopLeft), 2), + ( + (PointN([2, 0, 1]), CubeFace::Front, FaceVertex::BottomLeft), + 2, + ), + ((PointN([2, 0, 1]), CubeFace::Right, FaceVertex::TopLeft), 2), + ( + (PointN([2, 0, 1]), CubeFace::Right, FaceVertex::TopRight), + 2, + ), + ((PointN([2, 0, 1]), CubeFace::Back, FaceVertex::TopLeft), 2), + ((PointN([2, 0, 1]), CubeFace::Back, FaceVertex::TopRight), 1), + ((PointN([2, 0, 1]), CubeFace::Left, FaceVertex::TopLeft), 2), // NOTE: This face is hidden. + ((PointN([2, 0, 1]), CubeFace::Left, FaceVertex::TopRight), 2), // NOTE: This face is hidden. + ( + (PointN([2, 0, 1]), CubeFace::Left, FaceVertex::BottomRight), + 2, + ), // NOTE: This face is hidden. + // Left seat first extension forward + ((PointN([1, 0, 2]), CubeFace::Top, FaceVertex::TopLeft), 2), + ( + (PointN([1, 0, 2]), CubeFace::Front, FaceVertex::TopRight), + 2, + ), // NOTE: This face is hidden. + ( + (PointN([1, 0, 2]), CubeFace::Front, FaceVertex::BottomRight), + 2, + ), // NOTE: This face is hidden. + ((PointN([1, 0, 2]), CubeFace::Right, FaceVertex::TopLeft), 2), // NOTE: This face is hidden. + ( + (PointN([1, 0, 2]), CubeFace::Right, FaceVertex::TopRight), + 2, + ), // NOTE: This face is hidden. + ( + (PointN([1, 0, 2]), CubeFace::Right, FaceVertex::BottomRight), + 2, + ), // NOTE: This face is hidden. + ( + (PointN([1, 0, 2]), CubeFace::Right, FaceVertex::BottomLeft), + 2, + ), // NOTE: This face is hidden. + ((PointN([1, 0, 2]), CubeFace::Back, FaceVertex::TopLeft), 2), + ((PointN([1, 0, 2]), CubeFace::Back, FaceVertex::TopRight), 2), + ( + (PointN([1, 0, 2]), CubeFace::Back, FaceVertex::BottomLeft), + 2, + ), + ((PointN([1, 0, 2]), CubeFace::Left, FaceVertex::TopLeft), 2), + // Right seat first extension forward + ((PointN([2, 0, 2]), CubeFace::Top, FaceVertex::TopRight), 2), + ((PointN([2, 0, 2]), CubeFace::Front, FaceVertex::TopLeft), 2), // NOTE: This face is hidden. + ( + (PointN([2, 0, 2]), CubeFace::Front, FaceVertex::BottomLeft), + 2, + ), // NOTE: This face is hidden. + ( + (PointN([2, 0, 2]), CubeFace::Right, FaceVertex::TopRight), + 2, + ), // NOTE: This face is hidden. + ((PointN([2, 0, 2]), CubeFace::Back, FaceVertex::TopLeft), 2), + ((PointN([2, 0, 2]), CubeFace::Back, FaceVertex::TopRight), 2), + ( + (PointN([2, 0, 2]), CubeFace::Back, FaceVertex::BottomRight), + 2, + ), + ((PointN([2, 0, 2]), CubeFace::Left, FaceVertex::TopLeft), 2), + ((PointN([2, 0, 2]), CubeFace::Left, FaceVertex::TopRight), 2), + ( + (PointN([2, 0, 2]), CubeFace::Left, FaceVertex::BottomRight), + 2, + ), + ( + (PointN([2, 0, 2]), CubeFace::Left, FaceVertex::BottomLeft), + 2, + ), + // Left seat second extension forward + ( + (PointN([1, 0, 3]), CubeFace::Right, FaceVertex::TopRight), + 2, + ), // NOTE: This face is hidden. + ( + (PointN([1, 0, 3]), CubeFace::Right, FaceVertex::BottomRight), + 2, + ), // NOTE: This face is hidden. + ((PointN([1, 0, 3]), CubeFace::Back, FaceVertex::TopLeft), 2), + ( + (PointN([1, 0, 3]), CubeFace::Back, FaceVertex::BottomLeft), + 2, + ), + // Right seat second extension forward + ((PointN([2, 0, 3]), CubeFace::Back, FaceVertex::TopRight), 2), + ( + (PointN([2, 0, 3]), CubeFace::Back, FaceVertex::BottomRight), + 2, + ), + ((PointN([2, 0, 3]), CubeFace::Left, FaceVertex::TopLeft), 2), + ( + (PointN([2, 0, 3]), CubeFace::Left, FaceVertex::BottomLeft), + 2, + ), + // Left back + ((PointN([1, 1, 0]), CubeFace::Top, FaceVertex::TopRight), 2), // NOTE: This face is hidden. + ( + (PointN([1, 1, 0]), CubeFace::Top, FaceVertex::BottomRight), + 2, + ), // NOTE: This face is hidden. + ((PointN([1, 1, 0]), CubeFace::Front, FaceVertex::TopLeft), 2), + ( + (PointN([1, 1, 0]), CubeFace::Front, FaceVertex::BottomRight), + 1, + ), + ( + (PointN([1, 1, 0]), CubeFace::Front, FaceVertex::BottomLeft), + 0, + ), + ((PointN([1, 1, 0]), CubeFace::Right, FaceVertex::TopLeft), 2), // NOTE: This face is hidden. + ( + (PointN([1, 1, 0]), CubeFace::Right, FaceVertex::TopRight), + 2, + ), // NOTE: This face is hidden. + ( + (PointN([1, 1, 0]), CubeFace::Right, FaceVertex::BottomLeft), + 2, + ), // NOTE: This face is hidden. + ( + (PointN([1, 1, 0]), CubeFace::Bottom, FaceVertex::BottomRight), + 2, + ), + ( + (PointN([1, 1, 0]), CubeFace::Bottom, FaceVertex::BottomLeft), + 1, + ), + ((PointN([1, 1, 0]), CubeFace::Left, FaceVertex::TopRight), 2), + ( + (PointN([1, 1, 0]), CubeFace::Left, FaceVertex::BottomRight), + 2, + ), + // Right back + ((PointN([2, 1, 0]), CubeFace::Top, FaceVertex::TopLeft), 2), + ( + (PointN([2, 1, 0]), CubeFace::Top, FaceVertex::BottomLeft), + 2, + ), + ( + (PointN([2, 1, 0]), CubeFace::Front, FaceVertex::TopRight), + 2, + ), + ( + (PointN([2, 1, 0]), CubeFace::Front, FaceVertex::BottomRight), + 0, + ), + ( + (PointN([2, 1, 0]), CubeFace::Front, FaceVertex::BottomLeft), + 1, + ), + ((PointN([2, 1, 0]), CubeFace::Right, FaceVertex::TopLeft), 2), + ( + (PointN([2, 1, 0]), CubeFace::Right, FaceVertex::BottomLeft), + 2, + ), + ( + (PointN([2, 1, 0]), CubeFace::Bottom, FaceVertex::BottomRight), + 1, + ), + ( + (PointN([2, 1, 0]), CubeFace::Bottom, FaceVertex::BottomLeft), + 2, + ), + ((PointN([2, 1, 0]), CubeFace::Left, FaceVertex::TopLeft), 2), // NOTE: This face is hidden. + ((PointN([2, 1, 0]), CubeFace::Left, FaceVertex::TopRight), 2), // NOTE: This face is hidden. + ( + (PointN([2, 1, 0]), CubeFace::Left, FaceVertex::BottomRight), + 2, + ), // NOTE: This face is hidden. + // Left back first extension upward + ((PointN([1, 2, 0]), CubeFace::Top, FaceVertex::TopRight), 2), // NOTE: This face is hidden. + ( + (PointN([1, 2, 0]), CubeFace::Top, FaceVertex::BottomRight), + 2, + ), // NOTE: This face is hidden. + ( + (PointN([1, 2, 0]), CubeFace::Front, FaceVertex::BottomLeft), + 2, + ), + ((PointN([1, 2, 0]), CubeFace::Right, FaceVertex::TopLeft), 2), // NOTE: This face is hidden. + ( + (PointN([1, 2, 0]), CubeFace::Right, FaceVertex::TopRight), + 2, + ), // NOTE: This face is hidden. + ( + (PointN([1, 2, 0]), CubeFace::Right, FaceVertex::BottomRight), + 2, + ), // NOTE: This face is hidden. + ( + (PointN([1, 2, 0]), CubeFace::Right, FaceVertex::BottomLeft), + 2, + ), // NOTE: This face is hidden. + ( + (PointN([1, 2, 0]), CubeFace::Bottom, FaceVertex::TopLeft), + 2, + ), + ( + (PointN([1, 2, 0]), CubeFace::Bottom, FaceVertex::BottomRight), + 2, + ), + ( + (PointN([1, 2, 0]), CubeFace::Bottom, FaceVertex::BottomLeft), + 2, + ), + ( + (PointN([1, 2, 0]), CubeFace::Left, FaceVertex::BottomRight), + 2, + ), + // Right back first extension upward + ((PointN([2, 2, 0]), CubeFace::Top, FaceVertex::TopLeft), 2), + ( + (PointN([2, 2, 0]), CubeFace::Top, FaceVertex::BottomLeft), + 2, + ), + ( + (PointN([2, 2, 0]), CubeFace::Front, FaceVertex::BottomRight), + 2, + ), + ( + (PointN([2, 2, 0]), CubeFace::Right, FaceVertex::BottomLeft), + 2, + ), + ( + (PointN([2, 2, 0]), CubeFace::Bottom, FaceVertex::TopRight), + 2, + ), + ( + (PointN([2, 2, 0]), CubeFace::Bottom, FaceVertex::BottomRight), + 2, + ), + ( + (PointN([2, 2, 0]), CubeFace::Bottom, FaceVertex::BottomLeft), + 2, + ), + ((PointN([2, 2, 0]), CubeFace::Left, FaceVertex::TopLeft), 2), // NOTE: This face is hidden. + ((PointN([2, 2, 0]), CubeFace::Left, FaceVertex::TopRight), 2), // NOTE: This face is hidden. + ( + (PointN([2, 2, 0]), CubeFace::Left, FaceVertex::BottomRight), + 2, + ), // NOTE: This face is hidden. + ( + (PointN([2, 2, 0]), CubeFace::Left, FaceVertex::BottomLeft), + 2, + ), // NOTE: This face is hidden. + // Left back second extension upward + ( + (PointN([1, 3, 0]), CubeFace::Right, FaceVertex::BottomRight), + 2, + ), // NOTE: This face is hidden. + ( + (PointN([1, 3, 0]), CubeFace::Right, FaceVertex::BottomLeft), + 2, + ), // NOTE: This face is hidden. + ( + (PointN([1, 3, 0]), CubeFace::Bottom, FaceVertex::TopLeft), + 2, + ), + ( + (PointN([1, 3, 0]), CubeFace::Bottom, FaceVertex::BottomLeft), + 2, + ), + // Right back second extension upward + ( + (PointN([2, 3, 0]), CubeFace::Left, FaceVertex::BottomRight), + 2, + ), // NOTE: This face is hidden. + ( + (PointN([2, 3, 0]), CubeFace::Left, FaceVertex::BottomLeft), + 2, + ), // NOTE: This face is hidden. + ( + (PointN([2, 3, 0]), CubeFace::Bottom, FaceVertex::TopRight), + 2, + ), + ( + (PointN([2, 3, 0]), CubeFace::Bottom, FaceVertex::BottomRight), + 2, + ), + // Right arm rest + ((PointN([3, 1, 1]), CubeFace::Top, FaceVertex::TopLeft), 2), + ( + (PointN([3, 1, 1]), CubeFace::Front, FaceVertex::BottomLeft), + 2, + ), + ( + (PointN([3, 1, 1]), CubeFace::Bottom, FaceVertex::TopRight), + 2, + ), + ( + (PointN([3, 1, 1]), CubeFace::Bottom, FaceVertex::BottomRight), + 1, + ), + ((PointN([3, 1, 1]), CubeFace::Back, FaceVertex::TopRight), 1), + ( + (PointN([3, 1, 1]), CubeFace::Back, FaceVertex::BottomRight), + 2, + ), + ((PointN([3, 1, 1]), CubeFace::Left, FaceVertex::TopLeft), 1), + ( + (PointN([3, 1, 1]), CubeFace::Left, FaceVertex::BottomRight), + 1, + ), + ( + (PointN([3, 1, 1]), CubeFace::Left, FaceVertex::BottomLeft), + 0, + ), + ] + .iter() + .cloned() + .collect(); + populate_and_verify(&mut voxels, &positions, offset, &vertex_aos, verify); + offset += PointN([6, 0, 0]); + } + shift += 1; // Looks like a 4-seater sofa and we want to check that the faces of the seat and backrests are // merged for the center two seats but not the left/right seats as the ambient occlusion values // on the edges are different than in the middle - // Left armrest - *voxels.get_mut(offset + PointN([0, 1, 1])) = Voxel(true); - // Seat - *voxels.get_mut(offset + PointN([1, 0, 1])) = Voxel(true); - *voxels.get_mut(offset + PointN([2, 0, 1])) = Voxel(true); - *voxels.get_mut(offset + PointN([3, 0, 1])) = Voxel(true); - *voxels.get_mut(offset + PointN([4, 0, 1])) = Voxel(true); - *voxels.get_mut(offset + PointN([1, 0, 2])) = Voxel(true); - *voxels.get_mut(offset + PointN([2, 0, 2])) = Voxel(true); - *voxels.get_mut(offset + PointN([3, 0, 2])) = Voxel(true); - *voxels.get_mut(offset + PointN([4, 0, 2])) = Voxel(true); - *voxels.get_mut(offset + PointN([1, 0, 3])) = Voxel(true); - *voxels.get_mut(offset + PointN([2, 0, 3])) = Voxel(true); - *voxels.get_mut(offset + PointN([3, 0, 3])) = Voxel(true); - *voxels.get_mut(offset + PointN([4, 0, 3])) = Voxel(true); - // Back - *voxels.get_mut(offset + PointN([1, 1, 0])) = Voxel(true); - *voxels.get_mut(offset + PointN([2, 1, 0])) = Voxel(true); - *voxels.get_mut(offset + PointN([3, 1, 0])) = Voxel(true); - *voxels.get_mut(offset + PointN([4, 1, 0])) = Voxel(true); - *voxels.get_mut(offset + PointN([1, 2, 0])) = Voxel(true); - *voxels.get_mut(offset + PointN([2, 2, 0])) = Voxel(true); - *voxels.get_mut(offset + PointN([3, 2, 0])) = Voxel(true); - *voxels.get_mut(offset + PointN([4, 2, 0])) = Voxel(true); - *voxels.get_mut(offset + PointN([1, 3, 0])) = Voxel(true); - *voxels.get_mut(offset + PointN([2, 3, 0])) = Voxel(true); - *voxels.get_mut(offset + PointN([3, 3, 0])) = Voxel(true); - *voxels.get_mut(offset + PointN([4, 3, 0])) = Voxel(true); - // Right armrest - *voxels.get_mut(offset + PointN([5, 1, 1])) = Voxel(true); - offset += PointN([8, 0, 0]); + if (tests >> shift) & 1 == 1 { + // Left armrest + *voxels.get_mut(offset + PointN([0, 1, 1])) = Voxel(true); + // Seat + *voxels.get_mut(offset + PointN([1, 0, 1])) = Voxel(true); + *voxels.get_mut(offset + PointN([2, 0, 1])) = Voxel(true); + *voxels.get_mut(offset + PointN([3, 0, 1])) = Voxel(true); + *voxels.get_mut(offset + PointN([4, 0, 1])) = Voxel(true); + *voxels.get_mut(offset + PointN([1, 0, 2])) = Voxel(true); + *voxels.get_mut(offset + PointN([2, 0, 2])) = Voxel(true); + *voxels.get_mut(offset + PointN([3, 0, 2])) = Voxel(true); + *voxels.get_mut(offset + PointN([4, 0, 2])) = Voxel(true); + *voxels.get_mut(offset + PointN([1, 0, 3])) = Voxel(true); + *voxels.get_mut(offset + PointN([2, 0, 3])) = Voxel(true); + *voxels.get_mut(offset + PointN([3, 0, 3])) = Voxel(true); + *voxels.get_mut(offset + PointN([4, 0, 3])) = Voxel(true); + // Back + *voxels.get_mut(offset + PointN([1, 1, 0])) = Voxel(true); + *voxels.get_mut(offset + PointN([2, 1, 0])) = Voxel(true); + *voxels.get_mut(offset + PointN([3, 1, 0])) = Voxel(true); + *voxels.get_mut(offset + PointN([4, 1, 0])) = Voxel(true); + *voxels.get_mut(offset + PointN([1, 2, 0])) = Voxel(true); + *voxels.get_mut(offset + PointN([2, 2, 0])) = Voxel(true); + *voxels.get_mut(offset + PointN([3, 2, 0])) = Voxel(true); + *voxels.get_mut(offset + PointN([4, 2, 0])) = Voxel(true); + *voxels.get_mut(offset + PointN([1, 3, 0])) = Voxel(true); + *voxels.get_mut(offset + PointN([2, 3, 0])) = Voxel(true); + *voxels.get_mut(offset + PointN([3, 3, 0])) = Voxel(true); + *voxels.get_mut(offset + PointN([4, 3, 0])) = Voxel(true); + // Right armrest + *voxels.get_mut(offset + PointN([5, 1, 1])) = Voxel(true); + offset += PointN([8, 0, 0]); + } + shift += 1; // Looks like a 2-seater sofa and we want to check that the faces of the seat and backrests are NOT merged // as the ambient occlusion values on the edges are different than in the middle - // Seat - *voxels.get_mut(offset + PointN([0, 0, 1])) = Voxel(true); - *voxels.get_mut(offset + PointN([1, 0, 1])) = Voxel(true); - *voxels.get_mut(offset + PointN([0, 0, 2])) = Voxel(true); - *voxels.get_mut(offset + PointN([1, 0, 2])) = Voxel(true); - // Back - *voxels.get_mut(offset + PointN([0, 1, 0])) = Voxel(true); - *voxels.get_mut(offset + PointN([1, 1, 0])) = Voxel(true); - *voxels.get_mut(offset + PointN([0, 2, 0])) = Voxel(true); - *voxels.get_mut(offset + PointN([1, 2, 0])) = Voxel(true); - offset += PointN([4, 0, 0]); + if (tests >> shift) & 1 == 1 { + // Seat + *voxels.get_mut(offset + PointN([0, 0, 1])) = Voxel(true); + *voxels.get_mut(offset + PointN([1, 0, 1])) = Voxel(true); + *voxels.get_mut(offset + PointN([0, 0, 2])) = Voxel(true); + *voxels.get_mut(offset + PointN([1, 0, 2])) = Voxel(true); + // Back + *voxels.get_mut(offset + PointN([0, 1, 0])) = Voxel(true); + *voxels.get_mut(offset + PointN([1, 1, 0])) = Voxel(true); + *voxels.get_mut(offset + PointN([0, 2, 0])) = Voxel(true); + *voxels.get_mut(offset + PointN([1, 2, 0])) = Voxel(true); + offset += PointN([4, 0, 0]); - // Looks like a 4-seater sofa and we want to check that the faces of the seat and backrests are - // merged for the center two seats but not the left/right seats as the ambient occlusion values - // on the edges are different than in the middle - // Seat - *voxels.get_mut(offset + PointN([0, 0, 1])) = Voxel(true); - *voxels.get_mut(offset + PointN([1, 0, 1])) = Voxel(true); - *voxels.get_mut(offset + PointN([2, 0, 1])) = Voxel(true); - *voxels.get_mut(offset + PointN([3, 0, 1])) = Voxel(true); - *voxels.get_mut(offset + PointN([0, 0, 2])) = Voxel(true); - *voxels.get_mut(offset + PointN([1, 0, 2])) = Voxel(true); - *voxels.get_mut(offset + PointN([2, 0, 2])) = Voxel(true); - *voxels.get_mut(offset + PointN([3, 0, 2])) = Voxel(true); - // Back - *voxels.get_mut(offset + PointN([0, 1, 0])) = Voxel(true); - *voxels.get_mut(offset + PointN([1, 1, 0])) = Voxel(true); - *voxels.get_mut(offset + PointN([2, 1, 0])) = Voxel(true); - *voxels.get_mut(offset + PointN([3, 1, 0])) = Voxel(true); - *voxels.get_mut(offset + PointN([0, 2, 0])) = Voxel(true); - *voxels.get_mut(offset + PointN([1, 2, 0])) = Voxel(true); - *voxels.get_mut(offset + PointN([2, 2, 0])) = Voxel(true); - *voxels.get_mut(offset + PointN([3, 2, 0])) = Voxel(true); - *offset.x_mut() = 1; - offset += row_step; + // Looks like a 4-seater sofa and we want to check that the faces of the seat and backrests are + // merged for the center two seats but not the left/right seats as the ambient occlusion values + // on the edges are different than in the middle + // Seat + *voxels.get_mut(offset + PointN([0, 0, 1])) = Voxel(true); + *voxels.get_mut(offset + PointN([1, 0, 1])) = Voxel(true); + *voxels.get_mut(offset + PointN([2, 0, 1])) = Voxel(true); + *voxels.get_mut(offset + PointN([3, 0, 1])) = Voxel(true); + *voxels.get_mut(offset + PointN([0, 0, 2])) = Voxel(true); + *voxels.get_mut(offset + PointN([1, 0, 2])) = Voxel(true); + *voxels.get_mut(offset + PointN([2, 0, 2])) = Voxel(true); + *voxels.get_mut(offset + PointN([3, 0, 2])) = Voxel(true); + // Back + *voxels.get_mut(offset + PointN([0, 1, 0])) = Voxel(true); + *voxels.get_mut(offset + PointN([1, 1, 0])) = Voxel(true); + *voxels.get_mut(offset + PointN([2, 1, 0])) = Voxel(true); + *voxels.get_mut(offset + PointN([3, 1, 0])) = Voxel(true); + *voxels.get_mut(offset + PointN([0, 2, 0])) = Voxel(true); + *voxels.get_mut(offset + PointN([1, 2, 0])) = Voxel(true); + *voxels.get_mut(offset + PointN([2, 2, 0])) = Voxel(true); + *voxels.get_mut(offset + PointN([3, 2, 0])) = Voxel(true); + *offset.x_mut() = 1; + offset += row_step; + } + shift += 1; println!("<<< set_up_voxels"); voxels