{"TotalCount":68,"Files":[{"Ident":"ali3nsystems.lib-atmokinetic_assets","Path":"Camera/CameraController.cs","FileName":"CameraController.cs","PackageType":"library","CodeKind":"Game","AssetVersionId":338612,"IsPrivate":false,"Code":"namespace AtmokineticAssets;\r\n\r\npublic sealed partial class CameraController : BasePostProcess\u003CCameraController\u003E\r\n{\r\n\tpublic Material material {get;set;} = Material.Load(\u0022materials/mat-camera_weather.vmat\u0022);\r\n\tpublic bool IsCameraSubmerged { get; private set; }\r\n\tpublic bool IsCameraFullySubmerged { get; private set; }\r\n\tpublic float TimeSinceCameraLastExitedWater;\r\n\tpublic bool WasCameraFullySubmerged;\r\n\tprivate bool _transitioningFromFullySubmerged;\r\n\tpublic WaterController WaterController;\r\n\tpublic CameraComponent CameraComponent;\r\n\r\n\tprotected override void OnAwake()\r\n\t{\r\n\t\tCameraComponent = GameObject.GetComponent\u003CCameraComponent\u003E();\r\n\t}\r\n\r\n\tprotected override void OnUpdate()\r\n\t{\r\n\t\tCheckWaterSubmersion();\r\n\t\tUpdateWaterDrips();\r\n\t}\r\n\r\n\tpublic override void Render()\r\n\t{\r\n\t\tif (WaterLineCameraComponent.IsValid())\r\n\t\t{\r\n\t\t\tRenderWaterLineTexture();\r\n\t\t}\r\n\r\n\t\tif (WaterController.IsValid())\r\n\t\t{\r\n\t\t\tUnderWaterShaderAttributes();\r\n\t\t\t// WaterDripsShaderAttributes();\r\n\t\t\tWaterLineShaderAttributes();\r\n\t\t}\r\n\r\n\t\tWaterDripsFadeShaderAttribute();\r\n\t\tAttributes.Set(\u0022Is Camera Submerged\u0022, (IsCameraSubmerged \u0026\u0026 !IsCameraFullySubmerged) ? 1f : 0f);\r\n\t\tAttributes.Set(\u0022Is Fully Submerged\u0022, IsCameraFullySubmerged ? 1f : 0f);\r\n\r\n\t\tBlit(BlitMode.WithBackbuffer(material, Stage.BeforePostProcess, 100), \u0022Water Post Process\u0022);\r\n\t}\r\n}\r\n"},{"Ident":"ali3nsystems.lib-atmokinetic_assets","Path":"Camera/CameraController.Weather.WaterLine.cs","FileName":"CameraController.Weather.WaterLine.cs","PackageType":"library","CodeKind":"Game","AssetVersionId":338612,"IsPrivate":false,"Code":"namespace AtmokineticAssets;\r\n\r\npublic sealed partial class CameraController : BasePostProcess\u003CCameraController\u003E, Component.ExecuteInEditor\r\n{\r\n\tpublic GameObject WaterLineCameraGameObject;\r\n\tpublic CameraComponent WaterLineCameraComponent;\r\n\tpublic Texture WaterLineTexture;\r\n\r\n\tprivate void CheckWaterSubmersion()\r\n\t{\r\n\t\tvar corners = GetCameraNearPlaneCorners();\r\n\t\tif (corners == null)\r\n\t\t{\r\n\t\t\tIsCameraSubmerged = false;\r\n\t\t\tIsCameraFullySubmerged = false;\r\n\t\t\tif (WaterLineCameraGameObject.IsValid())\r\n\t\t\t{\r\n\t\t\t\tWaterController?.DestroyWaterLineMesh();\r\n\t\t\t\tDisableWaterLineRendering();\r\n\t\t\t}\r\n\t\t\treturn;\r\n\t\t}\r\n\r\n\t\t// Cached by EnvironmentController on awake - no per-frame scene scan.\r\n\t\tvar waterControllers = EnvironmentController.CachedWaterControllers;\r\n\t\tint maxSubmergedCount = 0;\r\n\t\tWaterController activeWater = null;\r\n\r\n\t\tforeach (var water in waterControllers)\r\n\t\t{\r\n\t\t\tif (!water.IsValid()) continue;\r\n\r\n\t\t\tint submergedCount = CountCameraSubmergedCorners(corners, water);\r\n\t\t\tif (submergedCount \u003E maxSubmergedCount)\r\n\t\t\t{\r\n\t\t\t\tmaxSubmergedCount = submergedCount;\r\n\t\t\t\tactiveWater = water;\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\tbool WasCameraFullySubmerged = IsCameraFullySubmerged;\r\n\t\tIsCameraSubmerged = maxSubmergedCount \u003E= 1;\r\n\r\n\t\t// Use offset check for fully Submerged (only applies when already in water)\r\n\t\tint fullySubmergedCount = activeWater != null ? CountCameraFullySubmergedCorners(corners, activeWater) : 0;\r\n\t\tbool isNowFullySubmerged = fullySubmergedCount == 4;\r\n\t\tbool isNowPartiallySubmerged = IsCameraSubmerged \u0026\u0026 !isNowFullySubmerged;\r\n\r\n\t\t// Handle water controller change\r\n\t\tif (activeWater != WaterController)\r\n\t\t{\r\n\t\t\tWaterController?.DestroyWaterLineMesh();\r\n\t\t\tWaterController = activeWater;\r\n\t\t}\r\n\r\n\t\t// Water line cube (only when partially Submerged)\r\n\t\tif (isNowPartiallySubmerged \u0026\u0026 !WaterLineCameraGameObject.IsValid())\r\n\t\t{\r\n\t\t\tWaterController?.CreateWaterLineMesh();\r\n\t\t\tActivateWaterLineRendering();\r\n\t\t}\r\n\r\n\t\telse if (!isNowPartiallySubmerged \u0026\u0026 WaterLineCameraGameObject.IsValid())\r\n\t\t{\r\n\t\t\tWaterController?.DestroyWaterLineMesh();\r\n\t\t\tDisableWaterLineRendering();\r\n\t\t}\r\n\r\n\t\t// Delay IsCameraFullySubmerged off by one frame to prevent flash\r\n\t\tif (WasCameraFullySubmerged \u0026\u0026 isNowPartiallySubmerged)\r\n\t\t{\r\n\t\t\tif (_transitioningFromFullySubmerged)\r\n\t\t\t{\r\n\t\t\t\tIsCameraFullySubmerged = false;\r\n\t\t\t\t_transitioningFromFullySubmerged = false;\r\n\t\t\t}\r\n\t\t\telse\r\n\t\t\t{\r\n\t\t\t\tIsCameraFullySubmerged = true;\r\n\t\t\t\t_transitioningFromFullySubmerged = true;\r\n\t\t\t}\r\n\t\t}\r\n\t\telse\r\n\t\t{\r\n\t\t\tIsCameraFullySubmerged = isNowFullySubmerged;\r\n\t\t\t_transitioningFromFullySubmerged = false;\r\n\t\t}\r\n\t}\r\n\r\n\tpublic void WaterLineShaderAttributes()\r\n\t{\r\n\t\tAttributes.Set(\u0022Water Line Size\u0022, WaterController.WaterLineSize);\r\n\t\tAttributes.Set(\u0022Water Line Blur\u0022, WaterController.WaterLineBlur);\r\n\t\tAttributes.Set(\u0022Water Line Color\u0022, WaterController.WaterLineColor);\r\n\t}\r\n\r\n    private void ActivateWaterLineRendering()\r\n\t{\r\n\t\tCameraComponent.RenderExcludeTags.Add(\u0022water_line_mesh\u0022);\r\n\r\n\t\tWaterLineCameraGameObject = new GameObject { Name = \u0022Water Line Camera\u0022, Parent = GameObject };\r\n\t\t// Local-only render helper - never include it in network snapshots.\r\n\t\tWaterLineCameraGameObject.Flags |= GameObjectFlags.NotNetworked;\r\n\t\tWaterLineCameraGameObject.NetworkMode = NetworkMode.Never;\r\n\t\tWaterLineCameraComponent = WaterLineCameraGameObject.AddComponent\u003CCameraComponent\u003E();\r\n\t\tWaterLineCameraComponent.BackgroundColor = Color.Black;\r\n\t\tWaterLineCameraComponent.IsMainCamera = false;\r\n\t\tWaterLineCameraComponent.FovAxis = CameraComponent.FovAxis;\r\n\t\tWaterLineCameraComponent.FieldOfView = CameraComponent.FieldOfView;\r\n\t\tWaterLineCameraComponent.ZNear = CameraComponent.ZNear;\r\n\t\tWaterLineCameraComponent.ZFar = CameraComponent.ZFar;\r\n\t\tWaterLineCameraComponent.Priority = CameraComponent.Priority \u002B 1;\r\n\t\tWaterLineCameraComponent.Orthographic = CameraComponent.Orthographic;\r\n\t\tWaterLineCameraComponent.OrthographicHeight = CameraComponent.OrthographicHeight;\r\n\t\tWaterLineCameraComponent.TargetEye = CameraComponent.TargetEye;\r\n\t\tWaterLineCameraComponent.Viewport = CameraComponent.Viewport;\r\n\t\tWaterLineCameraComponent.RenderTags.Add(\u0022water_line_mesh\u0022);\r\n\t\tWaterLineCameraComponent.RenderExcludeTags.RemoveAll();\r\n\t\tWaterLineCameraComponent.EnablePostProcessing = false;\r\n\t}\r\n\r\n\tprivate void RenderWaterLineTexture()\r\n\t{\r\n\t\tWaterLineTexture = Texture.CreateRenderTarget(\u0022WaterLineRenderTexture\u0022, ImageFormat.A8, Screen.Size, WaterLineTexture);\r\n\t\tWaterLineCameraComponent.RenderTarget = WaterLineTexture;\r\n\t\tAttributes.Set(\u0022WaterLineTexture\u0022, WaterLineTexture);\r\n\t}\r\n\r\n\tprivate void DisableWaterLineRendering()\r\n\t{\r\n\t\tWaterLineCameraGameObject.Destroy();\r\n\t\tWaterLineCameraGameObject = null;\r\n\t\tWaterLineCameraComponent = null;\r\n\r\n\t\tWaterLineTexture.Dispose();\r\n\t\tWaterLineTexture = null;\r\n\r\n\t\tCameraComponent.RenderExcludeTags.Remove(\u0022water_line_mesh\u0022);\r\n\t}\r\n}"},{"Ident":"ali3nsystems.lib-atmokinetic_assets","Path":"Code/Assembly.cs","FileName":"Assembly.cs","PackageType":"library","CodeKind":"Game","AssetVersionId":338612,"IsPrivate":false,"Code":"global using Sandbox;\r\nglobal using Sandbox.Rendering;\r\nglobal using System;\r\nglobal using System.Collections.Generic;\r\nglobal using System.Runtime.InteropServices;\r\nglobal using System.Text.RegularExpressions;\r\nglobal using System.Linq;\r\n"},{"Ident":"ali3nsystems.lib-atmokinetic_assets","Path":"Code/Camera/CameraController.Weather.UnderWater.cs","FileName":"CameraController.Weather.UnderWater.cs","PackageType":"library","CodeKind":"Game","AssetVersionId":338612,"IsPrivate":false,"Code":"namespace AtmokineticAssets;\r\n\r\npublic sealed partial class CameraController : BasePostProcess\u003CCameraController\u003E\r\n{\r\n    public void UnderWaterShaderAttributes()\r\n    {\r\n        Attributes.Set(\u0022Under Water Blur\u0022, WaterController.UnderWaterBlur);\r\n        Attributes.Set(\u0022Under Water Color\u0022, WaterController.UnderWaterColor);\r\n    }\t\r\n}"},{"Ident":"ali3nsystems.lib-atmokinetic_assets","Path":"Code/Graphics/GraphicsSettings.TerrainSettings.cs","FileName":"GraphicsSettings.TerrainSettings.cs","PackageType":"library","CodeKind":"Game","AssetVersionId":338612,"IsPrivate":false,"Code":"namespace AtmokineticAssets;\r\n\r\npublic sealed partial class GraphicsSettings : Component, Component.ExecuteInEditor\r\n{\r\n    //Terrain Settings\r\n    public enum TerrainRenderingModeEnumeration\r\n    {\r\n        [Title(\u0022Parallax Occlusion Mapping\u0022)]\r\n        ParallaxOcclusionMapping,\r\n        \r\n        [Title(\u0022Bump Offset\u0022)]\r\n        BumpOffset,\r\n       \r\n        Standard\r\n    }\r\n\r\n    //[Property, Feature(\u0022Terrain\u0022), Title(\u0022Rendering\u0022)]\r\n    public TerrainRenderingModeEnumeration TerrainRenderingMode { get; set; } = TerrainRenderingModeEnumeration.Standard;\r\n\r\n    public enum TerrainRenderingParallaxOcclusionMappingQualityEnumeration\r\n    {\r\n        Ultra,\r\n        High,\r\n        Medium,\r\n        Low\r\n    }\r\n\r\n    //[Property, Feature(\u0022Terrain\u0022), Title(\u0022Quality\u0022)] \r\n    [ShowIf(nameof(TerrainRenderingMode), TerrainRenderingModeEnumeration.ParallaxOcclusionMapping)]\r\n    public TerrainRenderingParallaxOcclusionMappingQualityEnumeration TerrainRenderingParallaxOcclusionMapping { get; set; } = TerrainRenderingParallaxOcclusionMappingQualityEnumeration.Medium;\r\n\r\n    public enum TerrainRenderingBumpOffsetQualityEnumeration\r\n    {\r\n        Ultra,\r\n        High,\r\n        Medium,\r\n        Low\r\n    }\r\n   \r\n    //[Property, Feature(\u0022Terrain\u0022), Title(\u0022Quality\u0022)] \r\n    [ShowIf(nameof(TerrainRenderingMode), TerrainRenderingModeEnumeration.BumpOffset)]\r\n    public TerrainRenderingParallaxOcclusionMappingQualityEnumeration TerrainRenderingBumpOffset { get; set; } = TerrainRenderingParallaxOcclusionMappingQualityEnumeration.Medium;\r\n\r\n\r\n    // public void ChangeTerrainSettings()\r\n    // {\r\n    //             // Terrain rendering mode: 0 = POM, 1 = Bump Offset, 2 = Standard\r\n    //     int terrainRenderingMode = TerrainRenderingMode switch\r\n    //     {\r\n    //         TerrainRenderingModeEnumeration.ParallaxOcclusionMapping =\u003E 0,\r\n    //         TerrainRenderingModeEnumeration.BumpOffset =\u003E 1,\r\n    //         TerrainRenderingModeEnumeration.Standard =\u003E 2,\r\n    //         _ =\u003E 1\r\n    //     };\r\n\r\n    //     // Quality: Ultra = 1, High = 2, Medium = 4, Low = 8\r\n    //     float quality;\r\n    //     switch ( TerrainRenderingMode )\r\n    //     {\r\n    //         case TerrainRenderingModeEnumeration.ParallaxOcclusionMapping:\r\n    //             quality = TerrainRenderingParallaxOcclusionMapping switch\r\n    //             {\r\n    //                 TerrainRenderingParallaxOcclusionMappingQualityEnumeration.Ultra =\u003E 1f,\r\n    //                 TerrainRenderingParallaxOcclusionMappingQualityEnumeration.High =\u003E 2f,\r\n    //                 TerrainRenderingParallaxOcclusionMappingQualityEnumeration.Medium =\u003E 4f,\r\n    //                 TerrainRenderingParallaxOcclusionMappingQualityEnumeration.Low =\u003E 8f,\r\n    //                 _ =\u003E 4f\r\n    //             };\r\n    //             break;\r\n\r\n    //         case TerrainRenderingModeEnumeration.BumpOffset:\r\n    //             quality = TerrainRenderingBumpOffset switch\r\n    //             {\r\n    //                 TerrainRenderingParallaxOcclusionMappingQualityEnumeration.Ultra =\u003E 1f,\r\n    //                 TerrainRenderingParallaxOcclusionMappingQualityEnumeration.High =\u003E 2f,\r\n    //                 TerrainRenderingParallaxOcclusionMappingQualityEnumeration.Medium =\u003E 4f,\r\n    //                 TerrainRenderingParallaxOcclusionMappingQualityEnumeration.Low =\u003E 8f,\r\n    //                 _ =\u003E 4f\r\n    //             };\r\n    //             break;\r\n\r\n    //         default:\r\n    //             quality = 4f;\r\n    //             break;\r\n    //     }\r\n\r\n    //     // Enumerate live \u2014 objects spawned after startup (e.g. baked water tiles) must be included.\r\n    //     foreach ( var sceneObject in Scene.SceneWorld.SceneObjects )\r\n    //     {\r\n    //         if ( sceneObject is null || !sceneObject.IsValid() ) continue;\r\n    //         sceneObject.Attributes.Set( \u0022D_Rendering\u0022, terrainRenderingMode );\r\n    //         sceneObject.Attributes.Set( \u0022Quality\u0022, quality );\r\n    //     }\r\n    // }\r\n}"},{"Ident":"ali3nsystems.lib-atmokinetic_assets","Path":"Player/CharacterControllerUtilities.DisableSelfCollision.cs","FileName":"CharacterControllerUtilities.DisableSelfCollision.cs","PackageType":"library","CodeKind":"Game","AssetVersionId":338612,"IsPrivate":false,"Code":"using Sandbox.Physics;\r\n\r\nnamespace AtmokineticAssets;\r\n\r\n[Title( \u0022Atmokinetic Assets - Character Controller Utilities\u0022 )]\r\n[Category( \u0022Atmokinetic Assets\u0022 )]\r\n[Icon( \u0022badge\u0022 )]\r\n\r\n\r\npublic sealed partial class CharacterControllerUtilities : Component, Component.INetworkSpawn\r\n{\r\n    /// \u003Csummary\u003E\r\n    /// THIS IS PRIMARILY USED FOR THE MODELPHYISICS TO GENERATE WATER SIMULATIONS/RIPPLES\r\n    /// WITHOUT COLLIDING WITH THE CHARACTER CONTROLLER\u0027S BOX/CAPSULE COLLIDER. \r\n    /// IT USES THE STEAMID AS AN IDENTIFIER.\r\n    /// \u003C/summary\u003E\r\n\r\n    private string _appliedTag;\r\n    private ModelPhysics _modelPhysics;\r\n    private int _lastTaggedBodyCount = -1;\r\n\r\n    public void OnNetworkSpawn( Connection owner )\r\n    {\r\n        EnsurePhysics();\r\n        ApplyTag( owner );\r\n    }\r\n\r\n    protected override void OnStart()\r\n    {\r\n        EnsurePhysics();\r\n\r\n        // Fallback for non-networked scenes: tag with the local connection\r\n        ApplyTag( Network.Owner ?? Connection.Local );\r\n    }\r\n\r\n    // Create a ModelPhysics from the player\u0027s SkinnedModelRenderer if one is\r\n    // missing, so the collision bodies this component tags actually exist.\r\n    private void EnsurePhysics()\r\n    {\r\n        if ( GameObject.GetComponentInChildren\u003CModelPhysics\u003E( true ).IsValid() )\r\n            return;\r\n\r\n        var renderer = GameObject.GetComponentInChildren\u003CSkinnedModelRenderer\u003E( true );\r\n        if ( !renderer.IsValid() || renderer.Model is null )\r\n            return;\r\n\r\n        var physics = renderer.GameObject.AddComponent\u003CModelPhysics\u003E();\r\n        physics.Renderer = renderer;\r\n        physics.Model = renderer.Model;\r\n        physics.MotionEnabled = false;\r\n    }\r\n\r\n    protected override void OnUpdate()\r\n    {\r\n        // ModelPhysics creates its ragdoll bone objects lazily (and rebuilds them on\r\n        // model reload). Instead of walking every child collider each tick, watch the\r\n        // body count - it only changes when the ragdoll is (re)built.\r\n        if ( string.IsNullOrEmpty( _appliedTag ) )\r\n            return;\r\n\r\n        if ( !_modelPhysics.IsValid() )\r\n            _modelPhysics = GameObject.GetComponentInChildren\u003CModelPhysics\u003E( true );\r\n\r\n        int bodyCount = _modelPhysics.IsValid() ? _modelPhysics.Bodies.Count : 0;\r\n        if ( bodyCount == _lastTaggedBodyCount )\r\n            return;\r\n\r\n        _lastTaggedBodyCount = bodyCount;\r\n        TagColliders( _appliedTag );\r\n    }\r\n\r\n    private void ApplyTag( Connection connection )\r\n    {\r\n        if ( connection is null )\r\n            return;\r\n\r\n        var tag = connection.SteamId.ToString();\r\n\r\n        if ( _appliedTag == tag )\r\n            return;\r\n\r\n        // Clean up a previously applied id (e.g. after ownership transfer)\r\n        if ( !string.IsNullOrEmpty( _appliedTag ) )\r\n            GameObject.Tags.Remove( _appliedTag );\r\n\r\n        // The tag itself replicates over the network (it\u0027s GameObject state), so a\r\n        // joining client can see it already applied from the snapshot - but the\r\n        // collision-ignore rule lives in this machine\u0027s own PhysicsWorld, which is\r\n        // NOT networked. So only skip re-adding the tag; always (re)register the\r\n        // rule locally, or clients would collide with their own ragdoll forever.\r\n        if ( !GameObject.Tags.Has( tag ) )\r\n            GameObject.Tags.Add( tag );\r\n\r\n        IgnoreSelfCollision( tag );\r\n        TagColliders( tag );\r\n\r\n        _appliedTag = tag;\r\n    }\r\n\r\n    // Add a rule so any two shapes both carrying this exact Steam ID tag don\u0027t\r\n    // collide. Only this player\u0027s own colliders carry it, so its ragdoll ignores\r\n    // its own body while still colliding with other players (whose tag differs).\r\n    private void IgnoreSelfCollision( string tag )\r\n    {\r\n        var rules = Scene.PhysicsWorld?.CollisionRules;\r\n        if ( rules is null )\r\n            return;\r\n\r\n        rules.Pairs[new CollisionRules.Pair( tag, tag )] = CollisionRules.Result.Ignore;\r\n    }\r\n\r\n    // Stamp the Steam ID tag onto every collider under the player. Shapes take\r\n    // their tags from their GameObject, and ragdoll bone objects don\u0027t inherit\r\n    // the root\u0027s tags, so each one has to be tagged directly.\r\n    private void TagColliders( string tag )\r\n    {\r\n        foreach ( var collider in GameObject.GetComponentsInChildren\u003CCollider\u003E( true ) )\r\n        {\r\n            if ( !collider.GameObject.Tags.Has( tag ) )\r\n                collider.GameObject.Tags.Add( tag );\r\n        }\r\n    }\r\n\r\n}\r\n"},{"Ident":"ali3nsystems.lib-atmokinetic_assets","Path":"Water/Modes/Tiles/WaterController.WaterTiles.cs","FileName":"WaterController.WaterTiles.cs","PackageType":"library","CodeKind":"Game","AssetVersionId":338612,"IsPrivate":false,"Code":"namespace AtmokineticAssets;\r\n\r\npublic sealed partial class WaterController: Component, Component.ITriggerListener\r\n{\r\n\t\r\n\t\r\n\t[Property, Title(\u0022Tile Model\u0022), Feature(\u0022General\u0022), Group(\u0022Properties\u0022), ShowIf( nameof(WaterMode), WaterModeEnumeration.Tiles )]\r\n\tpublic Model WaterTileModel { get; set; } = Model.Load(\u0022models/mdl-water_tile.vmdl\u0022);\r\n\t\r\n\t[Property, Title(\u0022Tile Size (cm)\u0022), Feature(\u0022General\u0022), Group(\u0022Properties\u0022), ShowIf( nameof(WaterMode), WaterModeEnumeration.Tiles )]\r\n\tpublic float WaterTileSize\r\n\t{\r\n\t\tget;\r\n\t\tset\r\n\t\t{\r\n\t\t\tfield = ResizeWaterTileBounds( field, value );\r\n\t\t}\r\n\t} = 500f;\r\n\t\r\n\t[Property, Title(\u0022Bounds Maximum (cm)\u0022), Feature(\u0022General\u0022), Group(\u0022Properties\u0022), ShowIf( nameof(WaterMode), WaterModeEnumeration.Tiles )]\r\n\tpublic Vector3 WaterTileBoundsMaximum { get; set; } = new( 500, 500, 500 );\r\n\r\n\t[Property, Title(\u0022Bounds Minimum (cm)\u0022), Feature(\u0022General\u0022), Group(\u0022Properties\u0022), ShowIf( nameof(WaterMode), WaterModeEnumeration.Tiles )]\r\n\tpublic Vector3 WaterTileBoundsMinimum { get; set; } = new( -500, -500, -500 );\r\n\r\n\t[Button(\u0022Generate Water Tiles\u0022), Feature(\u0022General\u0022), Group(\u0022Properties\u0022), ShowIf( nameof(WaterMode), WaterModeEnumeration.Tiles )]\r\n\tpublic void GenerateWaterTiles()\r\n\t{\r\n\t\tBake();\r\n\t\tWaterSurfaceAttributes();\r\n\t}\r\n\r\n\t[Property, Title(\u0022Enable Gizmo\u0022),Feature( \u0022General\u0022 ), ShowIf( nameof(WaterMode), WaterModeEnumeration.Tiles)]\r\n\tpublic bool EnableWaterTileGizmo { get; set; } = true;\r\n\tprivate readonly Dictionary\u003C(int x, int y), (GameObject go, ModelRenderer renderer)\u003E _tiles = [];\r\n\tprivate Vector2 _lastSnappedPosition = new( float.MinValue, float.MinValue );\r\n\tprivate BoxCollider _waterCollider;\r\n\tprivate bool _waterLineActive;\r\n\tprivate readonly Dictionary\u003C(int x, int y), GameObject\u003E _waterLineChildren = [];\r\n\r\n\t// True once the component has started \u2014 gates editor-only bounds rescaling so it\r\n\t// never fires during scene deserialization (which would clobber the saved bounds).\r\n\tprivate bool _tilesStarted;\r\n\r\n\tpublic float SubmersionOffset =\u003E -(WaterRipplesDisplacementA \u002B WaterRipplesDisplacementB);\r\n\r\n\t// World-adjusted snapped bounds (inches). Internal counterparts to the public\r\n\t// WaterTileBounds* props, which must NEVER be written by code.\r\n\tprivate Vector3 _waterTileBoundsMinimum;\r\n\tprivate Vector3 _waterTileBoundsMaximum;\r\n\r\n\t// Integer tile-grid indices, computed once in CalculateBounds (half-open range).\r\n\tprivate int _tileMinX, _tileMaxX, _tileMinY, _tileMaxY;\r\n\r\n\t// Cached bounds in world space (inches) \u2014 derived from the integer indices above.\r\n\tprivate Vector2 _boundsMin;\r\n\tprivate Vector2 _boundsMax;\r\n\tprivate float _waterZ;\r\n\r\n\t// Tile size in cm (all meshes are same size)\r\n\r\n\tpublic IEnumerable\u003CModelRenderer\u003E GetTileRenderers()\r\n\t{\r\n\t\tforeach ( var kvp in _tiles )\r\n\t\t{\r\n\t\t\tif ( kvp.Value.renderer.IsValid() )\r\n\t\t\t\tyield return kvp.Value.renderer;\r\n\t\t}\r\n\t}\r\n\r\n\tpublic void Bake()\r\n\t{\r\n\t\tCalculateRingLayout();\r\n\t\tDestroyAllTiles();\r\n\t\tCalculateBounds();\r\n\t\tCreateWaterCollider();\r\n\t\tBakeTiles();\r\n\t}\r\n\r\n\tpublic float GetWaterSurfaceZ() =\u003E _waterZ;\r\n\tpublic float GetWaterSurfaceZWithOffset() =\u003E _waterZ \u002B SubmersionOffset;\r\n\r\n\tpublic BBox GetWorldBounds()\r\n\t{\r\n\t\tfloat minZ = WorldPosition.z \u002B UnitConversion.CentimetersToInches( WaterTileBoundsMinimum.z );\r\n\t\treturn new BBox(\r\n\t\t\tnew Vector3( _boundsMin.x, _boundsMin.y, minZ ),\r\n\t\t\tnew Vector3( _boundsMax.x, _boundsMax.y, _waterZ ) );\r\n\t}\r\n\r\n\t// True when the whole water bounds are outside the camera frustum, i.e. every tile is\r\n\t// culled. One frustum-vs-BBox test per frame, independent of tile count.\r\n\t// (SceneObject.RenderingEnabled can\u0027t be used \u2014 it\u0027s a toggle you set, not a per-frame\r\n\t// cull result, and the engine doesn\u0027t expose \u0022was culled last frame\u0022 publicly. Frustum\r\n\t// is the only reliable option; it misses occlusion, which isn\u0027t cheaply queryable.)\r\n\tpublic bool AreAllTilesCulled()\r\n\t{\r\n\t\tvar camera = Scene.Camera;\r\n\t\tif ( !camera.IsValid() )\r\n\t\t\treturn true;\r\n\r\n\t\tvar frustum = camera.GetFrustum( new Rect( 0, 0, Screen.Width, Screen.Height ) );\r\n\t\treturn !frustum.IsInside( GetWorldBounds(), partially: true );\r\n\t}\r\n\r\n\tpublic bool IsPointInWaterVolume( Vector3 point )\r\n\t{\r\n\t\tvar b = GetWorldBounds();\r\n\t\treturn point.x \u003E= b.Mins.x \u0026\u0026 point.x \u003C= b.Maxs.x \u0026\u0026\r\n\t\t\t   point.y \u003E= b.Mins.y \u0026\u0026 point.y \u003C= b.Maxs.y \u0026\u0026\r\n\t\t\t   point.z \u003C GetWaterSurfaceZ() \u002B ripples.Count \u0026\u0026 point.z \u003E= b.Mins.z;\r\n\t}\r\n\r\n\tpublic bool IsPointFullySubmerged( Vector3 point )\r\n\t{\r\n\t\tvar b = GetWorldBounds();\r\n\t\treturn point.x \u003E= b.Mins.x \u0026\u0026 point.x \u003C= b.Maxs.x \u0026\u0026\r\n\t\t\t   point.y \u003E= b.Mins.y \u0026\u0026 point.y \u003C= b.Maxs.y \u0026\u0026\r\n\t\t\t   point.z \u003C GetWaterSurfaceZWithOffset() \u0026\u0026 point.z \u003E= b.Mins.z;\r\n\t}\r\n\r\n\t// Body group layout convention on the \u0022subdivision\u0022 body group:\r\n\t//   0        : water-line variant of ring 0 (rendered only by the water-line camera)\r\n\t//   1        : ring 0 (center, 2x2 quadrants)\r\n\t//   2..n-2   : an end/corner pair per ring (subdivided_N_end, subdivided_N_corner)\r\n\t//   n-1      : the last subdivision (far filler, no variants)\r\n\t// MaxRings and the far index are derived from the model in CalculateRingLayout.\r\n\tprivate int MaxRings { get; set; } = 4;\r\n\tprivate int FarBodyGroupIndex { get; set; } = 8;\r\n\r\n\t// Derive the ring count from the model\u0027s \u0022subdivision\u0022 body group, so adding or\r\n\t// removing ring meshes in the model needs no code change.\r\n\tprivate void CalculateRingLayout()\r\n\t{\r\n\t\tvar subdivisionPart = WaterTileModel?.Parts.All\r\n\t\t\t.FirstOrDefault( p =\u003E p.Name == \u0022subdivision\u0022 );\r\n\r\n\t\t// Minimum viable layout: waterline \u002B ring 0 \u002B one end/corner pair \u002B far.\r\n\t\tif ( subdivisionPart is null || subdivisionPart.Choices.Count \u003C 5 )\r\n\t\t\treturn;\r\n\r\n\t\tint choiceCount = subdivisionPart.Choices.Count;\r\n\r\n\t\tif ( ( choiceCount - 3 ) % 2 != 0 )\r\n\t\t\tLog.Warning( $\u0022Water tile model \u0027{WaterTileModel.ResourceName}\u0027 has {choiceCount} subdivision choices - expected waterline \u002B ring0 \u002B end/corner pairs \u002B far. Ring layout may be wrong.\u0022 );\r\n\r\n\t\tFarBodyGroupIndex = choiceCount - 1;\r\n\t\tMaxRings = ( choiceCount - 3 ) / 2 \u002B 1;\r\n\t}\r\n\r\n\tprivate enum TileType\r\n\t{\r\n\t\tEnd,      // Outline side\r\n\t\tCorner    // Outline corner\r\n\t}\r\n\r\n\tprivate int GetBodyGroupForRing( int ringLevel, TileType tileType )\r\n\t{\r\n\t\tif ( ringLevel == 0 )\r\n\t\t\treturn 1;\r\n\t\tif ( ringLevel \u003E= MaxRings )\r\n\t\t\treturn FarBodyGroupIndex;\r\n\r\n\t\tint baseIndex = ( ringLevel - 1 ) * 2 \u002B 2;\r\n\t\treturn tileType == TileType.Corner ? baseIndex \u002B 1 : baseIndex;\r\n\t}\r\n\r\n\tprivate void UpdateRings()\r\n\t{\r\n\t\tif ( _tiles.Count == 0 )\r\n\t\t\treturn;\r\n\r\n\t\tvar camera = Scene.Camera;\r\n\t\tif ( camera == null )\r\n\t\t\treturn;\r\n\r\n\t\t// Half-tile bias shifts ring transitions to tile centers instead of tile edges.\r\n\t\t// Snap the camera in box-local space (relative to the GameObject origin) to match the tile layout.\r\n\t\tfloat tileSizeInches = UnitConversion.CentimetersToInches( WaterTileSize );\r\n\t\tfloat halfTile = tileSizeInches * 0.5f;\r\n\t\tfloat localCamX = camera.WorldPosition.x - WorldPosition.x;\r\n\t\tfloat localCamY = camera.WorldPosition.y - WorldPosition.y;\r\n\t\tfloat snappedX = MathF.Floor( ( localCamX - halfTile ) / tileSizeInches ) * tileSizeInches;\r\n\t\tfloat snappedY = MathF.Floor( ( localCamY - halfTile ) / tileSizeInches ) * tileSizeInches;\r\n\t\tVector2 snappedPos = new( snappedX, snappedY );\r\n\r\n\t\tif ( snappedPos != _lastSnappedPosition )\r\n\t\t{\r\n\t\t\t_lastSnappedPosition = snappedPos;\r\n\t\t\tUpdateTileBodyGroups( snappedPos );\r\n\t\t\tSyncWaterLineChildren();\r\n\t\t}\r\n\t}\r\n\r\n\tprivate void DestroyAllTiles()\r\n\t{\r\n\t\tforeach ( var kvp in _tiles )\r\n\t\t{\r\n\t\t\tif ( kvp.Value.go.IsValid() )\r\n\t\t\t\tkvp.Value.go.Destroy();\r\n\t\t}\r\n\t\t_tiles.Clear();\r\n\t\tMarkRippleRenderersDirty();\r\n\t}\r\n\r\n\tpublic void CreateWaterLineMesh()\r\n\t{\r\n\t\tif ( _waterLineActive ) return;\r\n\t\t_waterLineActive = true;\r\n\t\t// If tiles are already live, sync immediately; otherwise OnUpdate will handle it on next snap.\r\n\t\tif ( _tiles.Count \u003E 0 )\r\n\t\t\tSyncWaterLineChildren();\r\n\t}\r\n\r\n\tpublic void DestroyWaterLineMesh()\r\n\t{\r\n\t\t_waterLineActive = false;\r\n\t\tforeach ( var kvp in _waterLineChildren )\r\n\t\t{\r\n\t\t\tif ( kvp.Value.IsValid() )\r\n\t\t\t\tkvp.Value.Destroy();\r\n\t\t}\r\n\t\t_waterLineChildren.Clear();\r\n\t\tMarkRippleRenderersDirty();\r\n\t}\r\n\r\n\tprivate void SyncWaterLineChildren()\r\n\t{\r\n\t\tif ( !_waterLineActive || WaterTileModel == null )\r\n\t\t\treturn;\r\n\r\n\t\tvar ringZeroKeys = new HashSet\u003C(int x, int y)\u003E();\r\n\t\tforeach ( var kvp in _tiles )\r\n\t\t{\r\n\t\t\tif ( kvp.Value.renderer.IsValid() \u0026\u0026 kvp.Value.renderer.GetBodyGroup( \u0022subdivision\u0022 ) == 1 )\r\n\t\t\t\tringZeroKeys.Add( kvp.Key );\r\n\t\t}\r\n\r\n\t\tvar stale = new List\u003C(int x, int y)\u003E();\r\n\t\tforeach ( var kvp in _waterLineChildren )\r\n\t\t{\r\n\t\t\tif ( ringZeroKeys.Contains( kvp.Key ) )\r\n\t\t\t\tcontinue;\r\n\t\t\tif ( kvp.Value.IsValid() )\r\n\t\t\t\tkvp.Value.Destroy();\r\n\t\t\tstale.Add( kvp.Key );\r\n\t\t}\r\n\t\tforeach ( var k in stale )\r\n\t\t\t_waterLineChildren.Remove( k );\r\n\r\n\t\tif ( stale.Count \u003E 0 )\r\n\t\t\tMarkRippleRenderersDirty();\r\n\r\n\t\tforeach ( var key in ringZeroKeys )\r\n\t\t{\r\n\t\t\tif ( _waterLineChildren.ContainsKey( key ) )\r\n\t\t\t\tcontinue;\r\n\t\t\tif ( !_tiles.TryGetValue( key, out var tile ) || !tile.go.IsValid() )\r\n\t\t\t\tcontinue;\r\n\r\n\t\t\tvar childGo = new GameObject( true, \u0022waterline\u0022 ) { Parent = tile.go };\r\n\t\t\t// Local-only render mesh - never include it in network snapshots.\r\n\t\t\tchildGo.Flags |= GameObjectFlags.NotNetworked;\r\n\t\t\tchildGo.NetworkMode = NetworkMode.Never;\r\n\t\t\tchildGo.Tags.Add( \u0022water_line_mesh\u0022 );\r\n\r\n\t\t\tvar r = childGo.Components.Create\u003CModelRenderer\u003E();\r\n\t\t\tr.Model = WaterTileModel;\r\n\r\n\t\t\tr.RenderType = ModelRenderer.ShadowRenderType.Off;\r\n\t\t\tr.SceneObject.Attributes.Set( \u0022BoundsMin\u0022, _boundsMin );\r\n\t\t\tr.SceneObject.Attributes.Set( \u0022BoundsMax\u0022, _boundsMax );\r\n\t\t\t\r\n\t\t\tr.SceneObject.Attributes.Set( \u0022WaterRipplesTextureA\u0022, WaterRipplesTextureA );\r\n\t\t\tr.SceneObject.Attributes.Set( \u0022Water Ripples Scale A\u0022, WaterRipplesScaleA );\r\n\t\t\tr.SceneObject.Attributes.Set( \u0022Water Ripples Rotation A\u0022, WaterRipplesRotationA );\r\n\t\t\tr.SceneObject.Attributes.Set( \u0022Water Ripples Speed A\u0022, WaterRipplesSpeedA );\r\n\t\t\tr.SceneObject.Attributes.Set( \u0022Water Ripples Displacement A\u0022, WaterRipplesDisplacementA );\r\n\r\n\t\t\tr.SceneObject.Attributes.Set( \u0022WaterRipplesTextureB\u0022, WaterRipplesTextureB );\r\n\t\t\tr.SceneObject.Attributes.Set( \u0022Water Ripples Scale B\u0022, WaterRipplesScaleB );\r\n\t\t\tr.SceneObject.Attributes.Set( \u0022Water Ripples Rotation B\u0022, WaterRipplesRotationB );\r\n\t\t\tr.SceneObject.Attributes.Set( \u0022Water Ripples Speed B\u0022, WaterRipplesSpeedB );\r\n\t\t\tr.SceneObject.Attributes.Set( \u0022Water Ripples Displacement B\u0022, WaterRipplesDisplacementB );\r\n\r\n\r\n\t\t\tr.Attributes.SetCombo( \u0022D_TRANSLUCENT\u0022, 0 );\r\n\t\t\tr.Attributes.SetCombo( \u0022D_Enable_Water_Line\u0022, 1 );\r\n\t\t\tr.SetBodyGroup( \u0022subdivision\u0022, 0 );\r\n\r\n\t\t\t_waterLineChildren[key] = childGo;\r\n\t\t\tMarkRippleRenderersDirty();\r\n\t\t}\r\n\t}\r\n\r\n\tpublic void OnTriggerEnter( Collider other )\r\n\t{\r\n\t\tif ( !other.GameObject.Tags.Has( \u0022particles\u0022 ) )\r\n\t\t\treturn;\r\n\r\n\t\tother.GameObject.Destroy();\r\n\t}\r\n\r\n\tpublic void OnTriggerExit( Collider other )\r\n\t{\r\n\t}\r\n\r\n\tprivate void CreateWaterCollider()\r\n\t{\r\n\t\t// Only create the trigger collider at runtime, not while editing the scene.\r\n\t\tif ( Scene.IsEditor )\r\n\t\t\treturn;\r\n\r\n\t\t// Use the snapped tile region so the trigger matches the spawned tiles.\r\n\t\t// _waterTileBounds* are world-space; convert to local for the (parented) collider.\r\n\t\tVector3 localMin = _waterTileBoundsMinimum - WorldPosition;\r\n\t\tVector3 localMax = _waterTileBoundsMaximum - WorldPosition;\r\n\r\n\t\tVector3 center = ( localMin \u002B localMax ) * 0.5f;\r\n\t\tVector3 size = localMax - localMin;\r\n\r\n\t\t_waterCollider ??= Components.Get\u003CBoxCollider\u003E( FindMode.InSelf ) ?? Components.Create\u003CBoxCollider\u003E();\r\n\t\t_waterCollider.Flags = ComponentFlags.Hidden;\r\n\t\t_waterCollider.Center = center;\r\n\t\t_waterCollider.Scale = size;\r\n\t\t_waterCollider.IsTrigger = true;\r\n\t\t_waterCollider.Static = true;\r\n\t}\r\n\r\n\tprivate void CalculateBounds()\r\n\t{\r\n\t\tvar minInches = UnitConversion.CentimetersToInches( WaterTileBoundsMinimum );\r\n\t\tvar maxInches = UnitConversion.CentimetersToInches( WaterTileBoundsMaximum );\r\n\r\n\t\tfloat tileSizeInches = UnitConversion.CentimetersToInches( WaterTileSize );\r\n\r\n\t\t// Snap the extent to the tile grid in LOCAL space (relative to the GameObject origin).\r\n\t\t// Tiles are anchored to the box, so they fill it exactly and follow the GameObject smoothly.\r\n\t\t_tileMinX = (int)MathF.Floor( MathF.Round( minInches.x / tileSizeInches, 4 ) );\r\n\t\t_tileMinY = (int)MathF.Floor( MathF.Round( minInches.y / tileSizeInches, 4 ) );\r\n\t\t_tileMaxX = (int)MathF.Ceiling( MathF.Round( maxInches.x / tileSizeInches, 4 ) );\r\n\t\t_tileMaxY = (int)MathF.Ceiling( MathF.Round( maxInches.y / tileSizeInches, 4 ) );\r\n\r\n\t\t// Local-space snapped corners.\r\n\t\tfloat minLocalX = _tileMinX * tileSizeInches;\r\n\t\tfloat minLocalY = _tileMinY * tileSizeInches;\r\n\t\tfloat maxLocalX = _tileMaxX * tileSizeInches;\r\n\t\tfloat maxLocalY = _tileMaxY * tileSizeInches;\r\n\r\n\t\t// World-space bounds (for collider-independent consumers, shader clipping, point queries).\r\n\t\t_waterTileBoundsMinimum = new Vector3( WorldPosition.x \u002B minLocalX, WorldPosition.y \u002B minLocalY, WorldPosition.z \u002B minInches.z );\r\n\t\t_waterTileBoundsMaximum = new Vector3( WorldPosition.x \u002B maxLocalX, WorldPosition.y \u002B maxLocalY, WorldPosition.z \u002B maxInches.z );\r\n\r\n\t\t_boundsMin = new Vector2( _waterTileBoundsMinimum.x, _waterTileBoundsMinimum.y );\r\n\t\t_boundsMax = new Vector2( _waterTileBoundsMaximum.x, _waterTileBoundsMaximum.y );\r\n\t\t_waterZ = _waterTileBoundsMaximum.z;\r\n\t}\r\n\r\n\tprivate void DiscoverBakedTiles()\r\n\t{\r\n\t\tCalculateRingLayout();\r\n\r\n\t\t// Rediscovered renderers need the surface attributes even if no property changed.\r\n\t\t_surfaceAttributesDirty = true;\r\n\t\tMarkRippleRenderersDirty();\r\n\r\n\r\n\t\tforeach ( var child in GameObject.Children.ToList() )\r\n\t\t{\r\n\t\t\tif ( !child.Name.StartsWith( \u0022tile_\u0022 ) )\r\n\t\t\t\tcontinue;\r\n\r\n\t\t\tvar renderer = child.Components.Get\u003CModelRenderer\u003E();\r\n\t\t\tif ( renderer == null )\r\n\t\t\t\tcontinue;\r\n\r\n\t\t\trenderer.SceneObject.Attributes.Set( \u0022BoundsMin\u0022, _boundsMin );\r\n\t\t\trenderer.SceneObject.Attributes.Set( \u0022BoundsMax\u0022, _boundsMax );\r\n\t\t\trenderer.Attributes.SetCombo( \u0022D_TRANSLUCENT\u0022, 1 );\r\n\t\t\trenderer.Attributes.SetCombo( \u0022D_Enable_Water_Line\u0022, 0 );\r\n\r\n\t\t\t// Parse tileX, tileY from name \u0022tile_X_Y\u0022\r\n\t\t\tvar parts = child.Name.Split( \u0027_\u0027 );\r\n\t\t\tif ( parts.Length == 3 \u0026\u0026 int.TryParse( parts[1], out int tileX ) \u0026\u0026 int.TryParse( parts[2], out int tileY ) )\r\n\t\t\t{\r\n\t\t\t\t_tiles[(tileX, tileY)] = (child, renderer);\r\n\t\t\t}\r\n\t\t}\r\n\t}\r\n\r\n\tprivate void BakeTiles()\r\n\t{\r\n\t\tif ( WaterTileModel == null )\r\n\t\t\treturn;\r\n\r\n\t\tfloat tileSizeInches = UnitConversion.CentimetersToInches( WaterTileSize );\r\n\t\tfloat halfTile = tileSizeInches * 0.5f;\r\n\r\n\t\tfor ( int tileX = _tileMinX; tileX \u003C _tileMaxX; tileX\u002B\u002B )\r\n\t\t{\r\n\t\t\tfor ( int tileY = _tileMinY; tileY \u003C _tileMaxY; tileY\u002B\u002B )\r\n\t\t\t{\r\n\t\t\t\t// Tile indices are local-grid; anchor positions to the GameObject origin.\r\n\t\t\t\tfloat worldX = WorldPosition.x \u002B tileX * tileSizeInches;\r\n\t\t\t\tfloat worldY = WorldPosition.y \u002B tileY * tileSizeInches;\r\n\r\n\t\t\t\tVector3 worldPosition = new Vector3( worldX \u002B halfTile, worldY \u002B halfTile, _waterZ );\r\n\t\t\t\tSpawnTile( worldPosition, tileX, tileY );\r\n\t\t\t}\r\n\t\t}\r\n\t}\r\n\r\n\tprivate void UpdateTileBodyGroups( Vector2 snappedPos )\r\n\t{\r\n\t\tfloat tileSizeInches = UnitConversion.CentimetersToInches( WaterTileSize );\r\n\r\n\t\t// First pass: calculate ring levels and find outermost ring\r\n\t\tvar tileData = new Dictionary\u003C(int x, int y), (int ringLevel, int gridX, int gridY)\u003E();\r\n\t\tint outermostRenderedRing = 0;\r\n\r\n\t\tforeach ( var kvp in _tiles )\r\n\t\t{\r\n\t\t\tint tileX = kvp.Key.x;\r\n\t\t\tint tileY = kvp.Key.y;\r\n\r\n\t\t\t// Box-local tile position (indices are local-grid; snappedPos is box-local).\r\n\t\t\tfloat localX = tileX * tileSizeInches;\r\n\t\t\tfloat localY = tileY * tileSizeInches;\r\n\r\n\t\t\tint gridX = (int)MathF.Round( ( localX - snappedPos.x ) / tileSizeInches );\r\n\t\t\tint gridY = (int)MathF.Round( ( localY - snappedPos.y ) / tileSizeInches );\r\n\r\n\t\t\tint ringLevel = Math.Max( GetRingForCoord( gridX ), GetRingForCoord( gridY ) );\r\n\t\t\tif ( ringLevel \u003E MaxRings )\r\n\t\t\t\tringLevel = MaxRings;\r\n\r\n\t\t\tif ( ringLevel \u003E outermostRenderedRing )\r\n\t\t\t\toutermostRenderedRing = ringLevel;\r\n\r\n\t\t\ttileData[(tileX, tileY)] = (ringLevel, gridX, gridY);\r\n\t\t}\r\n\r\n\t\t// Second pass: update body groups\r\n\t\tforeach ( var kvp in _tiles )\r\n\t\t{\r\n\t\t\tvar renderer = kvp.Value.renderer;\r\n\t\t\tif ( !renderer.IsValid() )\r\n\t\t\t\tcontinue;\r\n\r\n\t\t\tvar (ringLevel, gridX, gridY) = tileData[kvp.Key];\r\n\r\n\t\t\tTileType tileType = TileType.End;\r\n\t\t\tfloat rotation = 0f;\r\n\r\n\t\t\tif ( ringLevel == 0 )\r\n\t\t\t{\r\n\t\t\t\tif ( gridX == 0 \u0026\u0026 gridY == 0 ) rotation = 0f;\r\n\t\t\t\telse if ( gridX == 1 \u0026\u0026 gridY == 0 ) rotation = 90f;\r\n\t\t\t\telse if ( gridX == 0 \u0026\u0026 gridY == 1 ) rotation = 270f;\r\n\t\t\t\telse if ( gridX == 1 \u0026\u0026 gridY == 1 ) rotation = 180f;\r\n\t\t\t}\r\n\t\t\telse if ( ringLevel \u003C MaxRings )\r\n\t\t\t{\r\n\t\t\t\t// Rings are 1-tile-thick outlines - every tile is an End or a Corner.\r\n\t\t\t\tif ( IsCornerTile( gridX, gridY, ringLevel ) )\r\n\t\t\t\t{\r\n\t\t\t\t\ttileType = TileType.Corner;\r\n\t\t\t\t\trotation = GetCornerRotation( gridX, gridY, ringLevel );\r\n\t\t\t\t}\r\n\t\t\t\telse\r\n\t\t\t\t{\r\n\t\t\t\t\trotation = GetEdgeRotation( gridX, gridY, ringLevel );\r\n\t\t\t\t}\r\n\t\t\t}\r\n\r\n\t\t\trenderer.SetBodyGroup( \u0022subdivision\u0022, GetBodyGroupForRing( ringLevel, tileType ) );\r\n\t\t\tkvp.Value.go.WorldRotation = Rotation.FromYaw( -90f \u002B rotation );\r\n\t\t}\r\n\t}\r\n\r\n\tprivate int GetRingForCoord( int gridCoord )\r\n\t{\r\n\t\t// Center (ring 0): coords 0, 1 (the 2x2 quadrant block)\r\n\t\t// Ring N (1 tile thick outline): coord -N on the negative side, N\u002B1 on the positive side\r\n\t\t//   Ring 1: -1, 2\r\n\t\t//   Ring 2: -2, 3\r\n\t\t//   Ring 3: -3, 4\r\n\t\tif ( gridCoord \u003E= 0 \u0026\u0026 gridCoord \u003C= 1 )\r\n\t\t\treturn 0;\r\n\r\n\t\tint ring = gridCoord \u003C 0 ? -gridCoord : gridCoord - 1;\r\n\r\n\t\tif ( ring \u003E MaxRings )\r\n\t\t\treturn MaxRings \u002B 1;\r\n\r\n\t\treturn ring;\r\n\t}\r\n\r\n\tprivate bool IsCornerTile( int gridX, int gridY, int ringLevel )\r\n\t{\r\n\t\tint outerMin = -ringLevel;\r\n\t\tint outerMax = ringLevel \u002B 1;\r\n\r\n\t\tbool atCornerX = ( gridX == outerMin || gridX == outerMax );\r\n\t\tbool atCornerY = ( gridY == outerMin || gridY == outerMax );\r\n\r\n\t\treturn atCornerX \u0026\u0026 atCornerY;\r\n\t}\r\n\r\n\tprivate float GetCornerRotation( int gridX, int gridY, int ringLevel )\r\n\t{\r\n\t\tint outerMin = -ringLevel;\r\n\t\tint outerMax = ringLevel \u002B 1;\r\n\r\n\t\tif ( gridX == outerMin \u0026\u0026 gridY == outerMax ) return 270f;\r\n\t\tif ( gridX == outerMax \u0026\u0026 gridY == outerMax ) return 180f;\r\n\t\tif ( gridX == outerMax \u0026\u0026 gridY == outerMin ) return 90f;\r\n\t\tif ( gridX == outerMin \u0026\u0026 gridY == outerMin ) return 0f;\r\n\r\n\t\treturn 0f;\r\n\t}\r\n\r\n\tprivate float GetEdgeRotation( int gridX, int gridY, int ringLevel )\r\n\t{\r\n\t\tint outerMin = -ringLevel;\r\n\t\tint outerMax = ringLevel \u002B 1;\r\n\r\n\t\tif ( gridY == outerMax ) return 180f;\r\n\t\tif ( gridX == outerMax ) return 90f;\r\n\t\tif ( gridY == outerMin ) return 0f;\r\n\t\tif ( gridX == outerMin ) return 270f;\r\n\r\n\t\treturn 0f;\r\n\t}\r\n\r\n\tprivate void SpawnTile( Vector3 worldPosition, int tileX, int tileY )\r\n\t{\r\n\t\tvar tileGo = new GameObject( true, $\u0022tile_{tileX}_{tileY}\u0022 );\r\n\t\ttileGo.Parent = GameObject;\r\n\t\ttileGo.Tags.Add( \u0022reflection\u0022 ); // excluded from the reflection camera so water doesn\u0027t reflect itself\r\n\t\ttileGo.WorldPosition = worldPosition;\r\n\t\ttileGo.LocalScale = Vector3.One * ( WaterTileSize / 100f );\r\n\r\n\t\tvar renderer = tileGo.Components.Create\u003CModelRenderer\u003E();\r\n\t\trenderer.Model = WaterTileModel;\r\n\r\n\t\trenderer.SceneObject.Attributes.Set( \u0022BoundsMin\u0022, _boundsMin );\r\n\t\trenderer.SceneObject.Attributes.Set( \u0022BoundsMax\u0022, _boundsMax );\r\n\t\trenderer.Attributes.SetCombo( \u0022D_TRANSLUCENT\u0022, 1 );\r\n\t\trenderer.Attributes.SetCombo( \u0022D_Enable_Water_Line\u0022, 0 );\r\n\r\n\t\trenderer.SetBodyGroup( \u0022subdivision\u0022, FarBodyGroupIndex );\r\n\r\n\t\t_tiles[(tileX, tileY)] = (tileGo, renderer);\r\n\r\n\t\t// New renderer needs the surface attributes, ripple attributes and the\r\n\t\t// reflection-mode combo even if nothing changed.\r\n\t\t_surfaceAttributesDirty = true;\r\n\t\tMarkRippleRenderersDirty();\r\n\t\t_lastWaterReflectionPlanarRendering = null;\r\n\t}\r\n}\r\n"},{"Ident":"ali3nsystems.lib-atmokinetic_assets","Path":"Water/Modes/WorkInProgress/Area/WaterController.WaterArea.cs","FileName":"WaterController.WaterArea.cs","PackageType":"library","CodeKind":"Game","AssetVersionId":338612,"IsPrivate":false,"Code":"namespace AtmokineticAssets;\r\n\r\n// Soon TM\r\n// This will be used for very large body of waters like oceans, lakes, etc...\r\npublic sealed partial class WaterController: Component\r\n{\r\n\t// [Property, Title(\u0022Area Model\u0022), Feature(\u0022General\u0022), Group(\u0022Properties\u0022), ShowIf( nameof(WaterMode), WaterModeEnumeration.Area )]\r\n\t// public Model WaterAreaModel { get; set; }\r\n\r\n\t// [Property, Title(\u0022Bounds Minimum (cm)\u0022), Feature(\u0022General\u0022), Group(\u0022Properties\u0022), ShowIf( nameof(WaterMode), WaterModeEnumeration.Area )]\r\n\t// public Vector3 WaterAreaBoundsMinimum = new( -100, -100, -100 );\r\n\r\n\t// [Property, Title(\u0022Bounds Maximum (cm)\u0022), Feature(\u0022General\u0022), Group(\u0022Properties\u0022), ShowIf( nameof(WaterMode), WaterModeEnumeration.Area )]\r\n\t// public Vector3 WaterAreaBoundsMaximum = new( 100, 100, 0 );\r\n\r\n\t// [Button(\u0022Generate Water Area\u0022), Feature(\u0022General\u0022), Group(\u0022Properties\u0022), ShowIf( nameof(WaterMode), WaterModeEnumeration.Area )]\r\n\t// public void GenerateWaterArea()\r\n\t// {\r\n\r\n\t// }\r\n}\r\n"},{"Ident":"ali3nsystems.lib-atmokinetic_assets","Path":"Water/Reflection/Wip/WaterController.Reflection.Cubemap.cs","FileName":"WaterController.Reflection.Cubemap.cs","PackageType":"library","CodeKind":"Game","AssetVersionId":338612,"IsPrivate":false,"Code":"// namespace AtmokineticAssets;\r\n\r\n// public sealed partial class WaterController : Component, Component.ExecuteInEditor\r\n// {\r\n// \tpublic bool ShowWaterReflectionCubemapSettings =\u003E\r\n// \tWaterReflectionMode == WaterReflectionModeEnumeration.Dynamic\r\n// \t|| ( WaterReflectionMode == WaterReflectionModeEnumeration.Static\r\n// \t\u0026\u0026 WaterReflectionModeStaticMode == WaterReflectionModeStaticModeEnumeration.Cubemap );\r\n// \tpublic bool ShowWaterReflectionCubemapResolutionDynamic =\u003E ShowWaterReflectionCubemapSettings \u0026\u0026 WaterReflectionCubemapResolutionMode == WaterReflectionCubemapResolutionModeEnumeration.Dynamic;\r\n// \tpublic bool ShowWaterReflectionCubemapResolutionStatic =\u003E ShowWaterReflectionCubemapSettings \u0026\u0026 WaterReflectionCubemapResolutionMode == WaterReflectionCubemapResolutionModeEnumeration.Static;\r\n// \tpublic bool ShowWaterReflectionCubemapFramesPerSecondDynamic =\u003E ShowWaterReflectionCubemapSettings \u0026\u0026 WaterReflectionCubemapFramesPerSecondMode == WaterReflectionCubemapFramesPerSecondModeEnumeration.Dynamic;\r\n// \tpublic bool ShowWaterReflectionCubemapFramesPerSecondStatic =\u003E ShowWaterReflectionCubemapSettings \u0026\u0026 WaterReflectionCubemapFramesPerSecondMode == WaterReflectionCubemapFramesPerSecondModeEnumeration.Static;\r\n\r\n// \tpublic enum WaterReflectionCubemapResolutionModeEnumeration\r\n// \t{\r\n// \t\tDynamic,\r\n// \t\tStatic\r\n// \t}\r\n\r\n// \t[Property, Feature(\u0022Reflection\u0022), Group(\u0022Cubemap\u0022), Header(\u0022Resolution\u0022), ShowIf( nameof(ShowWaterReflectionCubemapSettings), true )]\r\n// \tpublic WaterReflectionCubemapResolutionModeEnumeration WaterReflectionCubemapResolutionMode {get;set;} = WaterReflectionCubemapResolutionModeEnumeration.Dynamic;\r\n\r\n// \t[Property, Feature(\u0022Reflection\u0022), Group(\u0022Cubemap\u0022), ShowIf( nameof(ShowWaterReflectionCubemapResolutionDynamic), true )]\r\n// \tpublic int WaterReflectionCubemapResolutionDynamicUltra {get;set;} = 1;\r\n\r\n// \t[Property, Feature(\u0022Reflection\u0022), Group(\u0022Cubemap\u0022), ShowIf( nameof(ShowWaterReflectionCubemapResolutionDynamic), true )]\r\n// \tpublic int WaterReflectionCubemapResolutionDynamicHigh {get;set;} = 2;\r\n\r\n// \t[Property, Feature(\u0022Reflection\u0022), Group(\u0022Cubemap\u0022), ShowIf( nameof(ShowWaterReflectionCubemapResolutionDynamic), true )]\r\n// \tpublic int WaterReflectionCubemapResolutionDynamicMedium {get;set;} = 4;\r\n\r\n// \t[Property, Feature(\u0022Reflection\u0022), Group(\u0022Cubemap\u0022), ShowIf( nameof(ShowWaterReflectionCubemapResolutionDynamic), true )]\r\n// \tpublic int WaterReflectionCubemapResolutionDynamicLow {get;set;} = 8;\r\n\r\n// \t[Property, Feature(\u0022Reflection\u0022), Group(\u0022Cubemap\u0022), ShowIf( nameof(ShowWaterReflectionCubemapResolutionStatic), true )]\r\n// \tpublic int WaterReflectionCubemapResolutionStatic {get;set;} = 2;\r\n\r\n// \t/// \u003Csummary\u003E\r\n// \t///\r\n// \t/// \u003C/summary\u003E\r\n// \tpublic enum WaterReflectionCubemapFramesPerSecondModeEnumeration\r\n// \t{\r\n// \t\tDynamic,\r\n// \t\tStatic\r\n// \t}\r\n\r\n// \t[Property, Feature(\u0022Reflection\u0022), Group(\u0022Cubemap\u0022), Header(\u0022Frames Per Second\u0022), ShowIf( nameof(ShowWaterReflectionCubemapSettings), true )]\r\n// \tpublic WaterReflectionCubemapFramesPerSecondModeEnumeration WaterReflectionCubemapFramesPerSecondMode {get;set;} = WaterReflectionCubemapFramesPerSecondModeEnumeration.Dynamic;\r\n\r\n// \t[Property, Feature(\u0022Reflection\u0022), Group(\u0022Cubemap\u0022), ShowIf( nameof(ShowWaterReflectionCubemapFramesPerSecondDynamic), true )]\r\n// \tpublic float WaterReflectionCubemapFramesPerSecondDynamicUltra {get;set;} = 120f;\r\n\r\n// \t[Property, Feature(\u0022Reflection\u0022), Group(\u0022Cubemap\u0022), ShowIf( nameof(ShowWaterReflectionCubemapFramesPerSecondDynamic), true )]\r\n// \tpublic float WaterReflectionCubemapFramesPerSecondDynamicHigh {get;set;} = 60f;\r\n\r\n// \t[Property, Feature(\u0022Reflection\u0022), Group(\u0022Cubemap\u0022), ShowIf( nameof(ShowWaterReflectionCubemapFramesPerSecondDynamic), true )]\r\n// \tpublic float WaterReflectionCubemapFramesPerSecondDynamicMedium {get;set;} = 30f;\r\n\r\n// \t[Property, Feature(\u0022Reflection\u0022), Group(\u0022Cubemap\u0022), ShowIf( nameof(ShowWaterReflectionCubemapFramesPerSecondDynamic), true )]\r\n// \tpublic float WaterReflectionCubemapFramesPerSecondDynamicLow {get;set;} = 15f;\r\n\r\n// \t[Property, Feature(\u0022Reflection\u0022), Group(\u0022Cubemap\u0022), ShowIf( nameof(ShowWaterReflectionCubemapFramesPerSecondStatic), true )]\r\n// \tpublic float WaterReflectionCubemapFramesPerSecondStatic {get;set;} = 60f;\r\n// }\r\n"},{"Ident":"ali3nsystems.lib-atmokinetic_assets","Path":"Code/Water/Modes/Tiles/WaterController.WaterTiles.Gizmo.cs","FileName":"WaterController.WaterTiles.Gizmo.cs","PackageType":"library","CodeKind":"Game","AssetVersionId":338612,"IsPrivate":false,"Code":"namespace AtmokineticAssets;\r\n\r\npublic sealed partial class WaterController : Component\r\n{\r\n\tprivate void DrawWaterTileGizmo()\r\n\t{\r\n\t\tif ( !EnableWaterTileGizmo )\r\n\t\t\treturn;\r\n\r\n\t\tif ( !Gizmo.IsSelected \u0026\u0026 !Gizmo.IsHovered )\r\n\t\t\treturn;\r\n\r\n\t\t// Raw box from the user\u0027s typed bounds \u2014 used for the editing handles.\r\n\t\tvar rawBox = new BBox(\r\n\t\t\tUnitConversion.CentimetersToInches( WaterTileBoundsMinimum ),\r\n\t\t\tUnitConversion.CentimetersToInches( WaterTileBoundsMaximum ) );\r\n\r\n\t\t// Snapped box \u2014 the actual world-grid region the tiles fill \u2014 used for drawing.\r\n\t\tvar snappedBox = GetSnappedLocalBox( rawBox );\r\n\r\n\t\tDrawWaterTileBounds( snappedBox );\r\n\t\tDrawWaterTileHandles( rawBox );\r\n\t}\r\n\r\n\t// Snaps the bounds extent to the tile grid relative to the GameObject\u0027s own origin,\r\n\t// so the box keeps its tile-sized footprint but follows the GameObject smoothly (no world-grid jumping).\r\n\tprivate BBox GetSnappedLocalBox( BBox localBox )\r\n\t{\r\n\t\tfloat tileSizeInches = UnitConversion.CentimetersToInches( WaterTileSize );\r\n\t\tif ( tileSizeInches \u003C= 0 )\r\n\t\t\treturn localBox;\r\n\r\n\t\tfloat minX = MathF.Floor( MathF.Round( localBox.Mins.x / tileSizeInches, 4 ) ) * tileSizeInches;\r\n\t\tfloat minY = MathF.Floor( MathF.Round( localBox.Mins.y / tileSizeInches, 4 ) ) * tileSizeInches;\r\n\t\tfloat maxX = MathF.Ceiling( MathF.Round( localBox.Maxs.x / tileSizeInches, 4 ) ) * tileSizeInches;\r\n\t\tfloat maxY = MathF.Ceiling( MathF.Round( localBox.Maxs.y / tileSizeInches, 4 ) ) * tileSizeInches;\r\n\r\n\t\treturn new BBox(\r\n\t\t\tnew Vector3( minX, minY, localBox.Mins.z ),\r\n\t\t\tnew Vector3( maxX, maxY, localBox.Maxs.z ) );\r\n\t}\r\n\r\n\tprivate void DrawWaterTileBounds( BBox box )\r\n\t{\r\n\t\tGizmo.Draw.LineThickness = 1;\r\n\t\tvar tint = Gizmo.IsSelected ? Gizmo.Colors.Blue : Gizmo.Colors.Blue.WithAlpha( 0.4f );\r\n\r\n\t\tfor ( int i = 0; i \u003C 2; i\u002B\u002B )\r\n\t\t{\r\n\t\t\tbool xray = i == 0;\r\n\t\t\tGizmo.Draw.IgnoreDepth = xray;\r\n\t\t\tfloat alpha = xray ? 0.2f : 1f;\r\n\r\n\t\t\tGizmo.Draw.Color = tint.WithAlpha( tint.a * alpha );\r\n\t\t\tGizmo.Draw.LineBBox( box );\r\n\r\n\t\t\tGizmo.Draw.Color = tint.WithAlpha( 0.05f * alpha );\r\n\t\t\tGizmo.Draw.SolidBox( box );\r\n\t\t}\r\n\t}\r\n\t\r\n\tprivate void DrawWaterTileHandles( BBox box )\r\n\t{\r\n\t\tGizmo.Hitbox.BBox( box );\r\n\r\n\t\t// Disable the editor\u0027s grid snap for this control so it doesn\u0027t double up with our tile snap.\r\n\t\tvar previousSnap = Gizmo.Settings.SnapToGrid;\r\n\t\tGizmo.Settings.SnapToGrid = false;\r\n\r\n\t\tif ( Gizmo.Control.BoundingBox( \u0022water_tile_bounds\u0022, box, out var newBox ) )\r\n\t\t{\r\n\t\t\tvar minCm = UnitConversion.InchesToCentimeters( newBox.Mins );\r\n\t\t\tvar maxCm = UnitConversion.InchesToCentimeters( newBox.Maxs );\r\n\r\n\t\t\tWaterTileBoundsMinimum = SnapChangedAxes( WaterTileBoundsMinimum, minCm, WaterTileSize, floor: true );\r\n\t\t\tWaterTileBoundsMaximum = SnapChangedAxes( WaterTileBoundsMaximum, maxCm, WaterTileSize, floor: false );\r\n\t\t}\r\n\r\n\t\tGizmo.Settings.SnapToGrid = previousSnap;\r\n\t}\r\n\r\n\tprivate float ResizeWaterTileBounds( float oldSize, float newSize )\r\n\t{\r\n\t\t// Skip during deserialization / before start \u2014 only rescale on genuine edits.\r\n\t\tif ( !_tilesStarted || oldSize == newSize || newSize \u003C= 0 || oldSize \u003C= 0 )\r\n\t\t\treturn newSize;\r\n\r\n\t\tfloat scale = newSize / oldSize;\r\n\r\n\t\t// Scale X/Y of the bounds so the box stays the same tile count.\r\n\t\tWaterTileBoundsMinimum = new Vector3( WaterTileBoundsMinimum.x * scale, WaterTileBoundsMinimum.y * scale, WaterTileBoundsMinimum.z );\r\n\t\tWaterTileBoundsMaximum = new Vector3( WaterTileBoundsMaximum.x * scale, WaterTileBoundsMaximum.y * scale, WaterTileBoundsMaximum.z );\r\n\r\n\t\treturn newSize;\r\n\t}\r\n\r\n\tprivate static Vector3 SnapChangedAxes( Vector3 oldValue, Vector3 newValue, float tileSize, bool floor )\r\n\t{\r\n\t\tif ( tileSize \u003C= 0 )\r\n\t\t\treturn newValue;\r\n\r\n\t\tfloat SnapAxis( float n ) =\u003E\r\n\t\t\t( floor ? MathF.Floor( n / tileSize ) : MathF.Ceiling( n / tileSize ) ) * tileSize;\r\n\r\n\t\treturn new Vector3(\r\n\t\t\toldValue.x.AlmostEqual( newValue.x ) ? oldValue.x : SnapAxis( newValue.x ),\r\n\t\t\toldValue.y.AlmostEqual( newValue.y ) ? oldValue.y : SnapAxis( newValue.y ),\r\n\t\t\toldValue.z.AlmostEqual( newValue.z ) ? oldValue.z : newValue.z );\r\n\t}\r\n}\r\n"},{"Ident":"ali3nsystems.lib-atmokinetic_assets","Path":"Code/Water/WaterController.Buoyancy.cs","FileName":"WaterController.Buoyancy.cs","PackageType":"library","CodeKind":"Game","AssetVersionId":338612,"IsPrivate":false,"Code":"namespace AtmokineticAssets;\r\n\r\npublic sealed partial class WaterController : Component, Component.ExecuteInEditor\r\n{\r\n    \r\n   \t/// \u003Csummary\u003E\r\n\t/// How heavy the fluid is. Higher values make things float more.\r\n\t/// Water is 1000, oil is around 900, something very heavy like mercury is 13600.\r\n\t/// \u003C/summary\u003E\r\n\t[Property, Feature(\u0022Buoyancy\u0022), Group( \u0022Fluid\u0022 ), Title(\u0022Fluid Density\u0022), Range( 0, 15000 )]\r\n\t[Sync]\r\n\tpublic float WaterBuoyancyFluidDensity { get; set; } = 1000.0f;\r\n\r\n\t/// \u003Csummary\u003E\r\n\t/// Direction and speed of the water current.\r\n\t/// \u003C/summary\u003E\r\n\t[Property, Feature(\u0022Buoyancy\u0022), Group( \u0022Fluid\u0022 ), Title(\u0022Fluid Velocity\u0022)]\r\n\t[Sync]\r\n\tpublic Vector3 WaterBuoyancyFluidVelocity { get; set; } = Vector3.Zero;\r\n\r\n\t/// \u003Csummary\u003E\r\n\t/// How much the fluid slows down movement.\r\n\t/// \u003C/summary\u003E\r\n\t[Property, Feature(\u0022Buoyancy\u0022), Group( \u0022Fluid\u0022 ), Title(\u0022Linear Drag\u0022),Range( 0, 1 )]\r\n\t[Sync]\r\n\tpublic float WaterBuoyancyLinearDrag { get; set; } = 0.1f;\r\n\r\n\t/// \u003Csummary\u003E\r\n\t/// How much the fluid slows down spinning.\r\n\t/// \u003C/summary\u003E\r\n\t[Property, Feature(\u0022Buoyancy\u0022), Group( \u0022Fluid\u0022 ), Title(\u0022Angular Drag\u0022), Range( 0, 2 )]\r\n\t[Sync]\r\n\tpublic float WaterBuoyancyAngularDrag { get; set; } = 0.5f;\r\n\r\n\t/// \u003Csummary\u003E\r\n\t/// Moves the water surface up or down.\r\n\t/// \u003C/summary\u003E\r\n\t[Property, Feature(\u0022Buoyancy\u0022), Group( \u0022Surface\u0022 ), Title(\u0022Surface Offset\u0022)]\r\n\t[Sync]\r\n\tpublic float WaterBuoyancySurfaceOffset { get; set; } = 0.0f;\r\n\r\n\t/// \u003Csummary\u003E\r\n\t/// How tall the waves are. Set to 0 for calm water.\r\n\t/// \u003C/summary\u003E\r\n\t[Property, Feature(\u0022Buoyancy\u0022), Group( \u0022Surface\u0022 ), Title(\u0022Wave Amplitude\u0022), Range( 0, 100 )]\r\n\t[Sync]\r\n\tpublic float WaterBuoyancyWaveAmplitude { get; set; } = 0.1f;\r\n\r\n\t/// \u003Csummary\u003E\r\n\t/// How fast the waves move.\r\n\t/// \u003C/summary\u003E\r\n\t[Property, Feature(\u0022Buoyancy\u0022), Group( \u0022Surface\u0022 ), Title(\u0022Wave Frequency\u0022), Range( 0, 5 )]\r\n\t[Sync]\r\n\tpublic float WaterBuoyancyWaveFrequency { get; set; } = 0.5f;\r\n\r\n\t/// \u003Csummary\u003E\r\n\t/// Objects with any of these tags won\u0027t be affected.\r\n\t/// \u003C/summary\u003E\r\n\t[Property, Feature(\u0022Buoyancy\u0022), Group( \u0022Filtering\u0022 ), Title(\u0022Ignore Tags\u0022)]\r\n\t[Sync]\r\n\tpublic TagSet WaterBuoyancyIgnoreTags { get; set; } = [];\r\n\r\n\tprivate WaterVolume _waterBuoyancy;\r\n\r\n\tpublic void CreateWaterBuoyancy()\r\n\t{\r\n\t\t_waterBuoyancy ??= Components.Get\u003CSandbox.WaterVolume\u003E( FindMode.InSelf ) ?? Components.Create\u003CSandbox.WaterVolume\u003E();\r\n\t\t_waterBuoyancy.Flags |= ComponentFlags.Hidden;\r\n\r\n\t\t_waterBuoyancy.FluidDensity = WaterBuoyancyFluidDensity;\r\n\t\t_waterBuoyancy.FluidVelocity = WaterBuoyancyFluidVelocity;\r\n\t\t_waterBuoyancy.LinearDrag = WaterBuoyancyLinearDrag;\r\n\t\t_waterBuoyancy.AngularDrag = WaterBuoyancyAngularDrag;\r\n\t\t_waterBuoyancy.SurfaceOffset = _waterZ - WorldPosition.z \u002B WaterBuoyancySurfaceOffset;\r\n\t\t_waterBuoyancy.WaveAmplitude = WaterBuoyancyWaveAmplitude;\r\n\t\t_waterBuoyancy.WaveFrequency = WaterBuoyancyWaveFrequency;\r\n\t\t_waterBuoyancy.IgnoreTags = WaterBuoyancyIgnoreTags;\r\n\t}\r\n}"},{"Ident":"ali3nsystems.lib-atmokinetic_assets","Path":"Graphics/GraphicsSettings.TerrainSettings.cs","FileName":"GraphicsSettings.TerrainSettings.cs","PackageType":"library","CodeKind":"Game","AssetVersionId":338612,"IsPrivate":false,"Code":"namespace AtmokineticAssets;\r\n\r\npublic sealed partial class GraphicsSettings : Component, Component.ExecuteInEditor\r\n{\r\n    //Terrain Settings\r\n    public enum TerrainRenderingModeEnumeration\r\n    {\r\n        [Title(\u0022Parallax Occlusion Mapping\u0022)]\r\n        ParallaxOcclusionMapping,\r\n        \r\n        [Title(\u0022Bump Offset\u0022)]\r\n        BumpOffset,\r\n       \r\n        Standard\r\n    }\r\n\r\n    //[Property, Feature(\u0022Terrain\u0022), Title(\u0022Rendering\u0022)]\r\n    public TerrainRenderingModeEnumeration TerrainRenderingMode { get; set; } = TerrainRenderingModeEnumeration.Standard;\r\n\r\n    public enum TerrainRenderingParallaxOcclusionMappingQualityEnumeration\r\n    {\r\n        Ultra,\r\n        High,\r\n        Medium,\r\n        Low\r\n    }\r\n\r\n    //[Property, Feature(\u0022Terrain\u0022), Title(\u0022Quality\u0022)] \r\n    [ShowIf(nameof(TerrainRenderingMode), TerrainRenderingModeEnumeration.ParallaxOcclusionMapping)]\r\n    public TerrainRenderingParallaxOcclusionMappingQualityEnumeration TerrainRenderingParallaxOcclusionMapping { get; set; } = TerrainRenderingParallaxOcclusionMappingQualityEnumeration.Medium;\r\n\r\n    public enum TerrainRenderingBumpOffsetQualityEnumeration\r\n    {\r\n        Ultra,\r\n        High,\r\n        Medium,\r\n        Low\r\n    }\r\n   \r\n    //[Property, Feature(\u0022Terrain\u0022), Title(\u0022Quality\u0022)] \r\n    [ShowIf(nameof(TerrainRenderingMode), TerrainRenderingModeEnumeration.BumpOffset)]\r\n    public TerrainRenderingParallaxOcclusionMappingQualityEnumeration TerrainRenderingBumpOffset { get; set; } = TerrainRenderingParallaxOcclusionMappingQualityEnumeration.Medium;\r\n\r\n\r\n    // public void ChangeTerrainSettings()\r\n    // {\r\n    //             // Terrain rendering mode: 0 = POM, 1 = Bump Offset, 2 = Standard\r\n    //     int terrainRenderingMode = TerrainRenderingMode switch\r\n    //     {\r\n    //         TerrainRenderingModeEnumeration.ParallaxOcclusionMapping =\u003E 0,\r\n    //         TerrainRenderingModeEnumeration.BumpOffset =\u003E 1,\r\n    //         TerrainRenderingModeEnumeration.Standard =\u003E 2,\r\n    //         _ =\u003E 1\r\n    //     };\r\n\r\n    //     // Quality: Ultra = 1, High = 2, Medium = 4, Low = 8\r\n    //     float quality;\r\n    //     switch ( TerrainRenderingMode )\r\n    //     {\r\n    //         case TerrainRenderingModeEnumeration.ParallaxOcclusionMapping:\r\n    //             quality = TerrainRenderingParallaxOcclusionMapping switch\r\n    //             {\r\n    //                 TerrainRenderingParallaxOcclusionMappingQualityEnumeration.Ultra =\u003E 1f,\r\n    //                 TerrainRenderingParallaxOcclusionMappingQualityEnumeration.High =\u003E 2f,\r\n    //                 TerrainRenderingParallaxOcclusionMappingQualityEnumeration.Medium =\u003E 4f,\r\n    //                 TerrainRenderingParallaxOcclusionMappingQualityEnumeration.Low =\u003E 8f,\r\n    //                 _ =\u003E 4f\r\n    //             };\r\n    //             break;\r\n\r\n    //         case TerrainRenderingModeEnumeration.BumpOffset:\r\n    //             quality = TerrainRenderingBumpOffset switch\r\n    //             {\r\n    //                 TerrainRenderingParallaxOcclusionMappingQualityEnumeration.Ultra =\u003E 1f,\r\n    //                 TerrainRenderingParallaxOcclusionMappingQualityEnumeration.High =\u003E 2f,\r\n    //                 TerrainRenderingParallaxOcclusionMappingQualityEnumeration.Medium =\u003E 4f,\r\n    //                 TerrainRenderingParallaxOcclusionMappingQualityEnumeration.Low =\u003E 8f,\r\n    //                 _ =\u003E 4f\r\n    //             };\r\n    //             break;\r\n\r\n    //         default:\r\n    //             quality = 4f;\r\n    //             break;\r\n    //     }\r\n\r\n    //     // Enumerate live \u2014 objects spawned after startup (e.g. baked water tiles) must be included.\r\n    //     foreach ( var sceneObject in Scene.SceneWorld.SceneObjects )\r\n    //     {\r\n    //         if ( sceneObject is null || !sceneObject.IsValid() ) continue;\r\n    //         sceneObject.Attributes.Set( \u0022D_Rendering\u0022, terrainRenderingMode );\r\n    //         sceneObject.Attributes.Set( \u0022Quality\u0022, quality );\r\n    //     }\r\n    // }\r\n}"},{"Ident":"ali3nsystems.lib-atmokinetic_assets","Path":"Utility/UnitConversion.cs","FileName":"UnitConversion.cs","PackageType":"library","CodeKind":"Game","AssetVersionId":338612,"IsPrivate":false,"Code":"namespace AtmokineticAssets;\r\n\r\npublic static class UnitConversion\r\n{\r\n\t//Inches To Meters\r\n\tpublic static float InchesToMeters( float inches )\r\n\t{\r\n\t\treturn inches * 0.0254f;\r\n\t}\r\n\r\n\tpublic static Vector2 InchesToMeters( Vector2 inches )\r\n\t{\r\n\t\treturn inches * 0.0254f;\r\n\t}\r\n\r\n\tpublic static Vector3 InchesToMeters( Vector3 inches )\r\n\t{\r\n\t\treturn inches * 0.0254f;\r\n\t}\r\n\r\n\tpublic static Vector4 InchesToMeters( Vector4 inches )\r\n\t{\r\n\t\treturn inches * 0.0254f;\r\n\t}\r\n\r\n\r\n\r\n\t//Inches To Centimeters\r\n\tpublic static float InchesToCentimeters( float inches)\r\n\t{\r\n\t\treturn inches * 2.54f;\r\n\t}\r\n\r\n\tpublic static Vector2 InchesToCentimeters( Vector2 inches)\r\n\t{\r\n\t\treturn inches * 2.54f;\r\n\t}\r\n\r\n\tpublic static Vector3 InchesToCentimeters( Vector3 inches)\r\n\t{\r\n\t\treturn inches * 2.54f;\r\n\t}\r\n\r\n\tpublic static Vector4 InchesToCentimeters( Vector4 inches)\r\n\t{\r\n\t\treturn inches * 2.54f;\r\n\t}\r\n\r\n\t//Centimeters To Inches\r\n\tpublic static float CentimetersToInches( float centimeters )\r\n\t{\r\n\t\treturn centimeters / 2.54f;\r\n\t}\r\n\t\r\n\tpublic static Vector2 CentimetersToInches( Vector2 centimeters )\r\n\t{\r\n\t\treturn centimeters / 2.54f;\r\n\t}\r\n\t\r\n\tpublic static Vector3 CentimetersToInches( Vector3 centimeters )\r\n\t{\r\n\t\treturn centimeters / 2.54f;\r\n\t}\r\n\t\r\n\tpublic static Vector4 CentimetersToInches( Vector4 centimeters )\r\n\t{\r\n\t\treturn centimeters / 2.54f;\r\n\t}\r\n\r\n\t//Meters To Centimeters\r\n\tpublic static float MetersToCentimeters( float meters )\r\n\t{\r\n\t\treturn meters * 100f;\r\n\t}\r\n\r\n\t//Meters To Inches\r\n\tpublic static float MetersToInches(float meters)\r\n\t{\r\n\t\treturn meters / 0.0254f;\r\n\t}\r\n}\r\n"},{"Ident":"ali3nsystems.lib-atmokinetic_assets","Path":"Water/Modes/WorkInProgress/Area/WaterController.WaterArea.Gizmo.cs","FileName":"WaterController.WaterArea.Gizmo.cs","PackageType":"library","CodeKind":"Game","AssetVersionId":338612,"IsPrivate":false,"Code":"namespace AtmokineticAssets;\r\n\r\npublic sealed partial class WaterController: Component\r\n{\r\n\t// [Property, Title(\u0022Enable Water Gizmo\u0022),Feature( \u0022General\u0022 ), ShowIf( nameof(WaterMode), WaterModeEnumeration.Area)]\r\n\t// public bool EnableWaterAreaGizmo { get; set; } = true;\r\n}\r\n"},{"Ident":"ali3nsystems.lib-atmokinetic_assets","Path":"Water/WaterController.Surface.cs","FileName":"WaterController.Surface.cs","PackageType":"library","CodeKind":"Game","AssetVersionId":338612,"IsPrivate":false,"Code":"namespace AtmokineticAssets;\r\n\r\n// Also add:\r\n\r\n//Ice\r\n//Caustics\r\n\r\npublic sealed partial class WaterController : Component, Component.ExecuteInEditor\r\n{\r\n\t// Surface\r\n\t[Property, Feature(\u0022Attributes\u0022), Group(\u0022Surface\u0022), Title(\u0022Color\u0022), Order(1)]\r\n\t[Sync]\r\n\tpublic Color WaterSurfaceColor { get; set { field = value; _surfaceAttributesDirty = true; } } = Color.Parse( \u0022#c5d9ff\u0022 ).Value;\r\n\r\n\t[Property, Feature(\u0022Attributes\u0022), Group(\u0022Surface\u0022), Title(\u0022Blur\u0022), Order(2)]\r\n\t[Sync]\r\n\tpublic float WaterSurfaceBlur { get; set { field = value; _surfaceAttributesDirty = true; } } = 0;\r\n\r\n\t// Reflection\r\n\t[Property, Feature(\u0022Attributes\u0022), Group(\u0022Reflection\u0022), Title(\u0022Color\u0022), Order(3)]\r\n\t[Sync]\r\n\tpublic Color WaterReflectionColor { get; set { field = value; _surfaceAttributesDirty = true; } } = Color.Parse( \u0022#c5d9ff\u0022 ).Value;\r\n\r\n\t[Property, Feature(\u0022Attributes\u0022), Group(\u0022Reflection\u0022), Title(\u0022Blur\u0022), Order(4)]\r\n\t[Sync]\r\n\tpublic float WaterReflectionBlur { get; set { field = value; _surfaceAttributesDirty = true; } } = 0;\r\n\r\n\t[Property, Feature(\u0022Attributes\u0022), Group(\u0022Reflection\u0022), Title(\u0022Distance\u0022), Order(5)]\r\n\t[Sync]\r\n\tpublic float WaterReflectionDistance { get; set { field = value; _surfaceAttributesDirty = true; } } = 4f;\r\n\r\n\t[Property, Feature(\u0022Attributes\u0022), Group(\u0022Reflection\u0022), Title(\u0022Threshold\u0022), Order(6)]\r\n\t[Sync]\r\n\tpublic Vector2 WaterReflectionThreshold { get; set { field = value; _surfaceAttributesDirty = true; } } = new Vector2( 0, 0.25f );\r\n\r\n\t// Shoreline\r\n\t[Property, Feature(\u0022Attributes\u0022), Group(\u0022Shoreline\u0022), Title(\u0022Fade\u0022), Order(7)]\r\n\t[Sync]\r\n\tpublic float WaterShorelineFade { get; set { field = value; _surfaceAttributesDirty = true; } } = 2f;\r\n\r\n\t// Tension\r\n\t[Property, Feature(\u0022Attributes\u0022), Group(\u0022Tension\u0022), Title(\u0022Distance\u0022), Order(8)]\r\n\t[Sync]\r\n\tpublic float WaterTensionDistance { get; set { field = value; _surfaceAttributesDirty = true; } } = 2f;\r\n\r\n\t[Property, Feature(\u0022Attributes\u0022), Group(\u0022Tension\u0022), Title(\u0022Refraction\u0022), Order(9)]\r\n\t[Sync]\r\n\tpublic float WaterTensionRefraction { get; set { field = value; _surfaceAttributesDirty = true; } } = 0.1f;\r\n\r\n\t// Texture A\r\n\t[Property, Feature(\u0022Attributes\u0022), Group(\u0022Water Ripples A\u0022), Title(\u0022Texture A\u0022), Order(10)]\r\n\t[Sync]\r\n\tpublic Texture WaterRipplesTextureA { get; set { field = value; _surfaceAttributesDirty = true; } } = Texture.Load(\u0022textures/tex-water_ripples_1.vtex\u0022);\r\n\r\n\t[Property, Feature(\u0022Attributes\u0022), Group(\u0022Water Ripples A\u0022), Title(\u0022Scale A\u0022), Order(11)]\r\n\t[Sync]\r\n\tpublic float WaterRipplesScaleA { get; set { field = value; _surfaceAttributesDirty = true; } } = 0.01f;\r\n\r\n\t[Property, Feature(\u0022Attributes\u0022), Group(\u0022Water Ripples A\u0022), Title(\u0022Rotation A\u0022), Order(12)]\r\n\t[Sync]\r\n\tpublic float WaterRipplesRotationA { get; set { field = value; _surfaceAttributesDirty = true; } } = 0f;\r\n\r\n\t[Property, Feature(\u0022Attributes\u0022), Group(\u0022Water Ripples A\u0022), Title(\u0022Speed A\u0022), Order(13)]\r\n\t[Sync]\r\n\tpublic float WaterRipplesSpeedA { get; set { field = value; _surfaceAttributesDirty = true; } } = 0.1f;\r\n\r\n\t[Property, Feature(\u0022Attributes\u0022), Group(\u0022Water Ripples A\u0022), Title(\u0022Refraction A\u0022), Order(14)]\r\n\t[Sync]\r\n\tpublic float WaterRipplesRefractionA { get; set { field = value; _surfaceAttributesDirty = true; } } = 0.05f;\r\n\r\n\t[Property, Feature(\u0022Attributes\u0022), Group(\u0022Water Ripples A\u0022), Title(\u0022Displacement A\u0022), Order(15)]\r\n\t[Sync]\r\n\tpublic float WaterRipplesDisplacementA { get; set { field = value; _surfaceAttributesDirty = true; } } = 0.5f;\r\n\r\n\t// Texture B\r\n\t[Property, Feature(\u0022Attributes\u0022), Group(\u0022Water Ripples B\u0022), Title(\u0022Texture B\u0022), Order(16)]\r\n\t[Sync]\r\n\tpublic Texture WaterRipplesTextureB { get; set { field = value; _surfaceAttributesDirty = true; } } = Texture.Load(\u0022textures/tex-water_ripples_2.vtex\u0022);\r\n\r\n\t[Property, Feature(\u0022Attributes\u0022), Group(\u0022Water Ripples B\u0022), Title(\u0022Scale B\u0022), Order(17)]\r\n\t[Sync]\r\n\tpublic float WaterRipplesScaleB { get; set { field = value; _surfaceAttributesDirty = true; } } = 0.01f;\r\n\r\n\t[Property, Feature(\u0022Attributes\u0022), Group(\u0022Water Ripples B\u0022), Title(\u0022Rotation B\u0022), Order(18)]\r\n\t[Sync]\r\n\tpublic float WaterRipplesRotationB { get; set { field = value; _surfaceAttributesDirty = true; } } = 180f;\r\n\r\n\t[Property, Feature(\u0022Attributes\u0022), Group(\u0022Water Ripples B\u0022), Title(\u0022Speed B\u0022), Order(19)]\r\n\t[Sync]\r\n\tpublic float WaterRipplesSpeedB { get; set { field = value; _surfaceAttributesDirty = true; } } = 0.1f;\r\n\r\n\t[Property, Feature(\u0022Attributes\u0022), Group(\u0022Water Ripples B\u0022), Title(\u0022Refraction B\u0022), Order(20)]\r\n\t[Sync]\r\n\tpublic float WaterRipplesRefractionB { get; set { field = value; _surfaceAttributesDirty = true; } } = 0.05f;\r\n\r\n\t[Property, Feature(\u0022Attributes\u0022), Group(\u0022Water Ripples B\u0022), Title(\u0022Displacement B\u0022), Order(21)]\r\n\t[Sync]\r\n\tpublic float WaterRipplesDisplacementB { get; set { field = value; _surfaceAttributesDirty = true; } } = 0.5f;\r\n\r\n\t// Starts dirty so the first WaterSurfaceAttributes call always pushes.\r\n\tprivate bool _surfaceAttributesDirty = true;\r\n\r\n\t[Rpc.Broadcast]\r\n\tpublic void WaterSurfaceAttributes()\r\n\t{\r\n\t\tif ( !_surfaceAttributesDirty )\r\n\t\t\treturn;\r\n\r\n\t\t_surfaceAttributesDirty = false;\r\n\r\n\t\tforeach ( var renderer in GetTileRenderers() )\r\n\t\t{\r\n\r\n\t\t\tif (WaterSurfaceBlur != 0)\r\n\t\t\t{\t\r\n\t\t\t\trenderer.Attributes.SetCombo(\u0022D_Enable_Water_Surface_Blur\u0022, 1);\r\n\t\t\t}\r\n\t\t\telse\r\n\t\t\t{\r\n\t\t\t\trenderer.Attributes.SetCombo(\u0022D_Enable_Water_Surface_Blur\u0022, 0);\r\n\t\t\t}\r\n\r\n\t\t\tif (WaterReflectionBlur != 0)\r\n\t\t\t{\r\n\t\t\t\trenderer.Attributes.SetCombo(\u0022D_Enable_Water_Reflection_Blur\u0022, 1);\r\n\t\t\t}\r\n\t\t\telse\r\n\t\t\t{\r\n\t\t\t\trenderer.Attributes.SetCombo(\u0022D_Enable_Water_Reflection_Blur\u0022, 0);\r\n\t\t\t}\r\n\r\n\r\n\t\t\t// Surface\r\n\t\t\trenderer.Attributes.Set( \u0022Water Surface Color\u0022, WaterSurfaceColor );\r\n\t\t\trenderer.Attributes.Set( \u0022Water Surface Blur\u0022, WaterSurfaceBlur );\r\n\r\n\t\t\t// Reflection\r\n\t\t\trenderer.Attributes.Set( \u0022Water Reflection Color\u0022, WaterReflectionColor );\r\n\t\t\trenderer.Attributes.Set( \u0022Water Reflection Blur\u0022, WaterReflectionBlur );\r\n\t\t\trenderer.Attributes.Set( \u0022Water Reflection Distance\u0022, WaterReflectionDistance );\r\n\t\t\trenderer.Attributes.Set( \u0022Water Reflection Threshold\u0022, WaterReflectionThreshold );\r\n\r\n\t\t\t// Shoreline\r\n\t\t\trenderer.Attributes.Set( \u0022Water Shoreline Fade\u0022, WaterShorelineFade );\r\n\t\t\t\r\n\t\t\t// Tension\r\n\t\t\trenderer.Attributes.Set( \u0022Water Tension Distance\u0022, WaterTensionDistance );\r\n\t\t\trenderer.Attributes.Set( \u0022Water Tension Refraction\u0022, WaterTensionRefraction );\r\n\r\n\t\t\t// Water Ripples A\r\n\t\t\trenderer.Attributes.Set( \u0022WaterRipplesTextureA\u0022, WaterRipplesTextureA );\r\n\t\t\trenderer.Attributes.Set( \u0022Water Ripples Scale A\u0022, WaterRipplesScaleA );\r\n\t\t\trenderer.Attributes.Set( \u0022Water Ripples Rotation A\u0022, WaterRipplesRotationA );\r\n\t\t\trenderer.Attributes.Set( \u0022Water Ripples Speed A\u0022, WaterRipplesSpeedA );\r\n\t\t\trenderer.Attributes.Set( \u0022Water Ripples Refraction A\u0022, WaterRipplesRefractionA );\r\n\t\t\trenderer.Attributes.Set( \u0022Water Ripples Displacement A\u0022, WaterRipplesDisplacementA );\r\n\r\n\t\t\t// Water Ripples B\r\n\t\t\trenderer.Attributes.Set( \u0022WaterRipplesTextureB\u0022, WaterRipplesTextureB );\r\n\t\t\trenderer.Attributes.Set( \u0022Water Ripples Scale B\u0022, WaterRipplesScaleB );\r\n\t\t\trenderer.Attributes.Set( \u0022Water Ripples Rotation B\u0022, WaterRipplesRotationB );\r\n\t\t\trenderer.Attributes.Set( \u0022Water Ripples Speed B\u0022, WaterRipplesSpeedB );\r\n\t\t\trenderer.Attributes.Set( \u0022Water Ripples Refraction B\u0022, WaterRipplesRefractionB );\r\n\t\t\trenderer.Attributes.Set( \u0022Water Ripples Displacement B\u0022, WaterRipplesDisplacementB );\r\n\t\t}\r\n\t}\r\n}"},{"Ident":"ali3nsystems.lib-atmokinetic_assets","Path":"Environment/EnvironmentController.cs","FileName":"EnvironmentController.cs","PackageType":"library","CodeKind":"Game","AssetVersionId":338612,"IsPrivate":false,"Code":"namespace AtmokineticAssets;\r\n\r\n[Title(\u0022Atmokinetic Assets - Environment Controller\u0022)]\r\n[Category(\u0022Atmokinetic Assets\u0022)]\r\n[Icon(\u0022cloud\u0022)]\r\n\r\npublic sealed partial class EnvironmentController : Component\r\n{\r\n\tprotected override void OnAwake()\r\n\t{\r\n\t\tCacheWaterControllers();\r\n\t}\r\n\r\n\tprotected override void OnUpdate()\r\n\t{\r\n\t\t\r\n\t} \r\n\r\n\t// public enum PrecipitationModeEnumeration\r\n\t// {\r\n\t// \tNone,\r\n\t// \tRain,\r\n\t// \tSnow\r\n\t// }\r\n\r\n\t// [Property, Feature(\u0022Weather\u0022), Sync]\r\n\t// public PrecipitationModeEnumeration PrecipitationMode { get; set; } = PrecipitationModeEnumeration.None;\r\n\r\n\t// [Property, Feature(\u0022Weather\u0022)] PrefabScene PrecipitationController { get; set;}\r\n\r\n\t// [Property, Feature(\u0022Weather\u0022), Range( 0f, 1f ), Sync]\r\n\t// public float WaterPuddles { get; set; } = 0f;\r\n\r\n\t// [Property, Feature(\u0022Weather\u0022), Range( 0.0001f, 1f ), Sync]\r\n\t// public float WaterLevel { get; set; } = 0f;\r\n\r\n\t// [Property, Feature(\u0022Weather\u0022), Range( 0, 4 ), Step(1), Sync]\r\n\t// public float PrecipitationIntensity { get; set; } = 0f;\r\n\r\n\t// [Property,Feature(\u0022Time\u0022), Sync]\r\n\t// public GameObject Sun {get;set;}\r\n\r\n\t// [Property, Feature(\u0022Time\u0022), Range(0,24), Sync]\r\n\t// public float Hour {get;set;} = 6f;\r\n\t\r\n\t// [Property, Feature(\u0022Time\u0022), Range(1,31), Step(1)]\r\n\t// public float Day {get;set;} = 0f;\r\n\t\r\n\t// [Property, Feature(\u0022Time\u0022), Range(1,12), Step(1)]\r\n\t// public float Month {get;set;} = 0f;\r\n\t\r\n\t// [Property, Feature(\u0022Time\u0022), Step(1)]\r\n\t// public float Year {get;set;} = 0f;\r\n\r\n\t// private float _lastWaterPuddlesValue = -1f;\r\n\t// private float _lastPrecipitationIntensityValue = -1f;\r\n\t// private float _lastPrecipitationIntensityValueForParticles = -1f;\r\n\t// private float _lastWaterLevelValue = -1f;\r\n\t\r\n\t// private List\u003CSceneObject\u003E _cachedSceneObjects;\r\n\t// private List\u003CEnvmapProbe\u003E _cachedEnvmapProbes;\r\n\r\n\t// // Rain Precipitation\r\n\t// private GameObject _rainGameObject;\r\n\t// private ParticleEffect _particleEffectRain;\r\n\t// private PrecipitationController _precipitationControllerRain;\r\n\t// private PrecipitationControllerEmitter _precipitationControllerRainEmitter;\r\n\t// private bool _activePrecipitationControllerRain;\r\n\r\n\t// // Snow Precipitation\r\n\t// private GameObject _snowGameObject;\r\n\t// private ParticleEffect _particleEffectSnow;\r\n\t// private PrecipitationController _precipitationControllerSnow;\r\n\t// private PrecipitationControllerEmitter _precipitationControllerSnowEmitter;\r\n\t// private bool _activePrecipitationControllerSnow;\r\n\r\n\t// private PrecipitationModeEnumeration _lastPrecipitationMode = PrecipitationModeEnumeration.None;\r\n\r\n\t// // Lerp state\r\n\t// private float _startMaxParticles;\r\n\t// private float _startRate;\r\n\t// private float _targetMaxParticles;\r\n\t// private float _targetRate;\r\n\t// private float _transitionProgress = 1f;\r\n\t// private float _lastHour = -1f;\r\n\r\n\t// protected override void OnStart()\r\n\t// {\r\n\t// \t// Cache all scene objects on startup\r\n\t// \t_cachedSceneObjects = Scene.SceneWorld.SceneObjects.Where( x =\u003E x != null ).ToList();\r\n\r\n\t// \t// Cache all environment map probes\r\n\t// \t_cachedEnvmapProbes = Scene.GetAllComponents\u003CEnvmapProbe\u003E().ToList();\r\n\t// }\r\n\t\r\n\t// [Rpc.Broadcast]\r\n\t// protected override void OnUpdate()\r\n\t// {\r\n\t// \tHandlePrecipitationModeChange();\r\n\t// \tPrecipitationEffects();\r\n\t// \tTimeEffects();\r\n\t// }\r\n\r\n\t// protected override void DrawGizmos()\r\n\t// {\r\n\t// \tif (_cachedSceneObjects == null)\r\n\t// \t{\r\n\t// \t\t_cachedSceneObjects = Scene.SceneWorld.SceneObjects.Where( x =\u003E x != null ).ToList();\r\n\t// \t}\r\n\r\n\t// \tif (_cachedEnvmapProbes == null)\r\n\t// \t{\r\n\t// \t\t_cachedEnvmapProbes = Scene.GetAllComponents\u003CEnvmapProbe\u003E().ToList();\r\n\t// \t}\r\n\r\n\t// \tif ( WaterPuddles != _lastWaterPuddlesValue || WaterLevel != _lastWaterLevelValue || PrecipitationIntensity != _lastPrecipitationIntensityValue)\r\n\t// \t{\r\n\t// \t\t_lastWaterPuddlesValue = WaterPuddles;\r\n\t// \t\t_lastWaterLevelValue = WaterLevel;\r\n\t// \t\t_lastPrecipitationIntensityValue = PrecipitationIntensity;\r\n\r\n\t// \t\tforeach ( var sceneModel in _cachedSceneObjects )\r\n\t// \t\t{\r\n\t// \t\t\tsceneModel.Attributes.Set( \u0022Water Puddles Accumulation\u0022, WaterPuddles );\r\n\t// \t\t\tsceneModel.Attributes.Set( \u0022Water Plane Level\u0022, WaterLevel );\r\n\t// \t\t\tsceneModel.Attributes.Set( \u0022Rain Ripples Intensity\u0022, PrecipitationIntensity );\r\n\t// \t\t}\r\n\t// \t}\r\n\r\n\t// \tTimeEffects();\r\n\t// }\r\n\r\n\t// private void HandlePrecipitationModeChange()\r\n\t// {\r\n\t// \tif (PrecipitationMode == _lastPrecipitationMode) return;\r\n\r\n\t// \t// Only host spawns networked objects\r\n\r\n\t// \t// Handle mode transitions\r\n\t// \tif (_lastPrecipitationMode == PrecipitationModeEnumeration.Rain)\r\n\t// \t{\r\n\t// \t\tTransitionOutRain();\r\n\t// \t}\r\n\t// \telse if (_lastPrecipitationMode == PrecipitationModeEnumeration.Snow)\r\n\t// \t{\r\n\t// \t\tTransitionOutSnow();\r\n\t// \t}\r\n\r\n\t// \t// Spawn new precipitation if not None\r\n\t// \tif (PrecipitationMode == PrecipitationModeEnumeration.Rain)\r\n\t// \t{\r\n\t// \t\tSpawnRain();\r\n\t// \t}\r\n\t// \telse if (PrecipitationMode == PrecipitationModeEnumeration.Snow)\r\n\t// \t{\r\n\t// \t\tSpawnSnow();\r\n\t// \t}\r\n\r\n\t// \t_lastPrecipitationMode = PrecipitationMode;\r\n\t// }\r\n\r\n\t// private void SpawnRain()\r\n\t// {\r\n\t// \t// Reset transition state to trigger fresh lerp\r\n\t// \t_lastPrecipitationIntensityValue = -1f;\r\n\t// \t_lastPrecipitationIntensityValueForParticles = -1f;\r\n\r\n\t// \t// If rain instance already exists (transitioning out), reactivate it\r\n\t// \tif (_precipitationControllerRain.IsValid())\r\n\t// \t{\r\n\t// \t\t_activePrecipitationControllerRain = true;\r\n\t// \t\treturn;\r\n\t// \t}\r\n\r\n\t// \tif (!PrecipitationController.IsValid()) return;\r\n\r\n\t// \tvar clonedGameObject = PrecipitationController.Clone();\r\n\t// \tclonedGameObject.Name = \u0022Precipitation Controller (Rain)\u0022;\r\n\r\n\t// \t_rainGameObject = clonedGameObject;\r\n\t// \t_particleEffectRain = clonedGameObject.GetComponent\u003CParticleEffect\u003E();\r\n\t// \t_precipitationControllerRain = clonedGameObject.GetComponent\u003CPrecipitationController\u003E();\r\n\t// \t_precipitationControllerRainEmitter = clonedGameObject.GetComponent\u003CPrecipitationControllerEmitter\u003E();\r\n\r\n\t// \t_precipitationControllerRain.PrecipitationMode = AtmokineticAssets.PrecipitationController.PrecipitationModeEnumeration.Rain;\r\n\r\n\t// \t_particleEffectRain.MaxParticles = 0;\r\n\t// \t_precipitationControllerRainEmitter.Rate = 0;\r\n\t// \t_activePrecipitationControllerRain = true;\r\n\r\n\t// \t// Reset transition to ensure smooth lerp from current state\r\n\t// }\r\n\r\n\t// private void SpawnSnow()\r\n\t// {\r\n\t// \t// Reset transition state to trigger fresh lerp\r\n\t// \t_lastPrecipitationIntensityValue = -1f;\r\n\t// \t_lastPrecipitationIntensityValueForParticles = -1f;\r\n\r\n\t// \t// If snow instance already exists (transitioning out), reactivate it\r\n\t// \tif (_precipitationControllerSnow.IsValid())\r\n\t// \t{\r\n\t// \t\t_activePrecipitationControllerSnow = true;\r\n\t// \t\treturn;\r\n\t// \t}\r\n\r\n\t// \tif (!PrecipitationController.IsValid()) return;\r\n\r\n\t// \tvar clonedGameObject = PrecipitationController.Clone();\r\n\t// \tclonedGameObject.Name = \u0022Precipitation Controller (Snow)\u0022;\r\n\r\n\t// \t_snowGameObject = clonedGameObject;\r\n\t// \t_particleEffectSnow = clonedGameObject.GetComponent\u003CParticleEffect\u003E();\r\n\t// \t_precipitationControllerSnow = clonedGameObject.GetComponent\u003CPrecipitationController\u003E();\r\n\t// \t_precipitationControllerSnowEmitter = clonedGameObject.GetComponent\u003CPrecipitationControllerEmitter\u003E();\r\n\r\n\t// \t_precipitationControllerSnow.PrecipitationMode = AtmokineticAssets.PrecipitationController.PrecipitationModeEnumeration.Snow;\r\n\r\n\t// \t_particleEffectSnow.MaxParticles = 0;\r\n\t// \t_precipitationControllerSnowEmitter.Rate = 0;\r\n\t// \t_activePrecipitationControllerSnow = true;\r\n\r\n\t// \t// Reset transition to ensure smooth lerp from current state\r\n\t// }\r\n\r\n\t// private void TransitionOutRain() =\u003E _activePrecipitationControllerRain = false;\r\n\r\n\t// private void TransitionOutSnow() =\u003E _activePrecipitationControllerSnow = false;\r\n\t\r\n\t// public void PrecipitationEffects()\r\n\t// {\r\n\t// \tif (_precipitationControllerRain.IsValid())\r\n\t// \t{\r\n\t// \t\tif (_activePrecipitationControllerRain)\r\n\t// \t\t{\r\n\t// \t\t\tUpdatePrecipitationTransition(\r\n\t// \t\t\t\t_particleEffectRain,\r\n\t// \t\t\t\t_precipitationControllerRainEmitter,\r\n\t// \t\t\t\t_precipitationControllerRain.RainIntensityMaximumParticlesLight,\r\n\t// \t\t\t\t_precipitationControllerRain.RainIntensityRateLight,\r\n\t// \t\t\t\t_precipitationControllerRain.RainIntensityMaximumParticlesModerate,\r\n\t// \t\t\t\t_precipitationControllerRain.RainIntensityRateModerate,\r\n\t// \t\t\t\t_precipitationControllerRain.RainIntensityMaximumParticlesHeavy,\r\n\t// \t\t\t\t_precipitationControllerRain.RainIntensityRateHeavy,\r\n\t// \t\t\t\t_precipitationControllerRain.RainIntensityMaximumParticlesExtreme,\r\n\t// \t\t\t\t_precipitationControllerRain.RainIntensityRateExtreme,\r\n\t// \t\t\t\t_precipitationControllerRain.RainParticleTransition\r\n\t// \t\t\t);\r\n\t// \t\t}\r\n\t// \t\telse\r\n\t// \t\t{\r\n\t// \t\t\tTransitionPrecipitationToZero(_particleEffectRain, _precipitationControllerRainEmitter, _precipitationControllerRain.RainParticleTransition);\r\n\t// \t\t}\r\n\t// \t}\r\n\r\n\t// \tif (_precipitationControllerSnow.IsValid())\r\n\t// \t{\r\n\t// \t\tif (_activePrecipitationControllerSnow)\r\n\t// \t\t{\r\n\t// \t\t\tUpdatePrecipitationTransition(\r\n\t// \t\t\t\t_particleEffectSnow,\r\n\t// \t\t\t\t_precipitationControllerSnowEmitter,\r\n\t// \t\t\t\t_precipitationControllerSnow.SnowIntensityMaximumParticlesLight,\r\n\t// \t\t\t\t_precipitationControllerSnow.SnowIntensityRateLight,\r\n\t// \t\t\t\t_precipitationControllerSnow.SnowIntensityMaximumParticlesModerate,\r\n\t// \t\t\t\t_precipitationControllerSnow.SnowIntensityRateModerate,\r\n\t// \t\t\t\t_precipitationControllerSnow.SnowIntensityMaximumParticlesHeavy,\r\n\t// \t\t\t\t_precipitationControllerSnow.SnowIntensityRateHeavy,\r\n\t// \t\t\t\t_precipitationControllerSnow.SnowIntensityMaximumParticlesExtreme,\r\n\t// \t\t\t\t_precipitationControllerSnow.SnowIntensityRateExtreme,\r\n\t// \t\t\t\t_precipitationControllerSnow.SnowParticleTransition\r\n\t// \t\t\t);\r\n\t// \t\t}\r\n\t// \t\telse\r\n\t// \t\t{\r\n\t// \t\t\tTransitionPrecipitationToZero(_particleEffectSnow, _precipitationControllerSnowEmitter, _precipitationControllerSnow.SnowParticleTransition);\r\n\t// \t\t}\r\n\t// \t}\r\n\r\n\t// \tif ( WaterPuddles != _lastWaterPuddlesValue || WaterLevel != _lastWaterLevelValue || PrecipitationIntensity != _lastPrecipitationIntensityValue)\r\n\t// \t{\r\n\t// \t\t_lastWaterPuddlesValue = WaterPuddles;\r\n\t// \t\t_lastWaterLevelValue = WaterLevel;\r\n\t// \t\t_lastPrecipitationIntensityValue = PrecipitationIntensity;\r\n\r\n\t// \t\tforeach ( var sceneModel in _cachedSceneObjects )\r\n\t// \t\t{\r\n\t// \t\t\tsceneModel.Attributes.Set( \u0022Water Puddles Accumulation\u0022, WaterPuddles );\r\n\t// \t\t\tsceneModel.Attributes.Set( \u0022Water Plane Level\u0022, WaterLevel );\r\n\t// \t\t\tsceneModel.Attributes.Set( \u0022Rain Ripples Intensity\u0022, PrecipitationIntensity );\r\n\t// \t\t}\r\n\t// \t}\r\n\t// }\r\n\r\n\t// private void UpdatePrecipitationTransition(\r\n\t// \tParticleEffect particleEffect,\r\n\t// \tPrecipitationControllerEmitter emitter,\r\n\t// \tint lightMaxParticles, int lightRate,\r\n\t// \tint moderateMaxParticles, int moderateRate,\r\n\t// \tint heavyMaxParticles, int heavyRate,\r\n\t// \tint extremeMaxParticles, int extremeRate,\r\n\t// \tfloat transitionDuration)\r\n\t// {\r\n\t// \tif ( PrecipitationIntensity != _lastPrecipitationIntensityValueForParticles )\r\n\t// \t{\r\n\t// \t\t_lastPrecipitationIntensityValueForParticles = PrecipitationIntensity;\r\n\r\n\t// \t\t_startMaxParticles = particleEffect.MaxParticles;\r\n\t// \t\t_startRate = emitter.Rate.ConstantValue;\r\n\r\n\t// \t\tint intensity = (int)PrecipitationIntensity;\r\n\t// \t\t(_targetMaxParticles, _targetRate) = intensity switch\r\n\t// \t\t{\r\n\t// \t\t\t1 =\u003E (lightMaxParticles, lightRate),\r\n\t// \t\t\t2 =\u003E (moderateMaxParticles, moderateRate),\r\n\t// \t\t\t3 =\u003E (heavyMaxParticles, heavyRate),\r\n\t// \t\t\t4 =\u003E (extremeMaxParticles, extremeRate),\r\n\t// \t\t\t_ =\u003E (0, 0)\r\n\t// \t\t};\r\n\t// \t\t_transitionProgress = 0f;\r\n\t// \t}\r\n\r\n\t// \tif ( _transitionProgress \u003C 1f )\r\n\t// \t{\r\n\t// \t\t_transitionProgress \u002B= Time.Delta / transitionDuration;\r\n\t// \t\tif ( _transitionProgress \u003E= 1f )\r\n\t// \t\t{\r\n\t// \t\t\t_transitionProgress = 1f;\r\n\t// \t\t\tparticleEffect.MaxParticles = (int)_targetMaxParticles;\r\n\t// \t\t\temitter.Rate = _targetRate;\r\n\t// \t\t}\r\n\t// \t\telse\r\n\t// \t\t{\r\n\t// \t\t\tparticleEffect.MaxParticles = (int)(_startMaxParticles \u002B (_targetMaxParticles - _startMaxParticles) * _transitionProgress);\r\n\t// \t\t\temitter.Rate = _startRate \u002B (_targetRate - _startRate) * _transitionProgress;\r\n\t// \t\t}\r\n\t// \t}\r\n\t// }\r\n\r\n\t// private void TransitionPrecipitationToZero(ParticleEffect particleEffect, PrecipitationControllerEmitter emitter, float transitionDuration)\r\n\t// {\r\n\t// \tfloat step = Time.Delta / transitionDuration;\r\n\t// \tparticleEffect.MaxParticles = (int)Math.Max(0, particleEffect.MaxParticles - (particleEffect.MaxParticles * step));\r\n\t// \temitter.Rate = Math.Max(0, emitter.Rate.ConstantValue - (emitter.Rate.ConstantValue * step));\r\n\r\n\t// \tif (particleEffect.MaxParticles == 0)\r\n\t// \t{\r\n\t// \t\tif (_precipitationControllerRain.IsValid() \u0026\u0026 particleEffect == _particleEffectRain)\r\n\t// \t\t{\r\n\t// \t\t\t_rainGameObject?.Destroy();\r\n\t// \t\t\t_rainGameObject = null;\r\n\t// \t\t\t_precipitationControllerRain = null;\r\n\t// \t\t\t_particleEffectRain = null;\r\n\t// \t\t\t_precipitationControllerRainEmitter = null;\r\n\t// \t\t}\r\n\t// \t\telse if (_precipitationControllerSnow.IsValid() \u0026\u0026 particleEffect == _particleEffectSnow)\r\n\t// \t\t{\r\n\t// \t\t\t_snowGameObject?.Destroy();\r\n\t// \t\t\t_snowGameObject = null;\r\n\t// \t\t\t_precipitationControllerSnow = null;\r\n\t// \t\t\t_particleEffectSnow = null;\r\n\t// \t\t\t_precipitationControllerSnowEmitter = null;\r\n\t// \t\t}\r\n\t// \t}\r\n\t// }\r\n\t// public void TimeEffects()\r\n\t// {\r\n\t// \tif ( Hour != _lastHour)\r\n\t// \t{\r\n\t// \t\t_lastHour = Hour;\r\n\r\n\t// \t\tconst float sunriseStart = 5.6f;\r\n\t// \t\tconst float sunrisePeak  = 6.3f;\r\n\t// \t\tconst float sunriseEnd   = 7f;\r\n\t// \t\tconst float sunsetStart  = 17f;\r\n\t// \t\tconst float sunsetPeak   = 18.0f;\r\n\t// \t\tconst float sunsetEnd    = 18.4f;\r\n\t// \t\tconst float degreesPerHour = 15f;\r\n\t// \t\tconst float sunAngleOffset = 90f;\r\n\r\n\t// \t\tSun.WorldRotation = Rotation.From( (Hour * degreesPerHour) - sunAngleOffset, 0, 0 );\r\n\r\n\t// \t\tvar directionalLight = Sun.Components.Get\u003CDirectionalLight\u003E();\r\n\t// \t\tif ( directionalLight.IsValid() )\r\n\t// \t\t{\r\n\t// \t\t\tvar goldenHour = new Color( 1.0f, 0.45f, 0.1f );\r\n\t// \t\t\tColor sunColor;\r\n\r\n\t// \t\t\tif ( Hour \u003E= sunsetEnd || Hour \u003C sunriseStart )\r\n\t// \t\t\t{\r\n\t// \t\t\t\tsunColor = Color.Black;\r\n\t// \t\t\t}\r\n\t// \t\t\telse if ( Hour \u003E= sunriseStart \u0026\u0026 Hour \u003C sunrisePeak )\r\n\t// \t\t\t{\r\n\t// \t\t\t\tfloat time = (Hour - sunriseStart) / (sunrisePeak - sunriseStart);\r\n\t// \t\t\t\tsunColor = Color.Lerp( Color.Black, goldenHour, time );\r\n\t// \t\t\t}\r\n\t// \t\t\telse if ( Hour \u003E= sunrisePeak \u0026\u0026 Hour \u003C sunriseEnd )\r\n\t// \t\t\t{\r\n\t// \t\t\t\tfloat time = (Hour - sunrisePeak) / (sunriseEnd - sunrisePeak);\r\n\t// \t\t\t\tsunColor = Color.Lerp( goldenHour, Color.White, time );\r\n\t// \t\t\t}\r\n\t// \t\t\telse if ( Hour \u003E= sunriseEnd \u0026\u0026 Hour \u003C sunsetStart )\r\n\t// \t\t\t{\r\n\t// \t\t\t\tsunColor = Color.White;\r\n\t// \t\t\t}\r\n\t// \t\t\telse if ( Hour \u003E= sunsetStart \u0026\u0026 Hour \u003C sunsetPeak )\r\n\t// \t\t\t{\r\n\t// \t\t\t\tfloat time = (Hour - sunsetStart) / (sunsetPeak - sunsetStart);\r\n\t// \t\t\t\tsunColor = Color.Lerp( Color.White, goldenHour, time );\r\n\t// \t\t\t}\r\n\t// \t\t\telse\r\n\t// \t\t\t{\r\n\t// \t\t\t\tfloat time = (Hour - sunsetPeak) / (sunsetEnd - sunsetPeak);\r\n\t// \t\t\t\tsunColor = Color.Lerp( goldenHour, Color.Black, time );\r\n\t// \t\t\t}\r\n\r\n\t// \t\t\tdirectionalLight.LightColor = sunColor;\r\n\t// \t\t}\r\n\r\n\t// \t\tforeach ( var envmapProbe in _cachedEnvmapProbes )\r\n\t// \t\t{\r\n\t// \t\t\tif ( envmapProbe.IsValid() )\r\n\t// \t\t\t{\r\n\t// \t\t\t\tenvmapProbe.Dirty = true;\r\n\t// \t\t\t}\r\n\t// \t\t}\r\n\t// \t}\r\n\t// }\r\n}"},{"Ident":"ali3nsystems.lib-atmokinetic_assets","Path":"Water/Done/Volume/Cube/WaterController.VolumeCube.cs","FileName":"WaterController.VolumeCube.cs","PackageType":"library","CodeKind":"Game","AssetVersionId":338612,"IsPrivate":false,"Code":"// namespace AtmokineticAssets;\r\n\r\n// [StructLayout(LayoutKind.Sequential)]\r\n// public struct WetnessVolumeCubeBuffer\r\n// {\r\n// \tpublic Vector3 VolumeCenter;\r\n// \tpublic Vector3 VolumeSize;\r\n// \tpublic float WetnessVolumeExtend;\r\n// \tpublic float WetnessVolumeFade;\r\n// \tpublic float WetnessVolumeSoftness;\r\n// }\r\n\r\n// public sealed partial class WaterController : Component\r\n// {\r\n// \t[Property, Title(\u0022Bounds Minimum (cm)\u0022), Feature(\u0022Wetness\u0022), Group(\u0022Properties\u0022), ShowIf( nameof(WetnessVolumeMode), WetnessVolumeModeEnumeration.Cube )]\r\n// \tpublic Vector3 WetnessVolumeCubeBoundsMinimum {get;set;} = new Vector3(-500, -500, -500);\r\n\r\n// \t[Property, Title(\u0022Bounds Maximum (cm)\u0022), Feature(\u0022Wetness\u0022), Group(\u0022Properties\u0022), ShowIf( nameof(WetnessVolumeMode), WetnessVolumeModeEnumeration.Cube )]\r\n// \tpublic Vector3 WetnessVolumeCubeBoundsMaximum {get;set;} = new Vector3( 500, 500, 500 );\r\n\r\n// \t[Property, Title( \u0022Enable Gizmo\u0022 ), Feature(\u0022Wetness\u0022), Group(\u0022Properties\u0022), ShowIf( nameof(WetnessVolumeMode), WetnessVolumeModeEnumeration.Cube )]\r\n// \tpublic bool EnableWetnessVolumeCubeGizmos { get; set; } = true;\r\n\r\n// \tprivate static readonly List\u003CWaterController\u003E _wetnessVolumeCubeList = new();\r\n// \tprivate static GpuBuffer\u003CWetnessVolumeCubeBuffer\u003E _wetnessVolumeCubeBuffer;\r\n// \tprivate static int _currentWetnessVolumeCubeBuffer = 0;\r\n\r\n// \tpublic void EnableWetnessVolumeCubeBuffer()\r\n// \t{\r\n// \t\tif (!_wetnessVolumeCubeList.Contains(this))\r\n// \t\t\t_wetnessVolumeCubeList.Add(this);\r\n\r\n// \t\tif (_wetnessVolumeCubeBuffer == null)\r\n// \t\t{\r\n// \t\t\t_wetnessVolumeCubeBuffer = new GpuBuffer\u003CWetnessVolumeCubeBuffer\u003E(1);\r\n// \t\t\t_currentWetnessVolumeCubeBuffer = 1;\r\n// \t\t}\r\n// \t\tUpdateWetnessVolumeCubeBuffer();\r\n// \t}\r\n\r\n// \tpublic void DisableWetnessVolumeCubeBuffer()\r\n// \t{\r\n// \t\t_wetnessVolumeCubeList.Remove( this );\r\n// \t\tUpdateWetnessVolumeCubeBuffer();\r\n// \t}\r\n\r\n// \tpublic static void UpdateWetnessVolumeCubeBuffer()\r\n// \t{\r\n// \t\tif (_wetnessVolumeCubeList.Count == 0) return;\r\n\r\n// \t\tvar volumeBufferList = new List\u003CWetnessVolumeCubeBuffer\u003E();\r\n\r\n// \t\tforeach (var wetnessVolumeCube in _wetnessVolumeCubeList)\r\n// \t\t{\r\n// \t\t\tif (!wetnessVolumeCube.IsValid()) continue;\r\n\r\n// \t\t\t// Convert cm to inches\r\n// \t\t\tvar boundsMinimumInches = UnitConversion.CentimetersToInches(wetnessVolumeCube.WetnessVolumeCubeBoundsMinimum);\r\n// \t\t\tvar boundsMaximumInches = UnitConversion.CentimetersToInches(wetnessVolumeCube.WetnessVolumeCubeBoundsMaximum);\r\n\r\n// \t\t\t// Calculate world space center and size\r\n// \t\t\tVector3 localCenter = (boundsMinimumInches \u002B boundsMaximumInches) * 0.5f;\r\n// \t\t\tVector3 worldCenter = wetnessVolumeCube.WorldPosition \u002B localCenter;\r\n// \t\t\tVector3 size = boundsMaximumInches - boundsMinimumInches;\r\n\r\n// \t\t\tvolumeBufferList.Add(new WetnessVolumeCubeBuffer\r\n// \t\t\t{\r\n// \t\t\t\tVolumeCenter = worldCenter,\r\n// \t\t\t\tWetnessVolumeExtend = UnitConversion.CentimetersToInches( wetnessVolumeCube.WetnessVolumeExtend ),\r\n// \t\t\t\tVolumeSize = size,\r\n// \t\t\t\tWetnessVolumeFade = wetnessVolumeCube.WetnessVolumeFade,\r\n// \t\t\t\tWetnessVolumeSoftness = wetnessVolumeCube.WetnessVolumeSoftness,\r\n\r\n// \t\t\t});\r\n// \t\t}\r\n\r\n// \t\tif (_wetnessVolumeCubeBuffer == null || volumeBufferList.Count \u003E _currentWetnessVolumeCubeBuffer)\r\n// \t\t{\r\n// \t\t\t_wetnessVolumeCubeBuffer = new GpuBuffer\u003CWetnessVolumeCubeBuffer\u003E(volumeBufferList.Count);\r\n// \t\t\t_currentWetnessVolumeCubeBuffer = volumeBufferList.Count;\r\n// \t\t}\r\n\r\n// \t\tif (volumeBufferList.Count \u003E 0)\r\n// \t\t{\r\n// \t\t\t_wetnessVolumeCubeBuffer.SetData(volumeBufferList);\r\n// \t\t}\r\n\r\n// \t\tif (Game.ActiveScene?.SceneWorld != null)\r\n// \t\t{\r\n// \t\t\tvar sceneObjects = Game.ActiveScene.SceneWorld.SceneObjects\r\n// \t\t\t\t.Where(x =\u003E x != null \u0026\u0026 x.IsValid())\r\n// \t\t\t\t.ToList();\r\n\r\n// \t\t\tforeach (var sceneObject in sceneObjects)\r\n// \t\t\t{\r\n// \t\t\t\tif (sceneObject.IsValid())\r\n// \t\t\t\t{\r\n// \t\t\t\t\tsceneObject.Attributes.Set(\u0022WetnessWetnessVolumeCubeBuffer\u0022, _wetnessVolumeCubeBuffer);\r\n// \t\t\t\t\tsceneObject.Attributes.Set(\u0022MaximumWetnessWetnessVolumeCubes\u0022, volumeBufferList.Count);\r\n// \t\t\t\t}\r\n// \t\t\t}\r\n// \t\t}\r\n// \t}\r\n// }\r\n"},{"Ident":"ali3nsystems.lib-atmokinetic_assets","Path":"Water/Reflection/WaterController.Reflection.cs","FileName":"WaterController.Reflection.cs","PackageType":"library","CodeKind":"Game","AssetVersionId":338612,"IsPrivate":false,"Code":"namespace AtmokineticAssets;\r\n\r\npublic sealed partial class WaterController : Component, Component.ExecuteInEditor\r\n{\r\n\tpublic enum WaterReflectionModeEnumeration\r\n\t{\r\n\t\tDynamic,\r\n\t\tStatic\r\n\t}\r\n\r\n\t[Property, Feature(\u0022Reflection\u0022), Header(\u0022Mode\u0022)]\r\n\t[Sync]\r\n\tpublic WaterReflectionModeEnumeration WaterReflectionMode\r\n\t{\r\n\t\tget;\r\n\t\tset { field = value; ApplyWaterReflectionMode(); }\r\n\t} = WaterReflectionModeEnumeration.Dynamic;\r\n\r\n\tpublic enum WaterReflectionModeStaticModeEnumeration\r\n\t{\r\n\t\tPlanar,\r\n\t\tCubemap\r\n\t}\r\n\r\n\t[Property, Feature(\u0022Reflection\u0022), ShowIf( nameof(WaterReflectionMode), WaterReflectionModeEnumeration.Static)]\r\n\t[Sync]\r\n\tpublic WaterReflectionModeStaticModeEnumeration WaterReflectionModeStaticMode\r\n\t{\r\n\t\tget;\r\n\t\tset { field = value; ApplyWaterReflectionMode(); }\r\n\t} = WaterReflectionModeStaticModeEnumeration.Planar;\r\n\r\n\t// Mark the reflection mode dirty after its own settings change. Deliberately does\r\n\t// NOT apply immediately: these setters also fire from [Sync] snapshots while a\r\n\t// client is joining, before the scene camera exists - frustum work here NREs.\r\n\t// OnUpdate\u0027s change-check applies it on the next frame instead.\r\n\tprivate void ApplyWaterReflectionMode()\r\n\t{\r\n\t\t_lastWaterReflectionPlanarRendering = null;\r\n\t}\r\n\r\n\tpublic Vector3 WaterNormalAxis { get; set; } = Vector3.Up;\r\n\t\r\n}\r\n"},{"Ident":"ali3nsystems.lib-atmokinetic_assets","Path":".obj/__compiler_extra.cs","FileName":"__compiler_extra.cs","PackageType":"library","CodeKind":"Game","AssetVersionId":338612,"IsPrivate":false,"Code":"global using static Sandbox.Internal.GlobalGameNamespace;\r\nglobal using Microsoft.AspNetCore.Components;\r\nglobal using Microsoft.AspNetCore.Components.Rendering;\r\n[assembly: global::System.Reflection.AssemblyMetadata( \u0022AddonTitle\u0022, \u0022Atmokinetic Assets\u0022 )]\r\n[assembly: global::System.Reflection.AssemblyMetadata( \u0022AddonIdent\u0022, \u0022lib-atmokinetic_assets\u0022 )]\r\n[assembly: global::System.Reflection.AssemblyMetadata( \u0022OrgIdent\u0022, \u0022ali3nsystems\u0022 )]\r\n[assembly: global::System.Reflection.AssemblyMetadata( \u0022Ident\u0022, \u0022ali3nsystems.lib-atmokinetic_assets\u0022 )]\r\n[assembly: global::System.Reflection.AssemblyMetadata( \u0022EngineVersion\u0022, \u002228\u0022 )]\r\n[assembly: global::System.Reflection.AssemblyMetadata( \u0022EngineMinorVersion\u0022, \u00221\u0022 )]\r\n\r\n[assembly: System.Runtime.Versioning.TargetFramework( \u0022.NETCoreApp,Version=v9.0\u0022, FrameworkDisplayName = \u0022.NET 9.0\u0022 )]\r\n[assembly: global::System.Reflection.AssemblyMetadata( \u0022CompileTime\u0022, \u00222026-08-03T22:07:53.7475400Z\u0022 )]\r\n[assembly: global::System.Reflection.AssemblyVersion(\u00220.0.111.0\u0022)]\r\n[assembly: global::System.Reflection.AssemblyFileVersion(\u00220.0.111.0\u0022)]"},{"Ident":"ali3nsystems.lib-atmokinetic_assets","Path":"Code/Camera/CameraController.Weather.WaterLine.cs","FileName":"CameraController.Weather.WaterLine.cs","PackageType":"library","CodeKind":"Game","AssetVersionId":338612,"IsPrivate":false,"Code":"namespace AtmokineticAssets;\r\n\r\npublic sealed partial class CameraController : BasePostProcess\u003CCameraController\u003E, Component.ExecuteInEditor\r\n{\r\n\tpublic GameObject WaterLineCameraGameObject;\r\n\tpublic CameraComponent WaterLineCameraComponent;\r\n\tpublic Texture WaterLineTexture;\r\n\r\n\tprivate void CheckWaterSubmersion()\r\n\t{\r\n\t\tvar corners = GetCameraNearPlaneCorners();\r\n\t\tif (corners == null)\r\n\t\t{\r\n\t\t\tIsCameraSubmerged = false;\r\n\t\t\tIsCameraFullySubmerged = false;\r\n\t\t\tif (WaterLineCameraGameObject.IsValid())\r\n\t\t\t{\r\n\t\t\t\tWaterController?.DestroyWaterLineMesh();\r\n\t\t\t\tDisableWaterLineRendering();\r\n\t\t\t}\r\n\t\t\treturn;\r\n\t\t}\r\n\r\n\t\t// Cached by EnvironmentController on awake - no per-frame scene scan.\r\n\t\tvar waterControllers = EnvironmentController.CachedWaterControllers;\r\n\t\tint maxSubmergedCount = 0;\r\n\t\tWaterController activeWater = null;\r\n\r\n\t\tforeach (var water in waterControllers)\r\n\t\t{\r\n\t\t\tif (!water.IsValid()) continue;\r\n\r\n\t\t\tint submergedCount = CountCameraSubmergedCorners(corners, water);\r\n\t\t\tif (submergedCount \u003E maxSubmergedCount)\r\n\t\t\t{\r\n\t\t\t\tmaxSubmergedCount = submergedCount;\r\n\t\t\t\tactiveWater = water;\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\tbool WasCameraFullySubmerged = IsCameraFullySubmerged;\r\n\t\tIsCameraSubmerged = maxSubmergedCount \u003E= 1;\r\n\r\n\t\t// Use offset check for fully Submerged (only applies when already in water)\r\n\t\tint fullySubmergedCount = activeWater != null ? CountCameraFullySubmergedCorners(corners, activeWater) : 0;\r\n\t\tbool isNowFullySubmerged = fullySubmergedCount == 4;\r\n\t\tbool isNowPartiallySubmerged = IsCameraSubmerged \u0026\u0026 !isNowFullySubmerged;\r\n\r\n\t\t// Handle water controller change\r\n\t\tif (activeWater != WaterController)\r\n\t\t{\r\n\t\t\tWaterController?.DestroyWaterLineMesh();\r\n\t\t\tWaterController = activeWater;\r\n\t\t}\r\n\r\n\t\t// Water line cube (only when partially Submerged)\r\n\t\tif (isNowPartiallySubmerged \u0026\u0026 !WaterLineCameraGameObject.IsValid())\r\n\t\t{\r\n\t\t\tWaterController?.CreateWaterLineMesh();\r\n\t\t\tActivateWaterLineRendering();\r\n\t\t}\r\n\r\n\t\telse if (!isNowPartiallySubmerged \u0026\u0026 WaterLineCameraGameObject.IsValid())\r\n\t\t{\r\n\t\t\tWaterController?.DestroyWaterLineMesh();\r\n\t\t\tDisableWaterLineRendering();\r\n\t\t}\r\n\r\n\t\t// Delay IsCameraFullySubmerged off by one frame to prevent flash\r\n\t\tif (WasCameraFullySubmerged \u0026\u0026 isNowPartiallySubmerged)\r\n\t\t{\r\n\t\t\tif (_transitioningFromFullySubmerged)\r\n\t\t\t{\r\n\t\t\t\tIsCameraFullySubmerged = false;\r\n\t\t\t\t_transitioningFromFullySubmerged = false;\r\n\t\t\t}\r\n\t\t\telse\r\n\t\t\t{\r\n\t\t\t\tIsCameraFullySubmerged = true;\r\n\t\t\t\t_transitioningFromFullySubmerged = true;\r\n\t\t\t}\r\n\t\t}\r\n\t\telse\r\n\t\t{\r\n\t\t\tIsCameraFullySubmerged = isNowFullySubmerged;\r\n\t\t\t_transitioningFromFullySubmerged = false;\r\n\t\t}\r\n\t}\r\n\r\n\tpublic void WaterLineShaderAttributes()\r\n\t{\r\n\t\tAttributes.Set(\u0022Water Line Size\u0022, WaterController.WaterLineSize);\r\n\t\tAttributes.Set(\u0022Water Line Blur\u0022, WaterController.WaterLineBlur);\r\n\t\tAttributes.Set(\u0022Water Line Color\u0022, WaterController.WaterLineColor);\r\n\t}\r\n\r\n    private void ActivateWaterLineRendering()\r\n\t{\r\n\t\tCameraComponent.RenderExcludeTags.Add(\u0022water_line_mesh\u0022);\r\n\r\n\t\tWaterLineCameraGameObject = new GameObject { Name = \u0022Water Line Camera\u0022, Parent = GameObject };\r\n\t\t// Local-only render helper - never include it in network snapshots.\r\n\t\tWaterLineCameraGameObject.Flags |= GameObjectFlags.NotNetworked;\r\n\t\tWaterLineCameraGameObject.NetworkMode = NetworkMode.Never;\r\n\t\tWaterLineCameraComponent = WaterLineCameraGameObject.AddComponent\u003CCameraComponent\u003E();\r\n\t\tWaterLineCameraComponent.BackgroundColor = Color.Black;\r\n\t\tWaterLineCameraComponent.IsMainCamera = false;\r\n\t\tWaterLineCameraComponent.FovAxis = CameraComponent.FovAxis;\r\n\t\tWaterLineCameraComponent.FieldOfView = CameraComponent.FieldOfView;\r\n\t\tWaterLineCameraComponent.ZNear = CameraComponent.ZNear;\r\n\t\tWaterLineCameraComponent.ZFar = CameraComponent.ZFar;\r\n\t\tWaterLineCameraComponent.Priority = CameraComponent.Priority \u002B 1;\r\n\t\tWaterLineCameraComponent.Orthographic = CameraComponent.Orthographic;\r\n\t\tWaterLineCameraComponent.OrthographicHeight = CameraComponent.OrthographicHeight;\r\n\t\tWaterLineCameraComponent.TargetEye = CameraComponent.TargetEye;\r\n\t\tWaterLineCameraComponent.Viewport = CameraComponent.Viewport;\r\n\t\tWaterLineCameraComponent.RenderTags.Add(\u0022water_line_mesh\u0022);\r\n\t\tWaterLineCameraComponent.RenderExcludeTags.RemoveAll();\r\n\t\tWaterLineCameraComponent.EnablePostProcessing = false;\r\n\t}\r\n\r\n\tprivate void RenderWaterLineTexture()\r\n\t{\r\n\t\tWaterLineTexture = Texture.CreateRenderTarget(\u0022WaterLineRenderTexture\u0022, ImageFormat.A8, Screen.Size, WaterLineTexture);\r\n\t\tWaterLineCameraComponent.RenderTarget = WaterLineTexture;\r\n\t\tAttributes.Set(\u0022WaterLineTexture\u0022, WaterLineTexture);\r\n\t}\r\n\r\n\tprivate void DisableWaterLineRendering()\r\n\t{\r\n\t\tWaterLineCameraGameObject.Destroy();\r\n\t\tWaterLineCameraGameObject = null;\r\n\t\tWaterLineCameraComponent = null;\r\n\r\n\t\tWaterLineTexture.Dispose();\r\n\t\tWaterLineTexture = null;\r\n\r\n\t\tCameraComponent.RenderExcludeTags.Remove(\u0022water_line_mesh\u0022);\r\n\t}\r\n}"}]}