Massive changes to the repository have been made.
feat: Performance optimizations and UI improvements - Added dormant state tracking for static particles - Fixed settings menu particle spawn overlap - Optimized particle batch processing - Added brush cursor visualization - Improved UI interaction zones - Enhanced gas particle effects - Added glow toggle functionality disabled by default due to performance impact - Fixed particle rendering issues - Debug Overlay Disabled by default - FPS counter added semi seperate from Debug Overlay, Turn this one off when using Debug Overlay - Improved particle rendering performance Performance improvements focus on reducing unnecessary calculations for static particles while maintaining core simulation mechanics. UI changes prevent unwanted particle spawning during menu interactions.
This commit is contained in:
parent
33404edcc1
commit
986750c722
11
.gitignore
vendored
11
.gitignore
vendored
@ -1,9 +1,8 @@
|
||||
# ---> Python
|
||||
# Byte-compiled / optimized / DLL files
|
||||
__pycache__/
|
||||
*.py[cod]
|
||||
*$py.class
|
||||
|
||||
__pycache__/
|
||||
# C extensions
|
||||
*.so
|
||||
|
||||
@ -160,9 +159,17 @@ cython_debug/
|
||||
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
|
||||
#.idea/
|
||||
|
||||
# Custom Ignores
|
||||
current_stations.html
|
||||
forecast_data.json
|
||||
openapi.json
|
||||
__pycache__/sim.cpython-312.pyc
|
||||
__pycache__/sim.cpython-312.pyc
|
||||
__pycache__/rendering.cpython-312.pyc
|
||||
sandpypi.dist/
|
||||
sandpypi.build/
|
||||
sandpypi.onefile-build/
|
||||
sandpypi.exe
|
||||
sandpypi.7z
|
||||
unittest/
|
||||
.7z
|
||||
23
README.md
23
README.md
@ -4,6 +4,29 @@ mostly a concept in python for falling sand simulation
|
||||
i guess the goal is to make a python version of the powder toy which is a falling sand sim
|
||||
the code is not finished yet, but i will update it as i go.
|
||||
|
||||
Main Features:
|
||||
-------------
|
||||
- Particle Physics
|
||||
- Gravity and wind effects
|
||||
- Temperature dynamics
|
||||
- State transitions (melting, freezing, evaporation)
|
||||
|
||||
- Particle Interactions
|
||||
- Collision detection
|
||||
- Chemical reactions (e.g., water + sand = mud)
|
||||
- Heat transfer between particles
|
||||
|
||||
- Special Effects
|
||||
- Fire propagation
|
||||
- Smoke generation
|
||||
- Liquid spreading
|
||||
|
||||
- Optimization Features
|
||||
- Spatial partitioning grid
|
||||
- Dormant particle tracking
|
||||
- Batch processing
|
||||
"""
|
||||
|
||||
## **Current Features**
|
||||
|
||||
| **Working** | **Partial** | **Not Working/Implemented** |
|
||||
|
||||
1
__init__.py
Normal file
1
__init__.py
Normal file
@ -0,0 +1 @@
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
820
particles.json
820
particles.json
@ -1,331 +1,333 @@
|
||||
{
|
||||
"sand": {
|
||||
"name": "Sand",
|
||||
"size": 1,
|
||||
"hardness": 0.5,
|
||||
"color": [255, 255, 0, 255],
|
||||
"velocity": 0.5,
|
||||
"mass": 0.5,
|
||||
"conductivity": 0,
|
||||
"heat_capacity": 1,
|
||||
"flamability": 0.8,
|
||||
"temperature": 0,
|
||||
"explosive": false,
|
||||
"explosion_radius": 0,
|
||||
"explosion_color": [0, 0, 0],
|
||||
"friction": 0.5,
|
||||
"viscosity": 0,
|
||||
"pressure": 0,
|
||||
"melt": "molten-Glass",
|
||||
"melt_temperature": 1000,
|
||||
"conductive": false,
|
||||
"liquid": false,
|
||||
"solid": true,
|
||||
"is_gas": false
|
||||
},
|
||||
"water": {
|
||||
"name": "Water",
|
||||
"size": 1,
|
||||
"hardness": 0.2,
|
||||
"velocity": 0.3,
|
||||
"conductivity": 1,
|
||||
"heat_capacity": 1,
|
||||
"color": [0, 0, 255, 255],
|
||||
"mass": 1,
|
||||
"flamability": 0,
|
||||
"temperature": 22,
|
||||
"explosive": false,
|
||||
"explosion_radius": 0,
|
||||
"explosion_color": [0, 0, 0],
|
||||
"friction": 1,
|
||||
"viscosity": 1,
|
||||
"pressure": 0.5,
|
||||
"evaporate": "steam",
|
||||
"evaporate_temperature": 145,
|
||||
"freeze": "ice",
|
||||
"freeze_temperature": 0,
|
||||
"melt": "water",
|
||||
"melt_temperature": 20,
|
||||
"liquid": true,
|
||||
"solid": false,
|
||||
"is_gas": false
|
||||
},
|
||||
"steam": {
|
||||
"name": "Steam",
|
||||
"size": 1,
|
||||
"hardness": 0.0,
|
||||
"velocity": 0.2,
|
||||
"conductivity": 1,
|
||||
"heat_capacity": 1,
|
||||
"color": [255, 255, 255, 255],
|
||||
"mass": 0.01,
|
||||
"flamability": 0,
|
||||
"temperature": 100,
|
||||
"solidify_temperature": 98,
|
||||
"solidify": "water",
|
||||
"explosive": false,
|
||||
"explosion_radius": 0,
|
||||
"explosion_color": [0, 0, 0],
|
||||
"friction": 0.5,
|
||||
"viscosity": 0.5,
|
||||
"liquid": false,
|
||||
"solid": false,
|
||||
"is_gas": true,
|
||||
"conductive": false
|
||||
},
|
||||
"ice": {
|
||||
"name": "Ice",
|
||||
"size": 1,
|
||||
"hardness": 1000,
|
||||
"velocity": 0.0,
|
||||
"conductivity": 0,
|
||||
"heat_capacity": 0,
|
||||
"color": [75, 75, 75, 255],
|
||||
"mass": 1,
|
||||
"flamability": 0.0,
|
||||
"temperature": 0,
|
||||
"explosive": false,
|
||||
"explosion_radius": 0,
|
||||
"explosion_color": [0, 0, 0],
|
||||
"friction": 1,
|
||||
"viscosity": 1,
|
||||
"liquid": false,
|
||||
"solid": true,
|
||||
"is_gas": false
|
||||
},
|
||||
"mud": {
|
||||
"name": "Mud",
|
||||
"size": 1,
|
||||
"hardness": 0.4,
|
||||
"velocity": 0.5,
|
||||
"conductivity": 1,
|
||||
"heat_capacity": 1,
|
||||
"color": [139, 69, 19, 255],
|
||||
"mass": 0.5,
|
||||
"flamability": 0,
|
||||
"temperature": 0,
|
||||
"explosive": false,
|
||||
"explosion_radius": 0,
|
||||
"explosion_color": [0, 0, 0],
|
||||
"friction": 0.5,
|
||||
"viscosity": 1,
|
||||
"liquid": false,
|
||||
"solid": true,
|
||||
"is_gas": false
|
||||
},
|
||||
"fire": {
|
||||
"name": "Fire",
|
||||
"size": 1,
|
||||
"hardness": 0.1,
|
||||
"velocity": 0.1,
|
||||
"conductivity": 0,
|
||||
"heat_capacity": 1,
|
||||
"color": [255, 0, 0, 255],
|
||||
"mass": 0.1,
|
||||
"flamability": 1,
|
||||
"temperature": 1000,
|
||||
"explosive": false,
|
||||
"explosion_radius": 0,
|
||||
"explosion_color": [0, 0, 0],
|
||||
"friction": 0.1,
|
||||
"viscosity": 0.1,
|
||||
"liquid": false,
|
||||
"solid": false,
|
||||
"is_gas": true
|
||||
},
|
||||
"smoke": {
|
||||
"name": "Smoke",
|
||||
"size": 1,
|
||||
"hardness": 0.1,
|
||||
"velocity": 0.1,
|
||||
"conductivity": 0,
|
||||
"heat_capacity": 1,
|
||||
"color": [115, 113, 95, 255],
|
||||
"mass": 0.01,
|
||||
"flamability": 0,
|
||||
"temperature": 85,
|
||||
"explosive": false,
|
||||
"explosion_radius": 0,
|
||||
"explosion_color": [0, 0, 0],
|
||||
"friction": 0.4,
|
||||
"viscosity": 0.1,
|
||||
"lifetime": 90,
|
||||
"liquid": false,
|
||||
"solid": false,
|
||||
"is_gas": true
|
||||
},
|
||||
"wall": {
|
||||
"name": "Wall",
|
||||
"size": 1,
|
||||
"hardness": 1000,
|
||||
"velocity": 0.0,
|
||||
"conductivity": 0,
|
||||
"heat_capacity": 0,
|
||||
"color": [75, 75, 75, 255],
|
||||
"mass": 1,
|
||||
"flamability": 0.1,
|
||||
"temperature": 0,
|
||||
"explosive": false,
|
||||
"explosion_radius": 0,
|
||||
"explosion_color": [0, 0, 0],
|
||||
"friction": 1,
|
||||
"viscosity": 1,
|
||||
"liquid": false,
|
||||
"solid": true,
|
||||
"is_gas": false
|
||||
},
|
||||
"dirt": {
|
||||
"name": "Dirt",
|
||||
"size": 1,
|
||||
"hardness": 0.5,
|
||||
"velocity": 0.5,
|
||||
"conductivity": 0,
|
||||
"heat_capacity": 1,
|
||||
"color": [139, 69, 19, 255],
|
||||
"mass": 0.5,
|
||||
"flamability": 0,
|
||||
"temperature": 0,
|
||||
"explosive": false,
|
||||
"explosion_radius": 0,
|
||||
"explosion_color": [0, 0, 0],
|
||||
"friction": 0.5,
|
||||
"viscosity": 0.5,
|
||||
"liquid": false,
|
||||
"solid": true,
|
||||
"is_gas": false
|
||||
},
|
||||
"stone": {
|
||||
"name": "Stone",
|
||||
"size": 1,
|
||||
"hardness": 0.7,
|
||||
"velocity": 1.5,
|
||||
"conductivity": 0,
|
||||
"heat_capacity": 0,
|
||||
"color": [128, 128, 128, 255],
|
||||
"mass": 1,
|
||||
"flamability": 0,
|
||||
"melt": "molten-Stone",
|
||||
"melt_temperature": 800,
|
||||
"solidify": "stone",
|
||||
"solidify_temperature": 799,
|
||||
"temperature": 0,
|
||||
"explosive": false,
|
||||
"explosion_radius": 0,
|
||||
"explosion_color": [0, 0, 0],
|
||||
"friction": 0.5,
|
||||
"viscosity": 0.5,
|
||||
"liquid": false,
|
||||
"solid": true,
|
||||
"is_gas": false
|
||||
},
|
||||
"snow": {
|
||||
"name": "Snow",
|
||||
"size": 1,
|
||||
"hardness": 0.1,
|
||||
"velocity": 0.2,
|
||||
"conductivity": 1,
|
||||
"heat_capacity": 1,
|
||||
"color": [255, 255, 255, 255],
|
||||
"mass": 0.01,
|
||||
"flamability": 0,
|
||||
"melt": "water",
|
||||
"melt_temperature": 10,
|
||||
"temperature": 0,
|
||||
"explosive": false,
|
||||
"explosion_radius": 0,
|
||||
"explosion_color": [0, 0, 0],
|
||||
"friction": 0.1,
|
||||
"viscosity": 0.01,
|
||||
"liquid": false,
|
||||
"solid": true,
|
||||
"is_gas": false
|
||||
},
|
||||
"wood": {
|
||||
"name": "Wood",
|
||||
"size": 1,
|
||||
"hardness": 0.5,
|
||||
"velocity": 0.5,
|
||||
"conductivity": 0,
|
||||
"heat_capacity": 1,
|
||||
"color": [139, 69, 19, 255],
|
||||
"mass": 0.5,
|
||||
"flamability": 0.8,
|
||||
"burning_temperature": 250,
|
||||
"burning_rate": 0.01,
|
||||
"burning_color": [255, 0, 0, 255],
|
||||
"burning": false,
|
||||
"temperature": 0,
|
||||
"explosive": false,
|
||||
"explosion_radius": 0,
|
||||
"explosion_color": [0, 0, 0],
|
||||
"friction": 0.5,
|
||||
"viscosity": 0.5,
|
||||
"liquid": false,
|
||||
"solid": true,
|
||||
"is_gas": false
|
||||
},
|
||||
"burning-wood": {
|
||||
"name": "Burning Wood",
|
||||
"size": 1,
|
||||
"hardness": 0.5,
|
||||
"velocity": 0.5,
|
||||
"conductivity": 0,
|
||||
"heat_capacity": 1,
|
||||
"color": [139, 69, 19, 255],
|
||||
"mass": 0.5,
|
||||
"flamability": 0.8,
|
||||
"temperature": 0,
|
||||
"explosive": false,
|
||||
"explosion_radius": 0,
|
||||
"explosion_color": [0, 0, 0],
|
||||
"friction": 0.5,
|
||||
"viscosity": 0.5,
|
||||
"liquid": false,
|
||||
"solid": true,
|
||||
"is_gas": false
|
||||
},
|
||||
"air": {
|
||||
"name": "Air",
|
||||
"size": 1,
|
||||
"hardness": 0.0,
|
||||
"velocity": 0.0,
|
||||
"conductivity": 0,
|
||||
"heat_capacity": 1,
|
||||
"color": [25, 25, 25, 25],
|
||||
"mass": 0.0,
|
||||
"flamability": 0,
|
||||
"temperature": 0,
|
||||
"explosive": false,
|
||||
"explosion_radius": 0,
|
||||
"explosion_color": [0, 0, 0],
|
||||
"friction": 0.0,
|
||||
"viscosity": 0.0,
|
||||
"liquid": false,
|
||||
"solid": false,
|
||||
"is_gas": true
|
||||
},
|
||||
"lava": {
|
||||
"name": "Lava",
|
||||
"size": 1,
|
||||
"hardness": 0.2,
|
||||
"velocity": 0.5,
|
||||
"conductivity": 0,
|
||||
"heat_capacity": 1,
|
||||
"color": [255, 45, 24, 255],
|
||||
"mass": 0.3,
|
||||
"flamability": 0,
|
||||
"temperature": 2700,
|
||||
"solidify": "molten-rock",
|
||||
"solidify_temperature": 799,
|
||||
"explosive": false,
|
||||
"explosion_radius": 0,
|
||||
"explosion_color": [0, 0, 0],
|
||||
"friction": 0.8,
|
||||
"viscosity": 0.8,
|
||||
"liquid": true,
|
||||
"solid": false,
|
||||
"is_gas": false
|
||||
},
|
||||
"sand": {
|
||||
"name": "Sand",
|
||||
"size": 1,
|
||||
"hardness": 0.5,
|
||||
"color": [255, 255, 0, 255],
|
||||
"velocity": 0.5,
|
||||
"mass": 0.5,
|
||||
"conductivity": 0,
|
||||
"heat_capacity": 1,
|
||||
"flamability": 0.8,
|
||||
"temperature": 0,
|
||||
"explosive": false,
|
||||
"explosion_radius": 0,
|
||||
"explosion_color": [0, 0, 0],
|
||||
"friction": 0.5,
|
||||
"viscosity": 0,
|
||||
"pressure": 0,
|
||||
"melt": "molten-Glass",
|
||||
"melt_temperature": 1700,
|
||||
"conductive": false,
|
||||
"liquid": false,
|
||||
"solid": true,
|
||||
"is_gas": false
|
||||
},
|
||||
"water": {
|
||||
"name": "Water",
|
||||
"size": 1,
|
||||
"hardness": 0.2,
|
||||
"velocity": 0.3,
|
||||
"conductivity": 1,
|
||||
"heat_capacity": 1,
|
||||
"color": [0, 0, 255, 255],
|
||||
"mass": 1,
|
||||
"flamability": 0,
|
||||
"temperature": 22,
|
||||
"explosive": false,
|
||||
"explosion_radius": 0,
|
||||
"explosion_color": [0, 0, 0],
|
||||
"friction": 1,
|
||||
"viscosity": 1,
|
||||
"pressure": 0.5,
|
||||
"evaporate": "steam",
|
||||
"evaporate_temperature": 100,
|
||||
"freeze": "ice",
|
||||
"freeze_temperature": 0,
|
||||
"conductive": true,
|
||||
"liquid": true,
|
||||
"solid": false,
|
||||
"is_gas": false
|
||||
},
|
||||
"steam": {
|
||||
"name": "Steam",
|
||||
"size": 1,
|
||||
"hardness": 0.0,
|
||||
"velocity": 0.3,
|
||||
"conductivity": 1,
|
||||
"heat_capacity": 1,
|
||||
"color": [255, 255, 255, 255],
|
||||
"mass": 0.01,
|
||||
"flamability": 0,
|
||||
"temperature": 100,
|
||||
"solidify_temperature": 98,
|
||||
"solidify": "water",
|
||||
"explosive": false,
|
||||
"explosion_radius": 0,
|
||||
"explosion_color": [0, 0, 0],
|
||||
"friction": 0.5,
|
||||
"viscosity": 0.5,
|
||||
"liquid": false,
|
||||
"solid": false,
|
||||
"is_gas": true,
|
||||
"conductive": false
|
||||
},
|
||||
"ice": {
|
||||
"name": "Ice",
|
||||
"size": 1,
|
||||
"hardness": 1000,
|
||||
"velocity": 0.0,
|
||||
"conductivity": 0,
|
||||
"heat_capacity": 0,
|
||||
"color": [75, 75, 75, 255],
|
||||
"mass": 1,
|
||||
"flamability": 0.0,
|
||||
"temperature": 0,
|
||||
"melt": "water",
|
||||
"melt_temperature": 0.05,
|
||||
"explosive": false,
|
||||
"explosion_radius": 0,
|
||||
"explosion_color": [0, 0, 0],
|
||||
"friction": 1,
|
||||
"viscosity": 1,
|
||||
"liquid": false,
|
||||
"solid": true,
|
||||
"is_gas": false
|
||||
},
|
||||
"mud": {
|
||||
"name": "Mud",
|
||||
"size": 1,
|
||||
"hardness": 0.4,
|
||||
"velocity": 0.5,
|
||||
"conductivity": 1,
|
||||
"heat_capacity": 1,
|
||||
"color": [139, 69, 19, 255],
|
||||
"mass": 0.5,
|
||||
"flamability": 0,
|
||||
"temperature": 0,
|
||||
"explosive": false,
|
||||
"explosion_radius": 0,
|
||||
"explosion_color": [0, 0, 0],
|
||||
"friction": 0.5,
|
||||
"viscosity": 1,
|
||||
"liquid": false,
|
||||
"solid": true,
|
||||
"is_gas": false
|
||||
},
|
||||
"fire": {
|
||||
"name": "Fire",
|
||||
"size": 1,
|
||||
"hardness": 0.1,
|
||||
"velocity": 0.1,
|
||||
"conductivity": 0,
|
||||
"heat_capacity": 1,
|
||||
"color": [255, 0, 0, 255],
|
||||
"mass": 0.1,
|
||||
"flamability": 1,
|
||||
"temperature": 800,
|
||||
"explosive": false,
|
||||
"explosion_radius": 0,
|
||||
"explosion_color": [0, 0, 0],
|
||||
"friction": 0.1,
|
||||
"viscosity": 0.1,
|
||||
"liquid": false,
|
||||
"solid": false,
|
||||
"is_gas": true
|
||||
},
|
||||
"smoke": {
|
||||
"name": "Smoke",
|
||||
"size": 1,
|
||||
"hardness": 0.1,
|
||||
"velocity": 0.07,
|
||||
"conductivity": 0,
|
||||
"heat_capacity": 1,
|
||||
"color": [115, 113, 95, 255],
|
||||
"mass": 0.01,
|
||||
"flamability": 0,
|
||||
"temperature": 85,
|
||||
"explosive": false,
|
||||
"explosion_radius": 0,
|
||||
"explosion_color": [0, 0, 0],
|
||||
"friction": 0.4,
|
||||
"viscosity": 0.1,
|
||||
"lifetime": 90,
|
||||
"liquid": false,
|
||||
"solid": false,
|
||||
"is_gas": true
|
||||
},
|
||||
"wall": {
|
||||
"name": "Wall",
|
||||
"size": 1,
|
||||
"hardness": 1000,
|
||||
"velocity": 0.0,
|
||||
"conductivity": 0,
|
||||
"heat_capacity": 0,
|
||||
"color": [75, 75, 75, 255],
|
||||
"mass": 1,
|
||||
"flamability": 0,
|
||||
"temperature": 0,
|
||||
"explosive": false,
|
||||
"explosion_radius": 0,
|
||||
"explosion_color": [0, 0, 0],
|
||||
"friction": 1,
|
||||
"viscosity": 1,
|
||||
"liquid": false,
|
||||
"solid": true,
|
||||
"is_gas": false
|
||||
},
|
||||
"dirt": {
|
||||
"name": "Dirt",
|
||||
"size": 1,
|
||||
"hardness": 0.5,
|
||||
"velocity": 0.5,
|
||||
"conductivity": 0,
|
||||
"heat_capacity": 1,
|
||||
"color": [139, 69, 19, 255],
|
||||
"mass": 0.5,
|
||||
"flamability": 0,
|
||||
"temperature": 0,
|
||||
"explosive": false,
|
||||
"explosion_radius": 0,
|
||||
"explosion_color": [0, 0, 0],
|
||||
"friction": 0.5,
|
||||
"viscosity": 0.5,
|
||||
"liquid": false,
|
||||
"solid": true,
|
||||
"is_gas": false
|
||||
},
|
||||
"stone": {
|
||||
"name": "Stone",
|
||||
"size": 1,
|
||||
"hardness": 0.7,
|
||||
"velocity": 1.5,
|
||||
"conductivity": 0,
|
||||
"heat_capacity": 0,
|
||||
"color": [128, 128, 128, 255],
|
||||
"mass": 1,
|
||||
"flamability": 0,
|
||||
"melt": "molten-Stone",
|
||||
"melt_temperature": 800,
|
||||
"solidify": "stone",
|
||||
"solidify_temperature": 799,
|
||||
"temperature": 0,
|
||||
"explosive": false,
|
||||
"explosion_radius": 0,
|
||||
"explosion_color": [0, 0, 0],
|
||||
"friction": 0.5,
|
||||
"viscosity": 0.5,
|
||||
"liquid": false,
|
||||
"solid": true,
|
||||
"is_gas": false
|
||||
},
|
||||
"snow": {
|
||||
"name": "Snow",
|
||||
"size": 1,
|
||||
"hardness": 0.1,
|
||||
"velocity": 0.2,
|
||||
"conductivity": 1,
|
||||
"heat_capacity": 1,
|
||||
"color": [255, 255, 255, 255],
|
||||
"mass": 0.01,
|
||||
"flamability": 0,
|
||||
"melt": "water",
|
||||
"melt_temperature": 1,
|
||||
"temperature": 0,
|
||||
"explosive": false,
|
||||
"explosion_radius": 0,
|
||||
"explosion_color": [0, 0, 0],
|
||||
"friction": 0.1,
|
||||
"viscosity": 0.01,
|
||||
"liquid": false,
|
||||
"solid": true,
|
||||
"is_gas": false
|
||||
},
|
||||
"wood": {
|
||||
"name": "Wood",
|
||||
"size": 1,
|
||||
"hardness": 0.5,
|
||||
"velocity": 0.5,
|
||||
"conductivity": 0,
|
||||
"heat_capacity": 1,
|
||||
"color": [139, 69, 19, 255],
|
||||
"mass": 0.5,
|
||||
"flamability": 0.8,
|
||||
"burning_temperature": 250,
|
||||
"burning_rate": 0.01,
|
||||
"burning_color": [255, 0, 0, 255],
|
||||
"burning": false,
|
||||
"temperature": 0,
|
||||
"explosive": false,
|
||||
"explosion_radius": 0,
|
||||
"explosion_color": [0, 0, 0],
|
||||
"friction": 0.5,
|
||||
"viscosity": 0.5,
|
||||
"liquid": false,
|
||||
"solid": true,
|
||||
"is_gas": false
|
||||
},
|
||||
"burning-wood": {
|
||||
"name": "Burning Wood",
|
||||
"size": 1,
|
||||
"hardness": 0.5,
|
||||
"velocity": 0.5,
|
||||
"conductivity": 0,
|
||||
"heat_capacity": 1,
|
||||
"color": [139, 69, 19, 255],
|
||||
"mass": 0.5,
|
||||
"flamability": 1,
|
||||
"temperature": 251,
|
||||
"burning": true,
|
||||
"explosive": false,
|
||||
"explosion_radius": 0,
|
||||
"explosion_color": [0, 0, 0],
|
||||
"friction": 0.5,
|
||||
"viscosity": 0.5,
|
||||
"liquid": false,
|
||||
"solid": true,
|
||||
"is_gas": false
|
||||
},
|
||||
"air": {
|
||||
"name": "Air",
|
||||
"size": 1,
|
||||
"hardness": 0.0,
|
||||
"velocity": 0.0,
|
||||
"conductivity": 0,
|
||||
"heat_capacity": 1,
|
||||
"color": [25, 25, 25, 25],
|
||||
"mass": 0.0,
|
||||
"flamability": 0,
|
||||
"temperature": 0,
|
||||
"explosive": false,
|
||||
"explosion_radius": 0,
|
||||
"explosion_color": [0, 0, 0],
|
||||
"friction": 0.0,
|
||||
"viscosity": 0.0,
|
||||
"liquid": false,
|
||||
"solid": false,
|
||||
"is_gas": true
|
||||
},
|
||||
"lava": {
|
||||
"name": "Lava",
|
||||
"size": 1,
|
||||
"hardness": 0.2,
|
||||
"velocity": 0.5,
|
||||
"conductivity": 0,
|
||||
"heat_capacity": 1,
|
||||
"color": [255, 45, 24, 255],
|
||||
"mass": 0.3,
|
||||
"flamability": 0,
|
||||
"temperature": 1400,
|
||||
"solidify": "molten-rock",
|
||||
"solidify_temperature": 799,
|
||||
"explosive": false,
|
||||
"explosion_radius": 0,
|
||||
"explosion_color": [0, 0, 0],
|
||||
"friction": 0.8,
|
||||
"viscosity": 0.8,
|
||||
"liquid": true,
|
||||
"solid": false,
|
||||
"is_gas": false
|
||||
},
|
||||
"rock": {
|
||||
"name": "Rock",
|
||||
"size": 1,
|
||||
@ -337,15 +339,11 @@
|
||||
"mass": 0.8,
|
||||
"flamability": 0,
|
||||
"melt": "molten-rock",
|
||||
"melt_temperature": 799,
|
||||
"melt_temperature": 600,
|
||||
"temperature": 0,
|
||||
"explosive": false,
|
||||
"explosion_radius": 0,
|
||||
"explosion_color": [
|
||||
0,
|
||||
0,
|
||||
0
|
||||
],
|
||||
"explosion_color": [0, 0, 0],
|
||||
"friction": 0.5,
|
||||
"viscosity": 0.5,
|
||||
"liquid": false,
|
||||
@ -362,9 +360,9 @@
|
||||
"color": [255, 140, 0, 255],
|
||||
"mass": 0.8,
|
||||
"flamability": 0,
|
||||
"temperature": 799,
|
||||
"melt": "lavaa",
|
||||
"melt_temperature": 1200,
|
||||
"temperature": 600,
|
||||
"melt": "lava",
|
||||
"melt_temperature": 1300,
|
||||
"explosive": false,
|
||||
"explosion_radius": 0,
|
||||
"explosion_color": [0,0,0],
|
||||
@ -376,70 +374,92 @@
|
||||
"solidify": "rock",
|
||||
"solidify_temperature": 200
|
||||
},
|
||||
"molten_stone": {
|
||||
"name": "Molten Stone",
|
||||
"size": 1,
|
||||
"hardness": 0.2,
|
||||
"velocity": 0.3,
|
||||
"conductivity": 1,
|
||||
"heat_capacity": 1,
|
||||
"color": [255, 140, 0, 255],
|
||||
"mass": 0.8,
|
||||
"flamability": 0,
|
||||
"temperature": 1200,
|
||||
"explosive": false,
|
||||
"explosion_radius": 0,
|
||||
"explosion_color": [0, 0, 0],
|
||||
"friction": 0.8,
|
||||
"viscosity": 0.8,
|
||||
"liquid": true,
|
||||
"solid": false,
|
||||
"is_gas": false,
|
||||
"solidify": "stone",
|
||||
"solidify_temperature": 800
|
||||
},
|
||||
"molten_glass": {
|
||||
"name": "Molten Glass",
|
||||
"size": 1,
|
||||
"hardness": 0.2,
|
||||
"velocity": 0.4,
|
||||
"conductivity": 0.8,
|
||||
"heat_capacity": 1,
|
||||
"color": [255, 200, 150, 200],
|
||||
"mass": 0.6,
|
||||
"flamability": 0,
|
||||
"temperature": 1200,
|
||||
"explosive": false,
|
||||
"explosion_radius": 0,
|
||||
"explosion_color": [0, 0, 0],
|
||||
"friction": 0.7,
|
||||
"viscosity": 0.9,
|
||||
"liquid": true,
|
||||
"solid": false,
|
||||
"is_gas": false,
|
||||
"solidify": "glass",
|
||||
"solidify_temperature": 800
|
||||
},
|
||||
"flame": {
|
||||
"name": "Flame",
|
||||
"size": 1,
|
||||
"hardness": 0.0,
|
||||
"velocity": 0.0,
|
||||
"conductivity": 0,
|
||||
"heat_capacity": 1,
|
||||
"color": [255, 100, 0, 255],
|
||||
"mass": 0.0,
|
||||
"flamability": 0,
|
||||
"temperature": 1000,
|
||||
"explosive": false,
|
||||
"explosion_radius": 0,
|
||||
"explosion_color": [0, 0, 0],
|
||||
"friction": 0.0,
|
||||
"viscosity": 0.0,
|
||||
"liquid": false,
|
||||
"solid": false,
|
||||
"is_gas": true
|
||||
}
|
||||
"molten_stone": {
|
||||
"name": "Molten Stone",
|
||||
"size": 1,
|
||||
"hardness": 0.2,
|
||||
"velocity": 0.3,
|
||||
"conductivity": 1,
|
||||
"heat_capacity": 1,
|
||||
"color": [255, 140, 0, 255],
|
||||
"mass": 0.8,
|
||||
"flamability": 0,
|
||||
"temperature": 1200,
|
||||
"explosive": false,
|
||||
"explosion_radius": 0,
|
||||
"explosion_color": [0, 0, 0],
|
||||
"friction": 0.8,
|
||||
"viscosity": 0.8,
|
||||
"liquid": true,
|
||||
"solid": false,
|
||||
"is_gas": false,
|
||||
"solidify": "stone",
|
||||
"solidify_temperature": 800
|
||||
},
|
||||
"molten_glass": {
|
||||
"name": "Molten Glass",
|
||||
"size": 1,
|
||||
"hardness": 0.2,
|
||||
"velocity": 0.4,
|
||||
"conductivity": 0.8,
|
||||
"heat_capacity": 1,
|
||||
"color": [255, 200, 150, 200],
|
||||
"mass": 0.6,
|
||||
"flamability": 0,
|
||||
"temperature": 600,
|
||||
"explosive": false,
|
||||
"explosion_radius": 0,
|
||||
"explosion_color": [0, 0, 0],
|
||||
"friction": 0.7,
|
||||
"viscosity": 0.9,
|
||||
"liquid": true,
|
||||
"solid": false,
|
||||
"is_gas": false,
|
||||
"solidify": "glass",
|
||||
"solidify_temperature": 599
|
||||
},
|
||||
"glass": {
|
||||
"name": "Glass",
|
||||
"size": 1,
|
||||
"hardness": 0.2,
|
||||
"velocity": 0.4,
|
||||
"conductivity": 0,
|
||||
"heat_capacity": 1,
|
||||
"color": [50, 45, 255, 100],
|
||||
"mass": 0.6,
|
||||
"flamability": 0,
|
||||
"temperature": 20,
|
||||
"explosive": false,
|
||||
"explosion_radius": 0,
|
||||
"explosion_color": [0, 0, 0],
|
||||
"friction": 0.7,
|
||||
"viscosity": 0.9,
|
||||
"liquid": false,
|
||||
"solid": true,
|
||||
"is_gas": false,
|
||||
"melt": "molten-glass",
|
||||
"melt_temperature": 1000
|
||||
},
|
||||
"flame": {
|
||||
"name": "Flame",
|
||||
"size": 1,
|
||||
"hardness": 0.0,
|
||||
"velocity": 0.0,
|
||||
"conductivity": 0,
|
||||
"heat_capacity": 1,
|
||||
"color": [255, 100, 0, 255],
|
||||
"mass": 0.0,
|
||||
"flamability": 0,
|
||||
"temperature": 1000,
|
||||
"explosive": false,
|
||||
"explosion_radius": 0,
|
||||
"explosion_color": [0, 0, 0],
|
||||
"friction": 0.0,
|
||||
"viscosity": 0.0,
|
||||
"liquid": false,
|
||||
"solid": false,
|
||||
"is_gas": true
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
279
rendering.py
279
rendering.py
@ -1,4 +1,26 @@
|
||||
"""
|
||||
#File Name: rendering.py
|
||||
Rendering class for the particle simulation.
|
||||
|
||||
This class is responsible for rendering the particles, UI elements, and debug information on the screen. It handles the setup of the display, pre-rendering of static UI elements, and the drawing of particles, buttons, and debug overlays.
|
||||
|
||||
The `draw_particles` function is the main method for rendering the particles on the screen. It takes the particle data, active particles, particle size, and particle colors as input, and renders the particles on the `particle_surface`. The `particle_surface` is then blitted onto the main screen.
|
||||
|
||||
The `draw_zoom_window` function is used to render a zoomed-in view of the particles around the mouse cursor. It creates a separate surface for the zoomed-in view and returns it.
|
||||
|
||||
The `draw_debug_overlay` function is responsible for rendering the debug information, such as FPS, mouse position, and particle information, on the screen.
|
||||
|
||||
The `draw_buttons` function handles the rendering of the category buttons, particle buttons, and other UI elements like the clear screen and settings buttons.
|
||||
|
||||
The `render_brush_cursor` function is used to draw a visual indicator for the current brush size.
|
||||
|
||||
The `draw_brush_size_slider` function renders a slider for adjusting the brush size.
|
||||
|
||||
The `draw_settings_menu` function creates a settings menu surface that can be displayed on the screen.
|
||||
|
||||
The `clear_screen` function is used to reset the simulation grid and clear the display surfaces.
|
||||
"""
|
||||
|
||||
|
||||
from settings import pygame, random, particle_properties, engine_settings
|
||||
|
||||
@ -11,14 +33,19 @@ class Rendering:
|
||||
self.background.fill((0, 0, 0))
|
||||
self.width = width
|
||||
self.height = height
|
||||
self.particle_surface = pygame.Surface((width, height), pygame.SRCALPHA)
|
||||
self.particle_colors = {}
|
||||
self.button_width, self.button_height = 30, 30
|
||||
self.particle_properties = particle_properties
|
||||
self.buttons = {}
|
||||
self.clear_screen_button = pygame.Rect(915, 10, 50, 30)
|
||||
self.load_buttons() # Load buttons dynamically
|
||||
self.particle_colors = {}
|
||||
self.particle_surface = pygame.Surface((width, height), pygame.SRCALPHA)
|
||||
self.debug_surface = pygame.Surface((300, 150), pygame.SRCALPHA)
|
||||
self.cached_fonts = {
|
||||
'debug': pygame.font.SysFont(None, 24),
|
||||
'button': pygame.font.SysFont(None, 20)
|
||||
}
|
||||
|
||||
# Pre-render static UI elements
|
||||
self.button_surfaces = {}
|
||||
|
||||
# Initialize categories
|
||||
for name, properties in particle_properties.items():
|
||||
if 'color' in properties:
|
||||
self.particle_colors[name.lower()] = properties['color']
|
||||
@ -36,7 +63,24 @@ class Rendering:
|
||||
self.current_category = 'Solids'
|
||||
self.category_buttons = {}
|
||||
self.setup_category_menu()
|
||||
|
||||
self.setup_static_ui()
|
||||
|
||||
|
||||
def setup_gpu_rendering(self):
|
||||
# Initialize OpenGL context
|
||||
pygame.display.gl_set_attribute(pygame.GL_ACCELERATED_VISUAL, 1)
|
||||
self.screen = pygame.display.set_mode((self.width, self.height),
|
||||
pygame.OPENGL | pygame.DOUBLEBUF)
|
||||
|
||||
|
||||
def setup_static_ui(self):
|
||||
for category in self.categories:
|
||||
surf = pygame.Surface((80, 25))
|
||||
surf.fill((150, 150, 150))
|
||||
text = self.cached_fonts['button'].render(category, True, (0, 0, 0))
|
||||
surf.blit(text, (5, 5))
|
||||
self.button_surfaces[category] = surf
|
||||
|
||||
|
||||
def setup_category_menu(self):
|
||||
# Category buttons at the top
|
||||
@ -60,25 +104,27 @@ class Rendering:
|
||||
|
||||
|
||||
def draw_particles(self, particles, active_particles, particle_size, particle_colors): # this is the function that draws the particles
|
||||
self.particle_surface = pygame.Surface((self.width, self.height), pygame.SRCALPHA)
|
||||
#self.particle_surface = pygame.Surface((self.width, self.height), pygame.SRCALPHA)
|
||||
self.particle_surface.fill((0, 0, 0, 0))
|
||||
|
||||
particle_batches = {}
|
||||
|
||||
for x, y in active_particles:
|
||||
particle = particles[x][y]
|
||||
if not particle:
|
||||
continue
|
||||
|
||||
base_color = particle_colors.get(particle.particle_type, (255, 255, 255))
|
||||
color = list(base_color)
|
||||
|
||||
if engine_settings['enable_glow']:
|
||||
glow_color = (255, 255, 255)
|
||||
glow_radius = 0.5 * particle_size
|
||||
glow_surface = pygame.Surface((glow_radius * 2, glow_radius * 2), pygame.SRCALPHA)
|
||||
pygame.draw.circle(glow_surface, glow_color, (glow_radius, glow_radius), glow_radius)
|
||||
glow_surface.set_alpha(100)
|
||||
self.particle_surface.blit(glow_surface, (x * particle_size - glow_radius, y * particle_size - glow_radius))
|
||||
p_type = particle.particle_type
|
||||
if p_type not in particle_batches:
|
||||
particle_batches[p_type] = []
|
||||
|
||||
rect = pygame.Rect(x * particle_size, y * particle_size,
|
||||
particle_size, particle_size)
|
||||
particle_batches[p_type].append(rect)
|
||||
if particle.particle_type in particle_colors:
|
||||
color = particle_colors[particle.particle_type]
|
||||
else:
|
||||
color = (255, 255, 255)
|
||||
|
||||
if engine_settings['enable_gas_effect']:
|
||||
if particle.is_gas:
|
||||
# Enhanced gas visibility
|
||||
@ -94,17 +140,38 @@ class Rendering:
|
||||
offset_y = random.randint(-1, 1)
|
||||
rect = (x * particle_size + offset_x, y * particle_size + offset_y,
|
||||
particle_size, particle_size)
|
||||
|
||||
rect = (x * particle_size, y * particle_size,
|
||||
particle_size, particle_size)
|
||||
else:
|
||||
rect = (x * particle_size, y * particle_size,
|
||||
particle_size, particle_size)
|
||||
|
||||
pygame.draw.rect(self.particle_surface, color, rect)
|
||||
|
||||
if engine_settings['enable_glow']:
|
||||
glow_color = (255, 255, 255)
|
||||
glow_radius = 0.5 * particle_size
|
||||
glow_surface = pygame.Surface((glow_radius * 2, glow_radius * 2), pygame.SRCALPHA)
|
||||
pygame.draw.circle(glow_surface, glow_color, (glow_radius, glow_radius), glow_radius)
|
||||
glow_surface.set_alpha(85)
|
||||
self.particle_surface.blit(glow_surface, (x * particle_size - glow_radius, y * particle_size - glow_radius))
|
||||
|
||||
|
||||
|
||||
self.screen.blit(self.background, (0, 0))
|
||||
self.screen.blit(self.particle_surface, (0, 0))
|
||||
"""#Potentially for future
|
||||
for p_type, rects in particle_batches.items():
|
||||
color = particle_colors.get(p_type, (255, 255, 255))
|
||||
rect = (x * particle_size, y * particle_size,
|
||||
particle_size, particle_size)
|
||||
if len(rects) > 1:
|
||||
pygame.draw.rect(self.particle_surface, color, rect)
|
||||
else:
|
||||
pygame.draw.rect(self.particle_surface, color, rect)
|
||||
|
||||
self.screen.blit(self.background, (0, 0))
|
||||
self.screen.blit(self.particle_surface, (0, 0))"""
|
||||
|
||||
def draw_zoom_window(self, particles, particle_size, particle_colors, mouse_pos, zoom_factor=4):
|
||||
def draw_zoom_window(self, particles, particle_size, particle_colors, mouse_pos, zoom_factor=4): # this is the function that draws the zoom window
|
||||
print(f"Drawing zoom window.")
|
||||
zoom_size = 100 # Size of zoom window
|
||||
zoom_surface = pygame.Surface((zoom_size, zoom_size))
|
||||
@ -132,101 +199,127 @@ class Rendering:
|
||||
return zoom_surface
|
||||
|
||||
|
||||
def draw_debug_overlay(self, fps, particles): # this is the function that draws the debug overlay
|
||||
# Get mouse position and convert to grid coordinates
|
||||
mouse_x, mouse_y = pygame.mouse.get_pos()
|
||||
grid_x = mouse_x // 3
|
||||
grid_y = mouse_y // 3
|
||||
|
||||
# Get particle info under cursor
|
||||
particle_info = "None"
|
||||
if 0 <= grid_x < len(particles) and 0 <= grid_y < len(particles[0]):
|
||||
particle = particles[grid_x][grid_y]
|
||||
def _get_particle_info(self, particles, x, y):
|
||||
if 0 <= x < len(particles) and 0 <= y < len(particles[0]):
|
||||
particle = particles[x][y]
|
||||
if particle:
|
||||
# Include more detailed particle information
|
||||
particle_info = f"Type: {particle.particle_type}"
|
||||
if hasattr(particle, 'temperature'):
|
||||
particle_info += f" | Temp: {particle.temperature}°C"
|
||||
if hasattr(particle, 'liquid'):
|
||||
particle_info += f" | Liquid: {particle.liquid}"
|
||||
if hasattr(particle, 'is_gas'):
|
||||
particle_info += f" | Gas: {particle.is_gas}"
|
||||
if hasattr(particle, 'solid'):
|
||||
particle_info += f" | Solid: {particle.solid}"
|
||||
if hasattr(particle, 'mass'):
|
||||
particle_info += f" | Mass: {particle.mass}"
|
||||
if hasattr(particle, 'velocity'):
|
||||
particle_info += f" | Velocity: {particle.velocity}"
|
||||
if hasattr(particle, 'friction'):
|
||||
particle_info += f" | Friction: {particle.friction}"
|
||||
attrs = ['temperature', 'liquid', 'is_gas', 'solid', 'mass', 'velocity', 'friction']
|
||||
info = [f"Type: {particle.particle_type}"]
|
||||
info.extend(f"{attr}: {getattr(particle, attr)}" for attr in attrs
|
||||
if hasattr(particle, attr))
|
||||
return " | ".join(info)
|
||||
return "None"
|
||||
|
||||
# Draw debug information
|
||||
font = pygame.font.SysFont(None, 24)
|
||||
particle_count = sum(1 for row in particles for cell in row if cell is not None)
|
||||
|
||||
def draw_debug_overlay(self, fps, particles): # this is the function that draws the debug overlay
|
||||
if engine_settings ['enable_fps']:
|
||||
# Draw FPS
|
||||
font = pygame.font.Font(None, 24)
|
||||
fps_text = font.render(f"FPS: {fps:.2f}", True, (255, 255, 255))
|
||||
self.screen.blit(fps_text, (10, 10))
|
||||
|
||||
if engine_settings ['enable_debug']:
|
||||
# Get mouse position and convert to grid coordinates
|
||||
self.debug_surface.fill((0, 0, 0, 0))
|
||||
mouse_x, mouse_y = pygame.mouse.get_pos()
|
||||
grid_x, grid_y = mouse_x // 3, mouse_y // 3
|
||||
|
||||
particle_info = self._get_particle_info(particles, grid_x, grid_y)
|
||||
|
||||
# Draw debug information
|
||||
font = pygame.font.SysFont(None, 24)
|
||||
particle_count = sum(1 for row in particles for cell in row if cell is not None)
|
||||
|
||||
debug_info = [
|
||||
f"FPS: {int(fps)}",
|
||||
f"Mouse: ({mouse_x}, {mouse_y})",
|
||||
f"Grid: ({grid_x}, {grid_y})",
|
||||
f"Particle: {particle_info}",
|
||||
#f"Active Particle Count: {sim.Simulation.get_active_particle_count(sim.Simulation)}",
|
||||
f"Particle Count: {particle_count}"
|
||||
]
|
||||
|
||||
y_offset = 10
|
||||
|
||||
for info in debug_info:
|
||||
debug_text = font.render(info, True, (255, 255, 255))
|
||||
self.screen.blit(debug_text, (10, y_offset))
|
||||
y_offset += 25
|
||||
self.screen.blit(self.debug_surface, (10, 10))
|
||||
|
||||
debug_info = [
|
||||
f"FPS: {int(fps)}",
|
||||
f"Mouse: ({mouse_x}, {mouse_y})",
|
||||
f"Grid: ({grid_x}, {grid_y})",
|
||||
f"Particle: {particle_info}",
|
||||
#f"Active Particle Count: {sim.Simulation.get_active_particle_count(sim.Simulation)}",
|
||||
f"Particle Count: {particle_count}"
|
||||
]
|
||||
y_offset = 10
|
||||
for info in debug_info:
|
||||
debug_text = font.render(info, True, (255, 255, 255))
|
||||
self.screen.blit(debug_text, (10, y_offset))
|
||||
y_offset += 25
|
||||
|
||||
|
||||
def draw_buttons(self): # this is the function that draws the buttons
|
||||
self.buttons = {}
|
||||
|
||||
# Draw category buttons vertically on right
|
||||
# Draw category buttons on right
|
||||
x_offset = self.width - 100
|
||||
y_offset = 10
|
||||
|
||||
# Draw cached category buttons
|
||||
for category, button in self.category_buttons.items():
|
||||
color = (200, 200, 200) if category == self.current_category else (150, 150, 150)
|
||||
pygame.draw.rect(self.screen, color, button)
|
||||
font = pygame.font.SysFont(None, 20)
|
||||
label = font.render(category, True, (0, 0, 0))
|
||||
self.screen.blit(label, (button.x + 5, button.y + 5))
|
||||
surf = self.button_surfaces[category]
|
||||
if category == self.current_category:
|
||||
surf.set_alpha(255)
|
||||
else:
|
||||
surf.set_alpha(200)
|
||||
self.screen.blit(surf, button)
|
||||
|
||||
# Draw particle buttons for current category
|
||||
y_offset = 150 # Start particle buttons below categories
|
||||
for particle_type in self.categories[self.current_category]:
|
||||
if particle_type in self.particle_properties:
|
||||
color = self.particle_properties[particle_type].get('color', (255, 255, 255))
|
||||
button_rect = pygame.Rect(x_offset, y_offset, 80, 25)
|
||||
self.buttons[particle_type] = button_rect
|
||||
|
||||
pygame.draw.rect(self.screen, color, button_rect)
|
||||
pygame.draw.rect(self.screen, (0, 0, 0), button_rect, 2)
|
||||
|
||||
font = pygame.font.SysFont(None, 20)
|
||||
label = font.render(particle_type, True, (0, 0, 0))
|
||||
self.screen.blit(label, (x_offset + 5, y_offset + 5))
|
||||
# Use cached button surface
|
||||
if particle_type in self.button_surfaces:
|
||||
self.screen.blit(self.button_surfaces[particle_type], button_rect)
|
||||
else:
|
||||
# Create and cache button surface if not exists
|
||||
button_surface = pygame.Surface((80, 25))
|
||||
color = self.particle_properties[particle_type].get('color', (255, 255, 255))
|
||||
button_surface.fill(color)
|
||||
font = pygame.font.SysFont(None, 20)
|
||||
label = font.render(particle_type, True, (0, 0, 0))
|
||||
button_surface.blit(label, (5, 5))
|
||||
self.button_surfaces[particle_type] = button_surface
|
||||
self.screen.blit(button_surface, button_rect)
|
||||
|
||||
y_offset += 30 # Stack buttons vertically
|
||||
|
||||
final_y_offset = y_offset + 10
|
||||
|
||||
# Draw clear screen button
|
||||
if 'clear' not in self.button_surfaces:
|
||||
clear_surface = pygame.Surface((80, 25))
|
||||
clear_surface.fill((255, 0, 0))
|
||||
font = pygame.font.SysFont(None, 24)
|
||||
label = font.render("Clear", True, (255, 255, 255))
|
||||
clear_surface.blit(label, (5, 5))
|
||||
self.button_surfaces['clear'] = clear_surface
|
||||
|
||||
self.clear_screen_button = pygame.Rect(x_offset, y_offset + 10, 80, 25)
|
||||
pygame.draw.rect(self.screen, (255, 0, 0), self.clear_screen_button)
|
||||
font = pygame.font.SysFont(None, 24)
|
||||
label = font.render("Clear", True, (255, 255, 255))
|
||||
self.screen.blit(label, (self.clear_screen_button.x + 5, self.clear_screen_button.y + 5))
|
||||
self.screen.blit(self.button_surfaces['clear'], self.clear_screen_button)
|
||||
|
||||
# Draw Settings menu directly below the buttons
|
||||
# Draw Settings menu button
|
||||
if 'settings' not in self.button_surfaces:
|
||||
settings_surface = pygame.Surface((80, 25))
|
||||
settings_surface.fill((255, 255, 255))
|
||||
font = pygame.font.SysFont(None, 24)
|
||||
label = font.render("Settings", True, (0, 0, 0))
|
||||
settings_surface.blit(label, (5, 5))
|
||||
self.button_surfaces['settings'] = settings_surface
|
||||
|
||||
self.settings_button = pygame.Rect(x_offset, y_offset + 50, 80, 25)
|
||||
pygame.draw.rect(self.screen, (255, 255, 255), self.settings_button)
|
||||
font = pygame.font.SysFont(None, 24)
|
||||
label = font.render("Settings", True, (0, 0, 0))
|
||||
self.screen.blit(label, (self.settings_button.x + 5, self.settings_button.y + 5))
|
||||
self.screen.blit(self.button_surfaces['settings'], self.settings_button)
|
||||
|
||||
|
||||
def render_brush_curser(self, x, y, radius): # this is the function that draws the brush curser but isn't used yet so unkown if works
|
||||
# Draw a circle cursor for brushsize
|
||||
pygame.draw.circle(self.screen, (255, 255, 255), (x, y), radius)
|
||||
def render_brush_cursor(self, x, y, radius):
|
||||
if engine_settings ['enable_cursor']:
|
||||
# Draw outline circle
|
||||
pygame.draw.circle(self.screen, (255, 255, 255), (x, y), radius, 1)
|
||||
# Draw slightly transparent fill
|
||||
cursor_surface = pygame.Surface((radius*2, radius*2), pygame.SRCALPHA)
|
||||
pygame.draw.circle(cursor_surface, (255, 255, 255, 55), (radius, radius), radius)
|
||||
self.screen.blit(cursor_surface, (x-radius, y-radius))
|
||||
|
||||
|
||||
def draw_brush_size_slider(self, brush_size): # this is the function that draws the brush size slider
|
||||
|
||||
96
sandpypi.py
96
sandpypi.py
@ -1,3 +1,4 @@
|
||||
"""
|
||||
#File Name: sandpypi.py
|
||||
# Sandpypi by Stanton.
|
||||
# Project name is a placeholder.
|
||||
@ -5,6 +6,13 @@
|
||||
# This is my most functional system for falling sand in python yet i took some things i learned in JS.
|
||||
# This needs further optimizations to core performance sections.
|
||||
|
||||
The main function to run the Sandpypi program.
|
||||
|
||||
This function initializes the Pygame environment, creates the simulation and rendering objects, and enters the main event loop.
|
||||
It handles user input events such as mouse clicks, mouse wheel scrolling, and keyboard presses.
|
||||
It also updates the simulation, draws the particles, buttons, and other UI elements, and manages the settings menu.
|
||||
The main loop runs at a target frame rate of 60 FPS, with the actual frame rate displayed in the debug overlay.
|
||||
"""
|
||||
from settings import pygame, engine_settings
|
||||
from rendering import Rendering
|
||||
from sim import Simulation
|
||||
@ -31,8 +39,8 @@ def main(): # Main function to run the program
|
||||
running = True
|
||||
|
||||
while running:
|
||||
fps = clock.get_fps()
|
||||
dt = clock.tick(60) / 1000
|
||||
fps = clock.get_fps() # Get the current frame rate
|
||||
dt = clock.tick(300) / 1000 # sets the target frame rate to 60 FPS
|
||||
keys = pygame.key.get_pressed()
|
||||
zoom_active = keys[pygame.K_z]
|
||||
|
||||
@ -41,6 +49,12 @@ def main(): # Main function to run the program
|
||||
for event in pygame.event.get():
|
||||
if event.type == pygame.MOUSEBUTTONDOWN:
|
||||
mouse_pos = pygame.mouse.get_pos()
|
||||
# Check if clicking in settings area when menu is visible
|
||||
in_settings_area = False
|
||||
if settings_visible:
|
||||
settings_rect = pygame.Rect(rendering.width - 320, 100, 300, 400)
|
||||
in_settings_area = settings_rect.collidepoint(mouse_pos)
|
||||
|
||||
if event.button == 4: # Mouse wheel up
|
||||
sim.brush_size = min(sim.brush_size + 1, sim.max_brush_size)
|
||||
elif event.button == 5: # Mouse wheel down
|
||||
@ -48,45 +62,48 @@ def main(): # Main function to run the program
|
||||
elif event.button == 1: # Left click
|
||||
over_button = False
|
||||
|
||||
# Check settings button first
|
||||
if rendering.settings_button.collidepoint(mouse_pos):
|
||||
settings_visible = not settings_visible
|
||||
over_button = True
|
||||
|
||||
if zoom_active:
|
||||
zoom_locked = not zoom_locked
|
||||
if zoom_locked:
|
||||
zoom_pos = mouse_pos
|
||||
|
||||
# Check category buttons
|
||||
for category, button in rendering.category_buttons.items():
|
||||
if button.collidepoint(mouse_pos):
|
||||
rendering.current_category = category
|
||||
over_button = True
|
||||
break
|
||||
|
||||
# Check particle buttons
|
||||
if not over_button:
|
||||
for particle_type, button in rendering.buttons.items():
|
||||
if button.collidepoint(mouse_pos):
|
||||
sim.current_particle_type = particle_type
|
||||
over_button = True
|
||||
break
|
||||
|
||||
# Check clear screen button
|
||||
if rendering.clear_screen_button.collidepoint(mouse_pos):
|
||||
rendering.clear_screen(sim)
|
||||
over_button = True
|
||||
|
||||
if rendering.settings_button.collidepoint(event.pos):
|
||||
settings_visible = not settings_visible
|
||||
|
||||
elif settings_visible:
|
||||
# Handle settings toggles
|
||||
mouse_pos = pygame.mouse.get_pos()
|
||||
# Handle settings menu interactions
|
||||
elif settings_visible and in_settings_area:
|
||||
relative_y = mouse_pos[1] - settings_menu_y
|
||||
setting_index = relative_y // 30
|
||||
if 0 <= setting_index < len(engine_settings):
|
||||
setting_name = list(engine_settings.keys())[setting_index]
|
||||
engine_settings[setting_name] = not engine_settings[setting_name]
|
||||
over_button = True
|
||||
|
||||
elif not in_settings_area:
|
||||
|
||||
# Check category buttons
|
||||
for category, button in rendering.category_buttons.items():
|
||||
if button.collidepoint(mouse_pos):
|
||||
rendering.current_category = category
|
||||
over_button = True
|
||||
break
|
||||
|
||||
# Check particle buttons
|
||||
if not over_button:
|
||||
for particle_type, button in rendering.buttons.items():
|
||||
if button.collidepoint(mouse_pos):
|
||||
sim.current_particle_type = particle_type
|
||||
over_button = True
|
||||
break
|
||||
|
||||
# Check clear screen button
|
||||
if rendering.clear_screen_button.collidepoint(mouse_pos):
|
||||
rendering.clear_screen(sim)
|
||||
over_button = True
|
||||
|
||||
if not over_button:
|
||||
if not over_button and not in_settings_area:
|
||||
mouse_down_left = True
|
||||
|
||||
elif event.button == 3: # Right click
|
||||
@ -136,11 +153,6 @@ def main(): # Main function to run the program
|
||||
x, y = pygame.mouse.get_pos()
|
||||
sim.create_particle(x, y)
|
||||
|
||||
|
||||
sim.simulate_step(dt)
|
||||
rendering.draw_particles(sim.particles, sim.active_particles, sim.particle_size, rendering.particle_colors)
|
||||
rendering.draw_buttons()
|
||||
rendering.draw_brush_size_slider(sim.brush_size)
|
||||
if zoom_active or zoom_locked:
|
||||
mouse_pos = zoom_pos if zoom_locked else pygame.mouse.get_pos()
|
||||
zoom_surface = rendering.draw_zoom_window(sim.particles, sim.particle_size, rendering.particle_colors, mouse_pos)
|
||||
@ -149,11 +161,27 @@ def main(): # Main function to run the program
|
||||
zoom_y = 10 if mouse_pos[1] > height/2 else height - 110
|
||||
screen.blit(zoom_surface, (zoom_x, zoom_y))
|
||||
|
||||
# Draw Settings
|
||||
if settings_visible:
|
||||
settings_menu = rendering.draw_settings_menu()
|
||||
rendering.screen.blit(settings_menu, (rendering.width - 320, 100))
|
||||
rendering.draw_debug_overlay(fps, sim.particles)
|
||||
pygame.display.flip()
|
||||
|
||||
# Update and draw particles
|
||||
sim.simulate_step(dt=0.016)
|
||||
# Draw particles
|
||||
rendering.draw_particles(sim.particles, sim.active_particles, sim.particle_size, rendering.particle_colors)
|
||||
# Draw buttons
|
||||
rendering.draw_buttons()
|
||||
# Draw brush size slider
|
||||
rendering.draw_brush_size_slider(sim.brush_size)
|
||||
# Get current mouse position
|
||||
mouse_x, mouse_y = pygame.mouse.get_pos()
|
||||
# Draw brush cursor at mouse position
|
||||
rendering.render_brush_cursor(mouse_x, mouse_y, sim.brush_size * sim.particle_size)
|
||||
|
||||
|
||||
|
||||
pygame.quit()
|
||||
|
||||
|
||||
23
settings.py
23
settings.py
@ -1,14 +1,29 @@
|
||||
"""
|
||||
#File Name: settings.py
|
||||
# Global settings and impoorts for the project.
|
||||
|
||||
Global settings and imports for the project.
|
||||
|
||||
This module defines various settings for the game engine, such as enabling or disabling the cursor, glow effect, gas effect, debug mode, and FPS display. It also provides a function to load particle properties from a JSON file.
|
||||
|
||||
The `engine_settings` dictionary contains the configurable settings for the game engine. These settings can be used to customize the behavior of the game.
|
||||
|
||||
The `load_particle_properties()` function attempts to load particle properties from a 'particles.json' file. If the file is not found or the JSON data is invalid, it returns an empty dictionary.
|
||||
|
||||
The `particle_properties` variable is initialized by calling `load_particle_properties()` when the module is imported.
|
||||
"""
|
||||
|
||||
|
||||
import pygame
|
||||
import json
|
||||
import random
|
||||
import time
|
||||
import numpy as np
|
||||
|
||||
engine_settings = {
|
||||
'enable_glow': True,
|
||||
'enable_gas_effect': True
|
||||
'enable_cursor': True,
|
||||
'enable_glow': False,
|
||||
'enable_gas_effect': True,
|
||||
'enable_debug': False,
|
||||
'enable_fps': True
|
||||
# 'settings': True/False
|
||||
}
|
||||
|
||||
|
||||
8
setup.py
8
setup.py
@ -1,8 +0,0 @@
|
||||
#this be a wip maybe gonna move heavy work loads to cython.
|
||||
|
||||
from setuptools import setup
|
||||
from Cython.Build import cythonize
|
||||
|
||||
setup(
|
||||
ext_modules=cythonize("simulation_core.pyx"),
|
||||
)
|
||||
175
sim.py
175
sim.py
@ -1,7 +1,24 @@
|
||||
"""
|
||||
#File Name: sim.py
|
||||
Particle-based Physics Simulation System
|
||||
======================================
|
||||
|
||||
This module implements a 2D particle simulation with physics, interactions, and state changes.
|
||||
|
||||
Key Components:
|
||||
--------------
|
||||
1. Particle Class
|
||||
- Handles individual particle properties and behaviors
|
||||
- Supports multiple particle types (solid, liquid, gas)
|
||||
- Manages temperature and state transitions
|
||||
|
||||
2. Simulation Class
|
||||
- Core simulation engine
|
||||
- Manages particle creation, movement and interactions
|
||||
- Handles physics calculations and spatial partitioning
|
||||
"""
|
||||
|
||||
#Load the imports. Pygame is what makes this even work and so simple may consider other engines for performance depends on learning curve.
|
||||
|
||||
from settings import random, particle_properties
|
||||
|
||||
# Load particle properties from json so we know what particles we got and how they should be simulated.
|
||||
@ -57,6 +74,9 @@ class Simulation:
|
||||
# the main class of the simulation.
|
||||
|
||||
def __init__(self, width, height, x=0, y=0):
|
||||
self.dormant_particles = set()
|
||||
self.particle_movement_counter = {}
|
||||
self.DORMANT_THRESHOLD = 10
|
||||
self.x = x
|
||||
self.y = y
|
||||
self.new_x = 0
|
||||
@ -100,15 +120,39 @@ class Simulation:
|
||||
|
||||
def update_spatial_grid(self): # this is where we update the spatial grid.
|
||||
"""Update spatial grid for optimized collision detection"""
|
||||
if len(self.active_particles) > 100: # Threshold for rebuild
|
||||
self.spatial_grid.clear()
|
||||
for x, y in self.active_particles:
|
||||
cell_key = self.get_cell_key(x, y)
|
||||
if cell_key not in self.spatial_grid:
|
||||
self.spatial_grid[cell_key] = set()
|
||||
self.spatial_grid[cell_key].add((x, y))
|
||||
if len(self.active_particles) > 100:
|
||||
self.spatial_grid = {}
|
||||
cell_lists = {}
|
||||
|
||||
|
||||
for x, y in self.active_particles:
|
||||
cell_key = (x // self.cell_size, y // self.cell_size)
|
||||
if cell_key not in cell_lists:
|
||||
cell_lists[cell_key] = []
|
||||
cell_lists[cell_key].append((x, y))
|
||||
|
||||
self.spatial_grid = {k: set(v) for k, v in cell_lists.items()}
|
||||
|
||||
def _check_dormant_state(self, x, y, particle):
|
||||
key = (x, y)
|
||||
if particle.particle_type == 'wall':
|
||||
self.dormant_particles.add(key)
|
||||
return True
|
||||
|
||||
if not hasattr(particle, 'last_position'):
|
||||
particle.last_position = (x, y)
|
||||
self.particle_movement_counter[key] = 0
|
||||
return False
|
||||
|
||||
if particle.last_position == (x, y):
|
||||
self.particle_movement_counter[key] = self.particle_movement_counter.get(key, 0) + 1
|
||||
if self.particle_movement_counter[key] >= self.DORMANT_THRESHOLD:
|
||||
self.dormant_particles.add(key)
|
||||
return True
|
||||
else:
|
||||
particle.last_position = (x, y)
|
||||
self.particle_movement_counter[key] = 0
|
||||
self.dormant_particles.discard(key)
|
||||
return False
|
||||
|
||||
def handle_phase_transitions(self, particle, x, y): # this is where we handle all the phase transitions.
|
||||
"""Handle all phase transitions for a particle"""
|
||||
@ -214,12 +258,12 @@ class Simulation:
|
||||
particle = self.particles[x][y]
|
||||
if not particle:
|
||||
continue
|
||||
if particle.temperature > 1100:
|
||||
if particle.temperature > 100:
|
||||
# Transition to gas
|
||||
particle.is_gas = True
|
||||
particle.temperature = 1100
|
||||
particle.temperature = 100
|
||||
particle.velocity = [random.uniform(-1, 1), random.uniform(-1, 1)]
|
||||
particle.temperature < 1100
|
||||
particle.temperature < 100
|
||||
particle.is_gas = False
|
||||
|
||||
|
||||
@ -228,8 +272,12 @@ class Simulation:
|
||||
fx, fy = 0.0, 0.0 # Initialize forces
|
||||
|
||||
# Apply wind force
|
||||
fx += self.wind[0] * (1.0 if not particle.is_gas else 0.5)
|
||||
fy += self.wind[1] * (1.0 if not particle.is_gas else 0.5)
|
||||
if particle.is_gas:
|
||||
fx += self.wind[0] * 0.5
|
||||
fy += self.wind[1] * 0.5
|
||||
else:
|
||||
fx += self.wind[0]
|
||||
fy += self.wind[1]
|
||||
|
||||
# Apply drag force
|
||||
drag = particle.viscosity * -1
|
||||
@ -237,23 +285,25 @@ class Simulation:
|
||||
fy += drag * particle.velocity[1]
|
||||
|
||||
# Check neighboring particles
|
||||
for dx, dy in [(-1, 0), (1, 0), (0, -1), (0, 1)]:
|
||||
nx, ny = x + dx, y + dy
|
||||
neighbors = self._get_quick_neighbors(x, y)
|
||||
for nx, ny in neighbors:
|
||||
if 0 <= nx < self.width and 0 <= ny < self.height:
|
||||
neighbor = self.particles[nx][ny]
|
||||
if neighbor:
|
||||
# Temperature effects
|
||||
if hasattr(neighbor, 'temperature') and hasattr(particle, 'temperature'):
|
||||
if neighbor.temperature > particle.temperature:
|
||||
fy += (neighbor.temperature - particle.temperature) * 0.1
|
||||
# Gas pressure effects
|
||||
if hasattr(neighbor, 'is_gas') and hasattr(particle, 'is_gas'):
|
||||
if neighbor.is_gas and not particle.is_gas:
|
||||
fx += dx * 0.1
|
||||
fy += dy * 0.1
|
||||
self._apply_neighbor_forces(particle, neighbor, fx, fy)
|
||||
|
||||
|
||||
return fx, fy
|
||||
|
||||
def _get_quick_neighbors(self, x, y):
|
||||
"""Quick neighbor lookup without full spatial grid"""
|
||||
return [(x+dx, y+dy) for dx, dy in [(-1,0), (1,0), (0,-1), (0,1)]]
|
||||
|
||||
def _apply_neighbor_forces(self, particle, neighbor, fx, fy):
|
||||
"""Optimized neighbor force calculation"""
|
||||
if hasattr(neighbor, 'temperature') and hasattr(particle, 'temperature'):
|
||||
temp_diff = neighbor.temperature - particle.temperature
|
||||
fy += temp_diff * 0.05
|
||||
|
||||
def ignite_particle(self, particle): # this is where we ignite the particle.
|
||||
"""Handle ignition and burning of flammable particles."""
|
||||
@ -518,7 +568,7 @@ class Simulation:
|
||||
# handle gas particles
|
||||
if particle.is_gas:
|
||||
# Gas-specific movement
|
||||
dx = random.uniform(-1, 1)
|
||||
dx = random.uniform(2, -1)
|
||||
dy = random.uniform(-2, 0) # Bias upward
|
||||
new_x = int(x + dx)
|
||||
new_y = int(y + dy)
|
||||
@ -540,7 +590,7 @@ class Simulation:
|
||||
|
||||
if particle.liquid:
|
||||
# Enhanced liquid spreading
|
||||
spread_chance = 0.8
|
||||
spread_chance = 0.5
|
||||
if random.random() < spread_chance:
|
||||
dx = random.choice([-1, 1])
|
||||
if (0 <= x + dx < self.width and
|
||||
@ -606,14 +656,75 @@ class Simulation:
|
||||
count += 1
|
||||
return count
|
||||
|
||||
def get_active_particle_count(self):
|
||||
"""Returns the number of active particles in the simulation for the Debug display and for performance analysis."""
|
||||
pass
|
||||
|
||||
|
||||
def _process_particle_batch(self, batch, dt):
|
||||
updates = []
|
||||
new_active = set()
|
||||
|
||||
# Filter out dormant particles from the batch
|
||||
active_batch = [pos for pos in batch if pos not in self.dormant_particles]
|
||||
|
||||
for x, y in active_batch:
|
||||
particle = self.particles[x][y]
|
||||
if not particle:
|
||||
continue
|
||||
|
||||
if particle.particle_type == 'wall':
|
||||
new_active.add((x, y))
|
||||
continue
|
||||
|
||||
# Check if particle should become dormant
|
||||
if self._check_dormant_state(x, y, particle):
|
||||
new_active.add((x, y))
|
||||
continue
|
||||
|
||||
# physics calculations
|
||||
fx, fy = self.calculate_forces(particle, x, y)
|
||||
particle.velocity[0] += (fx / particle.mass) * dt
|
||||
particle.velocity[1] += (fy / particle.mass) * dt
|
||||
|
||||
new_x = int(x + particle.velocity[0] * dt)
|
||||
new_y = int(y + particle.velocity[1] * dt)
|
||||
|
||||
if 0 <= new_x < self.width and 0 <= new_y < self.height:
|
||||
if self.particles[new_x][new_y] is None:
|
||||
updates.append((x, y, new_x, new_y, particle))
|
||||
new_active.add((new_x, new_y))
|
||||
# Wake up neighboring dormant particles
|
||||
self._wake_neighbors(new_x, new_y)
|
||||
else:
|
||||
new_active.add((x, y))
|
||||
|
||||
# Apply updates and return new active set
|
||||
for old_x, old_y, new_x, new_y, particle in updates:
|
||||
self.particles[old_x][old_y] = None
|
||||
self.particles[new_x][new_y] = particle
|
||||
particle.position = (new_x, new_y)
|
||||
|
||||
return new_active
|
||||
|
||||
def _wake_neighbors(self, x, y):
|
||||
for dx in [-1, 0, 1]:
|
||||
for dy in [-1, 0, 1]:
|
||||
nx, ny = x + dx, y + dy
|
||||
key = (nx, ny)
|
||||
if key in self.dormant_particles:
|
||||
self.dormant_particles.discard(key)
|
||||
self.particle_movement_counter[key] = 0
|
||||
|
||||
def simulate_step(self, dt):
|
||||
"""Run a single step of the simulation"""
|
||||
active_list = list(self.active_particles)
|
||||
batch_size = 1000
|
||||
|
||||
for i in range(0, len(active_list), batch_size):
|
||||
batch = active_list[i:i + batch_size]
|
||||
self._process_particle_batch(batch, dt)
|
||||
|
||||
# Update spatial grid only when needed
|
||||
if len(self.active_particles) > 100:
|
||||
self.update_spatial_grid()
|
||||
|
||||
# Update particle positions and physics
|
||||
self.apply_gravity(dt)
|
||||
self.apply_physics(dt)
|
||||
@ -624,5 +735,3 @@ class Simulation:
|
||||
self.burning()
|
||||
self.spread_fire()
|
||||
|
||||
# Update spatial grid
|
||||
self.update_spatial_grid()
|
||||
@ -1,36 +0,0 @@
|
||||
# simulation_core.pyx
|
||||
# Cython code for simulating the physics of the system
|
||||
|
||||
cimport cython
|
||||
from libc.math cimport sqrt
|
||||
|
||||
cdef class CParticle:
|
||||
cdef float x, y, vx, vy, mass, gravity
|
||||
|
||||
def __init__(self, float x, float y, float vx, float vy, float mass, float gravity):
|
||||
self.x = x
|
||||
self.y = y
|
||||
self.vx = vx
|
||||
self.vy = vy
|
||||
self.mass = mass
|
||||
self.gravity = gravity
|
||||
|
||||
cpdef void apply_gravity(self, float dt):
|
||||
self.vy += self.gravity * dt
|
||||
self.y += self.vy * dt
|
||||
self.x += self.vx * dt
|
||||
|
||||
|
||||
cdef class SimulationCore:
|
||||
cdef list particles
|
||||
|
||||
def __init__(self):
|
||||
self.particles = []
|
||||
|
||||
cpdef void add_particle(self, CParticle particle):
|
||||
self.particles.append(particle)
|
||||
|
||||
cpdef void update(self, float dt):
|
||||
cdef CParticle particle
|
||||
for particle in self.particles:
|
||||
particle.apply_gravity(dt)
|
||||
@ -1,7 +1,8 @@
|
||||
{
|
||||
"Template": {
|
||||
"description": "This is a template for particles.",
|
||||
"description2": "Remove this for your own particle mods to work"
|
||||
"description": "Defines the properties and behavior of various particle types, including sand, water, and steam.",
|
||||
"description2": "This template can be used as a starting point for creating custom particle mods.",
|
||||
"description3": "Remove 'Template' for your own particle mods to work only particles like below will work."
|
||||
},
|
||||
"sand": {
|
||||
"name": "Sand",
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user