Skip to content

Commit

Permalink
Further prework for block audio
Browse files Browse the repository at this point in the history
  • Loading branch information
drey7925 committed Dec 23, 2024
1 parent adc4024 commit 082eb54
Show file tree
Hide file tree
Showing 11 changed files with 309 additions and 238 deletions.
22 changes: 9 additions & 13 deletions perovskite_client/src/game_state/block_types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ use anyhow::{ensure, Context, Result};
use perovskite_core::block_id::BlockId;

use bitvec::prelude as bv;
use perovskite_core::block_id::special_block_defs::AIR_ID;
use perovskite_core::constants::blocks::AIR;
use perovskite_core::protocol::blocks::block_type_def::RenderInfo;
use perovskite_core::protocol::blocks::{BlockTypeDef, CubeRenderInfo, CubeRenderMode};
Expand All @@ -12,13 +13,13 @@ use super::make_fallback_blockdef;
pub(crate) struct ClientBlockTypeManager {
block_defs: Vec<Option<BlockTypeDef>>,
fallback_block_def: BlockTypeDef,
air_block: BlockId,
light_propagators: bv::BitVec,
light_emitters: Vec<u8>,
solid_opaque_blocks: bv::BitVec,
transparent_render_blocks: bv::BitVec,
translucent_render_blocks: bv::BitVec,
name_to_id: FxHashMap<String, BlockId>,
audio_emitters: Vec<(u32, f32)>,
}
impl ClientBlockTypeManager {
pub(crate) fn new(server_defs: Vec<BlockTypeDef>) -> Result<ClientBlockTypeManager> {
Expand All @@ -45,17 +46,15 @@ impl ClientBlockTypeManager {

let mut light_emitters = Vec::new();
light_emitters.resize(BlockId(max_id).index() + 1, 0);
let mut audio_emitters = Vec::new();
audio_emitters.resize(BlockId(max_id).index() + 1, (0, 0.0));

let mut name_to_id = FxHashMap::default();

let mut air_block = BlockId::from(u32::MAX);
for def in server_defs {
let id = BlockId(def.id);
ensure!(id.variant() == 0);
ensure!(block_defs[id.index()].is_none());
if def.short_name == AIR {
air_block = id;
}
name_to_id.insert(def.short_name.clone(), id);
if def.allow_light_propagation {
light_propagators.set(id.index(), true);
Expand Down Expand Up @@ -102,23 +101,24 @@ impl ClientBlockTypeManager {
translucent_render_blocks.set(id.index(), true);
}

if def.sound_id != 0 {
audio_emitters[id.index()] = (def.sound_id, def.sound_volume);
}

light_emitters[id.index()] = light_emission;
block_defs[id.index()] = Some(def);
}
if air_block.0 == u32::MAX {
log::warn!("Server didn't send an air block definition");
}

Ok(ClientBlockTypeManager {
block_defs,
fallback_block_def: make_fallback_blockdef(),
air_block,
light_propagators,
light_emitters,
solid_opaque_blocks,
transparent_render_blocks,
translucent_render_blocks,
name_to_id,
audio_emitters,
})
}

Expand All @@ -144,10 +144,6 @@ impl ClientBlockTypeManager {
self.name_to_id.get(name).copied()
}

pub(crate) fn air_block(&self) -> BlockId {
self.air_block
}

/// Determines whether this block propagates light
#[inline]
pub(crate) fn propagates_light(&self, id: BlockId) -> bool {
Expand Down
60 changes: 43 additions & 17 deletions perovskite_client/src/game_state/chunk.rs
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ use vulkano::DeviceSize;
use super::block_types::ClientBlockTypeManager;

pub(crate) trait ChunkDataView {
fn is_empty_optimization_hint(&self) -> bool;
fn block_ids(&self) -> &[BlockId; 18 * 18 * 18];
fn lightmap(&self) -> &[u8; 18 * 18 * 18];
fn get_block(&self, offset: ChunkOffset) -> BlockId {
Expand All @@ -58,28 +59,46 @@ pub(crate) trait ChunkDataView {

pub(crate) struct LockedChunkDataView<'a>(RwLockReadGuard<'a, ChunkData>);
impl ChunkDataView for LockedChunkDataView<'_> {
fn is_empty_optimization_hint(&self) -> bool {
!self
.0
.block_ids
.as_ref()
.is_some_and(|x| x.iter().any(|&v| v != BlockId(0)))
}
fn block_ids(&self) -> &[BlockId; 18 * 18 * 18] {
self.0.block_ids.as_deref().unwrap_or(&ZERO_CHUNK)
}

fn lightmap(&self) -> &[u8; 18 * 18 * 18] {
&self.0.lightmap
self.0.lightmap.as_deref().unwrap_or(&ZERO_LIGHTMAP)
}
}

pub(crate) struct ChunkDataViewMut<'a>(RwLockWriteGuard<'a, ChunkData>);
impl ChunkDataViewMut<'_> {
pub(crate) fn is_empty_optimization_hint(&self) -> bool {
!self
.0
.block_ids
.as_ref()
.is_some_and(|x| x.iter().any(|&v| v != BlockId(0)))
}

pub(crate) fn block_ids(&self) -> &[BlockId; 18 * 18 * 18] {
self.0.block_ids.as_deref().unwrap_or(&ZERO_CHUNK)
}
pub(crate) fn block_ids_mut(&mut self) -> Option<&mut [BlockId; 18 * 18 * 18]> {
self.0.block_ids.as_deref_mut()
}
pub(crate) fn lightmap_mut(&mut self) -> &mut [u8; 18 * 18 * 18] {
&mut self.0.lightmap
self.0.lightmap.get_or_insert_with(|| {
log::warn!("Filling nonexisting lightmap in mutator; likely a bug");
Box::new([0; 18 * 18 * 18])
})
}
pub(crate) fn set_state(&mut self, state: BlockIdState) {
self.0.data_state = state;
pub(crate) fn set_state(&mut self, state: ChunkRenderState) {
self.0.render_state = state;
}

pub(crate) fn get_block(&self, offset: ChunkOffset) -> BlockId {
Expand All @@ -92,8 +111,9 @@ impl ChunkDataViewMut<'_> {
}

const ZERO_CHUNK: [BlockId; 18 * 18 * 18] = [BlockId(0); 18 * 18 * 18];
const ZERO_LIGHTMAP: [u8; 18 * 18 * 18] = [0; 18 * 18 * 18];

pub(crate) enum BlockIdState {
pub(crate) enum ChunkRenderState {
/// We don't have this chunk's neighbors yet. It's not ready to render.
NeedProcessing,
/// We processed the chunk's neighbors, but we don't have anything to render.
Expand Down Expand Up @@ -121,9 +141,9 @@ pub(crate) struct ChunkData {
/// players, entities, etc.
///
/// This can't be optimized with None since we need to propagate light through the chunk
pub(crate) lightmap: Box<[u8; 18 * 18 * 18]>,
pub(crate) lightmap: Option<Box<[u8; 18 * 18 * 18]>>,

data_state: BlockIdState,
render_state: ChunkRenderState,
}

pub(crate) struct SnappyDecodeHelper {
Expand Down Expand Up @@ -262,13 +282,19 @@ impl ClientChunk {
}
};
let occlusion = get_occlusion_for_proto(block_ids, block_types);
let block_ids = Self::expand_ids(block_ids);
let lightmap = if block_ids.is_some() {
Some(Box::new([0; 18 * 18 * 18]))
} else {
None
};
Ok((
ClientChunk {
coord,
chunk_data: RwLock::new(ChunkData {
block_ids: Self::expand_ids(block_ids),
data_state: BlockIdState::NeedProcessing,
lightmap: Box::new([0; 18 * 18 * 18]),
block_ids,
render_state: ChunkRenderState::NeedProcessing,
lightmap,
}),
chunk_mesh: Mutex::new(ChunkMesh {
solo_cpu: None,
Expand All @@ -289,11 +315,11 @@ impl ClientChunk {
pub(crate) fn mesh_with(&self, renderer: &BlockRenderer) -> Result<MeshResult> {
let data = self.chunk_data();

let vertex_data = match data.0.data_state {
BlockIdState::NeedProcessing => Some(renderer.mesh_chunk(&data)?),
BlockIdState::NoRender => None,
BlockIdState::ReadyToRender => Some(renderer.mesh_chunk(&data)?),
BlockIdState::AuditNoRender => {
let vertex_data = match data.0.render_state {
ChunkRenderState::NeedProcessing => Some(renderer.mesh_chunk(&data)?),
ChunkRenderState::NoRender => None,
ChunkRenderState::ReadyToRender => Some(renderer.mesh_chunk(&data)?),
ChunkRenderState::AuditNoRender => {
let result = renderer.mesh_chunk(&data)?;
if result.solid_opaque.is_some()
|| result.transparent.is_some()
Expand Down Expand Up @@ -401,7 +427,7 @@ impl ClientChunk {
// unwrap is safe: we verified the length
let mut data_guard = self.chunk_data.write();
data_guard.block_ids = Self::expand_ids(block_ids);
data_guard.data_state = BlockIdState::NeedProcessing;
data_guard.render_state = ChunkRenderState::NeedProcessing;
Ok(occlusion)
}

Expand All @@ -423,7 +449,7 @@ impl ClientChunk {
// TODO future optimization: check whether a change in variant actually requires
// a redraw
if old_id != new_id {
chunk_data.data_state = BlockIdState::NeedProcessing;
chunk_data.render_state = ChunkRenderState::NeedProcessing;
chunk_data.block_ids.as_mut().unwrap()[block_coord.offset().as_extended_index()] =
new_id;
}
Expand Down
9 changes: 3 additions & 6 deletions perovskite_client/src/game_ui/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ use texture_packer::{importer::ImageImporter, Rect, TexturePacker};

use anyhow::{bail, Error, Result};
use arc_swap::ArcSwap;
use perovskite_core::block_id::special_block_defs::AIR_ID;

use crate::game_state::settings::GameSettings;
use crate::{
Expand Down Expand Up @@ -110,12 +111,8 @@ async fn build_texture_atlas(
}
}
handles.push(s.spawn(|| -> Result<()> {
let mut renderer = MiniBlockRenderer::new(
ctx,
[64, 64],
block_renderer.atlas(),
block_renderer.block_types().air_block(),
)?;
let mut renderer =
MiniBlockRenderer::new(ctx, [64, 64], block_renderer.atlas())?;
for name in tasks {
let block_id = match block_renderer.block_types().get_block_by_name(name) {
Some(id) => id,
Expand Down
9 changes: 4 additions & 5 deletions perovskite_client/src/net_client/mesh_worker.rs
Original file line number Diff line number Diff line change
Expand Up @@ -356,9 +356,8 @@ pub(crate) fn propagate_neighbor_data(
{
let _span = span!("chunk precheck");
// Fast-pass checks
let air = block_manager.air_block();
if current_chunk.block_ids().iter().all(|&x| x == air) {
current_chunk.set_state(crate::game_state::chunk::BlockIdState::NoRender);
if current_chunk.is_empty_optimization_hint() {
current_chunk.set_state(crate::game_state::chunk::ChunkRenderState::NoRender);
return Ok(true);
}
}
Expand Down Expand Up @@ -402,7 +401,7 @@ pub(crate) fn propagate_neighbor_data(
.iter()
.all(|&x| block_manager.is_solid_opaque(x))
{
current_chunk.set_state(crate::game_state::chunk::BlockIdState::NoRender);
current_chunk.set_state(crate::game_state::chunk::ChunkRenderState::NoRender);
return Ok(true);
}
}
Expand Down Expand Up @@ -596,7 +595,7 @@ pub(crate) fn propagate_neighbor_data(
}
}

current_chunk.set_state(crate::game_state::chunk::BlockIdState::ReadyToRender);
current_chunk.set_state(crate::game_state::chunk::ChunkRenderState::ReadyToRender);

Ok(true)
} else {
Expand Down
8 changes: 6 additions & 2 deletions perovskite_client/src/vulkan/mini_renderer.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
use anyhow::{Context, Result};
use cgmath::{vec3, Deg, Matrix4, SquareMatrix};
use image::{DynamicImage, RgbaImage};
use perovskite_core::block_id::special_block_defs::AIR_ID;
use perovskite_core::{
block_id::BlockId, coordinates::ChunkOffset, protocol::blocks::BlockTypeDef,
};
Expand Down Expand Up @@ -59,7 +60,6 @@ impl MiniBlockRenderer {
ctx: Arc<VulkanContext>,
surface_size: [u32; 2],
atlas_texture: &Texture2DHolder,
air_block: BlockId,
) -> Result<Self> {
// All compliant GPUs should be able to render to R8G8B8A8_SRGB
let render_pass = make_ssaa_render_pass(
Expand Down Expand Up @@ -151,7 +151,7 @@ impl MiniBlockRenderer {
cube_provider,
cube_pipeline,
download_buffer,
fake_chunk: Box::new([air_block; 18 * 18 * 18]),
fake_chunk: Box::new([AIR_ID; 18 * 18 * 18]),
})
}

Expand Down Expand Up @@ -244,6 +244,10 @@ struct FakeChunkDataView<'a> {
block_ids: &'a [BlockId; 18 * 18 * 18],
}
impl<'a> ChunkDataView for FakeChunkDataView<'a> {
fn is_empty_optimization_hint(&self) -> bool {
false
}

fn block_ids(&self) -> &[BlockId; 18 * 18 * 18] {
self.block_ids
}
Expand Down
3 changes: 2 additions & 1 deletion perovskite_game_api/src/blocks.rs
Original file line number Diff line number Diff line change
Expand Up @@ -167,6 +167,7 @@ enum VariantEffect {
}

/// Represents a block built by [GameBuilder::add_block]
#[derive(Debug, Clone)]
pub struct BuiltBlock {
/// The ID of the block
pub id: BlockId,
Expand Down Expand Up @@ -391,7 +392,7 @@ impl BlockBuilder {
}
/// Convenience method that sets this block to a simple appearance as a cube with the same texture on all faces,
/// no transparency/translucency, no light propagation or emission, and no additional appearance settings
/// (which may be added in the future)
/// (which may be added in the future and will be accessible via the full [CubeAppearanceBuilder])
pub fn set_cube_single_texture(self, texture: impl Into<TextureReference>) -> Self {
self.set_cube_appearance(CubeAppearanceBuilder::new().set_single_texture(texture))
}
Expand Down
Loading

0 comments on commit 082eb54

Please sign in to comment.