mirror of
https://github.com/csd4ni3l/simulator-games.git
synced 2026-01-01 04:13:44 +01:00
Added water splash simulator, made settings saving work for all simulators, moved all the simulators into their respective directories, improved the README, fixed too much logging
This commit is contained in:
146
game/water_simulator/game.py
Normal file
146
game/water_simulator/game.py
Normal file
@@ -0,0 +1,146 @@
|
||||
import arcade, arcade.gui, pyglet.gl, array, random, os, json
|
||||
|
||||
from utils.constants import WATER_ROWS, WATER_COLS
|
||||
from game.water_simulator.shader import create_shader
|
||||
|
||||
class Game(arcade.gui.UIView):
|
||||
def __init__(self, pypresence_client):
|
||||
super().__init__()
|
||||
|
||||
self.pypresence_client = pypresence_client
|
||||
self.pypresence_client.update(state="Playing a simulator", details="Water Simulator")
|
||||
|
||||
self.anchor = self.add_widget(arcade.gui.UIAnchorLayout(size_hint=(1, 1)))
|
||||
|
||||
self.settings_box = self.anchor.add(arcade.gui.UIBoxLayout(align="center", size_hint=(0.2, 1)).with_background(color=arcade.color.GRAY), anchor_x="right", anchor_y="bottom")
|
||||
self.settings_label = self.settings_box.add(arcade.gui.UILabel(text="Settings", font_size=24))
|
||||
|
||||
if os.path.exists("data.json"):
|
||||
with open("data.json", "r") as file:
|
||||
self.settings = json.load(file)
|
||||
else:
|
||||
self.settings = {}
|
||||
|
||||
if not "water_simulator" in self.settings:
|
||||
self.settings["water_simulator"] = {
|
||||
"splash_strength": 0.1,
|
||||
"splash_radius": 3,
|
||||
"wave_speed": 1,
|
||||
"damping": 0.02
|
||||
}
|
||||
|
||||
self.splash_row = 0
|
||||
self.splash_col = 0
|
||||
self.current_splash_strength = 0
|
||||
|
||||
self.splash_strength = self.settings["water_simulator"].get("splash_strength", 0.1)
|
||||
self.splash_radius = self.settings["water_simulator"].get("splash_radius", 3)
|
||||
|
||||
self.wave_speed = self.settings["water_simulator"].get("wave_speed", 1)
|
||||
self.damping = self.settings["water_simulator"].get("damping", 0.02)
|
||||
|
||||
def on_show_view(self):
|
||||
super().on_show_view()
|
||||
|
||||
self.settings_box.add(arcade.gui.UISpace(height=self.window.height / 75))
|
||||
|
||||
self.add_setting("Splash Strength: {value}", 0.1, 2.0, 0.1, "splash_strength")
|
||||
self.add_setting("Splash Radius: {value}", 0.5, 10, 0.5, "splash_radius")
|
||||
|
||||
self.settings_box.add(arcade.gui.UISpace(height=self.window.height / 50))
|
||||
|
||||
self.advanced_label = self.settings_box.add(arcade.gui.UILabel("Advanced Settings", font_size=18, multiline=True))
|
||||
|
||||
self.settings_box.add(arcade.gui.UISpace(height=self.window.height / 75))
|
||||
|
||||
self.add_setting("Wave Speed: {value}", 0.1, 1.25, 0.05, "wave_speed")
|
||||
self.add_setting("Damping: {value}", 0.005, 0.05, 0.001, "damping")
|
||||
self.setup_game()
|
||||
|
||||
def on_update(self, delta_time):
|
||||
with self.shader_program:
|
||||
self.shader_program["rows"] = WATER_ROWS
|
||||
self.shader_program["cols"] = WATER_COLS
|
||||
|
||||
self.shader_program["splash_row"] = self.splash_row
|
||||
self.shader_program["splash_col"] = self.splash_col
|
||||
self.shader_program["splash_strength"] = self.current_splash_strength
|
||||
self.shader_program["splash_radius"] = self.splash_radius
|
||||
|
||||
self.shader_program["wave_speed"] = self.wave_speed
|
||||
self.shader_program["damping"] = self.damping
|
||||
|
||||
self.shader_program.dispatch(self.water_image.width, self.water_image.height, 1, barrier=pyglet.gl.GL_ALL_BARRIER_BITS)
|
||||
|
||||
self.current_splash_strength = 0
|
||||
|
||||
def save_data(self):
|
||||
self.settings.update({
|
||||
"water_simulator": {
|
||||
"splash_strength": self.splash_strength,
|
||||
"splash_radius": self.splash_radius,
|
||||
"wave_speed": self.wave_speed,
|
||||
"damping": self.damping
|
||||
}
|
||||
})
|
||||
|
||||
with open("data.json", "w") as file:
|
||||
file.write(json.dumps(self.settings, indent=4))
|
||||
|
||||
def setup_game(self):
|
||||
self.shader_program, self.water_image, self.previous_heights_ssbo, self.current_heights_ssbo = create_shader()
|
||||
|
||||
self.image_sprite = pyglet.sprite.Sprite(img=self.water_image)
|
||||
|
||||
scale_x = (self.window.width * 0.8) / self.image_sprite.width
|
||||
scale_y = self.window.height / self.image_sprite.height
|
||||
|
||||
self.image_sprite.scale_x = scale_x
|
||||
self.image_sprite.scale_y = scale_y
|
||||
|
||||
grid = array.array('f', [random.uniform(-0.01, 0.01) for _ in range(WATER_ROWS * WATER_COLS)])
|
||||
|
||||
self.previous_heights_ssbo.set_data(grid.tobytes())
|
||||
self.current_heights_ssbo.set_data(grid.tobytes())
|
||||
|
||||
def add_setting(self, text, min_value, max_value, step, local_variable, on_change=None):
|
||||
label = self.settings_box.add(arcade.gui.UILabel(text.format(value=getattr(self, local_variable))))
|
||||
slider = self.settings_box.add(arcade.gui.UISlider(value=getattr(self, local_variable), min_value=min_value, max_value=max_value, step=step))
|
||||
slider._render_steps = lambda surface: None
|
||||
|
||||
if on_change:
|
||||
slider.on_change = lambda event, label=label: on_change(label, event.new_value)
|
||||
else:
|
||||
slider.on_change = lambda event, label=label: self.change_value(label, text, local_variable, event.new_value)
|
||||
|
||||
def change_value(self, label, text, local_variable, value):
|
||||
label.text = text.format(value=value)
|
||||
|
||||
self.settings["water_simulator"][local_variable] = value
|
||||
|
||||
setattr(self, local_variable, value)
|
||||
|
||||
def main_exit(self):
|
||||
self.shader_program.delete()
|
||||
self.previous_heights_ssbo.delete()
|
||||
self.current_heights_ssbo.delete()
|
||||
|
||||
def on_key_press(self, symbol, modifiers):
|
||||
if symbol == arcade.key.ESCAPE:
|
||||
self.save_data()
|
||||
|
||||
from menus.main import Main
|
||||
self.window.show_view(Main(self.pypresence_client))
|
||||
|
||||
def on_mouse_press(self, x, y, button, modifiers):
|
||||
col = int(x / (self.window.width * 0.8) * WATER_COLS)
|
||||
row = int(y / self.window.height * WATER_ROWS)
|
||||
|
||||
self.splash_row = row
|
||||
self.splash_col = col
|
||||
self.current_splash_strength = self.splash_strength
|
||||
|
||||
def on_draw(self):
|
||||
super().on_draw()
|
||||
|
||||
self.image_sprite.draw()
|
||||
80
game/water_simulator/shader.py
Normal file
80
game/water_simulator/shader.py
Normal file
@@ -0,0 +1,80 @@
|
||||
import pyglet, pyglet.graphics
|
||||
|
||||
from pyglet.gl import glBindBufferBase, GL_SHADER_STORAGE_BUFFER, GL_NEAREST
|
||||
|
||||
from utils.constants import WATER_ROWS, WATER_COLS
|
||||
|
||||
shader_source = f"""#version 430 core
|
||||
|
||||
layout(std430, binding = 3) buffer PreviousHeights {{
|
||||
float previous_heights[{WATER_ROWS * WATER_COLS}];
|
||||
}};
|
||||
|
||||
layout(std430, binding = 4) buffer CurrentHeights {{
|
||||
float current_heights[{WATER_ROWS * WATER_COLS}];
|
||||
}};
|
||||
|
||||
uniform int rows;
|
||||
uniform int cols;
|
||||
uniform int splash_row;
|
||||
uniform int splash_col;
|
||||
uniform float damping;
|
||||
uniform float wave_speed;
|
||||
uniform float splash_strength;
|
||||
uniform float splash_radius;
|
||||
|
||||
layout (local_size_x = 1, local_size_y = 1, local_size_z = 1) in;
|
||||
layout (location = 0, rgba32f) uniform image2D img_output;
|
||||
|
||||
void main() {{
|
||||
ivec2 texel_coord = ivec2(gl_GlobalInvocationID.xy);
|
||||
|
||||
int row = texel_coord.y * rows / imageSize(img_output).y;
|
||||
int col = texel_coord.x * cols / imageSize(img_output).x;
|
||||
int current_index = (row * cols) + col;
|
||||
|
||||
if(row == 0 || col == 0 || row == rows-1 || col == cols-1) return;
|
||||
|
||||
float dist = distance(vec2(row, col), vec2(splash_row, splash_col));
|
||||
if(dist <= splash_radius) current_heights[current_index] += splash_strength * (1.0 - dist / splash_radius);
|
||||
|
||||
float laplacian = current_heights[(row - 1) * cols + col] +
|
||||
current_heights[(row + 1) * cols + col] +
|
||||
current_heights[row * cols + (col - 1)] +
|
||||
current_heights[row * cols + (col + 1)] -
|
||||
4.0 * current_heights[current_index];
|
||||
|
||||
float dt = 0.1;
|
||||
|
||||
float h_new = 2.0 * current_heights[current_index]
|
||||
- previous_heights[current_index] +
|
||||
(wave_speed * wave_speed)*(dt*dt) * laplacian -
|
||||
damping * (current_heights[current_index] - previous_heights[current_index]);
|
||||
|
||||
previous_heights[current_index] = current_heights[current_index];
|
||||
current_heights[current_index] = h_new;
|
||||
|
||||
float minH = -0.5;
|
||||
float maxH = 0.5;
|
||||
float normH = clamp((h_new - minH) / (maxH - minH), 0.0, 1.0);
|
||||
|
||||
imageStore(img_output, texel_coord, vec4(0.0, 0.0, normH, 1.0));
|
||||
}}
|
||||
|
||||
"""
|
||||
|
||||
def create_shader():
|
||||
shader_program = pyglet.graphics.shader.ComputeShaderProgram(shader_source)
|
||||
|
||||
water_image = pyglet.image.Texture.create(WATER_COLS, WATER_ROWS, internalformat=pyglet.gl.GL_RGBA32F, min_filter=GL_NEAREST, mag_filter=GL_NEAREST)
|
||||
|
||||
uniform_location = shader_program['img_output']
|
||||
water_image.bind_image_texture(unit=uniform_location)
|
||||
|
||||
previous_heights_ssbo = pyglet.graphics.BufferObject(WATER_COLS * WATER_ROWS * 4, usage=pyglet.gl.GL_DYNAMIC_COPY)
|
||||
current_heights_ssbo = pyglet.graphics.BufferObject(WATER_COLS * WATER_ROWS * 4, usage=pyglet.gl.GL_DYNAMIC_COPY)
|
||||
|
||||
glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 3, previous_heights_ssbo.id)
|
||||
glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 4, current_heights_ssbo.id)
|
||||
|
||||
return shader_program, water_image, previous_heights_ssbo, current_heights_ssbo
|
||||
Reference in New Issue
Block a user