diff --git a/DeduplicatingExportPlugin.cs b/DeduplicatingExportPlugin.cs new file mode 100644 index 0000000..bb30a92 --- /dev/null +++ b/DeduplicatingExportPlugin.cs @@ -0,0 +1,40 @@ + +#if TOOLS + +using Godot; +using System.Collections.Generic; + +namespace Rokojori; + +[Tool] +public partial class DeduplicatingExportPlugin : EditorExportPlugin +{ + private HashSet _seenPaths = new HashSet(); + + public override string _GetName() => "RokojoriDeduplicatingExport"; + + public override void _ExportBegin( string[] features, bool isDebug, string path, uint flags ) + { + _seenPaths.Clear(); + GD.Print( "[DeduplicatingExport] Export started: ", path ); + } + + public override void _ExportFile( string path, string type, string[] features ) + { + if ( ! _seenPaths.Add( path ) ) + { + GD.Print( "[DeduplicatingExport] SKIPPED (duplicate): ", path ); + Skip(); + return; + } + + GD.Print( "[DeduplicatingExport] included: ", path ); + } + + public override void _ExportEnd() + { + GD.Print( "[DeduplicatingExport] Export ended. Total unique files: ", _seenPaths.Count ); + } +} + +#endif diff --git a/DeduplicatingExportPlugin.cs.uid b/DeduplicatingExportPlugin.cs.uid new file mode 100644 index 0000000..a8143d7 --- /dev/null +++ b/DeduplicatingExportPlugin.cs.uid @@ -0,0 +1 @@ +uid://2vyvvmkop2ec diff --git a/RokojoriPlugin.cs b/RokojoriPlugin.cs index d1ff41d..639f7dd 100644 --- a/RokojoriPlugin.cs +++ b/RokojoriPlugin.cs @@ -64,6 +64,7 @@ public partial class RokojoriPlugin: EditorPlugin { GizmoDrawerPlugin gizmoDrawerPlugin = new GizmoDrawerPlugin(); GodotEditorInspectorTools inspectorTools = new GodotEditorInspectorTools(); + DeduplicatingExportPlugin deduplicatingExportPlugin = new DeduplicatingExportPlugin(); public static readonly string path = "res://addons/rokojori_action_library"; @@ -163,6 +164,7 @@ public partial class RokojoriPlugin: EditorPlugin this.LogInfo(); AddNode3DGizmoPlugin( gizmoDrawerPlugin ); + AddExportPlugin( deduplicatingExportPlugin ); inspectorTools.rokojoriPlugin = this; inspectorTools.AddPlugins(); @@ -173,6 +175,7 @@ public partial class RokojoriPlugin: EditorPlugin this.LogInfo(); RemoveNode3DGizmoPlugin( gizmoDrawerPlugin ); + RemoveExportPlugin( deduplicatingExportPlugin ); inspectorTools.rokojoriPlugin = this; inspectorTools.RemovePlugins(); diff --git a/Runtime/Cameras/Adjustments/AdjustmentsDesigner.cs b/Runtime/Cameras/Adjustments/AdjustmentsDesigner.cs new file mode 100644 index 0000000..324585e --- /dev/null +++ b/Runtime/Cameras/Adjustments/AdjustmentsDesigner.cs @@ -0,0 +1,51 @@ +using Rokojori; +using Godot; + +namespace Rokojori; + +[GlobalClass,Tool] +public partial class AdjustmentsDesigner: Node +{ + [Export] + public AdjustmentsStack stack = new AdjustmentsStack(); + + [Export] + public bool autoUpdate = false; + + [Export] + public WorldEnvironment environment; + + [ExportToolButton( "Update Adjustments" )] + public Callable updateAdjustmentsButton => Callable.From( + ()=> + { + ProcessStack(); + } + ); + + public override void _Process( double delta ) + { + if ( ! Engine.IsEditorHint() ) + { + return; + } + + if ( ! autoUpdate ) + { + return; + } + + ProcessStack(); + + } + + void ProcessStack() + { + stack.GenerateAdjustmentsTexture(); + + if ( environment != null ) + { + environment.Environment.AdjustmentColorCorrection = stack.adjustmentsTexture; + } + } +} \ No newline at end of file diff --git a/Runtime/Cameras/Adjustments/AdjustmentsDesigner.cs.uid b/Runtime/Cameras/Adjustments/AdjustmentsDesigner.cs.uid new file mode 100644 index 0000000..a616ac1 --- /dev/null +++ b/Runtime/Cameras/Adjustments/AdjustmentsDesigner.cs.uid @@ -0,0 +1 @@ +uid://8yy5v376kadx diff --git a/Runtime/Cameras/Adjustments/AdjustmentsFilter.cs b/Runtime/Cameras/Adjustments/AdjustmentsFilter.cs new file mode 100644 index 0000000..812c795 --- /dev/null +++ b/Runtime/Cameras/Adjustments/AdjustmentsFilter.cs @@ -0,0 +1,58 @@ +using Rokojori; +using Godot; + +namespace Rokojori; + +[GlobalClass,Tool] +public abstract partial class AdjustmentsFilter: Resource +{ + [Export( PropertyHint.Range, "0,1" )] + public float amount; + + [Export] + public bool enabled = true; + + + [Export] + public AdjustmentsMask[] masks; + + public virtual void PrepareFilter() + { + + } + + public Color GetFiltered( Color rgb ) + { + if ( ! enabled ) + { + return rgb ; + } + + var filtered = _FilterColor( rgb ); + + var mask = amount; + + if ( masks != null && masks.Length > 0 ) + { + var maskStack = 0f; + + for ( int i = 0; i < masks.Length; i++ ) + { + var maskIndex = ( masks.Length - 1 ) - i ; + var maskValue = masks[ maskIndex ].GetMask( rgb ); + var maskBefore = i == 0 ? null : masks[ maskIndex + 1 ]; + + maskStack = AdjustmentsMask.Blend( maskBefore, maskStack, masks[ maskIndex ], maskValue ); + } + + mask *= maskStack; + } + + + return rgb.Lerp( filtered, mask ); + + } + + abstract protected Color _FilterColor( Color rgb ); + +} \ No newline at end of file diff --git a/Runtime/Cameras/Adjustments/AdjustmentsFilter.cs.uid b/Runtime/Cameras/Adjustments/AdjustmentsFilter.cs.uid new file mode 100644 index 0000000..3f90798 --- /dev/null +++ b/Runtime/Cameras/Adjustments/AdjustmentsFilter.cs.uid @@ -0,0 +1 @@ +uid://3orxcbnth5i6 diff --git a/Runtime/Cameras/Adjustments/AdjustmentsMask.cs b/Runtime/Cameras/Adjustments/AdjustmentsMask.cs new file mode 100644 index 0000000..ead6cd3 --- /dev/null +++ b/Runtime/Cameras/Adjustments/AdjustmentsMask.cs @@ -0,0 +1,63 @@ +using Rokojori; +using Godot; + +namespace Rokojori; + +[GlobalClass,Tool] +public abstract partial class AdjustmentsMask: Resource +{ + public enum Mode + { + Add, + Subtract, + Multiply, + Divide, + Max, + Min + } + + [Export] + public Mode mode = Mode.Multiply; + + public abstract float GetMask( Color rgb ); + + public static float Blend( AdjustmentsMask bottom, float bottomMask, AdjustmentsMask top, float topMask ) + { + if ( bottom == null ) + { + return bottomMask; + } + + if ( Mode.Add == top.mode ) + { + return bottomMask + topMask; + } + + if ( Mode.Subtract == top.mode ) + { + return bottomMask - topMask; + } + + if ( Mode.Multiply == top.mode ) + { + return bottomMask * topMask; + } + + if ( Mode.Divide == top.mode ) + { + return bottomMask / topMask; + } + + if ( Mode.Max == top.mode ) + { + return Mathf.Max( bottomMask, topMask ); + } + + if ( Mode.Min == top.mode ) + { + return Mathf.Min( bottomMask, topMask ); + } + + return 0; + } +} \ No newline at end of file diff --git a/Runtime/Cameras/Adjustments/AdjustmentsMask.cs.uid b/Runtime/Cameras/Adjustments/AdjustmentsMask.cs.uid new file mode 100644 index 0000000..ede3fd5 --- /dev/null +++ b/Runtime/Cameras/Adjustments/AdjustmentsMask.cs.uid @@ -0,0 +1 @@ +uid://b7m05qa3ygvy2 diff --git a/Runtime/Cameras/Adjustments/AdjustmentsStack.cs b/Runtime/Cameras/Adjustments/AdjustmentsStack.cs new file mode 100644 index 0000000..cf1f9a3 --- /dev/null +++ b/Runtime/Cameras/Adjustments/AdjustmentsStack.cs @@ -0,0 +1,81 @@ +using Rokojori; +using Godot; + +namespace Rokojori; + +[GlobalClass,Tool] +public partial class AdjustmentsStack: Resource +{ + [Export] + public AdjustmentsFilter[] filters = []; + + [Export( PropertyHint.Range, "0,1" )] + public float amount = 1; + + [Export] + public bool enabled = true; + + [Export] + public int adjustmentSize = 33; + + [Export] + public Texture3D adjustmentsTexture; + + public void GenerateAdjustmentsTexture() + { + filters.ForEach( f => f.PrepareFilter() ); + + var luts = new Godot.Collections.Array(); + + for ( int i = 0; i < adjustmentSize; i++ ) + { + luts.Add( Image.CreateEmpty( adjustmentSize, adjustmentSize, false, Image.Format.Rgb8 ) ); + } + + for ( int bi = 0; bi < adjustmentSize; bi++ ) + { + var image = luts[ bi ]; + + var b = bi / ( adjustmentSize - 1f ); + + for ( int ri = 0; ri < adjustmentSize; ri++ ) + { + var r = ri / ( adjustmentSize - 1f ); + + for ( int gi = 0; gi < adjustmentSize; gi++ ) + { + var g = gi / ( adjustmentSize - 1f ); + var filtered = GetFilteredColor( new Color( r, g, b ) ); + image.SetPixel( ri, gi, filtered ); + } + } + } + + var texture = new ImageTexture3D(); + + if ( adjustmentsTexture is ImageTexture3D existing && existing.GetWidth() == adjustmentSize ) + { + existing.Update( luts ); + } + else + { + texture.Create( Image.Format.Rgb8, adjustmentSize, adjustmentSize, adjustmentSize, false, luts ); + adjustmentsTexture = texture; + } + } + + public Color GetFilteredColor( Color rgb ) + { + if ( ! enabled ) + { + return rgb; + } + + var originalRGB = rgb; + + filters.ForEach( f => { rgb = f.GetFiltered( rgb ); } ); + + + return originalRGB.Lerp( rgb, amount ); + } +} \ No newline at end of file diff --git a/Runtime/Cameras/Adjustments/AdjustmentsStack.cs.uid b/Runtime/Cameras/Adjustments/AdjustmentsStack.cs.uid new file mode 100644 index 0000000..7eaeeb2 --- /dev/null +++ b/Runtime/Cameras/Adjustments/AdjustmentsStack.cs.uid @@ -0,0 +1 @@ +uid://bdubaufaxcujo diff --git a/Runtime/Cameras/Adjustments/Filter/AdjustmentsCurvesFilter.cs b/Runtime/Cameras/Adjustments/Filter/AdjustmentsCurvesFilter.cs new file mode 100644 index 0000000..3f1f135 --- /dev/null +++ b/Runtime/Cameras/Adjustments/Filter/AdjustmentsCurvesFilter.cs @@ -0,0 +1,67 @@ +using Rokojori; +using Godot; + +namespace Rokojori; + +[GlobalClass,Tool] +public partial class AdjustmentsCurvesFilter: AdjustmentsFilter +{ + [Export] + public ColorChannelType driverChannel = ColorChannelType.Lightness; + + [Export] + public ColorChannelType targetChannel = ColorChannelType.Lightness; + + [Export] + public Curve curve; + + public enum MappingMode + { + Auto, + Map, + Add + } + + [Export] + public MappingMode mappingMode = MappingMode.Auto; + + protected override Color _FilterColor( Color rgb ) + { + var driverValue = ColorX.GetNormalizedChannel( rgb, driverChannel ); + + var targetValue = curve.Sample( driverValue ); + + var mapping = ResolveMappingMode(); + + if ( MappingMode.Map == mapping ) + { + return ColorX.SetNormalizedChannel( rgb, targetChannel, targetValue ); + } + else if ( MappingMode.Add == mapping ) + { + var sourceValue = ColorX.GetNormalizedChannel( rgb, targetChannel ); + + if ( MappingMode.Auto == mappingMode ) + { + var min = curve.MinValue; + var max = curve.MaxValue; + targetValue = MathX.MapClamped( targetValue, min, max, -1, 1 ); + } + + return ColorX.SetNormalizedChannel( rgb, targetChannel, targetValue + sourceValue ); + } + + return rgb; + } + + MappingMode ResolveMappingMode() + { + if ( MappingMode.Auto == mappingMode ) + { + return driverChannel == targetChannel ? MappingMode.Map : MappingMode.Add; + } + + return mappingMode; + } + +} \ No newline at end of file diff --git a/Runtime/Cameras/Adjustments/Filter/AdjustmentsCurvesFilter.cs.uid b/Runtime/Cameras/Adjustments/Filter/AdjustmentsCurvesFilter.cs.uid new file mode 100644 index 0000000..9202ed1 --- /dev/null +++ b/Runtime/Cameras/Adjustments/Filter/AdjustmentsCurvesFilter.cs.uid @@ -0,0 +1 @@ +uid://bhegakbwte7hf diff --git a/Runtime/Cameras/Adjustments/Filter/AdjustmentsHueShifter.cs b/Runtime/Cameras/Adjustments/Filter/AdjustmentsHueShifter.cs new file mode 100644 index 0000000..f6b4ada --- /dev/null +++ b/Runtime/Cameras/Adjustments/Filter/AdjustmentsHueShifter.cs @@ -0,0 +1,71 @@ +using Rokojori; +using Godot; + +namespace Rokojori; + +[GlobalClass,Tool] +public partial class AdjustmentsHueShifter: AdjustmentsFilter +{ + + [Export] + public AdjustmentsHueShifterBand band0 = new AdjustmentsHueShifterBand(); + + [Export] + public AdjustmentsHueShifterBand band1 = new AdjustmentsHueShifterBand(); + + [Export] + public AdjustmentsHueShifterBand band2 = new AdjustmentsHueShifterBand(); + + + AdjustmentsHueShifterBand combined = new AdjustmentsHueShifterBand(); + + [Export] + public bool debug = false; + + [Export] + public int numZero = 0; + + public override void PrepareFilter() + { + numZero = 0; + } + + protected override Color _FilterColor( Color rgb ) + { + + var mask0 = band0 == null ? 0f : band0.GetMask( rgb ); + var mask1 = band1 == null ? 0f : band1.GetMask( rgb ); + var mask2 = band2 == null ? 0f : band2.GetMask( rgb ); + + var sum = mask0 + mask1 + mask2; + + if ( sum <= 0 ) + { + if ( debug ) + { + numZero ++; + // RJLog.Log( "Sum to small of masks", mask0, mask1, mask2 ); + } + + return rgb; + } + + + + combined.ResetValues(); + + combined.AddChanges( band0, mask0 ); + combined.AddChanges( band1, mask1 ); + combined.AddChanges( band2, mask2 ); + + // RJLog.Log( "mask", mask0, mask1, mask2, + // "hue", combined.hueOffset, + // "temp", combined.tempratureOffset, + // "sat", combined.saturationOffset, + // "light", combined.lightnessOffset + // ); + + return combined.ProcessColor( rgb ); + } + +} \ No newline at end of file diff --git a/Runtime/Cameras/Adjustments/Filter/AdjustmentsHueShifter.cs.uid b/Runtime/Cameras/Adjustments/Filter/AdjustmentsHueShifter.cs.uid new file mode 100644 index 0000000..e827159 --- /dev/null +++ b/Runtime/Cameras/Adjustments/Filter/AdjustmentsHueShifter.cs.uid @@ -0,0 +1 @@ +uid://b06d5aqfu7aoq diff --git a/Runtime/Cameras/Adjustments/Filter/AdjustmentsHueShifterBand.cs b/Runtime/Cameras/Adjustments/Filter/AdjustmentsHueShifterBand.cs new file mode 100644 index 0000000..10dcc0e --- /dev/null +++ b/Runtime/Cameras/Adjustments/Filter/AdjustmentsHueShifterBand.cs @@ -0,0 +1,88 @@ +using Rokojori; +using Godot; + +namespace Rokojori; + +[GlobalClass,Tool] +public partial class AdjustmentsHueShifterBand: Resource +{ + + [Export( PropertyHint.Range, "-60,420")] + public float sourceHue = 0; + + [Export( PropertyHint.Range, "0,360")] + public float sourceWidth = 30; + + [Export( PropertyHint.Range, "0,1")] + public float amount = 1f; + + [Export( PropertyHint.Range, "-180,180")] + public float hueOffset = 0; + + [Export( PropertyHint.Range, "-180,180")] + public float tempratureOffset = 0; + + [Export( PropertyHint.Range, "-100,100")] + public float saturationOffset = 0; + + [Export( PropertyHint.Range, "-100,100")] + public float lightnessOffset = 0; + + + + + public void ResetValues() + { + hueOffset = 0; + + tempratureOffset = 0; + + saturationOffset = 0; + + lightnessOffset = 0; + } + + [Export] + public float fixedMask = 0; + + public float GetMask( Color color ) + { + if ( fixedMask > 0 ) + { + return fixedMask * amount; + } + + var hue = HSLColor.FromRGBA( color ).h; + + var difference = Mathf.Abs( MathX.AngleDifferenceDegrees( sourceHue, hue ) ); + + return MathX.Clamp01( 1f - Mathf.Min( 1f, difference / sourceWidth ) ) * amount; + } + + public void AddChanges( AdjustmentsHueShifterBand band, float mask ) + { + if ( band == null || mask == 0 ) + { + return; + } + + hueOffset += band.hueOffset * mask; + + tempratureOffset += band.tempratureOffset * mask; + + saturationOffset += band.saturationOffset * mask; + + lightnessOffset += band.lightnessOffset * mask; + } + + public Color ProcessColor( Color color ) + { + var hsl = HSLColor.FromRGBA( color ); + hsl.h = MathX.Repeat( hsl.h + hueOffset, 360f ); + + hsl = hsl.ShiftTemperature( tempratureOffset, saturationOffset / 100f, lightnessOffset/ 100f ); + + return hsl; + } + +} \ No newline at end of file diff --git a/Runtime/Cameras/Adjustments/Filter/AdjustmentsHueShifterBand.cs.uid b/Runtime/Cameras/Adjustments/Filter/AdjustmentsHueShifterBand.cs.uid new file mode 100644 index 0000000..fa86a5a --- /dev/null +++ b/Runtime/Cameras/Adjustments/Filter/AdjustmentsHueShifterBand.cs.uid @@ -0,0 +1 @@ +uid://cmddo8mo0qulp diff --git a/Runtime/Cameras/Adjustments/Filter/AdjustmentstGradientTint.cs b/Runtime/Cameras/Adjustments/Filter/AdjustmentstGradientTint.cs new file mode 100644 index 0000000..1f74cd3 --- /dev/null +++ b/Runtime/Cameras/Adjustments/Filter/AdjustmentstGradientTint.cs @@ -0,0 +1,30 @@ +using Rokojori; +using Godot; + +namespace Rokojori; + +[GlobalClass,Tool] +public partial class AdjustmentstGradientTint: AdjustmentsFilter +{ + [Export] + public GradientTexture1D gradient; + + [Export] + public LumaMode lumaMode = LumaMode.Lightness; + + [Export] + public ColorBlendModeType colorBlendMode = ColorBlendModeType.Alpha; + + + protected override Color _FilterColor( Color rgb ) + { + var luma = Luma.Get( rgb, lumaMode ); + + var tint = gradient.Gradient.Sample( luma ); + var amount = tint.A; + rgb.A = 1.0f; + + return ColorBlendMode.Blend( colorBlendMode, rgb, tint ); + } + +} \ No newline at end of file diff --git a/Runtime/Cameras/Adjustments/Filter/AdjustmentstGradientTint.cs.uid b/Runtime/Cameras/Adjustments/Filter/AdjustmentstGradientTint.cs.uid new file mode 100644 index 0000000..1fad3f0 --- /dev/null +++ b/Runtime/Cameras/Adjustments/Filter/AdjustmentstGradientTint.cs.uid @@ -0,0 +1 @@ +uid://c5wqcbyxi6fgf diff --git a/Runtime/Cameras/CameraTypes/ThirdPersonCamera/ThirdPersonCamera.cs b/Runtime/Cameras/CameraTypes/ThirdPersonCamera/ThirdPersonCamera.cs index 1e7a0fa..70d75af 100644 --- a/Runtime/Cameras/CameraTypes/ThirdPersonCamera/ThirdPersonCamera.cs +++ b/Runtime/Cameras/CameraTypes/ThirdPersonCamera/ThirdPersonCamera.cs @@ -22,6 +22,7 @@ namespace Rokojori [Export] public float pitch; + [Export] public ThirdPersonCameraSettings settings; @@ -99,11 +100,15 @@ namespace Rokojori var smoothedPitch = Smoothing.Apply( settings.pitchSmoothing, pitch, delta ); + + var pitchState = MathX.NormalizeClamped( pitch, settings.minPitch, settings.maxPitch ); var distance = settings.distanceForPitch.Sample( pitchState ) * settings.distanceScale; + + var smoothedDistance = Smoothing.Apply( settings.distanceSmoothing, distance, delta ); FOV = settings.fovOffset + ( settings.fovForPitch?.Sample( pitchState ) ?? 0f ); - cameraPosition = targetPosition + Math3D.YawPitchRotation( smoothedYaw, smoothedPitch ) * Vector3.Forward * distance; + cameraPosition = targetPosition + Math3D.YawPitchRotation( smoothedYaw, smoothedPitch ) * Vector3.Forward * smoothedDistance; cameraForward = ( targetPosition - cameraPosition ).Normalized(); // LookAt( targetPosition, Vector3.Up, true ); diff --git a/Runtime/Cameras/CameraTypes/ThirdPersonCamera/ThirdPersonCameraSettings.cs b/Runtime/Cameras/CameraTypes/ThirdPersonCamera/ThirdPersonCameraSettings.cs index e84a618..f2a3c95 100644 --- a/Runtime/Cameras/CameraTypes/ThirdPersonCamera/ThirdPersonCameraSettings.cs +++ b/Runtime/Cameras/CameraTypes/ThirdPersonCamera/ThirdPersonCameraSettings.cs @@ -12,14 +12,16 @@ namespace Rokojori [Export] public Smoothing targetFollowSmoothing = new FrameSmoothing(); - - [Export] public Smoothing yawSmoothing = new FrameSmoothing(); [Export] public Smoothing pitchSmoothing = new FrameSmoothing(); + [Export] + public Smoothing distanceSmoothing = new FrameSmoothing(); + + [Export] public ThirdPersonCameraData[] deviceData = []; diff --git a/Runtime/Colors/ColorBlendMode.cs b/Runtime/Colors/ColorBlendMode.cs index a5d1c4a..5bbfa31 100644 --- a/Runtime/Colors/ColorBlendMode.cs +++ b/Runtime/Colors/ColorBlendMode.cs @@ -8,7 +8,16 @@ namespace Rokojori Alpha, Add, Multiply, - Replace + + Screen, + Overlay, + + HardLight, + SoftLight, + + Color, + Hue, + Saturation } public class ColorBlendMode @@ -17,41 +26,60 @@ namespace Rokojori { switch ( type ) { - case ColorBlendModeType.Alpha: return Alpha( bottom, top ); - case ColorBlendModeType.Multiply: return Multiply( bottom, top ); - case ColorBlendModeType.Add: return Add( bottom, top ); - case ColorBlendModeType.Replace: return Replace( bottom, top ); + case ColorBlendModeType.Alpha: return ColorX.Blend( bottom, top ); + case ColorBlendModeType.Multiply: return ColorX.BlendMultiply( bottom, top ); + case ColorBlendModeType.Add: return ColorX.BlendAdd( bottom, top ); + + case ColorBlendModeType.Screen: return ColorX.BlendScreen( bottom, top ); + case ColorBlendModeType.Overlay: return ColorX.BlendScreen( bottom, top ); + + case ColorBlendModeType.HardLight: return ColorX.BlendHardLight( bottom, top ); + case ColorBlendModeType.SoftLight: return ColorX.BlendSoftLight( bottom, top ); + + case ColorBlendModeType.Color: return ColorX.BlendColor( bottom, top ); + case ColorBlendModeType.Hue: return ColorX.BlendHue( bottom, top ); + case ColorBlendModeType.Saturation: return ColorX.BlendSaturation( bottom, top ); } return new Color( 1, 1, 1, 1 ); } - public static Color Alpha( Color bottom, Color top ) - { - var a = top.A + bottom.A * ( 1f - top.A ); + // public static Color Alpha( Color bottom, Color top ) + // { + // var a = top.A + bottom.A * ( 1f - top.A ); - var colorBottom = ColorX.RGB( bottom ); - var colorTop = ColorX.RGB( top ); + // var colorBottom = ColorX.RGB( bottom ); + // var colorTop = ColorX.RGB( top ); - var colorRGB = ( colorTop * top.A + colorBottom * bottom.A * ( 1f - top.A ) ) / a; + // var colorRGB = ( colorTop * top.A + colorBottom * bottom.A * ( 1f - top.A ) ) / a; - return ColorX.Create( colorRGB, a ); - } + // return ColorX.Create( colorRGB, a ); + // } - public static Color Multiply( Color bottom, Color top ) - { - return bottom * top; - } + // Color Multiply( Color bottom, Color top ) + // { + // if ( bottom.A <= 0.0 ) + // { + // return top; + // } + + // return Alpha( bottom, MultiplyFunction( bottom.rgb, top.rgb ), top.a ) ); + // } - public static Color Add( Color bottom, Color top ) - { - return bottom + top; - } + // public static Color MultiplyFunction( Color bottom, Color top ) + // { + // return bottom * top; + // } - public static Color Replace( Color bottom, Color top ) - { - return top; - } + // public static Color AddFunction( Color bottom, Color top ) + // { + // return bottom + top; + // } + + // public static Color ReplaceFunction( Color bottom, Color top ) + // { + // return top; + // } } } \ No newline at end of file diff --git a/Runtime/Colors/ColorChannelType.cs b/Runtime/Colors/ColorChannelType.cs index dbcbf36..a565b40 100644 --- a/Runtime/Colors/ColorChannelType.cs +++ b/Runtime/Colors/ColorChannelType.cs @@ -11,7 +11,7 @@ namespace Rokojori Blue, Hue, Saturation, - Luminance, + Lightness, Alpha } @@ -77,7 +77,7 @@ namespace Rokojori return $"RGBtoHSL( {source} ).y"; } - if ( ColorChannelType.Luminance == colorChannelType ) + if ( ColorChannelType.Lightness == colorChannelType ) { return $"RGBtoHSL( {source} ).u"; } diff --git a/Runtime/Colors/ColorX.cs b/Runtime/Colors/ColorX.cs index 9910458..6abfdf8 100644 --- a/Runtime/Colors/ColorX.cs +++ b/Runtime/Colors/ColorX.cs @@ -40,6 +40,18 @@ namespace Rokojori return new Vector3( rgba.X, rgba.Y, rgba.Z ); } + public static float GetNormalizedChannel( this Color color, ColorChannelType colorChannelType ) + { + var channel = color.GetChannel( colorChannelType ); + + if ( ColorChannelType.Hue == colorChannelType ) + { + channel /= 360.0f; + } + + return channel; + } + public static float GetChannel( this Color color, ColorChannelType colorChannelType ) { if ( ColorChannelType.Red == colorChannelType ) @@ -75,7 +87,7 @@ namespace Rokojori return hsl.s; } - if ( ColorChannelType.Luminance == colorChannelType ) + if ( ColorChannelType.Lightness == colorChannelType ) { return hsl.l; } @@ -84,6 +96,16 @@ namespace Rokojori } + public static Color SetNormalizedChannel( this Color color, ColorChannelType colorChannelType, float value ) + { + if ( ColorChannelType.Hue == colorChannelType ) + { + value *= 360.0f; + } + + return color.SetChannel( colorChannelType, value ); + } + public static Color SetChannel( this Color color, ColorChannelType colorChannelType, float value ) { if ( ColorChannelType.Red == colorChannelType ) @@ -121,7 +143,7 @@ namespace Rokojori color = hsl; } - if ( ColorChannelType.Luminance == colorChannelType ) + if ( ColorChannelType.Lightness == colorChannelType ) { hsl.l = value; color = hsl; diff --git a/Runtime/Colors/Luma.cs b/Runtime/Colors/Luma.cs new file mode 100644 index 0000000..7e83793 --- /dev/null +++ b/Runtime/Colors/Luma.cs @@ -0,0 +1,53 @@ +using Godot; +using System.Collections.Generic; + +using Rokojori.Extensions; +namespace Rokojori; + +public enum LumaMode +{ + Lightness, + Luminosity_709, + Luminosity_601, + Average, + Min, + Max +} + +public class Luma +{ + public static float Get( Color rgb, LumaMode mode ) + { + if ( LumaMode.Lightness == mode ) + { + return HSLColor.FromRGBA( rgb ).l; + } + + if ( LumaMode.Luminosity_709 == mode ) + { + return rgb.R * 0.2126f + rgb.G * 0.7152f + rgb.B * 0.0722f; + } + + if ( LumaMode.Luminosity_601 == mode ) + { + return rgb.R * 0.299f + rgb.G * 0.587f + rgb.B * 0.114f; + } + + if ( LumaMode.Average == mode ) + { + return ( rgb.R + rgb.G + rgb.B ) / 3f; + } + + if ( LumaMode.Min == mode ) + { + return MathX.Min( rgb.R, rgb.G, rgb.B ); + } + + if ( LumaMode.Max == mode ) + { + return MathX.Max( rgb.R, rgb.G, rgb.B ); + } + + return 0; + } +} \ No newline at end of file diff --git a/Runtime/Colors/Luma.cs.uid b/Runtime/Colors/Luma.cs.uid new file mode 100644 index 0000000..f382b9a --- /dev/null +++ b/Runtime/Colors/Luma.cs.uid @@ -0,0 +1 @@ +uid://wxxtcur80snr diff --git a/Runtime/Math/MathX.cs b/Runtime/Math/MathX.cs index 464bb68..b85d022 100644 --- a/Runtime/Math/MathX.cs +++ b/Runtime/Math/MathX.cs @@ -245,7 +245,10 @@ namespace Rokojori } - + public static float AngleDifferenceDegrees( float degreesA, float degreesB ) + { + return Mathf.AngleDifference( degreesA * DegreesToRadians, degreesB * DegreesToRadians ) * RadiansToDegrees; + } public static float AngleDelta( float degreesA, float degreesB) {