GameContextProtocol / backend /tools /environment_tools.py
ArturoNereu's picture
added lfs and files
cc6d03f
"""
Environment Tools
Skybox, particles, and other environmental effects
"""
from typing import Dict, Any, Optional
from backend.storage import storage
# =============================================================================
# Skybox Tools
# =============================================================================
def add_skybox(
scene_id: str,
preset: str = "day",
turbidity: float = 10.0,
rayleigh: float = 2.0,
sun_elevation: float = 45.0,
sun_azimuth: float = 180.0
) -> Dict[str, Any]:
"""
Add a procedural sky to the scene using Three.js Sky shader.
The sky simulates atmospheric scattering for realistic outdoor environments.
Args:
scene_id: ID of the scene
preset: Quick preset - "day", "sunset", "noon", "dawn", "night" (overrides other params)
turbidity: Haziness of the atmosphere (2.0=clear, 10.0=hazy, 20.0=foggy)
rayleigh: Amount of Rayleigh scattering (affects blue color of sky)
sun_elevation: Sun angle from horizon in degrees (0=horizon, 90=overhead)
sun_azimuth: Sun compass direction in degrees (0=north, 90=east, 180=south)
Returns:
Dictionary with skybox settings and message
"""
scene = storage.get(scene_id)
if not scene:
raise ValueError(f"Scene '{scene_id}' not found")
if "environment" not in scene:
scene["environment"] = {}
# Apply presets
presets = {
"day": {"turbidity": 10.0, "rayleigh": 2.0, "sun_elevation": 45.0, "sun_azimuth": 180.0},
"noon": {"turbidity": 8.0, "rayleigh": 1.5, "sun_elevation": 85.0, "sun_azimuth": 180.0},
"sunset": {"turbidity": 4.0, "rayleigh": 4.0, "sun_elevation": 5.0, "sun_azimuth": 180.0},
"dawn": {"turbidity": 4.0, "rayleigh": 4.0, "sun_elevation": 5.0, "sun_azimuth": 0.0},
"night": {"turbidity": 20.0, "rayleigh": 0.1, "sun_elevation": -10.0, "sun_azimuth": 180.0},
}
if preset in presets:
params = presets[preset]
turbidity = params["turbidity"]
rayleigh = params["rayleigh"]
sun_elevation = params["sun_elevation"]
sun_azimuth = params["sun_azimuth"]
# Validate ranges
turbidity = max(0.1, min(20.0, turbidity))
rayleigh = max(0.0, min(4.0, rayleigh))
sun_elevation = max(-90.0, min(90.0, sun_elevation))
sun_azimuth = sun_azimuth % 360.0
skybox = {
"enabled": True,
"type": "procedural",
"turbidity": turbidity,
"rayleigh": rayleigh,
"sun_elevation": sun_elevation,
"sun_azimuth": sun_azimuth,
"preset": preset
}
scene["environment"]["skybox"] = skybox
storage.save(scene)
return {
"scene_id": scene_id,
"message": f"Added {preset} sky (sun at {sun_elevation}° elevation)",
"skybox": skybox
}
def remove_skybox(scene_id: str) -> Dict[str, Any]:
"""
Remove the skybox from the scene, reverting to solid background color.
Args:
scene_id: ID of the scene
Returns:
Dictionary with confirmation message
"""
scene = storage.get(scene_id)
if not scene:
raise ValueError(f"Scene '{scene_id}' not found")
if "environment" not in scene:
scene["environment"] = {}
# Check if skybox exists
had_skybox = scene["environment"].get("skybox", {}).get("enabled", False)
scene["environment"]["skybox"] = {"enabled": False}
storage.save(scene)
if had_skybox:
return {
"scene_id": scene_id,
"message": "Removed skybox, reverted to solid background color"
}
else:
return {
"scene_id": scene_id,
"message": "No skybox was active"
}
# =============================================================================
# Particle System Tools
# =============================================================================
def add_particles(
scene_id: str,
preset: str,
position: Optional[Dict[str, float]] = None,
particle_id: Optional[str] = None
) -> Dict[str, Any]:
"""
Add a particle effect to the scene using preset configurations.
Args:
scene_id: ID of the scene
preset: Effect type - "fire", "smoke", "sparkle", "rain", "snow"
position: {x, y, z} position for localized effects (fire, smoke, sparkle)
For weather effects (rain, snow), position is ignored (covers world)
particle_id: Optional unique identifier for this particle system
Returns:
Dictionary with particle system info and message
"""
scene = storage.get(scene_id)
if not scene:
raise ValueError(f"Scene '{scene_id}' not found")
valid_presets = ["fire", "smoke", "sparkle", "rain", "snow"]
if preset not in valid_presets:
raise ValueError(f"Invalid preset '{preset}'. Must be one of: {valid_presets}")
if "particles" not in scene:
scene["particles"] = []
# Generate ID if not provided
if not particle_id:
particle_id = f"{preset}_{len(scene['particles'])}"
# Check for duplicate ID
for p in scene["particles"]:
if p.get("id") == particle_id:
raise ValueError(f"Particle system with id '{particle_id}' already exists")
# Default position for localized effects
if position is None:
position = {"x": 0, "y": 1, "z": 0}
# Preset configurations
preset_configs = {
"fire": {
"count": 200,
"size": 0.3,
"color_start": "#ff6600",
"color_end": "#ff0000",
"velocity": {"x": 0, "y": 2, "z": 0},
"lifetime": 1.5,
"spread": 0.5,
"localized": True
},
"smoke": {
"count": 100,
"size": 0.8,
"color_start": "#666666",
"color_end": "#333333",
"velocity": {"x": 0, "y": 1, "z": 0},
"lifetime": 3.0,
"spread": 1.0,
"localized": True
},
"sparkle": {
"count": 50,
"size": 0.1,
"color_start": "#ffffff",
"color_end": "#ffff00",
"velocity": {"x": 0, "y": 0.5, "z": 0},
"lifetime": 2.0,
"spread": 2.0,
"localized": True
},
"rain": {
"count": 1000,
"size": 0.05,
"color_start": "#aaccff",
"color_end": "#88aadd",
"velocity": {"x": 0, "y": -15, "z": 0},
"lifetime": 2.0,
"spread": 50.0,
"localized": False
},
"snow": {
"count": 500,
"size": 0.15,
"color_start": "#ffffff",
"color_end": "#eeeeff",
"velocity": {"x": 0.5, "y": -2, "z": 0.3},
"lifetime": 8.0,
"spread": 50.0,
"localized": False
}
}
config = preset_configs[preset]
particle_system = {
"id": particle_id,
"preset": preset,
"position": position,
"enabled": True,
**config
}
scene["particles"].append(particle_system)
storage.save(scene)
if config["localized"]:
pos_str = f" at ({position['x']}, {position['y']}, {position['z']})"
else:
pos_str = " (world-wide weather effect)"
return {
"scene_id": scene_id,
"particle_id": particle_id,
"message": f"Added {preset} particle effect{pos_str}",
"particle_system": particle_system
}
def remove_particles(scene_id: str, particle_id: str) -> Dict[str, Any]:
"""
Remove a particle system from the scene.
Args:
scene_id: ID of the scene
particle_id: ID of the particle system to remove
Returns:
Dictionary with confirmation message
"""
scene = storage.get(scene_id)
if not scene:
raise ValueError(f"Scene '{scene_id}' not found")
if "particles" not in scene or not scene["particles"]:
raise ValueError("Scene has no particle systems")
original_count = len(scene["particles"])
scene["particles"] = [p for p in scene["particles"] if p.get("id") != particle_id]
if len(scene["particles"]) == original_count:
raise ValueError(f"Particle system '{particle_id}' not found")
storage.save(scene)
return {
"scene_id": scene_id,
"message": f"Removed particle system '{particle_id}'",
"particle_id": particle_id
}