""" Data models for GCP - Game Context Protocol Using plain dictionaries for simplicity and clarity. """ from typing import Optional, Literal, Dict, Any, List from datetime import datetime import uuid # Game Types ObjectType = Literal["cube", "sphere", "cylinder", "plane", "cone", "torus", "model"] LightType = Literal["ambient", "directional", "point", "spot", "hemisphere"] LightingPreset = Literal["day", "night", "sunset", "studio"] CameraMode = Literal["fps", "orbit", "top_down", "free"] MaterialType = Literal["standard", "basic", "phong", "toon"] # Factory functions for creating default data structures def create_vector3(x: float = 0.0, y: float = 0.0, z: float = 0.0) -> Dict[str, float]: """Create a 3D vector for position, rotation, or scale.""" return {"x": x, "y": y, "z": z} def create_material( type: MaterialType = "standard", color: str = "#ffffff", metalness: float = 0.5, roughness: float = 0.5, opacity: float = 1.0, wireframe: bool = False ) -> Dict[str, Any]: """Create material properties.""" return { "type": type, "color": color, "metalness": metalness, "roughness": roughness, "opacity": opacity, "wireframe": wireframe } def create_game_object( object_type: ObjectType = "cube", name: Optional[str] = None, position: Optional[Dict[str, float]] = None, rotation: Optional[Dict[str, float]] = None, scale: Optional[Dict[str, float]] = None, material: Optional[Dict[str, Any]] = None, model_path: Optional[str] = None, metadata: Optional[Dict[str, Any]] = None ) -> Dict[str, Any]: """Create a 3D game object.""" return { "id": str(uuid.uuid4()), "name": name, "type": object_type, "position": position or create_vector3(), "rotation": rotation or create_vector3(), "scale": scale or create_vector3(1, 1, 1), "material": material or create_material(), "model_path": model_path, "metadata": metadata or {}, "created_at": datetime.utcnow().isoformat() } def create_light( light_type: LightType = "directional", name: Optional[str] = None, color: str = "#ffffff", intensity: float = 1.0, position: Optional[Dict[str, float]] = None, target: Optional[Dict[str, float]] = None, cast_shadow: bool = True, ground_color: Optional[str] = None, # For hemisphere lights distance: Optional[float] = None, # For point/spot lights decay: float = 2.0 # Physically correct decay for point lights ) -> Dict[str, Any]: """ Create a light source. Args: light_type: Type of light (ambient, directional, point, spot, hemisphere) name: Light name for identification color: Light color in hex format (sky color for hemisphere) intensity: Light intensity (0.0-2.0 typical, higher for specific effects) position: Light position in 3D space target: Target position for directional/spot lights cast_shadow: Whether the light casts shadows (directional/spot only) ground_color: Ground color for hemisphere lights (defaults to darker version of color) distance: Maximum range for point/spot lights (0 = infinite) decay: Light falloff rate for point/spot (2.0 = physically correct) """ light = { "id": str(uuid.uuid4()), "name": name, "type": light_type, "color": color, "intensity": intensity, "position": position or create_vector3(10, 10, 10), "target": target, "cast_shadow": cast_shadow, } # Add hemisphere-specific properties if light_type == "hemisphere": light["ground_color"] = ground_color or "#444444" # Add point/spot-specific properties if light_type in ["point", "spot"]: if distance is not None: light["distance"] = distance light["decay"] = decay return light def create_environment( background_color: str = "#87CEEB", fog_enabled: bool = False, fog_color: str = "#ffffff", fog_near: float = 10.0, fog_far: float = 100.0, ambient_light_intensity: float = 0.5, lighting_preset: LightingPreset = "day" ) -> Dict[str, Any]: """Create environment settings.""" return { "background_color": background_color, "fog_enabled": fog_enabled, "fog_color": fog_color, "fog_near": fog_near, "fog_far": fog_far, "ambient_light_intensity": ambient_light_intensity, "lighting_preset": lighting_preset } def create_player( position: Optional[Dict[str, float]] = None, rotation: Optional[Dict[str, float]] = None, camera_mode: CameraMode = "orbit", movement_speed: float = 5.0, look_sensitivity: float = 0.002 ) -> Dict[str, Any]: """Create player/camera configuration.""" return { "position": position or create_vector3(0, 5, 10), "rotation": rotation or create_vector3(), "camera_mode": camera_mode, "movement_speed": movement_speed, "look_sensitivity": look_sensitivity } def create_player_config( move_speed: float = 8.0, jump_force: float = 5.0, mouse_sensitivity: float = 0.002, invert_y: bool = False, gravity: float = -9.82, player_height: float = 1.7, player_radius: float = 0.3, eye_height: float = 1.6, player_mass: float = 80.0, linear_damping: float = 0.0, movement_acceleration: float = 0.0, air_control: float = 1.0, camera_fov: float = 75.0, min_pitch: float = -89.0, max_pitch: float = 89.0 ) -> Dict[str, Any]: """ Create player controller configuration for FPS mode. Args: move_speed: Horizontal movement speed in units/second jump_force: Initial upward velocity for jumps in m/s mouse_sensitivity: Mouse look sensitivity multiplier invert_y: Whether to invert vertical mouse look gravity: World gravity in m/s² (negative = downward) player_height: Player collision capsule height in meters player_radius: Player collision capsule radius in meters eye_height: Camera height from player feet in meters player_mass: Player body mass in kg linear_damping: Air resistance (0.0-1.0, higher = more friction) movement_acceleration: Acceleration time (0.0=instant, higher=slower) air_control: Movement control while airborne (0.0-1.0) camera_fov: Field of view in degrees (typical: 60-90) min_pitch: Minimum vertical look angle in degrees (looking down) max_pitch: Maximum vertical look angle in degrees (looking up) Returns: Dictionary with player controller configuration """ return { "move_speed": move_speed, "jump_force": jump_force, "mouse_sensitivity": mouse_sensitivity, "invert_y": invert_y, "gravity": gravity, "player_height": player_height, "player_radius": player_radius, "eye_height": eye_height, "player_mass": player_mass, "linear_damping": linear_damping, "movement_acceleration": movement_acceleration, "air_control": air_control, "camera_fov": camera_fov, "min_pitch": min_pitch, "max_pitch": max_pitch } def create_scene( name: str = "Untitled Scene", description: Optional[str] = None, world_width: float = 100.0, world_height: float = 100.0, world_depth: float = 100.0, objects: Optional[List[Dict[str, Any]]] = None, lights: Optional[List[Dict[str, Any]]] = None, environment: Optional[Dict[str, Any]] = None, player: Optional[Dict[str, Any]] = None, show_grid: bool = False, grid_size: float = 100.0, grid_divisions: int = 20, tags: Optional[List[str]] = None ) -> Dict[str, Any]: """Create a complete 3D scene.""" now = datetime.utcnow().isoformat() return { "scene_id": str(uuid.uuid4()), "name": name, "description": description, "world_width": world_width, "world_height": world_height, "world_depth": world_depth, "objects": objects or [], "lights": lights or [], "environment": environment or create_environment(), "player": player or create_player(), "show_grid": show_grid, "grid_size": grid_size, "grid_divisions": grid_divisions, "tags": tags or [], "created_at": now, "updated_at": now } # Validation helpers (optional, for type checking) def validate_vector3(v: Dict[str, float]) -> bool: """Check if a dict is a valid Vector3.""" return isinstance(v, dict) and all(k in v for k in ["x", "y", "z"]) def validate_lighting_preset(preset: str) -> bool: """Check if a string is a valid lighting preset.""" return preset in ["day", "night", "sunset", "studio"] def validate_object_type(obj_type: str) -> bool: """Check if a string is a valid object type.""" return obj_type in ["cube", "sphere", "cylinder", "plane", "cone", "torus", "model"]