2020-06-21 15:17:24 -04:00
|
|
|
use crate::model;
|
2018-11-04 14:48:03 -05:00
|
|
|
use crate::render;
|
|
|
|
use crate::resources;
|
|
|
|
use crate::shared::Direction;
|
2020-06-21 15:17:24 -04:00
|
|
|
use crate::types::bit::Set;
|
|
|
|
use crate::world;
|
|
|
|
use crate::world::block;
|
|
|
|
use rand::{self, Rng, SeedableRng};
|
|
|
|
use std::sync::mpsc;
|
|
|
|
use std::sync::{Arc, RwLock};
|
|
|
|
use std::thread;
|
2016-03-19 12:32:13 -04:00
|
|
|
|
WebGL fixes after disabling threaded chunk builder (#451)
At this point, the UI renders in the browser through WebGL, with no GL errors.
Progress towards #446 🕸️ Web support
* main: enable render loop on wasm, disable events_loop on wasm for now
Allow for testing rendering on WebGL
* chunk_builder: disable on wasm due to no threads on wasm
Chunks will not be correctly rendered, but other parts of the program now can be tested instead of crashing in std::thread
* chunk_frag: glBindFragDataLocation is only on native, WebGL 2 uses in-shader specification layout(location=), which works on native in OpenGL 4.1 but we're on OpenGL 3.2 - see https://www.khronos.org/opengl/wiki/Fragment_Shader#Output_buffers
* std_or_web: always fail File::open() to avoid servers.json empty string JSON parse failing
* www: update installation instructions
* render: fix apparent TEXTURE_MAX_LEVEL -> TEXTURE_MAG_FILTER typo
* render: correct type for internalFormat DEPTH_COMPONENT24
Valid combinations of format, type, and internalFormat are listed at https://www.khronos.org/registry/OpenGL/specs/es/3.0/es_spec_3.0.pdf#page=124&zoom=100,168,206. We had UNSIGNED_BYTE for DEPTH_COMPONENT24, but only UNSIGNED_INT is a valid type for this internal format.
Fixes texImage: Mismatched internalFormat and format/type: 0x81a6 and 0x1902/0x1401
and fixes the subsequent GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT error.
* render: gl::MULTISAMPLE (0x809d) is not available on WebGL
Fixes WebGL warning: enabled: cap: Invalid enum value <enum 0x809d> 0.bootstrap.js line 11 > eval:851:21
* gl: replace set_float_multi_raw with a safer set_float_multi
Removes use of passing raw pointers in set_float_multi_raw parameters
Instead, casts raw pointers to flatten, similar to set_matrix_multi
Fixes uniform setter: (uniform colorMul[0]) values length (1) must be a positive integer multiple of size of <enum 0x8b52>.
* render: model: send BYTE to id attrib, fixes type mismatch
Fixes drawElementsInstanced: Vertex attrib 0 requires data of type INT, but is being supplied with type UINT
2020-12-31 17:35:30 -05:00
|
|
|
#[cfg(not(target_arch = "wasm32"))]
|
2016-03-21 19:25:27 -04:00
|
|
|
const NUM_WORKERS: usize = 8;
|
2016-03-19 12:32:13 -04:00
|
|
|
|
WebGL fixes after disabling threaded chunk builder (#451)
At this point, the UI renders in the browser through WebGL, with no GL errors.
Progress towards #446 🕸️ Web support
* main: enable render loop on wasm, disable events_loop on wasm for now
Allow for testing rendering on WebGL
* chunk_builder: disable on wasm due to no threads on wasm
Chunks will not be correctly rendered, but other parts of the program now can be tested instead of crashing in std::thread
* chunk_frag: glBindFragDataLocation is only on native, WebGL 2 uses in-shader specification layout(location=), which works on native in OpenGL 4.1 but we're on OpenGL 3.2 - see https://www.khronos.org/opengl/wiki/Fragment_Shader#Output_buffers
* std_or_web: always fail File::open() to avoid servers.json empty string JSON parse failing
* www: update installation instructions
* render: fix apparent TEXTURE_MAX_LEVEL -> TEXTURE_MAG_FILTER typo
* render: correct type for internalFormat DEPTH_COMPONENT24
Valid combinations of format, type, and internalFormat are listed at https://www.khronos.org/registry/OpenGL/specs/es/3.0/es_spec_3.0.pdf#page=124&zoom=100,168,206. We had UNSIGNED_BYTE for DEPTH_COMPONENT24, but only UNSIGNED_INT is a valid type for this internal format.
Fixes texImage: Mismatched internalFormat and format/type: 0x81a6 and 0x1902/0x1401
and fixes the subsequent GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT error.
* render: gl::MULTISAMPLE (0x809d) is not available on WebGL
Fixes WebGL warning: enabled: cap: Invalid enum value <enum 0x809d> 0.bootstrap.js line 11 > eval:851:21
* gl: replace set_float_multi_raw with a safer set_float_multi
Removes use of passing raw pointers in set_float_multi_raw parameters
Instead, casts raw pointers to flatten, similar to set_matrix_multi
Fixes uniform setter: (uniform colorMul[0]) values length (1) must be a positive integer multiple of size of <enum 0x8b52>.
* render: model: send BYTE to id attrib, fixes type mismatch
Fixes drawElementsInstanced: Vertex attrib 0 requires data of type INT, but is being supplied with type UINT
2020-12-31 17:35:30 -05:00
|
|
|
// TODO: threads or web workers on wasm
|
|
|
|
#[cfg(target_arch = "wasm32")]
|
|
|
|
const NUM_WORKERS: usize = 0;
|
|
|
|
|
2016-03-19 12:32:13 -04:00
|
|
|
pub struct ChunkBuilder {
|
|
|
|
threads: Vec<(mpsc::Sender<BuildReq>, thread::JoinHandle<()>)>,
|
2016-03-24 21:17:03 -04:00
|
|
|
free_builders: Vec<(usize, Vec<u8>, Vec<u8>)>,
|
2016-03-19 12:32:13 -04:00
|
|
|
built_recv: mpsc::Receiver<(usize, BuildReply)>,
|
2016-03-21 12:51:19 -04:00
|
|
|
|
2016-03-24 11:39:57 -04:00
|
|
|
models: Arc<RwLock<model::Factory>>,
|
|
|
|
resource_version: usize,
|
2016-03-19 12:32:13 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
impl ChunkBuilder {
|
2020-06-21 15:17:24 -04:00
|
|
|
pub fn new(
|
|
|
|
resources: Arc<RwLock<resources::Manager>>,
|
|
|
|
textures: Arc<RwLock<render::TextureManager>>,
|
|
|
|
) -> ChunkBuilder {
|
2020-06-29 21:48:07 -04:00
|
|
|
let models = Arc::new(RwLock::new(model::Factory::new(resources, textures)));
|
2016-03-24 11:39:57 -04:00
|
|
|
|
2016-03-19 12:32:13 -04:00
|
|
|
let mut threads = vec![];
|
|
|
|
let mut free = vec![];
|
|
|
|
let (built_send, built_recv) = mpsc::channel();
|
2020-06-21 15:17:24 -04:00
|
|
|
for i in 0..NUM_WORKERS {
|
2016-03-19 12:32:13 -04:00
|
|
|
let built_send = built_send.clone();
|
|
|
|
let (work_send, work_recv) = mpsc::channel();
|
2016-03-24 11:39:57 -04:00
|
|
|
let models = models.clone();
|
2016-03-19 12:32:13 -04:00
|
|
|
let id = i;
|
2020-06-21 15:17:24 -04:00
|
|
|
threads.push((
|
|
|
|
work_send,
|
2021-01-07 20:26:58 -05:00
|
|
|
thread::spawn(move || build_func_threaded(id, models, work_recv, built_send)),
|
2020-06-21 15:17:24 -04:00
|
|
|
));
|
2016-03-24 21:17:03 -04:00
|
|
|
free.push((i, vec![], vec![]));
|
2016-03-19 12:32:13 -04:00
|
|
|
}
|
|
|
|
ChunkBuilder {
|
2018-11-04 16:43:30 -05:00
|
|
|
threads,
|
2016-03-19 12:32:13 -04:00
|
|
|
free_builders: free,
|
2018-11-04 16:43:30 -05:00
|
|
|
built_recv,
|
|
|
|
models,
|
2016-03-24 11:39:57 -04:00
|
|
|
resource_version: 0xFFFF,
|
2016-03-19 12:32:13 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-06-21 15:17:24 -04:00
|
|
|
pub fn tick(
|
|
|
|
&mut self,
|
|
|
|
world: &mut world::World,
|
|
|
|
renderer: &mut render::Renderer,
|
|
|
|
version: usize,
|
|
|
|
) {
|
2016-03-24 11:39:57 -04:00
|
|
|
{
|
2016-03-29 15:53:41 -04:00
|
|
|
if version != self.resource_version {
|
|
|
|
self.resource_version = version;
|
2016-03-24 15:39:14 -04:00
|
|
|
self.models.write().unwrap().version_change();
|
2016-03-24 11:39:57 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-01-07 20:26:58 -05:00
|
|
|
if NUM_WORKERS > 0 {
|
|
|
|
while let Ok((id, mut val)) = self.built_recv.try_recv() {
|
|
|
|
world.reset_building_flag(val.position);
|
2016-03-19 13:34:12 -04:00
|
|
|
|
2021-01-07 20:26:58 -05:00
|
|
|
if let Some(sec) =
|
|
|
|
world.get_section_mut(val.position.0, val.position.1, val.position.2)
|
|
|
|
{
|
|
|
|
sec.cull_info = val.cull_info;
|
|
|
|
renderer.update_chunk_solid(
|
|
|
|
&mut sec.render_buffer,
|
|
|
|
&val.solid_buffer,
|
|
|
|
val.solid_count,
|
|
|
|
);
|
|
|
|
renderer.update_chunk_trans(
|
|
|
|
&mut sec.render_buffer,
|
|
|
|
&val.trans_buffer,
|
|
|
|
val.trans_count,
|
|
|
|
);
|
|
|
|
}
|
2016-03-19 13:34:12 -04:00
|
|
|
|
2021-01-07 20:26:58 -05:00
|
|
|
val.solid_buffer.clear();
|
|
|
|
val.trans_buffer.clear();
|
|
|
|
self.free_builders
|
|
|
|
.push((id, val.solid_buffer, val.trans_buffer));
|
|
|
|
}
|
|
|
|
if self.free_builders.is_empty() {
|
|
|
|
return;
|
|
|
|
}
|
2016-03-19 12:32:13 -04:00
|
|
|
}
|
2021-01-07 20:26:58 -05:00
|
|
|
|
2020-06-21 15:17:24 -04:00
|
|
|
let dirty_sections = world
|
|
|
|
.get_render_list()
|
|
|
|
.iter()
|
|
|
|
.map(|v| v.0)
|
|
|
|
.filter(|v| world.is_section_dirty(*v))
|
|
|
|
.collect::<Vec<_>>();
|
|
|
|
for (x, y, z) in dirty_sections {
|
2021-01-07 20:26:58 -05:00
|
|
|
let t_id = if NUM_WORKERS > 0 {
|
|
|
|
self.free_builders.pop().unwrap()
|
|
|
|
} else {
|
|
|
|
(0, vec![], vec![])
|
|
|
|
};
|
2016-03-21 12:51:19 -04:00
|
|
|
world.set_building_flag((x, y, z));
|
2016-03-19 12:32:13 -04:00
|
|
|
let (cx, cy, cz) = (x << 4, y << 4, z << 4);
|
|
|
|
let mut snapshot = world.capture_snapshot(cx - 2, cy - 2, cz - 2, 20, 20, 20);
|
|
|
|
snapshot.make_relative(-2, -2, -2);
|
2021-01-07 20:26:58 -05:00
|
|
|
|
|
|
|
if NUM_WORKERS > 0 {
|
|
|
|
self.threads[t_id.0]
|
|
|
|
.0
|
|
|
|
.send(BuildReq {
|
|
|
|
snapshot,
|
|
|
|
position: (x, y, z),
|
|
|
|
solid_buffer: t_id.1,
|
|
|
|
trans_buffer: t_id.2,
|
|
|
|
})
|
|
|
|
.unwrap();
|
|
|
|
if self.free_builders.is_empty() {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
let mut val = build_func_1(
|
|
|
|
self.models.clone(),
|
|
|
|
BuildReq {
|
|
|
|
snapshot,
|
|
|
|
position: (x, y, z),
|
|
|
|
solid_buffer: t_id.1,
|
|
|
|
trans_buffer: t_id.2,
|
|
|
|
},
|
|
|
|
);
|
|
|
|
|
|
|
|
world.reset_building_flag(val.position);
|
|
|
|
|
|
|
|
if let Some(sec) =
|
|
|
|
world.get_section_mut(val.position.0, val.position.1, val.position.2)
|
|
|
|
{
|
|
|
|
sec.cull_info = val.cull_info;
|
|
|
|
renderer.update_chunk_solid(
|
|
|
|
&mut sec.render_buffer,
|
|
|
|
&val.solid_buffer,
|
|
|
|
val.solid_count,
|
|
|
|
);
|
|
|
|
renderer.update_chunk_trans(
|
|
|
|
&mut sec.render_buffer,
|
|
|
|
&val.trans_buffer,
|
|
|
|
val.trans_count,
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
val.solid_buffer.clear();
|
|
|
|
val.trans_buffer.clear();
|
2016-03-19 12:32:13 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
struct BuildReq {
|
|
|
|
snapshot: world::Snapshot,
|
|
|
|
position: (i32, i32, i32),
|
2016-03-24 21:17:03 -04:00
|
|
|
solid_buffer: Vec<u8>,
|
|
|
|
trans_buffer: Vec<u8>,
|
2016-03-19 12:32:13 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
struct BuildReply {
|
|
|
|
position: (i32, i32, i32),
|
|
|
|
solid_buffer: Vec<u8>,
|
2016-03-19 13:34:12 -04:00
|
|
|
solid_count: usize,
|
2016-03-24 21:17:03 -04:00
|
|
|
trans_buffer: Vec<u8>,
|
|
|
|
trans_count: usize,
|
2016-03-24 19:27:22 -04:00
|
|
|
cull_info: CullInfo,
|
2016-03-19 12:32:13 -04:00
|
|
|
}
|
|
|
|
|
2021-01-07 20:26:58 -05:00
|
|
|
fn build_func_threaded(
|
2020-06-21 15:17:24 -04:00
|
|
|
id: usize,
|
|
|
|
models: Arc<RwLock<model::Factory>>,
|
|
|
|
work_recv: mpsc::Receiver<BuildReq>,
|
|
|
|
built_send: mpsc::Sender<(usize, BuildReply)>,
|
|
|
|
) {
|
2016-03-19 12:32:13 -04:00
|
|
|
loop {
|
2021-01-07 20:26:58 -05:00
|
|
|
let work: BuildReq = match work_recv.recv() {
|
2016-03-20 08:21:10 -04:00
|
|
|
Ok(val) => val,
|
|
|
|
Err(_) => return,
|
|
|
|
};
|
2016-03-19 12:32:13 -04:00
|
|
|
|
2021-01-07 20:26:58 -05:00
|
|
|
let reply = build_func_1(models.clone(), work);
|
2016-03-19 12:32:13 -04:00
|
|
|
|
2021-01-07 20:26:58 -05:00
|
|
|
built_send.send((id, reply)).unwrap();
|
|
|
|
}
|
|
|
|
}
|
2016-03-25 05:47:39 -04:00
|
|
|
|
2021-01-07 20:26:58 -05:00
|
|
|
fn build_func_1(models: Arc<RwLock<model::Factory>>, work: BuildReq) -> BuildReply {
|
|
|
|
let BuildReq {
|
|
|
|
snapshot,
|
|
|
|
position,
|
|
|
|
mut solid_buffer,
|
|
|
|
mut trans_buffer,
|
|
|
|
} = work;
|
|
|
|
|
|
|
|
let mut rng = rand_pcg::Pcg32::from_seed([
|
|
|
|
((position.0 as u32) & 0xff) as u8,
|
|
|
|
(((position.0 as u32) >> 8) & 0xff) as u8,
|
|
|
|
(((position.0 as u32) >> 16) & 0xff) as u8,
|
|
|
|
((position.0 as u32) >> 24) as u8,
|
|
|
|
((position.1 as u32) & 0xff) as u8,
|
|
|
|
(((position.1 as u32) >> 8) & 0xff) as u8,
|
|
|
|
(((position.1 as u32) >> 16) & 0xff) as u8,
|
|
|
|
((position.1 as u32) >> 24) as u8,
|
|
|
|
((position.2 as u32) & 0xff) as u8,
|
|
|
|
(((position.2 as u32) >> 8) & 0xff) as u8,
|
|
|
|
(((position.2 as u32) >> 16) & 0xff) as u8,
|
|
|
|
((position.2 as u32) >> 24) as u8,
|
|
|
|
(((position.0 as u32 ^ position.2 as u32) | 1) & 0xff) as u8,
|
|
|
|
((((position.0 as u32 ^ position.2 as u32) | 1) >> 8) & 0xff) as u8,
|
|
|
|
((((position.0 as u32 ^ position.2 as u32) | 1) >> 16) & 0xff) as u8,
|
|
|
|
(((position.0 as u32 ^ position.2 as u32) | 1) >> 24) as u8,
|
|
|
|
]);
|
|
|
|
|
|
|
|
let mut solid_count = 0;
|
|
|
|
let mut trans_count = 0;
|
|
|
|
|
|
|
|
for y in 0..16 {
|
|
|
|
for x in 0..16 {
|
|
|
|
for z in 0..16 {
|
|
|
|
let block = snapshot.get_block(x, y, z);
|
|
|
|
let mat = block.get_material();
|
|
|
|
if !mat.renderable {
|
|
|
|
// Use one step of the rng so that
|
|
|
|
// if a block is placed in an empty
|
|
|
|
// location is variant doesn't change
|
|
|
|
let _: u32 = rng.gen();
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
match block {
|
|
|
|
block::Block::Water { .. } | block::Block::FlowingWater { .. } => {
|
|
|
|
let tex = models.read().unwrap().textures.clone();
|
|
|
|
trans_count += model::liquid::render_liquid(
|
|
|
|
tex,
|
|
|
|
false,
|
2020-06-21 15:17:24 -04:00
|
|
|
&snapshot,
|
|
|
|
x,
|
|
|
|
y,
|
|
|
|
z,
|
|
|
|
&mut trans_buffer,
|
2016-03-24 21:17:03 -04:00
|
|
|
);
|
2021-01-07 20:26:58 -05:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
block::Block::Lava { .. } | block::Block::FlowingLava { .. } => {
|
|
|
|
let tex = models.read().unwrap().textures.clone();
|
|
|
|
solid_count += model::liquid::render_liquid(
|
|
|
|
tex,
|
|
|
|
true,
|
2020-06-21 15:17:24 -04:00
|
|
|
&snapshot,
|
|
|
|
x,
|
|
|
|
y,
|
|
|
|
z,
|
|
|
|
&mut solid_buffer,
|
2016-04-02 20:26:31 -04:00
|
|
|
);
|
2021-01-07 20:26:58 -05:00
|
|
|
continue;
|
2016-03-24 21:17:03 -04:00
|
|
|
}
|
2021-01-07 20:26:58 -05:00
|
|
|
_ => {}
|
|
|
|
}
|
|
|
|
|
|
|
|
if mat.transparent {
|
|
|
|
trans_count += model::Factory::get_state_model(
|
|
|
|
&models,
|
|
|
|
block,
|
|
|
|
&mut rng,
|
|
|
|
&snapshot,
|
|
|
|
x,
|
|
|
|
y,
|
|
|
|
z,
|
|
|
|
&mut trans_buffer,
|
|
|
|
);
|
|
|
|
} else {
|
|
|
|
solid_count += model::Factory::get_state_model(
|
|
|
|
&models,
|
|
|
|
block,
|
|
|
|
&mut rng,
|
|
|
|
&snapshot,
|
|
|
|
x,
|
|
|
|
y,
|
|
|
|
z,
|
|
|
|
&mut solid_buffer,
|
|
|
|
);
|
2016-03-19 12:32:13 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2021-01-07 20:26:58 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
let cull_info = build_cull_info(&snapshot);
|
2016-03-19 12:32:13 -04:00
|
|
|
|
2021-01-07 20:26:58 -05:00
|
|
|
BuildReply {
|
|
|
|
position,
|
|
|
|
solid_buffer,
|
|
|
|
solid_count,
|
|
|
|
trans_buffer,
|
|
|
|
trans_count,
|
|
|
|
cull_info,
|
2016-03-19 12:32:13 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-03-24 19:27:22 -04:00
|
|
|
fn build_cull_info(snapshot: &world::Snapshot) -> CullInfo {
|
|
|
|
let mut visited = Set::new(16 * 16 * 16);
|
|
|
|
let mut info = CullInfo::new();
|
|
|
|
|
2020-06-21 15:17:24 -04:00
|
|
|
for y in 0..16 {
|
|
|
|
for z in 0..16 {
|
|
|
|
for x in 0..16 {
|
2016-03-24 19:27:22 -04:00
|
|
|
if visited.get(x | (z << 4) | (y << 8)) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
let touched = flood_fill(snapshot, &mut visited, x as i32, y as i32, z as i32);
|
|
|
|
if touched == 0 {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
for d1 in Direction::all() {
|
|
|
|
if (touched & (1 << d1.index())) != 0 {
|
|
|
|
for d2 in Direction::all() {
|
|
|
|
if (touched & (1 << d2.index())) != 0 {
|
|
|
|
info.set_visible(d1, d2);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
info
|
|
|
|
}
|
|
|
|
|
|
|
|
fn flood_fill(snapshot: &world::Snapshot, visited: &mut Set, x: i32, y: i32, z: i32) -> u8 {
|
2016-03-25 17:23:27 -04:00
|
|
|
use std::collections::VecDeque;
|
2016-03-24 19:27:22 -04:00
|
|
|
|
2016-03-25 17:23:27 -04:00
|
|
|
let mut next_position = VecDeque::with_capacity(16 * 16);
|
|
|
|
next_position.push_back((x, y, z));
|
2016-03-24 19:27:22 -04:00
|
|
|
|
|
|
|
let mut touched = 0;
|
2016-03-25 17:23:27 -04:00
|
|
|
while let Some((x, y, z)) = next_position.pop_front() {
|
|
|
|
let idx = (x | (z << 4) | (y << 8)) as usize;
|
2020-12-31 12:51:46 -05:00
|
|
|
if !(0..=15).contains(&x)
|
|
|
|
|| !(0..=15).contains(&y)
|
|
|
|
|| !(0..=15).contains(&z)
|
|
|
|
|| visited.get(idx)
|
|
|
|
{
|
2016-03-25 17:23:27 -04:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
visited.set(idx, true);
|
2016-03-24 19:27:22 -04:00
|
|
|
|
2020-06-21 15:17:24 -04:00
|
|
|
if snapshot
|
|
|
|
.get_block(x, y, z)
|
|
|
|
.get_material()
|
|
|
|
.should_cull_against
|
|
|
|
{
|
2016-03-25 17:23:27 -04:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
if x == 0 {
|
|
|
|
touched |= 1 << Direction::West.index();
|
|
|
|
} else if x == 15 {
|
|
|
|
touched |= 1 << Direction::East.index();
|
|
|
|
}
|
|
|
|
if y == 0 {
|
|
|
|
touched |= 1 << Direction::Down.index();
|
|
|
|
} else if y == 15 {
|
|
|
|
touched |= 1 << Direction::Up.index();
|
|
|
|
}
|
|
|
|
if z == 0 {
|
|
|
|
touched |= 1 << Direction::North.index();
|
|
|
|
} else if z == 15 {
|
|
|
|
touched |= 1 << Direction::South.index();
|
|
|
|
}
|
2016-03-24 19:27:22 -04:00
|
|
|
|
2016-03-25 17:23:27 -04:00
|
|
|
for d in Direction::all() {
|
|
|
|
let (ox, oy, oz) = d.get_offset();
|
2020-06-21 15:17:24 -04:00
|
|
|
next_position.push_back((x + ox, y + oy, z + oz));
|
2016-03-25 17:23:27 -04:00
|
|
|
}
|
2016-03-24 19:27:22 -04:00
|
|
|
}
|
|
|
|
touched
|
|
|
|
}
|
|
|
|
|
2016-09-15 10:15:52 -04:00
|
|
|
#[derive(Clone, Copy, Default)]
|
2016-03-24 19:27:22 -04:00
|
|
|
pub struct CullInfo(u64);
|
|
|
|
|
|
|
|
impl CullInfo {
|
2020-06-21 15:17:24 -04:00
|
|
|
pub fn new() -> CullInfo {
|
|
|
|
Default::default()
|
|
|
|
}
|
2016-03-24 19:27:22 -04:00
|
|
|
|
|
|
|
pub fn all_vis() -> CullInfo {
|
|
|
|
CullInfo(0xFFFFFFFFFFFFFFFF)
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn is_visible(&self, from: Direction, to: Direction) -> bool {
|
|
|
|
(self.0 & (1 << (from.index() * 6 + to.index()))) != 0
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn set_visible(&mut self, from: Direction, to: Direction) {
|
|
|
|
self.0 |= 1 << (from.index() * 6 + to.index());
|
|
|
|
}
|
|
|
|
}
|