feat: redone visualization to be human-generated
This commit is contained in:
parent
9edb79e970
commit
5b4ad9a47a
9 changed files with 214 additions and 24 deletions
|
|
@ -4,4 +4,4 @@ version = "0.1.0"
|
||||||
edition = "2024"
|
edition = "2024"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
bevy = "0.19.0"
|
bevy = { version = "0.19.0", features = ["bevy_feathers"] }
|
||||||
|
|
|
||||||
15
LICENSE
Normal file
15
LICENSE
Normal file
|
|
@ -0,0 +1,15 @@
|
||||||
|
GRES
|
||||||
|
Copyright (C) 2026 Veselov Fedor
|
||||||
|
|
||||||
|
This program is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
@ -10,7 +10,9 @@ impl<'a> GridManipulationTools<'a> {
|
||||||
Self { grid }
|
Self { grid }
|
||||||
}
|
}
|
||||||
pub fn set_xyz(&mut self,x: u32, y: u32, z: u32, element: GridElement) {
|
pub fn set_xyz(&mut self,x: u32, y: u32, z: u32, element: GridElement) {
|
||||||
let index = self.grid.indexify(UVec3::new(x,y,z));
|
let Some(index) = self.grid.indexify(UVec3::new(x,y,z)) else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
self.grid.data[index] = element;
|
self.grid.data[index] = element;
|
||||||
}
|
}
|
||||||
pub fn fill_xyz(&mut self,x1: u32, y1: u32, z1: u32, x2: u32, y2: u32, z2: u32, element: GridElement) {
|
pub fn fill_xyz(&mut self,x1: u32, y1: u32, z1: u32, x2: u32, y2: u32, z2: u32, element: GridElement) {
|
||||||
|
|
@ -21,7 +23,9 @@ impl<'a> GridManipulationTools<'a> {
|
||||||
for x in x1..=x2 {
|
for x in x1..=x2 {
|
||||||
for y in y1..=y2 {
|
for y in y1..=y2 {
|
||||||
for z in z1..=z2 {
|
for z in z1..=z2 {
|
||||||
let index = self.grid.indexify(UVec3::new(x,y,z));
|
let Some(index) = self.grid.indexify(UVec3::new(x,y,z)) else {
|
||||||
|
continue;
|
||||||
|
};
|
||||||
|
|
||||||
self.grid.data[index] = element.clone();
|
self.grid.data[index] = element.clone();
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -26,21 +26,26 @@ impl Grid {
|
||||||
data
|
data
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pub fn indexify(&self, vector: UVec3) -> usize{
|
pub fn cell_amount(&self) -> u32 {
|
||||||
let clamped_vector = vector.min(self.size - UVec3::ONE);
|
return self.size.x * self.size.y * self.size.z;
|
||||||
return (clamped_vector.x + self.size.x * clamped_vector.y + self.size.x * self.size.y * clamped_vector.z) as usize;
|
|
||||||
}
|
}
|
||||||
pub fn deindexify(&self, index: usize) -> UVec3 {
|
pub fn indexify(&self, vector: UVec3) -> Option<usize>{
|
||||||
let undex = index as u32;
|
if vector.x >= self.size.x || vector.y >= self.size.y || vector.z >= self.size.z {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
return Some((vector.x + self.size.x * vector.y + self.size.x * self.size.y * vector.z) as usize);
|
||||||
|
}
|
||||||
|
pub fn deindexify(&self, index: u32) -> UVec3 {
|
||||||
return UVec3::new(
|
return UVec3::new(
|
||||||
undex % self.size.x,
|
index % self.size.x,
|
||||||
undex / self.size.x % self.size.y,
|
index / self.size.x % self.size.y,
|
||||||
undex / (self.size.x*self.size.y)
|
index / (self.size.x*self.size.y)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
pub fn tranfer_fluid(&mut self,from: usize, to: usize, transfer_amount: u32) {
|
pub fn tranfer_fluid(&mut self,from: usize, to: usize, transfer_amount: u32) {
|
||||||
let bounds = self.indexify(self.size);
|
let bounds = self.cell_amount() as usize;
|
||||||
if from >= bounds || to >= bounds {
|
if from >= bounds || to >= bounds {
|
||||||
|
error!("Trying to transfer fluid out of bounds!");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,8 @@ use bevy::prelude::*;
|
||||||
use crate::grid::Grid;
|
use crate::grid::Grid;
|
||||||
use crate::simulation::tick;
|
use crate::simulation::tick;
|
||||||
|
|
||||||
|
use crate::view::ViewPlugin;
|
||||||
|
|
||||||
pub mod simulation;
|
pub mod simulation;
|
||||||
pub mod view;
|
pub mod view;
|
||||||
pub mod grid;
|
pub mod grid;
|
||||||
|
|
@ -9,8 +11,9 @@ pub mod grid;
|
||||||
fn main() {
|
fn main() {
|
||||||
App::new()
|
App::new()
|
||||||
.add_plugins(DefaultPlugins)
|
.add_plugins(DefaultPlugins)
|
||||||
|
.add_plugins(ViewPlugin)
|
||||||
.insert_resource(Grid::cube(16))
|
.insert_resource(Grid::cube(16))
|
||||||
.insert_resource(Time::<Fixed>::from_hz(120.))
|
.insert_resource(Time::<Fixed>::from_hz(20.))
|
||||||
.add_systems(FixedUpdate, tick)
|
.add_systems(FixedUpdate, tick)
|
||||||
.run();
|
.run();
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -16,7 +16,9 @@ pub fn tick(mut grid: ResMut<Grid>) {
|
||||||
for y in 0..grid.size.y {
|
for y in 0..grid.size.y {
|
||||||
for z in 0..grid.size.z {
|
for z in 0..grid.size.z {
|
||||||
let vector = UVec3::new(x,y,z);
|
let vector = UVec3::new(x,y,z);
|
||||||
let index = grid.indexify(vector);
|
let Some(index) = grid.indexify(vector) else {
|
||||||
|
continue;
|
||||||
|
};
|
||||||
|
|
||||||
match grid.data[index as usize] {
|
match grid.data[index as usize] {
|
||||||
GridElement::Water { amount } => {
|
GridElement::Water { amount } => {
|
||||||
|
|
@ -24,7 +26,7 @@ pub fn tick(mut grid: ResMut<Grid>) {
|
||||||
|
|
||||||
if let Some(gravity_vector) = vector.checked_sub(UVec3::new(0,1,0)) {
|
if let Some(gravity_vector) = vector.checked_sub(UVec3::new(0,1,0)) {
|
||||||
if let Some(gravity_amount) = grid.data
|
if let Some(gravity_amount) = grid.data
|
||||||
[grid.indexify(gravity_vector)]
|
[grid.indexify(gravity_vector).unwrap()]
|
||||||
.try_fill(transfer_amount.min(MAX_VERTICAL_FLOW)) {
|
.try_fill(transfer_amount.min(MAX_VERTICAL_FLOW)) {
|
||||||
stack.push(GridAction::MoveLiquid { from: vector, to: gravity_vector, amount: gravity_amount });
|
stack.push(GridAction::MoveLiquid { from: vector, to: gravity_vector, amount: gravity_amount });
|
||||||
transfer_amount -= gravity_amount;
|
transfer_amount -= gravity_amount;
|
||||||
|
|
@ -35,18 +37,22 @@ pub fn tick(mut grid: ResMut<Grid>) {
|
||||||
if transfer_amount > 0 {
|
if transfer_amount > 0 {
|
||||||
for lookup_vector in [UVec3::new(1,0,0),UVec3::new(0,0,1)] {
|
for lookup_vector in [UVec3::new(1,0,0),UVec3::new(0,0,1)] {
|
||||||
if let Some(toward_vector) = vector.checked_add(lookup_vector) {
|
if let Some(toward_vector) = vector.checked_add(lookup_vector) {
|
||||||
if let Some(toward_amount) = grid.data
|
if let Some(index) = grid.indexify(toward_vector) {
|
||||||
[grid.indexify(toward_vector)]
|
if let Some(toward_amount) = grid.data
|
||||||
.try_fill(transfer_amount.min(MAX_VERTICAL_FLOW)) {
|
[index]
|
||||||
stack.push(GridAction::MoveLiquid { from: vector, to: toward_vector, amount: toward_amount });
|
.try_fill(transfer_amount.min(MAX_VERTICAL_FLOW)) {
|
||||||
|
stack.push(GridAction::MoveLiquid { from: vector, to: toward_vector, amount: toward_amount });
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(toward_vector) = vector.checked_sub(lookup_vector) {
|
if let Some(toward_vector) = vector.checked_sub(lookup_vector) {
|
||||||
if let Some(toward_amount) = grid.data
|
if let Some(index) = grid.indexify(toward_vector) {
|
||||||
[grid.indexify(toward_vector)]
|
if let Some(toward_amount) = grid.data
|
||||||
.try_fill(transfer_amount.min(MAX_VERTICAL_FLOW)) {
|
[index]
|
||||||
stack.push(GridAction::MoveLiquid { from: vector, to: toward_vector, amount: toward_amount });
|
.try_fill(transfer_amount.min(MAX_VERTICAL_FLOW)) {
|
||||||
|
stack.push(GridAction::MoveLiquid { from: vector, to: toward_vector, amount: toward_amount });
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -62,10 +68,11 @@ pub fn tick(mut grid: ResMut<Grid>) {
|
||||||
for action in stack {
|
for action in stack {
|
||||||
match action {
|
match action {
|
||||||
GridAction::MoveLiquid { from, to, amount } => {
|
GridAction::MoveLiquid { from, to, amount } => {
|
||||||
let (from_index, to_index) = (grid.indexify(from), grid.indexify(to));
|
let (from_index, to_index) = (grid.indexify(from).unwrap(), grid.indexify(to).unwrap());
|
||||||
grid.tranfer_fluid(from_index, to_index, amount);
|
grid.tranfer_fluid(from_index, to_index, amount);
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
142
src/view/game.rs
Normal file
142
src/view/game.rs
Normal file
|
|
@ -0,0 +1,142 @@
|
||||||
|
use bevy::prelude::*;
|
||||||
|
use crate::grid::Grid;
|
||||||
|
use crate::MAX_FLUID_AMOUNT;
|
||||||
|
|
||||||
|
const FLUID_FULLNES_STAGES: usize = 7;
|
||||||
|
|
||||||
|
pub struct GameViewPlugin;
|
||||||
|
|
||||||
|
impl Plugin for GameViewPlugin {
|
||||||
|
fn build(&self, app: &mut App) {
|
||||||
|
app.add_systems(PreStartup, debug_setup_grid)
|
||||||
|
.init_resource::<CubeStages>()
|
||||||
|
.init_resource::<TileMaterials>()
|
||||||
|
.add_systems(Startup, (setup_view_cubes, scene.spawn()))
|
||||||
|
.add_systems(Update, update_cubes);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Resource, Default)]
|
||||||
|
pub struct CubeStages(pub Vec<Handle<Mesh>>);
|
||||||
|
|
||||||
|
#[derive(Component, FromTemplate)]
|
||||||
|
pub struct TileWatch(pub usize);
|
||||||
|
|
||||||
|
#[derive(Resource, Default)]
|
||||||
|
pub struct TileMaterials {
|
||||||
|
pub solid: Handle<StandardMaterial>,
|
||||||
|
pub water: Handle<StandardMaterial>,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn debug_setup_grid(mut grid: ResMut<Grid>) {
|
||||||
|
use crate::grid::GridElement;
|
||||||
|
let mut tool = grid.manipulate();
|
||||||
|
tool.fill_xyz(5, 0, 0, 5, 0, 14, GridElement::Solid);
|
||||||
|
tool.fill_xyz(0, 0, 0, 4, 0, 15, GridElement::Water { amount: MAX_FLUID_AMOUNT });
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn setup_view_cubes(
|
||||||
|
mut commands: Commands,
|
||||||
|
mut meshes: ResMut<Assets<Mesh>>,
|
||||||
|
mut materials: ResMut<Assets<StandardMaterial>>,
|
||||||
|
mut stages: ResMut<CubeStages>,
|
||||||
|
mut tile_materials: ResMut<TileMaterials>,
|
||||||
|
grid: Res<Grid>) {
|
||||||
|
for i in 0..FLUID_FULLNES_STAGES {
|
||||||
|
let tile_height = ((i+1) as f32)/(FLUID_FULLNES_STAGES as f32);
|
||||||
|
let mesh = meshes.add(Cuboid::new(1., tile_height, 1.));
|
||||||
|
stages.0.push(mesh);
|
||||||
|
}
|
||||||
|
|
||||||
|
tile_materials.water = materials.add(StandardMaterial {
|
||||||
|
base_color: Color::hsl(172.,0.502,0.571),
|
||||||
|
..default()
|
||||||
|
});
|
||||||
|
|
||||||
|
tile_materials.solid = materials.add(StandardMaterial {
|
||||||
|
base_color: Color::hsl(268., 0.778, 0.499),
|
||||||
|
..default()
|
||||||
|
});
|
||||||
|
|
||||||
|
let mesh = stages.0[FLUID_FULLNES_STAGES-1].clone();
|
||||||
|
|
||||||
|
for i in 0..grid.cell_amount() {
|
||||||
|
commands.spawn_scene(
|
||||||
|
cube(
|
||||||
|
grid.deindexify(i as u32).as_vec3(),
|
||||||
|
i as usize,
|
||||||
|
mesh.clone()
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn scene() -> impl SceneList {
|
||||||
|
bsn_list![
|
||||||
|
camera(),
|
||||||
|
sun()
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn camera() -> impl Scene {
|
||||||
|
bsn! {
|
||||||
|
Camera3d
|
||||||
|
template(
|
||||||
|
|ctx| {
|
||||||
|
let grid = ctx.resource::<Grid>();
|
||||||
|
let center = grid.size.as_vec3() * 0.5;
|
||||||
|
Ok(Transform::from_xyz(
|
||||||
|
center.x + grid.size.x as f32 * 1.5,
|
||||||
|
grid.size.y as f32 * 1.5,
|
||||||
|
center.z + grid.size.z as f32 * 1.5).looking_at(center, Vec3::Y))
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn sun() -> impl Scene {
|
||||||
|
bsn!{
|
||||||
|
DirectionalLight {
|
||||||
|
illuminance: 8_000.0,
|
||||||
|
shadow_maps_enabled: false,
|
||||||
|
}
|
||||||
|
template_value(Transform::from_xyz(10.0, 30.0, 10.0).looking_at(Vec3::ZERO, Vec3::Y))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
pub fn cube(position: Vec3,index: usize,mesh: Handle<Mesh>) -> impl Scene {
|
||||||
|
bsn! {
|
||||||
|
Mesh3d(mesh)
|
||||||
|
MeshMaterial3d<StandardMaterial>
|
||||||
|
Transform::from_translation(position)
|
||||||
|
TileWatch(index)
|
||||||
|
Visibility::Hidden
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn update_cubes(query: Query<(&mut Mesh3d,&mut Visibility,&mut MeshMaterial3d<StandardMaterial>,&TileWatch)>, materials: Res<TileMaterials>, stages: Res<CubeStages>, grid: Res<Grid>) {
|
||||||
|
if grid.is_changed() == false { return; }
|
||||||
|
|
||||||
|
for (mut mesh,mut visibility, mut material, index) in query {
|
||||||
|
let state = grid.data[index.0];
|
||||||
|
|
||||||
|
match state {
|
||||||
|
crate::grid::GridElement::Air => {
|
||||||
|
*visibility = Visibility::Hidden;
|
||||||
|
},
|
||||||
|
crate::grid::GridElement::Water { amount } => {
|
||||||
|
*visibility = Visibility::Visible;
|
||||||
|
material.0 = materials.water.clone();
|
||||||
|
let stage = ((amount as f32)/(MAX_FLUID_AMOUNT as f32) * (FLUID_FULLNES_STAGES-1) as f32).round() as usize;
|
||||||
|
mesh.0 = stages.0[stage].clone();
|
||||||
|
},
|
||||||
|
crate::grid::GridElement::Solid => {
|
||||||
|
*visibility = Visibility::Visible;
|
||||||
|
material.0 = materials.solid.clone();
|
||||||
|
mesh.0 = stages.0[FLUID_FULLNES_STAGES-1].clone();
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,14 @@
|
||||||
|
use bevy::prelude::*;
|
||||||
|
|
||||||
|
use game::GameViewPlugin;
|
||||||
|
|
||||||
|
pub mod ui;
|
||||||
|
pub mod game;
|
||||||
|
|
||||||
|
pub struct ViewPlugin;
|
||||||
|
|
||||||
|
impl Plugin for ViewPlugin {
|
||||||
|
fn build(&self, app: &mut App) {
|
||||||
|
app.add_plugins(GameViewPlugin);
|
||||||
|
}
|
||||||
|
}
|
||||||
0
src/view/ui.rs
Normal file
0
src/view/ui.rs
Normal file
Loading…
Add table
Add a link
Reference in a new issue