class_name JajceWorld extends Node3D enum PresentationQuality { HIGH, BALANCED, LOW, } const BALANCED_RENDER_SCALE := 0.85 const LOW_RENDER_SCALE := 0.7 const BALANCED_SHADOW_DISTANCE := 120.0 const LOW_SHADOW_DISTANCE := 80.0 const BALANCED_GRASS_SPACING := 1.0 const LOW_GRASS_SPACING := 2.0 const BALANCED_GRASS_FIXED_FPS := 18 const LOW_GRASS_FIXED_FPS := 12 const BALANCED_GRASS_INTERACTORS := 4 const BALANCED_GRASS_UPDATE_INTERVAL := 0.12 const HIGH_VOLUMETRIC_FOG_ENABLED := true static var _viewport_scale_states: Dictionary = {} @export_enum("High", "Balanced", "Low") var presentation_quality: int = PresentationQuality.BALANCED @onready var terrain: Terrain3D = $TerrainRoot/Terrain3D @onready var world_environment: WorldEnvironment = $WorldEnvironment @onready var directional_light: DirectionalLight3D = $DirectionalLight3D @onready var grass_field: Node3D = $TerrainRoot/Terrain3D/CozyGrassField @onready var grass_controller: Node = $TerrainRoot/GrassInteractionController var _active_presentation_quality := -1 var _authored_presentation_captured := false var _authored_render_scale := 1.0 var _authored_msaa: int = Viewport.MSAA_DISABLED var _authored_glow_enabled := false var _authored_volumetric_fog_enabled := HIGH_VOLUMETRIC_FOG_ENABLED var _authored_adjustment_enabled := false var _authored_shadow_distance := 0.0 var _authored_shadow_mode := DirectionalLight3D.SHADOW_PARALLEL_4_SPLITS var _authored_grass_visible := true var _authored_grass_physics_enabled := true var _authored_grass_spacing := 1.0 var _authored_grass_fixed_fps := 30 var _authored_grass_interactors := 1 var _authored_grass_update_interval := 0.1 var _authored_grass_controller_enabled := true var _last_applied_render_scale := -1.0 var _last_applied_msaa := -1 var _viewport_instance_id := 0 func _ready() -> void: _isolate_mutable_presentation_resources() _capture_authored_presentation() apply_presentation_quality(_resolve_requested_quality()) call_deferred("_initialize_world_presentation") func _exit_tree() -> void: _release_viewport_scale_owner() func apply_presentation_quality(quality: int) -> bool: if quality not in PresentationQuality.values(): push_warning("JajceWorld: unknown presentation quality %s" % quality) return false if not _authored_presentation_captured: _capture_authored_presentation() if not _authored_presentation_captured: return false if quality == _active_presentation_quality: return true match quality: PresentationQuality.HIGH: _apply_environment_quality( _authored_render_scale, _authored_glow_enabled, _authored_volumetric_fog_enabled, _authored_adjustment_enabled, _authored_shadow_distance, _authored_shadow_mode, _authored_msaa ) _apply_grass_quality( _authored_grass_visible, _authored_grass_physics_enabled, _authored_grass_spacing, _authored_grass_fixed_fps, _authored_grass_interactors, _authored_grass_update_interval, _authored_grass_controller_enabled ) PresentationQuality.BALANCED: _apply_environment_quality( minf(_authored_render_scale, BALANCED_RENDER_SCALE), _authored_glow_enabled, false, _authored_adjustment_enabled, minf(_authored_shadow_distance, BALANCED_SHADOW_DISTANCE), DirectionalLight3D.SHADOW_PARALLEL_2_SPLITS, mini(_authored_msaa, Viewport.MSAA_2X) ) _apply_grass_quality( _authored_grass_visible, _authored_grass_physics_enabled, maxf(_authored_grass_spacing, BALANCED_GRASS_SPACING), mini(_authored_grass_fixed_fps, BALANCED_GRASS_FIXED_FPS), mini(_authored_grass_interactors, BALANCED_GRASS_INTERACTORS), maxf(_authored_grass_update_interval, BALANCED_GRASS_UPDATE_INTERVAL), _authored_grass_controller_enabled ) PresentationQuality.LOW: _apply_environment_quality( minf(_authored_render_scale, LOW_RENDER_SCALE), false, false, false, minf(_authored_shadow_distance, LOW_SHADOW_DISTANCE), DirectionalLight3D.SHADOW_ORTHOGONAL, Viewport.MSAA_DISABLED ) _apply_grass_quality( false, false, maxf(_authored_grass_spacing, LOW_GRASS_SPACING), mini(_authored_grass_fixed_fps, LOW_GRASS_FIXED_FPS), 0, _authored_grass_update_interval, false ) var meadow := get_node_or_null("PainterlyMeadow") if meadow != null: meadow.call("apply_presentation_quality", quality) _active_presentation_quality = quality return true func get_active_presentation_quality() -> int: return _active_presentation_quality func get_active_presentation_quality_name() -> StringName: match _active_presentation_quality: PresentationQuality.HIGH: return &"high" PresentationQuality.BALANCED: return &"balanced" PresentationQuality.LOW: return &"low" return &"unknown" func _capture_authored_presentation() -> void: if _authored_presentation_captured: return var environment := _environment() if environment == null or grass_field == null or grass_controller == null: return _register_viewport_scale_owner() _authored_glow_enabled = environment.glow_enabled # Keep the serialized scene compatibility-safe; High deliberately opts into # this Forward+-only authored effect after the quality profile is resolved. _authored_volumetric_fog_enabled = HIGH_VOLUMETRIC_FOG_ENABLED _authored_adjustment_enabled = environment.adjustment_enabled _authored_shadow_distance = directional_light.directional_shadow_max_distance _authored_shadow_mode = directional_light.directional_shadow_mode _authored_grass_visible = grass_field.visible _authored_grass_physics_enabled = grass_field.is_physics_processing() _authored_grass_spacing = float(grass_field.get("instance_spacing")) _authored_grass_fixed_fps = int(grass_field.get("process_fixed_fps")) _authored_grass_interactors = int(grass_controller.get("max_interactors")) _authored_grass_update_interval = float(grass_controller.get("update_interval")) _authored_grass_controller_enabled = grass_controller.is_processing() _authored_presentation_captured = true func _isolate_mutable_presentation_resources() -> void: var source_environment := _environment() if source_environment != null: var isolated_environment := source_environment.duplicate(true) as Environment if source_environment.sky != null: var isolated_sky := source_environment.sky.duplicate(true) as Sky if source_environment.sky.sky_material != null: isolated_sky.sky_material = source_environment.sky.sky_material.duplicate(true) isolated_environment.sky = isolated_sky world_environment.environment = isolated_environment var source_grass_material := grass_field.get("mesh_material_override") as ShaderMaterial if source_grass_material != null: var isolated_grass_material := source_grass_material.duplicate(true) as ShaderMaterial grass_field.set("mesh_material_override", isolated_grass_material) grass_controller.set("grass_material", isolated_grass_material) var source_process_material := grass_field.get("process_material") as ShaderMaterial if source_process_material != null: var isolated_process_material := source_process_material.duplicate(true) as ShaderMaterial grass_field.set("process_material", isolated_process_material) var particles: Array = grass_field.get("particle_nodes") for value in particles: var particle := value as GPUParticles3D if particle != null: particle.process_material = isolated_process_material func _resolve_requested_quality() -> int: for argument in OS.get_cmdline_user_args(): if argument.begins_with("--quality="): return _quality_from_name(argument.trim_prefix("--quality=")) if argument.begins_with("--presentation-quality="): return _quality_from_name(argument.trim_prefix("--presentation-quality=")) return presentation_quality func _quality_from_name(value: String) -> int: match value.strip_edges().to_lower(): "high": return PresentationQuality.HIGH "low": return PresentationQuality.LOW "balanced": return PresentationQuality.BALANCED push_warning("JajceWorld: unknown presentation quality '%s'; using scene default" % value) return presentation_quality func _apply_environment_quality( render_scale: float, glow_enabled: bool, volumetric_fog_enabled: bool, adjustment_enabled: bool, shadow_distance: float, shadow_mode: int, msaa: int ) -> void: _claim_viewport_scale_owner() get_viewport().scaling_3d_scale = render_scale get_viewport().msaa_3d = msaa _last_applied_render_scale = render_scale _last_applied_msaa = msaa var environment := _environment() if environment != null: environment.glow_enabled = glow_enabled environment.volumetric_fog_enabled = (volumetric_fog_enabled and _supports_volumetric_fog()) environment.adjustment_enabled = adjustment_enabled directional_light.directional_shadow_max_distance = shadow_distance directional_light.directional_shadow_mode = shadow_mode func _apply_grass_quality( is_visible: bool, physics_enabled: bool, instance_spacing: float, fixed_fps: int, max_interactors: int, update_interval: float, controller_enabled: bool ) -> void: grass_field.set("instance_spacing", instance_spacing) grass_field.set("process_fixed_fps", fixed_fps) grass_field.visible = is_visible grass_field.set_physics_process(physics_enabled) grass_controller.set("max_interactors", maxi(max_interactors, 1)) grass_controller.set("update_interval", update_interval) grass_controller.call("set_interaction_enabled", controller_enabled) var particles: Array = grass_field.get("particle_nodes") for value in particles: var particle := value as GPUParticles3D if particle == null: continue particle.emitting = is_visible and physics_enabled if particle.emitting: particle.restart(true) var grass_material := grass_field.get("mesh_material_override") as ShaderMaterial if grass_material != null and not controller_enabled: grass_material.set_shader_parameter("interactor_count", 0) if physics_enabled: grass_field.set("last_pos", Vector3.INF) func _environment() -> Environment: if world_environment == null: return null return world_environment.environment func _supports_volumetric_fog() -> bool: return RenderingServer.get_current_rendering_method() == "forward_plus" func _register_viewport_scale_owner() -> void: var viewport := get_viewport() _viewport_instance_id = viewport.get_instance_id() var state: Dictionary = _viewport_scale_states.get(_viewport_instance_id, {}) if state.is_empty(): state = { "base_scale": viewport.scaling_3d_scale, "base_msaa": viewport.msaa_3d, "owners": [], } _authored_render_scale = float(state["base_scale"]) _authored_msaa = int(state["base_msaa"]) _viewport_scale_states[_viewport_instance_id] = state _claim_viewport_scale_owner() func _claim_viewport_scale_owner() -> void: if _viewport_instance_id == 0: return var state: Dictionary = _viewport_scale_states.get(_viewport_instance_id, {}) if state.is_empty(): return var owners: Array = state["owners"] for index in range(owners.size() - 1, -1, -1): var owner: Object = (owners[index] as WeakRef).get_ref() if owner == null or owner == self: owners.remove_at(index) owners.append(weakref(self)) state["owners"] = owners _viewport_scale_states[_viewport_instance_id] = state func _release_viewport_scale_owner() -> void: if _viewport_instance_id == 0 or not _viewport_scale_states.has(_viewport_instance_id): return var viewport := get_viewport() var state: Dictionary = _viewport_scale_states[_viewport_instance_id] var owners: Array = state["owners"] var was_active_owner := false for index in range(owners.size() - 1, -1, -1): var owner: Object = (owners[index] as WeakRef).get_ref() if owner == self: was_active_owner = index == owners.size() - 1 owners.remove_at(index) elif owner == null: owners.remove_at(index) if not owners.is_empty(): state["owners"] = owners _viewport_scale_states[_viewport_instance_id] = state if was_active_owner: var previous_owner := (owners[-1] as WeakRef).get_ref() as JajceWorld if ( previous_owner != null and previous_owner._last_applied_render_scale >= 0.0 and is_equal_approx(viewport.scaling_3d_scale, _last_applied_render_scale) ): viewport.scaling_3d_scale = previous_owner._last_applied_render_scale if ( previous_owner != null and previous_owner._last_applied_msaa >= 0 and viewport.msaa_3d == _last_applied_msaa ): viewport.msaa_3d = previous_owner._last_applied_msaa else: _viewport_scale_states.erase(_viewport_instance_id) if ( was_active_owner and is_equal_approx(viewport.scaling_3d_scale, _last_applied_render_scale) ): viewport.scaling_3d_scale = float(state["base_scale"]) if was_active_owner and viewport.msaa_3d == _last_applied_msaa: viewport.msaa_3d = int(state["base_msaa"]) _viewport_instance_id = 0 _last_applied_render_scale = -1.0 _last_applied_msaa = -1 func _initialize_world_presentation() -> void: var active_camera := get_viewport().get_camera_3d() if active_camera != null: terrain.set_camera(active_camera) var groups_to_snap: Array[Node] = [] groups_to_snap.append_array($FoliageRoot.get_children()) if has_node("FortressBlockout"): groups_to_snap.append($FortressBlockout) groups_to_snap.append_array($FortressBlockout.get_children()) for child in $VillageRoot.get_children(): groups_to_snap.append(child) if has_node("WorldObjects"): var wo := $WorldObjects for group_name in [ "Animals", "AnimalHabitats", "ResourceNodes", "StorageSites", "ActivitySites" ]: if wo.has_node(group_name): groups_to_snap.append_array(wo.get_node(group_name).get_children()) for item in groups_to_snap: if not item is Node3D: continue var node3d := item as Node3D var pos := node3d.global_position var h: float = terrain.data.get_height(pos) if not is_nan(h): node3d.global_position.y = h