s&box Package Code Search
Search C# source code, UI razor templates, shaders, and configs across s&box packages.
link Raw Facepunch API Link: https://public.facepunch.com/sbox/code/search/1/?ident=ali3nsystems.lib-atmokinetic_assets&take=20
Showing code results for query:
*
(68 total matches found)
Game
library
namespace AtmokineticAssets;
public sealed partial class CameraController : BasePostProcess<CameraController>
{
public Material material {get;set;} = Material.Load("materials/mat-camera_weather.vmat");
public bool IsCameraSubmerged { get; private set; }
public bool IsCameraFullySubmerged { get; private set; }
public float TimeSinceCameraLastExitedWater;
public bool WasCameraFullySubmerged;
private bool _transitioningFromFullySubmerged;
public WaterController WaterController;
public CameraComponent CameraComponent;
protected override void OnAwake()
{
CameraComponent = GameObject.GetComponent<CameraComponent>();
}
protected override void OnUpdate()
{
CheckWaterSubmersion();
UpdateWaterDrips();
}
public override void Render()
{
if (WaterLineCameraComponent.IsValid())
{
RenderWaterLineTexture();
}
if (WaterController.IsValid())
{
UnderWaterShaderAttributes();
// WaterDripsShaderAttributes();
WaterLineShaderAttributes();
}
WaterDripsFadeShaderAttribute();
Attributes.Set("Is Camera Submerged", (IsCameraSubmerged && !IsCameraFullySubmerged) ? 1f : 0f);
Attributes.Set("Is Fully Submerged", IsCameraFullySubmerged ? 1f : 0f);
Blit(BlitMode.WithBackbuffer(material, Stage.BeforePostProcess, 100), "Water Post Process");
}
}
Game
library
namespace AtmokineticAssets;
public sealed partial class CameraController : BasePostProcess<CameraController>, Component.ExecuteInEditor
{
public GameObject WaterLineCameraGameObject;
public CameraComponent WaterLineCameraComponent;
public Texture WaterLineTexture;
private void CheckWaterSubmersion()
{
var corners = GetCameraNearPlaneCorners();
if (corners == null)
{
IsCameraSubmerged = false;
IsCameraFullySubmerged = false;
if (WaterLineCameraGameObject.IsValid())
{
WaterController?.DestroyWaterLineMesh();
DisableWaterLineRendering();
}
return;
}
// Cached by EnvironmentController on awake - no per-frame scene scan.
var waterControllers = EnvironmentController.CachedWaterControllers;
int maxSubmergedCount = 0;
WaterController activeWater = null;
foreach (var water in waterControllers)
{
if (!water.IsValid()) continue;
int submergedCount = CountCameraSubmergedCorners(corners, water);
if (submergedCount > maxSubmergedCount)
{
maxSubmergedCount = submergedCount;
activeWater = water;
}
}
bool WasCameraFullySubmerged = IsCameraFullySubmerged;
IsCameraSubmerged = maxSubmergedCount >= 1;
// Use offset check for fully Submerged (only applies when already in water)
int fullySubmergedCount = activeWater != null ? CountCameraFullySubmergedCorners(corners, activeWater) : 0;
bool isNowFullySubmerged = fullySubmergedCount == 4;
bool isNowPartiallySubmerged = IsCameraSubmerged && !isNowFullySubmerged;
// Handle water controller change
if (activeWater != WaterController)
{
WaterController?.DestroyWaterLineMesh();
WaterController = activeWater;
}
// Water line cube (only when partially Submerged)
if (isNowPartiallySubmerged && !WaterLineCameraGameObject.IsValid())
{
WaterController?.CreateWaterLineMesh();
ActivateWaterLineRendering();
}
else if (!isNowPartiallySubmerged && WaterLineCameraGameObject.IsValid())
{
WaterController?.DestroyWaterLineMesh();
DisableWaterLineRendering();
}
// Delay IsCameraFullySubmerged off by one frame to prevent flash
if (WasCameraFullySubmerged && isNowPartiallySubmerged)
{
if (_transitioningFromFullySubmerged)
{
IsCameraFullySubmerged = false;
_transitioningFromFullySubmerged = false;
}
else
{
IsCameraFullySubmerged = true;
_transitioningFromFullySubmerged = true;
}
}
else
{
IsCameraFullySubmerged = isNowFullySubmerged;
_transitioningFromFullySubmerged = false;
}
}
public void WaterLineShaderAttributes()
{
Attributes.Set("Water Line Size", WaterController.WaterLineSize);
Attributes.Set("Water Line Blur", WaterController.WaterLineBlur);
Attributes.Set("Water Line Color", WaterController.WaterLineColor);
}
private void ActivateWaterLineRendering()
{
CameraComponent.RenderExcludeTags.Add("water_line_mesh");
WaterLineCameraGameObject = new GameObject { Name = "Water Line Camera", Parent = GameObject };
// Local-only render helper - never include it in network snapshots.
WaterLineCameraGameObject.Flags |= GameObjectFlags.NotNetworked;
WaterLineCameraGameObject.NetworkMode = NetworkMode.Never;
WaterLineCameraComponent = WaterLineCameraGameObject.AddComponent<CameraComponent>();
WaterLineCameraComponent.BackgroundColor = Color.Black;
WaterLineCameraComponent.IsMainCamera = false;
WaterLineCameraComponent.FovAxis = CameraComponent.FovAxis;
WaterLineCameraComponent.FieldOfView = CameraComponent.FieldOfView;
WaterLineCameraComponent.ZNear = CameraComponent.ZNear;
WaterLineCameraComponent.ZFar = CameraComponent.ZFar;
WaterLineCameraComponent.Priority = CameraComponent.Priority + 1;
WaterLineCameraComponent.Orthographic = CameraComponent.Orthographic;
WaterLineCameraComponent.OrthographicHeight = CameraComponent.OrthographicHeight;
WaterLineCameraComponent.TargetEye = CameraComponent.TargetEye;
WaterLineCameraComponent.Viewport = CameraComponent.Viewport;
WaterLineCameraComponent.RenderTags.Add("water_line_mesh");
WaterLineCameraComponent.RenderExcludeTags.RemoveAll();
WaterLineCameraComponent.EnablePostProcessing = false;
}
private void RenderWaterLineTexture()
{
WaterLineTexture = Texture.CreateRenderTarget("WaterLineRenderTexture", ImageFormat.A8, Screen.Size, WaterLineTexture);
WaterLineCameraComponent.RenderTarget = WaterLineTexture;
Attributes.Set("WaterLineTexture", WaterLineTexture);
}
private void DisableWaterLineRendering()
{
WaterLineCameraGameObject.Destroy();
WaterLineCameraGameObject = null;
WaterLineCameraComponent = null;
WaterLineTexture.Dispose();
WaterLineTexture = null;
CameraComponent.RenderExcludeTags.Remove("water_line_mesh");
}
}
Game
library
global using Sandbox; global using Sandbox.Rendering; global using System; global using System.Collections.Generic; global using System.Runtime.InteropServices; global using System.Text.RegularExpressions; global using System.Linq;
Game
library
namespace AtmokineticAssets;
public sealed partial class CameraController : BasePostProcess<CameraController>
{
public void UnderWaterShaderAttributes()
{
Attributes.Set("Under Water Blur", WaterController.UnderWaterBlur);
Attributes.Set("Under Water Color", WaterController.UnderWaterColor);
}
}
Game
library
namespace AtmokineticAssets;
public sealed partial class GraphicsSettings : Component, Component.ExecuteInEditor
{
//Terrain Settings
public enum TerrainRenderingModeEnumeration
{
[Title("Parallax Occlusion Mapping")]
ParallaxOcclusionMapping,
[Title("Bump Offset")]
BumpOffset,
Standard
}
//[Property, Feature("Terrain"), Title("Rendering")]
public TerrainRenderingModeEnumeration TerrainRenderingMode { get; set; } = TerrainRenderingModeEnumeration.Standard;
public enum TerrainRenderingParallaxOcclusionMappingQualityEnumeration
{
Ultra,
High,
Medium,
Low
}
//[Property, Feature("Terrain"), Title("Quality")]
[ShowIf(nameof(TerrainRenderingMode), TerrainRenderingModeEnumeration.ParallaxOcclusionMapping)]
public TerrainRenderingParallaxOcclusionMappingQualityEnumeration TerrainRenderingParallaxOcclusionMapping { get; set; } = TerrainRenderingParallaxOcclusionMappingQualityEnumeration.Medium;
public enum TerrainRenderingBumpOffsetQualityEnumeration
{
Ultra,
High,
Medium,
Low
}
//[Property, Feature("Terrain"), Title("Quality")]
[ShowIf(nameof(TerrainRenderingMode), TerrainRenderingModeEnumeration.BumpOffset)]
public TerrainRenderingParallaxOcclusionMappingQualityEnumeration TerrainRenderingBumpOffset { get; set; } = TerrainRenderingParallaxOcclusionMappingQualityEnumeration.Medium;
// public void ChangeTerrainSettings()
// {
// // Terrain rendering mode: 0 = POM, 1 = Bump Offset, 2 = Standard
// int terrainRenderingMode = TerrainRenderingMode switch
// {
// TerrainRenderingModeEnumeration.ParallaxOcclusionMapping => 0,
// TerrainRenderingModeEnumeration.BumpOffset => 1,
// TerrainRenderingModeEnumeration.Standard => 2,
// _ => 1
// };
// // Quality: Ultra = 1, High = 2, Medium = 4, Low = 8
// float quality;
// switch ( TerrainRenderingMode )
// {
// case TerrainRenderingModeEnumeration.ParallaxOcclusionMapping:
// quality = TerrainRenderingParallaxOcclusionMapping switch
// {
// TerrainRenderingParallaxOcclusionMappingQualityEnumeration.Ultra => 1f,
// TerrainRenderingParallaxOcclusionMappingQualityEnumeration.High => 2f,
// TerrainRenderingParallaxOcclusionMappingQualityEnumeration.Medium => 4f,
// TerrainRenderingParallaxOcclusionMappingQualityEnumeration.Low => 8f,
// _ => 4f
// };
// break;
// case TerrainRenderingModeEnumeration.BumpOffset:
// quality = TerrainRenderingBumpOffset switch
// {
// TerrainRenderingParallaxOcclusionMappingQualityEnumeration.Ultra => 1f,
// TerrainRenderingParallaxOcclusionMappingQualityEnumeration.High => 2f,
// TerrainRenderingParallaxOcclusionMappingQualityEnumeration.Medium => 4f,
// TerrainRenderingParallaxOcclusionMappingQualityEnumeration.Low => 8f,
// _ => 4f
// };
// break;
// default:
// quality = 4f;
// break;
// }
// // Enumerate live — objects spawned after startup (e.g. baked water tiles) must be included.
// foreach ( var sceneObject in Scene.SceneWorld.SceneObjects )
// {
// if ( sceneObject is null || !sceneObject.IsValid() ) continue;
// sceneObject.Attributes.Set( "D_Rendering", terrainRenderingMode );
// sceneObject.Attributes.Set( "Quality", quality );
// }
// }
}
Game
library
using Sandbox.Physics;
namespace AtmokineticAssets;
[Title( "Atmokinetic Assets - Character Controller Utilities" )]
[Category( "Atmokinetic Assets" )]
[Icon( "badge" )]
public sealed partial class CharacterControllerUtilities : Component, Component.INetworkSpawn
{
/// <summary>
/// THIS IS PRIMARILY USED FOR THE MODELPHYISICS TO GENERATE WATER SIMULATIONS/RIPPLES
/// WITHOUT COLLIDING WITH THE CHARACTER CONTROLLER'S BOX/CAPSULE COLLIDER.
/// IT USES THE STEAMID AS AN IDENTIFIER.
/// </summary>
private string _appliedTag;
private ModelPhysics _modelPhysics;
private int _lastTaggedBodyCount = -1;
public void OnNetworkSpawn( Connection owner )
{
EnsurePhysics();
ApplyTag( owner );
}
protected override void OnStart()
{
EnsurePhysics();
// Fallback for non-networked scenes: tag with the local connection
ApplyTag( Network.Owner ?? Connection.Local );
}
// Create a ModelPhysics from the player's SkinnedModelRenderer if one is
// missing, so the collision bodies this component tags actually exist.
private void EnsurePhysics()
{
if ( GameObject.GetComponentInChildren<ModelPhysics>( true ).IsValid() )
return;
var renderer = GameObject.GetComponentInChildren<SkinnedModelRenderer>( true );
if ( !renderer.IsValid() || renderer.Model is null )
return;
var physics = renderer.GameObject.AddComponent<ModelPhysics>();
physics.Renderer = renderer;
physics.Model = renderer.Model;
physics.MotionEnabled = false;
}
protected override void OnUpdate()
{
// ModelPhysics creates its ragdoll bone objects lazily (and rebuilds them on
// model reload). Instead of walking every child collider each tick, watch the
// body count - it only changes when the ragdoll is (re)built.
if ( string.IsNullOrEmpty( _appliedTag ) )
return;
if ( !_modelPhysics.IsValid() )
_modelPhysics = GameObject.GetComponentInChildren<ModelPhysics>( true );
int bodyCount = _modelPhysics.IsValid() ? _modelPhysics.Bodies.Count : 0;
if ( bodyCount == _lastTaggedBodyCount )
return;
_lastTaggedBodyCount = bodyCount;
TagColliders( _appliedTag );
}
private void ApplyTag( Connection connection )
{
if ( connection is null )
return;
var tag = connection.SteamId.ToString();
if ( _appliedTag == tag )
return;
// Clean up a previously applied id (e.g. after ownership transfer)
if ( !string.IsNullOrEmpty( _appliedTag ) )
GameObject.Tags.Remove( _appliedTag );
// The tag itself replicates over the network (it's GameObject state), so a
// joining client can see it already applied from the snapshot - but the
// collision-ignore rule lives in this machine's own PhysicsWorld, which is
// NOT networked. So only skip re-adding the tag; always (re)register the
// rule locally, or clients would collide with their own ragdoll forever.
if ( !GameObject.Tags.Has( tag ) )
GameObject.Tags.Add( tag );
IgnoreSelfCollision( tag );
TagColliders( tag );
_appliedTag = tag;
}
// Add a rule so any two shapes both carrying this exact Steam ID tag don't
// collide. Only this player's own colliders carry it, so its ragdoll ignores
// its own body while still colliding with other players (whose tag differs).
private void IgnoreSelfCollision( string tag )
{
var rules = Scene.PhysicsWorld?.CollisionRules;
if ( rules is null )
return;
rules.Pairs[new CollisionRules.Pair( tag, tag )] = CollisionRules.Result.Ignore;
}
// Stamp the Steam ID tag onto every collider under the player. Shapes take
// their tags from their GameObject, and ragdoll bone objects don't inherit
// the root's tags, so each one has to be tagged directly.
private void TagColliders( string tag )
{
foreach ( var collider in GameObject.GetComponentsInChildren<Collider>( true ) )
{
if ( !collider.GameObject.Tags.Has( tag ) )
collider.GameObject.Tags.Add( tag );
}
}
}
Game
library
namespace AtmokineticAssets;
public sealed partial class WaterController: Component, Component.ITriggerListener
{
[Property, Title("Tile Model"), Feature("General"), Group("Properties"), ShowIf( nameof(WaterMode), WaterModeEnumeration.Tiles )]
public Model WaterTileModel { get; set; } = Model.Load("models/mdl-water_tile.vmdl");
[Property, Title("Tile Size (cm)"), Feature("General"), Group("Properties"), ShowIf( nameof(WaterMode), WaterModeEnumeration.Tiles )]
public float WaterTileSize
{
get;
set
{
field = ResizeWaterTileBounds( field, value );
}
} = 500f;
[Property, Title("Bounds Maximum (cm)"), Feature("General"), Group("Properties"), ShowIf( nameof(WaterMode), WaterModeEnumeration.Tiles )]
public Vector3 WaterTileBoundsMaximum { get; set; } = new( 500, 500, 500 );
[Property, Title("Bounds Minimum (cm)"), Feature("General"), Group("Properties"), ShowIf( nameof(WaterMode), WaterModeEnumeration.Tiles )]
public Vector3 WaterTileBoundsMinimum { get; set; } = new( -500, -500, -500 );
[Button("Generate Water Tiles"), Feature("General"), Group("Properties"), ShowIf( nameof(WaterMode), WaterModeEnumeration.Tiles )]
public void GenerateWaterTiles()
{
Bake();
WaterSurfaceAttributes();
}
[Property, Title("Enable Gizmo"),Feature( "General" ), ShowIf( nameof(WaterMode), WaterModeEnumeration.Tiles)]
public bool EnableWaterTileGizmo { get; set; } = true;
private readonly Dictionary<(int x, int y), (GameObject go, ModelRenderer renderer)> _tiles = [];
private Vector2 _lastSnappedPosition = new( float.MinValue, float.MinValue );
private BoxCollider _waterCollider;
private bool _waterLineActive;
private readonly Dictionary<(int x, int y), GameObject> _waterLineChildren = [];
// True once the component has started — gates editor-only bounds rescaling so it
// never fires during scene deserialization (which would clobber the saved bounds).
private bool _tilesStarted;
public float SubmersionOffset => -(WaterRipplesDisplacementA + WaterRipplesDisplacementB);
// World-adjusted snapped bounds (inches). Internal counterparts to the public
// WaterTileBounds* props, which must NEVER be written by code.
private Vector3 _waterTileBoundsMinimum;
private Vector3 _waterTileBoundsMaximum;
// Integer tile-grid indices, computed once in CalculateBounds (half-open range).
private int _tileMinX, _tileMaxX, _tileMinY, _tileMaxY;
// Cached bounds in world space (inches) — derived from the integer indices above.
private Vector2 _boundsMin;
private Vector2 _boundsMax;
private float _waterZ;
// Tile size in cm (all meshes are same size)
public IEnumerable<ModelRenderer> GetTileRenderers()
{
foreach ( var kvp in _tiles )
{
if ( kvp.Value.renderer.IsValid() )
yield return kvp.Value.renderer;
}
}
public void Bake()
{
CalculateRingLayout();
DestroyAllTiles();
CalculateBounds();
CreateWaterCollider();
BakeTiles();
}
public float GetWaterSurfaceZ() => _waterZ;
public float GetWaterSurfaceZWithOffset() => _waterZ + SubmersionOffset;
public BBox GetWorldBounds()
{
float minZ = WorldPosition.z + UnitConversion.CentimetersToInches( WaterTileBoundsMinimum.z );
return new BBox(
new Vector3( _boundsMin.x, _boundsMin.y, minZ ),
new Vector3( _boundsMax.x, _boundsMax.y, _waterZ ) );
}
// True when the whole water bounds are outside the camera frustum, i.e. every tile is
// culled. One frustum-vs-BBox test per frame, independent of tile count.
// (SceneObject.RenderingEnabled can't be used — it's a toggle you set, not a per-frame
// cull result, and the engine doesn't expose "was culled last frame" publicly. Frustum
// is the only reliable option; it misses occlusion, which isn't cheaply queryable.)
public bool AreAllTilesCulled()
{
var camera = Scene.Camera;
if ( !camera.IsValid() )
return true;
var frustum = camera.GetFrustum( new Rect( 0, 0, Screen.Width, Screen.Height ) );
return !frustum.IsInside( GetWorldBounds(), partially: true );
}
public bool IsPointInWaterVolume( Vector3 point )
{
var b = GetWorldBounds();
return point.x >= b.Mins.x && point.x <= b.Maxs.x &&
point.y >= b.Mins.y && point.y <= b.Maxs.y &&
point.z < GetWaterSurfaceZ() + ripples.Count && point.z >= b.Mins.z;
}
public bool IsPointFullySubmerged( Vector3 point )
{
var b = GetWorldBounds();
return point.x >= b.Mins.x && point.x <= b.Maxs.x &&
point.y >= b.Mins.y && point.y <= b.Maxs.y &&
point.z < GetWaterSurfaceZWithOffset() && point.z >= b.Mins.z;
}
// Body group layout convention on the "subdivision" body group:
// 0 : water-line variant of ring 0 (rendered only by the water-line camera)
// 1 : ring 0 (center, 2x2 quadrants)
// 2..n-2 : an end/corner pair per ring (subdivided_N_end, subdivided_N_corner)
// n-1 : the last subdivision (far filler, no variants)
// MaxRings and the far index are derived from the model in CalculateRingLayout.
private int MaxRings { get; set; } = 4;
private int FarBodyGroupIndex { get; set; } = 8;
// Derive the ring count from the model's "subdivision" body group, so adding or
// removing ring meshes in the model needs no code change.
private void CalculateRingLayout()
{
var subdivisionPart = WaterTileModel?.Parts.All
.FirstOrDefault( p => p.Name == "subdivision" );
// Minimum viable layout: waterline + ring 0 + one end/corner pair + far.
if ( subdivisionPart is null || subdivisionPart.Choices.Count < 5 )
return;
int choiceCount = subdivisionPart.Choices.Count;
if ( ( choiceCount - 3 ) % 2 != 0 )
Log.Warning( $"Water tile model '{WaterTileModel.ResourceName}' has {choiceCount} subdivision choices - expected waterline + ring0 + end/corner pairs + far. Ring layout may be wrong." );
FarBodyGroupIndex = choiceCount - 1;
MaxRings = ( choiceCount - 3 ) / 2 + 1;
}
private enum TileType
{
End, // Outline side
Corner // Outline corner
}
private int GetBodyGroupForRing( int ringLevel, TileType tileType )
{
if ( ringLevel == 0 )
return 1;
if ( ringLevel >= MaxRings )
return FarBodyGroupIndex;
int baseIndex = ( ringLevel - 1 ) * 2 + 2;
return tileType == TileType.Corner ? baseIndex + 1 : baseIndex;
}
private void UpdateRings()
{
if ( _tiles.Count == 0 )
return;
var camera = Scene.Camera;
if ( camera == null )
return;
// Half-tile bias shifts ring transitions to tile centers instead of tile edges.
// Snap the camera in box-local space (relative to the GameObject origin) to match the tile layout.
float tileSizeInches = UnitConversion.CentimetersToInches( WaterTileSize );
float halfTile = tileSizeInches * 0.5f;
float localCamX = camera.WorldPosition.x - WorldPosition.x;
float localCamY = camera.WorldPosition.y - WorldPosition.y;
float snappedX = MathF.Floor( ( localCamX - halfTile ) / tileSizeInches ) * tileSizeInches;
float snappedY = MathF.Floor( ( localCamY - halfTile ) / tileSizeInches ) * tileSizeInches;
Vector2 snappedPos = new( snappedX, snappedY );
if ( snappedPos != _lastSnappedPosition )
{
_lastSnappedPosition = snappedPos;
UpdateTileBodyGroups( snappedPos );
SyncWaterLineChildren();
}
}
private void DestroyAllTiles()
{
foreach ( var kvp in _tiles )
{
if ( kvp.Value.go.IsValid() )
kvp.Value.go.Destroy();
}
_tiles.Clear();
MarkRippleRenderersDirty();
}
public void CreateWaterLineMesh()
{
if ( _waterLineActive ) return;
_waterLineActive = true;
// If tiles are already live, sync immediately; otherwise OnUpdate will handle it on next snap.
if ( _tiles.Count > 0 )
SyncWaterLineChildren();
}
public void DestroyWaterLineMesh()
{
_waterLineActive = false;
foreach ( var kvp in _waterLineChildren )
{
if ( kvp.Value.IsValid() )
kvp.Value.Destroy();
}
_waterLineChildren.Clear();
MarkRippleRenderersDirty();
}
private void SyncWaterLineChildren()
{
if ( !_waterLineActive || WaterTileModel == null )
return;
var ringZeroKeys = new HashSet<(int x, int y)>();
foreach ( var kvp in _tiles )
{
if ( kvp.Value.renderer.IsValid() && kvp.Value.renderer.GetBodyGroup( "subdivision" ) == 1 )
ringZeroKeys.Add( kvp.Key );
}
var stale = new List<(int x, int y)>();
foreach ( var kvp in _waterLineChildren )
{
if ( ringZeroKeys.Contains( kvp.Key ) )
continue;
if ( kvp.Value.IsValid() )
kvp.Value.Destroy();
stale.Add( kvp.Key );
}
foreach ( var k in stale )
_waterLineChildren.Remove( k );
if ( stale.Count > 0 )
MarkRippleRenderersDirty();
foreach ( var key in ringZeroKeys )
{
if ( _waterLineChildren.ContainsKey( key ) )
continue;
if ( !_tiles.TryGetValue( key, out var tile ) || !tile.go.IsValid() )
continue;
var childGo = new GameObject( true, "waterline" ) { Parent = tile.go };
// Local-only render mesh - never include it in network snapshots.
childGo.Flags |= GameObjectFlags.NotNetworked;
childGo.NetworkMode = NetworkMode.Never;
childGo.Tags.Add( "water_line_mesh" );
var r = childGo.Components.Create<ModelRenderer>();
r.Model = WaterTileModel;
r.RenderType = ModelRenderer.ShadowRenderType.Off;
r.SceneObject.Attributes.Set( "BoundsMin", _boundsMin );
r.SceneObject.Attributes.Set( "BoundsMax", _boundsMax );
r.SceneObject.Attributes.Set( "WaterRipplesTextureA", WaterRipplesTextureA );
r.SceneObject.Attributes.Set( "Water Ripples Scale A", WaterRipplesScaleA );
r.SceneObject.Attributes.Set( "Water Ripples Rotation A", WaterRipplesRotationA );
r.SceneObject.Attributes.Set( "Water Ripples Speed A", WaterRipplesSpeedA );
r.SceneObject.Attributes.Set( "Water Ripples Displacement A", WaterRipplesDisplacementA );
r.SceneObject.Attributes.Set( "WaterRipplesTextureB", WaterRipplesTextureB );
r.SceneObject.Attributes.Set( "Water Ripples Scale B", WaterRipplesScaleB );
r.SceneObject.Attributes.Set( "Water Ripples Rotation B", WaterRipplesRotationB );
r.SceneObject.Attributes.Set( "Water Ripples Speed B", WaterRipplesSpeedB );
r.SceneObject.Attributes.Set( "Water Ripples Displacement B", WaterRipplesDisplacementB );
r.Attributes.SetCombo( "D_TRANSLUCENT", 0 );
r.Attributes.SetCombo( "D_Enable_Water_Line", 1 );
r.SetBodyGroup( "subdivision", 0 );
_waterLineChildren[key] = childGo;
MarkRippleRenderersDirty();
}
}
public void OnTriggerEnter( Collider other )
{
if ( !other.GameObject.Tags.Has( "particles" ) )
return;
other.GameObject.Destroy();
}
public void OnTriggerExit( Collider other )
{
}
private void CreateWaterCollider()
{
// Only create the trigger collider at runtime, not while editing the scene.
if ( Scene.IsEditor )
return;
// Use the snapped tile region so the trigger matches the spawned tiles.
// _waterTileBounds* are world-space; convert to local for the (parented) collider.
Vector3 localMin = _waterTileBoundsMinimum - WorldPosition;
Vector3 localMax = _waterTileBoundsMaximum - WorldPosition;
Vector3 center = ( localMin + localMax ) * 0.5f;
Vector3 size = localMax - localMin;
_waterCollider ??= Components.Get<BoxCollider>( FindMode.InSelf ) ?? Components.Create<BoxCollider>();
_waterCollider.Flags = ComponentFlags.Hidden;
_waterCollider.Center = center;
_waterCollider.Scale = size;
_waterCollider.IsTrigger = true;
_waterCollider.Static = true;
}
private void CalculateBounds()
{
var minInches = UnitConversion.CentimetersToInches( WaterTileBoundsMinimum );
var maxInches = UnitConversion.CentimetersToInches( WaterTileBoundsMaximum );
float tileSizeInches = UnitConversion.CentimetersToInches( WaterTileSize );
// Snap the extent to the tile grid in LOCAL space (relative to the GameObject origin).
// Tiles are anchored to the box, so they fill it exactly and follow the GameObject smoothly.
_tileMinX = (int)MathF.Floor( MathF.Round( minInches.x / tileSizeInches, 4 ) );
_tileMinY = (int)MathF.Floor( MathF.Round( minInches.y / tileSizeInches, 4 ) );
_tileMaxX = (int)MathF.Ceiling( MathF.Round( maxInches.x / tileSizeInches, 4 ) );
_tileMaxY = (int)MathF.Ceiling( MathF.Round( maxInches.y / tileSizeInches, 4 ) );
// Local-space snapped corners.
float minLocalX = _tileMinX * tileSizeInches;
float minLocalY = _tileMinY * tileSizeInches;
float maxLocalX = _tileMaxX * tileSizeInches;
float maxLocalY = _tileMaxY * tileSizeInches;
// World-space bounds (for collider-independent consumers, shader clipping, point queries).
_waterTileBoundsMinimum = new Vector3( WorldPosition.x + minLocalX, WorldPosition.y + minLocalY, WorldPosition.z + minInches.z );
_waterTileBoundsMaximum = new Vector3( WorldPosition.x + maxLocalX, WorldPosition.y + maxLocalY, WorldPosition.z + maxInches.z );
_boundsMin = new Vector2( _waterTileBoundsMinimum.x, _waterTileBoundsMinimum.y );
_boundsMax = new Vector2( _waterTileBoundsMaximum.x, _waterTileBoundsMaximum.y );
_waterZ = _waterTileBoundsMaximum.z;
}
private void DiscoverBakedTiles()
{
CalculateRingLayout();
// Rediscovered renderers need the surface attributes even if no property changed.
_surfaceAttributesDirty = true;
MarkRippleRenderersDirty();
foreach ( var child in GameObject.Children.ToList() )
{
if ( !child.Name.StartsWith( "tile_" ) )
continue;
var renderer = child.Components.Get<ModelRenderer>();
if ( renderer == null )
continue;
renderer.SceneObject.Attributes.Set( "BoundsMin", _boundsMin );
renderer.SceneObject.Attributes.Set( "BoundsMax", _boundsMax );
renderer.Attributes.SetCombo( "D_TRANSLUCENT", 1 );
renderer.Attributes.SetCombo( "D_Enable_Water_Line", 0 );
// Parse tileX, tileY from name "tile_X_Y"
var parts = child.Name.Split( '_' );
if ( parts.Length == 3 && int.TryParse( parts[1], out int tileX ) && int.TryParse( parts[2], out int tileY ) )
{
_tiles[(tileX, tileY)] = (child, renderer);
}
}
}
private void BakeTiles()
{
if ( WaterTileModel == null )
return;
float tileSizeInches = UnitConversion.CentimetersToInches( WaterTileSize );
float halfTile = tileSizeInches * 0.5f;
for ( int tileX = _tileMinX; tileX < _tileMaxX; tileX++ )
{
for ( int tileY = _tileMinY; tileY < _tileMaxY; tileY++ )
{
// Tile indices are local-grid; anchor positions to the GameObject origin.
float worldX = WorldPosition.x + tileX * tileSizeInches;
float worldY = WorldPosition.y + tileY * tileSizeInches;
Vector3 worldPosition = new Vector3( worldX + halfTile, worldY + halfTile, _waterZ );
SpawnTile( worldPosition, tileX, tileY );
}
}
}
private void UpdateTileBodyGroups( Vector2 snappedPos )
{
float tileSizeInches = UnitConversion.CentimetersToInches( WaterTileSize );
// First pass: calculate ring levels and find outermost ring
var tileData = new Dictionary<(int x, int y), (int ringLevel, int gridX, int gridY)>();
int outermostRenderedRing = 0;
foreach ( var kvp in _tiles )
{
int tileX = kvp.Key.x;
int tileY = kvp.Key.y;
// Box-local tile position (indices are local-grid; snappedPos is box-local).
float localX = tileX * tileSizeInches;
float localY = tileY * tileSizeInches;
int gridX = (int)MathF.Round( ( localX - snappedPos.x ) / tileSizeInches );
int gridY = (int)MathF.Round( ( localY - snappedPos.y ) / tileSizeInches );
int ringLevel = Math.Max( GetRingForCoord( gridX ), GetRingForCoord( gridY ) );
if ( ringLevel > MaxRings )
ringLevel = MaxRings;
if ( ringLevel > outermostRenderedRing )
outermostRenderedRing = ringLevel;
tileData[(tileX, tileY)] = (ringLevel, gridX, gridY);
}
// Second pass: update body groups
foreach ( var kvp in _tiles )
{
var renderer = kvp.Value.renderer;
if ( !renderer.IsValid() )
continue;
var (ringLevel, gridX, gridY) = tileData[kvp.Key];
TileType tileType = TileType.End;
float rotation = 0f;
if ( ringLevel == 0 )
{
if ( gridX == 0 && gridY == 0 ) rotation = 0f;
else if ( gridX == 1 && gridY == 0 ) rotation = 90f;
else if ( gridX == 0 && gridY == 1 ) rotation = 270f;
else if ( gridX == 1 && gridY == 1 ) rotation = 180f;
}
else if ( ringLevel < MaxRings )
{
// Rings are 1-tile-thick outlines - every tile is an End or a Corner.
if ( IsCornerTile( gridX, gridY, ringLevel ) )
{
tileType = TileType.Corner;
rotation = GetCornerRotation( gridX, gridY, ringLevel );
}
else
{
rotation = GetEdgeRotation( gridX, gridY, ringLevel );
}
}
renderer.SetBodyGroup( "subdivision", GetBodyGroupForRing( ringLevel, tileType ) );
kvp.Value.go.WorldRotation = Rotation.FromYaw( -90f + rotation );
}
}
private int GetRingForCoord( int gridCoord )
{
// Center (ring 0): coords 0, 1 (the 2x2 quadrant block)
// Ring N (1 tile thick outline): coord -N on the negative side, N+1 on the positive side
// Ring 1: -1, 2
// Ring 2: -2, 3
// Ring 3: -3, 4
if ( gridCoord >= 0 && gridCoord <= 1 )
return 0;
int ring = gridCoord < 0 ? -gridCoord : gridCoord - 1;
if ( ring > MaxRings )
return MaxRings + 1;
return ring;
}
private bool IsCornerTile( int gridX, int gridY, int ringLevel )
{
int outerMin = -ringLevel;
int outerMax = ringLevel + 1;
bool atCornerX = ( gridX == outerMin || gridX == outerMax );
bool atCornerY = ( gridY == outerMin || gridY == outerMax );
return atCornerX && atCornerY;
}
private float GetCornerRotation( int gridX, int gridY, int ringLevel )
{
int outerMin = -ringLevel;
int outerMax = ringLevel + 1;
if ( gridX == outerMin && gridY == outerMax ) return 270f;
if ( gridX == outerMax && gridY == outerMax ) return 180f;
if ( gridX == outerMax && gridY == outerMin ) return 90f;
if ( gridX == outerMin && gridY == outerMin ) return 0f;
return 0f;
}
private float GetEdgeRotation( int gridX, int gridY, int ringLevel )
{
int outerMin = -ringLevel;
int outerMax = ringLevel + 1;
if ( gridY == outerMax ) return 180f;
if ( gridX == outerMax ) return 90f;
if ( gridY == outerMin ) return 0f;
if ( gridX == outerMin ) return 270f;
return 0f;
}
private void SpawnTile( Vector3 worldPosition, int tileX, int tileY )
{
var tileGo = new GameObject( true, $"tile_{tileX}_{tileY}" );
tileGo.Parent = GameObject;
tileGo.Tags.Add( "reflection" ); // excluded from the reflection camera so water doesn't reflect itself
tileGo.WorldPosition = worldPosition;
tileGo.LocalScale = Vector3.One * ( WaterTileSize / 100f );
var renderer = tileGo.Components.Create<ModelRenderer>();
renderer.Model = WaterTileModel;
renderer.SceneObject.Attributes.Set( "BoundsMin", _boundsMin );
renderer.SceneObject.Attributes.Set( "BoundsMax", _boundsMax );
renderer.Attributes.SetCombo( "D_TRANSLUCENT", 1 );
renderer.Attributes.SetCombo( "D_Enable_Water_Line", 0 );
renderer.SetBodyGroup( "subdivision", FarBodyGroupIndex );
_tiles[(tileX, tileY)] = (tileGo, renderer);
// New renderer needs the surface attributes, ripple attributes and the
// reflection-mode combo even if nothing changed.
_surfaceAttributesDirty = true;
MarkRippleRenderersDirty();
_lastWaterReflectionPlanarRendering = null;
}
}
Game
library
namespace AtmokineticAssets;
// Soon TM
// This will be used for very large body of waters like oceans, lakes, etc...
public sealed partial class WaterController: Component
{
// [Property, Title("Area Model"), Feature("General"), Group("Properties"), ShowIf( nameof(WaterMode), WaterModeEnumeration.Area )]
// public Model WaterAreaModel { get; set; }
// [Property, Title("Bounds Minimum (cm)"), Feature("General"), Group("Properties"), ShowIf( nameof(WaterMode), WaterModeEnumeration.Area )]
// public Vector3 WaterAreaBoundsMinimum = new( -100, -100, -100 );
// [Property, Title("Bounds Maximum (cm)"), Feature("General"), Group("Properties"), ShowIf( nameof(WaterMode), WaterModeEnumeration.Area )]
// public Vector3 WaterAreaBoundsMaximum = new( 100, 100, 0 );
// [Button("Generate Water Area"), Feature("General"), Group("Properties"), ShowIf( nameof(WaterMode), WaterModeEnumeration.Area )]
// public void GenerateWaterArea()
// {
// }
}
Game
library
// namespace AtmokineticAssets;
// public sealed partial class WaterController : Component, Component.ExecuteInEditor
// {
// public bool ShowWaterReflectionCubemapSettings =>
// WaterReflectionMode == WaterReflectionModeEnumeration.Dynamic
// || ( WaterReflectionMode == WaterReflectionModeEnumeration.Static
// && WaterReflectionModeStaticMode == WaterReflectionModeStaticModeEnumeration.Cubemap );
// public bool ShowWaterReflectionCubemapResolutionDynamic => ShowWaterReflectionCubemapSettings && WaterReflectionCubemapResolutionMode == WaterReflectionCubemapResolutionModeEnumeration.Dynamic;
// public bool ShowWaterReflectionCubemapResolutionStatic => ShowWaterReflectionCubemapSettings && WaterReflectionCubemapResolutionMode == WaterReflectionCubemapResolutionModeEnumeration.Static;
// public bool ShowWaterReflectionCubemapFramesPerSecondDynamic => ShowWaterReflectionCubemapSettings && WaterReflectionCubemapFramesPerSecondMode == WaterReflectionCubemapFramesPerSecondModeEnumeration.Dynamic;
// public bool ShowWaterReflectionCubemapFramesPerSecondStatic => ShowWaterReflectionCubemapSettings && WaterReflectionCubemapFramesPerSecondMode == WaterReflectionCubemapFramesPerSecondModeEnumeration.Static;
// public enum WaterReflectionCubemapResolutionModeEnumeration
// {
// Dynamic,
// Static
// }
// [Property, Feature("Reflection"), Group("Cubemap"), Header("Resolution"), ShowIf( nameof(ShowWaterReflectionCubemapSettings), true )]
// public WaterReflectionCubemapResolutionModeEnumeration WaterReflectionCubemapResolutionMode {get;set;} = WaterReflectionCubemapResolutionModeEnumeration.Dynamic;
// [Property, Feature("Reflection"), Group("Cubemap"), ShowIf( nameof(ShowWaterReflectionCubemapResolutionDynamic), true )]
// public int WaterReflectionCubemapResolutionDynamicUltra {get;set;} = 1;
// [Property, Feature("Reflection"), Group("Cubemap"), ShowIf( nameof(ShowWaterReflectionCubemapResolutionDynamic), true )]
// public int WaterReflectionCubemapResolutionDynamicHigh {get;set;} = 2;
// [Property, Feature("Reflection"), Group("Cubemap"), ShowIf( nameof(ShowWaterReflectionCubemapResolutionDynamic), true )]
// public int WaterReflectionCubemapResolutionDynamicMedium {get;set;} = 4;
// [Property, Feature("Reflection"), Group("Cubemap"), ShowIf( nameof(ShowWaterReflectionCubemapResolutionDynamic), true )]
// public int WaterReflectionCubemapResolutionDynamicLow {get;set;} = 8;
// [Property, Feature("Reflection"), Group("Cubemap"), ShowIf( nameof(ShowWaterReflectionCubemapResolutionStatic), true )]
// public int WaterReflectionCubemapResolutionStatic {get;set;} = 2;
// /// <summary>
// ///
// /// </summary>
// public enum WaterReflectionCubemapFramesPerSecondModeEnumeration
// {
// Dynamic,
// Static
// }
// [Property, Feature("Reflection"), Group("Cubemap"), Header("Frames Per Second"), ShowIf( nameof(ShowWaterReflectionCubemapSettings), true )]
// public WaterReflectionCubemapFramesPerSecondModeEnumeration WaterReflectionCubemapFramesPerSecondMode {get;set;} = WaterReflectionCubemapFramesPerSecondModeEnumeration.Dynamic;
// [Property, Feature("Reflection"), Group("Cubemap"), ShowIf( nameof(ShowWaterReflectionCubemapFramesPerSecondDynamic), true )]
// public float WaterReflectionCubemapFramesPerSecondDynamicUltra {get;set;} = 120f;
// [Property, Feature("Reflection"), Group("Cubemap"), ShowIf( nameof(ShowWaterReflectionCubemapFramesPerSecondDynamic), true )]
// public float WaterReflectionCubemapFramesPerSecondDynamicHigh {get;set;} = 60f;
// [Property, Feature("Reflection"), Group("Cubemap"), ShowIf( nameof(ShowWaterReflectionCubemapFramesPerSecondDynamic), true )]
// public float WaterReflectionCubemapFramesPerSecondDynamicMedium {get;set;} = 30f;
// [Property, Feature("Reflection"), Group("Cubemap"), ShowIf( nameof(ShowWaterReflectionCubemapFramesPerSecondDynamic), true )]
// public float WaterReflectionCubemapFramesPerSecondDynamicLow {get;set;} = 15f;
// [Property, Feature("Reflection"), Group("Cubemap"), ShowIf( nameof(ShowWaterReflectionCubemapFramesPerSecondStatic), true )]
// public float WaterReflectionCubemapFramesPerSecondStatic {get;set;} = 60f;
// }
Game
library
namespace AtmokineticAssets;
public sealed partial class WaterController : Component
{
private void DrawWaterTileGizmo()
{
if ( !EnableWaterTileGizmo )
return;
if ( !Gizmo.IsSelected && !Gizmo.IsHovered )
return;
// Raw box from the user's typed bounds — used for the editing handles.
var rawBox = new BBox(
UnitConversion.CentimetersToInches( WaterTileBoundsMinimum ),
UnitConversion.CentimetersToInches( WaterTileBoundsMaximum ) );
// Snapped box — the actual world-grid region the tiles fill — used for drawing.
var snappedBox = GetSnappedLocalBox( rawBox );
DrawWaterTileBounds( snappedBox );
DrawWaterTileHandles( rawBox );
}
// Snaps the bounds extent to the tile grid relative to the GameObject's own origin,
// so the box keeps its tile-sized footprint but follows the GameObject smoothly (no world-grid jumping).
private BBox GetSnappedLocalBox( BBox localBox )
{
float tileSizeInches = UnitConversion.CentimetersToInches( WaterTileSize );
if ( tileSizeInches <= 0 )
return localBox;
float minX = MathF.Floor( MathF.Round( localBox.Mins.x / tileSizeInches, 4 ) ) * tileSizeInches;
float minY = MathF.Floor( MathF.Round( localBox.Mins.y / tileSizeInches, 4 ) ) * tileSizeInches;
float maxX = MathF.Ceiling( MathF.Round( localBox.Maxs.x / tileSizeInches, 4 ) ) * tileSizeInches;
float maxY = MathF.Ceiling( MathF.Round( localBox.Maxs.y / tileSizeInches, 4 ) ) * tileSizeInches;
return new BBox(
new Vector3( minX, minY, localBox.Mins.z ),
new Vector3( maxX, maxY, localBox.Maxs.z ) );
}
private void DrawWaterTileBounds( BBox box )
{
Gizmo.Draw.LineThickness = 1;
var tint = Gizmo.IsSelected ? Gizmo.Colors.Blue : Gizmo.Colors.Blue.WithAlpha( 0.4f );
for ( int i = 0; i < 2; i++ )
{
bool xray = i == 0;
Gizmo.Draw.IgnoreDepth = xray;
float alpha = xray ? 0.2f : 1f;
Gizmo.Draw.Color = tint.WithAlpha( tint.a * alpha );
Gizmo.Draw.LineBBox( box );
Gizmo.Draw.Color = tint.WithAlpha( 0.05f * alpha );
Gizmo.Draw.SolidBox( box );
}
}
private void DrawWaterTileHandles( BBox box )
{
Gizmo.Hitbox.BBox( box );
// Disable the editor's grid snap for this control so it doesn't double up with our tile snap.
var previousSnap = Gizmo.Settings.SnapToGrid;
Gizmo.Settings.SnapToGrid = false;
if ( Gizmo.Control.BoundingBox( "water_tile_bounds", box, out var newBox ) )
{
var minCm = UnitConversion.InchesToCentimeters( newBox.Mins );
var maxCm = UnitConversion.InchesToCentimeters( newBox.Maxs );
WaterTileBoundsMinimum = SnapChangedAxes( WaterTileBoundsMinimum, minCm, WaterTileSize, floor: true );
WaterTileBoundsMaximum = SnapChangedAxes( WaterTileBoundsMaximum, maxCm, WaterTileSize, floor: false );
}
Gizmo.Settings.SnapToGrid = previousSnap;
}
private float ResizeWaterTileBounds( float oldSize, float newSize )
{
// Skip during deserialization / before start — only rescale on genuine edits.
if ( !_tilesStarted || oldSize == newSize || newSize <= 0 || oldSize <= 0 )
return newSize;
float scale = newSize / oldSize;
// Scale X/Y of the bounds so the box stays the same tile count.
WaterTileBoundsMinimum = new Vector3( WaterTileBoundsMinimum.x * scale, WaterTileBoundsMinimum.y * scale, WaterTileBoundsMinimum.z );
WaterTileBoundsMaximum = new Vector3( WaterTileBoundsMaximum.x * scale, WaterTileBoundsMaximum.y * scale, WaterTileBoundsMaximum.z );
return newSize;
}
private static Vector3 SnapChangedAxes( Vector3 oldValue, Vector3 newValue, float tileSize, bool floor )
{
if ( tileSize <= 0 )
return newValue;
float SnapAxis( float n ) =>
( floor ? MathF.Floor( n / tileSize ) : MathF.Ceiling( n / tileSize ) ) * tileSize;
return new Vector3(
oldValue.x.AlmostEqual( newValue.x ) ? oldValue.x : SnapAxis( newValue.x ),
oldValue.y.AlmostEqual( newValue.y ) ? oldValue.y : SnapAxis( newValue.y ),
oldValue.z.AlmostEqual( newValue.z ) ? oldValue.z : newValue.z );
}
}
Game
library
namespace AtmokineticAssets;
public sealed partial class WaterController : Component, Component.ExecuteInEditor
{
/// <summary>
/// How heavy the fluid is. Higher values make things float more.
/// Water is 1000, oil is around 900, something very heavy like mercury is 13600.
/// </summary>
[Property, Feature("Buoyancy"), Group( "Fluid" ), Title("Fluid Density"), Range( 0, 15000 )]
[Sync]
public float WaterBuoyancyFluidDensity { get; set; } = 1000.0f;
/// <summary>
/// Direction and speed of the water current.
/// </summary>
[Property, Feature("Buoyancy"), Group( "Fluid" ), Title("Fluid Velocity")]
[Sync]
public Vector3 WaterBuoyancyFluidVelocity { get; set; } = Vector3.Zero;
/// <summary>
/// How much the fluid slows down movement.
/// </summary>
[Property, Feature("Buoyancy"), Group( "Fluid" ), Title("Linear Drag"),Range( 0, 1 )]
[Sync]
public float WaterBuoyancyLinearDrag { get; set; } = 0.1f;
/// <summary>
/// How much the fluid slows down spinning.
/// </summary>
[Property, Feature("Buoyancy"), Group( "Fluid" ), Title("Angular Drag"), Range( 0, 2 )]
[Sync]
public float WaterBuoyancyAngularDrag { get; set; } = 0.5f;
/// <summary>
/// Moves the water surface up or down.
/// </summary>
[Property, Feature("Buoyancy"), Group( "Surface" ), Title("Surface Offset")]
[Sync]
public float WaterBuoyancySurfaceOffset { get; set; } = 0.0f;
/// <summary>
/// How tall the waves are. Set to 0 for calm water.
/// </summary>
[Property, Feature("Buoyancy"), Group( "Surface" ), Title("Wave Amplitude"), Range( 0, 100 )]
[Sync]
public float WaterBuoyancyWaveAmplitude { get; set; } = 0.1f;
/// <summary>
/// How fast the waves move.
/// </summary>
[Property, Feature("Buoyancy"), Group( "Surface" ), Title("Wave Frequency"), Range( 0, 5 )]
[Sync]
public float WaterBuoyancyWaveFrequency { get; set; } = 0.5f;
/// <summary>
/// Objects with any of these tags won't be affected.
/// </summary>
[Property, Feature("Buoyancy"), Group( "Filtering" ), Title("Ignore Tags")]
[Sync]
public TagSet WaterBuoyancyIgnoreTags { get; set; } = [];
private WaterVolume _waterBuoyancy;
public void CreateWaterBuoyancy()
{
_waterBuoyancy ??= Components.Get<Sandbox.WaterVolume>( FindMode.InSelf ) ?? Components.Create<Sandbox.WaterVolume>();
_waterBuoyancy.Flags |= ComponentFlags.Hidden;
_waterBuoyancy.FluidDensity = WaterBuoyancyFluidDensity;
_waterBuoyancy.FluidVelocity = WaterBuoyancyFluidVelocity;
_waterBuoyancy.LinearDrag = WaterBuoyancyLinearDrag;
_waterBuoyancy.AngularDrag = WaterBuoyancyAngularDrag;
_waterBuoyancy.SurfaceOffset = _waterZ - WorldPosition.z + WaterBuoyancySurfaceOffset;
_waterBuoyancy.WaveAmplitude = WaterBuoyancyWaveAmplitude;
_waterBuoyancy.WaveFrequency = WaterBuoyancyWaveFrequency;
_waterBuoyancy.IgnoreTags = WaterBuoyancyIgnoreTags;
}
}
Game
library
namespace AtmokineticAssets;
public sealed partial class GraphicsSettings : Component, Component.ExecuteInEditor
{
//Terrain Settings
public enum TerrainRenderingModeEnumeration
{
[Title("Parallax Occlusion Mapping")]
ParallaxOcclusionMapping,
[Title("Bump Offset")]
BumpOffset,
Standard
}
//[Property, Feature("Terrain"), Title("Rendering")]
public TerrainRenderingModeEnumeration TerrainRenderingMode { get; set; } = TerrainRenderingModeEnumeration.Standard;
public enum TerrainRenderingParallaxOcclusionMappingQualityEnumeration
{
Ultra,
High,
Medium,
Low
}
//[Property, Feature("Terrain"), Title("Quality")]
[ShowIf(nameof(TerrainRenderingMode), TerrainRenderingModeEnumeration.ParallaxOcclusionMapping)]
public TerrainRenderingParallaxOcclusionMappingQualityEnumeration TerrainRenderingParallaxOcclusionMapping { get; set; } = TerrainRenderingParallaxOcclusionMappingQualityEnumeration.Medium;
public enum TerrainRenderingBumpOffsetQualityEnumeration
{
Ultra,
High,
Medium,
Low
}
//[Property, Feature("Terrain"), Title("Quality")]
[ShowIf(nameof(TerrainRenderingMode), TerrainRenderingModeEnumeration.BumpOffset)]
public TerrainRenderingParallaxOcclusionMappingQualityEnumeration TerrainRenderingBumpOffset { get; set; } = TerrainRenderingParallaxOcclusionMappingQualityEnumeration.Medium;
// public void ChangeTerrainSettings()
// {
// // Terrain rendering mode: 0 = POM, 1 = Bump Offset, 2 = Standard
// int terrainRenderingMode = TerrainRenderingMode switch
// {
// TerrainRenderingModeEnumeration.ParallaxOcclusionMapping => 0,
// TerrainRenderingModeEnumeration.BumpOffset => 1,
// TerrainRenderingModeEnumeration.Standard => 2,
// _ => 1
// };
// // Quality: Ultra = 1, High = 2, Medium = 4, Low = 8
// float quality;
// switch ( TerrainRenderingMode )
// {
// case TerrainRenderingModeEnumeration.ParallaxOcclusionMapping:
// quality = TerrainRenderingParallaxOcclusionMapping switch
// {
// TerrainRenderingParallaxOcclusionMappingQualityEnumeration.Ultra => 1f,
// TerrainRenderingParallaxOcclusionMappingQualityEnumeration.High => 2f,
// TerrainRenderingParallaxOcclusionMappingQualityEnumeration.Medium => 4f,
// TerrainRenderingParallaxOcclusionMappingQualityEnumeration.Low => 8f,
// _ => 4f
// };
// break;
// case TerrainRenderingModeEnumeration.BumpOffset:
// quality = TerrainRenderingBumpOffset switch
// {
// TerrainRenderingParallaxOcclusionMappingQualityEnumeration.Ultra => 1f,
// TerrainRenderingParallaxOcclusionMappingQualityEnumeration.High => 2f,
// TerrainRenderingParallaxOcclusionMappingQualityEnumeration.Medium => 4f,
// TerrainRenderingParallaxOcclusionMappingQualityEnumeration.Low => 8f,
// _ => 4f
// };
// break;
// default:
// quality = 4f;
// break;
// }
// // Enumerate live — objects spawned after startup (e.g. baked water tiles) must be included.
// foreach ( var sceneObject in Scene.SceneWorld.SceneObjects )
// {
// if ( sceneObject is null || !sceneObject.IsValid() ) continue;
// sceneObject.Attributes.Set( "D_Rendering", terrainRenderingMode );
// sceneObject.Attributes.Set( "Quality", quality );
// }
// }
}
Game
library
namespace AtmokineticAssets;
public static class UnitConversion
{
//Inches To Meters
public static float InchesToMeters( float inches )
{
return inches * 0.0254f;
}
public static Vector2 InchesToMeters( Vector2 inches )
{
return inches * 0.0254f;
}
public static Vector3 InchesToMeters( Vector3 inches )
{
return inches * 0.0254f;
}
public static Vector4 InchesToMeters( Vector4 inches )
{
return inches * 0.0254f;
}
//Inches To Centimeters
public static float InchesToCentimeters( float inches)
{
return inches * 2.54f;
}
public static Vector2 InchesToCentimeters( Vector2 inches)
{
return inches * 2.54f;
}
public static Vector3 InchesToCentimeters( Vector3 inches)
{
return inches * 2.54f;
}
public static Vector4 InchesToCentimeters( Vector4 inches)
{
return inches * 2.54f;
}
//Centimeters To Inches
public static float CentimetersToInches( float centimeters )
{
return centimeters / 2.54f;
}
public static Vector2 CentimetersToInches( Vector2 centimeters )
{
return centimeters / 2.54f;
}
public static Vector3 CentimetersToInches( Vector3 centimeters )
{
return centimeters / 2.54f;
}
public static Vector4 CentimetersToInches( Vector4 centimeters )
{
return centimeters / 2.54f;
}
//Meters To Centimeters
public static float MetersToCentimeters( float meters )
{
return meters * 100f;
}
//Meters To Inches
public static float MetersToInches(float meters)
{
return meters / 0.0254f;
}
}
ali3nsystems.lib-atmokinetic_assets
/
Water/Modes/WorkInProgress/Area/WaterController.WaterArea.Gizmo.cs
Game
library
namespace AtmokineticAssets;
public sealed partial class WaterController: Component
{
// [Property, Title("Enable Water Gizmo"),Feature( "General" ), ShowIf( nameof(WaterMode), WaterModeEnumeration.Area)]
// public bool EnableWaterAreaGizmo { get; set; } = true;
}
Game
library
namespace AtmokineticAssets;
// Also add:
//Ice
//Caustics
public sealed partial class WaterController : Component, Component.ExecuteInEditor
{
// Surface
[Property, Feature("Attributes"), Group("Surface"), Title("Color"), Order(1)]
[Sync]
public Color WaterSurfaceColor { get; set { field = value; _surfaceAttributesDirty = true; } } = Color.Parse( "#c5d9ff" ).Value;
[Property, Feature("Attributes"), Group("Surface"), Title("Blur"), Order(2)]
[Sync]
public float WaterSurfaceBlur { get; set { field = value; _surfaceAttributesDirty = true; } } = 0;
// Reflection
[Property, Feature("Attributes"), Group("Reflection"), Title("Color"), Order(3)]
[Sync]
public Color WaterReflectionColor { get; set { field = value; _surfaceAttributesDirty = true; } } = Color.Parse( "#c5d9ff" ).Value;
[Property, Feature("Attributes"), Group("Reflection"), Title("Blur"), Order(4)]
[Sync]
public float WaterReflectionBlur { get; set { field = value; _surfaceAttributesDirty = true; } } = 0;
[Property, Feature("Attributes"), Group("Reflection"), Title("Distance"), Order(5)]
[Sync]
public float WaterReflectionDistance { get; set { field = value; _surfaceAttributesDirty = true; } } = 4f;
[Property, Feature("Attributes"), Group("Reflection"), Title("Threshold"), Order(6)]
[Sync]
public Vector2 WaterReflectionThreshold { get; set { field = value; _surfaceAttributesDirty = true; } } = new Vector2( 0, 0.25f );
// Shoreline
[Property, Feature("Attributes"), Group("Shoreline"), Title("Fade"), Order(7)]
[Sync]
public float WaterShorelineFade { get; set { field = value; _surfaceAttributesDirty = true; } } = 2f;
// Tension
[Property, Feature("Attributes"), Group("Tension"), Title("Distance"), Order(8)]
[Sync]
public float WaterTensionDistance { get; set { field = value; _surfaceAttributesDirty = true; } } = 2f;
[Property, Feature("Attributes"), Group("Tension"), Title("Refraction"), Order(9)]
[Sync]
public float WaterTensionRefraction { get; set { field = value; _surfaceAttributesDirty = true; } } = 0.1f;
// Texture A
[Property, Feature("Attributes"), Group("Water Ripples A"), Title("Texture A"), Order(10)]
[Sync]
public Texture WaterRipplesTextureA { get; set { field = value; _surfaceAttributesDirty = true; } } = Texture.Load("textures/tex-water_ripples_1.vtex");
[Property, Feature("Attributes"), Group("Water Ripples A"), Title("Scale A"), Order(11)]
[Sync]
public float WaterRipplesScaleA { get; set { field = value; _surfaceAttributesDirty = true; } } = 0.01f;
[Property, Feature("Attributes"), Group("Water Ripples A"), Title("Rotation A"), Order(12)]
[Sync]
public float WaterRipplesRotationA { get; set { field = value; _surfaceAttributesDirty = true; } } = 0f;
[Property, Feature("Attributes"), Group("Water Ripples A"), Title("Speed A"), Order(13)]
[Sync]
public float WaterRipplesSpeedA { get; set { field = value; _surfaceAttributesDirty = true; } } = 0.1f;
[Property, Feature("Attributes"), Group("Water Ripples A"), Title("Refraction A"), Order(14)]
[Sync]
public float WaterRipplesRefractionA { get; set { field = value; _surfaceAttributesDirty = true; } } = 0.05f;
[Property, Feature("Attributes"), Group("Water Ripples A"), Title("Displacement A"), Order(15)]
[Sync]
public float WaterRipplesDisplacementA { get; set { field = value; _surfaceAttributesDirty = true; } } = 0.5f;
// Texture B
[Property, Feature("Attributes"), Group("Water Ripples B"), Title("Texture B"), Order(16)]
[Sync]
public Texture WaterRipplesTextureB { get; set { field = value; _surfaceAttributesDirty = true; } } = Texture.Load("textures/tex-water_ripples_2.vtex");
[Property, Feature("Attributes"), Group("Water Ripples B"), Title("Scale B"), Order(17)]
[Sync]
public float WaterRipplesScaleB { get; set { field = value; _surfaceAttributesDirty = true; } } = 0.01f;
[Property, Feature("Attributes"), Group("Water Ripples B"), Title("Rotation B"), Order(18)]
[Sync]
public float WaterRipplesRotationB { get; set { field = value; _surfaceAttributesDirty = true; } } = 180f;
[Property, Feature("Attributes"), Group("Water Ripples B"), Title("Speed B"), Order(19)]
[Sync]
public float WaterRipplesSpeedB { get; set { field = value; _surfaceAttributesDirty = true; } } = 0.1f;
[Property, Feature("Attributes"), Group("Water Ripples B"), Title("Refraction B"), Order(20)]
[Sync]
public float WaterRipplesRefractionB { get; set { field = value; _surfaceAttributesDirty = true; } } = 0.05f;
[Property, Feature("Attributes"), Group("Water Ripples B"), Title("Displacement B"), Order(21)]
[Sync]
public float WaterRipplesDisplacementB { get; set { field = value; _surfaceAttributesDirty = true; } } = 0.5f;
// Starts dirty so the first WaterSurfaceAttributes call always pushes.
private bool _surfaceAttributesDirty = true;
[Rpc.Broadcast]
public void WaterSurfaceAttributes()
{
if ( !_surfaceAttributesDirty )
return;
_surfaceAttributesDirty = false;
foreach ( var renderer in GetTileRenderers() )
{
if (WaterSurfaceBlur != 0)
{
renderer.Attributes.SetCombo("D_Enable_Water_Surface_Blur", 1);
}
else
{
renderer.Attributes.SetCombo("D_Enable_Water_Surface_Blur", 0);
}
if (WaterReflectionBlur != 0)
{
renderer.Attributes.SetCombo("D_Enable_Water_Reflection_Blur", 1);
}
else
{
renderer.Attributes.SetCombo("D_Enable_Water_Reflection_Blur", 0);
}
// Surface
renderer.Attributes.Set( "Water Surface Color", WaterSurfaceColor );
renderer.Attributes.Set( "Water Surface Blur", WaterSurfaceBlur );
// Reflection
renderer.Attributes.Set( "Water Reflection Color", WaterReflectionColor );
renderer.Attributes.Set( "Water Reflection Blur", WaterReflectionBlur );
renderer.Attributes.Set( "Water Reflection Distance", WaterReflectionDistance );
renderer.Attributes.Set( "Water Reflection Threshold", WaterReflectionThreshold );
// Shoreline
renderer.Attributes.Set( "Water Shoreline Fade", WaterShorelineFade );
// Tension
renderer.Attributes.Set( "Water Tension Distance", WaterTensionDistance );
renderer.Attributes.Set( "Water Tension Refraction", WaterTensionRefraction );
// Water Ripples A
renderer.Attributes.Set( "WaterRipplesTextureA", WaterRipplesTextureA );
renderer.Attributes.Set( "Water Ripples Scale A", WaterRipplesScaleA );
renderer.Attributes.Set( "Water Ripples Rotation A", WaterRipplesRotationA );
renderer.Attributes.Set( "Water Ripples Speed A", WaterRipplesSpeedA );
renderer.Attributes.Set( "Water Ripples Refraction A", WaterRipplesRefractionA );
renderer.Attributes.Set( "Water Ripples Displacement A", WaterRipplesDisplacementA );
// Water Ripples B
renderer.Attributes.Set( "WaterRipplesTextureB", WaterRipplesTextureB );
renderer.Attributes.Set( "Water Ripples Scale B", WaterRipplesScaleB );
renderer.Attributes.Set( "Water Ripples Rotation B", WaterRipplesRotationB );
renderer.Attributes.Set( "Water Ripples Speed B", WaterRipplesSpeedB );
renderer.Attributes.Set( "Water Ripples Refraction B", WaterRipplesRefractionB );
renderer.Attributes.Set( "Water Ripples Displacement B", WaterRipplesDisplacementB );
}
}
}
Game
library
namespace AtmokineticAssets;
[Title("Atmokinetic Assets - Environment Controller")]
[Category("Atmokinetic Assets")]
[Icon("cloud")]
public sealed partial class EnvironmentController : Component
{
protected override void OnAwake()
{
CacheWaterControllers();
}
protected override void OnUpdate()
{
}
// public enum PrecipitationModeEnumeration
// {
// None,
// Rain,
// Snow
// }
// [Property, Feature("Weather"), Sync]
// public PrecipitationModeEnumeration PrecipitationMode { get; set; } = PrecipitationModeEnumeration.None;
// [Property, Feature("Weather")] PrefabScene PrecipitationController { get; set;}
// [Property, Feature("Weather"), Range( 0f, 1f ), Sync]
// public float WaterPuddles { get; set; } = 0f;
// [Property, Feature("Weather"), Range( 0.0001f, 1f ), Sync]
// public float WaterLevel { get; set; } = 0f;
// [Property, Feature("Weather"), Range( 0, 4 ), Step(1), Sync]
// public float PrecipitationIntensity { get; set; } = 0f;
// [Property,Feature("Time"), Sync]
// public GameObject Sun {get;set;}
// [Property, Feature("Time"), Range(0,24), Sync]
// public float Hour {get;set;} = 6f;
// [Property, Feature("Time"), Range(1,31), Step(1)]
// public float Day {get;set;} = 0f;
// [Property, Feature("Time"), Range(1,12), Step(1)]
// public float Month {get;set;} = 0f;
// [Property, Feature("Time"), Step(1)]
// public float Year {get;set;} = 0f;
// private float _lastWaterPuddlesValue = -1f;
// private float _lastPrecipitationIntensityValue = -1f;
// private float _lastPrecipitationIntensityValueForParticles = -1f;
// private float _lastWaterLevelValue = -1f;
// private List<SceneObject> _cachedSceneObjects;
// private List<EnvmapProbe> _cachedEnvmapProbes;
// // Rain Precipitation
// private GameObject _rainGameObject;
// private ParticleEffect _particleEffectRain;
// private PrecipitationController _precipitationControllerRain;
// private PrecipitationControllerEmitter _precipitationControllerRainEmitter;
// private bool _activePrecipitationControllerRain;
// // Snow Precipitation
// private GameObject _snowGameObject;
// private ParticleEffect _particleEffectSnow;
// private PrecipitationController _precipitationControllerSnow;
// private PrecipitationControllerEmitter _precipitationControllerSnowEmitter;
// private bool _activePrecipitationControllerSnow;
// private PrecipitationModeEnumeration _lastPrecipitationMode = PrecipitationModeEnumeration.None;
// // Lerp state
// private float _startMaxParticles;
// private float _startRate;
// private float _targetMaxParticles;
// private float _targetRate;
// private float _transitionProgress = 1f;
// private float _lastHour = -1f;
// protected override void OnStart()
// {
// // Cache all scene objects on startup
// _cachedSceneObjects = Scene.SceneWorld.SceneObjects.Where( x => x != null ).ToList();
// // Cache all environment map probes
// _cachedEnvmapProbes = Scene.GetAllComponents<EnvmapProbe>().ToList();
// }
// [Rpc.Broadcast]
// protected override void OnUpdate()
// {
// HandlePrecipitationModeChange();
// PrecipitationEffects();
// TimeEffects();
// }
// protected override void DrawGizmos()
// {
// if (_cachedSceneObjects == null)
// {
// _cachedSceneObjects = Scene.SceneWorld.SceneObjects.Where( x => x != null ).ToList();
// }
// if (_cachedEnvmapProbes == null)
// {
// _cachedEnvmapProbes = Scene.GetAllComponents<EnvmapProbe>().ToList();
// }
// if ( WaterPuddles != _lastWaterPuddlesValue || WaterLevel != _lastWaterLevelValue || PrecipitationIntensity != _lastPrecipitationIntensityValue)
// {
// _lastWaterPuddlesValue = WaterPuddles;
// _lastWaterLevelValue = WaterLevel;
// _lastPrecipitationIntensityValue = PrecipitationIntensity;
// foreach ( var sceneModel in _cachedSceneObjects )
// {
// sceneModel.Attributes.Set( "Water Puddles Accumulation", WaterPuddles );
// sceneModel.Attributes.Set( "Water Plane Level", WaterLevel );
// sceneModel.Attributes.Set( "Rain Ripples Intensity", PrecipitationIntensity );
// }
// }
// TimeEffects();
// }
// private void HandlePrecipitationModeChange()
// {
// if (PrecipitationMode == _lastPrecipitationMode) return;
// // Only host spawns networked objects
// // Handle mode transitions
// if (_lastPrecipitationMode == PrecipitationModeEnumeration.Rain)
// {
// TransitionOutRain();
// }
// else if (_lastPrecipitationMode == PrecipitationModeEnumeration.Snow)
// {
// TransitionOutSnow();
// }
// // Spawn new precipitation if not None
// if (PrecipitationMode == PrecipitationModeEnumeration.Rain)
// {
// SpawnRain();
// }
// else if (PrecipitationMode == PrecipitationModeEnumeration.Snow)
// {
// SpawnSnow();
// }
// _lastPrecipitationMode = PrecipitationMode;
// }
// private void SpawnRain()
// {
// // Reset transition state to trigger fresh lerp
// _lastPrecipitationIntensityValue = -1f;
// _lastPrecipitationIntensityValueForParticles = -1f;
// // If rain instance already exists (transitioning out), reactivate it
// if (_precipitationControllerRain.IsValid())
// {
// _activePrecipitationControllerRain = true;
// return;
// }
// if (!PrecipitationController.IsValid()) return;
// var clonedGameObject = PrecipitationController.Clone();
// clonedGameObject.Name = "Precipitation Controller (Rain)";
// _rainGameObject = clonedGameObject;
// _particleEffectRain = clonedGameObject.GetComponent<ParticleEffect>();
// _precipitationControllerRain = clonedGameObject.GetComponent<PrecipitationController>();
// _precipitationControllerRainEmitter = clonedGameObject.GetComponent<PrecipitationControllerEmitter>();
// _precipitationControllerRain.PrecipitationMode = AtmokineticAssets.PrecipitationController.PrecipitationModeEnumeration.Rain;
// _particleEffectRain.MaxParticles = 0;
// _precipitationControllerRainEmitter.Rate = 0;
// _activePrecipitationControllerRain = true;
// // Reset transition to ensure smooth lerp from current state
// }
// private void SpawnSnow()
// {
// // Reset transition state to trigger fresh lerp
// _lastPrecipitationIntensityValue = -1f;
// _lastPrecipitationIntensityValueForParticles = -1f;
// // If snow instance already exists (transitioning out), reactivate it
// if (_precipitationControllerSnow.IsValid())
// {
// _activePrecipitationControllerSnow = true;
// return;
// }
// if (!PrecipitationController.IsValid()) return;
// var clonedGameObject = PrecipitationController.Clone();
// clonedGameObject.Name = "Precipitation Controller (Snow)";
// _snowGameObject = clonedGameObject;
// _particleEffectSnow = clonedGameObject.GetComponent<ParticleEffect>();
// _precipitationControllerSnow = clonedGameObject.GetComponent<PrecipitationController>();
// _precipitationControllerSnowEmitter = clonedGameObject.GetComponent<PrecipitationControllerEmitter>();
// _precipitationControllerSnow.PrecipitationMode = AtmokineticAssets.PrecipitationController.PrecipitationModeEnumeration.Snow;
// _particleEffectSnow.MaxParticles = 0;
// _precipitationControllerSnowEmitter.Rate = 0;
// _activePrecipitationControllerSnow = true;
// // Reset transition to ensure smooth lerp from current state
// }
// private void TransitionOutRain() => _activePrecipitationControllerRain = false;
// private void TransitionOutSnow() => _activePrecipitationControllerSnow = false;
// public void PrecipitationEffects()
// {
// if (_precipitationControllerRain.IsValid())
// {
// if (_activePrecipitationControllerRain)
// {
// UpdatePrecipitationTransition(
// _particleEffectRain,
// _precipitationControllerRainEmitter,
// _precipitationControllerRain.RainIntensityMaximumParticlesLight,
// _precipitationControllerRain.RainIntensityRateLight,
// _precipitationControllerRain.RainIntensityMaximumParticlesModerate,
// _precipitationControllerRain.RainIntensityRateModerate,
// _precipitationControllerRain.RainIntensityMaximumParticlesHeavy,
// _precipitationControllerRain.RainIntensityRateHeavy,
// _precipitationControllerRain.RainIntensityMaximumParticlesExtreme,
// _precipitationControllerRain.RainIntensityRateExtreme,
// _precipitationControllerRain.RainParticleTransition
// );
// }
// else
// {
// TransitionPrecipitationToZero(_particleEffectRain, _precipitationControllerRainEmitter, _precipitationControllerRain.RainParticleTransition);
// }
// }
// if (_precipitationControllerSnow.IsValid())
// {
// if (_activePrecipitationControllerSnow)
// {
// UpdatePrecipitationTransition(
// _particleEffectSnow,
// _precipitationControllerSnowEmitter,
// _precipitationControllerSnow.SnowIntensityMaximumParticlesLight,
// _precipitationControllerSnow.SnowIntensityRateLight,
// _precipitationControllerSnow.SnowIntensityMaximumParticlesModerate,
// _precipitationControllerSnow.SnowIntensityRateModerate,
// _precipitationControllerSnow.SnowIntensityMaximumParticlesHeavy,
// _precipitationControllerSnow.SnowIntensityRateHeavy,
// _precipitationControllerSnow.SnowIntensityMaximumParticlesExtreme,
// _precipitationControllerSnow.SnowIntensityRateExtreme,
// _precipitationControllerSnow.SnowParticleTransition
// );
// }
// else
// {
// TransitionPrecipitationToZero(_particleEffectSnow, _precipitationControllerSnowEmitter, _precipitationControllerSnow.SnowParticleTransition);
// }
// }
// if ( WaterPuddles != _lastWaterPuddlesValue || WaterLevel != _lastWaterLevelValue || PrecipitationIntensity != _lastPrecipitationIntensityValue)
// {
// _lastWaterPuddlesValue = WaterPuddles;
// _lastWaterLevelValue = WaterLevel;
// _lastPrecipitationIntensityValue = PrecipitationIntensity;
// foreach ( var sceneModel in _cachedSceneObjects )
// {
// sceneModel.Attributes.Set( "Water Puddles Accumulation", WaterPuddles );
// sceneModel.Attributes.Set( "Water Plane Level", WaterLevel );
// sceneModel.Attributes.Set( "Rain Ripples Intensity", PrecipitationIntensity );
// }
// }
// }
// private void UpdatePrecipitationTransition(
// ParticleEffect particleEffect,
// PrecipitationControllerEmitter emitter,
// int lightMaxParticles, int lightRate,
// int moderateMaxParticles, int moderateRate,
// int heavyMaxParticles, int heavyRate,
// int extremeMaxParticles, int extremeRate,
// float transitionDuration)
// {
// if ( PrecipitationIntensity != _lastPrecipitationIntensityValueForParticles )
// {
// _lastPrecipitationIntensityValueForParticles = PrecipitationIntensity;
// _startMaxParticles = particleEffect.MaxParticles;
// _startRate = emitter.Rate.ConstantValue;
// int intensity = (int)PrecipitationIntensity;
// (_targetMaxParticles, _targetRate) = intensity switch
// {
// 1 => (lightMaxParticles, lightRate),
// 2 => (moderateMaxParticles, moderateRate),
// 3 => (heavyMaxParticles, heavyRate),
// 4 => (extremeMaxParticles, extremeRate),
// _ => (0, 0)
// };
// _transitionProgress = 0f;
// }
// if ( _transitionProgress < 1f )
// {
// _transitionProgress += Time.Delta / transitionDuration;
// if ( _transitionProgress >= 1f )
// {
// _transitionProgress = 1f;
// particleEffect.MaxParticles = (int)_targetMaxParticles;
// emitter.Rate = _targetRate;
// }
// else
// {
// particleEffect.MaxParticles = (int)(_startMaxParticles + (_targetMaxParticles - _startMaxParticles) * _transitionProgress);
// emitter.Rate = _startRate + (_targetRate - _startRate) * _transitionProgress;
// }
// }
// }
// private void TransitionPrecipitationToZero(ParticleEffect particleEffect, PrecipitationControllerEmitter emitter, float transitionDuration)
// {
// float step = Time.Delta / transitionDuration;
// particleEffect.MaxParticles = (int)Math.Max(0, particleEffect.MaxParticles - (particleEffect.MaxParticles * step));
// emitter.Rate = Math.Max(0, emitter.Rate.ConstantValue - (emitter.Rate.ConstantValue * step));
// if (particleEffect.MaxParticles == 0)
// {
// if (_precipitationControllerRain.IsValid() && particleEffect == _particleEffectRain)
// {
// _rainGameObject?.Destroy();
// _rainGameObject = null;
// _precipitationControllerRain = null;
// _particleEffectRain = null;
// _precipitationControllerRainEmitter = null;
// }
// else if (_precipitationControllerSnow.IsValid() && particleEffect == _particleEffectSnow)
// {
// _snowGameObject?.Destroy();
// _snowGameObject = null;
// _precipitationControllerSnow = null;
// _particleEffectSnow = null;
// _precipitationControllerSnowEmitter = null;
// }
// }
// }
// public void TimeEffects()
// {
// if ( Hour != _lastHour)
// {
// _lastHour = Hour;
// const float sunriseStart = 5.6f;
// const float sunrisePeak = 6.3f;
// const float sunriseEnd = 7f;
// const float sunsetStart = 17f;
// const float sunsetPeak = 18.0f;
// const float sunsetEnd = 18.4f;
// const float degreesPerHour = 15f;
// const float sunAngleOffset = 90f;
// Sun.WorldRotation = Rotation.From( (Hour * degreesPerHour) - sunAngleOffset, 0, 0 );
// var directionalLight = Sun.Components.Get<DirectionalLight>();
// if ( directionalLight.IsValid() )
// {
// var goldenHour = new Color( 1.0f, 0.45f, 0.1f );
// Color sunColor;
// if ( Hour >= sunsetEnd || Hour < sunriseStart )
// {
// sunColor = Color.Black;
// }
// else if ( Hour >= sunriseStart && Hour < sunrisePeak )
// {
// float time = (Hour - sunriseStart) / (sunrisePeak - sunriseStart);
// sunColor = Color.Lerp( Color.Black, goldenHour, time );
// }
// else if ( Hour >= sunrisePeak && Hour < sunriseEnd )
// {
// float time = (Hour - sunrisePeak) / (sunriseEnd - sunrisePeak);
// sunColor = Color.Lerp( goldenHour, Color.White, time );
// }
// else if ( Hour >= sunriseEnd && Hour < sunsetStart )
// {
// sunColor = Color.White;
// }
// else if ( Hour >= sunsetStart && Hour < sunsetPeak )
// {
// float time = (Hour - sunsetStart) / (sunsetPeak - sunsetStart);
// sunColor = Color.Lerp( Color.White, goldenHour, time );
// }
// else
// {
// float time = (Hour - sunsetPeak) / (sunsetEnd - sunsetPeak);
// sunColor = Color.Lerp( goldenHour, Color.Black, time );
// }
// directionalLight.LightColor = sunColor;
// }
// foreach ( var envmapProbe in _cachedEnvmapProbes )
// {
// if ( envmapProbe.IsValid() )
// {
// envmapProbe.Dirty = true;
// }
// }
// }
// }
}
Game
library
// namespace AtmokineticAssets;
// [StructLayout(LayoutKind.Sequential)]
// public struct WetnessVolumeCubeBuffer
// {
// public Vector3 VolumeCenter;
// public Vector3 VolumeSize;
// public float WetnessVolumeExtend;
// public float WetnessVolumeFade;
// public float WetnessVolumeSoftness;
// }
// public sealed partial class WaterController : Component
// {
// [Property, Title("Bounds Minimum (cm)"), Feature("Wetness"), Group("Properties"), ShowIf( nameof(WetnessVolumeMode), WetnessVolumeModeEnumeration.Cube )]
// public Vector3 WetnessVolumeCubeBoundsMinimum {get;set;} = new Vector3(-500, -500, -500);
// [Property, Title("Bounds Maximum (cm)"), Feature("Wetness"), Group("Properties"), ShowIf( nameof(WetnessVolumeMode), WetnessVolumeModeEnumeration.Cube )]
// public Vector3 WetnessVolumeCubeBoundsMaximum {get;set;} = new Vector3( 500, 500, 500 );
// [Property, Title( "Enable Gizmo" ), Feature("Wetness"), Group("Properties"), ShowIf( nameof(WetnessVolumeMode), WetnessVolumeModeEnumeration.Cube )]
// public bool EnableWetnessVolumeCubeGizmos { get; set; } = true;
// private static readonly List<WaterController> _wetnessVolumeCubeList = new();
// private static GpuBuffer<WetnessVolumeCubeBuffer> _wetnessVolumeCubeBuffer;
// private static int _currentWetnessVolumeCubeBuffer = 0;
// public void EnableWetnessVolumeCubeBuffer()
// {
// if (!_wetnessVolumeCubeList.Contains(this))
// _wetnessVolumeCubeList.Add(this);
// if (_wetnessVolumeCubeBuffer == null)
// {
// _wetnessVolumeCubeBuffer = new GpuBuffer<WetnessVolumeCubeBuffer>(1);
// _currentWetnessVolumeCubeBuffer = 1;
// }
// UpdateWetnessVolumeCubeBuffer();
// }
// public void DisableWetnessVolumeCubeBuffer()
// {
// _wetnessVolumeCubeList.Remove( this );
// UpdateWetnessVolumeCubeBuffer();
// }
// public static void UpdateWetnessVolumeCubeBuffer()
// {
// if (_wetnessVolumeCubeList.Count == 0) return;
// var volumeBufferList = new List<WetnessVolumeCubeBuffer>();
// foreach (var wetnessVolumeCube in _wetnessVolumeCubeList)
// {
// if (!wetnessVolumeCube.IsValid()) continue;
// // Convert cm to inches
// var boundsMinimumInches = UnitConversion.CentimetersToInches(wetnessVolumeCube.WetnessVolumeCubeBoundsMinimum);
// var boundsMaximumInches = UnitConversion.CentimetersToInches(wetnessVolumeCube.WetnessVolumeCubeBoundsMaximum);
// // Calculate world space center and size
// Vector3 localCenter = (boundsMinimumInches + boundsMaximumInches) * 0.5f;
// Vector3 worldCenter = wetnessVolumeCube.WorldPosition + localCenter;
// Vector3 size = boundsMaximumInches - boundsMinimumInches;
// volumeBufferList.Add(new WetnessVolumeCubeBuffer
// {
// VolumeCenter = worldCenter,
// WetnessVolumeExtend = UnitConversion.CentimetersToInches( wetnessVolumeCube.WetnessVolumeExtend ),
// VolumeSize = size,
// WetnessVolumeFade = wetnessVolumeCube.WetnessVolumeFade,
// WetnessVolumeSoftness = wetnessVolumeCube.WetnessVolumeSoftness,
// });
// }
// if (_wetnessVolumeCubeBuffer == null || volumeBufferList.Count > _currentWetnessVolumeCubeBuffer)
// {
// _wetnessVolumeCubeBuffer = new GpuBuffer<WetnessVolumeCubeBuffer>(volumeBufferList.Count);
// _currentWetnessVolumeCubeBuffer = volumeBufferList.Count;
// }
// if (volumeBufferList.Count > 0)
// {
// _wetnessVolumeCubeBuffer.SetData(volumeBufferList);
// }
// if (Game.ActiveScene?.SceneWorld != null)
// {
// var sceneObjects = Game.ActiveScene.SceneWorld.SceneObjects
// .Where(x => x != null && x.IsValid())
// .ToList();
// foreach (var sceneObject in sceneObjects)
// {
// if (sceneObject.IsValid())
// {
// sceneObject.Attributes.Set("WetnessWetnessVolumeCubeBuffer", _wetnessVolumeCubeBuffer);
// sceneObject.Attributes.Set("MaximumWetnessWetnessVolumeCubes", volumeBufferList.Count);
// }
// }
// }
// }
// }
Game
library
namespace AtmokineticAssets;
public sealed partial class WaterController : Component, Component.ExecuteInEditor
{
public enum WaterReflectionModeEnumeration
{
Dynamic,
Static
}
[Property, Feature("Reflection"), Header("Mode")]
[Sync]
public WaterReflectionModeEnumeration WaterReflectionMode
{
get;
set { field = value; ApplyWaterReflectionMode(); }
} = WaterReflectionModeEnumeration.Dynamic;
public enum WaterReflectionModeStaticModeEnumeration
{
Planar,
Cubemap
}
[Property, Feature("Reflection"), ShowIf( nameof(WaterReflectionMode), WaterReflectionModeEnumeration.Static)]
[Sync]
public WaterReflectionModeStaticModeEnumeration WaterReflectionModeStaticMode
{
get;
set { field = value; ApplyWaterReflectionMode(); }
} = WaterReflectionModeStaticModeEnumeration.Planar;
// Mark the reflection mode dirty after its own settings change. Deliberately does
// NOT apply immediately: these setters also fire from [Sync] snapshots while a
// client is joining, before the scene camera exists - frustum work here NREs.
// OnUpdate's change-check applies it on the next frame instead.
private void ApplyWaterReflectionMode()
{
_lastWaterReflectionPlanarRendering = null;
}
public Vector3 WaterNormalAxis { get; set; } = Vector3.Up;
}
Game
library
global using static Sandbox.Internal.GlobalGameNamespace;
global using Microsoft.AspNetCore.Components;
global using Microsoft.AspNetCore.Components.Rendering;
[assembly: global::System.Reflection.AssemblyMetadata( "AddonTitle", "Atmokinetic Assets" )]
[assembly: global::System.Reflection.AssemblyMetadata( "AddonIdent", "lib-atmokinetic_assets" )]
[assembly: global::System.Reflection.AssemblyMetadata( "OrgIdent", "ali3nsystems" )]
[assembly: global::System.Reflection.AssemblyMetadata( "Ident", "ali3nsystems.lib-atmokinetic_assets" )]
[assembly: global::System.Reflection.AssemblyMetadata( "EngineVersion", "28" )]
[assembly: global::System.Reflection.AssemblyMetadata( "EngineMinorVersion", "1" )]
[assembly: System.Runtime.Versioning.TargetFramework( ".NETCoreApp,Version=v9.0", FrameworkDisplayName = ".NET 9.0" )]
[assembly: global::System.Reflection.AssemblyMetadata( "CompileTime", "2026-08-03T22:07:53.7475400Z" )]
[assembly: global::System.Reflection.AssemblyVersion("0.0.111.0")]
[assembly: global::System.Reflection.AssemblyFileVersion("0.0.111.0")]
Game
library
namespace AtmokineticAssets;
public sealed partial class CameraController : BasePostProcess<CameraController>, Component.ExecuteInEditor
{
public GameObject WaterLineCameraGameObject;
public CameraComponent WaterLineCameraComponent;
public Texture WaterLineTexture;
private void CheckWaterSubmersion()
{
var corners = GetCameraNearPlaneCorners();
if (corners == null)
{
IsCameraSubmerged = false;
IsCameraFullySubmerged = false;
if (WaterLineCameraGameObject.IsValid())
{
WaterController?.DestroyWaterLineMesh();
DisableWaterLineRendering();
}
return;
}
// Cached by EnvironmentController on awake - no per-frame scene scan.
var waterControllers = EnvironmentController.CachedWaterControllers;
int maxSubmergedCount = 0;
WaterController activeWater = null;
foreach (var water in waterControllers)
{
if (!water.IsValid()) continue;
int submergedCount = CountCameraSubmergedCorners(corners, water);
if (submergedCount > maxSubmergedCount)
{
maxSubmergedCount = submergedCount;
activeWater = water;
}
}
bool WasCameraFullySubmerged = IsCameraFullySubmerged;
IsCameraSubmerged = maxSubmergedCount >= 1;
// Use offset check for fully Submerged (only applies when already in water)
int fullySubmergedCount = activeWater != null ? CountCameraFullySubmergedCorners(corners, activeWater) : 0;
bool isNowFullySubmerged = fullySubmergedCount == 4;
bool isNowPartiallySubmerged = IsCameraSubmerged && !isNowFullySubmerged;
// Handle water controller change
if (activeWater != WaterController)
{
WaterController?.DestroyWaterLineMesh();
WaterController = activeWater;
}
// Water line cube (only when partially Submerged)
if (isNowPartiallySubmerged && !WaterLineCameraGameObject.IsValid())
{
WaterController?.CreateWaterLineMesh();
ActivateWaterLineRendering();
}
else if (!isNowPartiallySubmerged && WaterLineCameraGameObject.IsValid())
{
WaterController?.DestroyWaterLineMesh();
DisableWaterLineRendering();
}
// Delay IsCameraFullySubmerged off by one frame to prevent flash
if (WasCameraFullySubmerged && isNowPartiallySubmerged)
{
if (_transitioningFromFullySubmerged)
{
IsCameraFullySubmerged = false;
_transitioningFromFullySubmerged = false;
}
else
{
IsCameraFullySubmerged = true;
_transitioningFromFullySubmerged = true;
}
}
else
{
IsCameraFullySubmerged = isNowFullySubmerged;
_transitioningFromFullySubmerged = false;
}
}
public void WaterLineShaderAttributes()
{
Attributes.Set("Water Line Size", WaterController.WaterLineSize);
Attributes.Set("Water Line Blur", WaterController.WaterLineBlur);
Attributes.Set("Water Line Color", WaterController.WaterLineColor);
}
private void ActivateWaterLineRendering()
{
CameraComponent.RenderExcludeTags.Add("water_line_mesh");
WaterLineCameraGameObject = new GameObject { Name = "Water Line Camera", Parent = GameObject };
// Local-only render helper - never include it in network snapshots.
WaterLineCameraGameObject.Flags |= GameObjectFlags.NotNetworked;
WaterLineCameraGameObject.NetworkMode = NetworkMode.Never;
WaterLineCameraComponent = WaterLineCameraGameObject.AddComponent<CameraComponent>();
WaterLineCameraComponent.BackgroundColor = Color.Black;
WaterLineCameraComponent.IsMainCamera = false;
WaterLineCameraComponent.FovAxis = CameraComponent.FovAxis;
WaterLineCameraComponent.FieldOfView = CameraComponent.FieldOfView;
WaterLineCameraComponent.ZNear = CameraComponent.ZNear;
WaterLineCameraComponent.ZFar = CameraComponent.ZFar;
WaterLineCameraComponent.Priority = CameraComponent.Priority + 1;
WaterLineCameraComponent.Orthographic = CameraComponent.Orthographic;
WaterLineCameraComponent.OrthographicHeight = CameraComponent.OrthographicHeight;
WaterLineCameraComponent.TargetEye = CameraComponent.TargetEye;
WaterLineCameraComponent.Viewport = CameraComponent.Viewport;
WaterLineCameraComponent.RenderTags.Add("water_line_mesh");
WaterLineCameraComponent.RenderExcludeTags.RemoveAll();
WaterLineCameraComponent.EnablePostProcessing = false;
}
private void RenderWaterLineTexture()
{
WaterLineTexture = Texture.CreateRenderTarget("WaterLineRenderTexture", ImageFormat.A8, Screen.Size, WaterLineTexture);
WaterLineCameraComponent.RenderTarget = WaterLineTexture;
Attributes.Set("WaterLineTexture", WaterLineTexture);
}
private void DisableWaterLineRendering()
{
WaterLineCameraGameObject.Destroy();
WaterLineCameraGameObject = null;
WaterLineCameraComponent = null;
WaterLineTexture.Dispose();
WaterLineTexture = null;
CameraComponent.RenderExcludeTags.Remove("water_line_mesh");
}
}
Debug: View Raw JSON Response
{
"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<CameraController>\r\n{\r\n\tpublic Material material {get;set;} = Material.Load(\"materials/mat-camera_weather.vmat\");\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<CameraComponent>();\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(\"Is Camera Submerged\", (IsCameraSubmerged && !IsCameraFullySubmerged) ? 1f : 0f);\r\n\t\tAttributes.Set(\"Is Fully Submerged\", IsCameraFullySubmerged ? 1f : 0f);\r\n\r\n\t\tBlit(BlitMode.WithBackbuffer(material, Stage.BeforePostProcess, 100), \"Water Post Process\");\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<CameraController>, 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 > 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 >= 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 && !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 && !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 && 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 && 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(\"Water Line Size\", WaterController.WaterLineSize);\r\n\t\tAttributes.Set(\"Water Line Blur\", WaterController.WaterLineBlur);\r\n\t\tAttributes.Set(\"Water Line Color\", WaterController.WaterLineColor);\r\n\t}\r\n\r\n private void ActivateWaterLineRendering()\r\n\t{\r\n\t\tCameraComponent.RenderExcludeTags.Add(\"water_line_mesh\");\r\n\r\n\t\tWaterLineCameraGameObject = new GameObject { Name = \"Water Line Camera\", 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<CameraComponent>();\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 + 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(\"water_line_mesh\");\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(\"WaterLineRenderTexture\", ImageFormat.A8, Screen.Size, WaterLineTexture);\r\n\t\tWaterLineCameraComponent.RenderTarget = WaterLineTexture;\r\n\t\tAttributes.Set(\"WaterLineTexture\", 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(\"water_line_mesh\");\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<CameraController>\r\n{\r\n public void UnderWaterShaderAttributes()\r\n {\r\n Attributes.Set(\"Under Water Blur\", WaterController.UnderWaterBlur);\r\n Attributes.Set(\"Under Water Color\", 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(\"Parallax Occlusion Mapping\")]\r\n ParallaxOcclusionMapping,\r\n \r\n [Title(\"Bump Offset\")]\r\n BumpOffset,\r\n \r\n Standard\r\n }\r\n\r\n //[Property, Feature(\"Terrain\"), Title(\"Rendering\")]\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(\"Terrain\"), Title(\"Quality\")] \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(\"Terrain\"), Title(\"Quality\")] \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 => 0,\r\n // TerrainRenderingModeEnumeration.BumpOffset => 1,\r\n // TerrainRenderingModeEnumeration.Standard => 2,\r\n // _ => 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 => 1f,\r\n // TerrainRenderingParallaxOcclusionMappingQualityEnumeration.High => 2f,\r\n // TerrainRenderingParallaxOcclusionMappingQualityEnumeration.Medium => 4f,\r\n // TerrainRenderingParallaxOcclusionMappingQualityEnumeration.Low => 8f,\r\n // _ => 4f\r\n // };\r\n // break;\r\n\r\n // case TerrainRenderingModeEnumeration.BumpOffset:\r\n // quality = TerrainRenderingBumpOffset switch\r\n // {\r\n // TerrainRenderingParallaxOcclusionMappingQualityEnumeration.Ultra => 1f,\r\n // TerrainRenderingParallaxOcclusionMappingQualityEnumeration.High => 2f,\r\n // TerrainRenderingParallaxOcclusionMappingQualityEnumeration.Medium => 4f,\r\n // TerrainRenderingParallaxOcclusionMappingQualityEnumeration.Low => 8f,\r\n // _ => 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( \"D_Rendering\", terrainRenderingMode );\r\n // sceneObject.Attributes.Set( \"Quality\", 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( \"Atmokinetic Assets - Character Controller Utilities\" )]\r\n[Category( \"Atmokinetic Assets\" )]\r\n[Icon( \"badge\" )]\r\n\r\n\r\npublic sealed partial class CharacterControllerUtilities : Component, Component.INetworkSpawn\r\n{\r\n /// <summary>\r\n /// THIS IS PRIMARILY USED FOR THE MODELPHYISICS TO GENERATE WATER SIMULATIONS/RIPPLES\r\n /// WITHOUT COLLIDING WITH THE CHARACTER CONTROLLER'S BOX/CAPSULE COLLIDER. \r\n /// IT USES THE STEAMID AS AN IDENTIFIER.\r\n /// </summary>\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's 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<ModelPhysics>( true ).IsValid() )\r\n return;\r\n\r\n var renderer = GameObject.GetComponentInChildren<SkinnedModelRenderer>( true );\r\n if ( !renderer.IsValid() || renderer.Model is null )\r\n return;\r\n\r\n var physics = renderer.GameObject.AddComponent<ModelPhysics>();\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<ModelPhysics>( 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's 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's 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't\r\n // collide. Only this player's 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't inherit\r\n // the root's 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<Collider>( 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(\"Tile Model\"), Feature(\"General\"), Group(\"Properties\"), ShowIf( nameof(WaterMode), WaterModeEnumeration.Tiles )]\r\n\tpublic Model WaterTileModel { get; set; } = Model.Load(\"models/mdl-water_tile.vmdl\");\r\n\t\r\n\t[Property, Title(\"Tile Size (cm)\"), Feature(\"General\"), Group(\"Properties\"), 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(\"Bounds Maximum (cm)\"), Feature(\"General\"), Group(\"Properties\"), ShowIf( nameof(WaterMode), WaterModeEnumeration.Tiles )]\r\n\tpublic Vector3 WaterTileBoundsMaximum { get; set; } = new( 500, 500, 500 );\r\n\r\n\t[Property, Title(\"Bounds Minimum (cm)\"), Feature(\"General\"), Group(\"Properties\"), ShowIf( nameof(WaterMode), WaterModeEnumeration.Tiles )]\r\n\tpublic Vector3 WaterTileBoundsMinimum { get; set; } = new( -500, -500, -500 );\r\n\r\n\t[Button(\"Generate Water Tiles\"), Feature(\"General\"), Group(\"Properties\"), 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(\"Enable Gizmo\"),Feature( \"General\" ), ShowIf( nameof(WaterMode), WaterModeEnumeration.Tiles)]\r\n\tpublic bool EnableWaterTileGizmo { get; set; } = true;\r\n\tprivate readonly Dictionary<(int x, int y), (GameObject go, ModelRenderer renderer)> _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<(int x, int y), GameObject> _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 => -(WaterRipplesDisplacementA + 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<ModelRenderer> 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() => _waterZ;\r\n\tpublic float GetWaterSurfaceZWithOffset() => _waterZ + SubmersionOffset;\r\n\r\n\tpublic BBox GetWorldBounds()\r\n\t{\r\n\t\tfloat minZ = WorldPosition.z + 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't be used \u2014 it's a toggle you set, not a per-frame\r\n\t// cull result, and the engine doesn't expose \"was culled last frame\" publicly. Frustum\r\n\t// is the only reliable option; it misses occlusion, which isn't 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 >= b.Mins.x && point.x <= b.Maxs.x &&\r\n\t\t\t point.y >= b.Mins.y && point.y <= b.Maxs.y &&\r\n\t\t\t point.z < GetWaterSurfaceZ() + ripples.Count && point.z >= 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 >= b.Mins.x && point.x <= b.Maxs.x &&\r\n\t\t\t point.y >= b.Mins.y && point.y <= b.Maxs.y &&\r\n\t\t\t point.z < GetWaterSurfaceZWithOffset() && point.z >= b.Mins.z;\r\n\t}\r\n\r\n\t// Body group layout convention on the \"subdivision\" 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's \"subdivision\" 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 => p.Name == \"subdivision\" );\r\n\r\n\t\t// Minimum viable layout: waterline + ring 0 + one end/corner pair + far.\r\n\t\tif ( subdivisionPart is null || subdivisionPart.Choices.Count < 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( $\"Water tile model '{WaterTileModel.ResourceName}' has {choiceCount} subdivision choices - expected waterline + ring0 + end/corner pairs + far. Ring layout may be wrong.\" );\r\n\r\n\t\tFarBodyGroupIndex = choiceCount - 1;\r\n\t\tMaxRings = ( choiceCount - 3 ) / 2 + 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 >= MaxRings )\r\n\t\t\treturn FarBodyGroupIndex;\r\n\r\n\t\tint baseIndex = ( ringLevel - 1 ) * 2 + 2;\r\n\t\treturn tileType == TileType.Corner ? baseIndex + 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 > 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<(int x, int y)>();\r\n\t\tforeach ( var kvp in _tiles )\r\n\t\t{\r\n\t\t\tif ( kvp.Value.renderer.IsValid() && kvp.Value.renderer.GetBodyGroup( \"subdivision\" ) == 1 )\r\n\t\t\t\tringZeroKeys.Add( kvp.Key );\r\n\t\t}\r\n\r\n\t\tvar stale = new List<(int x, int y)>();\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 > 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, \"waterline\" ) { 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( \"water_line_mesh\" );\r\n\r\n\t\t\tvar r = childGo.Components.Create<ModelRenderer>();\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( \"BoundsMin\", _boundsMin );\r\n\t\t\tr.SceneObject.Attributes.Set( \"BoundsMax\", _boundsMax );\r\n\t\t\t\r\n\t\t\tr.SceneObject.Attributes.Set( \"WaterRipplesTextureA\", WaterRipplesTextureA );\r\n\t\t\tr.SceneObject.Attributes.Set( \"Water Ripples Scale A\", WaterRipplesScaleA );\r\n\t\t\tr.SceneObject.Attributes.Set( \"Water Ripples Rotation A\", WaterRipplesRotationA );\r\n\t\t\tr.SceneObject.Attributes.Set( \"Water Ripples Speed A\", WaterRipplesSpeedA );\r\n\t\t\tr.SceneObject.Attributes.Set( \"Water Ripples Displacement A\", WaterRipplesDisplacementA );\r\n\r\n\t\t\tr.SceneObject.Attributes.Set( \"WaterRipplesTextureB\", WaterRipplesTextureB );\r\n\t\t\tr.SceneObject.Attributes.Set( \"Water Ripples Scale B\", WaterRipplesScaleB );\r\n\t\t\tr.SceneObject.Attributes.Set( \"Water Ripples Rotation B\", WaterRipplesRotationB );\r\n\t\t\tr.SceneObject.Attributes.Set( \"Water Ripples Speed B\", WaterRipplesSpeedB );\r\n\t\t\tr.SceneObject.Attributes.Set( \"Water Ripples Displacement B\", WaterRipplesDisplacementB );\r\n\r\n\r\n\t\t\tr.Attributes.SetCombo( \"D_TRANSLUCENT\", 0 );\r\n\t\t\tr.Attributes.SetCombo( \"D_Enable_Water_Line\", 1 );\r\n\t\t\tr.SetBodyGroup( \"subdivision\", 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( \"particles\" ) )\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 + localMax ) * 0.5f;\r\n\t\tVector3 size = localMax - localMin;\r\n\r\n\t\t_waterCollider ??= Components.Get<BoxCollider>( FindMode.InSelf ) ?? Components.Create<BoxCollider>();\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 + minLocalX, WorldPosition.y + minLocalY, WorldPosition.z + minInches.z );\r\n\t\t_waterTileBoundsMaximum = new Vector3( WorldPosition.x + maxLocalX, WorldPosition.y + maxLocalY, WorldPosition.z + 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( \"tile_\" ) )\r\n\t\t\t\tcontinue;\r\n\r\n\t\t\tvar renderer = child.Components.Get<ModelRenderer>();\r\n\t\t\tif ( renderer == null )\r\n\t\t\t\tcontinue;\r\n\r\n\t\t\trenderer.SceneObject.Attributes.Set( \"BoundsMin\", _boundsMin );\r\n\t\t\trenderer.SceneObject.Attributes.Set( \"BoundsMax\", _boundsMax );\r\n\t\t\trenderer.Attributes.SetCombo( \"D_TRANSLUCENT\", 1 );\r\n\t\t\trenderer.Attributes.SetCombo( \"D_Enable_Water_Line\", 0 );\r\n\r\n\t\t\t// Parse tileX, tileY from name \"tile_X_Y\"\r\n\t\t\tvar parts = child.Name.Split( '_' );\r\n\t\t\tif ( parts.Length == 3 && int.TryParse( parts[1], out int tileX ) && 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 < _tileMaxX; tileX++ )\r\n\t\t{\r\n\t\t\tfor ( int tileY = _tileMinY; tileY < _tileMaxY; tileY++ )\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 + tileX * tileSizeInches;\r\n\t\t\t\tfloat worldY = WorldPosition.y + tileY * tileSizeInches;\r\n\r\n\t\t\t\tVector3 worldPosition = new Vector3( worldX + halfTile, worldY + 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<(int x, int y), (int ringLevel, int gridX, int gridY)>();\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 > MaxRings )\r\n\t\t\t\tringLevel = MaxRings;\r\n\r\n\t\t\tif ( ringLevel > 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 && gridY == 0 ) rotation = 0f;\r\n\t\t\t\telse if ( gridX == 1 && gridY == 0 ) rotation = 90f;\r\n\t\t\t\telse if ( gridX == 0 && gridY == 1 ) rotation = 270f;\r\n\t\t\t\telse if ( gridX == 1 && gridY == 1 ) rotation = 180f;\r\n\t\t\t}\r\n\t\t\telse if ( ringLevel < 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( \"subdivision\", GetBodyGroupForRing( ringLevel, tileType ) );\r\n\t\t\tkvp.Value.go.WorldRotation = Rotation.FromYaw( -90f + 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+1 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 >= 0 && gridCoord <= 1 )\r\n\t\t\treturn 0;\r\n\r\n\t\tint ring = gridCoord < 0 ? -gridCoord : gridCoord - 1;\r\n\r\n\t\tif ( ring > MaxRings )\r\n\t\t\treturn MaxRings + 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 + 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 && 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 + 1;\r\n\r\n\t\tif ( gridX == outerMin && gridY == outerMax ) return 270f;\r\n\t\tif ( gridX == outerMax && gridY == outerMax ) return 180f;\r\n\t\tif ( gridX == outerMax && gridY == outerMin ) return 90f;\r\n\t\tif ( gridX == outerMin && 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 + 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, $\"tile_{tileX}_{tileY}\" );\r\n\t\ttileGo.Parent = GameObject;\r\n\t\ttileGo.Tags.Add( \"reflection\" ); // excluded from the reflection camera so water doesn't 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<ModelRenderer>();\r\n\t\trenderer.Model = WaterTileModel;\r\n\r\n\t\trenderer.SceneObject.Attributes.Set( \"BoundsMin\", _boundsMin );\r\n\t\trenderer.SceneObject.Attributes.Set( \"BoundsMax\", _boundsMax );\r\n\t\trenderer.Attributes.SetCombo( \"D_TRANSLUCENT\", 1 );\r\n\t\trenderer.Attributes.SetCombo( \"D_Enable_Water_Line\", 0 );\r\n\r\n\t\trenderer.SetBodyGroup( \"subdivision\", 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(\"Area Model\"), Feature(\"General\"), Group(\"Properties\"), ShowIf( nameof(WaterMode), WaterModeEnumeration.Area )]\r\n\t// public Model WaterAreaModel { get; set; }\r\n\r\n\t// [Property, Title(\"Bounds Minimum (cm)\"), Feature(\"General\"), Group(\"Properties\"), ShowIf( nameof(WaterMode), WaterModeEnumeration.Area )]\r\n\t// public Vector3 WaterAreaBoundsMinimum = new( -100, -100, -100 );\r\n\r\n\t// [Property, Title(\"Bounds Maximum (cm)\"), Feature(\"General\"), Group(\"Properties\"), ShowIf( nameof(WaterMode), WaterModeEnumeration.Area )]\r\n\t// public Vector3 WaterAreaBoundsMaximum = new( 100, 100, 0 );\r\n\r\n\t// [Button(\"Generate Water Area\"), Feature(\"General\"), Group(\"Properties\"), 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 =>\r\n// \tWaterReflectionMode == WaterReflectionModeEnumeration.Dynamic\r\n// \t|| ( WaterReflectionMode == WaterReflectionModeEnumeration.Static\r\n// \t&& WaterReflectionModeStaticMode == WaterReflectionModeStaticModeEnumeration.Cubemap );\r\n// \tpublic bool ShowWaterReflectionCubemapResolutionDynamic => ShowWaterReflectionCubemapSettings && WaterReflectionCubemapResolutionMode == WaterReflectionCubemapResolutionModeEnumeration.Dynamic;\r\n// \tpublic bool ShowWaterReflectionCubemapResolutionStatic => ShowWaterReflectionCubemapSettings && WaterReflectionCubemapResolutionMode == WaterReflectionCubemapResolutionModeEnumeration.Static;\r\n// \tpublic bool ShowWaterReflectionCubemapFramesPerSecondDynamic => ShowWaterReflectionCubemapSettings && WaterReflectionCubemapFramesPerSecondMode == WaterReflectionCubemapFramesPerSecondModeEnumeration.Dynamic;\r\n// \tpublic bool ShowWaterReflectionCubemapFramesPerSecondStatic => ShowWaterReflectionCubemapSettings && 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(\"Reflection\"), Group(\"Cubemap\"), Header(\"Resolution\"), ShowIf( nameof(ShowWaterReflectionCubemapSettings), true )]\r\n// \tpublic WaterReflectionCubemapResolutionModeEnumeration WaterReflectionCubemapResolutionMode {get;set;} = WaterReflectionCubemapResolutionModeEnumeration.Dynamic;\r\n\r\n// \t[Property, Feature(\"Reflection\"), Group(\"Cubemap\"), ShowIf( nameof(ShowWaterReflectionCubemapResolutionDynamic), true )]\r\n// \tpublic int WaterReflectionCubemapResolutionDynamicUltra {get;set;} = 1;\r\n\r\n// \t[Property, Feature(\"Reflection\"), Group(\"Cubemap\"), ShowIf( nameof(ShowWaterReflectionCubemapResolutionDynamic), true )]\r\n// \tpublic int WaterReflectionCubemapResolutionDynamicHigh {get;set;} = 2;\r\n\r\n// \t[Property, Feature(\"Reflection\"), Group(\"Cubemap\"), ShowIf( nameof(ShowWaterReflectionCubemapResolutionDynamic), true )]\r\n// \tpublic int WaterReflectionCubemapResolutionDynamicMedium {get;set;} = 4;\r\n\r\n// \t[Property, Feature(\"Reflection\"), Group(\"Cubemap\"), ShowIf( nameof(ShowWaterReflectionCubemapResolutionDynamic), true )]\r\n// \tpublic int WaterReflectionCubemapResolutionDynamicLow {get;set;} = 8;\r\n\r\n// \t[Property, Feature(\"Reflection\"), Group(\"Cubemap\"), ShowIf( nameof(ShowWaterReflectionCubemapResolutionStatic), true )]\r\n// \tpublic int WaterReflectionCubemapResolutionStatic {get;set;} = 2;\r\n\r\n// \t/// <summary>\r\n// \t///\r\n// \t/// </summary>\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(\"Reflection\"), Group(\"Cubemap\"), Header(\"Frames Per Second\"), ShowIf( nameof(ShowWaterReflectionCubemapSettings), true )]\r\n// \tpublic WaterReflectionCubemapFramesPerSecondModeEnumeration WaterReflectionCubemapFramesPerSecondMode {get;set;} = WaterReflectionCubemapFramesPerSecondModeEnumeration.Dynamic;\r\n\r\n// \t[Property, Feature(\"Reflection\"), Group(\"Cubemap\"), ShowIf( nameof(ShowWaterReflectionCubemapFramesPerSecondDynamic), true )]\r\n// \tpublic float WaterReflectionCubemapFramesPerSecondDynamicUltra {get;set;} = 120f;\r\n\r\n// \t[Property, Feature(\"Reflection\"), Group(\"Cubemap\"), ShowIf( nameof(ShowWaterReflectionCubemapFramesPerSecondDynamic), true )]\r\n// \tpublic float WaterReflectionCubemapFramesPerSecondDynamicHigh {get;set;} = 60f;\r\n\r\n// \t[Property, Feature(\"Reflection\"), Group(\"Cubemap\"), ShowIf( nameof(ShowWaterReflectionCubemapFramesPerSecondDynamic), true )]\r\n// \tpublic float WaterReflectionCubemapFramesPerSecondDynamicMedium {get;set;} = 30f;\r\n\r\n// \t[Property, Feature(\"Reflection\"), Group(\"Cubemap\"), ShowIf( nameof(ShowWaterReflectionCubemapFramesPerSecondDynamic), true )]\r\n// \tpublic float WaterReflectionCubemapFramesPerSecondDynamicLow {get;set;} = 15f;\r\n\r\n// \t[Property, Feature(\"Reflection\"), Group(\"Cubemap\"), 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 && !Gizmo.IsHovered )\r\n\t\t\treturn;\r\n\r\n\t\t// Raw box from the user's 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's 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 <= 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 < 2; i++ )\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's grid snap for this control so it doesn't 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( \"water_tile_bounds\", 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 <= 0 || oldSize <= 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 <= 0 )\r\n\t\t\treturn newValue;\r\n\r\n\t\tfloat SnapAxis( float n ) =>\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/// <summary>\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/// </summary>\r\n\t[Property, Feature(\"Buoyancy\"), Group( \"Fluid\" ), Title(\"Fluid Density\"), Range( 0, 15000 )]\r\n\t[Sync]\r\n\tpublic float WaterBuoyancyFluidDensity { get; set; } = 1000.0f;\r\n\r\n\t/// <summary>\r\n\t/// Direction and speed of the water current.\r\n\t/// </summary>\r\n\t[Property, Feature(\"Buoyancy\"), Group( \"Fluid\" ), Title(\"Fluid Velocity\")]\r\n\t[Sync]\r\n\tpublic Vector3 WaterBuoyancyFluidVelocity { get; set; } = Vector3.Zero;\r\n\r\n\t/// <summary>\r\n\t/// How much the fluid slows down movement.\r\n\t/// </summary>\r\n\t[Property, Feature(\"Buoyancy\"), Group( \"Fluid\" ), Title(\"Linear Drag\"),Range( 0, 1 )]\r\n\t[Sync]\r\n\tpublic float WaterBuoyancyLinearDrag { get; set; } = 0.1f;\r\n\r\n\t/// <summary>\r\n\t/// How much the fluid slows down spinning.\r\n\t/// </summary>\r\n\t[Property, Feature(\"Buoyancy\"), Group( \"Fluid\" ), Title(\"Angular Drag\"), Range( 0, 2 )]\r\n\t[Sync]\r\n\tpublic float WaterBuoyancyAngularDrag { get; set; } = 0.5f;\r\n\r\n\t/// <summary>\r\n\t/// Moves the water surface up or down.\r\n\t/// </summary>\r\n\t[Property, Feature(\"Buoyancy\"), Group( \"Surface\" ), Title(\"Surface Offset\")]\r\n\t[Sync]\r\n\tpublic float WaterBuoyancySurfaceOffset { get; set; } = 0.0f;\r\n\r\n\t/// <summary>\r\n\t/// How tall the waves are. Set to 0 for calm water.\r\n\t/// </summary>\r\n\t[Property, Feature(\"Buoyancy\"), Group( \"Surface\" ), Title(\"Wave Amplitude\"), Range( 0, 100 )]\r\n\t[Sync]\r\n\tpublic float WaterBuoyancyWaveAmplitude { get; set; } = 0.1f;\r\n\r\n\t/// <summary>\r\n\t/// How fast the waves move.\r\n\t/// </summary>\r\n\t[Property, Feature(\"Buoyancy\"), Group( \"Surface\" ), Title(\"Wave Frequency\"), Range( 0, 5 )]\r\n\t[Sync]\r\n\tpublic float WaterBuoyancyWaveFrequency { get; set; } = 0.5f;\r\n\r\n\t/// <summary>\r\n\t/// Objects with any of these tags won't be affected.\r\n\t/// </summary>\r\n\t[Property, Feature(\"Buoyancy\"), Group( \"Filtering\" ), Title(\"Ignore Tags\")]\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<Sandbox.WaterVolume>( FindMode.InSelf ) ?? Components.Create<Sandbox.WaterVolume>();\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 + 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(\"Parallax Occlusion Mapping\")]\r\n ParallaxOcclusionMapping,\r\n \r\n [Title(\"Bump Offset\")]\r\n BumpOffset,\r\n \r\n Standard\r\n }\r\n\r\n //[Property, Feature(\"Terrain\"), Title(\"Rendering\")]\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(\"Terrain\"), Title(\"Quality\")] \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(\"Terrain\"), Title(\"Quality\")] \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 => 0,\r\n // TerrainRenderingModeEnumeration.BumpOffset => 1,\r\n // TerrainRenderingModeEnumeration.Standard => 2,\r\n // _ => 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 => 1f,\r\n // TerrainRenderingParallaxOcclusionMappingQualityEnumeration.High => 2f,\r\n // TerrainRenderingParallaxOcclusionMappingQualityEnumeration.Medium => 4f,\r\n // TerrainRenderingParallaxOcclusionMappingQualityEnumeration.Low => 8f,\r\n // _ => 4f\r\n // };\r\n // break;\r\n\r\n // case TerrainRenderingModeEnumeration.BumpOffset:\r\n // quality = TerrainRenderingBumpOffset switch\r\n // {\r\n // TerrainRenderingParallaxOcclusionMappingQualityEnumeration.Ultra => 1f,\r\n // TerrainRenderingParallaxOcclusionMappingQualityEnumeration.High => 2f,\r\n // TerrainRenderingParallaxOcclusionMappingQualityEnumeration.Medium => 4f,\r\n // TerrainRenderingParallaxOcclusionMappingQualityEnumeration.Low => 8f,\r\n // _ => 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( \"D_Rendering\", terrainRenderingMode );\r\n // sceneObject.Attributes.Set( \"Quality\", 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(\"Enable Water Gizmo\"),Feature( \"General\" ), 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(\"Attributes\"), Group(\"Surface\"), Title(\"Color\"), Order(1)]\r\n\t[Sync]\r\n\tpublic Color WaterSurfaceColor { get; set { field = value; _surfaceAttributesDirty = true; } } = Color.Parse( \"#c5d9ff\" ).Value;\r\n\r\n\t[Property, Feature(\"Attributes\"), Group(\"Surface\"), Title(\"Blur\"), 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(\"Attributes\"), Group(\"Reflection\"), Title(\"Color\"), Order(3)]\r\n\t[Sync]\r\n\tpublic Color WaterReflectionColor { get; set { field = value; _surfaceAttributesDirty = true; } } = Color.Parse( \"#c5d9ff\" ).Value;\r\n\r\n\t[Property, Feature(\"Attributes\"), Group(\"Reflection\"), Title(\"Blur\"), 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(\"Attributes\"), Group(\"Reflection\"), Title(\"Distance\"), 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(\"Attributes\"), Group(\"Reflection\"), Title(\"Threshold\"), 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(\"Attributes\"), Group(\"Shoreline\"), Title(\"Fade\"), 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(\"Attributes\"), Group(\"Tension\"), Title(\"Distance\"), 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(\"Attributes\"), Group(\"Tension\"), Title(\"Refraction\"), 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(\"Attributes\"), Group(\"Water Ripples A\"), Title(\"Texture A\"), Order(10)]\r\n\t[Sync]\r\n\tpublic Texture WaterRipplesTextureA { get; set { field = value; _surfaceAttributesDirty = true; } } = Texture.Load(\"textures/tex-water_ripples_1.vtex\");\r\n\r\n\t[Property, Feature(\"Attributes\"), Group(\"Water Ripples A\"), Title(\"Scale A\"), 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(\"Attributes\"), Group(\"Water Ripples A\"), Title(\"Rotation A\"), 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(\"Attributes\"), Group(\"Water Ripples A\"), Title(\"Speed A\"), 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(\"Attributes\"), Group(\"Water Ripples A\"), Title(\"Refraction A\"), 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(\"Attributes\"), Group(\"Water Ripples A\"), Title(\"Displacement A\"), 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(\"Attributes\"), Group(\"Water Ripples B\"), Title(\"Texture B\"), Order(16)]\r\n\t[Sync]\r\n\tpublic Texture WaterRipplesTextureB { get; set { field = value; _surfaceAttributesDirty = true; } } = Texture.Load(\"textures/tex-water_ripples_2.vtex\");\r\n\r\n\t[Property, Feature(\"Attributes\"), Group(\"Water Ripples B\"), Title(\"Scale B\"), 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(\"Attributes\"), Group(\"Water Ripples B\"), Title(\"Rotation B\"), 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(\"Attributes\"), Group(\"Water Ripples B\"), Title(\"Speed B\"), 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(\"Attributes\"), Group(\"Water Ripples B\"), Title(\"Refraction B\"), 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(\"Attributes\"), Group(\"Water Ripples B\"), Title(\"Displacement B\"), 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(\"D_Enable_Water_Surface_Blur\", 1);\r\n\t\t\t}\r\n\t\t\telse\r\n\t\t\t{\r\n\t\t\t\trenderer.Attributes.SetCombo(\"D_Enable_Water_Surface_Blur\", 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(\"D_Enable_Water_Reflection_Blur\", 1);\r\n\t\t\t}\r\n\t\t\telse\r\n\t\t\t{\r\n\t\t\t\trenderer.Attributes.SetCombo(\"D_Enable_Water_Reflection_Blur\", 0);\r\n\t\t\t}\r\n\r\n\r\n\t\t\t// Surface\r\n\t\t\trenderer.Attributes.Set( \"Water Surface Color\", WaterSurfaceColor );\r\n\t\t\trenderer.Attributes.Set( \"Water Surface Blur\", WaterSurfaceBlur );\r\n\r\n\t\t\t// Reflection\r\n\t\t\trenderer.Attributes.Set( \"Water Reflection Color\", WaterReflectionColor );\r\n\t\t\trenderer.Attributes.Set( \"Water Reflection Blur\", WaterReflectionBlur );\r\n\t\t\trenderer.Attributes.Set( \"Water Reflection Distance\", WaterReflectionDistance );\r\n\t\t\trenderer.Attributes.Set( \"Water Reflection Threshold\", WaterReflectionThreshold );\r\n\r\n\t\t\t// Shoreline\r\n\t\t\trenderer.Attributes.Set( \"Water Shoreline Fade\", WaterShorelineFade );\r\n\t\t\t\r\n\t\t\t// Tension\r\n\t\t\trenderer.Attributes.Set( \"Water Tension Distance\", WaterTensionDistance );\r\n\t\t\trenderer.Attributes.Set( \"Water Tension Refraction\", WaterTensionRefraction );\r\n\r\n\t\t\t// Water Ripples A\r\n\t\t\trenderer.Attributes.Set( \"WaterRipplesTextureA\", WaterRipplesTextureA );\r\n\t\t\trenderer.Attributes.Set( \"Water Ripples Scale A\", WaterRipplesScaleA );\r\n\t\t\trenderer.Attributes.Set( \"Water Ripples Rotation A\", WaterRipplesRotationA );\r\n\t\t\trenderer.Attributes.Set( \"Water Ripples Speed A\", WaterRipplesSpeedA );\r\n\t\t\trenderer.Attributes.Set( \"Water Ripples Refraction A\", WaterRipplesRefractionA );\r\n\t\t\trenderer.Attributes.Set( \"Water Ripples Displacement A\", WaterRipplesDisplacementA );\r\n\r\n\t\t\t// Water Ripples B\r\n\t\t\trenderer.Attributes.Set( \"WaterRipplesTextureB\", WaterRipplesTextureB );\r\n\t\t\trenderer.Attributes.Set( \"Water Ripples Scale B\", WaterRipplesScaleB );\r\n\t\t\trenderer.Attributes.Set( \"Water Ripples Rotation B\", WaterRipplesRotationB );\r\n\t\t\trenderer.Attributes.Set( \"Water Ripples Speed B\", WaterRipplesSpeedB );\r\n\t\t\trenderer.Attributes.Set( \"Water Ripples Refraction B\", WaterRipplesRefractionB );\r\n\t\t\trenderer.Attributes.Set( \"Water Ripples Displacement B\", 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(\"Atmokinetic Assets - Environment Controller\")]\r\n[Category(\"Atmokinetic Assets\")]\r\n[Icon(\"cloud\")]\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(\"Weather\"), Sync]\r\n\t// public PrecipitationModeEnumeration PrecipitationMode { get; set; } = PrecipitationModeEnumeration.None;\r\n\r\n\t// [Property, Feature(\"Weather\")] PrefabScene PrecipitationController { get; set;}\r\n\r\n\t// [Property, Feature(\"Weather\"), Range( 0f, 1f ), Sync]\r\n\t// public float WaterPuddles { get; set; } = 0f;\r\n\r\n\t// [Property, Feature(\"Weather\"), Range( 0.0001f, 1f ), Sync]\r\n\t// public float WaterLevel { get; set; } = 0f;\r\n\r\n\t// [Property, Feature(\"Weather\"), Range( 0, 4 ), Step(1), Sync]\r\n\t// public float PrecipitationIntensity { get; set; } = 0f;\r\n\r\n\t// [Property,Feature(\"Time\"), Sync]\r\n\t// public GameObject Sun {get;set;}\r\n\r\n\t// [Property, Feature(\"Time\"), Range(0,24), Sync]\r\n\t// public float Hour {get;set;} = 6f;\r\n\t\r\n\t// [Property, Feature(\"Time\"), Range(1,31), Step(1)]\r\n\t// public float Day {get;set;} = 0f;\r\n\t\r\n\t// [Property, Feature(\"Time\"), Range(1,12), Step(1)]\r\n\t// public float Month {get;set;} = 0f;\r\n\t\r\n\t// [Property, Feature(\"Time\"), 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<SceneObject> _cachedSceneObjects;\r\n\t// private List<EnvmapProbe> _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 => x != null ).ToList();\r\n\r\n\t// \t// Cache all environment map probes\r\n\t// \t_cachedEnvmapProbes = Scene.GetAllComponents<EnvmapProbe>().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 => 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<EnvmapProbe>().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( \"Water Puddles Accumulation\", WaterPuddles );\r\n\t// \t\t\tsceneModel.Attributes.Set( \"Water Plane Level\", WaterLevel );\r\n\t// \t\t\tsceneModel.Attributes.Set( \"Rain Ripples Intensity\", 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 = \"Precipitation Controller (Rain)\";\r\n\r\n\t// \t_rainGameObject = clonedGameObject;\r\n\t// \t_particleEffectRain = clonedGameObject.GetComponent<ParticleEffect>();\r\n\t// \t_precipitationControllerRain = clonedGameObject.GetComponent<PrecipitationController>();\r\n\t// \t_precipitationControllerRainEmitter = clonedGameObject.GetComponent<PrecipitationControllerEmitter>();\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 = \"Precipitation Controller (Snow)\";\r\n\r\n\t// \t_snowGameObject = clonedGameObject;\r\n\t// \t_particleEffectSnow = clonedGameObject.GetComponent<ParticleEffect>();\r\n\t// \t_precipitationControllerSnow = clonedGameObject.GetComponent<PrecipitationController>();\r\n\t// \t_precipitationControllerSnowEmitter = clonedGameObject.GetComponent<PrecipitationControllerEmitter>();\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() => _activePrecipitationControllerRain = false;\r\n\r\n\t// private void TransitionOutSnow() => _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( \"Water Puddles Accumulation\", WaterPuddles );\r\n\t// \t\t\tsceneModel.Attributes.Set( \"Water Plane Level\", WaterLevel );\r\n\t// \t\t\tsceneModel.Attributes.Set( \"Rain Ripples Intensity\", 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 => (lightMaxParticles, lightRate),\r\n\t// \t\t\t2 => (moderateMaxParticles, moderateRate),\r\n\t// \t\t\t3 => (heavyMaxParticles, heavyRate),\r\n\t// \t\t\t4 => (extremeMaxParticles, extremeRate),\r\n\t// \t\t\t_ => (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 < 1f )\r\n\t// \t{\r\n\t// \t\t_transitionProgress += Time.Delta / transitionDuration;\r\n\t// \t\tif ( _transitionProgress >= 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 + (_targetMaxParticles - _startMaxParticles) * _transitionProgress);\r\n\t// \t\t\temitter.Rate = _startRate + (_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() && 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() && 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<DirectionalLight>();\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 >= sunsetEnd || Hour < 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 >= sunriseStart && Hour < 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 >= sunrisePeak && Hour < 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 >= sunriseEnd && Hour < 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 >= sunsetStart && Hour < 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(\"Bounds Minimum (cm)\"), Feature(\"Wetness\"), Group(\"Properties\"), ShowIf( nameof(WetnessVolumeMode), WetnessVolumeModeEnumeration.Cube )]\r\n// \tpublic Vector3 WetnessVolumeCubeBoundsMinimum {get;set;} = new Vector3(-500, -500, -500);\r\n\r\n// \t[Property, Title(\"Bounds Maximum (cm)\"), Feature(\"Wetness\"), Group(\"Properties\"), ShowIf( nameof(WetnessVolumeMode), WetnessVolumeModeEnumeration.Cube )]\r\n// \tpublic Vector3 WetnessVolumeCubeBoundsMaximum {get;set;} = new Vector3( 500, 500, 500 );\r\n\r\n// \t[Property, Title( \"Enable Gizmo\" ), Feature(\"Wetness\"), Group(\"Properties\"), ShowIf( nameof(WetnessVolumeMode), WetnessVolumeModeEnumeration.Cube )]\r\n// \tpublic bool EnableWetnessVolumeCubeGizmos { get; set; } = true;\r\n\r\n// \tprivate static readonly List<WaterController> _wetnessVolumeCubeList = new();\r\n// \tprivate static GpuBuffer<WetnessVolumeCubeBuffer> _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<WetnessVolumeCubeBuffer>(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<WetnessVolumeCubeBuffer>();\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 + boundsMaximumInches) * 0.5f;\r\n// \t\t\tVector3 worldCenter = wetnessVolumeCube.WorldPosition + 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 > _currentWetnessVolumeCubeBuffer)\r\n// \t\t{\r\n// \t\t\t_wetnessVolumeCubeBuffer = new GpuBuffer<WetnessVolumeCubeBuffer>(volumeBufferList.Count);\r\n// \t\t\t_currentWetnessVolumeCubeBuffer = volumeBufferList.Count;\r\n// \t\t}\r\n\r\n// \t\tif (volumeBufferList.Count > 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 => x != null && 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(\"WetnessWetnessVolumeCubeBuffer\", _wetnessVolumeCubeBuffer);\r\n// \t\t\t\t\tsceneObject.Attributes.Set(\"MaximumWetnessWetnessVolumeCubes\", 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(\"Reflection\"), Header(\"Mode\")]\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(\"Reflection\"), 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's 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( \"AddonTitle\", \"Atmokinetic Assets\" )]\r\n[assembly: global::System.Reflection.AssemblyMetadata( \"AddonIdent\", \"lib-atmokinetic_assets\" )]\r\n[assembly: global::System.Reflection.AssemblyMetadata( \"OrgIdent\", \"ali3nsystems\" )]\r\n[assembly: global::System.Reflection.AssemblyMetadata( \"Ident\", \"ali3nsystems.lib-atmokinetic_assets\" )]\r\n[assembly: global::System.Reflection.AssemblyMetadata( \"EngineVersion\", \"28\" )]\r\n[assembly: global::System.Reflection.AssemblyMetadata( \"EngineMinorVersion\", \"1\" )]\r\n\r\n[assembly: System.Runtime.Versioning.TargetFramework( \".NETCoreApp,Version=v9.0\", FrameworkDisplayName = \".NET 9.0\" )]\r\n[assembly: global::System.Reflection.AssemblyMetadata( \"CompileTime\", \"2026-08-03T22:07:53.7475400Z\" )]\r\n[assembly: global::System.Reflection.AssemblyVersion(\"0.0.111.0\")]\r\n[assembly: global::System.Reflection.AssemblyFileVersion(\"0.0.111.0\")]"
},
{
"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<CameraController>, 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 > 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 >= 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 && !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 && !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 && 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 && 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(\"Water Line Size\", WaterController.WaterLineSize);\r\n\t\tAttributes.Set(\"Water Line Blur\", WaterController.WaterLineBlur);\r\n\t\tAttributes.Set(\"Water Line Color\", WaterController.WaterLineColor);\r\n\t}\r\n\r\n private void ActivateWaterLineRendering()\r\n\t{\r\n\t\tCameraComponent.RenderExcludeTags.Add(\"water_line_mesh\");\r\n\r\n\t\tWaterLineCameraGameObject = new GameObject { Name = \"Water Line Camera\", 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<CameraComponent>();\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 + 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(\"water_line_mesh\");\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(\"WaterLineRenderTexture\", ImageFormat.A8, Screen.Size, WaterLineTexture);\r\n\t\tWaterLineCameraComponent.RenderTarget = WaterLineTexture;\r\n\t\tAttributes.Set(\"WaterLineTexture\", 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(\"water_line_mesh\");\r\n\t}\r\n}"
}
]
}