🔍 s&box Package Code Search
Search C# source code, UI razor templates, shaders, and configs across s&box packages.
🔗 Raw Facepunch API Link: https://public.facepunch.com/sbox/code/search/1/?ident=fwotsoft.lightinghelpers&take=20
Showing code results for query:
*
(10 total matches found)
Game
library
using System;
namespace RotSoft.Lighting;
/// <summary>Marks a float property as a color temperature in kelvin; the inspector renders it with the kelvin gradient slider.</summary>
[AttributeUsage( AttributeTargets.Property )]
public sealed class KelvinSliderAttribute : Attribute
{
}
Game
library
using Sandbox;
using System;
namespace RotSoft.Lighting;
/// <summary>Temperature to color conversions.</summary>
public static class TemperatureExtensions
{
/// <summary>
/// Converts this temperature to an sRGB color, clamped to <see cref="Temperature.MinTemperature"/>..<see cref="Temperature.MaxTemperature"/>.
/// </summary>
public static Color ToColor( this Temperature temperature )
{
float kelvin = Math.Clamp( temperature.Kelvin, Temperature.MinTemperature, Temperature.MaxTemperature );
float temp = kelvin / 100f;
float r, g, b;
if ( temp <= 66f )
r = 1f;
else
r = Math.Clamp( 1.29293618606f * MathF.Pow( temp - 60f, -0.1332047592f ), 0f, 1f );
if ( temp <= 66f )
g = Math.Clamp( 0.390081578769f * MathF.Log( temp ) - 0.63184144378f, 0f, 1f );
else
g = Math.Clamp( 1.12989086369f * MathF.Pow( temp - 60f, -0.0755148492f ), 0f, 1f );
if ( temp >= 66f )
b = 1f;
else if ( temp <= 19f )
b = 0f;
else
b = Math.Clamp( 0.54320678911f * MathF.Log( temp - 10f ) - 1.19625408914f, 0f, 1f );
return new Color( r, g, b );
}
}
Game
library
using Sandbox;
namespace RotSoft.Lighting;
/// <summary>
/// Adjusts the <see cref="Color"/> of a <see cref="Light"/> component using Color <see cref="Temperature"/> in Kelvin.
/// Applies once on add and whenever the temperature or brightness changes, so the light's color stays manually editable.
/// </summary>
[Title( "Light Temperature" )]
[Category( "Light" )]
[Icon( "thermostat" )]
[Alias( "Temperature", "Kelvin" )]
public sealed class LightTemperature : Component, Component.ExecuteInEditor
{
/// <summary>Common light temperature presets in Kelvin.</summary>
public enum LightTemperaturePreset
{
/// <summary>1000 K</summary>
MatchFlame = 1700,
/// <summary>2400 K</summary>
Incandescent = 2400,
/// <summary>2700 K</summary>
WarmWhite = 3000,
/// <summary>3200 K</summary>
Studio = 3200,
/// <summary>5000 K</summary>
CoolWhite = 5000,
/// <summary>6500 K</summary>
Daylight = 6500,
/// <summary>6500 K</summary>
LCD = 9500,
/// <summary>7500 K</summary>
BlueSky = 15000,
/// <summary>Custom value, set it with the Kelvin slider.</summary>
Custom = 0,
}
LightTemperaturePreset _preset = LightTemperaturePreset.CoolWhite;
float _kelvin = 5000f;
float _brightness = 1f;
bool _syncing;
Light _light;
/// <summary>Standard light temperature presets.</summary>
[Property]
public LightTemperaturePreset Preset
{
get => _preset;
set
{
if ( _preset == value ) return;
_preset = value;
if ( value != LightTemperaturePreset.Custom )
{
_syncing = true;
Kelvin = (float)(int)value;
_syncing = false;
}
else
{
ApplyColor();
}
}
}
/// <summary>The color temperature in Kelvin.</summary>
[Property, Title( "Temperature (K)" ), Range( 1500, 15000 ), KelvinSlider]
public float Kelvin
{
get => _kelvin;
set
{
if ( _kelvin == value ) return;
_kelvin = value;
if ( !_syncing && _preset != LightTemperaturePreset.Custom )
_preset = LightTemperaturePreset.Custom;
ApplyColor();
}
}
/// <summary>The brightness (range) of the Light.</summary>
[Property, Range( 0, 5f ), Step( 0.01f )]
public float Brightness
{
get => _brightness;
set
{
if ( _brightness == value ) return;
_brightness = value;
ApplyColor();
}
}
/// <summary>Applies the current temperature and brightness to the <see cref="Light"/>'s color once on add.</summary>
protected override void OnStart()
{
ApplyColor();
}
/// <summary>Sets the <see cref="Light"/>'s color from the current temperature and brightness, if a light is present.</summary>
void ApplyColor()
{
_light ??= GetComponentInChildren<Light>( true );
if ( _light is null ) return;
_light.LightColor = new Temperature( Kelvin ).ToColor() * Brightness;
}
}
Game
library
namespace RotSoft.Lighting;
/// <summary>
/// A color temperature in kelvin, converted to a color via <see cref="TemperatureExtensions.ToColor(Temperature)"/>.
/// </summary>
public struct Temperature
{
/// <summary>Lowest valid temperature, in kelvin.</summary>
public const float MinTemperature = 1500f;
/// <summary>Highest valid temperature, in kelvin.</summary>
public const float MaxTemperature = 15000f;
/// <summary>Temperature in kelvin.</summary>
public float Kelvin { get; set; }
/// <summary>Creates a temperature from a kelvin value.</summary>
public Temperature( float kelvin )
{
Kelvin = kelvin;
}
/// <summary>Implicitly converts a kelvin value to a <see cref="Temperature"/>.</summary>
public static implicit operator Temperature( float kelvin ) => new( kelvin );
/// <summary>Implicitly converts a <see cref="Temperature"/> back to its kelvin value.</summary>
public static implicit operator float( Temperature temperature ) => temperature.Kelvin;
}
Editor
library
using System;
using Sandbox;
using RotSoft.Lighting;
using Editor;
namespace RotSoft.Lighting;
/// <summary>
/// A <see cref="FloatControlWidget"/> for float properties marked with <see cref="KelvinSliderAttribute"/>:
/// paints the kelvin (blackbody) gradient as the slider track, with a thermostat icon. The property's
/// <see cref="RangeAttribute"/> supplies the track bounds.
/// </summary>
[CustomEditor( typeof( float ), WithAllAttributes = new[] { typeof( KelvinSliderAttribute ) } )]
public class KelvinFloatControlWidget : FloatControlWidget
{
/// <summary>Cached kelvin gradient strip, rebuilt only when the bar's width (or DPI) changes.</summary>
Pixmap _gradient;
/// <summary>Pixel size the gradient was baked at; rebuilt when width or DPI changes.</summary>
Vector2 _gradientSize;
public KelvinFloatControlWidget( SerializedProperty property ) : base( property )
{
Icon = "thermostat";
RangeStep = 1f; // snap to whole kelvin so values stay integers, without the [Step] spin arrows
SliderPaint = ( rect, _ ) =>
{
// A thin, full-width track strip (like the regular float slider's track), centered vertically.
const float TrackHeight = 6f;
const float ThumbWidth = 6f;
const float ThumbHeight = 12f;
var centerY = rect.Top + rect.Height / 2f;
var track = new Rect( rect.Left, centerY - TrackHeight / 2f, rect.Width, TrackHeight );
EnsureGradient( track.Width );
if ( _gradient is not null )
Paint.Draw( track, _gradient, 1f, TrackHeight / 2f );
// White thumb at the value position, like the regular float slider.
var current = SerializedProperty.GetValue<float>();
var t = MathX.LerpInverse( current, Range.x, Range.y, true ).Clamp( 0f, 1f );
var thumbX = rect.Left + t * (rect.Width - ThumbWidth);
Paint.Antialiasing = true;
// Even soft shadow ring around the thumb, so no bare thumb edge meets the bright gradient.
Paint.ClearPen();
Paint.Antialiasing = true;
Paint.SetBrush( Color.Black.WithAlpha( 0.35f ) );
Paint.DrawRect( new Rect( thumbX - 1f, centerY - ThumbHeight / 2f - 1f, ThumbWidth + 2f, ThumbHeight + 2f ), 2f );
// Thumb itself.
Paint.ClearPen();
Paint.Antialiasing = true;
Paint.SetBrush( Color.White.Darken( 0.1f ) );
Paint.DrawRect( new Rect( thumbX, centerY - ThumbHeight / 2f, ThumbWidth, ThumbHeight ), 2f );
};
}
/// <summary>Bakes the kelvin gradient into a cached pixmap, rebuilt only when the bar's width (or DPI) changes.</summary>
void EnsureGradient( float width )
{
var dpi = DpiScale;
var size = new Vector2( Math.Max( (int)(width * dpi), 1 ), Math.Max( (int)(10f * dpi), 1 ) );
if ( _gradient is not null && _gradientSize == size ) return;
_gradient = new Pixmap( size );
_gradientSize = size;
using ( Paint.ToPixmap( _gradient ) )
{
Paint.Antialiasing = true;
Paint.ClearPen();
int steps = (int)size.x;
float denom = Math.Max( steps - 1, 1 );
for ( int i = 0; i < steps; i++ )
{
float frac = i / denom;
Paint.SetBrush( new Temperature( MathX.LerpTo( Range.x, Range.y, frac, true ) ).ToColor() );
Paint.DrawRect( new Rect( i, 0, 1f, size.y ) );
}
}
}
}
Game
library
using Sandbox;
namespace RotSoft.Lighting;
/// <summary>
/// Adjusts the <see cref="Color"/> of a <see cref="Light"/> component using Color <see cref="Temperature"/> in Kelvin.
/// Applies once on add and whenever the temperature or brightness changes, so the light's color stays manually editable.
/// </summary>
[Title( "Light Temperature" )]
[Category( "Light" )]
[Icon( "thermostat" )]
[Alias( "Temperature", "Kelvin" )]
public sealed class LightTemperature : Component, Component.ExecuteInEditor
{
/// <summary>Common light temperature presets in Kelvin.</summary>
public enum LightTemperaturePreset
{
/// <summary>1000 K</summary>
MatchFlame = 1700,
/// <summary>2400 K</summary>
Incandescent = 2400,
/// <summary>2700 K</summary>
WarmWhite = 3000,
/// <summary>3200 K</summary>
Studio = 3200,
/// <summary>5000 K</summary>
CoolWhite = 5000,
/// <summary>6500 K</summary>
Daylight = 6500,
/// <summary>6500 K</summary>
LCD = 9500,
/// <summary>7500 K</summary>
BlueSky = 15000,
/// <summary>Custom value, set it with the Kelvin slider.</summary>
Custom = 0,
}
LightTemperaturePreset _preset = LightTemperaturePreset.CoolWhite;
float _kelvin = 5000f;
float _brightness = 1f;
bool _syncing;
Light _light;
/// <summary>Standard light temperature presets.</summary>
[Property]
public LightTemperaturePreset Preset
{
get => _preset;
set
{
if ( _preset == value ) return;
_preset = value;
if ( value != LightTemperaturePreset.Custom )
{
_syncing = true;
Kelvin = (float)(int)value;
_syncing = false;
}
else
{
ApplyColor();
}
}
}
/// <summary>The color temperature in Kelvin.</summary>
[Property, Title( "Temperature (K)" ), Range( 1500, 15000 ), KelvinSlider]
public float Kelvin
{
get => _kelvin;
set
{
if ( _kelvin == value ) return;
_kelvin = value;
if ( !_syncing && _preset != LightTemperaturePreset.Custom )
_preset = LightTemperaturePreset.Custom;
ApplyColor();
}
}
/// <summary>The brightness (range) of the Light.</summary>
[Property, Range( 0, 5f ), Step( 0.01f )]
public float Brightness
{
get => _brightness;
set
{
if ( _brightness == value ) return;
_brightness = value;
ApplyColor();
}
}
/// <summary>Applies the current temperature and brightness to the <see cref="Light"/>'s color once on add.</summary>
protected override void OnStart()
{
ApplyColor();
}
/// <summary>Sets the <see cref="Light"/>'s color from the current temperature and brightness, if a light is present.</summary>
void ApplyColor()
{
_light ??= GetComponentInChildren<Light>( true );
if ( _light is null ) return;
_light.LightColor = new Temperature( Kelvin ).ToColor() * Brightness;
}
}
Game
library
using Sandbox;
using System;
namespace RotSoft.Lighting;
/// <summary>Temperature to color conversions.</summary>
public static class TemperatureExtensions
{
/// <summary>
/// Converts this temperature to an sRGB color, clamped to <see cref="Temperature.MinTemperature"/>..<see cref="Temperature.MaxTemperature"/>.
/// </summary>
public static Color ToColor( this Temperature temperature )
{
float kelvin = Math.Clamp( temperature.Kelvin, Temperature.MinTemperature, Temperature.MaxTemperature );
float temp = kelvin / 100f;
float r, g, b;
if ( temp <= 66f )
r = 1f;
else
r = Math.Clamp( 1.29293618606f * MathF.Pow( temp - 60f, -0.1332047592f ), 0f, 1f );
if ( temp <= 66f )
g = Math.Clamp( 0.390081578769f * MathF.Log( temp ) - 0.63184144378f, 0f, 1f );
else
g = Math.Clamp( 1.12989086369f * MathF.Pow( temp - 60f, -0.0755148492f ), 0f, 1f );
if ( temp >= 66f )
b = 1f;
else if ( temp <= 19f )
b = 0f;
else
b = Math.Clamp( 0.54320678911f * MathF.Log( temp - 10f ) - 1.19625408914f, 0f, 1f );
return new Color( r, g, b );
}
}
Game
library
namespace RotSoft.Lighting;
/// <summary>
/// A color temperature in kelvin, converted to a color via <see cref="TemperatureExtensions.ToColor(Temperature)"/>.
/// </summary>
public struct Temperature
{
/// <summary>Lowest valid temperature, in kelvin.</summary>
public const float MinTemperature = 1500f;
/// <summary>Highest valid temperature, in kelvin.</summary>
public const float MaxTemperature = 15000f;
/// <summary>Temperature in kelvin.</summary>
public float Kelvin { get; set; }
/// <summary>Creates a temperature from a kelvin value.</summary>
public Temperature( float kelvin )
{
Kelvin = kelvin;
}
/// <summary>Implicitly converts a kelvin value to a <see cref="Temperature"/>.</summary>
public static implicit operator Temperature( float kelvin ) => new( kelvin );
/// <summary>Implicitly converts a <see cref="Temperature"/> back to its kelvin value.</summary>
public static implicit operator float( Temperature temperature ) => temperature.Kelvin;
}
Game
library
using System;
namespace RotSoft.Lighting;
/// <summary>Marks a float property as a color temperature in kelvin; the inspector renders it with the kelvin gradient slider.</summary>
[AttributeUsage( AttributeTargets.Property )]
public sealed class KelvinSliderAttribute : Attribute
{
}
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", "Lighting Helpers" )]
[assembly: global::System.Reflection.AssemblyMetadata( "AddonIdent", "lightinghelpers" )]
[assembly: global::System.Reflection.AssemblyMetadata( "OrgIdent", "fwotsoft" )]
[assembly: global::System.Reflection.AssemblyMetadata( "Ident", "fwotsoft.lightinghelpers" )]
[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-27T09:20:31.5361275Z" )]
[assembly: global::System.Reflection.AssemblyVersion("0.0.120.0")]
[assembly: global::System.Reflection.AssemblyFileVersion("0.0.120.0")]
Debug: View Raw JSON Response
{
"TotalCount": 10,
"Files": [
{
"Ident": "fwotsoft.lightinghelpers",
"Path": "Code/KelvinSliderAttribute.cs",
"FileName": "KelvinSliderAttribute.cs",
"PackageType": "library",
"CodeKind": "Game",
"AssetVersionId": 355266,
"Code": "using System;\r\n\r\nnamespace RotSoft.Lighting;\r\n\r\n/// <summary>Marks a float property as a color temperature in kelvin; the inspector renders it with the kelvin gradient slider.</summary>\r\n[AttributeUsage( AttributeTargets.Property )]\r\npublic sealed class KelvinSliderAttribute : Attribute\r\n{\r\n}\r\n"
},
{
"Ident": "fwotsoft.lightinghelpers",
"Path": "Code/Helpers/TemperatureExtensions.cs",
"FileName": "TemperatureExtensions.cs",
"PackageType": "library",
"CodeKind": "Game",
"AssetVersionId": 355266,
"Code": "using Sandbox;\r\nusing System;\r\n\r\nnamespace RotSoft.Lighting;\r\n\r\n/// <summary>Temperature to color conversions.</summary>\r\npublic static class TemperatureExtensions\r\n{\r\n\t/// <summary>\r\n\t/// Converts this temperature to an sRGB color, clamped to <see cref=\"Temperature.MinTemperature\"/>..<see cref=\"Temperature.MaxTemperature\"/>.\r\n\t/// </summary>\r\n\tpublic static Color ToColor( this Temperature temperature )\r\n\t{\r\n\t\tfloat kelvin = Math.Clamp( temperature.Kelvin, Temperature.MinTemperature, Temperature.MaxTemperature );\r\n\t\tfloat temp = kelvin / 100f;\r\n\t\tfloat r, g, b;\r\n\r\n\t\tif ( temp <= 66f )\r\n\t\t\tr = 1f;\r\n\t\telse\r\n\t\t\tr = Math.Clamp( 1.29293618606f * MathF.Pow( temp - 60f, -0.1332047592f ), 0f, 1f );\r\n\r\n\t\tif ( temp <= 66f )\r\n\t\t\tg = Math.Clamp( 0.390081578769f * MathF.Log( temp ) - 0.63184144378f, 0f, 1f );\r\n\t\telse\r\n\t\t\tg = Math.Clamp( 1.12989086369f * MathF.Pow( temp - 60f, -0.0755148492f ), 0f, 1f );\r\n\r\n\t\tif ( temp >= 66f )\r\n\t\t\tb = 1f;\r\n\t\telse if ( temp <= 19f )\r\n\t\t\tb = 0f;\r\n\t\telse\r\n\t\t\tb = Math.Clamp( 0.54320678911f * MathF.Log( temp - 10f ) - 1.19625408914f, 0f, 1f );\r\n\r\n\t\treturn new Color( r, g, b );\r\n\t}\r\n}\r\n"
},
{
"Ident": "fwotsoft.lightinghelpers",
"Path": "LightTemperature.cs",
"FileName": "LightTemperature.cs",
"PackageType": "library",
"CodeKind": "Game",
"AssetVersionId": 355266,
"Code": "using Sandbox;\r\n\r\nnamespace RotSoft.Lighting;\r\n\r\n\r\n\r\n\r\n/// <summary>\r\n/// Adjusts the <see cref=\"Color\"/> of a <see cref=\"Light\"/> component using Color <see cref=\"Temperature\"/> in Kelvin.\r\n/// Applies once on add and whenever the temperature or brightness changes, so the light's color stays manually editable.\r\n/// </summary>\r\n[Title( \"Light Temperature\" )]\r\n[Category( \"Light\" )]\r\n[Icon( \"thermostat\" )]\r\n[Alias( \"Temperature\", \"Kelvin\" )]\r\npublic sealed class LightTemperature : Component, Component.ExecuteInEditor\r\n{\r\n\t/// <summary>Common light temperature presets in Kelvin.</summary>\r\n\tpublic enum LightTemperaturePreset\r\n\t{\r\n\t\t/// <summary>1000 K</summary>\r\n\t\tMatchFlame = 1700,\r\n\t\t/// <summary>2400 K</summary>\r\n\t\tIncandescent = 2400,\r\n\t\t/// <summary>2700 K</summary>\r\n\t\tWarmWhite = 3000,\r\n\t\t/// <summary>3200 K</summary>\r\n\t\tStudio = 3200,\r\n\t\t/// <summary>5000 K</summary>\r\n\t\tCoolWhite = 5000,\r\n\t\t/// <summary>6500 K</summary>\r\n\t\tDaylight = 6500,\r\n\t\t/// <summary>6500 K</summary>\r\n\t\tLCD = 9500,\r\n\t\t/// <summary>7500 K</summary>\r\n\t\tBlueSky = 15000,\r\n\t\t/// <summary>Custom value, set it with the Kelvin slider.</summary>\r\n\t\tCustom = 0,\r\n\t}\r\n\r\n\tLightTemperaturePreset _preset = LightTemperaturePreset.CoolWhite;\r\n\tfloat _kelvin = 5000f;\r\n\tfloat _brightness = 1f;\r\n\tbool _syncing;\r\n\tLight _light;\r\n\r\n\t/// <summary>Standard light temperature presets.</summary>\r\n\t[Property]\r\n\tpublic LightTemperaturePreset Preset\r\n\t{\r\n\t\tget => _preset;\r\n\t\tset\r\n\t\t{\r\n\t\t\tif ( _preset == value ) return;\r\n\t\t\t_preset = value;\r\n\t\t\tif ( value != LightTemperaturePreset.Custom )\r\n\t\t\t{\r\n\t\t\t\t_syncing = true;\r\n\t\t\t\tKelvin = (float)(int)value;\r\n\t\t\t\t_syncing = false;\r\n\t\t\t}\r\n\t\t\telse\r\n\t\t\t{\r\n\t\t\t\tApplyColor();\r\n\t\t\t}\r\n\t\t}\r\n\t}\r\n\r\n\t/// <summary>The color temperature in Kelvin.</summary>\r\n\t[Property, Title( \"Temperature (K)\" ), Range( 1500, 15000 ), KelvinSlider]\r\n\tpublic float Kelvin\r\n\t{\r\n\t\tget => _kelvin;\r\n\t\tset\r\n\t\t{\r\n\t\t\tif ( _kelvin == value ) return;\r\n\t\t\t_kelvin = value;\r\n\t\t\tif ( !_syncing && _preset != LightTemperaturePreset.Custom )\r\n\t\t\t\t_preset = LightTemperaturePreset.Custom;\r\n\t\t\tApplyColor();\r\n\t\t}\r\n\t}\r\n\r\n\t/// <summary>The brightness (range) of the Light.</summary>\r\n\t[Property, Range( 0, 5f ), Step( 0.01f )]\r\n\tpublic float Brightness\r\n\t{\r\n\t\tget => _brightness;\r\n\t\tset\r\n\t\t{\r\n\t\t\tif ( _brightness == value ) return;\r\n\t\t\t_brightness = value;\r\n\t\t\tApplyColor();\r\n\t\t}\r\n\t}\r\n\r\n\t/// <summary>Applies the current temperature and brightness to the <see cref=\"Light\"/>'s color once on add.</summary>\r\n\tprotected override void OnStart()\r\n\t{\r\n\t\tApplyColor();\r\n\t}\r\n\r\n\t/// <summary>Sets the <see cref=\"Light\"/>'s color from the current temperature and brightness, if a light is present.</summary>\r\n\tvoid ApplyColor()\r\n\t{\r\n\t\t_light ??= GetComponentInChildren<Light>( true );\r\n\t\tif ( _light is null ) return;\r\n\t\t_light.LightColor = new Temperature( Kelvin ).ToColor() * Brightness;\r\n\t}\r\n}\r\n"
},
{
"Ident": "fwotsoft.lightinghelpers",
"Path": "Temperature.cs",
"FileName": "Temperature.cs",
"PackageType": "library",
"CodeKind": "Game",
"AssetVersionId": 355266,
"Code": "namespace RotSoft.Lighting;\r\n\r\n/// <summary>\r\n/// A color temperature in kelvin, converted to a color via <see cref=\"TemperatureExtensions.ToColor(Temperature)\"/>.\r\n/// </summary>\r\npublic struct Temperature\r\n{\r\n\t/// <summary>Lowest valid temperature, in kelvin.</summary>\r\n\tpublic const float MinTemperature = 1500f;\r\n\r\n\t/// <summary>Highest valid temperature, in kelvin.</summary>\r\n\tpublic const float MaxTemperature = 15000f;\r\n\r\n\t/// <summary>Temperature in kelvin.</summary>\r\n\tpublic float Kelvin { get; set; }\r\n\r\n\t/// <summary>Creates a temperature from a kelvin value.</summary>\r\n\tpublic Temperature( float kelvin )\r\n\t{\r\n\t\tKelvin = kelvin;\r\n\t}\r\n\r\n\t/// <summary>Implicitly converts a kelvin value to a <see cref=\"Temperature\"/>.</summary>\r\n\tpublic static implicit operator Temperature( float kelvin ) => new( kelvin );\r\n\r\n\t/// <summary>Implicitly converts a <see cref=\"Temperature\"/> back to its kelvin value.</summary>\r\n\tpublic static implicit operator float( Temperature temperature ) => temperature.Kelvin;\r\n\r\n}\r\n"
},
{
"Ident": "fwotsoft.lightinghelpers",
"Path": "Editor/KelvinFloatControlWidget.cs",
"FileName": "KelvinFloatControlWidget.cs",
"PackageType": "library",
"CodeKind": "Editor",
"AssetVersionId": 355266,
"Code": "using System;\r\nusing Sandbox;\r\nusing RotSoft.Lighting;\r\nusing Editor;\r\n\r\nnamespace RotSoft.Lighting;\r\n\r\n/// <summary>\r\n/// A <see cref=\"FloatControlWidget\"/> for float properties marked with <see cref=\"KelvinSliderAttribute\"/>:\r\n/// paints the kelvin (blackbody) gradient as the slider track, with a thermostat icon. The property's\r\n/// <see cref=\"RangeAttribute\"/> supplies the track bounds.\r\n/// </summary>\r\n[CustomEditor( typeof( float ), WithAllAttributes = new[] { typeof( KelvinSliderAttribute ) } )]\r\npublic class KelvinFloatControlWidget : FloatControlWidget\r\n{\r\n\t/// <summary>Cached kelvin gradient strip, rebuilt only when the bar's width (or DPI) changes.</summary>\r\n\tPixmap _gradient;\r\n\r\n\t/// <summary>Pixel size the gradient was baked at; rebuilt when width or DPI changes.</summary>\r\n\tVector2 _gradientSize;\r\n\r\n\tpublic KelvinFloatControlWidget( SerializedProperty property ) : base( property )\r\n\t{\r\n\t\tIcon = \"thermostat\";\r\n\t\tRangeStep = 1f; // snap to whole kelvin so values stay integers, without the [Step] spin arrows\r\n\r\n\t\tSliderPaint = ( rect, _ ) =>\r\n\t\t{\r\n\t\t\t// A thin, full-width track strip (like the regular float slider's track), centered vertically.\r\n\t\t\tconst float TrackHeight = 6f;\r\n\t\t\tconst float ThumbWidth = 6f;\r\n\t\t\tconst float ThumbHeight = 12f;\r\n\r\n\t\t\tvar centerY = rect.Top + rect.Height / 2f;\r\n\t\t\tvar track = new Rect( rect.Left, centerY - TrackHeight / 2f, rect.Width, TrackHeight );\r\n\r\n\t\t\tEnsureGradient( track.Width );\r\n\t\t\tif ( _gradient is not null )\r\n\t\t\t\tPaint.Draw( track, _gradient, 1f, TrackHeight / 2f );\r\n\r\n\t\t\t// White thumb at the value position, like the regular float slider.\r\n\t\t\tvar current = SerializedProperty.GetValue<float>();\r\n\t\t\tvar t = MathX.LerpInverse( current, Range.x, Range.y, true ).Clamp( 0f, 1f );\r\n\t\t\tvar thumbX = rect.Left + t * (rect.Width - ThumbWidth);\r\n\t\t\tPaint.Antialiasing = true;\r\n\r\n\t\t\t// Even soft shadow ring around the thumb, so no bare thumb edge meets the bright gradient.\r\n\t\t\tPaint.ClearPen();\r\n\t\t\tPaint.Antialiasing = true;\r\n\t\t\tPaint.SetBrush( Color.Black.WithAlpha( 0.35f ) );\r\n\t\t\tPaint.DrawRect( new Rect( thumbX - 1f, centerY - ThumbHeight / 2f - 1f, ThumbWidth + 2f, ThumbHeight + 2f ), 2f );\r\n\r\n\t\t\t// Thumb itself.\r\n\t\t\tPaint.ClearPen();\r\n\t\t\tPaint.Antialiasing = true;\r\n\t\t\tPaint.SetBrush( Color.White.Darken( 0.1f ) );\r\n\t\t\tPaint.DrawRect( new Rect( thumbX, centerY - ThumbHeight / 2f, ThumbWidth, ThumbHeight ), 2f );\r\n\t\t};\r\n\t}\r\n\r\n\t/// <summary>Bakes the kelvin gradient into a cached pixmap, rebuilt only when the bar's width (or DPI) changes.</summary>\r\n\tvoid EnsureGradient( float width )\r\n\t{\r\n\t\tvar dpi = DpiScale;\r\n\t\tvar size = new Vector2( Math.Max( (int)(width * dpi), 1 ), Math.Max( (int)(10f * dpi), 1 ) );\r\n\r\n\t\tif ( _gradient is not null && _gradientSize == size ) return;\r\n\r\n\t\t_gradient = new Pixmap( size );\r\n\t\t_gradientSize = size;\r\n\r\n\t\tusing ( Paint.ToPixmap( _gradient ) )\r\n\t\t{\r\n\t\t\tPaint.Antialiasing = true;\r\n\t\t\tPaint.ClearPen();\r\n\r\n\t\t\tint steps = (int)size.x;\r\n\t\t\tfloat denom = Math.Max( steps - 1, 1 );\r\n\t\t\tfor ( int i = 0; i < steps; i++ )\r\n\t\t\t{\r\n\t\t\t\tfloat frac = i / denom;\r\n\t\t\t\tPaint.SetBrush( new Temperature( MathX.LerpTo( Range.x, Range.y, frac, true ) ).ToColor() );\r\n\t\t\t\tPaint.DrawRect( new Rect( i, 0, 1f, size.y ) );\r\n\t\t\t}\r\n\t\t}\r\n\t}\r\n}\r\n"
},
{
"Ident": "fwotsoft.lightinghelpers",
"Path": "Code/LightTemperature.cs",
"FileName": "LightTemperature.cs",
"PackageType": "library",
"CodeKind": "Game",
"AssetVersionId": 355266,
"Code": "using Sandbox;\r\n\r\nnamespace RotSoft.Lighting;\r\n\r\n\r\n\r\n\r\n/// <summary>\r\n/// Adjusts the <see cref=\"Color\"/> of a <see cref=\"Light\"/> component using Color <see cref=\"Temperature\"/> in Kelvin.\r\n/// Applies once on add and whenever the temperature or brightness changes, so the light's color stays manually editable.\r\n/// </summary>\r\n[Title( \"Light Temperature\" )]\r\n[Category( \"Light\" )]\r\n[Icon( \"thermostat\" )]\r\n[Alias( \"Temperature\", \"Kelvin\" )]\r\npublic sealed class LightTemperature : Component, Component.ExecuteInEditor\r\n{\r\n\t/// <summary>Common light temperature presets in Kelvin.</summary>\r\n\tpublic enum LightTemperaturePreset\r\n\t{\r\n\t\t/// <summary>1000 K</summary>\r\n\t\tMatchFlame = 1700,\r\n\t\t/// <summary>2400 K</summary>\r\n\t\tIncandescent = 2400,\r\n\t\t/// <summary>2700 K</summary>\r\n\t\tWarmWhite = 3000,\r\n\t\t/// <summary>3200 K</summary>\r\n\t\tStudio = 3200,\r\n\t\t/// <summary>5000 K</summary>\r\n\t\tCoolWhite = 5000,\r\n\t\t/// <summary>6500 K</summary>\r\n\t\tDaylight = 6500,\r\n\t\t/// <summary>6500 K</summary>\r\n\t\tLCD = 9500,\r\n\t\t/// <summary>7500 K</summary>\r\n\t\tBlueSky = 15000,\r\n\t\t/// <summary>Custom value, set it with the Kelvin slider.</summary>\r\n\t\tCustom = 0,\r\n\t}\r\n\r\n\tLightTemperaturePreset _preset = LightTemperaturePreset.CoolWhite;\r\n\tfloat _kelvin = 5000f;\r\n\tfloat _brightness = 1f;\r\n\tbool _syncing;\r\n\tLight _light;\r\n\r\n\t/// <summary>Standard light temperature presets.</summary>\r\n\t[Property]\r\n\tpublic LightTemperaturePreset Preset\r\n\t{\r\n\t\tget => _preset;\r\n\t\tset\r\n\t\t{\r\n\t\t\tif ( _preset == value ) return;\r\n\t\t\t_preset = value;\r\n\t\t\tif ( value != LightTemperaturePreset.Custom )\r\n\t\t\t{\r\n\t\t\t\t_syncing = true;\r\n\t\t\t\tKelvin = (float)(int)value;\r\n\t\t\t\t_syncing = false;\r\n\t\t\t}\r\n\t\t\telse\r\n\t\t\t{\r\n\t\t\t\tApplyColor();\r\n\t\t\t}\r\n\t\t}\r\n\t}\r\n\r\n\t/// <summary>The color temperature in Kelvin.</summary>\r\n\t[Property, Title( \"Temperature (K)\" ), Range( 1500, 15000 ), KelvinSlider]\r\n\tpublic float Kelvin\r\n\t{\r\n\t\tget => _kelvin;\r\n\t\tset\r\n\t\t{\r\n\t\t\tif ( _kelvin == value ) return;\r\n\t\t\t_kelvin = value;\r\n\t\t\tif ( !_syncing && _preset != LightTemperaturePreset.Custom )\r\n\t\t\t\t_preset = LightTemperaturePreset.Custom;\r\n\t\t\tApplyColor();\r\n\t\t}\r\n\t}\r\n\r\n\t/// <summary>The brightness (range) of the Light.</summary>\r\n\t[Property, Range( 0, 5f ), Step( 0.01f )]\r\n\tpublic float Brightness\r\n\t{\r\n\t\tget => _brightness;\r\n\t\tset\r\n\t\t{\r\n\t\t\tif ( _brightness == value ) return;\r\n\t\t\t_brightness = value;\r\n\t\t\tApplyColor();\r\n\t\t}\r\n\t}\r\n\r\n\t/// <summary>Applies the current temperature and brightness to the <see cref=\"Light\"/>'s color once on add.</summary>\r\n\tprotected override void OnStart()\r\n\t{\r\n\t\tApplyColor();\r\n\t}\r\n\r\n\t/// <summary>Sets the <see cref=\"Light\"/>'s color from the current temperature and brightness, if a light is present.</summary>\r\n\tvoid ApplyColor()\r\n\t{\r\n\t\t_light ??= GetComponentInChildren<Light>( true );\r\n\t\tif ( _light is null ) return;\r\n\t\t_light.LightColor = new Temperature( Kelvin ).ToColor() * Brightness;\r\n\t}\r\n}\r\n"
},
{
"Ident": "fwotsoft.lightinghelpers",
"Path": "Helpers/TemperatureExtensions.cs",
"FileName": "TemperatureExtensions.cs",
"PackageType": "library",
"CodeKind": "Game",
"AssetVersionId": 355266,
"Code": "using Sandbox;\r\nusing System;\r\n\r\nnamespace RotSoft.Lighting;\r\n\r\n/// <summary>Temperature to color conversions.</summary>\r\npublic static class TemperatureExtensions\r\n{\r\n\t/// <summary>\r\n\t/// Converts this temperature to an sRGB color, clamped to <see cref=\"Temperature.MinTemperature\"/>..<see cref=\"Temperature.MaxTemperature\"/>.\r\n\t/// </summary>\r\n\tpublic static Color ToColor( this Temperature temperature )\r\n\t{\r\n\t\tfloat kelvin = Math.Clamp( temperature.Kelvin, Temperature.MinTemperature, Temperature.MaxTemperature );\r\n\t\tfloat temp = kelvin / 100f;\r\n\t\tfloat r, g, b;\r\n\r\n\t\tif ( temp <= 66f )\r\n\t\t\tr = 1f;\r\n\t\telse\r\n\t\t\tr = Math.Clamp( 1.29293618606f * MathF.Pow( temp - 60f, -0.1332047592f ), 0f, 1f );\r\n\r\n\t\tif ( temp <= 66f )\r\n\t\t\tg = Math.Clamp( 0.390081578769f * MathF.Log( temp ) - 0.63184144378f, 0f, 1f );\r\n\t\telse\r\n\t\t\tg = Math.Clamp( 1.12989086369f * MathF.Pow( temp - 60f, -0.0755148492f ), 0f, 1f );\r\n\r\n\t\tif ( temp >= 66f )\r\n\t\t\tb = 1f;\r\n\t\telse if ( temp <= 19f )\r\n\t\t\tb = 0f;\r\n\t\telse\r\n\t\t\tb = Math.Clamp( 0.54320678911f * MathF.Log( temp - 10f ) - 1.19625408914f, 0f, 1f );\r\n\r\n\t\treturn new Color( r, g, b );\r\n\t}\r\n}\r\n"
},
{
"Ident": "fwotsoft.lightinghelpers",
"Path": "Code/Temperature.cs",
"FileName": "Temperature.cs",
"PackageType": "library",
"CodeKind": "Game",
"AssetVersionId": 355266,
"Code": "namespace RotSoft.Lighting;\r\n\r\n/// <summary>\r\n/// A color temperature in kelvin, converted to a color via <see cref=\"TemperatureExtensions.ToColor(Temperature)\"/>.\r\n/// </summary>\r\npublic struct Temperature\r\n{\r\n\t/// <summary>Lowest valid temperature, in kelvin.</summary>\r\n\tpublic const float MinTemperature = 1500f;\r\n\r\n\t/// <summary>Highest valid temperature, in kelvin.</summary>\r\n\tpublic const float MaxTemperature = 15000f;\r\n\r\n\t/// <summary>Temperature in kelvin.</summary>\r\n\tpublic float Kelvin { get; set; }\r\n\r\n\t/// <summary>Creates a temperature from a kelvin value.</summary>\r\n\tpublic Temperature( float kelvin )\r\n\t{\r\n\t\tKelvin = kelvin;\r\n\t}\r\n\r\n\t/// <summary>Implicitly converts a kelvin value to a <see cref=\"Temperature\"/>.</summary>\r\n\tpublic static implicit operator Temperature( float kelvin ) => new( kelvin );\r\n\r\n\t/// <summary>Implicitly converts a <see cref=\"Temperature\"/> back to its kelvin value.</summary>\r\n\tpublic static implicit operator float( Temperature temperature ) => temperature.Kelvin;\r\n\r\n}\r\n"
},
{
"Ident": "fwotsoft.lightinghelpers",
"Path": "KelvinSliderAttribute.cs",
"FileName": "KelvinSliderAttribute.cs",
"PackageType": "library",
"CodeKind": "Game",
"AssetVersionId": 355266,
"Code": "using System;\r\n\r\nnamespace RotSoft.Lighting;\r\n\r\n/// <summary>Marks a float property as a color temperature in kelvin; the inspector renders it with the kelvin gradient slider.</summary>\r\n[AttributeUsage( AttributeTargets.Property )]\r\npublic sealed class KelvinSliderAttribute : Attribute\r\n{\r\n}\r\n"
},
{
"Ident": "fwotsoft.lightinghelpers",
"Path": ".obj/__compiler_extra.cs",
"FileName": "__compiler_extra.cs",
"PackageType": "library",
"CodeKind": "Game",
"AssetVersionId": 355266,
"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\", \"Lighting Helpers\" )]\r\n[assembly: global::System.Reflection.AssemblyMetadata( \"AddonIdent\", \"lightinghelpers\" )]\r\n[assembly: global::System.Reflection.AssemblyMetadata( \"OrgIdent\", \"fwotsoft\" )]\r\n[assembly: global::System.Reflection.AssemblyMetadata( \"Ident\", \"fwotsoft.lightinghelpers\" )]\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-27T09:20:31.5361275Z\" )]\r\n[assembly: global::System.Reflection.AssemblyVersion(\"0.0.120.0\")]\r\n[assembly: global::System.Reflection.AssemblyFileVersion(\"0.0.120.0\")]"
}
]
}