Skip to content

Commit

Permalink
Added mirror mode visuals
Browse files Browse the repository at this point in the history
  • Loading branch information
AnthonyTornetta committed Oct 26, 2023
1 parent b0c2cf1 commit a740e08
Show file tree
Hide file tree
Showing 8 changed files with 317 additions and 14 deletions.
70 changes: 70 additions & 0 deletions cosmos_client/assets/cosmos/shaders/repeated.wgsl
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
// Stolen and heavily modified from: https://github.com/janhohenheim/foxtrot/blob/main/assets/shaders/repeated.wgsl - TY!

#import bevy_pbr::mesh_vertex_output MeshVertexOutput
#import bevy_pbr::mesh_vertex_output as OutputTypes
#import bevy_pbr::pbr_functions as PbrCore
#import bevy_pbr::pbr_bindings as MaterialBindings
#import bevy_pbr::pbr_types as PbrTypes
#import bevy_pbr::mesh_view_bindings as ViewBindings

struct Repeats {
horizontal: u32,
vertical: u32,
_wasm_padding1: u32,
_wasm_padding2: u32,
}

@group(1) @binding(0)
var texture: texture_2d<f32>;
@group(1) @binding(1)
var texture_sampler: sampler;
@group(1) @binding(2)
var<uniform> repeats: Repeats;
@group(1) @binding(3)
var<uniform> color: vec4<f32>;

fn get_texture_sample(coords: vec2<f32>) -> vec4<f32> {
let repeated_coords = vec2<f32>(
(coords.x % (1. / f32(repeats.horizontal))) * f32(repeats.horizontal),
(coords.y % (1. / f32(repeats.vertical))) * f32(repeats.vertical)
);
return textureSample(texture, texture_sampler, repeated_coords);
}

/// Adapted from <https://github.com/bevyengine/bevy/blob/main/crates/bevy_pbr/src/render/pbr.wgsl#L30>
fn get_pbr_output(in: MeshVertexOutput) -> vec4<f32> {
var material = PbrTypes::standard_material_new();
material.perceptual_roughness = 1.0;

var output_color: vec4<f32> = color;

output_color = PbrCore::alpha_discard(material, output_color);

#ifdef TONEMAP_IN_SHADER
output_color = tone_mapping(output_color);
#endif
#ifdef DEBAND_DITHER
var output_rgb = output_color.rgb;
output_rgb = powsafe(output_rgb, 1.0 / 2.2);
output_rgb = output_rgb + screen_space_dither(in.frag_coord.xy);
// This conversion back to linear space is required because our output texture format is
// SRGB; the GPU will assume our output is linear and will apply an SRGB conversion.
output_rgb = powsafe(output_rgb, 2.2);
output_color = vec4(output_rgb, output_color.a);
#endif
#ifdef PREMULTIPLY_ALPHA
output_color = premultiply_alpha(material.flags, output_color);
#endif
return output_color;
}

@fragment
fn fragment(mesh: MeshVertexOutput) -> @location(0) vec4<f32> {
let texture = get_texture_sample(mesh.uv);
if (texture[3] < 0.5) {
discard;
}
let pbr_output = get_pbr_output(mesh);

return texture * pbr_output;
}
Binary file added cosmos_client/assets/images/misc/symmetry.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 2 additions & 0 deletions cosmos_client/src/asset/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,9 @@ use bevy::prelude::App;

pub mod asset_loader;
pub mod asset_loading;
pub mod repeating_material;

pub(super) fn register(app: &mut App) {
asset_loading::register(app);
repeating_material::register(app);
}
42 changes: 42 additions & 0 deletions cosmos_client/src/asset/repeating_material.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
//! A garbage repeated material. Don't use this.
use bevy::{
prelude::{AlphaMode, App, Color, Handle, Image, Material, MaterialPlugin},
reflect::{Reflect, TypeUuid},
render::render_resource::{AsBindGroup, ShaderRef, ShaderType},
};

#[repr(C, align(16))] // All WebGPU uniforms must be aligned to 16 bytes
#[derive(Clone, Copy, ShaderType, Debug, Hash, Eq, PartialEq, Default, Reflect)]
pub(crate) struct Repeats {
pub(crate) horizontal: u32,
pub(crate) vertical: u32,
pub(crate) _wasm_padding1: u32,
pub(crate) _wasm_padding2: u32,
}

#[derive(AsBindGroup, Debug, Clone, TypeUuid, Reflect)]
#[uuid = "82d336c5-fd6c-41a3-bdd4-267cd4c9be22"]
pub(crate) struct UnlitRepeatedMaterial {
#[texture(0)]
#[sampler(1)]
pub(crate) texture: Handle<Image>,
#[uniform(2)]
pub(crate) repeats: Repeats,
#[uniform(3)]
pub(crate) color: Color,
}

impl Material for UnlitRepeatedMaterial {
fn fragment_shader() -> ShaderRef {
"cosmos/shaders/repeated.wgsl".into()
}

fn alpha_mode(&self) -> AlphaMode {
AlphaMode::Mask(0.5)
}
}

pub(super) fn register(app: &mut App) {
app.add_plugins(MaterialPlugin::<UnlitRepeatedMaterial>::default());
}
10 changes: 10 additions & 0 deletions cosmos_client/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,9 @@ pub mod universe;
pub mod window;

use std::env;
use std::time::Duration;

use bevy::asset::ChangeWatcher;
use bevy::core::TaskPoolThreadAssignmentPolicy;
use bevy_renet::transport::NetcodeClientPlugin;
use cosmos_core::netty::get_local_ipaddress;
Expand Down Expand Up @@ -92,6 +94,14 @@ fn main() {
..Default::default()
},
})
.set(AssetPlugin {
watch_for_changes: if cfg!(debug_assertions) {
ChangeWatcher::with_delay(Duration::from_secs(1))
} else {
None
},
..Default::default()
})
.set(ImagePlugin::default_nearest()),
)
.add_plugins(CosmosCorePluginGroup::new(
Expand Down
158 changes: 152 additions & 6 deletions cosmos_client/src/structure/ship/build_mode.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
//! Handles the build mode logic on the client-side
use bevy::{
prelude::{in_state, App, IntoSystemConfigs, Query, Res, ResMut, Transform, Update, Vec3, With, Without},
prelude::{
in_state, shape, App, AssetServer, Assets, BuildChildren, Changed, Color, Commands, Component, DespawnRecursiveExt, Entity,
EventReader, IntoSystemConfigs, MaterialMeshBundle, Mesh, Parent, Query, Res, ResMut, Transform, Update, Vec3, With, Without,
},
time::Time,
};
use bevy_rapier3d::prelude::Velocity;
Expand All @@ -10,11 +13,14 @@ use cosmos_core::{
netty::{client_reliable_messages::ClientReliableMessages, cosmos_encoder, NettyChannelClient},
structure::{
chunk::CHUNK_DIMENSIONSF,
ship::build_mode::{BuildAxis, BuildMode},
coordinates::BlockCoordinate,
ship::build_mode::{BuildAxis, BuildMode, ExitBuildModeEvent},
Structure,
},
};

use crate::{
asset::repeating_material::{Repeats, UnlitRepeatedMaterial},
input::inputs::{CosmosInputs, InputChecker, InputHandler},
interactions::block_interactions::LookingAt,
netty::flags::LocalPlayer,
Expand All @@ -38,6 +44,9 @@ fn exit_build_mode(
}

Check failure on line 44 in cosmos_client/src/structure/ship/build_mode.rs

View workflow job for this annotation

GitHub Actions / clippy

this `if` statement can be collapsed

error: this `if` statement can be collapsed --> cosmos_client/src/structure/ship/build_mode.rs:37:5 | 37 | / if local_player_in_build_mode.get_single().is_ok() { 38 | | if input_handler.check_just_pressed(CosmosInputs::ToggleBuildMode) { 39 | | client.send_message( 40 | | NettyChannelClient::Reliable, ... | 43 | | } 44 | | } | |_____^ | = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#collapsible_if = note: `-D clippy::collapsible-if` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::collapsible_if)]` help: collapse nested if block | 37 ~ if local_player_in_build_mode.get_single().is_ok() && input_handler.check_just_pressed(CosmosInputs::ToggleBuildMode) { 38 + client.send_message( 39 + NettyChannelClient::Reliable, 40 + cosmos_encoder::serialize(&ClientReliableMessages::ExitBuildMode), 41 + ); 42 + } |
}

#[derive(Component, Clone, Copy, Default)]
struct SymmetryVisuals(Option<Entity>, Option<Entity>, Option<Entity>);

fn control_build_mode(
input_handler: InputChecker,
cam_query: Query<&Transform, With<MainCamera>>,
Expand Down Expand Up @@ -127,7 +136,6 @@ fn place_symmetries(
}

if input_handler.check_just_pressed(CosmosInputs::SymmetryX) {
println!("Sending X {looking_at_block:?}");
client.send_message(
NettyChannelClient::Reliable,
cosmos_encoder::serialize(&ClientReliableMessages::SetSymmetry {
Expand All @@ -138,7 +146,6 @@ fn place_symmetries(
}

if input_handler.check_just_pressed(CosmosInputs::SymmetryY) {
println!("Sending Y {looking_at_block:?}");
client.send_message(
NettyChannelClient::Reliable,
cosmos_encoder::serialize(&ClientReliableMessages::SetSymmetry {
Expand All @@ -149,7 +156,6 @@ fn place_symmetries(
}

if input_handler.check_just_pressed(CosmosInputs::SymmetryZ) {
println!("Sending Z {looking_at_block:?}");
client.send_message(
NettyChannelClient::Reliable,
cosmos_encoder::serialize(&ClientReliableMessages::SetSymmetry {
Expand All @@ -160,10 +166,150 @@ fn place_symmetries(
}
}

fn clear_visuals(
parent_query: Query<&Parent>,
visuals_query: Query<&SymmetryVisuals>,
mut event_reader: EventReader<ExitBuildModeEvent>,
mut commands: Commands,
) {
for ev in event_reader.iter() {
if let Ok(parent) = parent_query.get(ev.player_entity).map(|p| p.get()) {
if let Ok(sym_visuals) = visuals_query.get(parent) {
if let Some(ent) = sym_visuals.0 {
commands.entity(ent).despawn_recursive();
}
if let Some(ent) = sym_visuals.1 {
commands.entity(ent).despawn_recursive();
}
if let Some(ent) = sym_visuals.2 {
commands.entity(ent).despawn_recursive();
}
}

if let Some(mut ecmds) = commands.get_entity(parent) {
ecmds.remove::<SymmetryVisuals>();
}
}
}
}

fn change_visuals(
mut commands: Commands,
query: Query<(&BuildMode, &Parent), (With<LocalPlayer>, Changed<BuildMode>)>,
structure_query: Query<&Structure>,
visuals: Query<&SymmetryVisuals>,
mut meshes: ResMut<Assets<Mesh>>,
mut materials: ResMut<Assets<UnlitRepeatedMaterial>>,
asset_server: Res<AssetServer>,
) {
if let Ok((build_mode, parent)) = query.get_single() {
let structure_entity = parent.get();
let Ok(structure) = structure_query.get(structure_entity) else {
return;
};

let mut visuals = visuals.get(structure_entity).copied().unwrap_or_default();

if let Some(ent) = visuals.0 {
commands.entity(ent).despawn_recursive();
visuals.0 = None;
}
if let Some(ent) = visuals.1 {
commands.entity(ent).despawn_recursive();
visuals.1 = None;
}
if let Some(ent) = visuals.2 {
commands.entity(ent).despawn_recursive();
visuals.2 = None;
}

let texture_handle = asset_server.load("images/misc/symmetry.png");

let size = structure.block_dimensions().x;

if let Some(coords) = build_mode.get_symmetry(BuildAxis::X) {
let coords = structure.block_relative_position(BlockCoordinate::new(coords, 0, 0));

commands.entity(structure_entity).with_children(|ecmds| {
visuals.0 = Some(
ecmds
.spawn(MaterialMeshBundle {
mesh: meshes.add(shape::Box::new(0.001, size as f32, size as f32).into()),
material: materials.add(UnlitRepeatedMaterial {
repeats: Repeats {
horizontal: size as u32,
vertical: size as u32,
..Default::default()
},
texture: texture_handle.clone(),
color: Color::rgb(1.0, 0.0, 0.0).into(),

Check failure on line 245 in cosmos_client/src/structure/ship/build_mode.rs

View workflow job for this annotation

GitHub Actions / clippy

useless conversion to the same type: `bevy::prelude::Color`

error: useless conversion to the same type: `bevy::prelude::Color` --> cosmos_client/src/structure/ship/build_mode.rs:245:40 | 245 | ... color: Color::rgb(1.0, 0.0, 0.0).into(), | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider removing `.into()`: `Color::rgb(1.0, 0.0, 0.0)` | = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#useless_conversion = note: `-D clippy::useless-conversion` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::useless_conversion)]`
}),
transform: Transform::from_xyz(coords.x, 0.5, 0.5),
..Default::default()
})
.id(),
);
});
}

if let Some(coords) = build_mode.get_symmetry(BuildAxis::Y) {
let coords = structure.block_relative_position(BlockCoordinate::new(0, coords, 0));

commands.entity(structure_entity).with_children(|ecmds| {
visuals.1 = Some(
ecmds
.spawn(MaterialMeshBundle {
mesh: meshes.add(shape::Box::new(size as f32, 0.001, size as f32).into()),
material: materials.add(UnlitRepeatedMaterial {
repeats: Repeats {
horizontal: size as u32,
vertical: size as u32,
..Default::default()
},
texture: texture_handle.clone(),
color: Color::rgb(0.0, 1.0, 0.0).into(),

Check failure on line 270 in cosmos_client/src/structure/ship/build_mode.rs

View workflow job for this annotation

GitHub Actions / clippy

useless conversion to the same type: `bevy::prelude::Color`

error: useless conversion to the same type: `bevy::prelude::Color` --> cosmos_client/src/structure/ship/build_mode.rs:270:40 | 270 | ... color: Color::rgb(0.0, 1.0, 0.0).into(), | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider removing `.into()`: `Color::rgb(0.0, 1.0, 0.0)` | = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#useless_conversion
}),
transform: Transform::from_xyz(0.5, coords.y, 0.5),
..Default::default()
})
.id(),
);
});
}

if let Some(coords) = build_mode.get_symmetry(BuildAxis::Z) {
let coords = structure.block_relative_position(BlockCoordinate::new(0, 0, coords));

commands.entity(structure_entity).with_children(|ecmds| {
visuals.2 = Some(
ecmds
.spawn(MaterialMeshBundle {
mesh: meshes.add(shape::Box::new(size as f32, size as f32, 0.001).into()),
material: materials.add(UnlitRepeatedMaterial {
repeats: Repeats {
horizontal: size as u32 / 4,
vertical: size as u32 / 4,
..Default::default()
},
texture: texture_handle.clone(),
color: Color::rgb(0.0, 0.0, 1.0).into(),

Check failure on line 295 in cosmos_client/src/structure/ship/build_mode.rs

View workflow job for this annotation

GitHub Actions / clippy

useless conversion to the same type: `bevy::prelude::Color`

error: useless conversion to the same type: `bevy::prelude::Color` --> cosmos_client/src/structure/ship/build_mode.rs:295:40 | 295 | ... color: Color::rgb(0.0, 0.0, 1.0).into(), | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider removing `.into()`: `Color::rgb(0.0, 0.0, 1.0)` | = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#useless_conversion
}),
transform: Transform::from_xyz(0.5, 0.5, coords.z),
..Default::default()
})
.id(),
);
});
}

commands.entity(structure_entity).insert(visuals);
}
}

pub(super) fn register(app: &mut App) {
app.add_systems(
Update,
(place_symmetries, exit_build_mode, control_build_mode)
(place_symmetries, exit_build_mode, control_build_mode, change_visuals, clear_visuals)
.chain()
.run_if(in_state(GameState::Playing)),
);
Expand Down
Loading

0 comments on commit a740e08

Please sign in to comment.